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 3 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,26 +368,49 @@ 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(); | ||
this.relevantJavaTypes = new HashSet<>(CollectionsPlume.mapCapacity(classes.length)); | ||
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 " | ||
+ this.getClass().getSimpleName()); | ||
} else { | ||
TypeMirror relevantType = TypesUtils.typeFromClass(clazz, types, elements); | ||
relevantJavaTypes.add(types.erasure(relevantType)); | ||
TypeMirror erased = types.erasure(relevantType); | ||
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.
I think this will also complicate the specification of 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. There are 71 instances of 58 have 0 classes in a So, I don't think this is a heavy cost, either in time to compute the strings nor in space to store the strings. |
||
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(); | ||
|
@@ -2393,8 +2426,9 @@ private static void log(String format, Object... args) { | |
* Returns true if users can write type annotations from this type system directly on the given | ||
* Java type. | ||
* | ||
* <p>May return false for a compound type (for which it is possible to write type qualifiers on | ||
* elements of the type). | ||
* <p>For a compound type, returns true only if a programmer may 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. | ||
* | ||
|
@@ -2403,12 +2437,15 @@ private static void log(String format, Object... args) { | |
* Java type | ||
*/ | ||
public final boolean isRelevant(TypeMirror tm) { | ||
if (relevantJavaTypes == null) { | ||
return true; | ||
} | ||
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); | ||
|
@@ -2419,8 +2456,9 @@ public final boolean isRelevant(TypeMirror tm) { | |
* Returns true if users can write type annotations from this type system directly on the given | ||
* Java type. | ||
* | ||
* <p>May return false for a compound type (for which it is possible to write type qualifiers on | ||
* elements of the type). | ||
* <p>For a compound type, returns true only if it a programmer may 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. | ||
* | ||
|
@@ -2439,12 +2477,19 @@ 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 | ||
*/ | ||
protected boolean isRelevantImpl(TypeMirror tm) { | ||
|
||
if (relevantJavaTypes == null || relevantJavaTypes.contains(tm)) { | ||
if (relevantJavaTypes == null) { | ||
return true; | ||
} | ||
|
||
if (relevantJavaTypes.contains(tm)) { | ||
return true; | ||
} | ||
|
||
|
@@ -2524,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.
Why were these annotations removed?
If
DependentTypesViewpointAdapationTest
isn't being considered a relevant Java type for@SameLen
at this location, I think that's a problem with either@SameLen
's definition of its relevant types or with the implementation in this PR, not with this test.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.
SameLenChecker.java
contains:Is that incorrect? Why is
@SameLen
applicable to an arbitrary class?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.
The
@RelevantJavaTypes
annotation is probably incorrect. The pattern that this test is testing is documented in the manual here: https://checkerframework.org/manual/#index-annotating-fixed-size