{"action":"created","comment":{"url":"https://api.github.com/repos/typetools/checker-framework/pulls/comments/46478779","id":46478779,"diff_hunk":"@@ -0,0 +1,588 @@\n+package org.checkerframework.common.signatureinference;\n+\n+import java.io.File;\n+import java.io.FileWriter;\n+import java.io.IOException;\n+import java.lang.annotation.Target;\n+import java.util.ArrayList;\n+import java.util.HashMap;\n+import java.util.HashSet;\n+import java.util.List;\n+import java.util.Map;\n+import java.util.Set;\n+\n+import javax.annotation.processing.ProcessingEnvironment;\n+import javax.lang.model.element.AnnotationMirror;\n+import javax.lang.model.element.AnnotationValue;\n+import javax.lang.model.element.Element;\n+import javax.lang.model.element.ExecutableElement;\n+import javax.lang.model.element.VariableElement;\n+import javax.lang.model.type.TypeMirror;\n+\n+import org.checkerframework.dataflow.cfg.node.FieldAccessNode;\n+import org.checkerframework.dataflow.cfg.node.ImplicitThisLiteralNode;\n+import org.checkerframework.dataflow.cfg.node.MethodInvocationNode;\n+import org.checkerframework.dataflow.cfg.node.Node;\n+import org.checkerframework.dataflow.cfg.node.ReturnNode;\n+import org.checkerframework.framework.type.AnnotatedTypeFactory;\n+import org.checkerframework.framework.type.AnnotatedTypeMirror;\n+import org.checkerframework.framework.util.AnnotatedTypes;\n+import org.checkerframework.framework.util.AnnotationBuilder;\n+import org.checkerframework.javacutil.AnnotationUtils;\n+import org.checkerframework.javacutil.ErrorReporter;\n+import org.checkerframework.javacutil.InternalUtils;\n+\n+import annotations.Annotation;\n+import annotations.el.AClass;\n+import annotations.el.AField;\n+import annotations.el.AMethod;\n+import annotations.el.AScene;\n+import annotations.el.AnnotationDef;\n+import annotations.el.DefException;\n+import annotations.field.AnnotationFieldType;\n+import annotations.field.ArrayAFT;\n+import annotations.field.BasicAFT;\n+import annotations.field.ScalarAFT;\n+import annotations.io.IndexFileParser;\n+import annotations.io.IndexFileWriter;\n+import annotations.util.JVMNames;\n+\n+import com.sun.source.tree.ClassTree;\n+import com.sun.source.tree.MethodTree;\n+import com.sun.source.tree.Tree;\n+import com.sun.tools.javac.code.Attribute.Array;\n+import com.sun.tools.javac.code.Symbol.ClassSymbol;\n+import com.sun.tools.javac.code.Symbol.TypeSymbol;\n+import com.sun.tools.javac.code.Symbol.VarSymbol;\n+import com.sun.tools.javac.code.Type;\n+import com.sun.tools.javac.code.Type.ArrayType;\n+import com.sun.tools.javac.code.Type.ClassType;\n+\n+/**\n+ * This class writes inferred types for fields, method return types and method\n+ * parameters to a .jaif file.  Calling an update* method\n+ * ({@link SignatureInferenceScenes#updateFieldTypeInScene updateFieldTypeInScene},\n+ * {@link SignatureInferenceScenes#updateMethodParameterTypeInScene updateMethodParameterTypeInScene} or\n+ * {@link SignatureInferenceScenes#updateMethodReturnTypeInScene updateMethodReturnTypeInScene}) \n+ * reads the currently-stored type, if any, and replaces it by the LUB of\n+ * it and the update method's argument.\n+ *\n+ * This class does not perform inference for elements that have explicit\n+ * annotations - an explicitly annotated field type, method return type or\n+ * method parameter type will not have its respective inferred type written\n+ * into a .jaif file.\n+ *\n+ * @author pbsf\n+ */\n+public class SignatureInferenceScenes {\n+\n+    /**\n+     * Path to where .jaif files will be written to and read from.\n+     * This path is relative to where the CF's javac command is executed.\n+     */\n+    public final static String JAIF_FILES_PATH = \"build/jaif-files/\";\n+\n+    /** Maps file paths (Strings) to Scenes. */\n+    private static Map<String, AScene> scenes = new HashMap<String, AScene>();\n+\n+    /** Set containing all Scenes that were modified in the current ClassTree.\n+     * Modifying a Scene means adding (or changing) a type annotation for a\n+     * field, method return type or method parameter type in the Scene.\n+     * (Scenes are modified by the method\n+     * {@link SignatureInferenceScenes#updateAnnotationSetInScene updateAnnotationSetInScene)}\n+     */\n+    private static Set<String> modifiedScenes = new HashSet<String>();\n+\n+    /**\n+     * Returns the Scene related to a .jaif file path passed as input.\n+     */\n+    private static AScene getScene(String jaifPath) {\n+        if (!scenes.containsKey(jaifPath)) {\n+            File jaifFile = new File(jaifPath);\n+            AScene scene = new AScene();\n+            if (jaifFile.exists()) {\n+                try {\n+                    IndexFileParser.parseFile(jaifPath, scene);\n+                } catch (IOException e) {\n+                    ErrorReporter.errorAbort(\"Could not open file in: \" + jaifPath + \".\"\n+                            + \" Exception message: \" + e.getMessage());\n+                }\n+            }\n+            scenes.put(jaifPath, scene);\n+        }\n+        return scenes.get(jaifPath);\n+    }\n+\n+    /**\n+     * Clears the set of modified scenes.\n+     * (Scenes are modified by the method\n+     * {@link SignatureInferenceScenes#updateAnnotationSetInScene updateAnnotationSetInScene)}\n+     */\n+    public static void clearModifiedScenes() {\n+        modifiedScenes.clear();\n+    }\n+\n+    /**\n+     * Adds the identifier of a Scene in the set of modified scenes.\n+     * (Scenes are modified by the method\n+     * {@link SignatureInferenceScenes#updateAnnotationSetInScene updateAnnotationSetInScene)}\n+     */\n+    private static void addModifiedScene(String scene) {\n+        modifiedScenes.add(scene);\n+    }\n+\n+    /**\n+     * Write all modified scenes into .jaif files.\n+     * (Scenes are modified by the method\n+     * {@link SignatureInferenceScenes#updateAnnotationSetInScene updateAnnotationSetInScene)}\n+     */\n+    public static void writeScenesToJaif() {\n+        for (String jaifPath : modifiedScenes) {\n+            try {\n+                File jaifFile = new File(jaifPath);\n+                if (!jaifFile.exists()) {\n+                    jaifFile.getParentFile().mkdirs();\n+                }\n+                AScene scene = scenes.get(jaifPath);\n+                IndexFileWriter.write(scene, new FileWriter(jaifPath));\n+            } catch (IOException e) {\n+                ErrorReporter.errorAbort(\"Could not open file in: \" + jaifPath\n+                        + \". Exception message: \" + e.getMessage());\n+            } catch (DefException e) {\n+                ErrorReporter.errorAbort(e.getMessage());\n+            } catch (Exception e) {\n+                System.out.println();\n+            }\n+        }\n+    }\n+\n+    /**\n+     * Updates the parameters' types of the method methodElt in the Scene of the\n+     * class with symbol classSymbol.\n+     *\n+     * For each method parameter in methodElt:\n+     *   If the Scene does not contain an annotated type for that parameter,\n+     *   then the type of the respective value passed as argument in the\n+     *   method call methodInvNode will be added to the parameter in the Scene.\n+     *   If the Scene previously contained an annotated type for that parameter,\n+     *   then its new type will be the LUB between the previous type and the\n+     *   type of the respective value passed as argument in the method call.\n+     *\n+     * @param methodInvNode the node representing a method invocation.\n+     * @param classSymbol the symbol of the class that contains the method being\n+     * invoked.\n+     * @param methodElt the element of the method being invoked.\n+     * @param atf the annotated type factory of a given type system, whose\n+     * type hierarchy will be used to update the method parameters' types.\n+     */\n+    public static void updateMethodParameterTypeInScene(\n+            MethodInvocationNode methodInvNode, ClassSymbol classSymbol,\n+            ExecutableElement methodElt, AnnotatedTypeFactory atf) {\n+        if (classSymbol == null) return;\n+        String jaifPath = JAIF_FILES_PATH + classSymbol.flatname.toString() +\n+                \".jaif\";\n+        AScene scene = getScene(jaifPath);\n+\n+        AClass clazz = getJaifClass(classSymbol, scene);\n+        if (clazz == null) return; // Anonymous class => Ignore, for now.\n+        String methodName = JVMNames.getJVMMethodName(methodElt);\n+        AMethod method = getJaifMethod(clazz, methodName, scene);\n+        for (int i = 0; i < methodInvNode.getArguments().size(); i++) {\n+            VariableElement ve = methodElt.getParameters().get(i);\n+            if (atf.getAnnotatedType(ve).getExplicitAnnotations().size() > 0) {\n+                // Ignore parameters that have a declared annotated type.\n+                continue;\n+            }\n+\n+            Node arg = methodInvNode.getArgument(i);\n+            AnnotatedTypeMirror argType = atf.getAnnotatedType(arg.getTree());\n+            AField param = method.parameters.vivify(i);\n+            updateAnnotationSetInScene(\n+                    param.type.tlAnnotationsHere, atf, jaifPath, argType);\n+        }\n+    }\n+\n+    /**\n+     * Updates the type of the field lhs in the Scene of the class with\n+     * symbol classSymbol.\n+     *\n+     * If the Scene contains no entry for the field lhs,\n+     * the entry will be created and its type will be the type of rhs. If the\n+     * Scene previously contained an entry/type for lhs, its new type will be\n+     * the LUB between the previous type and the type of rhs.\n+     *\n+     * @param lhs the field.\n+     * @param rhs the expression being assigned to the field.\n+     * @param classSymbol the symbol of the class that contains the field.\n+     * @param atf the annotated type factory of a given type system, whose\n+     * type hierarchy will be used to update the field's type.\n+     */\n+    public static void updateFieldTypeInScene(FieldAccessNode lhs, Node rhs,\n+            ClassSymbol classSymbol, AnnotatedTypeFactory atf) {\n+        if (classSymbol == null) return;\n+        AnnotatedTypeMirror lhsATM = atf.getAnnotatedType(lhs.getTree());\n+        if (lhsATM.getExplicitAnnotations().size() > 0) {\n+            // We do not infer types if there are explicit annotations.\n+            // See https://github.com/typetools/annotation-tools/issues/105\n+            return;\n+        }\n+        String jaifPath = JAIF_FILES_PATH + classSymbol.flatname.toString() +\n+                \".jaif\";\n+        AScene scene = getScene(jaifPath);\n+        AnnotatedTypeMirror rhsATM = atf.getAnnotatedType(rhs.getTree());\n+\n+        AClass clazz = getJaifClass(classSymbol, scene);\n+        if (clazz == null) return; // Anonymous class => Ignore, for now.\n+        AField field = getJaifField(clazz, lhs);\n+        updateAnnotationSetInScene(\n+                field.tlAnnotationsHere, atf, jaifPath, rhsATM);\n+    }\n+\n+    /**\n+     * Updates the return type of the method methodTree in the\n+     * Scene of the class with symbol classSymbol.\n+     *\n+     * If the Scene does not contain an annotated return type for the method\n+     * methodTree, then the type of the value passed to the return expression\n+     * will be added to the return type of that method in the Scene.\n+     * If the Scene previously contained an annotated return type for the\n+     * method methodTree, its new type will be the LUB between the previous\n+     * type and the type of the value passed to the return expression.\n+     *\n+     * @param retNode the node that contains the expression returned.\n+     * @param classSymbol the symbol of the class that contains the method.\n+     * @param atf the annotated type factory of a given type system, whose\n+     * type hierarchy will be used to update the method's return type.\n+     */\n+    public static void updateMethodReturnTypeInScene(ReturnNode retNode,\n+            ClassSymbol classSymbol, MethodTree methodTree,\n+            AnnotatedTypeFactory atf) {\n+        if (classSymbol == null) return;\n+        AnnotatedTypeMirror methodReturnType = atf.getAnnotatedType(methodTree).\n+                getReturnType();\n+        if (methodReturnType.getExplicitAnnotations().size() > 0) {\n+            // We do not infer types if there are explicit annotations.\n+            // See https://github.com/typetools/annotation-tools/issues/105\n+            return;\n+        }\n+        String jaifPath = JAIF_FILES_PATH + classSymbol.flatname.toString() +\n+                \".jaif\";\n+        AScene scene = getScene(jaifPath);\n+        AnnotatedTypeMirror returnExprATM = atf.getAnnotatedType(retNode.\n+                getTree().getExpression());\n+\n+        AClass clazz = getJaifClass(classSymbol, scene);\n+        if (clazz == null) return; // Anonymous class => Ignore, for now.\n+        String methodName = JVMNames.getJVMMethodName(methodTree);\n+        AMethod method = getJaifMethod(clazz, methodName, scene);\n+        if (method.returnType != null) {\n+            updateAnnotationSetInScene(\n+                    method.returnType.tlAnnotationsHere, atf,\n+                    jaifPath, returnExprATM);\n+        }\n+    }\n+\n+    /**\n+     * Updates the set of annotations in a location of a Scene. If the set of\n+     * annotations (curAnnos) is empty, then the updated set will be the set of\n+     * annotations in newATM.\n+     * If curAnnos is not empty, the updated set will be the LUB between\n+     * curAnnos and newATM.\n+     *\n+     * @param curAnnos set of annotations located somewhere in a Scene.\n+     * @param atf the annotated type factory of a given type system, whose\n+     * type hierarchy will be used to update curAnnos.\n+     * @param jaifPath identifies a Scene.\n+     * @param newATM the type containing a set of annotations to be\n+     * added (or \"lubbed\") to the Scene.\n+     */\n+    private static void updateAnnotationSetInScene(Set<Annotation> curAnnos,\n+            AnnotatedTypeFactory atf, String jaifPath,\n+            AnnotatedTypeMirror newATM) {\n+        if (curAnnos != null && curAnnos.size() > 0) {\n+            AnnotatedTypeMirror curAnnosATM = setOfAnnotationsToATM(\n+                    curAnnos, atf, false, newATM.getUnderlyingType());\n+            if (curAnnosATM != null) {\n+                newATM = AnnotatedTypes.leastUpperBound(\n+                        atf.getProcessingEnv(), atf, newATM, curAnnosATM);\n+            }\n+        }\n+\n+        Set<Annotation> setOfAnnos = atmToSetOfAnnotations(newATM, atf);\n+        curAnnos.clear();\n+        curAnnos.addAll(setOfAnnos);\n+        addModifiedScene(jaifPath);\n+    }\n+\n+    /**\n+     * Returns the AField in an AScene, given an AClass and a FieldAccessNode.\n+     */\n+    private static AField getJaifField(AClass clazz, FieldAccessNode lhs) {\n+        String fieldName = lhs.getFieldName();\n+        AField field = clazz.fields.get(fieldName);\n+        if (field == null) {\n+            field = clazz.fields.vivify(fieldName);\n+        }\n+        return field;\n+    }\n+\n+    /**\n+     * Returns the AMethod in an AScene, given an AClass and a MethodTree.\n+     */\n+    private static AMethod getJaifMethod(AClass clazz, String methodName,\n+            AScene scene) {\n+        AMethod method = clazz.methods.get(methodName);\n+        if (method == null) {\n+            method = clazz.methods.vivify(methodName);\n+        }\n+        return method;\n+    }\n+\n+    /**\n+     * Returns the AClass in an AScene, given a ClassSymbol.\n+     */\n+    private static AClass getJaifClass(ClassSymbol classSymbol, AScene scene) {\n+        String className = classSymbol.getQualifiedName().toString();\n+        AClass clazz = scene.classes.get(className);\n+        if (clazz == null) {\n+            clazz = scene.classes.vivify(className);\n+        }\n+        if (className.equals(\"\")) {\n+            // TODO: Handle anonymous classes soundly, if possible\n+            // (I can't think of a way to do that).\n+            // Their name is equal to an empty String. For now we are ignoring them.\n+            return null;\n+        }\n+        return clazz;\n+    }\n+\n+\n+    /**\n+     * Returns true if am should not be inserted in source code,\n+     * but is rather an implementation detail.\n+     * I.E. {@link org.checkerframework.common.value.qual.BottomVal}.\n+     * Returns false otherwise.\n+     */\n+    private static boolean ignoreAnnotation(AnnotationMirror am) {\n+        Target target = am.getAnnotationType().asElement().","path":"framework/src/org/checkerframework/common/signatureinference/SignatureInferenceScenes.java","position":null,"original_position":367,"commit_id":"164c1b35b70d39b2c654774bfb1ff5a28d354a08","original_commit_id":"d2a27223789a02a177b6898e168562a658c2e075","user":{"login":"pbsf","id":4238254,"avatar_url":"https://avatars.githubusercontent.com/u/4238254?v=3","gravatar_id":"","url":"https://api.github.com/users/pbsf","html_url":"https://github.com/pbsf","followers_url":"https://api.github.com/users/pbsf/followers","following_url":"https://api.github.com/users/pbsf/following{/other_user}","gists_url":"https://api.github.com/users/pbsf/gists{/gist_id}","starred_url":"https://api.github.com/users/pbsf/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pbsf/subscriptions","organizations_url":"https://api.github.com/users/pbsf/orgs","repos_url":"https://api.github.com/users/pbsf/repos","events_url":"https://api.github.com/users/pbsf/events{/privacy}","received_events_url":"https://api.github.com/users/pbsf/received_events","type":"User","site_admin":false},"body":"Yes it can! Thanks for catching that. I'm now handling that case too.","created_at":"2015-12-02T21:35:00Z","updated_at":"2015-12-02T21:35:00Z","html_url":"https://github.com/typetools/checker-framework/pull/477#discussion_r46478779","pull_request_url":"https://api.github.com/repos/typetools/checker-framework/pulls/477","_links":{"self":{"href":"https://api.github.com/repos/typetools/checker-framework/pulls/comments/46478779"},"html":{"href":"https://github.com/typetools/checker-framework/pull/477#discussion_r46478779"},"pull_request":{"href":"https://api.github.com/repos/typetools/checker-framework/pulls/477"}}},"pull_request":{"url":"https://api.github.com/repos/typetools/checker-framework/pulls/477","id":45806315,"html_url":"https://github.com/typetools/checker-framework/pull/477","diff_url":"https://github.com/typetools/checker-framework/pull/477.diff","patch_url":"https://github.com/typetools/checker-framework/pull/477.patch","issue_url":"https://api.github.com/repos/typetools/checker-framework/issues/477","number":477,"state":"open","locked":false,"title":"Whole-program type inference using .jaif files","user":{"login":"pbsf","id":4238254,"avatar_url":"https://avatars.githubusercontent.com/u/4238254?v=3","gravatar_id":"","url":"https://api.github.com/users/pbsf","html_url":"https://github.com/pbsf","followers_url":"https://api.github.com/users/pbsf/followers","following_url":"https://api.github.com/users/pbsf/following{/other_user}","gists_url":"https://api.github.com/users/pbsf/gists{/gist_id}","starred_url":"https://api.github.com/users/pbsf/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pbsf/subscriptions","organizations_url":"https://api.github.com/users/pbsf/orgs","repos_url":"https://api.github.com/users/pbsf/repos","events_url":"https://api.github.com/users/pbsf/events{/privacy}","received_events_url":"https://api.github.com/users/pbsf/received_events","type":"User","site_admin":false},"body":"Added a behavior to the CFAbstractTransfer that enables a whole-program type inference using .jaif files. \r\nTo enable this feature the argument -AperformWholeProgramInference must be passed to javac.\r\n\r\nWith this feature on, method return types and field types are stored in .jaif files. The types in the .jaif files get updated at return statements and assignments. Note that this works for both private and non-private methods and fields.\r\n\r\nAfter generating the .jaif files, the insert-annotations-to-source tool can be used to add annotations to .java files. I created a script that iteratively runs inference and adds annotations from .jaif files to the .java files on the program until the .jaif files from one execution are similar to the .jaif files from the previous execution.\r\n\r\nExample of usage:\r\n$CHECKERFRAMEWORK/framework/whole-program-inference-scripts/infer-and-annotate.sh org.checkerframework.common.value.ValueChecker $PROJECT_CP $JAVA_FILES_PATHS $EXTRA_PROCESSOR_ARGS\r\n\r\nTesting done:\r\nant jaif-inference-tests\r\nant javadoc\r\nant dist (Since AFU is now added to the classpath, it made sense to test this.)\r\n","created_at":"2015-09-23T17:19:51Z","updated_at":"2015-12-02T21:35:00Z","closed_at":null,"merged_at":null,"merge_commit_sha":"e5ea96a88c30d8755f86281f63408c14471b61b4","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/typetools/checker-framework/pulls/477/commits","review_comments_url":"https://api.github.com/repos/typetools/checker-framework/pulls/477/comments","review_comment_url":"https://api.github.com/repos/typetools/checker-framework/pulls/comments{/number}","comments_url":"https://api.github.com/repos/typetools/checker-framework/issues/477/comments","statuses_url":"https://api.github.com/repos/typetools/checker-framework/statuses/164c1b35b70d39b2c654774bfb1ff5a28d354a08","head":{"label":"pbsf:master","ref":"master","sha":"164c1b35b70d39b2c654774bfb1ff5a28d354a08","user":{"login":"pbsf","id":4238254,"avatar_url":"https://avatars.githubusercontent.com/u/4238254?v=3","gravatar_id":"","url":"https://api.github.com/users/pbsf","html_url":"https://github.com/pbsf","followers_url":"https://api.github.com/users/pbsf/followers","following_url":"https://api.github.com/users/pbsf/following{/other_user}","gists_url":"https://api.github.com/users/pbsf/gists{/gist_id}","starred_url":"https://api.github.com/users/pbsf/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pbsf/subscriptions","organizations_url":"https://api.github.com/users/pbsf/orgs","repos_url":"https://api.github.com/users/pbsf/repos","events_url":"https://api.github.com/users/pbsf/events{/privacy}","received_events_url":"https://api.github.com/users/pbsf/received_events","type":"User","site_admin":false},"repo":{"id":42934530,"name":"checker-framework","full_name":"pbsf/checker-framework","owner":{"login":"pbsf","id":4238254,"avatar_url":"https://avatars.githubusercontent.com/u/4238254?v=3","gravatar_id":"","url":"https://api.github.com/users/pbsf","html_url":"https://github.com/pbsf","followers_url":"https://api.github.com/users/pbsf/followers","following_url":"https://api.github.com/users/pbsf/following{/other_user}","gists_url":"https://api.github.com/users/pbsf/gists{/gist_id}","starred_url":"https://api.github.com/users/pbsf/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pbsf/subscriptions","organizations_url":"https://api.github.com/users/pbsf/orgs","repos_url":"https://api.github.com/users/pbsf/repos","events_url":"https://api.github.com/users/pbsf/events{/privacy}","received_events_url":"https://api.github.com/users/pbsf/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/pbsf/checker-framework","description":"Pluggable type-checking for Java","fork":true,"url":"https://api.github.com/repos/pbsf/checker-framework","forks_url":"https://api.github.com/repos/pbsf/checker-framework/forks","keys_url":"https://api.github.com/repos/pbsf/checker-framework/keys{/key_id}","collaborators_url":"https://api.github.com/repos/pbsf/checker-framework/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/pbsf/checker-framework/teams","hooks_url":"https://api.github.com/repos/pbsf/checker-framework/hooks","issue_events_url":"https://api.github.com/repos/pbsf/checker-framework/issues/events{/number}","events_url":"https://api.github.com/repos/pbsf/checker-framework/events","assignees_url":"https://api.github.com/repos/pbsf/checker-framework/assignees{/user}","branches_url":"https://api.github.com/repos/pbsf/checker-framework/branches{/branch}","tags_url":"https://api.github.com/repos/pbsf/checker-framework/tags","blobs_url":"https://api.github.com/repos/pbsf/checker-framework/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/pbsf/checker-framework/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/pbsf/checker-framework/git/refs{/sha}","trees_url":"https://api.github.com/repos/pbsf/checker-framework/git/trees{/sha}","statuses_url":"https://api.github.com/repos/pbsf/checker-framework/statuses/{sha}","languages_url":"https://api.github.com/repos/pbsf/checker-framework/languages","stargazers_url":"https://api.github.com/repos/pbsf/checker-framework/stargazers","contributors_url":"https://api.github.com/repos/pbsf/checker-framework/contributors","subscribers_url":"https://api.github.com/repos/pbsf/checker-framework/subscribers","subscription_url":"https://api.github.com/repos/pbsf/checker-framework/subscription","commits_url":"https://api.github.com/repos/pbsf/checker-framework/commits{/sha}","git_commits_url":"https://api.github.com/repos/pbsf/checker-framework/git/commits{/sha}","comments_url":"https://api.github.com/repos/pbsf/checker-framework/comments{/number}","issue_comment_url":"https://api.github.com/repos/pbsf/checker-framework/issues/comments{/number}","contents_url":"https://api.github.com/repos/pbsf/checker-framework/contents/{+path}","compare_url":"https://api.github.com/repos/pbsf/checker-framework/compare/{base}...{head}","merges_url":"https://api.github.com/repos/pbsf/checker-framework/merges","archive_url":"https://api.github.com/repos/pbsf/checker-framework/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/pbsf/checker-framework/downloads","issues_url":"https://api.github.com/repos/pbsf/checker-framework/issues{/number}","pulls_url":"https://api.github.com/repos/pbsf/checker-framework/pulls{/number}","milestones_url":"https://api.github.com/repos/pbsf/checker-framework/milestones{/number}","notifications_url":"https://api.github.com/repos/pbsf/checker-framework/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/pbsf/checker-framework/labels{/name}","releases_url":"https://api.github.com/repos/pbsf/checker-framework/releases{/id}","created_at":"2015-09-22T13:12:51Z","updated_at":"2015-09-22T13:13:07Z","pushed_at":"2015-12-02T20:28:50Z","git_url":"git://github.com/pbsf/checker-framework.git","ssh_url":"git@github.com:pbsf/checker-framework.git","clone_url":"https://github.com/pbsf/checker-framework.git","svn_url":"https://github.com/pbsf/checker-framework","homepage":"http://checkerframework.org/","size":165023,"stargazers_count":0,"watchers_count":0,"language":"Java","has_issues":false,"has_downloads":false,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":1,"forks":0,"open_issues":1,"watchers":0,"default_branch":"master"}},"base":{"label":"typetools:master","ref":"master","sha":"aa8beda966c73336048d66f31adfff63d1825be0","user":{"login":"typetools","id":12883499,"avatar_url":"https://avatars.githubusercontent.com/u/12883499?v=3","gravatar_id":"","url":"https://api.github.com/users/typetools","html_url":"https://github.com/typetools","followers_url":"https://api.github.com/users/typetools/followers","following_url":"https://api.github.com/users/typetools/following{/other_user}","gists_url":"https://api.github.com/users/typetools/gists{/gist_id}","starred_url":"https://api.github.com/users/typetools/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/typetools/subscriptions","organizations_url":"https://api.github.com/users/typetools/orgs","repos_url":"https://api.github.com/users/typetools/repos","events_url":"https://api.github.com/users/typetools/events{/privacy}","received_events_url":"https://api.github.com/users/typetools/received_events","type":"Organization","site_admin":false},"repo":{"id":38488726,"name":"checker-framework","full_name":"typetools/checker-framework","owner":{"login":"typetools","id":12883499,"avatar_url":"https://avatars.githubusercontent.com/u/12883499?v=3","gravatar_id":"","url":"https://api.github.com/users/typetools","html_url":"https://github.com/typetools","followers_url":"https://api.github.com/users/typetools/followers","following_url":"https://api.github.com/users/typetools/following{/other_user}","gists_url":"https://api.github.com/users/typetools/gists{/gist_id}","starred_url":"https://api.github.com/users/typetools/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/typetools/subscriptions","organizations_url":"https://api.github.com/users/typetools/orgs","repos_url":"https://api.github.com/users/typetools/repos","events_url":"https://api.github.com/users/typetools/events{/privacy}","received_events_url":"https://api.github.com/users/typetools/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/typetools/checker-framework","description":"Pluggable type-checking for Java","fork":false,"url":"https://api.github.com/repos/typetools/checker-framework","forks_url":"https://api.github.com/repos/typetools/checker-framework/forks","keys_url":"https://api.github.com/repos/typetools/checker-framework/keys{/key_id}","collaborators_url":"https://api.github.com/repos/typetools/checker-framework/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/typetools/checker-framework/teams","hooks_url":"https://api.github.com/repos/typetools/checker-framework/hooks","issue_events_url":"https://api.github.com/repos/typetools/checker-framework/issues/events{/number}","events_url":"https://api.github.com/repos/typetools/checker-framework/events","assignees_url":"https://api.github.com/repos/typetools/checker-framework/assignees{/user}","branches_url":"https://api.github.com/repos/typetools/checker-framework/branches{/branch}","tags_url":"https://api.github.com/repos/typetools/checker-framework/tags","blobs_url":"https://api.github.com/repos/typetools/checker-framework/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/typetools/checker-framework/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/typetools/checker-framework/git/refs{/sha}","trees_url":"https://api.github.com/repos/typetools/checker-framework/git/trees{/sha}","statuses_url":"https://api.github.com/repos/typetools/checker-framework/statuses/{sha}","languages_url":"https://api.github.com/repos/typetools/checker-framework/languages","stargazers_url":"https://api.github.com/repos/typetools/checker-framework/stargazers","contributors_url":"https://api.github.com/repos/typetools/checker-framework/contributors","subscribers_url":"https://api.github.com/repos/typetools/checker-framework/subscribers","subscription_url":"https://api.github.com/repos/typetools/checker-framework/subscription","commits_url":"https://api.github.com/repos/typetools/checker-framework/commits{/sha}","git_commits_url":"https://api.github.com/repos/typetools/checker-framework/git/commits{/sha}","comments_url":"https://api.github.com/repos/typetools/checker-framework/comments{/number}","issue_comment_url":"https://api.github.com/repos/typetools/checker-framework/issues/comments{/number}","contents_url":"https://api.github.com/repos/typetools/checker-framework/contents/{+path}","compare_url":"https://api.github.com/repos/typetools/checker-framework/compare/{base}...{head}","merges_url":"https://api.github.com/repos/typetools/checker-framework/merges","archive_url":"https://api.github.com/repos/typetools/checker-framework/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/typetools/checker-framework/downloads","issues_url":"https://api.github.com/repos/typetools/checker-framework/issues{/number}","pulls_url":"https://api.github.com/repos/typetools/checker-framework/pulls{/number}","milestones_url":"https://api.github.com/repos/typetools/checker-framework/milestones{/number}","notifications_url":"https://api.github.com/repos/typetools/checker-framework/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/typetools/checker-framework/labels{/name}","releases_url":"https://api.github.com/repos/typetools/checker-framework/releases{/id}","created_at":"2015-07-03T11:09:17Z","updated_at":"2015-11-26T16:43:27Z","pushed_at":"2015-12-02T20:28:51Z","git_url":"git://github.com/typetools/checker-framework.git","ssh_url":"git@github.com:typetools/checker-framework.git","clone_url":"https://github.com/typetools/checker-framework.git","svn_url":"https://github.com/typetools/checker-framework","homepage":"http://checkerframework.org/","size":167740,"stargazers_count":60,"watchers_count":60,"language":"Java","has_issues":true,"has_downloads":false,"has_wiki":false,"has_pages":false,"forks_count":22,"mirror_url":null,"open_issues_count":142,"forks":22,"open_issues":142,"watchers":60,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/typetools/checker-framework/pulls/477"},"html":{"href":"https://github.com/typetools/checker-framework/pull/477"},"issue":{"href":"https://api.github.com/repos/typetools/checker-framework/issues/477"},"comments":{"href":"https://api.github.com/repos/typetools/checker-framework/issues/477/comments"},"review_comments":{"href":"https://api.github.com/repos/typetools/checker-framework/pulls/477/comments"},"review_comment":{"href":"https://api.github.com/repos/typetools/checker-framework/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/typetools/checker-framework/pulls/477/commits"},"statuses":{"href":"https://api.github.com/repos/typetools/checker-framework/statuses/164c1b35b70d39b2c654774bfb1ff5a28d354a08"}}}}