private PsiElement getStatementToInsertBefore() { PsiElement declarationScope = myVariable instanceof PsiParameter ? ((PsiParameter) myVariable).getDeclarationScope() : PsiUtil.getVariableCodeBlock(myVariable, null); if (declarationScope == null) return null; PsiElement statement = myClass; nextInnerClass: do { statement = PsiUtil.getEnclosingStatement(statement); if (statement == null || statement.getParent() == null) { return null; } PsiElement element = statement; while (element != declarationScope && !(element instanceof PsiFile)) { if (element instanceof PsiClass) { statement = statement.getParent(); continue nextInnerClass; } element = element.getParent(); } return statement; } while (true); }
private static int getQuickFixType(PsiVariable variable) { PsiElement outerCodeBlock = PsiUtil.getVariableCodeBlock(variable, null); if (outerCodeBlock == null) return -1; List<PsiReferenceExpression> outerReferences = new ArrayList<PsiReferenceExpression>(); collectReferences(outerCodeBlock, variable, outerReferences); int type = MAKE_FINAL; for (PsiReferenceExpression expression : outerReferences) { // if it happens that variable referenced from another inner class, make sure it can be make // final from there PsiClass innerClass = HighlightControlFlowUtil.getInnerClassVariableReferencedFrom(variable, expression); if (innerClass != null) { int thisType = MAKE_FINAL; if (writtenInside(variable, innerClass)) { // cannot make parameter array if (variable instanceof PsiParameter) return -1; thisType = MAKE_ARRAY; } if (thisType == MAKE_FINAL && !canBeFinal(variable, outerReferences)) { thisType = COPY_TO_FINAL; } type = Math.max(type, thisType); } } return type; }
private static boolean isEffectivelyFinal( PsiVariable variable, PsiElement scope, PsiJavaCodeReferenceElement context) { boolean effectivelyFinal; if (variable instanceof PsiParameter) { effectivelyFinal = notAccessedForWriting( variable, new LocalSearchScope(((PsiParameter) variable).getDeclarationScope())); } else { final ControlFlow controlFlow; try { controlFlow = getControlFlow(PsiUtil.getVariableCodeBlock(variable, context)); } catch (AnalysisCanceledException e) { return true; } if (ControlFlowUtil.isVariableDefinitelyAssigned(variable, controlFlow)) { final Collection<ControlFlowUtil.VariableInfo> initializedTwice = ControlFlowUtil.getInitializedTwice(controlFlow); effectivelyFinal = !initializedTwice.contains(new ControlFlowUtil.VariableInfo(variable, null)); if (effectivelyFinal) { effectivelyFinal = notAccessedForWriting(variable, new LocalSearchScope(scope)); } } else { effectivelyFinal = false; } } return effectivelyFinal; }
private void removeVariableAndReferencingStatements(Editor editor) { final List<PsiElement> references = new ArrayList<>(); final List<PsiElement> sideEffects = new ArrayList<>(); final boolean[] canCopeWithSideEffects = {true}; try { PsiElement context = myVariable instanceof PsiField ? ((PsiField) myVariable).getContainingClass() : PsiUtil.getVariableCodeBlock(myVariable, null); if (context != null) { RemoveUnusedVariableUtil.collectReferences(context, myVariable, references); } // do not forget to delete variable declaration references.add(myVariable); // check for side effects for (PsiElement element : references) { Boolean result = RemoveUnusedVariableUtil.processUsage( element, myVariable, sideEffects, RemoveUnusedVariableUtil.RemoveMode.CANCEL); if (result == null) return; canCopeWithSideEffects[0] &= result; } } catch (IncorrectOperationException e) { LOG.error(e); } final RemoveUnusedVariableUtil.RemoveMode deleteMode = showSideEffectsWarning(sideEffects, myVariable, editor, canCopeWithSideEffects[0]); ApplicationManager.getApplication() .runWriteAction( () -> { try { RemoveUnusedVariableUtil.deleteReferences(myVariable, references, deleteMode); } catch (IncorrectOperationException e) { LOG.error(e); } }); }
public static boolean isEffectivelyFinal( @NotNull PsiVariable variable, @NotNull 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<PsiReferenceExpression> readBeforeWriteLocals = ControlFlowUtil.getReadBeforeWriteLocals(controlFlow); for (PsiReferenceExpression expression : readBeforeWriteLocals) { if (expression.resolve() == variable) { return PsiUtil.isAccessedForReading(expression); } } final Collection<ControlFlowUtil.VariableInfo> initializedTwice = ControlFlowUtil.getInitializedTwice(controlFlow); effectivelyFinal = !initializedTwice.contains(new ControlFlowUtil.VariableInfo(variable, null)); if (effectivelyFinal) { effectivelyFinal = notAccessedForWriting(variable, new LocalSearchScope(scope)); } } return effectivelyFinal; }
private void makeArray(PsiVariable variable) throws IncorrectOperationException { PsiType type = variable.getType(); PsiElementFactory factory = JavaPsiFacade.getInstance(myClass.getProject()).getElementFactory(); PsiType newType = type.createArrayType(); PsiDeclarationStatement variableDeclarationStatement; PsiExpression initializer = variable.getInitializer(); if (initializer == null) { String expression = "[1]"; while (type instanceof PsiArrayType) { expression += "[1]"; type = ((PsiArrayType) type).getComponentType(); } PsiExpression init = factory.createExpressionFromText("new " + type.getCanonicalText() + expression, variable); variableDeclarationStatement = factory.createVariableDeclarationStatement(variable.getName(), newType, init); } else { PsiExpression init = factory.createExpressionFromText("{ " + initializer.getText() + " }", variable); variableDeclarationStatement = factory.createVariableDeclarationStatement(variable.getName(), newType, init); } PsiVariable newVariable = (PsiVariable) variableDeclarationStatement.getDeclaredElements()[0]; PsiUtil.setModifierProperty(newVariable, PsiModifier.FINAL, true); PsiElement newExpression = factory.createExpressionFromText(variable.getName() + "[0]", variable); PsiElement outerCodeBlock = PsiUtil.getVariableCodeBlock(variable, null); if (outerCodeBlock == null) return; List<PsiReferenceExpression> outerReferences = new ArrayList<PsiReferenceExpression>(); collectReferences(outerCodeBlock, variable, outerReferences); replaceReferences(outerReferences, newExpression); variable.replace(newVariable); }
@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; }