From 014d62c7195e39bf769918498173277cdbf11c5e Mon Sep 17 00:00:00 2001 From: UNV Date: Sun, 4 May 2025 00:47:53 +0300 Subject: [PATCH 1/4] Reformatting HighlightControlFlowUtil and HighlightNamesUtil. --- .../analysis/HighlightControlFlowUtil.java | 1678 +++++++++-------- .../impl/analysis/HighlightNamesUtil.java | 607 +++--- 2 files changed, 1233 insertions(+), 1052 deletions(-) diff --git a/java-analysis-impl/src/main/java/com/intellij/java/analysis/impl/codeInsight/daemon/impl/analysis/HighlightControlFlowUtil.java b/java-analysis-impl/src/main/java/com/intellij/java/analysis/impl/codeInsight/daemon/impl/analysis/HighlightControlFlowUtil.java index a73192d17a..4a3f902a82 100644 --- a/java-analysis-impl/src/main/java/com/intellij/java/analysis/impl/codeInsight/daemon/impl/analysis/HighlightControlFlowUtil.java +++ b/java-analysis-impl/src/main/java/com/intellij/java/analysis/impl/codeInsight/daemon/impl/analysis/HighlightControlFlowUtil.java @@ -54,852 +54,986 @@ * @since Aug 8, 2002 */ public class HighlightControlFlowUtil { - - private HighlightControlFlowUtil() { - } - - @Nullable - public static HighlightInfo checkMissingReturnStatement(@Nullable PsiCodeBlock body, @Nullable 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 = JavaErrorBundle.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, QuickFixFactory.getInstance().createAddReturnFix(method)); - QuickFixAction.registerQuickFixAction(info, QuickFixFactory.getInstance().createMethodReturnFix(method, PsiType.VOID, true)); - } - return info; - } - } catch (AnalysisCanceledException ignored) { + private HighlightControlFlowUtil() { } - return null; - } - - @Nonnull - public static ControlFlow getControlFlowNoConstantEvaluate(@Nonnull PsiElement body) throws AnalysisCanceledException { - LocalsOrMyInstanceFieldsControlFlowPolicy policy = LocalsOrMyInstanceFieldsControlFlowPolicy.getInstance(); - return ControlFlowFactory.getControlFlow(body, policy, ControlFlowOptions.NO_CONST_EVALUATE); - } + @Nullable + public static HighlightInfo checkMissingReturnStatement(@Nullable PsiCodeBlock body, @Nullable PsiType returnType) { + if (body == null || returnType == null || PsiType.VOID.equals(returnType.getDeepComponentType())) { + return null; + } - @Nonnull - private static ControlFlow getControlFlow(@Nonnull PsiElement context) throws AnalysisCanceledException { - LocalsOrMyInstanceFieldsControlFlowPolicy policy = LocalsOrMyInstanceFieldsControlFlowPolicy.getInstance(); - return ControlFlowFactory.getInstance(context.getProject()).getControlFlow(context, policy); - } + // 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 = JavaErrorBundle.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, QuickFixFactory.getInstance().createAddReturnFix(method)); + QuickFixAction.registerQuickFixAction( + info, + QuickFixFactory.getInstance().createMethodReturnFix(method, PsiType.VOID, true) + ); + } + return info; + } + } + catch (AnalysisCanceledException ignored) { + } - public static HighlightInfo checkUnreachableStatement(@Nullable PsiCodeBlock codeBlock) { - if (codeBlock == null) { - return null; - } - // do not compute constant expressions for if() statement condition - // see JLS 14.20 Unreachable Statements - try { - AllVariablesControlFlowPolicy policy = AllVariablesControlFlowPolicy.getInstance(); - final ControlFlow controlFlow = ControlFlowFactory.getControlFlow(codeBlock, policy, ControlFlowOptions.NO_CONST_EVALUATE); - final PsiElement unreachableStatement = ControlFlowUtil.getUnreachableStatement(controlFlow); - if (unreachableStatement != null) { - String description = JavaErrorBundle.message("unreachable.statement"); - PsiElement keyword = null; - if (unreachableStatement instanceof PsiIfStatement || - unreachableStatement instanceof PsiSwitchBlock || - unreachableStatement instanceof PsiLoopStatement) { - keyword = unreachableStatement.getFirstChild(); - } - final PsiElement element = keyword != null ? keyword : unreachableStatement; - final HighlightInfo info = - HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(element).descriptionAndTooltip(description).create(); - QuickFixAction.registerQuickFixAction( - info, QuickFixFactory.getInstance() - .createDeleteFix(unreachableStatement, JavaQuickFixBundle.message("delete.unreachable.statement.fix.text"))); - return info; - } - } catch (AnalysisCanceledException | IndexNotReadyException e) { - // incomplete code + return null; } - return null; - } - public static boolean isFieldInitializedAfterObjectConstruction(@Nonnull PsiField field) { - if (field.hasInitializer()) { - return true; - } - final boolean isFieldStatic = field.hasModifierProperty(PsiModifier.STATIC); - final PsiClass aClass = field.getContainingClass(); - if (aClass != null) { - // field might be assigned in the other field initializers - if (isFieldInitializedInOtherFieldInitializer(aClass, field, isFieldStatic, __ -> true)) { - return true; - } - } - final PsiClassInitializer[] initializers; - if (aClass != null) { - initializers = aClass.getInitializers(); - } else { - return false; + @Nonnull + public static ControlFlow getControlFlowNoConstantEvaluate(@Nonnull PsiElement body) throws AnalysisCanceledException { + LocalsOrMyInstanceFieldsControlFlowPolicy policy = LocalsOrMyInstanceFieldsControlFlowPolicy.getInstance(); + return ControlFlowFactory.getControlFlow(body, policy, ControlFlowOptions.NO_CONST_EVALUATE); } - if (isFieldInitializedInClassInitializer(field, isFieldStatic, initializers)) { - return true; + + @Nonnull + private static ControlFlow getControlFlow(@Nonnull PsiElement context) throws AnalysisCanceledException { + LocalsOrMyInstanceFieldsControlFlowPolicy policy = LocalsOrMyInstanceFieldsControlFlowPolicy.getInstance(); + return ControlFlowFactory.getInstance(context.getProject()).getControlFlow(context, policy); } - if (isFieldStatic) { - return false; - } else { - // instance field should be initialized at the end of the each constructor - final PsiMethod[] constructors = aClass.getConstructors(); - if (constructors.length == 0) { - return false; - } - nextConstructor: - for (PsiMethod constructor : constructors) { - PsiCodeBlock ctrBody = constructor.getBody(); - if (ctrBody == null) { - return false; - } - final List redirectedConstructors = JavaHighlightUtil.getChainedConstructors(constructor); - for (PsiMethod redirectedConstructor : redirectedConstructors) { - final PsiCodeBlock body = redirectedConstructor.getBody(); - if (body != null && variableDefinitelyAssignedIn(field, body)) { - continue nextConstructor; - } - } - if (!ctrBody.isValid() || variableDefinitelyAssignedIn(field, ctrBody)) { - continue; + public static HighlightInfo checkUnreachableStatement(@Nullable PsiCodeBlock codeBlock) { + if (codeBlock == null) { + return null; } - return false; - } - return true; - } - } - - private static boolean isFieldInitializedInOtherFieldInitializer(@Nonnull PsiClass aClass, - @Nonnull PsiField field, - final boolean fieldStatic, - @Nonnull Predicate condition) { - PsiField[] fields = aClass.getFields(); - for (PsiField psiField : fields) { - if (psiField != field - && psiField.hasModifierProperty(PsiModifier.STATIC) == fieldStatic - && variableDefinitelyAssignedIn(field, psiField) - && condition.test(psiField)) { - return true; - } + // do not compute constant expressions for if() statement condition + // see JLS 14.20 Unreachable Statements + try { + AllVariablesControlFlowPolicy policy = AllVariablesControlFlowPolicy.getInstance(); + final ControlFlow controlFlow = ControlFlowFactory.getControlFlow(codeBlock, policy, ControlFlowOptions.NO_CONST_EVALUATE); + final PsiElement unreachableStatement = ControlFlowUtil.getUnreachableStatement(controlFlow); + if (unreachableStatement != null) { + String description = JavaErrorBundle.message("unreachable.statement"); + PsiElement keyword = null; + if (unreachableStatement instanceof PsiIfStatement || + unreachableStatement instanceof PsiSwitchBlock || + unreachableStatement instanceof PsiLoopStatement) { + keyword = unreachableStatement.getFirstChild(); + } + final PsiElement element = keyword != null ? keyword : unreachableStatement; + final HighlightInfo info = + HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(element).descriptionAndTooltip(description).create(); + QuickFixAction.registerQuickFixAction( + info, QuickFixFactory.getInstance() + .createDeleteFix(unreachableStatement, JavaQuickFixBundle.message("delete.unreachable.statement.fix.text"))); + return info; + } + } + catch (AnalysisCanceledException | IndexNotReadyException e) { + // incomplete code + } + return null; } - return false; - } - - public static boolean isRecursivelyCalledConstructor(@Nonnull PsiMethod constructor) { - final JavaHighlightUtil.ConstructorVisitorInfo info = new JavaHighlightUtil.ConstructorVisitorInfo(); - JavaHighlightUtil.visitConstructorChain(constructor, info); - if (info.recursivelyCalledConstructor == null) { - return false; + + public static boolean isFieldInitializedAfterObjectConstruction(@Nonnull PsiField field) { + if (field.hasInitializer()) { + return true; + } + final boolean isFieldStatic = field.hasModifierProperty(PsiModifier.STATIC); + final PsiClass aClass = field.getContainingClass(); + if (aClass != null) { + // field might be assigned in the other field initializers + if (isFieldInitializedInOtherFieldInitializer(aClass, field, isFieldStatic, __ -> true)) { + return true; + } + } + final PsiClassInitializer[] initializers; + if (aClass != null) { + initializers = aClass.getInitializers(); + } + else { + return false; + } + if (isFieldInitializedInClassInitializer(field, isFieldStatic, initializers)) { + return true; + } + if (isFieldStatic) { + return false; + } + else { + // instance field should be initialized at the end of the each constructor + final PsiMethod[] constructors = aClass.getConstructors(); + + if (constructors.length == 0) { + return false; + } + nextConstructor: + for (PsiMethod constructor : constructors) { + PsiCodeBlock ctrBody = constructor.getBody(); + if (ctrBody == null) { + return false; + } + final List redirectedConstructors = JavaHighlightUtil.getChainedConstructors(constructor); + for (PsiMethod redirectedConstructor : redirectedConstructors) { + final PsiCodeBlock body = redirectedConstructor.getBody(); + if (body != null && variableDefinitelyAssignedIn(field, body)) { + continue nextConstructor; + } + } + if (!ctrBody.isValid() || variableDefinitelyAssignedIn(field, ctrBody)) { + continue; + } + return false; + } + return true; + } } - // our constructor is reached from some other constructor by constructor chain - return info.visitedConstructors.indexOf(info.recursivelyCalledConstructor) <= info.visitedConstructors.indexOf(constructor); - } - - public static boolean isAssigned(@Nonnull PsiParameter parameter) { - ParamWriteProcessor processor = new ParamWriteProcessor(); - ReferencesSearch.search(parameter, new LocalSearchScope(parameter.getDeclarationScope()), true).forEach(processor); - return processor.isWriteRefFound(); - } - - private static class ParamWriteProcessor implements Processor { - private volatile boolean myIsWriteRefFound; - - @Override - public boolean process(PsiReference reference) { - final PsiElement element = reference.getElement(); - if (element instanceof PsiReferenceExpression && PsiUtil.isAccessedForWriting((PsiExpression) element)) { - myIsWriteRefFound = true; + + private static boolean isFieldInitializedInOtherFieldInitializer( + @Nonnull PsiClass aClass, + @Nonnull PsiField field, + final boolean fieldStatic, + @Nonnull Predicate condition + ) { + PsiField[] fields = aClass.getFields(); + for (PsiField psiField : fields) { + if (psiField != field + && psiField.hasModifierProperty(PsiModifier.STATIC) == fieldStatic + && variableDefinitelyAssignedIn(field, psiField) + && condition.test(psiField)) { + return true; + } + } return false; - } - return true; } - private boolean isWriteRefFound() { - return myIsWriteRefFound; - } - } - - /** - * see JLS chapter 16 - * - * @return true if variable assigned (maybe more than once) - */ - private static boolean variableDefinitelyAssignedIn(@Nonnull PsiVariable variable, @Nonnull PsiElement context) { - try { - ControlFlow controlFlow = getControlFlow(context); - return ControlFlowUtil.isVariableDefinitelyAssigned(variable, controlFlow); - } catch (AnalysisCanceledException e) { - return false; + public static boolean isRecursivelyCalledConstructor(@Nonnull PsiMethod constructor) { + final JavaHighlightUtil.ConstructorVisitorInfo info = new JavaHighlightUtil.ConstructorVisitorInfo(); + JavaHighlightUtil.visitConstructorChain(constructor, info); + if (info.recursivelyCalledConstructor == null) { + return false; + } + // our constructor is reached from some other constructor by constructor chain + return info.visitedConstructors.indexOf(info.recursivelyCalledConstructor) <= info.visitedConstructors.indexOf(constructor); } - } - - private static boolean variableDefinitelyNotAssignedIn(@Nonnull PsiVariable variable, @Nonnull PsiElement context) { - try { - ControlFlow controlFlow = getControlFlow(context); - return ControlFlowUtil.isVariableDefinitelyNotAssigned(variable, controlFlow); - } catch (AnalysisCanceledException e) { - return false; + + public static boolean isAssigned(@Nonnull PsiParameter parameter) { + ParamWriteProcessor processor = new ParamWriteProcessor(); + ReferencesSearch.search(parameter, new LocalSearchScope(parameter.getDeclarationScope()), true).forEach(processor); + return processor.isWriteRefFound(); } - } + private static class ParamWriteProcessor implements Processor { + private volatile boolean myIsWriteRefFound; - @Nullable - public static HighlightInfo checkFinalFieldInitialized(@Nonnull PsiField field) { - if (!field.hasModifierProperty(PsiModifier.FINAL)) { - return null; - } - if (isFieldInitializedAfterObjectConstruction(field)) { - return null; - } + @Override + public boolean process(PsiReference reference) { + final PsiElement element = reference.getElement(); + if (element instanceof PsiReferenceExpression && PsiUtil.isAccessedForWriting((PsiExpression)element)) { + myIsWriteRefFound = true; + return false; + } + return true; + } - String description = JavaErrorBundle.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), QuickFixFactory.getInstance() - .createCreateConstructorParameterFromFieldFix(field)); - QuickFixAction.registerQuickFixAction(highlightInfo, HighlightMethodUtil.getFixRange(field), QuickFixFactory.getInstance() - .createInitializeFinalFieldInConstructorFix(field)); - final PsiClass containingClass = field.getContainingClass(); - if (containingClass != null && !containingClass.isInterface()) { - QuickFixAction.registerQuickFixAction(highlightInfo, QuickFixFactory.getInstance() - .createModifierListFix(field, PsiModifier.FINAL, false, false)); - } - QuickFixAction.registerQuickFixAction(highlightInfo, QuickFixFactory.getInstance().createAddVariableInitializerFix(field)); - return highlightInfo; - } - - - public static HighlightInfo checkVariableInitializedBeforeUsage(@Nonnull PsiReferenceExpression expression, - @Nonnull PsiVariable variable, - @Nonnull Map> uninitializedVarProblems, - @Nonnull PsiFile containingFile) { - return checkVariableInitializedBeforeUsage(expression, variable, uninitializedVarProblems, containingFile, false); - } - - public static HighlightInfo checkVariableInitializedBeforeUsage(@Nonnull PsiReferenceExpression expression, - @Nonnull PsiVariable variable, - @Nonnull Map> uninitializedVarProblems, - @Nonnull PsiFile containingFile, - boolean ignoreFinality) { - if (variable instanceof ImplicitVariable) { - return null; + private boolean isWriteRefFound() { + return myIsWriteRefFound; + } } - if (!PsiUtil.isAccessedForReading(expression)) { - return null; + + /** + * see JLS chapter 16 + * + * @return true if variable assigned (maybe more than once) + */ + private static boolean variableDefinitelyAssignedIn(@Nonnull PsiVariable variable, @Nonnull PsiElement context) { + try { + ControlFlow controlFlow = getControlFlow(context); + return ControlFlowUtil.isVariableDefinitelyAssigned(variable, controlFlow); + } + catch (AnalysisCanceledException e) { + return false; + } } - 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; - while (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 (!ignoreFinality && !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; + + private static boolean variableDefinitelyNotAssignedIn(@Nonnull PsiVariable variable, @Nonnull PsiElement context) { + try { + ControlFlow controlFlow = getControlFlow(context); + return ControlFlowUtil.isVariableDefinitelyNotAssigned(variable, controlFlow); } - 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())) { - 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 redirectedConstructors = JavaHighlightUtil.getChainedConstructors(constructor); - for (PsiMethod redirectedConstructor : redirectedConstructors) { - // variable must be initialized before its usage - //??? - //if (startOffset < redirectedConstructor.getTextRange().getStartOffset()) continue; - if (JavaPsiRecordUtil.isCompactConstructor(redirectedConstructor)) { - return null; - } - 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(); + catch (AnalysisCanceledException e) { + return false; + } + } - if (aClass == null || isFieldInitializedInOtherFieldInitializer(aClass, (PsiField) variable, variable.hasModifierProperty(PsiModifier.STATIC), field -> startOffset > field - .getTextOffset())) { - return null; - } - } else { - // field reference outside code block - // check variable initialized before its usage - final PsiField field = (PsiField) variable; - - aClass = field.getContainingClass(); - final PsiField anotherField = PsiTreeUtil.getTopmostParentOfType(expression, PsiField.class); - if (aClass == null || - isFieldInitializedInOtherFieldInitializer(aClass, field, field.hasModifierProperty(PsiModifier.STATIC), psiField -> startOffset > psiField.getTextOffset())) { + @Nullable + public static HighlightInfo checkFinalFieldInitialized(@Nonnull PsiField field) { + if (!field.hasModifierProperty(PsiModifier.FINAL)) { return null; - } - if (anotherField != null && !anotherField.hasModifierProperty(PsiModifier.STATIC) && field.hasModifierProperty(PsiModifier.STATIC) && - isFieldInitializedInClassInitializer(field, true, aClass.getInitializers())) { + } + if (isFieldInitializedAfterObjectConstruction(field)) { return null; - } + } + + String description = JavaErrorBundle.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), + QuickFixFactory.getInstance().createCreateConstructorParameterFromFieldFix(field) + ); + QuickFixAction.registerQuickFixAction( + highlightInfo, + HighlightMethodUtil.getFixRange(field), + QuickFixFactory.getInstance().createInitializeFinalFieldInConstructorFix(field) + ); + final PsiClass containingClass = field.getContainingClass(); + if (containingClass != null && !containingClass.isInterface()) { + QuickFixAction.registerQuickFixAction( + highlightInfo, + QuickFixFactory.getInstance().createModifierListFix(field, PsiModifier.FINAL, false, false) + ); + } + QuickFixAction.registerQuickFixAction(highlightInfo, QuickFixFactory.getInstance().createAddVariableInitializerFix(field)); + return highlightInfo; + } + - if (anotherField != null && anotherField.hasInitializer() && !PsiAugmentProvider.canTrustFieldInitializer(anotherField)) { + public static HighlightInfo checkVariableInitializedBeforeUsage( + @Nonnull PsiReferenceExpression expression, + @Nonnull PsiVariable variable, + @Nonnull Map> uninitializedVarProblems, + @Nonnull PsiFile containingFile + ) { + return checkVariableInitializedBeforeUsage(expression, variable, uninitializedVarProblems, containingFile, false); + } + + public static HighlightInfo checkVariableInitializedBeforeUsage( + @Nonnull PsiReferenceExpression expression, + @Nonnull PsiVariable variable, + @Nonnull Map> uninitializedVarProblems, + @Nonnull PsiFile containingFile, + boolean ignoreFinality + ) { + if (variable instanceof ImplicitVariable) { + return null; + } + if (!PsiUtil.isAccessedForReading(expression)) { return null; - } - - 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 redirectedConstructors = JavaHighlightUtil.getChainedConstructors(constructor); - for (PsiMethod redirectedConstructor : redirectedConstructors) { - // variable must be initialized before its usage - if (offset < redirectedConstructor.getTextRange().getStartOffset()) { - continue; - } - PsiCodeBlock redirectedBody = redirectedConstructor.getBody(); - if (redirectedBody != null && variableDefinitelyAssignedIn(variable, redirectedBody)) { + } + 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; + while (scope instanceof PsiCodeBlock && scope.getParent() instanceof PsiSwitchStatement) { + scope = PsiTreeUtil.getParentOfType(scope, PsiCodeBlock.class); + } - 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; - } + 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 (!ignoreFinality && !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())) { + 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 redirectedConstructors = JavaHighlightUtil.getChainedConstructors(constructor); + for (PsiMethod redirectedConstructor : redirectedConstructors) { + // variable must be initialized before its usage + //??? + //if (startOffset < redirectedConstructor.getTextRange().getStartOffset()) continue; + if (JavaPsiRecordUtil.isCompactConstructor(redirectedConstructor)) { + return null; + } + 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(); + + if (aClass == null || isFieldInitializedInOtherFieldInitializer( + aClass, + (PsiField)variable, + variable.hasModifierProperty(PsiModifier.STATIC), + field -> startOffset > field.getTextOffset() + )) { + return null; + } + } + else { + // field reference outside code block + // check variable initialized before its usage + final PsiField field = (PsiField)variable; + + aClass = field.getContainingClass(); + final PsiField anotherField = PsiTreeUtil.getTopmostParentOfType(expression, PsiField.class); + if (aClass == null || + isFieldInitializedInOtherFieldInitializer( + aClass, + field, + field.hasModifierProperty(PsiModifier.STATIC), + psiField -> startOffset > psiField.getTextOffset() + )) { + return null; + } + if (anotherField != null + && !anotherField.hasModifierProperty(PsiModifier.STATIC) + && field.hasModifierProperty(PsiModifier.STATIC) + && isFieldInitializedInClassInitializer(field, true, aClass.getInitializers())) { + return null; + } + + if (anotherField != null && anotherField.hasInitializer() && !PsiAugmentProvider.canTrustFieldInitializer(anotherField)) { + return null; + } + + 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 redirectedConstructors = JavaHighlightUtil.getChainedConstructors(constructor); + for (PsiMethod redirectedConstructor : redirectedConstructors) { + // 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 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 (topBlock == null) { + return null; + } + Collection 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 = JavaErrorBundle.message("variable.not.initialized", name); + HighlightInfo highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) + .range(expression) + .descriptionAndTooltip(description) + .create(); + QuickFixAction.registerQuickFixAction(highlightInfo, QuickFixFactory.getInstance().createAddVariableInitializerFix(variable)); + if (variable instanceof PsiLocalVariable) { + //QuickFixAction.registerQuickFixAction(highlightInfo, HighlightFixUtil.createInsertSwitchDefaultFix(variable, topBlock, expression)); + } + if (variable instanceof PsiField) { + QuickFixAction.registerQuickFixAction( + highlightInfo, + QuickFixFactory.getInstance().createModifierListFix(variable, PsiModifier.FINAL, false, false) + ); + } + return highlightInfo; + } + + return null; } - if (codeBlockProblems.contains(expression)) { - final String name = expression.getElement().getText(); - String description = JavaErrorBundle.message("variable.not.initialized", name); - HighlightInfo highlightInfo = - HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression).descriptionAndTooltip(description).create(); - QuickFixAction.registerQuickFixAction(highlightInfo, QuickFixFactory.getInstance().createAddVariableInitializerFix(variable)); - if (variable instanceof PsiLocalVariable) { - //QuickFixAction.registerQuickFixAction(highlightInfo, HighlightFixUtil.createInsertSwitchDefaultFix(variable, topBlock, expression)); - } - if (variable instanceof PsiField) { - QuickFixAction.registerQuickFixAction(highlightInfo, QuickFixFactory.getInstance() - .createModifierListFix(variable, PsiModifier.FINAL, false, false)); - } - return highlightInfo; + + private static boolean isFieldInitializedInClassInitializer( + @Nonnull PsiField field, + boolean isFieldStatic, + @Nonnull PsiClassInitializer[] initializers + ) { + return ContainerUtil.find( + initializers, + initializer -> initializer.hasModifierProperty(PsiModifier.STATIC) == isFieldStatic + && variableDefinitelyAssignedIn(field, initializer.getBody()) + ) != null; } - return null; - } - - private static boolean isFieldInitializedInClassInitializer(@Nonnull PsiField field, - boolean isFieldStatic, - @Nonnull PsiClassInitializer[] initializers) { - return ContainerUtil.find(initializers, initializer -> initializer.hasModifierProperty(PsiModifier.STATIC) == isFieldStatic - && variableDefinitelyAssignedIn(field, initializer.getBody())) != null; - } - - private static boolean inInnerClass(@Nonnull PsiElement psiElement, @Nullable PsiClass containingClass) { - for (PsiElement element = psiElement; element != null; element = element.getParent()) { - if (element instanceof PsiClass) { - final boolean innerClass = !psiElement.getManager().areElementsEquivalent(element, containingClass); - if (innerClass) { - if (element instanceof PsiAnonymousClass) { - if (PsiTreeUtil.isAncestor(((PsiAnonymousClass) element).getArgumentList(), psiElement, false)) { - continue; - } - return !insideClassInitialization(containingClass, (PsiClass) element); - } - final PsiLambdaExpression lambdaExpression = PsiTreeUtil.getParentOfType(psiElement, PsiLambdaExpression.class); - return lambdaExpression == null || !insideClassInitialization(containingClass, (PsiClass) element); + private static boolean inInnerClass(@Nonnull PsiElement psiElement, @Nullable PsiClass containingClass) { + for (PsiElement element = psiElement; element != null; element = element.getParent()) { + if (element instanceof PsiClass) { + final boolean innerClass = !psiElement.getManager().areElementsEquivalent(element, containingClass); + if (innerClass) { + if (element instanceof PsiAnonymousClass) { + if (PsiTreeUtil.isAncestor(((PsiAnonymousClass)element).getArgumentList(), psiElement, false)) { + continue; + } + return !insideClassInitialization(containingClass, (PsiClass)element); + } + final PsiLambdaExpression lambdaExpression = PsiTreeUtil.getParentOfType(psiElement, PsiLambdaExpression.class); + return lambdaExpression == null || !insideClassInitialization(containingClass, (PsiClass)element); + } + return false; + } } return false; - } } - return false; - } - - private static boolean insideClassInitialization(@Nullable PsiClass containingClass, PsiClass aClass) { - PsiMember member = aClass; - while (member != null) { - if (member.getContainingClass() == containingClass) { - return member instanceof PsiField || - member instanceof PsiMethod && ((PsiMethod) member).isConstructor() || - member instanceof PsiClassInitializer; - } - member = PsiTreeUtil.getParentOfType(member, PsiMember.class, true); - } - return false; - } - public static boolean isReassigned(@Nonnull PsiVariable variable, @Nonnull Map> finalVarProblems) { - if (variable instanceof PsiLocalVariable) { - final PsiElement parent = variable.getParent(); - if (parent == null) { - return false; - } - final PsiElement declarationScope = parent.getParent(); - if (declarationScope == null) { + private static boolean insideClassInitialization(@Nullable PsiClass containingClass, PsiClass aClass) { + PsiMember member = aClass; + while (member != null) { + if (member.getContainingClass() == containingClass) { + return member instanceof PsiField + || member instanceof PsiMethod && ((PsiMethod)member).isConstructor() + || member instanceof PsiClassInitializer; + } + member = PsiTreeUtil.getParentOfType(member, PsiMember.class, true); + } return false; - } - Collection codeBlockProblems = getFinalVariableProblemsInBlock(finalVarProblems, declarationScope); - return codeBlockProblems.contains(new ControlFlowUtil.VariableInfo(variable, null)); } - if (variable instanceof PsiParameter) { - final PsiParameter parameter = (PsiParameter) variable; - return isAssigned(parameter); + + public static boolean isReassigned( + @Nonnull PsiVariable variable, + @Nonnull Map> finalVarProblems + ) { + if (variable instanceof PsiLocalVariable) { + final PsiElement parent = variable.getParent(); + if (parent == null) { + return false; + } + final PsiElement declarationScope = parent.getParent(); + if (declarationScope == null) { + return false; + } + Collection codeBlockProblems = + getFinalVariableProblemsInBlock(finalVarProblems, declarationScope); + return codeBlockProblems.contains(new ControlFlowUtil.VariableInfo(variable, null)); + } + if (variable instanceof PsiParameter) { + final PsiParameter parameter = (PsiParameter)variable; + return isAssigned(parameter); + } + return false; } - return false; - } - @Nullable - public static HighlightInfo checkFinalVariableMightAlreadyHaveBeenAssignedTo(@Nonnull PsiVariable variable, - @Nonnull PsiReferenceExpression expression, - @Nonnull Map> finalVarProblems) { - if (!PsiUtil.isAccessedForWriting(expression)) { - return null; - } + @Nullable + public static HighlightInfo checkFinalVariableMightAlreadyHaveBeenAssignedTo( + @Nonnull PsiVariable variable, + @Nonnull PsiReferenceExpression expression, + @Nonnull Map> 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 codeBlockProblems = getFinalVariableProblemsInBlock(finalVarProblems, codeBlock); - - boolean alreadyAssigned = false; - for (ControlFlowUtil.VariableInfo variableInfo : codeBlockProblems) { - if (variableInfo.expression == expression) { - alreadyAssigned = true; - break; - } - } + 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 codeBlockProblems = getFinalVariableProblemsInBlock(finalVarProblems, codeBlock); - 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)) { + boolean alreadyAssigned = false; + for (ControlFlowUtil.VariableInfo variableInfo : codeBlockProblems) { + if (variableInfo.expression == expression) { 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 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 = JavaErrorBundle.message("variable.already.assigned", variable.getName()); - final HighlightInfo highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression).descriptionAndTooltip(description).create(); - QuickFixAction.registerQuickFixAction(highlightInfo, QuickFixFactory.getInstance() - .createModifierListFix(variable, PsiModifier.FINAL, false, false)); - QuickFixAction.registerQuickFixAction(highlightInfo, QuickFixFactory.getInstance().createDeferFinalAssignmentFix(variable, expression)); - return highlightInfo; - } + 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; + } + } - return null; - } - - @Nonnull - private static Collection getFinalVariableProblemsInBlock(@Nonnull Map> finalVarProblems, - @Nonnull PsiElement codeBlock) { - Collection codeBlockProblems = finalVarProblems.get(codeBlock); - if (codeBlockProblems == null) { - try { - final ControlFlow controlFlow = getControlFlowNoConstantEvaluate(codeBlock); - codeBlockProblems = ControlFlowUtil.getInitializedTwice(controlFlow); - } catch (AnalysisCanceledException e) { - codeBlockProblems = Collections.emptyList(); - } - finalVarProblems.put(codeBlock, codeBlockProblems); - } - return codeBlockProblems; - } - - - @Nullable - public static HighlightInfo checkFinalVariableInitializedInLoop(@Nonnull PsiReferenceExpression expression, @Nonnull PsiElement resolved) { - if (ControlFlowUtil.isVariableAssignedInLoop(expression, resolved)) { - String description = JavaErrorBundle.message("variable.assigned.in.loop", ((PsiVariable) resolved).getName()); - final HighlightInfo highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression).descriptionAndTooltip(description).create(); - QuickFixAction.registerQuickFixAction(highlightInfo, QuickFixFactory.getInstance() - .createModifierListFix((PsiVariable) resolved, PsiModifier.FINAL, false, false)); - return highlightInfo; - } - return null; - } - - - @Nullable - public static HighlightInfo checkCannotWriteToFinal(@Nonnull PsiExpression expression, @Nonnull 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; + 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 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 = JavaErrorBundle.message("variable.already.assigned", variable.getName()); + final HighlightInfo highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) + .range(expression) + .descriptionAndTooltip(description) + .create(); + QuickFixAction.registerQuickFixAction( + highlightInfo, + QuickFixFactory.getInstance().createModifierListFix(variable, PsiModifier.FINAL, false, false) + ); + QuickFixAction.registerQuickFixAction( + highlightInfo, + QuickFixFactory.getInstance().createDeferFinalAssignmentFix(variable, expression) + ); + return highlightInfo; + } + + 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 ? JavaErrorBundle.message("variable.not.initialized", name) : JavaErrorBundle.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, QuickFixFactory.getInstance() - .createModifierListFix(variable, PsiModifier.FINAL, false, false)); - } else { - QuickFixAction.registerQuickFixAction(highlightInfo, QuickFixFactory.getInstance() - .createVariableAccessFromInnerClassFix(variable, innerClass)); - } - return highlightInfo; + + @Nonnull + private static Collection getFinalVariableProblemsInBlock( + @Nonnull Map> finalVarProblems, + @Nonnull PsiElement codeBlock + ) { + Collection codeBlockProblems = finalVarProblems.get(codeBlock); + if (codeBlockProblems == null) { + try { + final ControlFlow controlFlow = getControlFlowNoConstantEvaluate(codeBlock); + codeBlockProblems = ControlFlowUtil.getInitializedTwice(controlFlow); + } + catch (AnalysisCanceledException e) { + codeBlockProblems = Collections.emptyList(); + } + finalVarProblems.put(codeBlock, codeBlockProblems); + } + return codeBlockProblems; } - return null; - } - private static boolean canWriteToFinal(@Nonnull PsiVariable variable, @Nonnull PsiExpression expression, @Nonnull PsiReferenceExpression reference, @Nonnull PsiFile containingFile) { - if (variable.hasInitializer()) { - return false; + @Nullable + public static HighlightInfo checkFinalVariableInitializedInLoop( + @Nonnull PsiReferenceExpression expression, + @Nonnull PsiElement resolved + ) { + if (ControlFlowUtil.isVariableAssignedInLoop(expression, resolved)) { + String description = JavaErrorBundle.message("variable.assigned.in.loop", ((PsiVariable)resolved).getName()); + final HighlightInfo highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) + .range(expression) + .descriptionAndTooltip(description) + .create(); + QuickFixAction.registerQuickFixAction(highlightInfo, QuickFixFactory.getInstance() + .createModifierListFix((PsiVariable)resolved, PsiModifier.FINAL, false, false)); + return highlightInfo; + } + return null; } - if (variable instanceof PsiParameter) { - return false; + + + @Nullable + public static HighlightInfo checkCannotWriteToFinal(@Nonnull PsiExpression expression, @Nonnull 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 + ? JavaErrorBundle.message("variable.not.initialized", name) + : JavaErrorBundle.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, QuickFixFactory.getInstance() + .createModifierListFix(variable, PsiModifier.FINAL, false, false)); + } + else { + QuickFixAction.registerQuickFixAction(highlightInfo, QuickFixFactory.getInstance() + .createVariableAccessFromInnerClassFix(variable, innerClass)); + } + return highlightInfo; + } + + return null; } - PsiElement innerClass = getInnerClassVariableReferencedFrom(variable, expression); - if (variable instanceof PsiField) { - // if inside some field initializer - if (HighlightUtil.findEnclosingFieldInitializer(expression) != null) { + + private static boolean canWriteToFinal( + @Nonnull PsiVariable variable, + @Nonnull PsiExpression expression, + @Nonnull PsiReferenceExpression reference, + @Nonnull PsiFile containingFile + ) { + if (variable.hasInitializer()) { + return false; + } + if (variable instanceof PsiParameter) { + return false; + } + PsiElement innerClass = getInnerClassVariableReferencedFrom(variable, expression); + if (variable instanceof PsiField) { + // if inside some field initializer + if (HighlightUtil.findEnclosingFieldInitializer(expression) != null) { + return true; + } + // assignment from within inner class is illegal always + PsiField field = (PsiField)variable; + if (innerClass != null && !containingFile.getManager().areElementsEquivalent(innerClass, field.getContainingClass())) { + return false; + } + final PsiMember enclosingCtrOrInitializer = PsiUtil.findEnclosingConstructorOrInitializer(expression); + return enclosingCtrOrInitializer != null && isSameField(enclosingCtrOrInitializer, field, reference, containingFile); + } + if (variable instanceof PsiLocalVariable) { + boolean isAccessedFromOtherClass = innerClass != null; + if (isAccessedFromOtherClass) { + return false; + } + } return true; - } - // assignment from within inner class is illegal always - PsiField field = (PsiField) variable; - if (innerClass != null && !containingFile.getManager().areElementsEquivalent(innerClass, field.getContainingClass())) { - return false; - } - final PsiMember enclosingCtrOrInitializer = PsiUtil.findEnclosingConstructorOrInitializer(expression); - return enclosingCtrOrInitializer != null && isSameField(enclosingCtrOrInitializer, field, reference, containingFile); - } - if (variable instanceof PsiLocalVariable) { - boolean isAccessedFromOtherClass = innerClass != null; - if (isAccessedFromOtherClass) { - return false; - } } - return true; - } - private static boolean isSameField(@Nonnull PsiMember enclosingCtrOrInitializer, @Nonnull PsiField field, @Nonnull PsiReferenceExpression reference, @Nonnull PsiFile containingFile) { - if (!containingFile.getManager().areElementsEquivalent(enclosingCtrOrInitializer.getContainingClass(), field.getContainingClass())) { - return false; + private static boolean isSameField( + @Nonnull PsiMember enclosingCtrOrInitializer, + @Nonnull PsiField field, + @Nonnull PsiReferenceExpression reference, + @Nonnull PsiFile containingFile + ) { + if (!containingFile.getManager() + .areElementsEquivalent(enclosingCtrOrInitializer.getContainingClass(), field.getContainingClass())) { + return false; + } + return LocalsOrMyInstanceFieldsControlFlowPolicy.isLocalOrMyInstanceReference(reference); } - return LocalsOrMyInstanceFieldsControlFlowPolicy.isLocalOrMyInstanceReference(reference); - } - @Nullable - public static HighlightInfo checkVariableMustBeFinal(@Nonnull PsiVariable variable, @Nonnull PsiJavaCodeReferenceElement context, @Nonnull LanguageLevel languageLevel) { - if (variable.hasModifierProperty(PsiModifier.FINAL)) { - return null; + @Nullable + public static HighlightInfo checkVariableMustBeFinal( + @Nonnull PsiVariable variable, + @Nonnull PsiJavaCodeReferenceElement context, + @Nonnull 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 = JavaErrorBundle.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, QuickFixFactory.getInstance() + .createVariableAccessFromInnerClassFix(variable, innerClass)); + return highlightInfo; + } + return checkWriteToFinalInsideLambda(variable, context); } - 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 = JavaErrorBundle.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, QuickFixFactory.getInstance() - .createVariableAccessFromInnerClassFix(variable, innerClass)); - return highlightInfo; - } - return checkWriteToFinalInsideLambda(variable, context); - } - - private static HighlightInfo checkWriteToFinalInsideLambda(@Nonnull PsiVariable variable, @Nonnull 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) { + private static HighlightInfo checkWriteToFinalInsideLambda( + @Nonnull PsiVariable variable, + @Nonnull 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 = JavaErrorBundle.message("lambda.variable.must.be.final"); + HighlightInfo highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) + .range(context) + .descriptionAndTooltip(text) + .create(); + QuickFixAction.registerQuickFixAction(highlightInfo, QuickFixFactory.getInstance() + .createVariableAccessFromInnerClassFix(variable, lambdaExpression)); + return highlightInfo; + } + } return null; - } - if (!isEffectivelyFinal(variable, lambdaExpression, context)) { - String text = JavaErrorBundle.message("lambda.variable.must.be.final"); - HighlightInfo highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(context).descriptionAndTooltip(text).create(); - QuickFixAction.registerQuickFixAction(highlightInfo, QuickFixFactory.getInstance() - .createVariableAccessFromInnerClassFix(variable, lambdaExpression)); - return highlightInfo; - } } - return null; - } - - public static boolean isEffectivelyFinal(@Nonnull PsiVariable variable, @Nonnull PsiElement scope, @Nullable PsiJavaCodeReferenceElement context) { - boolean effectivelyFinal; - if (variable instanceof PsiParameter) { - effectivelyFinal = notAccessedForWriting(variable, new LocalSearchScope(((PsiParameter) variable).getDeclarationScope())); - } else { - final ControlFlow controlFlow; - try { - PsiElement codeBlock = PsiUtil.getVariableCodeBlock(variable, context); - if (codeBlock == null) { - return true; - } - controlFlow = getControlFlow(codeBlock); - } catch (AnalysisCanceledException e) { - return true; - } - final List readBeforeWriteLocals = ControlFlowUtil.getReadBeforeWriteLocals(controlFlow); - for (PsiReferenceExpression expression : readBeforeWriteLocals) { - if (expression.resolve() == variable) { - return PsiUtil.isAccessedForReading(expression); + public static boolean isEffectivelyFinal( + @Nonnull PsiVariable variable, + @Nonnull PsiElement scope, + @Nullable PsiJavaCodeReferenceElement context + ) { + boolean effectivelyFinal; + if (variable instanceof PsiParameter) { + effectivelyFinal = notAccessedForWriting(variable, new LocalSearchScope(((PsiParameter)variable).getDeclarationScope())); } - } + else { + final ControlFlow controlFlow; + try { + PsiElement codeBlock = PsiUtil.getVariableCodeBlock(variable, context); + if (codeBlock == null) { + return true; + } + controlFlow = getControlFlow(codeBlock); + } + catch (AnalysisCanceledException e) { + return true; + } - final Collection initializedTwice = ControlFlowUtil.getInitializedTwice(controlFlow); - effectivelyFinal = !initializedTwice.contains(new ControlFlowUtil.VariableInfo(variable, null)); - if (effectivelyFinal) { - effectivelyFinal = notAccessedForWriting(variable, new LocalSearchScope(scope)); - } - } - return effectivelyFinal; - } + final List readBeforeWriteLocals = ControlFlowUtil.getReadBeforeWriteLocals(controlFlow); + for (PsiReferenceExpression expression : readBeforeWriteLocals) { + if (expression.resolve() == variable) { + return PsiUtil.isAccessedForReading(expression); + } + } - private static boolean notAccessedForWriting(@Nonnull PsiVariable variable, @Nonnull LocalSearchScope searchScope) { - for (PsiReference reference : ReferencesSearch.search(variable, searchScope)) { - final PsiElement element = reference.getElement(); - if (element instanceof PsiExpression && PsiUtil.isAccessedForWriting((PsiExpression) element)) { - return false; - } - } - return true; - } - - @Nullable - public static PsiElement getInnerClassVariableReferencedFrom(@Nonnull PsiVariable variable, @Nonnull PsiElement context) { - final PsiElement[] scope; - if (variable instanceof PsiResourceVariable) { - scope = ((PsiResourceVariable) variable).getDeclarationScope(); - } else if (variable instanceof PsiLocalVariable) { - final PsiElement parent = variable.getParent(); - scope = new PsiElement[]{parent != null ? parent.getParent() : null}; // code block or for statement - } else if (variable instanceof PsiParameter) { - scope = new PsiElement[]{((PsiParameter) variable).getDeclarationScope()}; - } else { - scope = new PsiElement[]{variable.getParent()}; + final Collection initializedTwice = ControlFlowUtil.getInitializedTwice(controlFlow); + effectivelyFinal = !initializedTwice.contains(new ControlFlowUtil.VariableInfo(variable, null)); + if (effectivelyFinal) { + effectivelyFinal = notAccessedForWriting(variable, new LocalSearchScope(scope)); + } + } + return effectivelyFinal; } - if (scope.length < 1 || scope[0] == null || scope[0].getContainingFile() != context.getContainingFile()) { - return null; + + private static boolean notAccessedForWriting(@Nonnull PsiVariable variable, @Nonnull LocalSearchScope searchScope) { + for (PsiReference reference : ReferencesSearch.search(variable, searchScope)) { + final PsiElement element = reference.getElement(); + if (element instanceof PsiExpression && PsiUtil.isAccessedForWriting((PsiExpression)element)) { + return false; + } + } + return true; } - PsiElement parent = context.getParent(); - PsiElement prevParent = context; - outer: - while (parent != null) { - for (PsiElement scopeElement : scope) { - if (parent.equals(scopeElement)) { - break outer; - } - } - if (parent instanceof PsiClass && !(prevParent instanceof PsiExpressionList && parent instanceof PsiAnonymousClass)) { - return parent; - } - if (parent instanceof PsiLambdaExpression) { - return parent; - } - prevParent = parent; - parent = parent.getParent(); + @Nullable + public static PsiElement getInnerClassVariableReferencedFrom(@Nonnull PsiVariable variable, @Nonnull PsiElement context) { + final PsiElement[] scope; + if (variable instanceof PsiResourceVariable) { + scope = ((PsiResourceVariable)variable).getDeclarationScope(); + } + else if (variable instanceof PsiLocalVariable) { + final PsiElement parent = variable.getParent(); + scope = new PsiElement[]{parent != null ? parent.getParent() : null}; // code block or for statement + } + else if (variable instanceof PsiParameter) { + scope = new PsiElement[]{((PsiParameter)variable).getDeclarationScope()}; + } + else { + scope = new PsiElement[]{variable.getParent()}; + } + if (scope.length < 1 || scope[0] == null || scope[0].getContainingFile() != context.getContainingFile()) { + return null; + } + + PsiElement parent = context.getParent(); + PsiElement prevParent = context; + outer: + while (parent != null) { + for (PsiElement scopeElement : scope) { + if (parent.equals(scopeElement)) { + break outer; + } + } + if (parent instanceof PsiClass && !(prevParent instanceof PsiExpressionList && parent instanceof PsiAnonymousClass)) { + return parent; + } + if (parent instanceof PsiLambdaExpression) { + return parent; + } + prevParent = parent; + parent = parent.getParent(); + } + return null; } - return null; - } - - - @Nullable - public static HighlightInfo checkInitializerCompleteNormally(@Nonnull PsiClassInitializer initializer) { - final PsiCodeBlock body = initializer.getBody(); - // unhandled exceptions already reported - try { - final ControlFlow controlFlow = getControlFlowNoConstantEvaluate(body); - final int completionReasons = ControlFlowUtil.getCompletionReasons(controlFlow, 0, controlFlow.getSize()); - if ((completionReasons & ControlFlowUtil.NORMAL_COMPLETION_REASON) == 0) { - String description = JavaErrorBundle.message("initializer.must.be.able.to.complete.normally"); - return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(body).descriptionAndTooltip(description).create(); - } - } catch (AnalysisCanceledException e) { - // incomplete code + + + @Nullable + public static HighlightInfo checkInitializerCompleteNormally(@Nonnull PsiClassInitializer initializer) { + final PsiCodeBlock body = initializer.getBody(); + // unhandled exceptions already reported + try { + final ControlFlow controlFlow = getControlFlowNoConstantEvaluate(body); + final int completionReasons = ControlFlowUtil.getCompletionReasons(controlFlow, 0, controlFlow.getSize()); + if ((completionReasons & ControlFlowUtil.NORMAL_COMPLETION_REASON) == 0) { + String description = JavaErrorBundle.message("initializer.must.be.able.to.complete.normally"); + return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) + .range(body) + .descriptionAndTooltip(description) + .create(); + } + } + catch (AnalysisCanceledException e) { + // incomplete code + } + return null; } - return null; - } } diff --git a/java-analysis-impl/src/main/java/com/intellij/java/analysis/impl/codeInsight/daemon/impl/analysis/HighlightNamesUtil.java b/java-analysis-impl/src/main/java/com/intellij/java/analysis/impl/codeInsight/daemon/impl/analysis/HighlightNamesUtil.java index b1991dc4ae..3c9b254dd7 100644 --- a/java-analysis-impl/src/main/java/com/intellij/java/analysis/impl/codeInsight/daemon/impl/analysis/HighlightNamesUtil.java +++ b/java-analysis-impl/src/main/java/com/intellij/java/analysis/impl/codeInsight/daemon/impl/analysis/HighlightNamesUtil.java @@ -46,322 +46,369 @@ import java.util.List; public class HighlightNamesUtil { - private static final Logger LOG = Logger.getInstance(HighlightNamesUtil.class); + private static final Logger LOG = Logger.getInstance(HighlightNamesUtil.class); - @Nullable - public static HighlightInfo highlightMethodName(@Nonnull PsiMethod method, @Nonnull PsiElement elementToHighlight, final boolean isDeclaration, @Nonnull TextAttributesScheme colorsScheme) { - return highlightMethodName(method, elementToHighlight, elementToHighlight.getTextRange(), colorsScheme, isDeclaration); - } - - /** - * @param methodOrClass method to highlight; class is passed instead of implicit constructor - */ - @Nullable - public static HighlightInfo highlightMethodName(@Nonnull PsiMember methodOrClass, - @Nonnull PsiElement elementToHighlight, - @Nonnull TextRange range, - @Nonnull TextAttributesScheme colorsScheme, - final boolean isDeclaration) { - boolean isInherited = false; - - if (!isDeclaration) { - if (isCalledOnThis(elementToHighlight)) { - final PsiClass containingClass = methodOrClass instanceof PsiMethod ? methodOrClass.getContainingClass() : null; - PsiClass enclosingClass = containingClass == null ? null : PsiTreeUtil.getParentOfType(elementToHighlight, PsiClass.class); - while (enclosingClass != null) { - isInherited = enclosingClass.isInheritor(containingClass, true); - if (isInherited) { - break; - } - enclosingClass = PsiTreeUtil.getParentOfType(enclosingClass, PsiClass.class, true); - } - } + @Nullable + public static HighlightInfo highlightMethodName( + @Nonnull PsiMethod method, + @Nonnull PsiElement elementToHighlight, + final boolean isDeclaration, + @Nonnull TextAttributesScheme colorsScheme + ) { + return highlightMethodName(method, elementToHighlight, elementToHighlight.getTextRange(), colorsScheme, isDeclaration); } - LOG.assertTrue(methodOrClass instanceof PsiMethod || !isDeclaration); - HighlightInfoType type = methodOrClass instanceof PsiMethod ? getMethodNameHighlightType((PsiMethod) methodOrClass, isDeclaration, isInherited) : JavaHighlightInfoTypes.CONSTRUCTOR_CALL; - if (type != null) { - TextAttributes attributes = mergeWithScopeAttributes(methodOrClass, type, colorsScheme); - HighlightInfo.Builder builder = HighlightInfo.newHighlightInfo(type).range(range); - if (attributes != null) { - builder.textAttributes(attributes); - } - return builder.createUnconditionally(); - } - return null; - } + /** + * @param methodOrClass method to highlight; class is passed instead of implicit constructor + */ + @Nullable + public static HighlightInfo highlightMethodName( + @Nonnull PsiMember methodOrClass, + @Nonnull PsiElement elementToHighlight, + @Nonnull TextRange range, + @Nonnull TextAttributesScheme colorsScheme, + final boolean isDeclaration + ) { + boolean isInherited = false; - private static boolean isCalledOnThis(@Nonnull PsiElement elementToHighlight) { - PsiMethodCallExpression methodCallExpression = PsiTreeUtil.getParentOfType(elementToHighlight, PsiMethodCallExpression.class); - if (methodCallExpression != null) { - PsiElement qualifier = methodCallExpression.getMethodExpression().getQualifier(); - if (qualifier == null || qualifier instanceof PsiThisExpression) { - return true; - } - } - return false; - } + if (!isDeclaration) { + if (isCalledOnThis(elementToHighlight)) { + final PsiClass containingClass = methodOrClass instanceof PsiMethod ? methodOrClass.getContainingClass() : null; + PsiClass enclosingClass = containingClass == null ? null : PsiTreeUtil.getParentOfType(elementToHighlight, PsiClass.class); + while (enclosingClass != null) { + isInherited = enclosingClass.isInheritor(containingClass, true); + if (isInherited) { + break; + } + enclosingClass = PsiTreeUtil.getParentOfType(enclosingClass, PsiClass.class, true); + } + } + } - private static TextAttributes mergeWithScopeAttributes(@Nullable PsiElement element, @Nonnull HighlightInfoType type, @Nonnull TextAttributesScheme colorsScheme) { - TextAttributes regularAttributes = SeverityRegistrarUtil.getAttributesByType(element, type, colorsScheme); - if (element == null) { - return regularAttributes; + LOG.assertTrue(methodOrClass instanceof PsiMethod || !isDeclaration); + HighlightInfoType type = methodOrClass instanceof PsiMethod + ? getMethodNameHighlightType((PsiMethod)methodOrClass, isDeclaration, isInherited) + : JavaHighlightInfoTypes.CONSTRUCTOR_CALL; + if (type != null) { + TextAttributes attributes = mergeWithScopeAttributes(methodOrClass, type, colorsScheme); + HighlightInfo.Builder builder = HighlightInfo.newHighlightInfo(type).range(range); + if (attributes != null) { + builder.textAttributes(attributes); + } + return builder.createUnconditionally(); + } + return null; } - TextAttributes scopeAttributes = getScopeAttributes(element, colorsScheme); - return TextAttributes.merge(scopeAttributes, regularAttributes); - } - @Nonnull - public static HighlightInfo highlightClassName(@Nullable PsiClass aClass, @Nonnull PsiElement elementToHighlight, @Nonnull TextAttributesScheme colorsScheme) { - TextRange range = elementToHighlight.getTextRange(); - if (elementToHighlight instanceof PsiJavaCodeReferenceElement) { - final PsiJavaCodeReferenceElement referenceElement = (PsiJavaCodeReferenceElement) elementToHighlight; - PsiElement identifier = referenceElement.getReferenceNameElement(); - if (identifier != null) { - range = identifier.getTextRange(); - } + private static boolean isCalledOnThis(@Nonnull PsiElement elementToHighlight) { + PsiMethodCallExpression methodCallExpression = PsiTreeUtil.getParentOfType(elementToHighlight, PsiMethodCallExpression.class); + if (methodCallExpression != null) { + PsiElement qualifier = methodCallExpression.getMethodExpression().getQualifier(); + if (qualifier == null || qualifier instanceof PsiThisExpression) { + return true; + } + } + return false; } - // This will highlight @ sign in annotation as well. - final PsiElement parent = elementToHighlight.getParent(); - if (parent instanceof PsiAnnotation) { - final PsiAnnotation psiAnnotation = (PsiAnnotation) parent; - range = new TextRange(psiAnnotation.getTextRange().getStartOffset(), range.getEndOffset()); + private static TextAttributes mergeWithScopeAttributes( + @Nullable PsiElement element, + @Nonnull HighlightInfoType type, + @Nonnull TextAttributesScheme colorsScheme + ) { + TextAttributes regularAttributes = SeverityRegistrarUtil.getAttributesByType(element, type, colorsScheme); + if (element == null) { + return regularAttributes; + } + TextAttributes scopeAttributes = getScopeAttributes(element, colorsScheme); + return TextAttributes.merge(scopeAttributes, regularAttributes); } - HighlightInfoType type = getClassNameHighlightType(aClass, elementToHighlight); - TextAttributes attributes = mergeWithScopeAttributes(aClass, type, colorsScheme); - HighlightInfo.Builder builder = HighlightInfo.newHighlightInfo(type).range(range); - if (attributes != null) { - builder.textAttributes(attributes); - } - return builder.createUnconditionally(); - } + @Nonnull + public static HighlightInfo highlightClassName( + @Nullable PsiClass aClass, + @Nonnull PsiElement elementToHighlight, + @Nonnull TextAttributesScheme colorsScheme + ) { + TextRange range = elementToHighlight.getTextRange(); + if (elementToHighlight instanceof PsiJavaCodeReferenceElement) { + final PsiJavaCodeReferenceElement referenceElement = (PsiJavaCodeReferenceElement)elementToHighlight; + PsiElement identifier = referenceElement.getReferenceNameElement(); + if (identifier != null) { + range = identifier.getTextRange(); + } + } - @Nullable - public static HighlightInfo highlightVariableName(@Nonnull PsiVariable variable, @Nonnull PsiElement elementToHighlight, @Nonnull TextAttributesScheme colorsScheme) { - HighlightInfoType varType = getVariableNameHighlightType(variable); - if (varType == null) { - return null; - } - if (variable instanceof PsiField) { - TextAttributes attributes = mergeWithScopeAttributes(variable, varType, colorsScheme); - HighlightInfo.Builder builder = HighlightInfo.newHighlightInfo(varType).range(elementToHighlight.getTextRange()); - if (attributes != null) { - builder.textAttributes(attributes); - } - return builder.createUnconditionally(); + // This will highlight @ sign in annotation as well. + final PsiElement parent = elementToHighlight.getParent(); + if (parent instanceof PsiAnnotation) { + final PsiAnnotation psiAnnotation = (PsiAnnotation)parent; + range = new TextRange(psiAnnotation.getTextRange().getStartOffset(), range.getEndOffset()); + } + + HighlightInfoType type = getClassNameHighlightType(aClass, elementToHighlight); + TextAttributes attributes = mergeWithScopeAttributes(aClass, type, colorsScheme); + HighlightInfo.Builder builder = HighlightInfo.newHighlightInfo(type).range(range); + if (attributes != null) { + builder.textAttributes(attributes); + } + return builder.createUnconditionally(); } - HighlightInfo.Builder builder = HighlightInfo.newHighlightInfo(varType).range(elementToHighlight); - return RainbowHighlighter.isRainbowEnabledWithInheritance(colorsScheme, JavaLanguage.INSTANCE) ? builder.createUnconditionally() : builder.create(); - } + @Nullable + public static HighlightInfo highlightVariableName( + @Nonnull PsiVariable variable, + @Nonnull PsiElement elementToHighlight, + @Nonnull TextAttributesScheme colorsScheme + ) { + HighlightInfoType varType = getVariableNameHighlightType(variable); + if (varType == null) { + return null; + } + if (variable instanceof PsiField) { + TextAttributes attributes = mergeWithScopeAttributes(variable, varType, colorsScheme); + HighlightInfo.Builder builder = HighlightInfo.newHighlightInfo(varType).range(elementToHighlight.getTextRange()); + if (attributes != null) { + builder.textAttributes(attributes); + } + return builder.createUnconditionally(); + } - @Nullable - public static HighlightInfo highlightClassNameInQualifier(@Nonnull PsiJavaCodeReferenceElement element, @Nonnull TextAttributesScheme colorsScheme) { - PsiElement qualifierExpression = element.getQualifier(); - if (qualifierExpression instanceof PsiJavaCodeReferenceElement) { - PsiElement resolved = ((PsiJavaCodeReferenceElement) qualifierExpression).resolve(); - if (resolved instanceof PsiClass) { - return highlightClassName((PsiClass) resolved, qualifierExpression, colorsScheme); - } + HighlightInfo.Builder builder = HighlightInfo.newHighlightInfo(varType).range(elementToHighlight); + return RainbowHighlighter.isRainbowEnabledWithInheritance( + colorsScheme, + JavaLanguage.INSTANCE + ) ? builder.createUnconditionally() : builder.create(); } - return null; - } - private static HighlightInfoType getMethodNameHighlightType(@Nonnull PsiMethod method, boolean isDeclaration, boolean isInheritedMethod) { - if (method.isConstructor()) { - return isDeclaration ? JavaHighlightInfoTypes.CONSTRUCTOR_DECLARATION : JavaHighlightInfoTypes.CONSTRUCTOR_CALL; - } - if (isDeclaration) { - return JavaHighlightInfoTypes.METHOD_DECLARATION; - } - if (method.hasModifierProperty(PsiModifier.STATIC)) { - return JavaHighlightInfoTypes.STATIC_METHOD; - } - if (isInheritedMethod) { - return JavaHighlightInfoTypes.INHERITED_METHOD; - } - if (method.hasModifierProperty(PsiModifier.ABSTRACT)) { - return JavaHighlightInfoTypes.ABSTRACT_METHOD; + @Nullable + public static HighlightInfo highlightClassNameInQualifier( + @Nonnull PsiJavaCodeReferenceElement element, + @Nonnull TextAttributesScheme colorsScheme + ) { + PsiElement qualifierExpression = element.getQualifier(); + if (qualifierExpression instanceof PsiJavaCodeReferenceElement) { + PsiElement resolved = ((PsiJavaCodeReferenceElement)qualifierExpression).resolve(); + if (resolved instanceof PsiClass) { + return highlightClassName((PsiClass)resolved, qualifierExpression, colorsScheme); + } + } + return null; } - return JavaHighlightInfoTypes.METHOD_CALL; - } - @Nullable - private static HighlightInfoType getVariableNameHighlightType(@Nonnull PsiVariable var) { - if (var instanceof PsiLocalVariable || var instanceof PsiParameter && ((PsiParameter) var).getDeclarationScope() instanceof PsiForeachStatement) { - return JavaHighlightInfoTypes.LOCAL_VARIABLE; - } - if (var instanceof PsiField) { - return var.hasModifierProperty(PsiModifier.STATIC) ? var.hasModifierProperty(PsiModifier.FINAL) ? JavaHighlightInfoTypes.STATIC_FINAL_FIELD : JavaHighlightInfoTypes.STATIC_FIELD : var - .hasModifierProperty(PsiModifier.FINAL) ? JavaHighlightInfoTypes.INSTANCE_FINAL_FIELD : JavaHighlightInfoTypes.INSTANCE_FIELD; - } - if (var instanceof PsiParameter) { - return ((PsiParameter) var).getDeclarationScope() instanceof PsiLambdaExpression ? JavaHighlightInfoTypes.LAMBDA_PARAMETER : JavaHighlightInfoTypes.PARAMETER; + private static HighlightInfoType getMethodNameHighlightType( + @Nonnull PsiMethod method, + boolean isDeclaration, + boolean isInheritedMethod + ) { + if (method.isConstructor()) { + return isDeclaration ? JavaHighlightInfoTypes.CONSTRUCTOR_DECLARATION : JavaHighlightInfoTypes.CONSTRUCTOR_CALL; + } + if (isDeclaration) { + return JavaHighlightInfoTypes.METHOD_DECLARATION; + } + if (method.hasModifierProperty(PsiModifier.STATIC)) { + return JavaHighlightInfoTypes.STATIC_METHOD; + } + if (isInheritedMethod) { + return JavaHighlightInfoTypes.INHERITED_METHOD; + } + if (method.hasModifierProperty(PsiModifier.ABSTRACT)) { + return JavaHighlightInfoTypes.ABSTRACT_METHOD; + } + return JavaHighlightInfoTypes.METHOD_CALL; } - return null; - } - @Nonnull - private static HighlightInfoType getClassNameHighlightType(@Nullable PsiClass aClass, @Nullable PsiElement element) { - if (element instanceof PsiJavaCodeReferenceElement && element.getParent() instanceof PsiAnonymousClass) { - return JavaHighlightInfoTypes.ANONYMOUS_CLASS_NAME; - } - if (aClass != null) { - if (aClass.isAnnotationType()) { - return JavaHighlightInfoTypes.ANNOTATION_NAME; - } - if (aClass.isInterface()) { - return JavaHighlightInfoTypes.INTERFACE_NAME; - } - if (aClass.isEnum()) { - return JavaHighlightInfoTypes.ENUM_NAME; - } - if (aClass instanceof PsiTypeParameter) { - return JavaHighlightInfoTypes.TYPE_PARAMETER_NAME; - } - final PsiModifierList modList = aClass.getModifierList(); - if (modList != null && modList.hasModifierProperty(PsiModifier.ABSTRACT)) { - return JavaHighlightInfoTypes.ABSTRACT_CLASS_NAME; - } + @Nullable + private static HighlightInfoType getVariableNameHighlightType(@Nonnull PsiVariable var) { + if (var instanceof PsiLocalVariable + || var instanceof PsiParameter && ((PsiParameter)var).getDeclarationScope() instanceof PsiForeachStatement) { + return JavaHighlightInfoTypes.LOCAL_VARIABLE; + } + if (var instanceof PsiField) { + return var.hasModifierProperty(PsiModifier.STATIC) + ? var.hasModifierProperty(PsiModifier.FINAL) ? JavaHighlightInfoTypes.STATIC_FINAL_FIELD : JavaHighlightInfoTypes.STATIC_FIELD + : var.hasModifierProperty(PsiModifier.FINAL) ? JavaHighlightInfoTypes.INSTANCE_FINAL_FIELD : JavaHighlightInfoTypes.INSTANCE_FIELD; + } + if (var instanceof PsiParameter) { + return ((PsiParameter)var).getDeclarationScope() instanceof PsiLambdaExpression + ? JavaHighlightInfoTypes.LAMBDA_PARAMETER + : JavaHighlightInfoTypes.PARAMETER; + } + return null; } - // use class by default - return JavaHighlightInfoTypes.CLASS_NAME; - } - @Nullable - public static HighlightInfo highlightReassignedVariable(@Nonnull PsiVariable variable, @Nonnull PsiElement elementToHighlight) { - if (variable instanceof PsiLocalVariable) { - return HighlightInfo.newHighlightInfo(JavaHighlightInfoTypes.REASSIGNED_LOCAL_VARIABLE).range(elementToHighlight).create(); - } - if (variable instanceof PsiParameter) { - return HighlightInfo.newHighlightInfo(JavaHighlightInfoTypes.REASSIGNED_PARAMETER).range(elementToHighlight).create(); + @Nonnull + private static HighlightInfoType getClassNameHighlightType(@Nullable PsiClass aClass, @Nullable PsiElement element) { + if (element instanceof PsiJavaCodeReferenceElement && element.getParent() instanceof PsiAnonymousClass) { + return JavaHighlightInfoTypes.ANONYMOUS_CLASS_NAME; + } + if (aClass != null) { + if (aClass.isAnnotationType()) { + return JavaHighlightInfoTypes.ANNOTATION_NAME; + } + if (aClass.isInterface()) { + return JavaHighlightInfoTypes.INTERFACE_NAME; + } + if (aClass.isEnum()) { + return JavaHighlightInfoTypes.ENUM_NAME; + } + if (aClass instanceof PsiTypeParameter) { + return JavaHighlightInfoTypes.TYPE_PARAMETER_NAME; + } + final PsiModifierList modList = aClass.getModifierList(); + if (modList != null && modList.hasModifierProperty(PsiModifier.ABSTRACT)) { + return JavaHighlightInfoTypes.ABSTRACT_CLASS_NAME; + } + } + // use class by default + return JavaHighlightInfoTypes.CLASS_NAME; } - return null; - } - private static TextAttributes getScopeAttributes(@Nonnull PsiElement element, @Nonnull TextAttributesScheme colorsScheme) { - PsiFile file = element.getContainingFile(); - if (file == null) { - return null; - } - TextAttributes result = null; - DependencyValidationManager validationManager = DependencyValidationManager.getInstance(file.getProject()); - List> scopes = validationManager.getScopeBasedHighlightingCachedScopes(); - for (Pair scope : scopes) { - final NamedScope namedScope = scope.getFirst(); - final TextAttributesKey scopeKey = ScopeAttributesUtil.getScopeTextAttributeKey(namedScope.getName()); - final TextAttributes attributes = colorsScheme.getAttributes(scopeKey); - if (attributes == null || attributes.isEmpty()) { - continue; - } - final PackageSet packageSet = namedScope.getValue(); - if (packageSet != null && packageSet.contains(file.getVirtualFile(), file.getProject(), scope.getSecond())) { - result = TextAttributes.merge(attributes, result); - } + @Nullable + public static HighlightInfo highlightReassignedVariable(@Nonnull PsiVariable variable, @Nonnull PsiElement elementToHighlight) { + if (variable instanceof PsiLocalVariable) { + return HighlightInfo.newHighlightInfo(JavaHighlightInfoTypes.REASSIGNED_LOCAL_VARIABLE) + .range(elementToHighlight) + .create(); + } + if (variable instanceof PsiParameter) { + return HighlightInfo.newHighlightInfo(JavaHighlightInfoTypes.REASSIGNED_PARAMETER) + .range(elementToHighlight) + .create(); + } + return null; } - return result; - } - @Nonnull - public static TextRange getMethodDeclarationTextRange(@Nonnull PsiMethod method) { - if (method instanceof SyntheticElement) { - return TextRange.EMPTY_RANGE; + private static TextAttributes getScopeAttributes(@Nonnull PsiElement element, @Nonnull TextAttributesScheme colorsScheme) { + PsiFile file = element.getContainingFile(); + if (file == null) { + return null; + } + TextAttributes result = null; + DependencyValidationManager validationManager = DependencyValidationManager.getInstance(file.getProject()); + List> scopes = validationManager.getScopeBasedHighlightingCachedScopes(); + for (Pair scope : scopes) { + final NamedScope namedScope = scope.getFirst(); + final TextAttributesKey scopeKey = ScopeAttributesUtil.getScopeTextAttributeKey(namedScope.getName()); + final TextAttributes attributes = colorsScheme.getAttributes(scopeKey); + if (attributes == null || attributes.isEmpty()) { + continue; + } + final PackageSet packageSet = namedScope.getValue(); + if (packageSet != null && packageSet.contains(file.getVirtualFile(), file.getProject(), scope.getSecond())) { + result = TextAttributes.merge(attributes, result); + } + } + return result; } - int start = stripAnnotationsFromModifierList(method.getModifierList()); - final TextRange throwsRange = method.getThrowsList().getTextRange(); - LOG.assertTrue(throwsRange != null, method); - int end = throwsRange.getEndOffset(); - return new TextRange(start, end); - } - - @Nonnull - public static TextRange getFieldDeclarationTextRange(@Nonnull PsiField field) { - int start = stripAnnotationsFromModifierList(field.getModifierList()); - int end = field.getNameIdentifier().getTextRange().getEndOffset(); - return new TextRange(start, end); - } - @Nonnull - public static TextRange getClassDeclarationTextRange(@Nonnull PsiClass aClass) { - if (aClass instanceof PsiEnumConstantInitializer) { - return ((PsiEnumConstantInitializer) aClass).getEnumConstant().getNameIdentifier().getTextRange(); - } - final PsiElement psiElement = aClass instanceof PsiAnonymousClass ? ((PsiAnonymousClass) aClass).getBaseClassReference() : aClass.getModifierList() == null ? aClass.getNameIdentifier() : - aClass.getModifierList(); - if (psiElement == null) { - return new TextRange(aClass.getTextRange().getStartOffset(), aClass.getTextRange().getStartOffset()); - } - int start = stripAnnotationsFromModifierList(psiElement); - PsiElement endElement = aClass instanceof PsiAnonymousClass ? ((PsiAnonymousClass) aClass).getBaseClassReference() : aClass.getImplementsList(); - if (endElement == null) { - endElement = aClass.getNameIdentifier(); + @Nonnull + public static TextRange getMethodDeclarationTextRange(@Nonnull PsiMethod method) { + if (method instanceof SyntheticElement) { + return TextRange.EMPTY_RANGE; + } + int start = stripAnnotationsFromModifierList(method.getModifierList()); + final TextRange throwsRange = method.getThrowsList().getTextRange(); + LOG.assertTrue(throwsRange != null, method); + int end = throwsRange.getEndOffset(); + return new TextRange(start, end); } - TextRange endTextRange = endElement == null ? TextRange.EMPTY_RANGE : endElement.getTextRange(); - int end = endTextRange == TextRange.EMPTY_RANGE ? start : endTextRange.getEndOffset(); - return new TextRange(start, end); - } - private static int stripAnnotationsFromModifierList(@Nonnull PsiElement element) { - TextRange textRange = element.getTextRange(); - if (textRange == TextRange.EMPTY_RANGE) { - return 0; - } - PsiAnnotation lastAnnotation = null; - for (PsiElement child : element.getChildren()) { - if (child instanceof PsiAnnotation) { - lastAnnotation = (PsiAnnotation) child; - } - } - if (lastAnnotation == null) { - return textRange.getStartOffset(); - } - ASTNode node = lastAnnotation.getNode(); - if (node != null) { - do { - node = TreeUtil.nextLeaf(node); - } - while (node != null && ElementType.JAVA_COMMENT_OR_WHITESPACE_BIT_SET.contains(node.getElementType())); + @Nonnull + public static TextRange getFieldDeclarationTextRange(@Nonnull PsiField field) { + int start = stripAnnotationsFromModifierList(field.getModifierList()); + int end = field.getNameIdentifier().getTextRange().getEndOffset(); + return new TextRange(start, end); } - if (node != null) { - return node.getTextRange().getStartOffset(); + + @Nonnull + public static TextRange getClassDeclarationTextRange(@Nonnull PsiClass aClass) { + if (aClass instanceof PsiEnumConstantInitializer) { + return ((PsiEnumConstantInitializer)aClass).getEnumConstant().getNameIdentifier().getTextRange(); + } + final PsiElement psiElement = + aClass instanceof PsiAnonymousClass ? ((PsiAnonymousClass)aClass).getBaseClassReference() : aClass.getModifierList() == null ? aClass.getNameIdentifier() : + aClass.getModifierList(); + if (psiElement == null) { + return new TextRange(aClass.getTextRange().getStartOffset(), aClass.getTextRange().getStartOffset()); + } + int start = stripAnnotationsFromModifierList(psiElement); + PsiElement endElement = + aClass instanceof PsiAnonymousClass ? ((PsiAnonymousClass)aClass).getBaseClassReference() : aClass.getImplementsList(); + if (endElement == null) { + endElement = aClass.getNameIdentifier(); + } + TextRange endTextRange = endElement == null ? TextRange.EMPTY_RANGE : endElement.getTextRange(); + int end = endTextRange == TextRange.EMPTY_RANGE ? start : endTextRange.getEndOffset(); + return new TextRange(start, end); } - return textRange.getStartOffset(); - } - public static HighlightInfo highlightPackage(@Nonnull PsiElement resolved, @Nonnull PsiJavaCodeReferenceElement elementToHighlight, @Nonnull TextAttributesScheme scheme) { - PsiElement referenceNameElement = elementToHighlight.getReferenceNameElement(); - TextRange range; - if (referenceNameElement == null) { - range = elementToHighlight.getTextRange(); - } else { - PsiElement nextSibling = PsiTreeUtil.nextLeaf(referenceNameElement); - if (nextSibling != null && nextSibling.getTextRange().isEmpty()) { - // empty PsiReferenceParameterList - nextSibling = PsiTreeUtil.nextLeaf(nextSibling); - } - if (nextSibling instanceof PsiJavaToken && ((PsiJavaToken) nextSibling).getTokenType() == JavaTokenType.DOT) { - range = new TextRange(referenceNameElement.getTextRange().getStartOffset(), nextSibling.getTextRange().getEndOffset()); - } else { - range = referenceNameElement.getTextRange(); - } + private static int stripAnnotationsFromModifierList(@Nonnull PsiElement element) { + TextRange textRange = element.getTextRange(); + if (textRange == TextRange.EMPTY_RANGE) { + return 0; + } + PsiAnnotation lastAnnotation = null; + for (PsiElement child : element.getChildren()) { + if (child instanceof PsiAnnotation) { + lastAnnotation = (PsiAnnotation)child; + } + } + if (lastAnnotation == null) { + return textRange.getStartOffset(); + } + ASTNode node = lastAnnotation.getNode(); + if (node != null) { + do { + node = TreeUtil.nextLeaf(node); + } + while (node != null && ElementType.JAVA_COMMENT_OR_WHITESPACE_BIT_SET.contains(node.getElementType())); + } + if (node != null) { + return node.getTextRange().getStartOffset(); + } + return textRange.getStartOffset(); } - HighlightInfoType type = JavaHighlightInfoTypes.PACKAGE_NAME; - TextAttributes attributes = mergeWithScopeAttributes(resolved, type, scheme); - HighlightInfo.Builder builder = HighlightInfo.newHighlightInfo(type).range(range); - if (attributes != null) { - builder.textAttributes(attributes); + + public static HighlightInfo highlightPackage( + @Nonnull PsiElement resolved, + @Nonnull PsiJavaCodeReferenceElement elementToHighlight, + @Nonnull TextAttributesScheme scheme + ) { + PsiElement referenceNameElement = elementToHighlight.getReferenceNameElement(); + TextRange range; + if (referenceNameElement == null) { + range = elementToHighlight.getTextRange(); + } + else { + PsiElement nextSibling = PsiTreeUtil.nextLeaf(referenceNameElement); + if (nextSibling != null && nextSibling.getTextRange().isEmpty()) { + // empty PsiReferenceParameterList + nextSibling = PsiTreeUtil.nextLeaf(nextSibling); + } + if (nextSibling instanceof PsiJavaToken && ((PsiJavaToken)nextSibling).getTokenType() == JavaTokenType.DOT) { + range = new TextRange(referenceNameElement.getTextRange().getStartOffset(), nextSibling.getTextRange().getEndOffset()); + } + else { + range = referenceNameElement.getTextRange(); + } + } + HighlightInfoType type = JavaHighlightInfoTypes.PACKAGE_NAME; + TextAttributes attributes = mergeWithScopeAttributes(resolved, type, scheme); + HighlightInfo.Builder builder = HighlightInfo.newHighlightInfo(type).range(range); + if (attributes != null) { + builder.textAttributes(attributes); + } + return builder.createUnconditionally(); } - return builder.createUnconditionally(); - } - @Nonnull - private static HighlightInfo.Builder nameBuilder(@Nonnull HighlightInfoType type) { - return HighlightInfo.newHighlightInfo(type)/*.toolId(JavaNamesHighlightVisitor.class)*/; - } + @Nonnull + private static HighlightInfo.Builder nameBuilder(@Nonnull HighlightInfoType type) { + return HighlightInfo.newHighlightInfo(type)/*.toolId(JavaNamesHighlightVisitor.class)*/; + } - static HighlightInfo highlightKeyword(@Nonnull PsiKeyword keyword) { - return nameBuilder(JavaHighlightInfoTypes.JAVA_KEYWORD).range(keyword).create(); - } + static HighlightInfo highlightKeyword(@Nonnull PsiKeyword keyword) { + return nameBuilder(JavaHighlightInfoTypes.JAVA_KEYWORD).range(keyword).create(); + } } From b0c547e75f67939daa96d707cb06a8ca3adde451 Mon Sep 17 00:00:00 2001 From: UNV Date: Sun, 4 May 2025 01:23:27 +0300 Subject: [PATCH 2/4] Refactoring and localizing users of EP_NAME (part 7). --- .../impl/analysis/GenericsHighlightUtil.java | 4 + .../analysis/HighlightControlFlowUtil.java | 184 +++++++++--------- .../impl/analysis/HighlightNamesUtil.java | 122 ++++++------ .../impl/analysis/HighlightVisitorImpl.java | 79 ++++---- 4 files changed, 204 insertions(+), 185 deletions(-) diff --git a/java-analysis-impl/src/main/java/com/intellij/java/analysis/impl/codeInsight/daemon/impl/analysis/GenericsHighlightUtil.java b/java-analysis-impl/src/main/java/com/intellij/java/analysis/impl/codeInsight/daemon/impl/analysis/GenericsHighlightUtil.java index 70a37820c6..90f1e2326d 100644 --- a/java-analysis-impl/src/main/java/com/intellij/java/analysis/impl/codeInsight/daemon/impl/analysis/GenericsHighlightUtil.java +++ b/java-analysis-impl/src/main/java/com/intellij/java/analysis/impl/codeInsight/daemon/impl/analysis/GenericsHighlightUtil.java @@ -414,6 +414,7 @@ else if (referenceElements.length != 0 && element != referenceElements[0] return null; } + @RequiredReadAction public static HighlightInfo checkInterfaceMultipleInheritance(PsiClass aClass) { PsiClassType[] types = aClass.getSuperTypes(); if (types.length < 2) { @@ -1587,6 +1588,7 @@ public static HighlightInfo checkGenericCannotExtendException(PsiReferenceList l return null; } + @RequiredReadAction public static HighlightInfo checkEnumMustNotBeLocal(PsiClass aClass) { if (!aClass.isEnum()) { return null; @@ -1602,6 +1604,7 @@ public static HighlightInfo checkEnumMustNotBeLocal(PsiClass aClass) { return null; } + @RequiredReadAction public static HighlightInfo checkEnumWithoutConstantsCantHaveAbstractMethods(PsiClass aClass) { if (!aClass.isEnum()) { return null; @@ -1787,6 +1790,7 @@ public static HighlightInfo checkInferredIntersections(PsiSubstitutor substituto return null; } + @RequiredReadAction public static HighlightInfo areSupersAccessible(@Nonnull PsiClass aClass) { return areSupersAccessible(aClass, aClass.getResolveScope(), HighlightNamesUtil.getClassDeclarationTextRange(aClass), true); } diff --git a/java-analysis-impl/src/main/java/com/intellij/java/analysis/impl/codeInsight/daemon/impl/analysis/HighlightControlFlowUtil.java b/java-analysis-impl/src/main/java/com/intellij/java/analysis/impl/codeInsight/daemon/impl/analysis/HighlightControlFlowUtil.java index 4a3f902a82..6cb0082369 100644 --- a/java-analysis-impl/src/main/java/com/intellij/java/analysis/impl/codeInsight/daemon/impl/analysis/HighlightControlFlowUtil.java +++ b/java-analysis-impl/src/main/java/com/intellij/java/analysis/impl/codeInsight/daemon/impl/analysis/HighlightControlFlowUtil.java @@ -25,10 +25,11 @@ import com.intellij.java.language.psi.augment.PsiAugmentProvider; import com.intellij.java.language.psi.util.FileTypeUtils; import com.intellij.java.language.psi.util.PsiUtil; +import consulo.annotation.access.RequiredReadAction; import consulo.application.dumb.IndexNotReadyException; -import consulo.application.util.function.Processor; import consulo.document.util.TextRange; import consulo.java.analysis.impl.JavaQuickFixBundle; +import consulo.java.language.impl.localize.JavaErrorLocalize; import consulo.language.ast.IElementType; import consulo.language.editor.intention.QuickFixAction; import consulo.language.editor.rawHighlight.HighlightInfo; @@ -39,6 +40,7 @@ import consulo.language.psi.scope.LocalSearchScope; import consulo.language.psi.search.ReferencesSearch; import consulo.language.psi.util.PsiTreeUtil; +import consulo.localize.LocalizeValue; import consulo.util.collection.ContainerUtil; import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; @@ -58,6 +60,7 @@ private HighlightControlFlowUtil() { } @Nullable + @RequiredReadAction public static HighlightInfo checkMissingReturnStatement(@Nullable PsiCodeBlock body, @Nullable PsiType returnType) { if (body == null || returnType == null || PsiType.VOID.equals(returnType.getDeepComponentType())) { return null; @@ -69,15 +72,12 @@ public static HighlightInfo checkMissingReturnStatement(@Nullable PsiCodeBlock b ControlFlow controlFlow = getControlFlowNoConstantEvaluate(body); if (!ControlFlowUtil.returnPresent(controlFlow)) { PsiJavaToken rBrace = body.getRBrace(); - PsiElement context = rBrace == null ? body.getLastChild() : rBrace; - String message = JavaErrorBundle.message("missing.return.statement"); HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) - .range(context) - .descriptionAndTooltip(message) + .range(rBrace == null ? body.getLastChild() : rBrace) + .descriptionAndTooltip(JavaErrorLocalize.missingReturnStatement()) .create(); PsiElement parent = body.getParent(); - if (parent instanceof PsiMethod) { - PsiMethod method = (PsiMethod)parent; + if (parent instanceof PsiMethod method) { QuickFixAction.registerQuickFixAction(info, QuickFixFactory.getInstance().createAddReturnFix(method)); QuickFixAction.registerQuickFixAction( info, @@ -105,6 +105,7 @@ private static ControlFlow getControlFlow(@Nonnull PsiElement context) throws An return ControlFlowFactory.getInstance(context.getProject()).getControlFlow(context, policy); } + @RequiredReadAction public static HighlightInfo checkUnreachableStatement(@Nullable PsiCodeBlock codeBlock) { if (codeBlock == null) { return null; @@ -113,23 +114,24 @@ public static HighlightInfo checkUnreachableStatement(@Nullable PsiCodeBlock cod // see JLS 14.20 Unreachable Statements try { AllVariablesControlFlowPolicy policy = AllVariablesControlFlowPolicy.getInstance(); - final ControlFlow controlFlow = ControlFlowFactory.getControlFlow(codeBlock, policy, ControlFlowOptions.NO_CONST_EVALUATE); - final PsiElement unreachableStatement = ControlFlowUtil.getUnreachableStatement(controlFlow); + ControlFlow controlFlow = ControlFlowFactory.getControlFlow(codeBlock, policy, ControlFlowOptions.NO_CONST_EVALUATE); + PsiElement unreachableStatement = ControlFlowUtil.getUnreachableStatement(controlFlow); if (unreachableStatement != null) { - String description = JavaErrorBundle.message("unreachable.statement"); PsiElement keyword = null; if (unreachableStatement instanceof PsiIfStatement || unreachableStatement instanceof PsiSwitchBlock || unreachableStatement instanceof PsiLoopStatement) { keyword = unreachableStatement.getFirstChild(); } - final PsiElement element = keyword != null ? keyword : unreachableStatement; - final HighlightInfo info = - HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(element).descriptionAndTooltip(description).create(); - QuickFixAction.registerQuickFixAction( - info, QuickFixFactory.getInstance() - .createDeleteFix(unreachableStatement, JavaQuickFixBundle.message("delete.unreachable.statement.fix.text"))); - return info; + PsiElement element = keyword != null ? keyword : unreachableStatement; + return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) + .range(element) + .descriptionAndTooltip(JavaErrorLocalize.unreachableStatement()) + .registerFix(QuickFixFactory.getInstance().createDeleteFix( + unreachableStatement, + JavaQuickFixBundle.message("delete.unreachable.statement.fix.text") + ), null, null, null, null) + .create(); } } catch (AnalysisCanceledException | IndexNotReadyException e) { @@ -142,7 +144,7 @@ public static boolean isFieldInitializedAfterObjectConstruction(@Nonnull PsiFiel if (field.hasInitializer()) { return true; } - final boolean isFieldStatic = field.hasModifierProperty(PsiModifier.STATIC); + final boolean isFieldStatic = field.isStatic(); final PsiClass aClass = field.getContainingClass(); if (aClass != null) { // field might be assigned in the other field initializers @@ -201,7 +203,7 @@ private static boolean isFieldInitializedInOtherFieldInitializer( PsiField[] fields = aClass.getFields(); for (PsiField psiField : fields) { if (psiField != field - && psiField.hasModifierProperty(PsiModifier.STATIC) == fieldStatic + && psiField.isStatic() == fieldStatic && variableDefinitelyAssignedIn(field, psiField) && condition.test(psiField)) { return true; @@ -226,12 +228,13 @@ public static boolean isAssigned(@Nonnull PsiParameter parameter) { return processor.isWriteRefFound(); } - private static class ParamWriteProcessor implements Processor { + private static class ParamWriteProcessor implements Predicate { private volatile boolean myIsWriteRefFound; @Override - public boolean process(PsiReference reference) { - final PsiElement element = reference.getElement(); + @RequiredReadAction + public boolean test(PsiReference reference) { + PsiElement element = reference.getElement(); if (element instanceof PsiReferenceExpression && PsiUtil.isAccessedForWriting((PsiExpression)element)) { myIsWriteRefFound = true; return false; @@ -270,15 +273,16 @@ private static boolean variableDefinitelyNotAssignedIn(@Nonnull PsiVariable vari } @Nullable + @RequiredReadAction public static HighlightInfo checkFinalFieldInitialized(@Nonnull PsiField field) { - if (!field.hasModifierProperty(PsiModifier.FINAL)) { + if (!field.isFinal()) { return null; } if (isFieldInitializedAfterObjectConstruction(field)) { return null; } - String description = JavaErrorBundle.message("variable.not.initialized", field.getName()); + String description = JavaErrorLocalize.variableNotInitialized(field.getName()).get(); TextRange range = HighlightNamesUtil.getFieldDeclarationTextRange(field); HighlightInfo highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) .range(range) @@ -305,7 +309,7 @@ public static HighlightInfo checkFinalFieldInitialized(@Nonnull PsiField field) return highlightInfo; } - + @RequiredReadAction public static HighlightInfo checkVariableInitializedBeforeUsage( @Nonnull PsiReferenceExpression expression, @Nonnull PsiVariable variable, @@ -315,6 +319,7 @@ public static HighlightInfo checkVariableInitializedBeforeUsage( return checkVariableInitializedBeforeUsage(expression, variable, uninitializedVarProblems, containingFile, false); } + @RequiredReadAction public static HighlightInfo checkVariableInitializedBeforeUsage( @Nonnull PsiReferenceExpression expression, @Nonnull PsiVariable variable, @@ -329,7 +334,7 @@ public static HighlightInfo checkVariableInitializedBeforeUsage( return null; } int startOffset = expression.getTextRange().getStartOffset(); - final PsiElement topBlock; + PsiElement topBlock; if (variable.hasInitializer()) { topBlock = PsiUtil.getVariableCodeBlock(variable, variable); if (topBlock == null) { @@ -337,8 +342,8 @@ public static HighlightInfo checkVariableInitializedBeforeUsage( } } else { - PsiElement scope = variable instanceof PsiField - ? ((PsiField)variable).getContainingClass() + PsiElement scope = variable instanceof PsiField field + ? field.getContainingClass() : variable.getParent() != null ? variable.getParent().getParent() : null; while (scope instanceof PsiCodeBlock && scope.getParent() instanceof PsiSwitchStatement) { scope = PsiTreeUtil.getParentOfType(scope, PsiCodeBlock.class); @@ -347,9 +352,9 @@ public static HighlightInfo checkVariableInitializedBeforeUsage( topBlock = FileTypeUtils.isInServerPageFile(scope) && scope instanceof PsiFile ? scope : PsiUtil.getTopLevelEnclosingCodeBlock(expression, scope); - if (variable instanceof PsiField) { + if (variable instanceof PsiField field) { // non final field already initialized with default value - if (!ignoreFinality && !variable.hasModifierProperty(PsiModifier.FINAL)) { + if (!ignoreFinality && !field.isFinal()) { return null; } // final field may be initialized in ctor or class initializer only @@ -363,7 +368,7 @@ public static HighlightInfo checkVariableInitializedBeforeUsage( } final PsiElement parent = topBlock.getParent(); // access to final fields from inner classes always allowed - if (inInnerClass(expression, ((PsiField)variable).getContainingClass())) { + if (inInnerClass(expression, field.getContainingClass())) { return null; } final PsiCodeBlock block; @@ -371,11 +376,11 @@ public static HighlightInfo checkVariableInitializedBeforeUsage( if (parent instanceof PsiMethod) { PsiMethod constructor = (PsiMethod)parent; if (!containingFile.getManager() - .areElementsEquivalent(constructor.getContainingClass(), ((PsiField)variable).getContainingClass())) { + .areElementsEquivalent(constructor.getContainingClass(), field.getContainingClass())) { return null; } // static variables already initialized in class initializers - if (variable.hasModifierProperty(PsiModifier.STATIC)) { + if (field.isStatic()) { return null; } // as a last chance, field may be initialized in this() call @@ -388,7 +393,7 @@ public static HighlightInfo checkVariableInitializedBeforeUsage( return null; } PsiCodeBlock body = redirectedConstructor.getBody(); - if (body != null && variableDefinitelyAssignedIn(variable, body)) { + if (body != null && variableDefinitelyAssignedIn(field, body)) { return null; } } @@ -410,7 +415,7 @@ else if (parent instanceof PsiClassInitializer) { aClass, (PsiField)variable, variable.hasModifierProperty(PsiModifier.STATIC), - field -> startOffset > field.getTextOffset() + field1 -> startOffset > field1.getTextOffset() )) { return null; } @@ -418,22 +423,18 @@ else if (parent instanceof PsiClassInitializer) { else { // field reference outside code block // check variable initialized before its usage - final PsiField field = (PsiField)variable; - aClass = field.getContainingClass(); final PsiField anotherField = PsiTreeUtil.getTopmostParentOfType(expression, PsiField.class); if (aClass == null || isFieldInitializedInOtherFieldInitializer( aClass, field, - field.hasModifierProperty(PsiModifier.STATIC), + field.isStatic(), psiField -> startOffset > psiField.getTextOffset() )) { return null; } - if (anotherField != null - && !anotherField.hasModifierProperty(PsiModifier.STATIC) - && field.hasModifierProperty(PsiModifier.STATIC) + if (anotherField != null && !anotherField.isStatic() && field.isStatic() && isFieldInitializedInClassInitializer(field, true, aClass.getInitializers())) { return null; } @@ -443,7 +444,7 @@ && isFieldInitializedInClassInitializer(field, true, aClass.getInitializers())) } int offset = startOffset; - if (anotherField != null && anotherField.getContainingClass() == aClass && !field.hasModifierProperty(PsiModifier.STATIC)) { + if (anotherField != null && anotherField.getContainingClass() == aClass && !field.isStatic()) { offset = 0; } block = null; @@ -455,7 +456,7 @@ && isFieldInitializedInClassInitializer(field, true, aClass.getInitializers())) continue; } PsiCodeBlock body = constructor.getBody(); - if (body != null && variableDefinitelyAssignedIn(variable, body)) { + if (body != null && variableDefinitelyAssignedIn(field, body)) { return null; } // as a last chance, field may be initialized in this() call @@ -466,7 +467,7 @@ && isFieldInitializedInClassInitializer(field, true, aClass.getInitializers())) continue; } PsiCodeBlock redirectedBody = redirectedConstructor.getBody(); - if (redirectedBody != null && variableDefinitelyAssignedIn(variable, redirectedBody)) { + if (redirectedBody != null && variableDefinitelyAssignedIn(field, redirectedBody)) { return null; } } @@ -487,10 +488,8 @@ && isFieldInitializedInClassInitializer(field, true, aClass.getInitializers())) if (shouldCheckInitializerOrder && startOffset < initializer.getTextRange().getStartOffset()) { continue; } - if (initializer.hasModifierProperty(PsiModifier.STATIC) == variable.hasModifierProperty(PsiModifier.STATIC)) { - if (variableDefinitelyAssignedIn(variable, body)) { - return null; - } + if (initializer.isStatic() == field.isStatic() && variableDefinitelyAssignedIn(field, body)) { + return null; } } } @@ -511,13 +510,12 @@ && isFieldInitializedInClassInitializer(field, true, aClass.getInitializers())) uninitializedVarProblems.put(topBlock, codeBlockProblems); } if (codeBlockProblems.contains(expression)) { - final String name = expression.getElement().getText(); - String description = JavaErrorBundle.message("variable.not.initialized", name); + String name = expression.getElement().getText(); HighlightInfo highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) .range(expression) - .descriptionAndTooltip(description) + .descriptionAndTooltip(JavaErrorLocalize.variableNotInitialized(name)) + .registerFix(QuickFixFactory.getInstance().createAddVariableInitializerFix(variable), null, null, null, null) .create(); - QuickFixAction.registerQuickFixAction(highlightInfo, QuickFixFactory.getInstance().createAddVariableInitializerFix(variable)); if (variable instanceof PsiLocalVariable) { //QuickFixAction.registerQuickFixAction(highlightInfo, HighlightFixUtil.createInsertSwitchDefaultFix(variable, topBlock, expression)); } @@ -540,8 +538,7 @@ private static boolean isFieldInitializedInClassInitializer( ) { return ContainerUtil.find( initializers, - initializer -> initializer.hasModifierProperty(PsiModifier.STATIC) == isFieldStatic - && variableDefinitelyAssignedIn(field, initializer.getBody()) + initializer -> initializer.isStatic() == isFieldStatic && variableDefinitelyAssignedIn(field, initializer.getBody()) ) != null; } @@ -604,6 +601,7 @@ public static boolean isReassigned( @Nullable + @RequiredReadAction public static HighlightInfo checkFinalVariableMightAlreadyHaveBeenAssignedTo( @Nonnull PsiVariable variable, @Nonnull PsiReferenceExpression expression, @@ -640,11 +638,11 @@ public static HighlightInfo checkFinalVariableMightAlreadyHaveBeenAssignedTo( } // field can get assigned in other field initializers final PsiField[] fields = aClass.getFields(); - boolean isFieldStatic = field.hasModifierProperty(PsiModifier.STATIC); + boolean isFieldStatic = field.isStatic(); for (PsiField psiField : fields) { PsiExpression initializer = psiField.getInitializer(); if (psiField != field - && psiField.hasModifierProperty(PsiModifier.STATIC) == isFieldStatic + && psiField.isStatic() == isFieldStatic && initializer != null && initializer != codeBlock && !variableDefinitelyNotAssignedIn(field, initializer)) { alreadyAssigned = true; @@ -661,7 +659,7 @@ public static HighlightInfo checkFinalVariableMightAlreadyHaveBeenAssignedTo( } final PsiClassInitializer[] initializers = aClass.getInitializers(); for (PsiClassInitializer initializer : initializers) { - if (initializer.hasModifierProperty(PsiModifier.STATIC) == field.hasModifierProperty(PsiModifier.STATIC)) { + if (initializer.isStatic() == field.isStatic()) { final PsiCodeBlock body = initializer.getBody(); if (body == codeBlock) { return null; @@ -681,7 +679,7 @@ public static HighlightInfo checkFinalVariableMightAlreadyHaveBeenAssignedTo( } } - if (!alreadyAssigned && !field.hasModifierProperty(PsiModifier.STATIC)) { + if (!alreadyAssigned && !field.isStatic()) { // 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) @@ -699,10 +697,9 @@ public static HighlightInfo checkFinalVariableMightAlreadyHaveBeenAssignedTo( } if (alreadyAssigned) { - String description = JavaErrorBundle.message("variable.already.assigned", variable.getName()); final HighlightInfo highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) .range(expression) - .descriptionAndTooltip(description) + .descriptionAndTooltip(JavaErrorLocalize.variableAlreadyAssigned(variable.getName())) .create(); QuickFixAction.registerQuickFixAction( highlightInfo, @@ -739,15 +736,15 @@ private static Collection getFinalVariableProblems @Nullable + @RequiredReadAction public static HighlightInfo checkFinalVariableInitializedInLoop( @Nonnull PsiReferenceExpression expression, @Nonnull PsiElement resolved ) { if (ControlFlowUtil.isVariableAssignedInLoop(expression, resolved)) { - String description = JavaErrorBundle.message("variable.assigned.in.loop", ((PsiVariable)resolved).getName()); final HighlightInfo highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) .range(expression) - .descriptionAndTooltip(description) + .descriptionAndTooltip(JavaErrorLocalize.variableAssignedInLoop(((PsiVariable)resolved).getName())) .create(); QuickFixAction.registerQuickFixAction(highlightInfo, QuickFixFactory.getInstance() .createModifierListFix((PsiVariable)resolved, PsiModifier.FINAL, false, false)); @@ -756,8 +753,8 @@ public static HighlightInfo checkFinalVariableInitializedInLoop( return null; } - @Nullable + @RequiredReadAction public static HighlightInfo checkCannotWriteToFinal(@Nonnull PsiExpression expression, @Nonnull PsiFile containingFile) { PsiReferenceExpression reference = null; boolean readBeforeWrite = false; @@ -794,9 +791,9 @@ else if (expression instanceof PsiPrefixExpression) { canWriteToFinal(variable, expression, reference, containingFile) && checkWriteToFinalInsideLambda(variable, reference) == null; if (readBeforeWrite || !canWrite) { final String name = variable.getName(); - String description = canWrite - ? JavaErrorBundle.message("variable.not.initialized", name) - : JavaErrorBundle.message("assignment.to.final.variable", name); + LocalizeValue description = canWrite + ? JavaErrorLocalize.variableNotInitialized(name) + : JavaErrorLocalize.assignmentToFinalVariable(name); final HighlightInfo highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) .range(reference.getTextRange()) .descriptionAndTooltip(description) @@ -865,6 +862,7 @@ private static boolean isSameField( } @Nullable + @RequiredReadAction public static HighlightInfo checkVariableMustBeFinal( @Nonnull PsiVariable variable, @Nonnull PsiJavaCodeReferenceElement context, @@ -886,10 +884,9 @@ public static HighlightInfo checkVariableMustBeFinal( if (isToBeEffectivelyFinal && isEffectivelyFinal(variable, innerClass, context)) { return null; } - final String description = JavaErrorBundle.message( - isToBeEffectivelyFinal ? "variable.must.be.final.or.effectively.final" : "variable.must.be.final", - context.getText() - ); + final String description = isToBeEffectivelyFinal + ? JavaErrorBundle.message("variable.must.be.final.or.effectively.final", context.getText()) + : JavaErrorLocalize.variableMustBeFinal(context.getText()).get(); final HighlightInfo highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) .range(context) @@ -902,30 +899,29 @@ public static HighlightInfo checkVariableMustBeFinal( return checkWriteToFinalInsideLambda(variable, context); } + @RequiredReadAction private static HighlightInfo checkWriteToFinalInsideLambda( @Nonnull PsiVariable variable, @Nonnull PsiJavaCodeReferenceElement context ) { - final PsiLambdaExpression lambdaExpression = PsiTreeUtil.getParentOfType(context, PsiLambdaExpression.class); + PsiLambdaExpression lambdaExpression = PsiTreeUtil.getParentOfType(context, PsiLambdaExpression.class); if (lambdaExpression != null && !PsiTreeUtil.isAncestor(lambdaExpression, variable, true)) { - final PsiElement parent = variable.getParent(); + PsiElement parent = variable.getParent(); if (parent instanceof PsiParameterList && parent.getParent() == lambdaExpression) { return null; } if (!isEffectivelyFinal(variable, lambdaExpression, context)) { - String text = JavaErrorBundle.message("lambda.variable.must.be.final"); - HighlightInfo highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) + return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) .range(context) - .descriptionAndTooltip(text) + .descriptionAndTooltip(JavaErrorLocalize.lambdaVariableMustBeFinal()) + .registerFix(QuickFixFactory.getInstance().createVariableAccessFromInnerClassFix(variable, lambdaExpression), null, null, null, null) .create(); - QuickFixAction.registerQuickFixAction(highlightInfo, QuickFixFactory.getInstance() - .createVariableAccessFromInnerClassFix(variable, lambdaExpression)); - return highlightInfo; } } return null; } + @RequiredReadAction public static boolean isEffectivelyFinal( @Nonnull PsiVariable variable, @Nonnull PsiElement scope, @@ -936,7 +932,7 @@ public static boolean isEffectivelyFinal( effectivelyFinal = notAccessedForWriting(variable, new LocalSearchScope(((PsiParameter)variable).getDeclarationScope())); } else { - final ControlFlow controlFlow; + ControlFlow controlFlow; try { PsiElement codeBlock = PsiUtil.getVariableCodeBlock(variable, context); if (codeBlock == null) { @@ -948,14 +944,14 @@ public static boolean isEffectivelyFinal( return true; } - final List readBeforeWriteLocals = ControlFlowUtil.getReadBeforeWriteLocals(controlFlow); + List readBeforeWriteLocals = ControlFlowUtil.getReadBeforeWriteLocals(controlFlow); for (PsiReferenceExpression expression : readBeforeWriteLocals) { if (expression.resolve() == variable) { return PsiUtil.isAccessedForReading(expression); } } - final Collection initializedTwice = ControlFlowUtil.getInitializedTwice(controlFlow); + Collection initializedTwice = ControlFlowUtil.getInitializedTwice(controlFlow); effectivelyFinal = !initializedTwice.contains(new ControlFlowUtil.VariableInfo(variable, null)); if (effectivelyFinal) { effectivelyFinal = notAccessedForWriting(variable, new LocalSearchScope(scope)); @@ -964,9 +960,10 @@ public static boolean isEffectivelyFinal( return effectivelyFinal; } + @RequiredReadAction private static boolean notAccessedForWriting(@Nonnull PsiVariable variable, @Nonnull LocalSearchScope searchScope) { for (PsiReference reference : ReferencesSearch.search(variable, searchScope)) { - final PsiElement element = reference.getElement(); + PsiElement element = reference.getElement(); if (element instanceof PsiExpression && PsiUtil.isAccessedForWriting((PsiExpression)element)) { return false; } @@ -976,16 +973,16 @@ private static boolean notAccessedForWriting(@Nonnull PsiVariable variable, @Non @Nullable public static PsiElement getInnerClassVariableReferencedFrom(@Nonnull PsiVariable variable, @Nonnull PsiElement context) { - final PsiElement[] scope; - if (variable instanceof PsiResourceVariable) { - scope = ((PsiResourceVariable)variable).getDeclarationScope(); + PsiElement[] scope; + if (variable instanceof PsiResourceVariable resourceVar) { + scope = resourceVar.getDeclarationScope(); } else if (variable instanceof PsiLocalVariable) { - final PsiElement parent = variable.getParent(); + PsiElement parent = variable.getParent(); scope = new PsiElement[]{parent != null ? parent.getParent() : null}; // code block or for statement } - else if (variable instanceof PsiParameter) { - scope = new PsiElement[]{((PsiParameter)variable).getDeclarationScope()}; + else if (variable instanceof PsiParameter param) { + scope = new PsiElement[]{param.getDeclarationScope()}; } else { scope = new PsiElement[]{variable.getParent()}; @@ -1015,19 +1012,18 @@ else if (variable instanceof PsiParameter) { return null; } - @Nullable + @RequiredReadAction public static HighlightInfo checkInitializerCompleteNormally(@Nonnull PsiClassInitializer initializer) { - final PsiCodeBlock body = initializer.getBody(); + PsiCodeBlock body = initializer.getBody(); // unhandled exceptions already reported try { - final ControlFlow controlFlow = getControlFlowNoConstantEvaluate(body); - final int completionReasons = ControlFlowUtil.getCompletionReasons(controlFlow, 0, controlFlow.getSize()); + ControlFlow controlFlow = getControlFlowNoConstantEvaluate(body); + int completionReasons = ControlFlowUtil.getCompletionReasons(controlFlow, 0, controlFlow.getSize()); if ((completionReasons & ControlFlowUtil.NORMAL_COMPLETION_REASON) == 0) { - String description = JavaErrorBundle.message("initializer.must.be.able.to.complete.normally"); return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) .range(body) - .descriptionAndTooltip(description) + .descriptionAndTooltip(JavaErrorLocalize.initializerMustBeAbleToCompleteNormally()) .create(); } } diff --git a/java-analysis-impl/src/main/java/com/intellij/java/analysis/impl/codeInsight/daemon/impl/analysis/HighlightNamesUtil.java b/java-analysis-impl/src/main/java/com/intellij/java/analysis/impl/codeInsight/daemon/impl/analysis/HighlightNamesUtil.java index 3c9b254dd7..ad57839b89 100644 --- a/java-analysis-impl/src/main/java/com/intellij/java/analysis/impl/codeInsight/daemon/impl/analysis/HighlightNamesUtil.java +++ b/java-analysis-impl/src/main/java/com/intellij/java/analysis/impl/codeInsight/daemon/impl/analysis/HighlightNamesUtil.java @@ -19,6 +19,7 @@ import com.intellij.java.language.JavaLanguage; import com.intellij.java.language.impl.psi.impl.source.tree.ElementType; import com.intellij.java.language.psi.*; +import consulo.annotation.access.RequiredReadAction; import consulo.colorScheme.TextAttributes; import consulo.colorScheme.TextAttributesKey; import consulo.colorScheme.TextAttributesScheme; @@ -49,10 +50,11 @@ public class HighlightNamesUtil { private static final Logger LOG = Logger.getInstance(HighlightNamesUtil.class); @Nullable + @RequiredReadAction public static HighlightInfo highlightMethodName( @Nonnull PsiMethod method, @Nonnull PsiElement elementToHighlight, - final boolean isDeclaration, + boolean isDeclaration, @Nonnull TextAttributesScheme colorsScheme ) { return highlightMethodName(method, elementToHighlight, elementToHighlight.getTextRange(), colorsScheme, isDeclaration); @@ -67,27 +69,25 @@ public static HighlightInfo highlightMethodName( @Nonnull PsiElement elementToHighlight, @Nonnull TextRange range, @Nonnull TextAttributesScheme colorsScheme, - final boolean isDeclaration + boolean isDeclaration ) { boolean isInherited = false; - if (!isDeclaration) { - if (isCalledOnThis(elementToHighlight)) { - final PsiClass containingClass = methodOrClass instanceof PsiMethod ? methodOrClass.getContainingClass() : null; - PsiClass enclosingClass = containingClass == null ? null : PsiTreeUtil.getParentOfType(elementToHighlight, PsiClass.class); - while (enclosingClass != null) { - isInherited = enclosingClass.isInheritor(containingClass, true); - if (isInherited) { - break; - } - enclosingClass = PsiTreeUtil.getParentOfType(enclosingClass, PsiClass.class, true); + if (!isDeclaration && isCalledOnThis(elementToHighlight)) { + PsiClass containingClass = methodOrClass instanceof PsiMethod ? methodOrClass.getContainingClass() : null; + PsiClass enclosingClass = containingClass == null ? null : PsiTreeUtil.getParentOfType(elementToHighlight, PsiClass.class); + while (enclosingClass != null) { + isInherited = enclosingClass.isInheritor(containingClass, true); + if (isInherited) { + break; } + enclosingClass = PsiTreeUtil.getParentOfType(enclosingClass, PsiClass.class, true); } } LOG.assertTrue(methodOrClass instanceof PsiMethod || !isDeclaration); - HighlightInfoType type = methodOrClass instanceof PsiMethod - ? getMethodNameHighlightType((PsiMethod)methodOrClass, isDeclaration, isInherited) + HighlightInfoType type = methodOrClass instanceof PsiMethod method + ? getMethodNameHighlightType(method, isDeclaration, isInherited) : JavaHighlightInfoTypes.CONSTRUCTOR_CALL; if (type != null) { TextAttributes attributes = mergeWithScopeAttributes(methodOrClass, type, colorsScheme); @@ -125,25 +125,24 @@ private static TextAttributes mergeWithScopeAttributes( } @Nonnull + @RequiredReadAction public static HighlightInfo highlightClassName( @Nullable PsiClass aClass, @Nonnull PsiElement elementToHighlight, @Nonnull TextAttributesScheme colorsScheme ) { TextRange range = elementToHighlight.getTextRange(); - if (elementToHighlight instanceof PsiJavaCodeReferenceElement) { - final PsiJavaCodeReferenceElement referenceElement = (PsiJavaCodeReferenceElement)elementToHighlight; - PsiElement identifier = referenceElement.getReferenceNameElement(); + if (elementToHighlight instanceof PsiJavaCodeReferenceElement javaCodeRef) { + PsiElement identifier = javaCodeRef.getReferenceNameElement(); if (identifier != null) { range = identifier.getTextRange(); } } // This will highlight @ sign in annotation as well. - final PsiElement parent = elementToHighlight.getParent(); - if (parent instanceof PsiAnnotation) { - final PsiAnnotation psiAnnotation = (PsiAnnotation)parent; - range = new TextRange(psiAnnotation.getTextRange().getStartOffset(), range.getEndOffset()); + PsiElement parent = elementToHighlight.getParent(); + if (parent instanceof PsiAnnotation annotation) { + range = new TextRange(annotation.getTextRange().getStartOffset(), range.getEndOffset()); } HighlightInfoType type = getClassNameHighlightType(aClass, elementToHighlight); @@ -156,6 +155,7 @@ public static HighlightInfo highlightClassName( } @Nullable + @RequiredReadAction public static HighlightInfo highlightVariableName( @Nonnull PsiVariable variable, @Nonnull PsiElement elementToHighlight, @@ -165,9 +165,10 @@ public static HighlightInfo highlightVariableName( if (varType == null) { return null; } - if (variable instanceof PsiField) { - TextAttributes attributes = mergeWithScopeAttributes(variable, varType, colorsScheme); - HighlightInfo.Builder builder = HighlightInfo.newHighlightInfo(varType).range(elementToHighlight.getTextRange()); + if (variable instanceof PsiField field) { + TextAttributes attributes = mergeWithScopeAttributes(field, varType, colorsScheme); + HighlightInfo.Builder builder = HighlightInfo.newHighlightInfo(varType) + .range(elementToHighlight.getTextRange()); if (attributes != null) { builder.textAttributes(attributes); } @@ -175,23 +176,21 @@ public static HighlightInfo highlightVariableName( } HighlightInfo.Builder builder = HighlightInfo.newHighlightInfo(varType).range(elementToHighlight); - return RainbowHighlighter.isRainbowEnabledWithInheritance( - colorsScheme, - JavaLanguage.INSTANCE - ) ? builder.createUnconditionally() : builder.create(); + return RainbowHighlighter.isRainbowEnabledWithInheritance(colorsScheme, JavaLanguage.INSTANCE) + ? builder.createUnconditionally() + : builder.create(); } @Nullable + @RequiredReadAction public static HighlightInfo highlightClassNameInQualifier( @Nonnull PsiJavaCodeReferenceElement element, @Nonnull TextAttributesScheme colorsScheme ) { PsiElement qualifierExpression = element.getQualifier(); - if (qualifierExpression instanceof PsiJavaCodeReferenceElement) { - PsiElement resolved = ((PsiJavaCodeReferenceElement)qualifierExpression).resolve(); - if (resolved instanceof PsiClass) { - return highlightClassName((PsiClass)resolved, qualifierExpression, colorsScheme); - } + if (qualifierExpression instanceof PsiJavaCodeReferenceElement javaCodeRef + && javaCodeRef.resolve() instanceof PsiClass psiClass) { + return highlightClassName(psiClass, qualifierExpression, colorsScheme); } return null; } @@ -207,13 +206,13 @@ private static HighlightInfoType getMethodNameHighlightType( if (isDeclaration) { return JavaHighlightInfoTypes.METHOD_DECLARATION; } - if (method.hasModifierProperty(PsiModifier.STATIC)) { + if (method.isStatic()) { return JavaHighlightInfoTypes.STATIC_METHOD; } if (isInheritedMethod) { return JavaHighlightInfoTypes.INHERITED_METHOD; } - if (method.hasModifierProperty(PsiModifier.ABSTRACT)) { + if (method.isAbstract()) { return JavaHighlightInfoTypes.ABSTRACT_METHOD; } return JavaHighlightInfoTypes.METHOD_CALL; @@ -222,16 +221,16 @@ private static HighlightInfoType getMethodNameHighlightType( @Nullable private static HighlightInfoType getVariableNameHighlightType(@Nonnull PsiVariable var) { if (var instanceof PsiLocalVariable - || var instanceof PsiParameter && ((PsiParameter)var).getDeclarationScope() instanceof PsiForeachStatement) { + || var instanceof PsiParameter parameter && parameter.getDeclarationScope() instanceof PsiForeachStatement) { return JavaHighlightInfoTypes.LOCAL_VARIABLE; } - if (var instanceof PsiField) { - return var.hasModifierProperty(PsiModifier.STATIC) - ? var.hasModifierProperty(PsiModifier.FINAL) ? JavaHighlightInfoTypes.STATIC_FINAL_FIELD : JavaHighlightInfoTypes.STATIC_FIELD - : var.hasModifierProperty(PsiModifier.FINAL) ? JavaHighlightInfoTypes.INSTANCE_FINAL_FIELD : JavaHighlightInfoTypes.INSTANCE_FIELD; + if (var instanceof PsiField field) { + return field.isStatic() + ? field.isFinal() ? JavaHighlightInfoTypes.STATIC_FINAL_FIELD : JavaHighlightInfoTypes.STATIC_FIELD + : field.isFinal() ? JavaHighlightInfoTypes.INSTANCE_FINAL_FIELD : JavaHighlightInfoTypes.INSTANCE_FIELD; } - if (var instanceof PsiParameter) { - return ((PsiParameter)var).getDeclarationScope() instanceof PsiLambdaExpression + if (var instanceof PsiParameter parameter) { + return parameter.getDeclarationScope() instanceof PsiLambdaExpression ? JavaHighlightInfoTypes.LAMBDA_PARAMETER : JavaHighlightInfoTypes.PARAMETER; } @@ -256,7 +255,7 @@ private static HighlightInfoType getClassNameHighlightType(@Nullable PsiClass aC if (aClass instanceof PsiTypeParameter) { return JavaHighlightInfoTypes.TYPE_PARAMETER_NAME; } - final PsiModifierList modList = aClass.getModifierList(); + PsiModifierList modList = aClass.getModifierList(); if (modList != null && modList.hasModifierProperty(PsiModifier.ABSTRACT)) { return JavaHighlightInfoTypes.ABSTRACT_CLASS_NAME; } @@ -266,6 +265,7 @@ private static HighlightInfoType getClassNameHighlightType(@Nullable PsiClass aC } @Nullable + @RequiredReadAction public static HighlightInfo highlightReassignedVariable(@Nonnull PsiVariable variable, @Nonnull PsiElement elementToHighlight) { if (variable instanceof PsiLocalVariable) { return HighlightInfo.newHighlightInfo(JavaHighlightInfoTypes.REASSIGNED_LOCAL_VARIABLE) @@ -289,13 +289,13 @@ private static TextAttributes getScopeAttributes(@Nonnull PsiElement element, @N DependencyValidationManager validationManager = DependencyValidationManager.getInstance(file.getProject()); List> scopes = validationManager.getScopeBasedHighlightingCachedScopes(); for (Pair scope : scopes) { - final NamedScope namedScope = scope.getFirst(); - final TextAttributesKey scopeKey = ScopeAttributesUtil.getScopeTextAttributeKey(namedScope.getName()); - final TextAttributes attributes = colorsScheme.getAttributes(scopeKey); + NamedScope namedScope = scope.getFirst(); + TextAttributesKey scopeKey = ScopeAttributesUtil.getScopeTextAttributeKey(namedScope.getName()); + TextAttributes attributes = colorsScheme.getAttributes(scopeKey); if (attributes == null || attributes.isEmpty()) { continue; } - final PackageSet packageSet = namedScope.getValue(); + PackageSet packageSet = namedScope.getValue(); if (packageSet != null && packageSet.contains(file.getVirtualFile(), file.getProject(), scope.getSecond())) { result = TextAttributes.merge(attributes, result); } @@ -304,18 +304,20 @@ private static TextAttributes getScopeAttributes(@Nonnull PsiElement element, @N } @Nonnull + @RequiredReadAction public static TextRange getMethodDeclarationTextRange(@Nonnull PsiMethod method) { if (method instanceof SyntheticElement) { return TextRange.EMPTY_RANGE; } int start = stripAnnotationsFromModifierList(method.getModifierList()); - final TextRange throwsRange = method.getThrowsList().getTextRange(); + TextRange throwsRange = method.getThrowsList().getTextRange(); LOG.assertTrue(throwsRange != null, method); int end = throwsRange.getEndOffset(); return new TextRange(start, end); } @Nonnull + @RequiredReadAction public static TextRange getFieldDeclarationTextRange(@Nonnull PsiField field) { int start = stripAnnotationsFromModifierList(field.getModifierList()); int end = field.getNameIdentifier().getTextRange().getEndOffset(); @@ -323,19 +325,20 @@ public static TextRange getFieldDeclarationTextRange(@Nonnull PsiField field) { } @Nonnull + @RequiredReadAction public static TextRange getClassDeclarationTextRange(@Nonnull PsiClass aClass) { - if (aClass instanceof PsiEnumConstantInitializer) { - return ((PsiEnumConstantInitializer)aClass).getEnumConstant().getNameIdentifier().getTextRange(); + if (aClass instanceof PsiEnumConstantInitializer enumInitializer) { + return enumInitializer.getEnumConstant().getNameIdentifier().getTextRange(); } - final PsiElement psiElement = - aClass instanceof PsiAnonymousClass ? ((PsiAnonymousClass)aClass).getBaseClassReference() : aClass.getModifierList() == null ? aClass.getNameIdentifier() : - aClass.getModifierList(); + PsiElement psiElement = aClass instanceof PsiAnonymousClass anonymousClass + ? anonymousClass.getBaseClassReference() + : aClass.getModifierList() == null ? aClass.getNameIdentifier() : aClass.getModifierList(); if (psiElement == null) { return new TextRange(aClass.getTextRange().getStartOffset(), aClass.getTextRange().getStartOffset()); } int start = stripAnnotationsFromModifierList(psiElement); PsiElement endElement = - aClass instanceof PsiAnonymousClass ? ((PsiAnonymousClass)aClass).getBaseClassReference() : aClass.getImplementsList(); + aClass instanceof PsiAnonymousClass anonymousClass ? anonymousClass.getBaseClassReference() : aClass.getImplementsList(); if (endElement == null) { endElement = aClass.getNameIdentifier(); } @@ -344,6 +347,7 @@ public static TextRange getClassDeclarationTextRange(@Nonnull PsiClass aClass) { return new TextRange(start, end); } + @RequiredReadAction private static int stripAnnotationsFromModifierList(@Nonnull PsiElement element) { TextRange textRange = element.getTextRange(); if (textRange == TextRange.EMPTY_RANGE) { @@ -351,8 +355,8 @@ private static int stripAnnotationsFromModifierList(@Nonnull PsiElement element) } PsiAnnotation lastAnnotation = null; for (PsiElement child : element.getChildren()) { - if (child instanceof PsiAnnotation) { - lastAnnotation = (PsiAnnotation)child; + if (child instanceof PsiAnnotation annotation) { + lastAnnotation = annotation; } } if (lastAnnotation == null) { @@ -371,6 +375,7 @@ private static int stripAnnotationsFromModifierList(@Nonnull PsiElement element) return textRange.getStartOffset(); } + @RequiredReadAction public static HighlightInfo highlightPackage( @Nonnull PsiElement resolved, @Nonnull PsiJavaCodeReferenceElement elementToHighlight, @@ -387,8 +392,8 @@ public static HighlightInfo highlightPackage( // empty PsiReferenceParameterList nextSibling = PsiTreeUtil.nextLeaf(nextSibling); } - if (nextSibling instanceof PsiJavaToken && ((PsiJavaToken)nextSibling).getTokenType() == JavaTokenType.DOT) { - range = new TextRange(referenceNameElement.getTextRange().getStartOffset(), nextSibling.getTextRange().getEndOffset()); + if (nextSibling instanceof PsiJavaToken javaToken && javaToken.getTokenType() == JavaTokenType.DOT) { + range = new TextRange(referenceNameElement.getTextRange().getStartOffset(), javaToken.getTextRange().getEndOffset()); } else { range = referenceNameElement.getTextRange(); @@ -408,6 +413,7 @@ private static HighlightInfo.Builder nameBuilder(@Nonnull HighlightInfoType type return HighlightInfo.newHighlightInfo(type)/*.toolId(JavaNamesHighlightVisitor.class)*/; } + @RequiredReadAction static HighlightInfo highlightKeyword(@Nonnull PsiKeyword keyword) { return nameBuilder(JavaHighlightInfoTypes.JAVA_KEYWORD).range(keyword).create(); } diff --git a/java-analysis-impl/src/main/java/com/intellij/java/analysis/impl/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java b/java-analysis-impl/src/main/java/com/intellij/java/analysis/impl/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java index e5a6f1a798..1e4861e96b 100644 --- a/java-analysis-impl/src/main/java/com/intellij/java/analysis/impl/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java +++ b/java-analysis-impl/src/main/java/com/intellij/java/analysis/impl/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java @@ -464,7 +464,8 @@ else if (returnErrors != null) { } @Override - public void visitBreakStatement(PsiBreakStatement statement) { + @RequiredReadAction + public void visitBreakStatement(@Nonnull PsiBreakStatement statement) { super.visitBreakStatement(statement); if (!myHolder.hasErrorResults()) { myHolder.add(HighlightUtil.checkLabelDefined(statement.getLabelIdentifier(), statement.findExitedStatement())); @@ -475,7 +476,8 @@ public void visitBreakStatement(PsiBreakStatement statement) { } @Override - public void visitClass(PsiClass aClass) { + @RequiredReadAction + public void visitClass(@Nonnull PsiClass aClass) { super.visitClass(aClass); if (aClass instanceof PsiSyntheticClass) { return; @@ -510,7 +512,8 @@ public void visitClass(PsiClass aClass) { } @Override - public void visitClassInitializer(PsiClassInitializer initializer) { + @RequiredReadAction + public void visitClassInitializer(@Nonnull PsiClassInitializer initializer) { super.visitClassInitializer(initializer); if (!myHolder.hasErrorResults()) { myHolder.add(HighlightControlFlowUtil.checkInitializerCompleteNormally(initializer)); @@ -524,7 +527,8 @@ public void visitClassInitializer(PsiClassInitializer initializer) { } @Override - public void visitClassObjectAccessExpression(PsiClassObjectAccessExpression expression) { + @RequiredReadAction + public void visitClassObjectAccessExpression(@Nonnull PsiClassObjectAccessExpression expression) { super.visitClassObjectAccessExpression(expression); if (!myHolder.hasErrorResults()) { myHolder.add(GenericsHighlightUtil.checkClassObjectAccessExpression(expression)); @@ -532,6 +536,7 @@ public void visitClassObjectAccessExpression(PsiClassObjectAccessExpression expr } @Override + @RequiredReadAction public void visitComment(PsiComment comment) { super.visitComment(comment); if (!myHolder.hasErrorResults()) { @@ -543,7 +548,8 @@ public void visitComment(PsiComment comment) { } @Override - public void visitContinueStatement(PsiContinueStatement statement) { + @RequiredReadAction + public void visitContinueStatement(@Nonnull PsiContinueStatement statement) { super.visitContinueStatement(statement); if (!myHolder.hasErrorResults()) { myHolder.add(HighlightUtil.checkLabelDefined(statement.getLabelIdentifier(), statement.findContinuedStatement())); @@ -554,7 +560,7 @@ public void visitContinueStatement(PsiContinueStatement statement) { } @Override - public void visitJavaToken(PsiJavaToken token) { + public void visitJavaToken(@Nonnull PsiJavaToken token) { super.visitJavaToken(token); if (!myHolder.hasErrorResults() && token.getTokenType() == JavaTokenType.RBRACE && token.getParent() instanceof PsiCodeBlock) { @@ -596,20 +602,20 @@ public void visitDocTagValue(PsiDocTagValue value) { if (reference != null) { PsiElement element = reference.resolve(); TextAttributesScheme colorsScheme = myHolder.getColorsScheme(); - if (element instanceof PsiMethod) { + if (element instanceof PsiMethod method) { PsiElement nameElement = ((PsiDocMethodOrFieldRef)value).getNameElement(); if (nameElement != null) { - myHolder.add(HighlightNamesUtil.highlightMethodName((PsiMethod)element, nameElement, false, colorsScheme)); + myHolder.add(HighlightNamesUtil.highlightMethodName(method, nameElement, false, colorsScheme)); } } - else if (element instanceof PsiParameter) { - myHolder.add(HighlightNamesUtil.highlightVariableName((PsiVariable)element, value.getNavigationElement(), colorsScheme)); + else if (element instanceof PsiParameter parameter) { + myHolder.add(HighlightNamesUtil.highlightVariableName(parameter, value.getNavigationElement(), colorsScheme)); } } } @Override - public void visitEnumConstant(PsiEnumConstant enumConstant) { + public void visitEnumConstant(@Nonnull PsiEnumConstant enumConstant) { super.visitEnumConstant(enumConstant); if (!myHolder.hasErrorResults()) { GenericsHighlightUtil.checkEnumConstantForConstructorProblems(enumConstant, myHolder, myJavaSdkVersion); @@ -1337,16 +1343,15 @@ public void visitPrefixExpression(PsiPrefixExpression expression) { private void registerConstructorCall(@Nonnull PsiConstructorCall constructorCall) { if (myRefCountHolder != null) { - JavaResolveResult resolveResult = constructorCall.resolveMethodGenerics(); - final PsiElement resolved = resolveResult.getElement(); - if (resolved instanceof PsiNamedElement) { - myRefCountHolder.registerLocallyReferenced((PsiNamedElement)resolved); + if (constructorCall.resolveMethodGenerics().getElement() instanceof PsiNamedElement namedElem) { + myRefCountHolder.registerLocallyReferenced(namedElem); } } } @Override - public void visitReferenceElement(PsiJavaCodeReferenceElement ref) { + @RequiredReadAction + public void visitReferenceElement(@Nonnull PsiJavaCodeReferenceElement ref) { JavaResolveResult result = doVisitReferenceElement(ref); if (result != null) { PsiElement resolved = result.getElement(); @@ -1359,6 +1364,7 @@ public void visitReferenceElement(PsiJavaCodeReferenceElement ref) { } } + @RequiredReadAction private JavaResolveResult doVisitReferenceElement(@Nonnull PsiJavaCodeReferenceElement ref) { JavaResolveResult result = resolveOptimised(ref); if (result == null) { @@ -1463,34 +1469,38 @@ private JavaResolveResult doVisitReferenceElement(@Nonnull PsiJavaCodeReferenceE highlightReferencedMethodOrClassName(ref, resolved); } - if (parent instanceof PsiNewExpression && !(resolved instanceof PsiClass) && resolved instanceof PsiNamedElement && ((PsiNewExpression)parent).getClassOrAnonymousClassReference() == ref) { - String text = JavaErrorBundle.message("cannot.resolve.symbol", ((PsiNamedElement)resolved).getName()); - myHolder.add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(ref).descriptionAndTooltip(text).create()); + if (parent instanceof PsiNewExpression newExpr && !(resolved instanceof PsiClass) && resolved instanceof PsiNamedElement namedElem + && newExpr.getClassOrAnonymousClassReference() == ref) { + myHolder.add( + HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) + .range(ref) + .descriptionAndTooltip(JavaErrorLocalize.cannotResolveSymbol(namedElem.getName())) + .create() + ); } - if (!myHolder.hasErrorResults() && resolved instanceof PsiClass) { - final PsiClass aClass = ((PsiClass)resolved).getContainingClass(); - if (aClass != null) { + if (!myHolder.hasErrorResults() && resolved instanceof PsiClass psiClass) { + final PsiClass containingClass = psiClass.getContainingClass(); + if (containingClass != null) { final PsiElement qualifier = ref.getQualifier(); final PsiElement place; - if (qualifier instanceof PsiJavaCodeReferenceElement) { - place = ((PsiJavaCodeReferenceElement)qualifier).resolve(); + if (qualifier instanceof PsiJavaCodeReferenceElement javaCodeRef) { + place = javaCodeRef.resolve(); } - else if (parent instanceof PsiNewExpression) { - final PsiExpression newQualifier = ((PsiNewExpression)parent).getQualifier(); + else if (parent instanceof PsiNewExpression newExpr) { + final PsiExpression newQualifier = newExpr.getQualifier(); place = newQualifier == null ? ref : PsiUtil.resolveClassInType(newQualifier.getType()); } else { place = ref; } - if (place != null && PsiTreeUtil.isAncestor(aClass, place, false) && aClass.hasTypeParameters()) { + if (place != null && PsiTreeUtil.isAncestor(containingClass, place, false) && containingClass.hasTypeParameters()) { myHolder.add(HighlightClassUtil.checkCreateInnerClassFromStaticContext(ref, place, (PsiClass)resolved)); } } - else if (resolved instanceof PsiTypeParameter) { - final PsiTypeParameterListOwner owner = ((PsiTypeParameter)resolved).getOwner(); - if (owner instanceof PsiClass) { - final PsiClass outerClass = (PsiClass)owner; + else if (resolved instanceof PsiTypeParameter typeParam) { + final PsiTypeParameterListOwner owner = typeParam.getOwner(); + if (owner instanceof PsiClass outerClass) { if (!InheritanceUtil.hasEnclosingInstanceInScope(outerClass, ref, false, false)) { myHolder.add(HighlightClassUtil.reportIllegalEnclosingUsage(ref, null, (PsiClass)owner, ref)); } @@ -1539,7 +1549,8 @@ private JavaResolveResult[] resolveOptimised(@Nonnull PsiReferenceExpression exp } @Override - public void visitReferenceExpression(PsiReferenceExpression expression) { + @RequiredReadAction + public void visitReferenceExpression(@Nonnull PsiReferenceExpression expression) { JavaResolveResult resultForIncompleteCode = doVisitReferenceElement(expression); if (!myHolder.hasErrorResults()) { @@ -2016,6 +2027,7 @@ public void visitTryStatement(@Nonnull PsiTryStatement statement) { } @Override + @RequiredReadAction public void visitResourceList(@Nonnull PsiResourceList resourceList) { super.visitResourceList(resourceList); if (!myHolder.hasErrorResults()) { @@ -2151,7 +2163,8 @@ public void visitConditionalExpression(@Nonnull PsiConditionalExpression express } @Override - public void visitReceiverParameter(PsiReceiverParameter parameter) { + @RequiredReadAction + public void visitReceiverParameter(@Nonnull PsiReceiverParameter parameter) { super.visitReceiverParameter(parameter); if (!myHolder.hasErrorResults()) { myHolder.add(checkFeature(parameter, JavaFeature.RECEIVERS)); From 96c6a2bf8a21b7d515de17916352ce1b85bb11ea Mon Sep 17 00:00:00 2001 From: UNV Date: Sun, 4 May 2025 14:17:44 +0300 Subject: [PATCH 3/4] Reformatting PsiAnnotation. --- .../java/language/psi/PsiAnnotation.java | 260 +++++++++--------- 1 file changed, 131 insertions(+), 129 deletions(-) diff --git a/java-language-api/src/main/java/com/intellij/java/language/psi/PsiAnnotation.java b/java-language-api/src/main/java/com/intellij/java/language/psi/PsiAnnotation.java index 6beacde9ce..d88a33c4dc 100644 --- a/java-language-api/src/main/java/com/intellij/java/language/psi/PsiAnnotation.java +++ b/java-language-api/src/main/java/com/intellij/java/language/psi/PsiAnnotation.java @@ -35,133 +35,135 @@ * @author ven */ public interface PsiAnnotation extends PsiAnnotationMemberValue, PsiMetaOwner, JvmAnnotation { - /** - * The empty array of PSI annotations which can be reused to avoid unnecessary allocations. - */ - PsiAnnotation[] EMPTY_ARRAY = new PsiAnnotation[0]; - - ArrayFactory ARRAY_FACTORY = count -> count == 0 ? EMPTY_ARRAY : new PsiAnnotation[count]; - - @NonNls - String DEFAULT_REFERENCED_METHOD_NAME = "value"; - - /** - * Kinds of element to which an annotation type is applicable (see {@link ElementType}). - */ - enum TargetType { - // see java.lang.annotation.ElementType - TYPE, - FIELD, - METHOD, - PARAMETER, - CONSTRUCTOR, - LOCAL_VARIABLE, - ANNOTATION_TYPE, - PACKAGE, - TYPE_USE, - TYPE_PARAMETER, - MODULE, - RECORD_COMPONENT, - // auxiliary value, used when it's impossible to determine annotation's targets - UNKNOWN; - - public static final TargetType[] EMPTY_ARRAY = {}; - } - - /** - * Returns the list of parameters for the annotation. - * - * @return the parameter list instance. - */ - @Nonnull - PsiAnnotationParameterList getParameterList(); - - /** - * Returns the fully qualified name of the annotation class. - * - * @return the class name, or null if the annotation is unresolved. - */ - @Nullable - @NonNls - String getQualifiedName(); - - /** - * Returns the reference element representing the name of the annotation. - * - * @return the annotation name element. - */ - @Nullable - PsiJavaCodeReferenceElement getNameReferenceElement(); - - /** - * Returns the value of the annotation element with the specified name. - * - * @param attributeName name of the annotation element for which the value is requested. If it isn't defined in annotation, - * the default value is returned. - * @return the element value, or null if the annotation does not contain a value for - * the element and the element has no default value. - */ - @Nullable - PsiAnnotationMemberValue findAttributeValue(@Nullable String attributeName); - - /** - * Returns the value of the annotation element with the specified name. - * - * @param attributeName name of the annotation element for which the value is requested, declared in this annotation. - * @return the element value, or null if the annotation does not contain a value for - * the element. - */ - @Nullable - PsiAnnotationMemberValue findDeclaredAttributeValue(@Nullable String attributeName); - - /** - * @see PsiNameValuePair#getDetachedValue() - */ - @Nullable - default PsiAnnotationMemberValue findDeclaredAttributeDetachedValue(@Nullable String attributeName) { - return findDeclaredAttributeValue(attributeName); - } - - /** - * Set annotation attribute value. Adds new name-value pair or uses an existing one, expands unnamed 'value' attribute name if needed. - * - * @param attributeName attribute name - * @param value new value template element - * @return new declared attribute value - */ - T setDeclaredAttributeValue(@Nullable String attributeName, @Nullable T value); - - /** - * Returns an owner of the annotation - usually a parent, but for type annotations the owner might be a type element. - * - * @return annotation owner - */ - @Nullable - PsiAnnotationOwner getOwner(); - - /** - * @return whether the annotation has the given qualified name. Specific languages may provide efficient implementation - * that doesn't always create/resolve annotation reference. - */ - default boolean hasQualifiedName(@Nonnull String qualifiedName) { - return qualifiedName.equals(getQualifiedName()); - } - - @Nonnull - @Override - default List getAttributes() { - return Arrays.asList(getParameterList().getAttributes()); - } - - /** - * @return the target of {@link #getNameReferenceElement()}, if it's an {@code @interface}, otherwise null - */ - @Nullable - @RequiredReadAction - default PsiClass resolveAnnotationType() { - PsiJavaCodeReferenceElement element = getNameReferenceElement(); - PsiElement declaration = element == null ? null : element.resolve(); - if (!(declaration instanceof PsiClass) || !((PsiClass)declaration).isAnnotationType()) return null; - return (PsiClass)declaration; - } + /** + * The empty array of PSI annotations which can be reused to avoid unnecessary allocations. + */ + PsiAnnotation[] EMPTY_ARRAY = new PsiAnnotation[0]; + + ArrayFactory ARRAY_FACTORY = count -> count == 0 ? EMPTY_ARRAY : new PsiAnnotation[count]; + + @NonNls + String DEFAULT_REFERENCED_METHOD_NAME = "value"; + + /** + * Kinds of element to which an annotation type is applicable (see {@link ElementType}). + */ + enum TargetType { + // see java.lang.annotation.ElementType + TYPE, + FIELD, + METHOD, + PARAMETER, + CONSTRUCTOR, + LOCAL_VARIABLE, + ANNOTATION_TYPE, + PACKAGE, + TYPE_USE, + TYPE_PARAMETER, + MODULE, + RECORD_COMPONENT, + // auxiliary value, used when it's impossible to determine annotation's targets + UNKNOWN; + + public static final TargetType[] EMPTY_ARRAY = {}; + } + + /** + * Returns the list of parameters for the annotation. + * + * @return the parameter list instance. + */ + @Nonnull + PsiAnnotationParameterList getParameterList(); + + /** + * Returns the fully qualified name of the annotation class. + * + * @return the class name, or null if the annotation is unresolved. + */ + @Nullable + @NonNls + String getQualifiedName(); + + /** + * Returns the reference element representing the name of the annotation. + * + * @return the annotation name element. + */ + @Nullable + PsiJavaCodeReferenceElement getNameReferenceElement(); + + /** + * Returns the value of the annotation element with the specified name. + * + * @param attributeName name of the annotation element for which the value is requested. If it isn't defined in annotation, + * the default value is returned. + * @return the element value, or null if the annotation does not contain a value for + * the element and the element has no default value. + */ + @Nullable + PsiAnnotationMemberValue findAttributeValue(@Nullable String attributeName); + + /** + * Returns the value of the annotation element with the specified name. + * + * @param attributeName name of the annotation element for which the value is requested, declared in this annotation. + * @return the element value, or null if the annotation does not contain a value for + * the element. + */ + @Nullable + PsiAnnotationMemberValue findDeclaredAttributeValue(@Nullable String attributeName); + + /** + * @see PsiNameValuePair#getDetachedValue() + */ + @Nullable + default PsiAnnotationMemberValue findDeclaredAttributeDetachedValue(@Nullable String attributeName) { + return findDeclaredAttributeValue(attributeName); + } + + /** + * Set annotation attribute value. Adds new name-value pair or uses an existing one, expands unnamed 'value' attribute name if needed. + * + * @param attributeName attribute name + * @param value new value template element + * @return new declared attribute value + */ + T setDeclaredAttributeValue(@Nullable String attributeName, @Nullable T value); + + /** + * Returns an owner of the annotation - usually a parent, but for type annotations the owner might be a type element. + * + * @return annotation owner + */ + @Nullable + PsiAnnotationOwner getOwner(); + + /** + * @return whether the annotation has the given qualified name. Specific languages may provide efficient implementation + * that doesn't always create/resolve annotation reference. + */ + default boolean hasQualifiedName(@Nonnull String qualifiedName) { + return qualifiedName.equals(getQualifiedName()); + } + + @Nonnull + @Override + default List getAttributes() { + return Arrays.asList(getParameterList().getAttributes()); + } + + /** + * @return the target of {@link #getNameReferenceElement()}, if it's an {@code @interface}, otherwise null + */ + @Nullable + @RequiredReadAction + default PsiClass resolveAnnotationType() { + PsiJavaCodeReferenceElement element = getNameReferenceElement(); + PsiElement declaration = element == null ? null : element.resolve(); + if (!(declaration instanceof PsiClass) || !((PsiClass)declaration).isAnnotationType()) { + return null; + } + return (PsiClass)declaration; + } } From 958d8ed57f7d7f4dae93bf953452b1051cbf21a6 Mon Sep 17 00:00:00 2001 From: UNV Date: Sun, 4 May 2025 14:49:08 +0300 Subject: [PATCH 4/4] Localizing PsiAnnotation. --- ...lo.java.analysis.JavaAnalysisLocalize.yaml | 24 --------- .../analysis/AnnotationsHighlightUtil.java | 6 +-- .../impl/analysis/HighlightVisitorImpl.java | 21 ++++---- .../java/language/psi/PsiAnnotation.java | 49 +++++++++++-------- ...lo.java.language.JavaLanguageLocalize.yaml | 24 +++++++++ ....java.language.impl.JavaErrorLocalize.yaml | 20 -------- 6 files changed, 66 insertions(+), 78 deletions(-) diff --git a/java-analysis-api/src/main/resources/LOCALIZE-LIB/en_US/consulo.java.analysis.JavaAnalysisLocalize.yaml b/java-analysis-api/src/main/resources/LOCALIZE-LIB/en_US/consulo.java.analysis.JavaAnalysisLocalize.yaml index 6b69721a04..5e78ee599d 100644 --- a/java-analysis-api/src/main/resources/LOCALIZE-LIB/en_US/consulo.java.analysis.JavaAnalysisLocalize.yaml +++ b/java-analysis-api/src/main/resources/LOCALIZE-LIB/en_US/consulo.java.analysis.JavaAnalysisLocalize.yaml @@ -1,29 +1,5 @@ add.explicit.type.arguments: text: Add explicit type arguments -annotation.target.ANNOTATION_TYPE: - text: annotation type -annotation.target.CONSTRUCTOR: - text: constructor -annotation.target.FIELD: - text: field -annotation.target.LOCAL_VARIABLE: - text: local variable -annotation.target.METHOD: - text: method -annotation.target.MODULE: - text: module -annotation.target.PACKAGE: - text: package -annotation.target.PARAMETER: - text: parameter -annotation.target.RECORD_COMPONENT: - text: record component -annotation.target.TYPE: - text: type -annotation.target.TYPE_PARAMETER: - text: type parameter -annotation.target.TYPE_USE: - text: type use change.type.arguments: text: Change type arguments change.type.arguments.to.0: diff --git a/java-analysis-impl/src/main/java/com/intellij/java/analysis/impl/codeInsight/daemon/impl/analysis/AnnotationsHighlightUtil.java b/java-analysis-impl/src/main/java/com/intellij/java/analysis/impl/codeInsight/daemon/impl/analysis/AnnotationsHighlightUtil.java index 323d5cc4a7..afdb71b5a6 100644 --- a/java-analysis-impl/src/main/java/com/intellij/java/analysis/impl/codeInsight/daemon/impl/analysis/AnnotationsHighlightUtil.java +++ b/java-analysis-impl/src/main/java/com/intellij/java/analysis/impl/codeInsight/daemon/impl/analysis/AnnotationsHighlightUtil.java @@ -271,10 +271,9 @@ else if (isAnnotationRepeatedTwice(owner, annotationType.getQualifiedName())) { PsiAnnotation.TargetType[] targets = AnnotationTargetUtil.getTargetsForLocation(owner); PsiAnnotation.TargetType applicable = AnnotationTargetUtil.findAnnotationTarget(container, targets); if (applicable == null) { - String target = JavaErrorBundle.message("annotation.target." + targets[0]); return annotationError( annotationToCheck, - JavaErrorLocalize.annotationContainerNotApplicable(container.getName(), target) + JavaErrorLocalize.annotationContainerNotApplicable(container.getName(), targets[0].getPresentableText()) ); } } @@ -446,8 +445,7 @@ public static HighlightInfo checkApplicability(@Nonnull PsiAnnotation annotation } if (applicable == null) { - String target = JavaErrorBundle.message("annotation.target." + targets[0]); - return annotationError(annotation, JavaErrorLocalize.annotationNotApplicable(nameRef.getText(), target)); + return annotationError(annotation, JavaErrorLocalize.annotationNotApplicable(nameRef.getText(), targets[0].getPresentableText())); } if (applicable == PsiAnnotation.TargetType.TYPE_USE) { diff --git a/java-analysis-impl/src/main/java/com/intellij/java/analysis/impl/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java b/java-analysis-impl/src/main/java/com/intellij/java/analysis/impl/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java index 1e4861e96b..6ec19bc244 100644 --- a/java-analysis-impl/src/main/java/com/intellij/java/analysis/impl/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java +++ b/java-analysis-impl/src/main/java/com/intellij/java/analysis/impl/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java @@ -560,25 +560,26 @@ public void visitContinueStatement(@Nonnull PsiContinueStatement statement) { } @Override + @RequiredReadAction public void visitJavaToken(@Nonnull PsiJavaToken token) { super.visitJavaToken(token); - if (!myHolder.hasErrorResults() && token.getTokenType() == JavaTokenType.RBRACE && token.getParent() instanceof PsiCodeBlock) { - - PsiElement gParent = token.getParent().getParent(); + if (!myHolder.hasErrorResults() + && token.getTokenType() == JavaTokenType.RBRACE + && token.getParent() instanceof PsiCodeBlock tokenCodeBlock) { + PsiElement gParent = tokenCodeBlock.getParent(); PsiCodeBlock codeBlock; PsiType returnType; - if (gParent instanceof PsiMethod) { - PsiMethod method = (PsiMethod)gParent; + if (gParent instanceof PsiMethod method) { codeBlock = method.getBody(); returnType = method.getReturnType(); } - else if (gParent instanceof PsiLambdaExpression) { - PsiElement body = ((PsiLambdaExpression)gParent).getBody(); - if (!(body instanceof PsiCodeBlock)) { + else if (gParent instanceof PsiLambdaExpression lambda) { + PsiElement body = lambda.getBody(); + if (!(body instanceof PsiCodeBlock lambdaCodeBlock)) { return; } - codeBlock = (PsiCodeBlock)body; - returnType = LambdaUtil.getFunctionalInterfaceReturnType((PsiLambdaExpression)gParent); + codeBlock = lambdaCodeBlock; + returnType = LambdaUtil.getFunctionalInterfaceReturnType(lambda); } else { return; diff --git a/java-language-api/src/main/java/com/intellij/java/language/psi/PsiAnnotation.java b/java-language-api/src/main/java/com/intellij/java/language/psi/PsiAnnotation.java index d88a33c4dc..6935ef2856 100644 --- a/java-language-api/src/main/java/com/intellij/java/language/psi/PsiAnnotation.java +++ b/java-language-api/src/main/java/com/intellij/java/language/psi/PsiAnnotation.java @@ -18,12 +18,13 @@ import com.intellij.java.language.jvm.JvmAnnotation; import com.intellij.java.language.jvm.annotation.JvmAnnotationAttribute; import consulo.annotation.access.RequiredReadAction; +import consulo.java.language.localize.JavaLanguageLocalize; import consulo.language.psi.PsiElement; import consulo.language.psi.meta.PsiMetaOwner; +import consulo.localize.LocalizeValue; import consulo.util.collection.ArrayFactory; import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; -import org.jetbrains.annotations.NonNls; import java.lang.annotation.ElementType; import java.util.Arrays; @@ -42,7 +43,6 @@ public interface PsiAnnotation extends PsiAnnotationMemberValue, PsiMetaOwner, J ArrayFactory ARRAY_FACTORY = count -> count == 0 ? EMPTY_ARRAY : new PsiAnnotation[count]; - @NonNls String DEFAULT_REFERENCED_METHOD_NAME = "value"; /** @@ -50,22 +50,34 @@ public interface PsiAnnotation extends PsiAnnotationMemberValue, PsiMetaOwner, J */ enum TargetType { // see java.lang.annotation.ElementType - TYPE, - FIELD, - METHOD, - PARAMETER, - CONSTRUCTOR, - LOCAL_VARIABLE, - ANNOTATION_TYPE, - PACKAGE, - TYPE_USE, - TYPE_PARAMETER, - MODULE, - RECORD_COMPONENT, + TYPE(JavaLanguageLocalize.annotationTargetType()), + FIELD(JavaLanguageLocalize.annotationTargetField()), + METHOD(JavaLanguageLocalize.annotationTargetMethod()), + PARAMETER(JavaLanguageLocalize.annotationTargetParameter()), + CONSTRUCTOR(JavaLanguageLocalize.annotationTargetConstructor()), + LOCAL_VARIABLE(JavaLanguageLocalize.annotationTargetLocalVariable()), + ANNOTATION_TYPE(JavaLanguageLocalize.annotationTargetAnnotationType()), + PACKAGE(JavaLanguageLocalize.annotationTargetPackage()), + TYPE_USE(JavaLanguageLocalize.annotationTargetTypeUse()), + TYPE_PARAMETER(JavaLanguageLocalize.annotationTargetTypeParameter()), + MODULE(JavaLanguageLocalize.annotationTargetModule()), + RECORD_COMPONENT(JavaLanguageLocalize.annotationTargetRecordComponent()), // auxiliary value, used when it's impossible to determine annotation's targets - UNKNOWN; + UNKNOWN(LocalizeValue.of("?")); public static final TargetType[] EMPTY_ARRAY = {}; + + @Nonnull + private final LocalizeValue myPresentableText; + + TargetType(@Nonnull LocalizeValue presentableText) { + myPresentableText = presentableText; + } + + @Nonnull + public LocalizeValue getPresentableText() { + return myPresentableText; + } } /** @@ -82,7 +94,7 @@ enum TargetType { * @return the class name, or null if the annotation is unresolved. */ @Nullable - @NonNls + @Override String getQualifiedName(); /** @@ -161,9 +173,6 @@ default List getAttributes() { default PsiClass resolveAnnotationType() { PsiJavaCodeReferenceElement element = getNameReferenceElement(); PsiElement declaration = element == null ? null : element.resolve(); - if (!(declaration instanceof PsiClass) || !((PsiClass)declaration).isAnnotationType()) { - return null; - } - return (PsiClass)declaration; + return declaration instanceof PsiClass annotationClass && annotationClass.isAnnotationType() ? annotationClass : null; } } diff --git a/java-language-api/src/main/resources/LOCALIZE-LIB/en_US/consulo.java.language.JavaLanguageLocalize.yaml b/java-language-api/src/main/resources/LOCALIZE-LIB/en_US/consulo.java.language.JavaLanguageLocalize.yaml index ac74467b19..7d5a716ac8 100644 --- a/java-language-api/src/main/resources/LOCALIZE-LIB/en_US/consulo.java.language.JavaLanguageLocalize.yaml +++ b/java-language-api/src/main/resources/LOCALIZE-LIB/en_US/consulo.java.language.JavaLanguageLocalize.yaml @@ -505,3 +505,27 @@ filetype.description.class: text: Java class files filetype.description.java: text: Java source files +annotation.target.annotation.type: + text: annotation type +annotation.target.constructor: + text: constructor +annotation.target.field: + text: field +annotation.target.local.variable: + text: local variable +annotation.target.method: + text: method +annotation.target.module: + text: module +annotation.target.package: + text: package +annotation.target.parameter: + text: parameter +annotation.target.record.component: + text: record component +annotation.target.type: + text: type +annotation.target.type.parameter: + text: type parameter +annotation.target.type.use: + text: type use diff --git a/java-language-impl/src/main/resources/LOCALIZE-LIB/en_US/consulo.java.language.impl.JavaErrorLocalize.yaml b/java-language-impl/src/main/resources/LOCALIZE-LIB/en_US/consulo.java.language.impl.JavaErrorLocalize.yaml index 0b53b2675a..82f1a2ffe9 100644 --- a/java-language-impl/src/main/resources/LOCALIZE-LIB/en_US/consulo.java.language.impl.JavaErrorLocalize.yaml +++ b/java-language-impl/src/main/resources/LOCALIZE-LIB/en_US/consulo.java.language.impl.JavaErrorLocalize.yaml @@ -56,26 +56,6 @@ annotation.container.wrong.place: text: Container annotation ''{0}'' must not be present at the same time as the element it contains annotation.container.not.applicable: text: Container annotation ''@{0}'' is not applicable to {1} -annotation.target.ANNOTATION_TYPE: - text: annotation type -annotation.target.TYPE: - text: type -annotation.target.TYPE_USE: - text: type use -annotation.target.TYPE_PARAMETER: - text: type parameter -annotation.target.CONSTRUCTOR: - text: constructor -annotation.target.METHOD: - text: method -annotation.target.FIELD: - text: field -annotation.target.PARAMETER: - text: parameter -annotation.target.LOCAL_VARIABLE: - text: local variable -annotation.target.PACKAGE: - text: package generics.holder.type: text: Type generics.holder.method: