in nullaway/src/main/java/com/uber/nullaway/NullAway.java [1793:1858]
public Description matchClass(ClassTree tree, VisitorState state) {
// Ensure codeAnnotationInfo is initialized here since it requires access to the Context,
// which is not available in the constructor
if (codeAnnotationInfo == null) {
codeAnnotationInfo = CodeAnnotationInfo.instance(state.context);
}
if (!checkedJDKVersionForJSpecifyMode) {
checkedJDKVersionForJSpecifyMode = true;
if (config.isJSpecifyMode()
&& !JSpecifyJavacConfig.isValidJavacConfigForJSpecifyMode(state)) {
String msg =
"Running NullAway in JSpecify mode requires either JDK 22+"
+ " or passing the flag -XDaddTypeAnnotationsToSymbol=true to an older JDK that supports it;"
+ " see https://github.com/uber/NullAway/wiki/JSpecify-Support#supported-jdk-versions for details.";
throw new IllegalStateException(msg);
}
}
// Check if the class is excluded according to the filter
// if so, set the flag to match within the class to false
// NOTE: for this mechanism to work, we rely on the enclosing ClassTree
// always being visited before code within that class. We also
// assume that a single checker object is not being
// used from multiple threads
// We don't want to update the flag for nested classes.
// Ideally we would keep a stack of flags to handle nested types,
// but this is not easy within the Error Prone APIs.
// Instead, we use this flag as an optimization, skipping work if the
// top-level class is to be skipped. If a nested class should be
// skipped, we instead rely on last-minute suppression of the
// error message, using the mechanism in
// ErrorBuilder.hasPathSuppression(...)
Symbol.ClassSymbol classSymbol = ASTHelpers.getSymbol(tree);
NestingKind nestingKind = classSymbol.getNestingKind();
if (!nestingKind.isNested()) {
// Here we optimistically set the marking to either FULLY_UNMARKED or FULLY_MARKED. If a
// nested entity has a contradicting annotation, at that point we update the marking level to
// PARTIALLY_MARKED, which will increase checking overhead for the remainder of the top-level
// class
nullMarkingForTopLevelClass =
isExcludedClass(classSymbol) ? NullMarking.FULLY_UNMARKED : NullMarking.FULLY_MARKED;
// since we are processing a new top-level class, invalidate any cached
// results for previous classes
handler.onMatchTopLevelClass(this, tree, state, classSymbol);
getNullnessAnalysis(state).invalidateCaches();
initTree2PrevFieldInit.clear();
class2Entities.clear();
class2ConstructorUninit.clear();
computedNullnessMap.clear();
genericsChecks.clearCache();
EnclosingEnvironmentNullness.instance(state.context).clear();
} else if (classAnnotationIntroducesPartialMarking(classSymbol)) {
// Handle the case where the top-class is unannotated, but there is a @NullMarked annotation
// on a nested class, or, conversely the top-level is annotated but there is a @NullUnmarked
// annotation on a nested class.
nullMarkingForTopLevelClass = NullMarking.PARTIALLY_MARKED;
}
if (withinAnnotatedCode(state)) {
// we need to update the environment before checking field initialization, as the latter
// may run dataflow analysis
if (nestingKind.equals(NestingKind.LOCAL) || nestingKind.equals(NestingKind.ANONYMOUS)) {
updateEnvironmentMapping(state.getPath(), state);
}
checkFieldInitialization(tree, state);
}
return Description.NO_MATCH;
}