diff --git a/src/checkers/inference/InferenceAnnotatedTypeFactory.java b/src/checkers/inference/InferenceAnnotatedTypeFactory.java index 015664c61..254491027 100644 --- a/src/checkers/inference/InferenceAnnotatedTypeFactory.java +++ b/src/checkers/inference/InferenceAnnotatedTypeFactory.java @@ -579,5 +579,18 @@ public Set getTypeDeclarationBounds(TypeMirror type) { // If the declaration bound of the underlying type is not cached, use default return (Set) getDefaultTypeDeclarationBounds(); } + + /** + * Unlike the cases in type checking, in inference we should: + * (1) if the clause tree contains explicit annotation, return the corresponding @VarAnnot + * (2) otherwise, return the primary variable created for the clause + * + * @param clause the tree that represents an extends or implements clause + * @return the annotated type of the clause tree + */ + @Override + public AnnotatedTypeMirror getTypeOfExtendsImplements(Tree clause) { + return getAnnotatedTypeFromTypeTree(clause); + } } diff --git a/src/checkers/inference/InferenceTreeAnnotator.java b/src/checkers/inference/InferenceTreeAnnotator.java index a52bd3934..40168f999 100644 --- a/src/checkers/inference/InferenceTreeAnnotator.java +++ b/src/checkers/inference/InferenceTreeAnnotator.java @@ -75,7 +75,7 @@ public InferenceTreeAnnotator(final InferenceAnnotatedTypeFactory atypeFactory, @Override public Void visitAnnotatedType(AnnotatedTypeTree node, AnnotatedTypeMirror atm) { - visit(node.getUnderlyingType(), atm); + variableAnnotator.visit(atm, node); return null; } @@ -155,6 +155,11 @@ public Void visitIdentifier(IdentifierTree node, AnnotatedTypeMirror identifierT } } + } else if (parentNode.getKind() == Kind.CLASS) { + // This happens when a class explicitly extends another class or implements + // another interface + variableAnnotator.visit(identifierType, node); + } } } diff --git a/src/checkers/inference/VariableAnnotator.java b/src/checkers/inference/VariableAnnotator.java index 506a4a59d..3de941444 100644 --- a/src/checkers/inference/VariableAnnotator.java +++ b/src/checkers/inference/VariableAnnotator.java @@ -513,6 +513,7 @@ private Slot addPrimaryVariable(AnnotatedTypeMirror atm, final Tree tree) { treeToVarAnnoPair.put(tree, varATMPair); } + atm.removeAnnotationInHierarchy(realTop); atm.replaceAnnotation(slotManager.getAnnotation(variable)); return variable; @@ -570,6 +571,11 @@ private Slot replaceOrCreateEquivalentVarAnno(AnnotatedTypeMirror atm, Tree tree varSlot = createVariable(location, atm.getUnderlyingType()); } + if (realQualifier != null) { + // Remove the real qualifier in the atm, to make sure the source code + // is only annotated with @VarAnnot + atm.removeAnnotation(realQualifier); + } atm.replaceAnnotation(slotManager.getAnnotation(varSlot)); return varSlot; } @@ -773,8 +779,16 @@ private void handleClassDeclaration(AnnotatedDeclaredType classType, ClassTree c superTypes.get(0).replaceAnnotation(slotManager.getAnnotation(extendsSlot)); } else { - final AnnotatedTypeMirror extendsType = inferenceTypeFactory.getAnnotatedTypeFromTypeTree(extendsTree); - visit(extendsType, extendsTree); + // Since only extends trees with a non-null tree path are handled (see + // checkers.inference.InferenceTreeAnnotator#visitIdentifier for more details), + // here don't dig deeper onto the extends tree when the classTree path is null. + // Note: the classTree path is null when the variableAnnotater is visiting it from + // a different compilation unit. The extends tree should be annotated when the + // compiler moves forward to the compilation unit containing the class definition. + if (inferenceTypeFactory.getPath(classTree) != null) { + final AnnotatedTypeMirror extendsType = inferenceTypeFactory.getAnnotatedTypeFromTypeTree(extendsTree); + visit(extendsType, extendsTree); + } } // // TODO: NOT SURE THIS HANDLES MEMBER SELECT CORRECTLY @@ -1617,16 +1631,16 @@ protected Boolean scan(AnnotatedTypeMirror type, Void aVoid) { * If it does not already exist, this method creates the annotation and stores it in classDeclAnnos. */ private Slot getOrCreateDeclBound(AnnotatedDeclaredType type) { - - TypeElement classDecl = (TypeElement) type.getUnderlyingType().asElement(); + TypeElement classElt = (TypeElement) type.getUnderlyingType().asElement(); Slot topConstant = getTopConstant(); - Slot declSlot = classDeclAnnos.get(classDecl); + Slot declSlot = classDeclAnnos.get(classElt); if (declSlot == null) { - Tree decl = inferenceTypeFactory.declarationFromElement(classDecl); - if (decl != null) { - declSlot = createVariable(decl); - classDeclAnnos.put(classDecl, (SourceVariableSlot) declSlot); + Tree classTree = inferenceTypeFactory.declarationFromElement(classElt); + if (classTree != null) { + final AnnotatedDeclaredType declType = inferenceTypeFactory.fromElement(classElt); + declSlot = replaceOrCreateEquivalentVarAnno(declType, classTree, treeToLocation(classTree)); + classDeclAnnos.put(classElt, (SourceVariableSlot) declSlot); } else { declSlot = topConstant; diff --git a/src/ostrusted/jdk.astub b/src/ostrusted/jdk.astub index 266019462..4cb453a92 100644 --- a/src/ostrusted/jdk.astub +++ b/src/ostrusted/jdk.astub @@ -29,29 +29,6 @@ class System { public static void loadLibrary(@OsTrusted String libname); } - - - - - - - - - - - - - - - - - - - - - - - - - - +public class Object { + public @OsTrusted Object() {} +} diff --git a/testdata/ostrusted-inferrable-test/Bounds.java b/testdata/ostrusted-inferrable-test/Bounds.java new file mode 100644 index 000000000..839d79d87 --- /dev/null +++ b/testdata/ostrusted-inferrable-test/Bounds.java @@ -0,0 +1,9 @@ +// Test case for issue 326: +// https://github.com/opprop/checker-framework-inference/issues/326 + +import ostrusted.qual.OsTrusted; + +@SuppressWarnings("inconsistent.constructor.type") +// :: fixable-error: (declaration.inconsistent.with.extends.clause) +class A extends @OsTrusted Object {} +