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 void makeVariableFinalIfNeeded( InsertionContext context, @Nullable PsiReferenceExpression ref) { if (!Registry.is("java.completion.make.outer.variables.final") || ref == null || PsiUtil.isLanguageLevel8OrHigher(ref) || JspPsiUtil.isInJspFile(ref)) { return; } PsiElement target = ref.resolve(); if (target instanceof PsiLocalVariable || target instanceof PsiParameter) { PsiClass placeClass = PsiTreeUtil.findElementOfClassAtOffset( context.getFile(), context.getTailOffset() - 1, PsiClass.class, false); if (placeClass != null && !PsiTreeUtil.isAncestor(placeClass, target, true) && !HighlightControlFlowUtil.isReassigned( (PsiVariable) target, new HashMap<PsiElement, Collection<ControlFlowUtil.VariableInfo>>())) { PsiModifierList modifierList = ((PsiVariable) target).getModifierList(); if (modifierList != null) { modifierList.setModifierProperty(PsiModifier.FINAL, true); } } } }
public PsiReturnStatement addReturnForMethod(final PsiFile file, final PsiMethod method) { final PsiModifierList modifiers = method.getModifierList(); if (modifiers.hasModifierProperty(PsiModifier.ABSTRACT) || method.getBody() == null) { return null; } try { final ConvertReturnStatementsVisitor visitor = new ConvertReturnStatementsVisitor(factory, method, myTargetType); ControlFlow controlFlow; try { controlFlow = HighlightControlFlowUtil.getControlFlowNoConstantEvaluate(method.getBody()); } catch (AnalysisCanceledException e) { return null; // must be an error } PsiReturnStatement returnStatement; if (controlFlow != null && ControlFlowUtil.processReturns(controlFlow, visitor)) { // extra return statement not needed // get latest modified return statement and select... returnStatement = visitor.getLatestReturn(); } else { returnStatement = visitor.createReturnInLastStatement(); } if (method.getContainingFile() != file) { UndoUtil.markPsiFileForUndo(file); } return returnStatement; } catch (IncorrectOperationException e) { LOG.error(e); } return null; }
private static boolean canBeFinal(PsiVariable variable, List<PsiReferenceExpression> references) { // if there is at least one assignment to this variable, it cannot be final Map<PsiElement, Collection<PsiReferenceExpression>> uninitializedVarProblems = new THashMap<PsiElement, Collection<PsiReferenceExpression>>(); Map<PsiElement, Collection<ControlFlowUtil.VariableInfo>> finalVarProblems = new THashMap<PsiElement, Collection<ControlFlowUtil.VariableInfo>>(); for (PsiReferenceExpression expression : references) { if (ControlFlowUtil.isVariableAssignedInLoop(expression, variable)) return false; HighlightInfo highlightInfo = HighlightControlFlowUtil.checkVariableInitializedBeforeUsage( expression, variable, uninitializedVarProblems); if (highlightInfo != null) return false; highlightInfo = HighlightControlFlowUtil.checkFinalVariableMightAlreadyHaveBeenAssignedTo( variable, expression, finalVarProblems); if (highlightInfo != null) return false; if (variable instanceof PsiParameter && PsiUtil.isAccessedForWriting(expression)) return false; } return true; }
@Override public void onReferencesBuild(RefElement refElement) { if (refElement instanceof RefClass) { final PsiClass psiClass = (PsiClass) refElement.getElement(); if (psiClass != null) { if (refElement.isEntry()) { ((RefClassImpl) refElement).setFlag(false, CAN_BE_FINAL_MASK); } PsiMethod[] psiMethods = psiClass.getMethods(); PsiField[] psiFields = psiClass.getFields(); HashSet<PsiVariable> allFields = new HashSet<PsiVariable>(); ContainerUtil.addAll(allFields, psiFields); ArrayList<PsiVariable> instanceInitializerInitializedFields = new ArrayList<PsiVariable>(); boolean hasInitializers = false; for (PsiClassInitializer initializer : psiClass.getInitializers()) { PsiCodeBlock body = initializer.getBody(); hasInitializers = true; ControlFlow flow; try { flow = ControlFlowFactory.getInstance(body.getProject()) .getControlFlow( body, LocalsOrMyInstanceFieldsControlFlowPolicy.getInstance(), false); } catch (AnalysisCanceledException e) { flow = ControlFlow.EMPTY; } Collection<PsiVariable> writtenVariables = new ArrayList<PsiVariable>(); ControlFlowUtil.getWrittenVariables(flow, 0, flow.getSize(), false, writtenVariables); for (PsiVariable psiVariable : writtenVariables) { if (allFields.contains(psiVariable)) { if (instanceInitializerInitializedFields.contains(psiVariable)) { allFields.remove(psiVariable); instanceInitializerInitializedFields.remove(psiVariable); } else { instanceInitializerInitializedFields.add(psiVariable); } } } for (PsiVariable psiVariable : writtenVariables) { if (!instanceInitializerInitializedFields.contains(psiVariable)) { allFields.remove(psiVariable); } } } for (PsiMethod psiMethod : psiMethods) { if (psiMethod.isConstructor()) { PsiCodeBlock body = psiMethod.getBody(); if (body != null) { hasInitializers = true; ControlFlow flow; try { flow = ControlFlowFactory.getInstance(body.getProject()) .getControlFlow( body, LocalsOrMyInstanceFieldsControlFlowPolicy.getInstance(), false); } catch (AnalysisCanceledException e) { flow = ControlFlow.EMPTY; } Collection<PsiVariable> writtenVariables = ControlFlowUtil.getWrittenVariables(flow, 0, flow.getSize(), false); for (PsiVariable psiVariable : writtenVariables) { if (instanceInitializerInitializedFields.contains(psiVariable)) { allFields.remove(psiVariable); instanceInitializerInitializedFields.remove(psiVariable); } } List<PsiMethod> redirectedConstructors = HighlightControlFlowUtil.getChainedConstructors(psiMethod); if (redirectedConstructors == null || redirectedConstructors.isEmpty()) { List<PsiVariable> ssaVariables = ControlFlowUtil.getSSAVariables(flow); ArrayList<PsiVariable> good = new ArrayList<PsiVariable>(ssaVariables); good.addAll(instanceInitializerInitializedFields); allFields.retainAll(good); } else { allFields.removeAll(writtenVariables); } } } } for (PsiField psiField : psiFields) { if ((!hasInitializers || !allFields.contains(psiField)) && psiField.getInitializer() == null) { final RefFieldImpl refField = (RefFieldImpl) myManager.getReference(psiField); if (refField != null) { refField.setFlag(false, CAN_BE_FINAL_MASK); } } } } } else if (refElement instanceof RefMethod) { final RefMethod refMethod = (RefMethod) refElement; if (refMethod.isEntry()) { ((RefMethodImpl) refMethod).setFlag(false, CAN_BE_FINAL_MASK); } } }
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { LOG.assertTrue(myOutOfScopeVariable != null); PsiManager manager = file.getManager(); myOutOfScopeVariable.normalizeDeclaration(); PsiUtil.setModifierProperty(myOutOfScopeVariable, PsiModifier.FINAL, false); PsiElement commonParent = PsiTreeUtil.findCommonParent(myOutOfScopeVariable, myUnresolvedReference); LOG.assertTrue(commonParent != null); PsiElement child = myOutOfScopeVariable.getTextRange().getStartOffset() < myUnresolvedReference.getTextRange().getStartOffset() ? myOutOfScopeVariable : myUnresolvedReference; while (child.getParent() != commonParent) child = child.getParent(); PsiDeclarationStatement newDeclaration = (PsiDeclarationStatement) JavaPsiFacade.getInstance(manager.getProject()) .getElementFactory() .createStatementFromText("int i = 0", null); PsiVariable variable = (PsiVariable) newDeclaration.getDeclaredElements()[0].replace(myOutOfScopeVariable); if (variable.getInitializer() != null) { variable.getInitializer().delete(); } while (!(child instanceof PsiStatement) || !(child.getParent() instanceof PsiCodeBlock)) { child = child.getParent(); commonParent = commonParent.getParent(); } LOG.assertTrue(commonParent != null); PsiDeclarationStatement added = (PsiDeclarationStatement) commonParent.addBefore(newDeclaration, child); PsiLocalVariable addedVar = (PsiLocalVariable) added.getDeclaredElements()[0]; manager.getCodeStyleManager().reformat(commonParent); // Leave initializer assignment PsiExpression initializer = myOutOfScopeVariable.getInitializer(); if (initializer != null) { PsiExpressionStatement assignment = (PsiExpressionStatement) JavaPsiFacade.getInstance(manager.getProject()) .getElementFactory() .createStatementFromText(myOutOfScopeVariable.getName() + "= e;", null); ((PsiAssignmentExpression) assignment.getExpression()).getRExpression().replace(initializer); assignment = (PsiExpressionStatement) manager.getCodeStyleManager().reformat(assignment); PsiDeclarationStatement declStatement = PsiTreeUtil.getParentOfType(myOutOfScopeVariable, PsiDeclarationStatement.class); LOG.assertTrue(declStatement != null); PsiElement parent = declStatement.getParent(); if (parent instanceof PsiForStatement) { declStatement.replace(assignment); } else { parent.addAfter(assignment, declStatement); } } if (myOutOfScopeVariable.isValid()) { myOutOfScopeVariable.delete(); } if (HighlightControlFlowUtil.checkVariableInitializedBeforeUsage( myUnresolvedReference, addedVar, new THashMap<PsiElement, Collection<PsiReferenceExpression>>()) != null) { initialize(addedVar); } DaemonCodeAnalyzer.getInstance(project).updateVisibleHighlighters(editor); }