docs: private Description checkParamOverriding(
        List<VarSymbol> overridingParamSymbols,
        Symbol.MethodSymbol overriddenMethod,
        @Nullable LambdaExpressionTree lambdaExpressionTree,
        @Nullable MemberReferenceTree memberReferenceTree,
        VisitorState state) {
      com.sun.tools.javac.util.List<VarSymbol> superParamSymbols = overriddenMethod.getParameters();
      boolean unboundMemberRef =
          (memberReferenceTree != null)
              && ((JCTree.JCMemberReference) memberReferenceTree).kind.isUnbound();
      // if we have an unbound method reference, the first parameter of the overridden method must be
      // @NonNull, as this parameter will be used as a method receiver inside the generated lambda
      if (unboundMemberRef) {
        // there must be at least one parameter; otherwise code wouldn't compile
        if (Nullness.hasNullableAnnotation(superParamSymbols.get(0))) {
          String message =
              "unbound instance method reference cannot be used, as first parameter of "
                  + "functional interface method "
                  + ASTHelpers.enclosingClass(overriddenMethod)
                  + "."
                  + overriddenMethod.toString()
                  + " is @Nullable";
          return errorBuilder.createErrorDescription(
              new ErrorMessage(MessageTypes.WRONG_OVERRIDE_PARAM, message),
              state.getPath(),
              buildDescription(memberReferenceTree));
        }
      }
      // for unbound member references, we need to adjust parameter indices by 1 when matching with
      // overridden method
      int startParam = unboundMemberRef ? 1 : 0;
      // Collect @Nullable params of overriden method
      ImmutableSet<Integer> nullableParamsOfOverriden;
      if (NullabilityUtil.isUnannotated(overriddenMethod, config)) {
        nullableParamsOfOverriden =
            handler.onUnannotatedInvocationGetExplicitlyNullablePositions(
                state.context, overriddenMethod, ImmutableSet.of());
      } else {
        ImmutableSet.Builder<Integer> builder = ImmutableSet.builder();
        for (int i = startParam; i < superParamSymbols.size(); i++) {
          // we need to call paramHasNullableAnnotation here since overriddenMethod may be defined
          // in a class file
          if (Nullness.paramHasNullableAnnotation(overriddenMethod, i)) {
            builder.add(i);
          }
        }
        nullableParamsOfOverriden = builder.build();
      }
      for (int i : nullableParamsOfOverriden) {
        int methodParamInd = i - startParam;
        VarSymbol paramSymbol = overridingParamSymbols.get(methodParamInd);
        // in the case where we have a parameter of a lambda expression, we do
        // *not* force the parameter to be annotated with @Nullable; instead we "inherit"
        // nullability from the corresponding functional interface method.
        // So, we report an error if the @Nullable annotation is missing *and*
        // we don't have a lambda with implicitly typed parameters
        boolean implicitlyTypedLambdaParam =
            lambdaExpressionTree != null
                && NullabilityUtil.lambdaParamIsImplicitlyTyped(
                    lambdaExpressionTree.getParameters().get(methodParamInd));
        if (!Nullness.hasNullableAnnotation(paramSymbol) && !implicitlyTypedLambdaParam) {
          final String message =
              "parameter "
                  + paramSymbol.name.toString()
                  + (memberReferenceTree != null ? " of referenced method" : "")
                  + " is @NonNull, but parameter in "
                  + ((lambdaExpressionTree != null || memberReferenceTree != null)
                      ? "functional interface "
                      : "superclass ")
                  + "method "
                  + ASTHelpers.enclosingClass(overriddenMethod)
                  + "."
                  + overriddenMethod.toString()
                  + " is @Nullable";
          Tree errorTree;
          if (memberReferenceTree != null) {
            errorTree = memberReferenceTree;
          } else {
            errorTree = getTreesInstance(state).getTree(paramSymbol);
          }
          return errorBuilder.createErrorDescription(
              new ErrorMessage(MessageTypes.WRONG_OVERRIDE_PARAM, message),
              state.getPath(),
              buildDescription(errorTree));
        }
      }
      return Description.NO_MATCH;
    }