private static void addDynamicAnnotation(
      HighlightInfo info, GrReferenceExpression referenceExpression, HighlightDisplayKey key) {
    final PsiFile containingFile = referenceExpression.getContainingFile();
    if (containingFile != null) {
      VirtualFile file = containingFile.getVirtualFile();
      if (file == null) return;
    } else {
      return;
    }

    if (isCall(referenceExpression)) {
      PsiType[] argumentTypes = PsiUtil.getArgumentTypes(referenceExpression, false);
      if (argumentTypes != null) {
        QuickFixAction.registerQuickFixAction(
            info,
            referenceExpression.getTextRange(),
            new DynamicMethodFix(referenceExpression, argumentTypes),
            key);
      }
    } else {
      QuickFixAction.registerQuickFixAction(
          info,
          referenceExpression.getTextRange(),
          new DynamicPropertyFix(referenceExpression),
          key);
    }
  }
  private static void registerReferenceFixes(
      GrReferenceExpression refExpr,
      HighlightInfo info,
      boolean compileStatic,
      final HighlightDisplayKey key) {
    PsiClass targetClass = QuickfixUtil.findTargetClass(refExpr, compileStatic);
    if (targetClass == null) return;

    if (!compileStatic) {
      addDynamicAnnotation(info, refExpr, key);
    }
    if (targetClass.isWritable()) {
      QuickFixAction.registerQuickFixAction(
          info, new CreateFieldFromUsageFix(refExpr, targetClass), key);

      if (refExpr.getParent() instanceof GrCall && refExpr.getParent() instanceof GrExpression) {
        QuickFixAction.registerQuickFixAction(
            info, new CreateMethodFromUsageFix(refExpr, targetClass), key);
      }
    }

    if (!refExpr.isQualified()) {
      GrVariableDeclarationOwner owner =
          PsiTreeUtil.getParentOfType(refExpr, GrVariableDeclarationOwner.class);
      if (!(owner instanceof GroovyFileBase) || ((GroovyFileBase) owner).isScript()) {
        QuickFixAction.registerQuickFixAction(
            info, new CreateLocalVariableFromUsageFix(refExpr, owner), key);
      }
      if (PsiTreeUtil.getParentOfType(refExpr, GrMethod.class) != null) {
        QuickFixAction.registerQuickFixAction(info, new CreateParameterFromUsageFix(refExpr), key);
      }
    }
  }
  @Nullable
  static HighlightInfo checkFinalFieldInitialized(@NotNull PsiField field) {
    if (!field.hasModifierProperty(PsiModifier.FINAL)) return null;
    if (isFieldInitializedAfterObjectConstruction(field)) return null;

    String description = JavaErrorMessages.message("variable.not.initialized", field.getName());
    TextRange range = HighlightNamesUtil.getFieldDeclarationTextRange(field);
    HighlightInfo highlightInfo =
        HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
            .range(range)
            .descriptionAndTooltip(description)
            .create();
    QuickFixAction.registerQuickFixAction(
        highlightInfo,
        HighlightMethodUtil.getFixRange(field),
        QUICK_FIX_FACTORY.createCreateConstructorParameterFromFieldFix(field));
    QuickFixAction.registerQuickFixAction(
        highlightInfo,
        HighlightMethodUtil.getFixRange(field),
        QUICK_FIX_FACTORY.createInitializeFinalFieldInConstructorFix(field));
    final PsiClass containingClass = field.getContainingClass();
    if (containingClass != null && !containingClass.isInterface()) {
      QuickFixAction.registerQuickFixAction(
          highlightInfo,
          QUICK_FIX_FACTORY.createModifierListFix(field, PsiModifier.FINAL, false, false));
    }
    QuickFixAction.registerQuickFixAction(
        highlightInfo, QUICK_FIX_FACTORY.createAddVariableInitializerFix(field));
    return highlightInfo;
  }
  @Nullable
  public static HighlightInfo checkMissingReturnStatement(PsiCodeBlock body, PsiType returnType) {
    if (body == null
        || returnType == null
        || PsiType.VOID.equals(returnType.getDeepComponentType())) {
      return null;
    }

    // do not compute constant expressions for if() statement condition
    // see JLS 14.20 Unreachable Statements
    try {
      ControlFlow controlFlow = getControlFlowNoConstantEvaluate(body);
      if (!ControlFlowUtil.returnPresent(controlFlow)) {
        PsiJavaToken rBrace = body.getRBrace();
        PsiElement context = rBrace == null ? body.getLastChild() : rBrace;
        String message = JavaErrorMessages.message("missing.return.statement");
        HighlightInfo info =
            HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
                .range(context)
                .descriptionAndTooltip(message)
                .create();
        PsiElement parent = body.getParent();
        if (parent instanceof PsiMethod) {
          PsiMethod method = (PsiMethod) parent;
          QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createAddReturnFix(method));
          QuickFixAction.registerQuickFixAction(
              info, QUICK_FIX_FACTORY.createMethodReturnFix(method, PsiType.VOID, true));
        }
        return info;
      }
    } catch (AnalysisCanceledException ignored) {
    }

    return null;
  }
  @Nullable
  static HighlightInfo checkFileDuplicates(@NotNull PsiJavaModule element, @NotNull PsiFile file) {
    Module module = ModuleUtilCore.findModuleForPsiElement(element);
    if (module != null) {
      Project project = file.getProject();
      Collection<VirtualFile> others =
          FilenameIndex.getVirtualFilesByName(project, MODULE_INFO_FILE, new ModulesScope(module));
      if (others.size() > 1) {
        String message = JavaErrorMessages.message("module.file.duplicate");
        HighlightInfo info =
            HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
                .range(range(element))
                .description(message)
                .create();
        others
            .stream()
            .map(f -> PsiManager.getInstance(project).findFile(f))
            .filter(f -> f != file)
            .findFirst()
            .ifPresent(
                duplicate ->
                    QuickFixAction.registerQuickFixAction(
                        info,
                        new GoToSymbolFix(
                            duplicate, JavaErrorMessages.message("module.open.duplicate.text"))));
        return info;
      }
    }

    return null;
  }
  @Nullable
  private HighlightInfo reportAttributeProblem(
      final XmlTag tag,
      final String localName,
      final XmlAttribute attribute,
      final String localizedMessage) {

    final RemoveAttributeIntentionFix removeAttributeIntention =
        new RemoveAttributeIntentionFix(localName, attribute);

    if (!(tag instanceof HtmlTag)) {
      final HighlightInfoType tagProblemInfoType = HighlightInfoType.WRONG_REF;

      final ASTNode node = SourceTreeToPsiMap.psiElementToTree(attribute);
      assert node != null;
      final ASTNode child = XmlChildRole.ATTRIBUTE_NAME_FINDER.findChild(node);
      assert child != null;
      final HighlightInfo highlightInfo =
          HighlightInfo.createHighlightInfo(tagProblemInfoType, child, localizedMessage);
      addToResults(highlightInfo);

      QuickFixAction.registerQuickFixAction(highlightInfo, removeAttributeIntention);

      return highlightInfo;
    }

    return null;
  }
  private void checkDuplicateAttribute(XmlTag tag, final XmlAttribute attribute) {
    if (skipValidation(tag)) {
      return;
    }

    final XmlAttribute[] attributes = tag.getAttributes();
    final PsiFile containingFile = tag.getContainingFile();
    final XmlExtension extension =
        containingFile instanceof XmlFile
            ? XmlExtension.getExtension(containingFile)
            : XmlExtension.DEFAULT_EXTENSION;
    for (XmlAttribute tagAttribute : attributes) {
      ProgressManager.checkCanceled();
      if (attribute != tagAttribute
          && Comparing.strEqual(attribute.getName(), tagAttribute.getName())) {
        final String localName = attribute.getLocalName();

        if (extension.canBeDuplicated(tagAttribute))
          continue; // multiple import attributes are allowed in jsp directive

        HighlightInfo highlightInfo =
            HighlightInfo.createHighlightInfo(
                getTagProblemInfoType(tag),
                XmlChildRole.ATTRIBUTE_NAME_FINDER.findChild(
                    SourceTreeToPsiMap.psiElementToTree(attribute)),
                XmlErrorMessages.message("duplicate.attribute", localName));
        addToResults(highlightInfo);

        IntentionAction intentionAction = new RemoveAttributeIntentionFix(localName, attribute);

        QuickFixAction.registerQuickFixAction(highlightInfo, intentionAction);
      }
    }
  }
  @NotNull
  static List<HighlightInfo> checkDuplicateRequires(@NotNull PsiJavaModule module) {
    List<HighlightInfo> results = ContainerUtil.newSmartList();

    Map<String, PsiElement> map = ContainerUtil.newHashMap();
    for (PsiElement child = module.getFirstChild(); child != null; child = child.getNextSibling()) {
      if (child instanceof PsiRequiresStatement) {
        PsiJavaModuleReferenceElement ref = ((PsiRequiresStatement) child).getReferenceElement();
        if (ref != null) {
          String text = ref.getReferenceText();
          if (!map.containsKey(text)) {
            map.put(text, child);
          } else {
            String message = JavaErrorMessages.message("module.duplicate.requires", text);
            HighlightInfo info =
                HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
                    .range(child)
                    .description(message)
                    .create();
            QuickFixAction.registerQuickFixAction(info, new DeleteElementFix(child));
            results.add(info);
          }
        }
      }
    }

    return results;
  }
 private static HighlightInfo annotationError(PsiAnnotation annotation, String message) {
   HighlightInfo info =
       HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
           .range(annotation)
           .descriptionAndTooltip(message)
           .create();
   QuickFixAction.registerQuickFixAction(info, new DeleteAnnotationAction(annotation));
   return info;
 }
  private static void registerAddImportFixes(
      GrReferenceElement refElement, @Nullable HighlightInfo info, final HighlightDisplayKey key) {
    final String referenceName = refElement.getReferenceName();
    //noinspection ConstantConditions
    if (StringUtil.isEmpty(referenceName)) return;
    if (!(refElement instanceof GrCodeReferenceElement)
        && Character.isLowerCase(referenceName.charAt(0))) return;
    if (refElement.getQualifier() != null) return;

    QuickFixAction.registerQuickFixAction(info, new GroovyAddImportAction(refElement), key);
  }
  private static void registerStaticImportFix(
      @NotNull GrReferenceExpression referenceExpression,
      @Nullable HighlightInfo info,
      @Nullable final HighlightDisplayKey key) {
    final String referenceName = referenceExpression.getReferenceName();
    if (StringUtil.isEmpty(referenceName)) return;
    if (referenceExpression.getQualifier() != null) return;

    QuickFixAction.registerQuickFixAction(
        info, new GroovyStaticImportMethodFix((GrMethodCall) referenceExpression.getParent()), key);
  }
  @Nullable
  private List<? extends LocalQuickFix> registerFixes(final HighlightInfo info) {

    final List<LocalQuickFix> list =
        OrderEntryFix.registerFixes(new QuickFixActionRegistrarImpl(info), this);

    final String[] extendClasses = getExtendClassNames();
    final String extendClass =
        extendClasses != null && extendClasses.length > 0 ? extendClasses[0] : null;

    final JavaClassReference[] references = getJavaClassReferenceSet().getAllReferences();
    PsiPackage contextPackage = null;
    for (int i = myIndex; i >= 0; i--) {
      final PsiElement context = references[i].getContext();
      if (context != null) {
        if (context instanceof PsiPackage) {
          contextPackage = (PsiPackage) context;
        }
        break;
      }
    }

    boolean createJavaClass = !canReferencePackage();
    ClassKind kind = createJavaClass ? getClassKind() : null;
    if (createJavaClass && kind == null) kind = ClassKind.CLASS;
    final String templateName = JavaClassReferenceProvider.CLASS_TEMPLATE.getValue(getOptions());
    final TextRange range =
        new TextRange(
            references[0].getRangeInElement().getStartOffset(), getRangeInElement().getEndOffset());
    final String qualifiedName = range.substring(getElement().getText());
    final CreateClassOrPackageFix action =
        CreateClassOrPackageFix.createFix(
            qualifiedName,
            getScope(),
            getElement(),
            contextPackage,
            kind,
            extendClass,
            templateName);
    if (action != null) {
      QuickFixAction.registerQuickFixAction(info, action);
      if (list == null) {
        return Arrays.asList(action);
      } else {
        final ArrayList<LocalQuickFix> fixes = new ArrayList<LocalQuickFix>(list.size() + 1);
        fixes.addAll(list);
        fixes.add(action);
        return fixes;
      }
    }
    return list;
  }
  private static void addEmptyIntentionIfNeeded(@Nullable HighlightInfo info) {
    if (info != null) {
      int s1 = info.quickFixActionMarkers != null ? info.quickFixActionMarkers.size() : 0;
      int s2 = info.quickFixActionRanges != null ? info.quickFixActionRanges.size() : 0;

      if (s1 + s2 == 0) {
        EmptyIntentionAction emptyIntentionAction =
            new EmptyIntentionAction("Access to unresolved expression");
        QuickFixAction.registerQuickFixAction(
            info, emptyIntentionAction, HighlightDisplayKey.find(SHORT_NAME));
      }
    }
  }
  @Nullable
  static HighlightInfo checkVariableMustBeFinal(
      PsiVariable variable,
      PsiJavaCodeReferenceElement context,
      @NotNull LanguageLevel languageLevel) {
    if (variable.hasModifierProperty(PsiModifier.FINAL)) return null;
    final PsiClass innerClass = getInnerClassVariableReferencedFrom(variable, context);
    if (innerClass != null) {
      if (variable instanceof PsiParameter) {
        final PsiElement parent = variable.getParent();
        if (parent instanceof PsiParameterList
            && parent.getParent() instanceof PsiLambdaExpression
            && notAccessedForWriting(
                variable, new LocalSearchScope(((PsiParameter) variable).getDeclarationScope()))) {
          return null;
        }
      }
      if (languageLevel.isAtLeast(LanguageLevel.JDK_1_8)
          && isEffectivelyFinal(variable, innerClass, context)) {
        return null;
      }
      final String description =
          JavaErrorMessages.message("variable.must.be.final", context.getText());

      final HighlightInfo highlightInfo =
          HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
              .range(context)
              .descriptionAndTooltip(description)
              .create();
      QuickFixAction.registerQuickFixAction(
          highlightInfo,
          QUICK_FIX_FACTORY.createVariableAccessFromInnerClassFix(variable, innerClass));
      return highlightInfo;
    }
    final PsiLambdaExpression lambdaExpression =
        PsiTreeUtil.getParentOfType(context, PsiLambdaExpression.class);
    if (lambdaExpression != null && !PsiTreeUtil.isAncestor(lambdaExpression, variable, true)) {
      final PsiElement parent = variable.getParent();
      if (parent instanceof PsiParameterList && parent.getParent() == lambdaExpression) {
        return null;
      }
      if (!isEffectivelyFinal(variable, lambdaExpression, context)) {
        return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
            .range(context)
            .descriptionAndTooltip("Variable used in lambda expression should be effectively final")
            .create();
      }
    }
    return null;
  }
  @Nullable
  static HighlightInfo checkFileName(@NotNull PsiJavaModule element, @NotNull PsiFile file) {
    if (!MODULE_INFO_FILE.equals(file.getName())) {
      String message = JavaErrorMessages.message("module.file.wrong.name");
      HighlightInfo info =
          HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
              .range(range(element))
              .description(message)
              .create();
      QuickFixAction.registerQuickFixAction(info, factory().createRenameFileFix(MODULE_INFO_FILE));
      return info;
    }

    return null;
  }
 private static void appendFixes(
     @Nullable TextRange fixedRange, HighlightInfo info, List<Annotation.QuickFixInfo> fixes) {
   if (fixes != null) {
     for (final Annotation.QuickFixInfo quickFixInfo : fixes) {
       QuickFixAction.registerQuickFixAction(
           info,
           fixedRange != null ? fixedRange : quickFixInfo.textRange,
           quickFixInfo.quickFix,
           quickFixInfo.key != null
               ? quickFixInfo.key
               : HighlightDisplayKey.find(
                   DefaultHighlightVisitorBasedInspection.AnnotatorBasedInspection
                       .ANNOTATOR_SHORT_NAME));
     }
   }
 }
  private static void registerCreateClassByTypeFix(
      GrReferenceElement refElement, @Nullable HighlightInfo info, final HighlightDisplayKey key) {
    GrPackageDefinition packageDefinition =
        PsiTreeUtil.getParentOfType(refElement, GrPackageDefinition.class);
    if (packageDefinition != null) return;

    PsiElement parent = refElement.getParent();
    if (parent instanceof GrNewExpression
        && refElement
            .getManager()
            .areElementsEquivalent(((GrNewExpression) parent).getReferenceElement(), refElement)) {
      QuickFixAction.registerQuickFixAction(
          info, CreateClassFix.createClassFromNewAction((GrNewExpression) parent), key);
    } else {
      if (shouldBeInterface(refElement)) {
        QuickFixAction.registerQuickFixAction(
            info, CreateClassFix.createClassFixAction(refElement, CreateClassKind.INTERFACE), key);
      } else if (shouldBeClass(refElement)) {
        QuickFixAction.registerQuickFixAction(
            info, CreateClassFix.createClassFixAction(refElement, CreateClassKind.CLASS), key);
        QuickFixAction.registerQuickFixAction(
            info, CreateClassFix.createClassFixAction(refElement, CreateClassKind.ENUM), key);
      } else if (shouldBeAnnotation(refElement)) {
        QuickFixAction.registerQuickFixAction(
            info, CreateClassFix.createClassFixAction(refElement, CreateClassKind.ANNOTATION), key);
      } else {
        QuickFixAction.registerQuickFixAction(
            info, CreateClassFix.createClassFixAction(refElement, CreateClassKind.CLASS), key);
        QuickFixAction.registerQuickFixAction(
            info, CreateClassFix.createClassFixAction(refElement, CreateClassKind.INTERFACE), key);
        QuickFixAction.registerQuickFixAction(
            info, CreateClassFix.createClassFixAction(refElement, CreateClassKind.ENUM), key);
        QuickFixAction.registerQuickFixAction(
            info, CreateClassFix.createClassFixAction(refElement, CreateClassKind.ANNOTATION), key);
      }
    }
  }
 @Nullable
 static HighlightInfo checkFinalVariableInitializedInLoop(
     @NotNull PsiReferenceExpression expression, @NotNull PsiElement resolved) {
   if (ControlFlowUtil.isVariableAssignedInLoop(expression, resolved)) {
     String description =
         JavaErrorMessages.message(
             "variable.assigned.in.loop", ((PsiVariable) resolved).getName());
     final HighlightInfo highlightInfo =
         HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
             .range(expression)
             .descriptionAndTooltip(description)
             .create();
     QuickFixAction.registerQuickFixAction(
         highlightInfo,
         QUICK_FIX_FACTORY.createModifierListFix(
             (PsiVariable) resolved, PsiModifier.FINAL, false, false));
     return highlightInfo;
   }
   return null;
 }
  public void addMessage(
      final PsiElement context,
      final String message,
      final ErrorType type,
      final IntentionAction... fixes) {
    if (message != null && message.length() > 0) {
      final PsiFile containingFile = context.getContainingFile();
      final HighlightInfoType defaultInfoType =
          type == ErrorType.ERROR
              ? HighlightInfoType.ERROR
              : type == ErrorType.WARNING
                  ? HighlightInfoType.WARNING
                  : HighlightInfoType.WEAK_WARNING;

      if (context instanceof XmlTag
          && XmlExtension.getExtension(containingFile).shouldBeHighlightedAsTag((XmlTag) context)) {
        addElementsForTagWithManyQuickFixes((XmlTag) context, message, defaultInfoType, fixes);
      } else {
        final PsiElement contextOfFile = containingFile.getContext();
        final HighlightInfo highlightInfo;

        if (contextOfFile != null) {
          TextRange range =
              InjectedLanguageManager.getInstance(context.getProject())
                  .injectedToHost(context, context.getTextRange());
          highlightInfo = HighlightInfo.createHighlightInfo(defaultInfoType, range, message);
        } else {
          highlightInfo =
              HighlightInfo.createHighlightInfo(HighlightInfoType.WRONG_REF, context, message);
        }

        if (fixes != null) {
          for (final IntentionAction quickFixAction : fixes) {
            if (quickFixAction == null) continue;
            QuickFixAction.registerQuickFixAction(highlightInfo, quickFixAction);
          }
        }
        addToResults(highlightInfo);
      }
    }
  }
  @Nullable
  static HighlightInfo checkVariableMustBeFinal(
      @NotNull PsiVariable variable,
      @NotNull PsiJavaCodeReferenceElement context,
      @NotNull LanguageLevel languageLevel) {
    if (variable.hasModifierProperty(PsiModifier.FINAL)) return null;
    final PsiElement innerClass = getInnerClassVariableReferencedFrom(variable, context);
    if (innerClass instanceof PsiClass) {
      if (variable instanceof PsiParameter) {
        final PsiElement parent = variable.getParent();
        if (parent instanceof PsiParameterList
            && parent.getParent() instanceof PsiLambdaExpression
            && notAccessedForWriting(
                variable, new LocalSearchScope(((PsiParameter) variable).getDeclarationScope()))) {
          return null;
        }
      }
      final boolean isToBeEffectivelyFinal = languageLevel.isAtLeast(LanguageLevel.JDK_1_8);
      if (isToBeEffectivelyFinal && isEffectivelyFinal(variable, innerClass, context)) {
        return null;
      }
      final String description =
          JavaErrorMessages.message(
              isToBeEffectivelyFinal
                  ? "variable.must.be.final.or.effectively.final"
                  : "variable.must.be.final",
              context.getText());

      final HighlightInfo highlightInfo =
          HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
              .range(context)
              .descriptionAndTooltip(description)
              .create();
      QuickFixAction.registerQuickFixAction(
          highlightInfo,
          QUICK_FIX_FACTORY.createVariableAccessFromInnerClassFix(variable, innerClass));
      return highlightInfo;
    }
    return checkWriteToFinalInsideLambda(variable, context);
  }
  @Nullable
  static HighlightInfo checkFileLocation(@NotNull PsiJavaModule element, @NotNull PsiFile file) {
    VirtualFile vFile = file.getVirtualFile();
    if (vFile != null) {
      VirtualFile root =
          ProjectFileIndex.SERVICE.getInstance(file.getProject()).getSourceRootForFile(vFile);
      if (root != null && !root.equals(vFile.getParent())) {
        String message = JavaErrorMessages.message("module.file.wrong.location");
        HighlightInfo info =
            HighlightInfo.newHighlightInfo(HighlightInfoType.WARNING)
                .range(range(element))
                .description(message)
                .create();
        QuickFixAction.registerQuickFixAction(
            info,
            new MoveFileFix(vFile, root, QuickFixBundle.message("move.file.to.source.root.text")));
        return info;
      }
    }

    return null;
  }
  @Nullable
  public static HighlightInfo checkNameValuePair(PsiNameValuePair pair) {
    PsiReference ref = pair.getReference();
    if (ref == null) return null;
    PsiMethod method = (PsiMethod) ref.resolve();
    if (method == null) {
      if (pair.getName() != null) {
        final String description =
            JavaErrorMessages.message("annotation.unknown.method", ref.getCanonicalText());
        PsiElement element = ref.getElement();
        final HighlightInfo highlightInfo =
            HighlightInfo.newHighlightInfo(HighlightInfoType.WRONG_REF)
                .range(element)
                .descriptionAndTooltip(description)
                .create();
        QuickFixAction.registerQuickFixAction(
            highlightInfo,
            QuickFixFactory.getInstance().createCreateAnnotationMethodFromUsageFix(pair));
        return highlightInfo;
      } else {
        String description =
            JavaErrorMessages.message("annotation.missing.method", ref.getCanonicalText());
        PsiElement element = ref.getElement();
        return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
            .range(element)
            .descriptionAndTooltip(description)
            .create();
      }
    } else {
      PsiType returnType = method.getReturnType();
      assert returnType != null : method;
      PsiAnnotationMemberValue value = pair.getValue();
      HighlightInfo info = checkMemberValueType(value, returnType);
      if (info != null) return info;

      return checkDuplicateAttribute(pair);
    }
  }
  private void bindMessageToAstNode(
      final PsiElement childByRole,
      final HighlightInfoType warning,
      final int offset,
      int length,
      final String localizedMessage,
      IntentionAction... quickFixActions) {
    if (childByRole != null) {
      final TextRange textRange = childByRole.getTextRange();
      if (length == -1) length = textRange.getLength();
      final int startOffset = textRange.getStartOffset() + offset;

      HighlightInfo highlightInfo =
          HighlightInfo.createHighlightInfo(
              warning,
              childByRole,
              startOffset,
              startOffset + length,
              localizedMessage,
              HighlightInfo.htmlEscapeToolTip(localizedMessage));

      if (highlightInfo == null) {
        highlightInfo =
            HighlightInfo.createHighlightInfo(
                warning,
                new TextRange(startOffset, startOffset + length),
                localizedMessage,
                localizedMessage,
                NONEMPTY_TEXT_ATTRIBUTES);
      }

      for (final IntentionAction quickFixAction : quickFixActions) {
        if (quickFixAction == null) continue;
        QuickFixAction.registerQuickFixAction(highlightInfo, textRange, quickFixAction, null);
      }
      addToResults(highlightInfo);
    }
  }
 private static HighlightInfo checkWriteToFinalInsideLambda(
     @NotNull PsiVariable variable, @NotNull PsiJavaCodeReferenceElement context) {
   final PsiLambdaExpression lambdaExpression =
       PsiTreeUtil.getParentOfType(context, PsiLambdaExpression.class);
   if (lambdaExpression != null && !PsiTreeUtil.isAncestor(lambdaExpression, variable, true)) {
     final PsiElement parent = variable.getParent();
     if (parent instanceof PsiParameterList && parent.getParent() == lambdaExpression) {
       return null;
     }
     if (!isEffectivelyFinal(variable, lambdaExpression, context)) {
       String text = JavaErrorMessages.message("lambda.variable.must.be.final");
       HighlightInfo highlightInfo =
           HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
               .range(context)
               .descriptionAndTooltip(text)
               .create();
       QuickFixAction.registerQuickFixAction(
           highlightInfo,
           QUICK_FIX_FACTORY.createVariableAccessFromInnerClassFix(variable, lambdaExpression));
       return highlightInfo;
     }
   }
   return null;
 }
 @Override
 public void register(IntentionAction action) {
   myKey = HighlightDisplayKey.find(SHORT_NAME);
   QuickFixAction.registerQuickFixAction(myInfo, action, myKey);
 }
 @Override
 public void register(TextRange fixRange, IntentionAction action, HighlightDisplayKey key) {
   QuickFixAction.registerQuickFixAction(myInfo, fixRange, action, key);
 }
  @Nullable
  static HighlightInfo checkCannotWriteToFinal(
      @NotNull PsiExpression expression, @NotNull PsiFile containingFile) {
    PsiReferenceExpression reference = null;
    boolean readBeforeWrite = false;
    if (expression instanceof PsiAssignmentExpression) {
      final PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression) expression;
      final PsiExpression left =
          PsiUtil.skipParenthesizedExprDown(assignmentExpression.getLExpression());
      if (left instanceof PsiReferenceExpression) {
        reference = (PsiReferenceExpression) left;
      }
      readBeforeWrite = assignmentExpression.getOperationTokenType() != JavaTokenType.EQ;
    } else if (expression instanceof PsiPostfixExpression) {
      final PsiExpression operand =
          PsiUtil.skipParenthesizedExprDown(((PsiPostfixExpression) expression).getOperand());
      final IElementType sign = ((PsiPostfixExpression) expression).getOperationTokenType();
      if (operand instanceof PsiReferenceExpression
          && (sign == JavaTokenType.PLUSPLUS || sign == JavaTokenType.MINUSMINUS)) {
        reference = (PsiReferenceExpression) operand;
      }
      readBeforeWrite = true;
    } else if (expression instanceof PsiPrefixExpression) {
      final PsiExpression operand =
          PsiUtil.skipParenthesizedExprDown(((PsiPrefixExpression) expression).getOperand());
      final IElementType sign = ((PsiPrefixExpression) expression).getOperationTokenType();
      if (operand instanceof PsiReferenceExpression
          && (sign == JavaTokenType.PLUSPLUS || sign == JavaTokenType.MINUSMINUS)) {
        reference = (PsiReferenceExpression) operand;
      }
      readBeforeWrite = true;
    }
    final PsiElement resolved = reference == null ? null : reference.resolve();
    PsiVariable variable = resolved instanceof PsiVariable ? (PsiVariable) resolved : null;
    if (variable == null || !variable.hasModifierProperty(PsiModifier.FINAL)) return null;
    final boolean canWrite =
        canWriteToFinal(variable, expression, reference, containingFile)
            && checkWriteToFinalInsideLambda(variable, reference) == null;
    if (readBeforeWrite || !canWrite) {
      final String name = variable.getName();
      String description =
          canWrite
              ? JavaErrorMessages.message("variable.not.initialized", name)
              : JavaErrorMessages.message("assignment.to.final.variable", name);
      final HighlightInfo highlightInfo =
          HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
              .range(reference.getTextRange())
              .descriptionAndTooltip(description)
              .create();
      final PsiElement innerClass = getInnerClassVariableReferencedFrom(variable, expression);
      if (innerClass == null || variable instanceof PsiField) {
        QuickFixAction.registerQuickFixAction(
            highlightInfo,
            QUICK_FIX_FACTORY.createModifierListFix(variable, PsiModifier.FINAL, false, false));
      } else {
        QuickFixAction.registerQuickFixAction(
            highlightInfo,
            QUICK_FIX_FACTORY.createVariableAccessFromInnerClassFix(variable, innerClass));
      }
      return highlightInfo;
    }

    return null;
  }
 @Override
 public void unregister(Condition<IntentionAction> condition) {
   if (myInfo != null) {
     QuickFixAction.unregisterQuickFixAction(myInfo, condition);
   }
 }
  @Nullable
  public static HighlightInfo checkFinalVariableMightAlreadyHaveBeenAssignedTo(
      @NotNull PsiVariable variable,
      @NotNull PsiReferenceExpression expression,
      @NotNull Map<PsiElement, Collection<ControlFlowUtil.VariableInfo>> finalVarProblems) {
    if (!PsiUtil.isAccessedForWriting(expression)) return null;

    final PsiElement scope =
        variable instanceof PsiField
            ? variable.getParent()
            : variable.getParent() == null ? null : variable.getParent().getParent();
    PsiElement codeBlock = PsiUtil.getTopLevelEnclosingCodeBlock(expression, scope);
    if (codeBlock == null) return null;
    Collection<ControlFlowUtil.VariableInfo> codeBlockProblems =
        getFinalVariableProblemsInBlock(finalVarProblems, codeBlock);

    boolean alreadyAssigned = false;
    for (ControlFlowUtil.VariableInfo variableInfo : codeBlockProblems) {
      if (variableInfo.expression == expression) {
        alreadyAssigned = true;
        break;
      }
    }

    if (!alreadyAssigned) {
      if (!(variable instanceof PsiField)) return null;
      final PsiField field = (PsiField) variable;
      final PsiClass aClass = field.getContainingClass();
      if (aClass == null) return null;
      // field can get assigned in other field initializers
      final PsiField[] fields = aClass.getFields();
      boolean isFieldStatic = field.hasModifierProperty(PsiModifier.STATIC);
      for (PsiField psiField : fields) {
        PsiExpression initializer = psiField.getInitializer();
        if (psiField != field
            && psiField.hasModifierProperty(PsiModifier.STATIC) == isFieldStatic
            && initializer != null
            && initializer != codeBlock
            && !variableDefinitelyNotAssignedIn(field, initializer)) {
          alreadyAssigned = true;
          break;
        }
      }

      if (!alreadyAssigned) {
        // field can get assigned in class initializers
        final PsiMember enclosingConstructorOrInitializer =
            PsiUtil.findEnclosingConstructorOrInitializer(expression);
        if (enclosingConstructorOrInitializer == null
            || !aClass
                .getManager()
                .areElementsEquivalent(
                    enclosingConstructorOrInitializer.getContainingClass(), aClass)) {
          return null;
        }
        final PsiClassInitializer[] initializers = aClass.getInitializers();
        for (PsiClassInitializer initializer : initializers) {
          if (initializer.hasModifierProperty(PsiModifier.STATIC)
              == field.hasModifierProperty(PsiModifier.STATIC)) {
            final PsiCodeBlock body = initializer.getBody();
            if (body == codeBlock) return null;
            try {
              final ControlFlow controlFlow = getControlFlow(body);
              if (!ControlFlowUtil.isVariableDefinitelyNotAssigned(field, controlFlow)) {
                alreadyAssigned = true;
                break;
              }
            } catch (AnalysisCanceledException e) {
              // incomplete code
              return null;
            }
          }
        }
      }

      if (!alreadyAssigned && !field.hasModifierProperty(PsiModifier.STATIC)) {
        // then check if instance field already assigned in other constructor
        final PsiMethod ctr =
            codeBlock.getParent() instanceof PsiMethod ? (PsiMethod) codeBlock.getParent() : null;
        // assignment to final field in several constructors threatens us only if these are linked
        // (there is this() call in the beginning)
        final List<PsiMethod> redirectedConstructors =
            ctr != null && ctr.isConstructor()
                ? JavaHighlightUtil.getChainedConstructors(ctr)
                : null;
        for (int j = 0; redirectedConstructors != null && j < redirectedConstructors.size(); j++) {
          PsiMethod redirectedConstructor = redirectedConstructors.get(j);
          PsiCodeBlock body = redirectedConstructor.getBody();
          if (body != null && variableDefinitelyAssignedIn(variable, body)) {
            alreadyAssigned = true;
            break;
          }
        }
      }
    }

    if (alreadyAssigned) {
      String description =
          JavaErrorMessages.message("variable.already.assigned", variable.getName());
      final HighlightInfo highlightInfo =
          HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
              .range(expression)
              .descriptionAndTooltip(description)
              .create();
      QuickFixAction.registerQuickFixAction(
          highlightInfo,
          QUICK_FIX_FACTORY.createModifierListFix(variable, PsiModifier.FINAL, false, false));
      QuickFixAction.registerQuickFixAction(
          highlightInfo, QUICK_FIX_FACTORY.createDeferFinalAssignmentFix(variable, expression));
      return highlightInfo;
    }

    return null;
  }
  @Nullable
  public static HighlightInfo checkVariableInitializedBeforeUsage(
      @NotNull PsiReferenceExpression expression,
      @NotNull PsiVariable variable,
      @NotNull Map<PsiElement, Collection<PsiReferenceExpression>> uninitializedVarProblems,
      @NotNull PsiFile containingFile) {
    if (variable instanceof ImplicitVariable) return null;
    if (!PsiUtil.isAccessedForReading(expression)) return null;
    int startOffset = expression.getTextRange().getStartOffset();
    final PsiElement topBlock;
    if (variable.hasInitializer()) {
      topBlock = PsiUtil.getVariableCodeBlock(variable, variable);
      if (topBlock == null) return null;
    } else {
      PsiElement scope =
          variable instanceof PsiField
              ? ((PsiField) variable).getContainingClass()
              : variable.getParent() != null ? variable.getParent().getParent() : null;
      if (scope instanceof PsiCodeBlock && scope.getParent() instanceof PsiSwitchStatement) {
        scope = PsiTreeUtil.getParentOfType(scope, PsiCodeBlock.class);
      }

      topBlock =
          FileTypeUtils.isInServerPageFile(scope) && scope instanceof PsiFile
              ? scope
              : PsiUtil.getTopLevelEnclosingCodeBlock(expression, scope);
      if (variable instanceof PsiField) {
        // non final field already initialized with default value
        if (!variable.hasModifierProperty(PsiModifier.FINAL)) return null;
        // final field may be initialized in ctor or class initializer only
        // if we're inside non-ctr method, skip it
        if (PsiUtil.findEnclosingConstructorOrInitializer(expression) == null
            && HighlightUtil.findEnclosingFieldInitializer(expression) == null) {
          return null;
        }
        if (topBlock == null) return null;
        final PsiElement parent = topBlock.getParent();
        // access to final fields from inner classes always allowed
        if (inInnerClass(expression, ((PsiField) variable).getContainingClass(), containingFile))
          return null;
        final PsiCodeBlock block;
        final PsiClass aClass;
        if (parent instanceof PsiMethod) {
          PsiMethod constructor = (PsiMethod) parent;
          if (!containingFile
              .getManager()
              .areElementsEquivalent(
                  constructor.getContainingClass(), ((PsiField) variable).getContainingClass()))
            return null;
          // static variables already initialized in class initializers
          if (variable.hasModifierProperty(PsiModifier.STATIC)) return null;
          // as a last chance, field may be initialized in this() call
          final List<PsiMethod> redirectedConstructors =
              JavaHighlightUtil.getChainedConstructors(constructor);
          for (int j = 0;
              redirectedConstructors != null && j < redirectedConstructors.size();
              j++) {
            PsiMethod redirectedConstructor = redirectedConstructors.get(j);
            // variable must be initialized before its usage
            // ???
            // if (startOffset < redirectedConstructor.getTextRange().getStartOffset()) continue;
            PsiCodeBlock body = redirectedConstructor.getBody();
            if (body != null && variableDefinitelyAssignedIn(variable, body)) {
              return null;
            }
          }
          block = constructor.getBody();
          aClass = constructor.getContainingClass();
        } else if (parent instanceof PsiClassInitializer) {
          final PsiClassInitializer classInitializer = (PsiClassInitializer) parent;
          if (!containingFile
              .getManager()
              .areElementsEquivalent(
                  classInitializer.getContainingClass(),
                  ((PsiField) variable).getContainingClass())) return null;
          block = classInitializer.getBody();
          aClass = classInitializer.getContainingClass();
        } else {
          // field reference outside code block
          // check variable initialized before its usage
          final PsiField field = (PsiField) variable;

          aClass = field.getContainingClass();
          if (aClass == null
              || isFieldInitializedInOtherFieldInitializer(
                  aClass, field, field.hasModifierProperty(PsiModifier.STATIC))) {
            return null;
          }
          final PsiField anotherField =
              PsiTreeUtil.getTopmostParentOfType(expression, PsiField.class);
          int offset = startOffset;
          if (anotherField != null
              && anotherField.getContainingClass() == aClass
              && !field.hasModifierProperty(PsiModifier.STATIC)) {
            offset = 0;
          }
          block = null;
          // initializers will be checked later
          final PsiMethod[] constructors = aClass.getConstructors();
          for (PsiMethod constructor : constructors) {
            // variable must be initialized before its usage
            if (offset < constructor.getTextRange().getStartOffset()) continue;
            PsiCodeBlock body = constructor.getBody();
            if (body != null && variableDefinitelyAssignedIn(variable, body)) {
              return null;
            }
            // as a last chance, field may be initialized in this() call
            final List<PsiMethod> redirectedConstructors =
                JavaHighlightUtil.getChainedConstructors(constructor);
            for (int j = 0;
                redirectedConstructors != null && j < redirectedConstructors.size();
                j++) {
              PsiMethod redirectedConstructor = redirectedConstructors.get(j);
              // variable must be initialized before its usage
              if (offset < redirectedConstructor.getTextRange().getStartOffset()) continue;
              PsiCodeBlock redirectedBody = redirectedConstructor.getBody();
              if (redirectedBody != null
                  && variableDefinitelyAssignedIn(variable, redirectedBody)) {
                return null;
              }
            }
          }
        }

        if (aClass != null) {
          // field may be initialized in class initializer
          final PsiClassInitializer[] initializers = aClass.getInitializers();
          for (PsiClassInitializer initializer : initializers) {
            PsiCodeBlock body = initializer.getBody();
            if (body == block) break;
            // variable referenced in initializer must be initialized in initializer preceding
            // assignment
            // variable referenced in field initializer or in class initializer
            boolean shouldCheckInitializerOrder =
                block == null || block.getParent() instanceof PsiClassInitializer;
            if (shouldCheckInitializerOrder
                && startOffset < initializer.getTextRange().getStartOffset()) continue;
            if (initializer.hasModifierProperty(PsiModifier.STATIC)
                == variable.hasModifierProperty(PsiModifier.STATIC)) {
              if (variableDefinitelyAssignedIn(variable, body)) return null;
            }
          }
        }
      }
    }
    if (topBlock == null) return null;
    Collection<PsiReferenceExpression> codeBlockProblems = uninitializedVarProblems.get(topBlock);
    if (codeBlockProblems == null) {
      try {
        final ControlFlow controlFlow = getControlFlow(topBlock);
        codeBlockProblems = ControlFlowUtil.getReadBeforeWriteLocals(controlFlow);
      } catch (AnalysisCanceledException | IndexNotReadyException e) {
        codeBlockProblems = Collections.emptyList();
      }
      uninitializedVarProblems.put(topBlock, codeBlockProblems);
    }
    if (codeBlockProblems.contains(expression)) {
      final String name = expression.getElement().getText();
      String description = JavaErrorMessages.message("variable.not.initialized", name);
      HighlightInfo highlightInfo =
          HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
              .range(expression)
              .descriptionAndTooltip(description)
              .create();
      QuickFixAction.registerQuickFixAction(
          highlightInfo, QUICK_FIX_FACTORY.createAddVariableInitializerFix(variable));
      if (variable instanceof PsiField) {
        QuickFixAction.registerQuickFixAction(
            highlightInfo,
            QUICK_FIX_FACTORY.createModifierListFix(variable, PsiModifier.FINAL, false, false));
      }
      return highlightInfo;
    }

    return null;
  }