private static void addDynamicAnnotation( HighlightInfo info, GrReferenceExpression referenceExpression, HighlightDisplayKey key) { final PsiFile containingFile = referenceExpression.getContainingFile(); if (containingFile != null) { VirtualFile file = containingFile.getVirtualFile(); if (file == null) return; } else { return; } if (isCall(referenceExpression)) { PsiType[] argumentTypes = PsiUtil.getArgumentTypes(referenceExpression, false); if (argumentTypes != null) { QuickFixAction.registerQuickFixAction( info, referenceExpression.getTextRange(), new DynamicMethodFix(referenceExpression, argumentTypes), key); } } else { QuickFixAction.registerQuickFixAction( info, referenceExpression.getTextRange(), new DynamicPropertyFix(referenceExpression), key); } }
private static void registerReferenceFixes( GrReferenceExpression refExpr, HighlightInfo info, boolean compileStatic, final HighlightDisplayKey key) { PsiClass targetClass = QuickfixUtil.findTargetClass(refExpr, compileStatic); if (targetClass == null) return; if (!compileStatic) { addDynamicAnnotation(info, refExpr, key); } if (targetClass.isWritable()) { QuickFixAction.registerQuickFixAction( info, new CreateFieldFromUsageFix(refExpr, targetClass), key); if (refExpr.getParent() instanceof GrCall && refExpr.getParent() instanceof GrExpression) { QuickFixAction.registerQuickFixAction( info, new CreateMethodFromUsageFix(refExpr, targetClass), key); } } if (!refExpr.isQualified()) { GrVariableDeclarationOwner owner = PsiTreeUtil.getParentOfType(refExpr, GrVariableDeclarationOwner.class); if (!(owner instanceof GroovyFileBase) || ((GroovyFileBase) owner).isScript()) { QuickFixAction.registerQuickFixAction( info, new CreateLocalVariableFromUsageFix(refExpr, owner), key); } if (PsiTreeUtil.getParentOfType(refExpr, GrMethod.class) != null) { QuickFixAction.registerQuickFixAction(info, new CreateParameterFromUsageFix(refExpr), key); } } }
@Nullable static HighlightInfo checkFinalFieldInitialized(@NotNull PsiField field) { if (!field.hasModifierProperty(PsiModifier.FINAL)) return null; if (isFieldInitializedAfterObjectConstruction(field)) return null; String description = JavaErrorMessages.message("variable.not.initialized", field.getName()); TextRange range = HighlightNamesUtil.getFieldDeclarationTextRange(field); HighlightInfo highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) .range(range) .descriptionAndTooltip(description) .create(); QuickFixAction.registerQuickFixAction( highlightInfo, HighlightMethodUtil.getFixRange(field), QUICK_FIX_FACTORY.createCreateConstructorParameterFromFieldFix(field)); QuickFixAction.registerQuickFixAction( highlightInfo, HighlightMethodUtil.getFixRange(field), QUICK_FIX_FACTORY.createInitializeFinalFieldInConstructorFix(field)); final PsiClass containingClass = field.getContainingClass(); if (containingClass != null && !containingClass.isInterface()) { QuickFixAction.registerQuickFixAction( highlightInfo, QUICK_FIX_FACTORY.createModifierListFix(field, PsiModifier.FINAL, false, false)); } QuickFixAction.registerQuickFixAction( highlightInfo, QUICK_FIX_FACTORY.createAddVariableInitializerFix(field)); return highlightInfo; }
@Nullable public static HighlightInfo checkMissingReturnStatement(PsiCodeBlock body, PsiType returnType) { if (body == null || returnType == null || PsiType.VOID.equals(returnType.getDeepComponentType())) { return null; } // do not compute constant expressions for if() statement condition // see JLS 14.20 Unreachable Statements try { ControlFlow controlFlow = getControlFlowNoConstantEvaluate(body); if (!ControlFlowUtil.returnPresent(controlFlow)) { PsiJavaToken rBrace = body.getRBrace(); PsiElement context = rBrace == null ? body.getLastChild() : rBrace; String message = JavaErrorMessages.message("missing.return.statement"); HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) .range(context) .descriptionAndTooltip(message) .create(); PsiElement parent = body.getParent(); if (parent instanceof PsiMethod) { PsiMethod method = (PsiMethod) parent; QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createAddReturnFix(method)); QuickFixAction.registerQuickFixAction( info, QUICK_FIX_FACTORY.createMethodReturnFix(method, PsiType.VOID, true)); } return info; } } catch (AnalysisCanceledException ignored) { } return null; }
@Nullable static HighlightInfo checkFileDuplicates(@NotNull PsiJavaModule element, @NotNull PsiFile file) { Module module = ModuleUtilCore.findModuleForPsiElement(element); if (module != null) { Project project = file.getProject(); Collection<VirtualFile> others = FilenameIndex.getVirtualFilesByName(project, MODULE_INFO_FILE, new ModulesScope(module)); if (others.size() > 1) { String message = JavaErrorMessages.message("module.file.duplicate"); HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) .range(range(element)) .description(message) .create(); others .stream() .map(f -> PsiManager.getInstance(project).findFile(f)) .filter(f -> f != file) .findFirst() .ifPresent( duplicate -> QuickFixAction.registerQuickFixAction( info, new GoToSymbolFix( duplicate, JavaErrorMessages.message("module.open.duplicate.text")))); return info; } } return null; }
@Nullable private HighlightInfo reportAttributeProblem( final XmlTag tag, final String localName, final XmlAttribute attribute, final String localizedMessage) { final RemoveAttributeIntentionFix removeAttributeIntention = new RemoveAttributeIntentionFix(localName, attribute); if (!(tag instanceof HtmlTag)) { final HighlightInfoType tagProblemInfoType = HighlightInfoType.WRONG_REF; final ASTNode node = SourceTreeToPsiMap.psiElementToTree(attribute); assert node != null; final ASTNode child = XmlChildRole.ATTRIBUTE_NAME_FINDER.findChild(node); assert child != null; final HighlightInfo highlightInfo = HighlightInfo.createHighlightInfo(tagProblemInfoType, child, localizedMessage); addToResults(highlightInfo); QuickFixAction.registerQuickFixAction(highlightInfo, removeAttributeIntention); return highlightInfo; } return null; }
private void checkDuplicateAttribute(XmlTag tag, final XmlAttribute attribute) { if (skipValidation(tag)) { return; } final XmlAttribute[] attributes = tag.getAttributes(); final PsiFile containingFile = tag.getContainingFile(); final XmlExtension extension = containingFile instanceof XmlFile ? XmlExtension.getExtension(containingFile) : XmlExtension.DEFAULT_EXTENSION; for (XmlAttribute tagAttribute : attributes) { ProgressManager.checkCanceled(); if (attribute != tagAttribute && Comparing.strEqual(attribute.getName(), tagAttribute.getName())) { final String localName = attribute.getLocalName(); if (extension.canBeDuplicated(tagAttribute)) continue; // multiple import attributes are allowed in jsp directive HighlightInfo highlightInfo = HighlightInfo.createHighlightInfo( getTagProblemInfoType(tag), XmlChildRole.ATTRIBUTE_NAME_FINDER.findChild( SourceTreeToPsiMap.psiElementToTree(attribute)), XmlErrorMessages.message("duplicate.attribute", localName)); addToResults(highlightInfo); IntentionAction intentionAction = new RemoveAttributeIntentionFix(localName, attribute); QuickFixAction.registerQuickFixAction(highlightInfo, intentionAction); } } }
@NotNull static List<HighlightInfo> checkDuplicateRequires(@NotNull PsiJavaModule module) { List<HighlightInfo> results = ContainerUtil.newSmartList(); Map<String, PsiElement> map = ContainerUtil.newHashMap(); for (PsiElement child = module.getFirstChild(); child != null; child = child.getNextSibling()) { if (child instanceof PsiRequiresStatement) { PsiJavaModuleReferenceElement ref = ((PsiRequiresStatement) child).getReferenceElement(); if (ref != null) { String text = ref.getReferenceText(); if (!map.containsKey(text)) { map.put(text, child); } else { String message = JavaErrorMessages.message("module.duplicate.requires", text); HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) .range(child) .description(message) .create(); QuickFixAction.registerQuickFixAction(info, new DeleteElementFix(child)); results.add(info); } } } } return results; }
private static HighlightInfo annotationError(PsiAnnotation annotation, String message) { HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) .range(annotation) .descriptionAndTooltip(message) .create(); QuickFixAction.registerQuickFixAction(info, new DeleteAnnotationAction(annotation)); return info; }
private static void registerAddImportFixes( GrReferenceElement refElement, @Nullable HighlightInfo info, final HighlightDisplayKey key) { final String referenceName = refElement.getReferenceName(); //noinspection ConstantConditions if (StringUtil.isEmpty(referenceName)) return; if (!(refElement instanceof GrCodeReferenceElement) && Character.isLowerCase(referenceName.charAt(0))) return; if (refElement.getQualifier() != null) return; QuickFixAction.registerQuickFixAction(info, new GroovyAddImportAction(refElement), key); }
private static void registerStaticImportFix( @NotNull GrReferenceExpression referenceExpression, @Nullable HighlightInfo info, @Nullable final HighlightDisplayKey key) { final String referenceName = referenceExpression.getReferenceName(); if (StringUtil.isEmpty(referenceName)) return; if (referenceExpression.getQualifier() != null) return; QuickFixAction.registerQuickFixAction( info, new GroovyStaticImportMethodFix((GrMethodCall) referenceExpression.getParent()), key); }
@Nullable private List<? extends LocalQuickFix> registerFixes(final HighlightInfo info) { final List<LocalQuickFix> list = OrderEntryFix.registerFixes(new QuickFixActionRegistrarImpl(info), this); final String[] extendClasses = getExtendClassNames(); final String extendClass = extendClasses != null && extendClasses.length > 0 ? extendClasses[0] : null; final JavaClassReference[] references = getJavaClassReferenceSet().getAllReferences(); PsiPackage contextPackage = null; for (int i = myIndex; i >= 0; i--) { final PsiElement context = references[i].getContext(); if (context != null) { if (context instanceof PsiPackage) { contextPackage = (PsiPackage) context; } break; } } boolean createJavaClass = !canReferencePackage(); ClassKind kind = createJavaClass ? getClassKind() : null; if (createJavaClass && kind == null) kind = ClassKind.CLASS; final String templateName = JavaClassReferenceProvider.CLASS_TEMPLATE.getValue(getOptions()); final TextRange range = new TextRange( references[0].getRangeInElement().getStartOffset(), getRangeInElement().getEndOffset()); final String qualifiedName = range.substring(getElement().getText()); final CreateClassOrPackageFix action = CreateClassOrPackageFix.createFix( qualifiedName, getScope(), getElement(), contextPackage, kind, extendClass, templateName); if (action != null) { QuickFixAction.registerQuickFixAction(info, action); if (list == null) { return Arrays.asList(action); } else { final ArrayList<LocalQuickFix> fixes = new ArrayList<LocalQuickFix>(list.size() + 1); fixes.addAll(list); fixes.add(action); return fixes; } } return list; }
private static void addEmptyIntentionIfNeeded(@Nullable HighlightInfo info) { if (info != null) { int s1 = info.quickFixActionMarkers != null ? info.quickFixActionMarkers.size() : 0; int s2 = info.quickFixActionRanges != null ? info.quickFixActionRanges.size() : 0; if (s1 + s2 == 0) { EmptyIntentionAction emptyIntentionAction = new EmptyIntentionAction("Access to unresolved expression"); QuickFixAction.registerQuickFixAction( info, emptyIntentionAction, HighlightDisplayKey.find(SHORT_NAME)); } } }
@Nullable static HighlightInfo checkVariableMustBeFinal( PsiVariable variable, PsiJavaCodeReferenceElement context, @NotNull LanguageLevel languageLevel) { if (variable.hasModifierProperty(PsiModifier.FINAL)) return null; final PsiClass innerClass = getInnerClassVariableReferencedFrom(variable, context); if (innerClass != null) { if (variable instanceof PsiParameter) { final PsiElement parent = variable.getParent(); if (parent instanceof PsiParameterList && parent.getParent() instanceof PsiLambdaExpression && notAccessedForWriting( variable, new LocalSearchScope(((PsiParameter) variable).getDeclarationScope()))) { return null; } } if (languageLevel.isAtLeast(LanguageLevel.JDK_1_8) && isEffectivelyFinal(variable, innerClass, context)) { return null; } final String description = JavaErrorMessages.message("variable.must.be.final", context.getText()); final HighlightInfo highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) .range(context) .descriptionAndTooltip(description) .create(); QuickFixAction.registerQuickFixAction( highlightInfo, QUICK_FIX_FACTORY.createVariableAccessFromInnerClassFix(variable, innerClass)); return highlightInfo; } final PsiLambdaExpression lambdaExpression = PsiTreeUtil.getParentOfType(context, PsiLambdaExpression.class); if (lambdaExpression != null && !PsiTreeUtil.isAncestor(lambdaExpression, variable, true)) { final PsiElement parent = variable.getParent(); if (parent instanceof PsiParameterList && parent.getParent() == lambdaExpression) { return null; } if (!isEffectivelyFinal(variable, lambdaExpression, context)) { return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) .range(context) .descriptionAndTooltip("Variable used in lambda expression should be effectively final") .create(); } } return null; }
@Nullable static HighlightInfo checkFileName(@NotNull PsiJavaModule element, @NotNull PsiFile file) { if (!MODULE_INFO_FILE.equals(file.getName())) { String message = JavaErrorMessages.message("module.file.wrong.name"); HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) .range(range(element)) .description(message) .create(); QuickFixAction.registerQuickFixAction(info, factory().createRenameFileFix(MODULE_INFO_FILE)); return info; } return null; }
private static void appendFixes( @Nullable TextRange fixedRange, HighlightInfo info, List<Annotation.QuickFixInfo> fixes) { if (fixes != null) { for (final Annotation.QuickFixInfo quickFixInfo : fixes) { QuickFixAction.registerQuickFixAction( info, fixedRange != null ? fixedRange : quickFixInfo.textRange, quickFixInfo.quickFix, quickFixInfo.key != null ? quickFixInfo.key : HighlightDisplayKey.find( DefaultHighlightVisitorBasedInspection.AnnotatorBasedInspection .ANNOTATOR_SHORT_NAME)); } } }
private static void registerCreateClassByTypeFix( GrReferenceElement refElement, @Nullable HighlightInfo info, final HighlightDisplayKey key) { GrPackageDefinition packageDefinition = PsiTreeUtil.getParentOfType(refElement, GrPackageDefinition.class); if (packageDefinition != null) return; PsiElement parent = refElement.getParent(); if (parent instanceof GrNewExpression && refElement .getManager() .areElementsEquivalent(((GrNewExpression) parent).getReferenceElement(), refElement)) { QuickFixAction.registerQuickFixAction( info, CreateClassFix.createClassFromNewAction((GrNewExpression) parent), key); } else { if (shouldBeInterface(refElement)) { QuickFixAction.registerQuickFixAction( info, CreateClassFix.createClassFixAction(refElement, CreateClassKind.INTERFACE), key); } else if (shouldBeClass(refElement)) { QuickFixAction.registerQuickFixAction( info, CreateClassFix.createClassFixAction(refElement, CreateClassKind.CLASS), key); QuickFixAction.registerQuickFixAction( info, CreateClassFix.createClassFixAction(refElement, CreateClassKind.ENUM), key); } else if (shouldBeAnnotation(refElement)) { QuickFixAction.registerQuickFixAction( info, CreateClassFix.createClassFixAction(refElement, CreateClassKind.ANNOTATION), key); } else { QuickFixAction.registerQuickFixAction( info, CreateClassFix.createClassFixAction(refElement, CreateClassKind.CLASS), key); QuickFixAction.registerQuickFixAction( info, CreateClassFix.createClassFixAction(refElement, CreateClassKind.INTERFACE), key); QuickFixAction.registerQuickFixAction( info, CreateClassFix.createClassFixAction(refElement, CreateClassKind.ENUM), key); QuickFixAction.registerQuickFixAction( info, CreateClassFix.createClassFixAction(refElement, CreateClassKind.ANNOTATION), key); } } }
@Nullable static HighlightInfo checkFinalVariableInitializedInLoop( @NotNull PsiReferenceExpression expression, @NotNull PsiElement resolved) { if (ControlFlowUtil.isVariableAssignedInLoop(expression, resolved)) { String description = JavaErrorMessages.message( "variable.assigned.in.loop", ((PsiVariable) resolved).getName()); final HighlightInfo highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) .range(expression) .descriptionAndTooltip(description) .create(); QuickFixAction.registerQuickFixAction( highlightInfo, QUICK_FIX_FACTORY.createModifierListFix( (PsiVariable) resolved, PsiModifier.FINAL, false, false)); return highlightInfo; } return null; }
public void addMessage( final PsiElement context, final String message, final ErrorType type, final IntentionAction... fixes) { if (message != null && message.length() > 0) { final PsiFile containingFile = context.getContainingFile(); final HighlightInfoType defaultInfoType = type == ErrorType.ERROR ? HighlightInfoType.ERROR : type == ErrorType.WARNING ? HighlightInfoType.WARNING : HighlightInfoType.WEAK_WARNING; if (context instanceof XmlTag && XmlExtension.getExtension(containingFile).shouldBeHighlightedAsTag((XmlTag) context)) { addElementsForTagWithManyQuickFixes((XmlTag) context, message, defaultInfoType, fixes); } else { final PsiElement contextOfFile = containingFile.getContext(); final HighlightInfo highlightInfo; if (contextOfFile != null) { TextRange range = InjectedLanguageManager.getInstance(context.getProject()) .injectedToHost(context, context.getTextRange()); highlightInfo = HighlightInfo.createHighlightInfo(defaultInfoType, range, message); } else { highlightInfo = HighlightInfo.createHighlightInfo(HighlightInfoType.WRONG_REF, context, message); } if (fixes != null) { for (final IntentionAction quickFixAction : fixes) { if (quickFixAction == null) continue; QuickFixAction.registerQuickFixAction(highlightInfo, quickFixAction); } } addToResults(highlightInfo); } } }
@Nullable static HighlightInfo checkVariableMustBeFinal( @NotNull PsiVariable variable, @NotNull PsiJavaCodeReferenceElement context, @NotNull LanguageLevel languageLevel) { if (variable.hasModifierProperty(PsiModifier.FINAL)) return null; final PsiElement innerClass = getInnerClassVariableReferencedFrom(variable, context); if (innerClass instanceof PsiClass) { if (variable instanceof PsiParameter) { final PsiElement parent = variable.getParent(); if (parent instanceof PsiParameterList && parent.getParent() instanceof PsiLambdaExpression && notAccessedForWriting( variable, new LocalSearchScope(((PsiParameter) variable).getDeclarationScope()))) { return null; } } final boolean isToBeEffectivelyFinal = languageLevel.isAtLeast(LanguageLevel.JDK_1_8); if (isToBeEffectivelyFinal && isEffectivelyFinal(variable, innerClass, context)) { return null; } final String description = JavaErrorMessages.message( isToBeEffectivelyFinal ? "variable.must.be.final.or.effectively.final" : "variable.must.be.final", context.getText()); final HighlightInfo highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) .range(context) .descriptionAndTooltip(description) .create(); QuickFixAction.registerQuickFixAction( highlightInfo, QUICK_FIX_FACTORY.createVariableAccessFromInnerClassFix(variable, innerClass)); return highlightInfo; } return checkWriteToFinalInsideLambda(variable, context); }
@Nullable static HighlightInfo checkFileLocation(@NotNull PsiJavaModule element, @NotNull PsiFile file) { VirtualFile vFile = file.getVirtualFile(); if (vFile != null) { VirtualFile root = ProjectFileIndex.SERVICE.getInstance(file.getProject()).getSourceRootForFile(vFile); if (root != null && !root.equals(vFile.getParent())) { String message = JavaErrorMessages.message("module.file.wrong.location"); HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.WARNING) .range(range(element)) .description(message) .create(); QuickFixAction.registerQuickFixAction( info, new MoveFileFix(vFile, root, QuickFixBundle.message("move.file.to.source.root.text"))); return info; } } return null; }
@Nullable public static HighlightInfo checkNameValuePair(PsiNameValuePair pair) { PsiReference ref = pair.getReference(); if (ref == null) return null; PsiMethod method = (PsiMethod) ref.resolve(); if (method == null) { if (pair.getName() != null) { final String description = JavaErrorMessages.message("annotation.unknown.method", ref.getCanonicalText()); PsiElement element = ref.getElement(); final HighlightInfo highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.WRONG_REF) .range(element) .descriptionAndTooltip(description) .create(); QuickFixAction.registerQuickFixAction( highlightInfo, QuickFixFactory.getInstance().createCreateAnnotationMethodFromUsageFix(pair)); return highlightInfo; } else { String description = JavaErrorMessages.message("annotation.missing.method", ref.getCanonicalText()); PsiElement element = ref.getElement(); return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) .range(element) .descriptionAndTooltip(description) .create(); } } else { PsiType returnType = method.getReturnType(); assert returnType != null : method; PsiAnnotationMemberValue value = pair.getValue(); HighlightInfo info = checkMemberValueType(value, returnType); if (info != null) return info; return checkDuplicateAttribute(pair); } }
private void bindMessageToAstNode( final PsiElement childByRole, final HighlightInfoType warning, final int offset, int length, final String localizedMessage, IntentionAction... quickFixActions) { if (childByRole != null) { final TextRange textRange = childByRole.getTextRange(); if (length == -1) length = textRange.getLength(); final int startOffset = textRange.getStartOffset() + offset; HighlightInfo highlightInfo = HighlightInfo.createHighlightInfo( warning, childByRole, startOffset, startOffset + length, localizedMessage, HighlightInfo.htmlEscapeToolTip(localizedMessage)); if (highlightInfo == null) { highlightInfo = HighlightInfo.createHighlightInfo( warning, new TextRange(startOffset, startOffset + length), localizedMessage, localizedMessage, NONEMPTY_TEXT_ATTRIBUTES); } for (final IntentionAction quickFixAction : quickFixActions) { if (quickFixAction == null) continue; QuickFixAction.registerQuickFixAction(highlightInfo, textRange, quickFixAction, null); } addToResults(highlightInfo); } }
private static HighlightInfo checkWriteToFinalInsideLambda( @NotNull PsiVariable variable, @NotNull PsiJavaCodeReferenceElement context) { final PsiLambdaExpression lambdaExpression = PsiTreeUtil.getParentOfType(context, PsiLambdaExpression.class); if (lambdaExpression != null && !PsiTreeUtil.isAncestor(lambdaExpression, variable, true)) { final PsiElement parent = variable.getParent(); if (parent instanceof PsiParameterList && parent.getParent() == lambdaExpression) { return null; } if (!isEffectivelyFinal(variable, lambdaExpression, context)) { String text = JavaErrorMessages.message("lambda.variable.must.be.final"); HighlightInfo highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) .range(context) .descriptionAndTooltip(text) .create(); QuickFixAction.registerQuickFixAction( highlightInfo, QUICK_FIX_FACTORY.createVariableAccessFromInnerClassFix(variable, lambdaExpression)); return highlightInfo; } } return null; }
@Override public void register(IntentionAction action) { myKey = HighlightDisplayKey.find(SHORT_NAME); QuickFixAction.registerQuickFixAction(myInfo, action, myKey); }
@Override public void register(TextRange fixRange, IntentionAction action, HighlightDisplayKey key) { QuickFixAction.registerQuickFixAction(myInfo, fixRange, action, key); }
@Nullable static HighlightInfo checkCannotWriteToFinal( @NotNull PsiExpression expression, @NotNull PsiFile containingFile) { PsiReferenceExpression reference = null; boolean readBeforeWrite = false; if (expression instanceof PsiAssignmentExpression) { final PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression) expression; final PsiExpression left = PsiUtil.skipParenthesizedExprDown(assignmentExpression.getLExpression()); if (left instanceof PsiReferenceExpression) { reference = (PsiReferenceExpression) left; } readBeforeWrite = assignmentExpression.getOperationTokenType() != JavaTokenType.EQ; } else if (expression instanceof PsiPostfixExpression) { final PsiExpression operand = PsiUtil.skipParenthesizedExprDown(((PsiPostfixExpression) expression).getOperand()); final IElementType sign = ((PsiPostfixExpression) expression).getOperationTokenType(); if (operand instanceof PsiReferenceExpression && (sign == JavaTokenType.PLUSPLUS || sign == JavaTokenType.MINUSMINUS)) { reference = (PsiReferenceExpression) operand; } readBeforeWrite = true; } else if (expression instanceof PsiPrefixExpression) { final PsiExpression operand = PsiUtil.skipParenthesizedExprDown(((PsiPrefixExpression) expression).getOperand()); final IElementType sign = ((PsiPrefixExpression) expression).getOperationTokenType(); if (operand instanceof PsiReferenceExpression && (sign == JavaTokenType.PLUSPLUS || sign == JavaTokenType.MINUSMINUS)) { reference = (PsiReferenceExpression) operand; } readBeforeWrite = true; } final PsiElement resolved = reference == null ? null : reference.resolve(); PsiVariable variable = resolved instanceof PsiVariable ? (PsiVariable) resolved : null; if (variable == null || !variable.hasModifierProperty(PsiModifier.FINAL)) return null; final boolean canWrite = canWriteToFinal(variable, expression, reference, containingFile) && checkWriteToFinalInsideLambda(variable, reference) == null; if (readBeforeWrite || !canWrite) { final String name = variable.getName(); String description = canWrite ? JavaErrorMessages.message("variable.not.initialized", name) : JavaErrorMessages.message("assignment.to.final.variable", name); final HighlightInfo highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) .range(reference.getTextRange()) .descriptionAndTooltip(description) .create(); final PsiElement innerClass = getInnerClassVariableReferencedFrom(variable, expression); if (innerClass == null || variable instanceof PsiField) { QuickFixAction.registerQuickFixAction( highlightInfo, QUICK_FIX_FACTORY.createModifierListFix(variable, PsiModifier.FINAL, false, false)); } else { QuickFixAction.registerQuickFixAction( highlightInfo, QUICK_FIX_FACTORY.createVariableAccessFromInnerClassFix(variable, innerClass)); } return highlightInfo; } return null; }
@Override public void unregister(Condition<IntentionAction> condition) { if (myInfo != null) { QuickFixAction.unregisterQuickFixAction(myInfo, condition); } }
@Nullable public static HighlightInfo checkFinalVariableMightAlreadyHaveBeenAssignedTo( @NotNull PsiVariable variable, @NotNull PsiReferenceExpression expression, @NotNull Map<PsiElement, Collection<ControlFlowUtil.VariableInfo>> finalVarProblems) { if (!PsiUtil.isAccessedForWriting(expression)) return null; final PsiElement scope = variable instanceof PsiField ? variable.getParent() : variable.getParent() == null ? null : variable.getParent().getParent(); PsiElement codeBlock = PsiUtil.getTopLevelEnclosingCodeBlock(expression, scope); if (codeBlock == null) return null; Collection<ControlFlowUtil.VariableInfo> codeBlockProblems = getFinalVariableProblemsInBlock(finalVarProblems, codeBlock); boolean alreadyAssigned = false; for (ControlFlowUtil.VariableInfo variableInfo : codeBlockProblems) { if (variableInfo.expression == expression) { alreadyAssigned = true; break; } } if (!alreadyAssigned) { if (!(variable instanceof PsiField)) return null; final PsiField field = (PsiField) variable; final PsiClass aClass = field.getContainingClass(); if (aClass == null) return null; // field can get assigned in other field initializers final PsiField[] fields = aClass.getFields(); boolean isFieldStatic = field.hasModifierProperty(PsiModifier.STATIC); for (PsiField psiField : fields) { PsiExpression initializer = psiField.getInitializer(); if (psiField != field && psiField.hasModifierProperty(PsiModifier.STATIC) == isFieldStatic && initializer != null && initializer != codeBlock && !variableDefinitelyNotAssignedIn(field, initializer)) { alreadyAssigned = true; break; } } if (!alreadyAssigned) { // field can get assigned in class initializers final PsiMember enclosingConstructorOrInitializer = PsiUtil.findEnclosingConstructorOrInitializer(expression); if (enclosingConstructorOrInitializer == null || !aClass .getManager() .areElementsEquivalent( enclosingConstructorOrInitializer.getContainingClass(), aClass)) { return null; } final PsiClassInitializer[] initializers = aClass.getInitializers(); for (PsiClassInitializer initializer : initializers) { if (initializer.hasModifierProperty(PsiModifier.STATIC) == field.hasModifierProperty(PsiModifier.STATIC)) { final PsiCodeBlock body = initializer.getBody(); if (body == codeBlock) return null; try { final ControlFlow controlFlow = getControlFlow(body); if (!ControlFlowUtil.isVariableDefinitelyNotAssigned(field, controlFlow)) { alreadyAssigned = true; break; } } catch (AnalysisCanceledException e) { // incomplete code return null; } } } } if (!alreadyAssigned && !field.hasModifierProperty(PsiModifier.STATIC)) { // then check if instance field already assigned in other constructor final PsiMethod ctr = codeBlock.getParent() instanceof PsiMethod ? (PsiMethod) codeBlock.getParent() : null; // assignment to final field in several constructors threatens us only if these are linked // (there is this() call in the beginning) final List<PsiMethod> redirectedConstructors = ctr != null && ctr.isConstructor() ? JavaHighlightUtil.getChainedConstructors(ctr) : null; for (int j = 0; redirectedConstructors != null && j < redirectedConstructors.size(); j++) { PsiMethod redirectedConstructor = redirectedConstructors.get(j); PsiCodeBlock body = redirectedConstructor.getBody(); if (body != null && variableDefinitelyAssignedIn(variable, body)) { alreadyAssigned = true; break; } } } } if (alreadyAssigned) { String description = JavaErrorMessages.message("variable.already.assigned", variable.getName()); final HighlightInfo highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) .range(expression) .descriptionAndTooltip(description) .create(); QuickFixAction.registerQuickFixAction( highlightInfo, QUICK_FIX_FACTORY.createModifierListFix(variable, PsiModifier.FINAL, false, false)); QuickFixAction.registerQuickFixAction( highlightInfo, QUICK_FIX_FACTORY.createDeferFinalAssignmentFix(variable, expression)); return highlightInfo; } return null; }
@Nullable public static HighlightInfo checkVariableInitializedBeforeUsage( @NotNull PsiReferenceExpression expression, @NotNull PsiVariable variable, @NotNull Map<PsiElement, Collection<PsiReferenceExpression>> uninitializedVarProblems, @NotNull PsiFile containingFile) { if (variable instanceof ImplicitVariable) return null; if (!PsiUtil.isAccessedForReading(expression)) return null; int startOffset = expression.getTextRange().getStartOffset(); final PsiElement topBlock; if (variable.hasInitializer()) { topBlock = PsiUtil.getVariableCodeBlock(variable, variable); if (topBlock == null) return null; } else { PsiElement scope = variable instanceof PsiField ? ((PsiField) variable).getContainingClass() : variable.getParent() != null ? variable.getParent().getParent() : null; if (scope instanceof PsiCodeBlock && scope.getParent() instanceof PsiSwitchStatement) { scope = PsiTreeUtil.getParentOfType(scope, PsiCodeBlock.class); } topBlock = FileTypeUtils.isInServerPageFile(scope) && scope instanceof PsiFile ? scope : PsiUtil.getTopLevelEnclosingCodeBlock(expression, scope); if (variable instanceof PsiField) { // non final field already initialized with default value if (!variable.hasModifierProperty(PsiModifier.FINAL)) return null; // final field may be initialized in ctor or class initializer only // if we're inside non-ctr method, skip it if (PsiUtil.findEnclosingConstructorOrInitializer(expression) == null && HighlightUtil.findEnclosingFieldInitializer(expression) == null) { return null; } if (topBlock == null) return null; final PsiElement parent = topBlock.getParent(); // access to final fields from inner classes always allowed if (inInnerClass(expression, ((PsiField) variable).getContainingClass(), containingFile)) return null; final PsiCodeBlock block; final PsiClass aClass; if (parent instanceof PsiMethod) { PsiMethod constructor = (PsiMethod) parent; if (!containingFile .getManager() .areElementsEquivalent( constructor.getContainingClass(), ((PsiField) variable).getContainingClass())) return null; // static variables already initialized in class initializers if (variable.hasModifierProperty(PsiModifier.STATIC)) return null; // as a last chance, field may be initialized in this() call final List<PsiMethod> redirectedConstructors = JavaHighlightUtil.getChainedConstructors(constructor); for (int j = 0; redirectedConstructors != null && j < redirectedConstructors.size(); j++) { PsiMethod redirectedConstructor = redirectedConstructors.get(j); // variable must be initialized before its usage // ??? // if (startOffset < redirectedConstructor.getTextRange().getStartOffset()) continue; PsiCodeBlock body = redirectedConstructor.getBody(); if (body != null && variableDefinitelyAssignedIn(variable, body)) { return null; } } block = constructor.getBody(); aClass = constructor.getContainingClass(); } else if (parent instanceof PsiClassInitializer) { final PsiClassInitializer classInitializer = (PsiClassInitializer) parent; if (!containingFile .getManager() .areElementsEquivalent( classInitializer.getContainingClass(), ((PsiField) variable).getContainingClass())) return null; block = classInitializer.getBody(); aClass = classInitializer.getContainingClass(); } else { // field reference outside code block // check variable initialized before its usage final PsiField field = (PsiField) variable; aClass = field.getContainingClass(); if (aClass == null || isFieldInitializedInOtherFieldInitializer( aClass, field, field.hasModifierProperty(PsiModifier.STATIC))) { return null; } final PsiField anotherField = PsiTreeUtil.getTopmostParentOfType(expression, PsiField.class); int offset = startOffset; if (anotherField != null && anotherField.getContainingClass() == aClass && !field.hasModifierProperty(PsiModifier.STATIC)) { offset = 0; } block = null; // initializers will be checked later final PsiMethod[] constructors = aClass.getConstructors(); for (PsiMethod constructor : constructors) { // variable must be initialized before its usage if (offset < constructor.getTextRange().getStartOffset()) continue; PsiCodeBlock body = constructor.getBody(); if (body != null && variableDefinitelyAssignedIn(variable, body)) { return null; } // as a last chance, field may be initialized in this() call final List<PsiMethod> redirectedConstructors = JavaHighlightUtil.getChainedConstructors(constructor); for (int j = 0; redirectedConstructors != null && j < redirectedConstructors.size(); j++) { PsiMethod redirectedConstructor = redirectedConstructors.get(j); // variable must be initialized before its usage if (offset < redirectedConstructor.getTextRange().getStartOffset()) continue; PsiCodeBlock redirectedBody = redirectedConstructor.getBody(); if (redirectedBody != null && variableDefinitelyAssignedIn(variable, redirectedBody)) { return null; } } } } if (aClass != null) { // field may be initialized in class initializer final PsiClassInitializer[] initializers = aClass.getInitializers(); for (PsiClassInitializer initializer : initializers) { PsiCodeBlock body = initializer.getBody(); if (body == block) break; // variable referenced in initializer must be initialized in initializer preceding // assignment // variable referenced in field initializer or in class initializer boolean shouldCheckInitializerOrder = block == null || block.getParent() instanceof PsiClassInitializer; if (shouldCheckInitializerOrder && startOffset < initializer.getTextRange().getStartOffset()) continue; if (initializer.hasModifierProperty(PsiModifier.STATIC) == variable.hasModifierProperty(PsiModifier.STATIC)) { if (variableDefinitelyAssignedIn(variable, body)) return null; } } } } } if (topBlock == null) return null; Collection<PsiReferenceExpression> codeBlockProblems = uninitializedVarProblems.get(topBlock); if (codeBlockProblems == null) { try { final ControlFlow controlFlow = getControlFlow(topBlock); codeBlockProblems = ControlFlowUtil.getReadBeforeWriteLocals(controlFlow); } catch (AnalysisCanceledException | IndexNotReadyException e) { codeBlockProblems = Collections.emptyList(); } uninitializedVarProblems.put(topBlock, codeBlockProblems); } if (codeBlockProblems.contains(expression)) { final String name = expression.getElement().getText(); String description = JavaErrorMessages.message("variable.not.initialized", name); HighlightInfo highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) .range(expression) .descriptionAndTooltip(description) .create(); QuickFixAction.registerQuickFixAction( highlightInfo, QUICK_FIX_FACTORY.createAddVariableInitializerFix(variable)); if (variable instanceof PsiField) { QuickFixAction.registerQuickFixAction( highlightInfo, QUICK_FIX_FACTORY.createModifierListFix(variable, PsiModifier.FINAL, false, false)); } return highlightInfo; } return null; }