public Description matchClass()

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;
  }