ModCommand (@NotNull ActionContext context, @NotNull PsiLocalVariable local, @Nullable PsiReferenceExpression refExpr, @NotNull List<PsiReferenceExpression> allRefs, @NotNull InlineMode mode) { final String localName = local.getName(); InnerClassUsages innerClassUses = InnerClassUsages.getUsages(local, allRefs); final PsiCodeBlock containerBlock = PsiTreeUtil.getParentOfType(local, PsiCodeBlock.class); if (containerBlock == null) { return ModCommand.error(JavaRefactoringBundle.message("inline.local.variable.declared.outside.cannot.refactor.message")); } final PsiExpression defToInline; try { List<PsiElement> innerClassesWithUsages = innerClassUses.innerClassesWithUsages(); defToInline = getDefToInline(local, innerClassesWithUsages.isEmpty() ? refExpr : innerClassesWithUsages.get(0), containerBlock, true); if (defToInline == null) { final String key = refExpr == null ? "variable.has.no.initializer" : "variable.has.no.dominating.definition"; String message = RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message(key, localName)); return ModCommand.error(message); } } catch (RuntimeException e) { return processWrappedAnalysisCanceledException(e); } List<PsiElement> refsToInlineList = new ArrayList<>(); boolean simpleInlining = false; if (mode == InlineMode.INLINE_ONE) { refsToInlineList.add(refExpr); } else { if (defToInline == local.getInitializer()) { // Do not rely on ref-def analysis in a simple case when we inline an initializer and there are no subsequent writes. // This allows inlining in the presense of syntax errors. List<PsiReferenceExpression> refs = VariableAccessUtils.getVariableReferences(local, containerBlock); if (!ContainerUtil.exists(refs, ref -> PsiUtil.isAccessedForWriting(ref))) { simpleInlining = true; refsToInlineList.addAll(refs); } } if (!simpleInlining) { try { Collections.addAll(refsToInlineList, DefUseUtil.getRefs(containerBlock, local, defToInline, true)); } catch (RuntimeException e) { return processWrappedAnalysisCanceledException(e); } } for (PsiElement innerClassUsage : innerClassUses.innerClassUsages()) { if (!refsToInlineList.contains(innerClassUsage)) { refsToInlineList.add(innerClassUsage); } } } if (refsToInlineList.isEmpty()) { return ModCommand.error(JavaRefactoringBundle.message("variable.is.never.used.before.modification", localName)); } if (mode == InlineMode.CHECK_CONFLICTS) { Map<PsiElement, PsiVariable> conflicts = InlineUtil.getChangedBeforeLastAccessMap(defToInline, local); if (conflicts.isEmpty()) { mode = InlineMode.ASK; } else { return createConflictChooser(local, refExpr, conflicts); } } if (mode == InlineMode.ASK && refExpr != null && refsToInlineList.size() > 1 && EditorSettingsExternalizable.getInstance().isShowInlineLocalDialog()) { return createChooser(local, refExpr, refsToInlineList); } final PsiElement[] refsToInline = PsiUtilCore.toPsiElementArray(refsToInlineList); if (refExpr != null && PsiUtil.isAccessedForReading(refExpr) && ArrayUtil.find(refsToInline, refExpr) < 0) { final PsiElement[] defs = DefUseUtil.getDefs(containerBlock, local, refExpr); LOG.assertTrue(defs.length > 0); return ModCommand.highlight(defs).andThen( ModCommand.error( RefactoringBundle.getCannotRefactorMessage(JavaRefactoringBundle.message("variable.is.accessed.for.writing", localName)))); } PsiTryStatement tryStatement = PsiTreeUtil.getParentOfType(defToInline, PsiTryStatement.class); if (tryStatement != null) { if (ExceptionUtil.getThrownExceptions(defToInline).isEmpty()) { tryStatement = null; } } PsiFile workingFile = local.getContainingFile(); for (PsiElement ref : refsToInline) { final PsiFile otherFile = ref.getContainingFile(); if (!otherFile.equals(workingFile)) { return ModCommand.error(RefactoringBundle.message("variable.is.referenced.in.multiple.files", localName)); } if (tryStatement != null && !PsiTreeUtil.isAncestor(tryStatement, ref, false)) { return ModCommand.error(JavaRefactoringBundle.message("inline.local.unable.try.catch.warning.message")); } } for (final PsiElement ref : refsToInline) { final PsiElement[] defs = DefUseUtil.getDefs(containerBlock, local, ref); boolean isSameDefinition = true; for (PsiElement def : defs) { isSameDefinition &= isSameDefinition(def, defToInline); } if (!isSameDefinition) { String message = RefactoringBundle.getCannotRefactorMessage( RefactoringBundle.message("variable.is.accessed.for.writing.and.used.with.inlined", localName)); return ModCommand.highlight(EditorColors.WRITE_SEARCH_RESULT_ATTRIBUTES, defs) .andThen(ModCommand.highlight(ref)) .andThen(ModCommand.error(message)); } } final PsiElement writeAccess = checkRefsInAugmentedAssignmentOrUnaryModified(refsToInline, defToInline); if (writeAccess != null && ! (writeAccess.getParent() instanceof PsiAssignmentExpression assignment && assignment.getLExpression() == writeAccess && ArrayUtil.contains(writeAccess, refsToInline))) { String message = RefactoringBundle.getCannotRefactorMessage(JavaRefactoringBundle.message("variable.is.accessed.for.writing", localName)); return ModCommand.highlight(EditorColors.WRITE_SEARCH_RESULT_ATTRIBUTES, writeAccess) .andThen(ModCommand.error(message)); } if (ContainerUtil.exists(refsToInline, ref -> ref.getParent() instanceof PsiResourceExpression)) { return ModCommand.error( RefactoringBundle.getCannotRefactorMessage(JavaRefactoringBundle.message("inline.local.used.as.resource.cannot.refactor.message"))); } Project project = context.project(); boolean inlineAll = mode != InlineMode.INLINE_ONE; return ModCommand.psiUpdate(context, updater -> { PsiExpression writableDef = updater.getWritable(defToInline); PsiLocalVariable writableLocal = updater.getWritable(local); List<PsiElement> writableRefs = StreamEx.of(refsToInline).without(writeAccess).map(updater::getWritable).toList(); PsiElement writableWrite = updater.getWritable(writeAccess); List<SmartPsiElementPointer<PsiExpression>> pointers = inlineOccurrences(project, writableLocal, writableDef, writableRefs); if (writableWrite != null && writableWrite.isValid()) { PsiAssignmentExpression newAssignment = PsiReplacementUtil.replaceOperatorAssignmentWithAssignmentExpression((PsiAssignmentExpression)writableWrite.getParent()); for (PsiReferenceExpression ref : VariableAccessUtils.getVariableReferences(writableLocal, newAssignment.getRExpression())) { pointers.add(SmartPointerManager.createPointer(InlineUtil.inlineVariable(local, defToInline, ref))); } } if (inlineAll) { if (!isInliningVariableInitializer(writableDef)) { deleteInitializer(writableDef); } else { writableDef.delete(); } } if (inlineAll && !VariableAccessUtils.variableIsUsed(writableLocal, PsiUtil.getVariableCodeBlock(writableLocal, null))) { writableLocal.normalizeDeclaration(); new CommentTracker().deleteAndRestoreComments(writableLocal); } highlightOccurrences(updater, pointers); for (SmartPsiElementPointer<PsiExpression> expr : pointers) { CommonJavaRefactoringUtil.tryToInlineArrayCreationForVarargs(expr.getElement()); } }); }