Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@
*/
public enum TypeUseLocation {

/**
* Apply default annotations to unannotated top-level types of classes, interfaces, enums, and
* records.
*/
TYPE,

/** Apply default annotations to unannotated top-level types of fields. */
FIELD,

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -811,6 +811,25 @@ protected QualifierDefaults createQualifierDefaults() {
return new QualifierDefaults(elements, this);
}

@Override
protected QualifierUpperBounds createQualifierUpperBounds() {
return new QualifierUpperBounds(this) {
@Override
protected AnnotationMirrorSet getAnnotationFromElement(Element element) {
AnnotationMirrorSet result =
new AnnotationMirrorSet(super.getAnnotationFromElement(element));
for (AnnotationMirror anno :
defaults.getDefaultAnnotations(element, TypeUseLocation.TYPE)) {
if (getQualifierHierarchy().findAnnotationInSameHierarchy(result, anno)
== null) {
result.add(anno);
}
}
return result;
}
};
}

/**
* Creates and returns a string containing the number of qualifiers and the canonical class
* names of each qualifier that has been added to this checker's supported qualifier set. The
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -876,6 +876,45 @@ private void applyDefaultsElement(
}
}

/**
* Returns the default annotations that apply to {@code annotationScope} at {@code location}.
*
* @param annotationScope the element representing the nearest enclosing default annotation
* scope
* @param location the location whose defaults to return
* @return the default annotations that apply at {@code location}
*/
public AnnotationMirrorSet getDefaultAnnotations(
Element annotationScope, TypeUseLocation location) {
AnnotationMirrorSet result = new AnnotationMirrorSet();
addDefaultAnnotationsAt(result, defaultsAt(annotationScope), location);

if (applyConservativeDefaults(annotationScope)) {
addDefaultAnnotationsAt(result, uncheckedCodeDefaults, location);
}

addDefaultAnnotationsAt(result, checkedCodeDefaults, location);
return result;
}

/**
* Adds defaults from {@code defaults} that apply at {@code location} to {@code result}.
*
* @param result the annotations accumulated so far
* @param defaults the defaults to inspect
* @param location the location whose defaults should be added
*/
private void addDefaultAnnotationsAt(
AnnotationMirrorSet result, DefaultSet defaults, TypeUseLocation location) {
QualifierHierarchy qualHierarchy = atypeFactory.getQualifierHierarchy();
for (Default def : defaults) {
if (def.location == location
&& qualHierarchy.findAnnotationInSameHierarchy(result, def.anno) == null) {
result.add(def.anno);
}
}
}

/**
* Create the default applier element.
*
Expand Down Expand Up @@ -1080,6 +1119,11 @@ public Void scan(@FindDistinct AnnotatedTypeMirror t, AnnotationMirror qual) {
// Some defaults only apply to the top level type.
boolean isTopLevelType = t == outer.type;
switch (outer.location) {
case TYPE:
if (outer.scope != null && outer.scope.getKind().isClass() && isTopLevelType) {
Comment thread
aosen-xiong marked this conversation as resolved.
outer.addAnnotation(t, qual);
}
break;
case FIELD:
if (outer.scope != null
&& outer.scope.getKind() == ElementKind.FIELD
Expand Down
60 changes: 60 additions & 0 deletions framework/tests/viewpointtest/DefaultQualifierTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import org.checkerframework.framework.qual.DefaultQualifier;
import org.checkerframework.framework.qual.TypeUseLocation;

import viewpointtest.quals.*;

public class DefaultQualifierTest {
@DefaultQualifier(
value = A.class,
locations = {TypeUseLocation.TYPE})
class DefaultAClass {
// :: error: (type.invalid.annotations.on.use)
// :: warning: (inconsistent.constructor.type)
// :: error: (super.invocation.invalid)
@B DefaultAClass() {}

void typeUses() {
@A DefaultAClass defaultAsA;
@B DefaultAClass defaultAsB;
}
}

@A class ExplicitAClass {
// :: error: (type.invalid.annotations.on.use)
// :: warning: (inconsistent.constructor.type)
// :: error: (super.invocation.invalid)
@B ExplicitAClass() {}

void typeUses() {
@A ExplicitAClass explicitAsA;
@B ExplicitAClass explicitAsB;
}
}

@DefaultQualifier(
value = B.class,
locations = {TypeUseLocation.TYPE})
class DefaultBClass {
// :: error: (type.invalid.annotations.on.use)
// :: warning: (inconsistent.constructor.type)
// :: error: (super.invocation.invalid)
@A DefaultBClass() {}

void typeUses() {
@B DefaultBClass defaultAsB;
@A DefaultBClass defaultAsA;
}
}

@B class ExplicitBClass {
// :: error: (type.invalid.annotations.on.use)
// :: warning: (inconsistent.constructor.type)
// :: error: (super.invocation.invalid)
@A ExplicitBClass() {}

void typeUses() {
@B ExplicitBClass explicitAsB;
@A ExplicitBClass explicitAsA;
}
}
}
Loading