Skip to content

Commit 764feed

Browse files
authored
Special handling of explicit annotations on anonymous class instantiation (#343)
1 parent 659bf69 commit 764feed

3 files changed

Lines changed: 42 additions & 7 deletions

File tree

src/checkers/inference/InferenceAnnotatedTypeFactory.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,19 @@ public Set<AnnotationMirror> getTypeDeclarationBounds(TypeMirror type) {
576576
return Collections.singleton(vAnno);
577577
}
578578

579+
// This is to handle the special case of anonymous classes when the super class (or interface)
580+
// identifier is explicit annotated, e.g.
581+
// A a1 = new @OsUntrusted A() {};
582+
// In such cases, the declaration bound of the anonymous class is the explicit annotation on
583+
// the super class (or interface) identifier.
584+
final List<? extends AnnotationMirror> annos = type.getAnnotationMirrors();
585+
AnnotationMirror realAnno = qualHierarchy.findAnnotationInHierarchy(annos, realTop);
586+
if (realAnno != null) {
587+
Slot slot = slotManager.getSlot(realAnno);
588+
vAnno = slotManager.getAnnotation(slot);
589+
return Collections.singleton(vAnno);
590+
}
591+
579592
// If the declaration bound of the underlying type is not cached, use default
580593
return (Set<AnnotationMirror>) getDefaultTypeDeclarationBounds();
581594
}
@@ -592,5 +605,6 @@ public Set<AnnotationMirror> getTypeDeclarationBounds(TypeMirror type) {
592605
public AnnotatedTypeMirror getTypeOfExtendsImplements(Tree clause) {
593606
return getAnnotatedTypeFromTypeTree(clause);
594607
}
608+
595609
}
596610

src/checkers/inference/InferenceTreeAnnotator.java

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import checkers.inference.model.ConstraintManager;
44
import checkers.inference.model.SourceVariableSlot;
55
import checkers.inference.model.Slot;
6+
67
import org.checkerframework.framework.type.AnnotatedTypeFactory;
78
import org.checkerframework.framework.type.AnnotatedTypeFactory.ParameterizedExecutableType;
89
import org.checkerframework.framework.type.AnnotatedTypeMirror;
@@ -23,6 +24,7 @@
2324
import javax.lang.model.type.TypeKind;
2425

2526
import com.sun.source.tree.AnnotatedTypeTree;
27+
import com.sun.source.tree.AnnotationTree;
2628
import com.sun.source.tree.AssignmentTree;
2729
import com.sun.source.tree.BinaryTree;
2830
import com.sun.source.tree.ClassTree;
@@ -182,16 +184,28 @@ public Void visitIdentifier(IdentifierTree node, AnnotatedTypeMirror identifierT
182184

183185
} else if (parentNode.getKind() == Kind.NEW_CLASS
184186
&& ((NewClassTree) parentNode).getIdentifier() == node) {
185-
// This can happen at two locations in a NewClassTree:
186-
// (1) The type identifier of the NewClassTree, as `A` of `new A() {}`,
187-
// (2) The type identifier on the anonymous class's extends/implements clause.
188-
// Note that the identifiers trees described in the two cases above are identical
189-
// for one NewClassTree, i.e. they share the same slot
190-
// TODO: A NewClassTree should be handled in visitNewClass method exclusively
187+
// This can happen in two cases related to NewClassTrees:
188+
// (1) The type identifier of non-anonymous class instantiations, without explict
189+
// annotations, such as `A` of `new A()`;
190+
// (2) The type identifier of anonymous class instantiations, with or without
191+
// explicit annotations.
192+
// TODO: A NewClassTree should be handled in visitNewClass method exclusively
191193
// without messing around with the IdentifierTree or ClassTree, see issue:
192194
// https://github.com/opprop/checker-framework-inference/issues/332
193-
variableAnnotator.visit(identifierType, node);
194195

196+
NewClassTree newClassTree = (NewClassTree) parentNode;
197+
if (newClassTree.getClassBody() != null) {
198+
// For case 2, get the explicit annotation if any exists so that no variable slot
199+
// is created. Note the annotation cannot be retrieved from the identifier, but
200+
// from the modifier of the anonymous class body. e.g. for the following case
201+
// new @HERE Class() {}
202+
// @HERE is on the modifier of the anonymous class body, instead of on the type identifier.
203+
List<? extends AnnotationTree> annos =
204+
newClassTree.getClassBody().getModifiers().getAnnotations();
205+
identifierType.addAnnotations(TreeUtils.annotationsFromTypeAnnotationTrees(annos));
206+
((InferenceAnnotatedTypeFactory) atypeFactory).getConstantToVariableAnnotator().visit(identifierType);
207+
}
208+
variableAnnotator.visit(identifierType, node);
195209
}
196210
}
197211
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import ostrusted.qual.OsUntrusted;
2+
3+
class Anonymous {
4+
A a1 = new @OsUntrusted A() {};
5+
6+
class A {}
7+
}

0 commit comments

Comments
 (0)