-
Notifications
You must be signed in to change notification settings - Fork 355
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Don't write irrelevant annotations in .ajava files #6254
base: master
Are you sure you want to change the base?
Changes from all commits
59d2b4b
5e51863
6b54b61
b9a34bc
b947f16
f1b7064
9f4523b
8197f24
62e0f64
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -187,15 +187,25 @@ public abstract class GenericAnnotatedTypeFactory< | |
* <p>Although a {@code Class<?>} object exists for every element, this does not contain those | ||
* {@code Class<?>} objects because the elements will be compared to TypeMirrors for which Class | ||
* objects may not exist (they might not be on the classpath). | ||
* | ||
* <p>For their names, see {@link #relevantJavaTypeNames}. | ||
*/ | ||
public final @Nullable Set<TypeMirror> relevantJavaTypes; | ||
|
||
/** | ||
* Whether users may write type annotations on arrays. Ignored unless {@link #relevantJavaTypes} | ||
* is non-null. | ||
* The fully-qualified names <b>and</b> simple names of the types in {@link #relevantJavaTypes}. | ||
*/ | ||
public final @Nullable Set<String> relevantJavaTypeNames; | ||
|
||
/** Whether users may write type annotations on arrays. */ | ||
protected final boolean arraysAreRelevant; | ||
|
||
/** | ||
* Whether users may write type annotations on non-primitives (classes, arrays, etc.). This is | ||
* redundant with the value of {@link #relevantJavaTypes} but is included for efficiency. | ||
*/ | ||
protected final boolean nonprimitivesAreRelevant; | ||
|
||
// Flow related fields | ||
|
||
/** Should flow be used by default? */ | ||
|
@@ -358,17 +368,23 @@ protected GenericAnnotatedTypeFactory(BaseTypeChecker checker, boolean useFlow) | |
checker.getClass().getAnnotation(RelevantJavaTypes.class); | ||
if (relevantJavaTypesAnno == null) { | ||
this.relevantJavaTypes = null; | ||
this.relevantJavaTypeNames = null; | ||
this.arraysAreRelevant = true; | ||
this.nonprimitivesAreRelevant = true; | ||
} else { | ||
Types types = getChecker().getTypeUtils(); | ||
Elements elements = getElementUtils(); | ||
Class<?>[] classes = relevantJavaTypesAnno.value(); | ||
Set<TypeMirror> relevantJavaTypesTemp = | ||
new HashSet<>(CollectionsPlume.mapCapacity(classes.length)); | ||
Set<String> relevantJavaTypeNamesTemp = | ||
new HashSet<>(CollectionsPlume.mapCapacity(classes.length)); | ||
boolean arraysAreRelevantTemp = false; | ||
boolean nonprimitivesAreRelevantTemp = false; | ||
for (Class<?> clazz : classes) { | ||
if (clazz == Object[].class) { | ||
arraysAreRelevantTemp = true; | ||
nonprimitivesAreRelevantTemp = true; | ||
} else if (clazz.isArray()) { | ||
throw new TypeSystemError( | ||
"Don't use arrays other than Object[] in @RelevantJavaTypes on " | ||
|
@@ -377,10 +393,24 @@ protected GenericAnnotatedTypeFactory(BaseTypeChecker checker, boolean useFlow) | |
TypeMirror relevantType = TypesUtils.typeFromClass(clazz, types, elements); | ||
TypeMirror erased = types.erasure(relevantType); | ||
relevantJavaTypesTemp.add(erased); | ||
String typeString = erased.toString(); | ||
relevantJavaTypeNamesTemp.add(typeString); | ||
if (clazz.isPrimitive()) { | ||
nonprimitivesAreRelevantTemp = true; | ||
} else { | ||
int dotIndex = typeString.lastIndexOf('.'); | ||
if (dotIndex != -1) { | ||
// It's a fully-qualified name. Add the simple name as well. | ||
// TODO: This might not handle a user writing a nested class like "Map.Entry". | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Given the TODO comments here and about handling type variables (at Probably the easiest way to test that would be to add another annotation to the |
||
relevantJavaTypeNamesTemp.add(typeString.substring(dotIndex + 1)); | ||
} | ||
} | ||
} | ||
} | ||
this.relevantJavaTypes = Collections.unmodifiableSet(relevantJavaTypesTemp); | ||
this.relevantJavaTypeNames = Collections.unmodifiableSet(relevantJavaTypeNamesTemp); | ||
this.arraysAreRelevant = arraysAreRelevantTemp; | ||
this.nonprimitivesAreRelevant = nonprimitivesAreRelevantTemp; | ||
} | ||
|
||
contractsUtils = createContractsFromMethod(); | ||
|
@@ -2413,9 +2443,9 @@ public final boolean isRelevant(TypeMirror tm) { | |
if (tm.getKind() != TypeKind.PACKAGE && tm.getKind() != TypeKind.MODULE) { | ||
tm = types.erasure(tm); | ||
} | ||
Boolean cachedResult = isRelevantCache.get(tm); | ||
if (cachedResult != null) { | ||
return cachedResult; | ||
Boolean resultBoxed = isRelevantCache.get(tm); | ||
if (resultBoxed != null) { | ||
return resultBoxed; | ||
} | ||
boolean result = isRelevantImpl(tm); | ||
isRelevantCache.put(tm, result); | ||
|
@@ -2447,6 +2477,9 @@ public final boolean isRelevant(AnnotatedTypeMirror tm) { | |
* <p>Clients should never call this. Call {@link #isRelevant} instead. This is a helper method | ||
* for {@link #isRelevant}. | ||
* | ||
* <p>This should <b>not</b> be called if {@code relevantJavaTypes == null || | ||
* relevantJavaTypes.contains(tm))}. | ||
* | ||
* @param tm a type | ||
* @return true if users can write type annotations from this type system on the given Java type | ||
*/ | ||
|
@@ -2536,6 +2569,43 @@ protected boolean isRelevantImpl(TypeMirror tm) { | |
} | ||
} | ||
|
||
/** | ||
* Returns true if users can write type annotations from this type system directly on the given | ||
* Java type. | ||
* | ||
* <p>For a compound type, returns true only if it is permitted to write a type qualifier on the | ||
* top level of the compound type. That is, this method may return false, when it is possible to | ||
* write type qualifiers on elements of the type. | ||
* | ||
* <p>Subclasses should override {@code #isRelevantImpl} instead of this method. | ||
* | ||
* @param type a fully-qualified or simple type; should not be an array (use {@link | ||
* #arraysAreRelevant} instead) | ||
* @return true if users can write type annotations from this type system directly on the given | ||
* Java type | ||
*/ | ||
public final boolean isRelevant(String type) { | ||
return relevantJavaTypeNames == null || relevantJavaTypeNames.contains(type); | ||
} | ||
|
||
/** | ||
* Returns true if users can write type annotations from this type system on array types. | ||
* | ||
* @return true if users can write type annotations from this type system on array types | ||
*/ | ||
public final boolean arraysAreRelevant() { | ||
return arraysAreRelevant; | ||
} | ||
|
||
/** | ||
* Returns true if users can write type annotations from this type system on non-primitive types. | ||
* | ||
* @return true if users can write type annotations from this type system on non-primitive types | ||
*/ | ||
public final boolean nonprimitivesAreRelevant() { | ||
return nonprimitivesAreRelevant; | ||
} | ||
|
||
/** The cached message about relevant types. */ | ||
private @MonotonicNonNull String irrelevantExtraMessage = null; | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
relevantJavaTypesNames
, at least, is only used by WPI (i.e., in this PR). That means that most (all?) of this computation is wasted for non-WPI runs of the checker, which makes me a bit uncomfortable: it does not seem reasonable to slow down the checker in the non-WPI case to speed it up in the WPI case. Can we guard some of this work behind a check that WPI is actually running?I think this will also complicate the specification of
relevantJavaTypeNames
, which will need to say something like "always null if WPI is not enabled"There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are 71 instances of
extends BaseTypeChecker
in the Checker Framework (excluding checkers used only for testing). Of those:58 have 0 classes in a
@RelevantJavaTypes
annotation5 have 1 class in a
@RelevantJavaTypes
annotation2 have 3 classes in a
@RelevantJavaTypes
annotation1 has 6 classes in a
@RelevantJavaTypes
annotation1 has 8 classes in a
@RelevantJavaTypes
annotation4 have 10 classes in a
@RelevantJavaTypes
annotationSo, I don't think this is a heavy cost, either in time to compute the strings nor in space to store the strings.
But if @smillst agrees with the efficiency concern, I will implement it.