IDEA-42116 inline local based on control flow improvemnt

This commit is contained in:
Maxim Medvedev
2011-07-09 09:20:11 +04:00
parent 57e4652759
commit 975174dd9d
16 changed files with 560 additions and 90 deletions
@@ -24,5 +24,5 @@ import java.util.Collection;
* @author Max Medvedev
*/
public interface ReferencesToInlineSearcher {
Collection<PsiReference> findReferences(PsiElement element);
Collection<? extends PsiReference> findReferences(PsiElement element, InlineHandler.Settings settings);
}
@@ -57,18 +57,18 @@ public class GenericInlineHandler {
return settings != null;
}
final Collection<PsiReference> allReferences;
final Collection<? extends PsiReference> allReferences;
if (settings.isOnlyOneReferenceToInline()) {
allReferences = Collections.singleton(invocationReference);
} else {
final Ref<Collection<PsiReference>> usagesRef = new Ref<Collection<PsiReference>>();
final Ref<Collection<? extends PsiReference>> usagesRef = new Ref<Collection<? extends PsiReference>>();
ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() {
@Override
public void run() {
final ReferencesToInlineSearcher searcher = ReferencesToInlineSearchers.getSearcher(element.getLanguage());
if (searcher != null) {
usagesRef.set(searcher.findReferences(element));
usagesRef.set(searcher.findReferences(element, settings));
}
else {
usagesRef.set(ReferencesSearch.search(element).findAll());
@@ -185,7 +185,7 @@ public class GenericInlineHandler {
}
//order of usages across different files is irrelevant
public static PsiReference[] sortDepthFirstRightLeftOrder(final Collection<PsiReference> allReferences) {
public static PsiReference[] sortDepthFirstRightLeftOrder(final Collection<? extends PsiReference> allReferences) {
final PsiReference[] usages = allReferences.toArray(new PsiReference[allReferences.size()]);
Arrays.sort(usages, new Comparator<PsiReference>() {
public int compare(final PsiReference usage1, final PsiReference usage2) {
@@ -15,9 +15,13 @@
*/
package org.jetbrains.plugins.groovy.codeInspection.utils;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Condition;
import com.intellij.psi.PsiElement;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.HashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
@@ -41,21 +45,26 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrRefere
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrUnaryExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.Instruction;
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.ReadWriteVariableInstruction;
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.impl.ControlFlowBuilder;
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.impl.IfEndInstruction;
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.impl.MaybeReturnInstruction;
import org.jetbrains.plugins.groovy.lang.psi.dataFlow.DFAEngine;
import org.jetbrains.plugins.groovy.lang.psi.dataFlow.DfaInstance;
import org.jetbrains.plugins.groovy.lang.psi.dataFlow.Semilattice;
import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringUtil;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.*;
@SuppressWarnings({"OverlyComplexClass"})
public class ControlFlowUtils {
private static final Logger LOG = Logger.getInstance(ControlFlowUtils.class);
private ControlFlowUtils() {
super();
}
public static boolean statementMayCompleteNormally(
@Nullable GrStatement statement) {
if (statement == null) {
@@ -616,4 +625,103 @@ public class ControlFlowUtils {
}
return null;
}
/**
* searches for next or previous write access to local variable
* @param local variable to analyze
* @param place place to start searching
* @param ahead if true search for next write. if false searches for previous write
* @return all write instructions leading to (or preceding) the place
*/
public static ReadWriteVariableInstruction[] findWriteAccess(GrVariable local, final PsiElement place, boolean ahead) {
LOG.assertTrue(GroovyRefactoringUtil.isLocalVariable(local), local.getClass());
final GrControlFlowOwner owner = findControlFlowOwner(local);
LOG.assertTrue(owner != null);
final Instruction cur = findInstruction(place, owner.getControlFlow());
if (cur == null) throw new IllegalArgumentException("place is not in the flow");
final ArrayList<ReadWriteVariableInstruction> result = new ArrayList<ReadWriteVariableInstruction>();
final HashSet<Instruction> visited = new HashSet<Instruction>();
visited.add(cur);
writeAccess(cur, local.getName(), visited, result, ahead);
return result.toArray(new ReadWriteVariableInstruction[result.size()]);
}
@Nullable
private static Instruction findInstruction(final PsiElement place, Instruction[] controlFlow) {
return ContainerUtil.find(controlFlow, new Condition<Instruction>() {
@Override
public boolean value(Instruction instruction) {
return instruction.getElement() == place;
}
});
}
private static void writeAccess(Instruction cur, String name, Set<Instruction> visited, Collection<ReadWriteVariableInstruction> result, boolean ahead) {
final Iterable<? extends Instruction> toIterate = ahead ? cur.allSucc() : cur.allPred();
for (Instruction i : toIterate) {
if (visited.contains(i)) continue;
visited.add(i);
if (i instanceof ReadWriteVariableInstruction && ((ReadWriteVariableInstruction)i).isWrite() && name.equals(((ReadWriteVariableInstruction)i).getVariableName())) {
result.add((ReadWriteVariableInstruction)i);
}
else {
writeAccess(i, name, visited, result, ahead);
}
}
}
public static ArrayList<BitSet> inferWriteAccessMap(final Instruction[] flow, final GrVariable var) {
final Semilattice<BitSet> sem = new Semilattice<BitSet>() {
@Override
public BitSet join(ArrayList<BitSet> ins) {
BitSet result = new BitSet(flow.length);
for (BitSet set : ins) {
result.or(set);
}
return result;
}
@Override
public boolean eq(BitSet e1, BitSet e2) {
return e1.equals(e2);
}
};
DfaInstance<BitSet> dfa = new DfaInstance<BitSet>() {
@Override
public void fun(BitSet bitSet, Instruction instruction) {
if (!(instruction instanceof ReadWriteVariableInstruction)) return;
if (!((ReadWriteVariableInstruction)instruction).isWrite()) return;
final PsiElement element = instruction.getElement();
if (element instanceof GrVariable && element != var) return;
if (element instanceof GrReferenceExpression) {
final GrReferenceExpression ref = (GrReferenceExpression)element;
if (ref.isQualified() || ref.resolve() != var) return;
}
bitSet.clear();
bitSet.set(instruction.num());
}
@NotNull
@Override
public BitSet initial() {
return new BitSet(flow.length);
}
@Override
public boolean isForward() {
return true;
}
};
return new DFAEngine<BitSet>(flow, dfa, sem).performDFA();
}
}
@@ -312,4 +312,13 @@ public abstract class GrVariableBaseImpl<T extends StubElement> extends GrStubEl
}
return null;
}
@Override
public void deleteChildInternal(@NotNull ASTNode child) {
final PsiElement psi = child.getPsi();
if (psi == getInitializerGroovy()) {
deleteChildInternal(findChildByType(GroovyTokenTypes.mASSIGN).getNode());
}
super.deleteChildInternal(child);
}
}
@@ -1192,4 +1192,25 @@ public class PsiUtil {
return !ref.isQualified() && name.equals(ref.getName());
}
@Nullable
public static GrExpression getInitializerFor(GrReferenceExpression lValue) {
if (!isLValue(lValue)) throw new IllegalArgumentException("arg is not lValue");
final PsiElement parent = lValue.getParent();
if (parent instanceof GrAssignmentExpression) return ((GrAssignmentExpression)parent).getRValue();
if (parent instanceof GrTupleExpression) {
final int i = ((GrTupleExpression)parent).indexOf(lValue);
final PsiElement pparent = parent.getParent();
LOG.assertTrue(pparent instanceof GrAssignmentExpression);
final GrExpression rValue = ((GrAssignmentExpression)pparent).getRValue();
if (rValue instanceof GrListOrMap && !((GrListOrMap)rValue).isMap()) {
final GrExpression[] initializers = ((GrListOrMap)rValue).getInitializers();
if (initializers.length < i) return initializers[i];
}
}
return null;
}
}
@@ -43,7 +43,7 @@ cannot.find.a.single.definition.to.inline.local.var=Cannot find a single definit
cannot.find.a.single.definition.to.inline.field=No initializer present for the field
inline.local.command=Inline Variable
local.varaible.is.lvalue=Variable to be inlined has occurrence as lvalue in assignment
inline.local.variable.prompt=Inline local variable ''{0}''?
inline.local.variable.prompt.0.1=Inline local variable ''{0}'' ({1,choice,1#1 occurrence|2#{1, number} occurrences})?
inline.field.prompt=Inline field ''{0}''?
########################################################################################################################
@@ -162,3 +162,4 @@ files.to.be.converted=Files to be converted
converting.files.to.java=Converting files to Java
rename.groovy.property=Rename &Groovy property
reference.to.accessor.0.is.used=Reference to accessor <bold>{0}<bold> cannot be inlined.
variable.is.accessed.for.writing=Variable ''{0}'' is accessed for writing
@@ -0,0 +1,83 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.plugins.groovy.refactoring.inline;
import com.intellij.lang.refactoring.InlineHandler;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiModifier;
import com.intellij.refactoring.HelpID;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.util.CommonRefactoringUtil;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField;
import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringBundle;
/**
* @author Max Medvedev
*/
public class GrInlineFieldUtil {
public static final String INLINE_FIELD = RefactoringBundle.message("inline.field.title");
private GrInlineFieldUtil() {
}
@Nullable
static InlineHandler.Settings inlineFieldSettings(final GrField field, Editor editor, boolean invokedOnReference) {
final Project project = field.getProject();
if (!field.hasModifierProperty(PsiModifier.FINAL)) {
String message = RefactoringBundle.message("0.refactoring.is.supported.only.for.final.fields", INLINE_FIELD);
CommonRefactoringUtil.showErrorHint(project, editor, message, INLINE_FIELD, HelpID.INLINE_FIELD);
return InlineHandler.Settings.CANNOT_INLINE_SETTINGS;
}
if (field.getInitializerGroovy() == null) {
String message = GroovyRefactoringBundle.message("cannot.find.a.single.definition.to.inline.field");
CommonRefactoringUtil.showErrorHint(project, editor, message, INLINE_FIELD, HelpID.INLINE_FIELD);
return InlineHandler.Settings.CANNOT_INLINE_SETTINGS;
}
return inlineFieldDialogResult(project, field, invokedOnReference);
}
private static InlineHandler.Settings inlineFieldDialogResult(Project project, GrField field, final boolean invokedOnReference) {
if (ApplicationManager.getApplication().isUnitTestMode()) {
return new InlineHandler.Settings() {
@Override
public boolean isOnlyOneReferenceToInline() {
return invokedOnReference;
}
};
}
final InlineGroovyFieldDialog dialog = new InlineGroovyFieldDialog(project, field, invokedOnReference);
dialog.show();
if (dialog.isOK()) {
return new InlineHandler.Settings() {
@Override
public boolean isOnlyOneReferenceToInline() {
return dialog.isInlineThisOnly();
}
};
}
else {
return InlineHandler.Settings.CANNOT_INLINE_SETTINGS;
}
}
}
@@ -50,12 +50,17 @@ public class GrVariableInliner implements InlineHandler.Inliner {
private Project myProject;
private final GrExpression myTempExpr;
public GrVariableInliner(GrVariable variable) {
public GrVariableInliner(GrVariable variable, InlineHandler.Settings settings) {
myProject = variable.getProject();
GrExpression initializer = variable.getInitializerGroovy();
LOG.assertTrue(initializer != null);
GrExpression initializer;
if (settings instanceof InlineLocalVarSettings) {
initializer = ((InlineLocalVarSettings)settings).getInitializer();
}
else {
initializer = variable.getInitializerGroovy();
LOG.assertTrue(initializer != null);
}
myTempExpr = (GrExpression)skipParentheses(initializer, false);
}
@@ -49,14 +49,14 @@ public class GroovyInlineHandler implements InlineHandler, ReferencesToInlineSea
public Settings prepareInlineElement(final PsiElement element, Editor editor, boolean invokedOnReference) {
if (element instanceof GrVariable) {
if (GroovyRefactoringUtil.isLocalVariable((GrVariable)element)) {
return GroovyInlineVariableUtil.inlineLocalVariableSettings((GrVariable)element, editor);
return GroovyInlineVariableUtil.inlineLocalVariableSettings((GrVariable)element, editor, invokedOnReference);
}
else if (element instanceof GrField) {
return GroovyInlineVariableUtil.inlineFieldSettings((GrField)element, editor, invokedOnReference);
return GrInlineFieldUtil.inlineFieldSettings((GrField)element, editor, invokedOnReference);
}
}
else if (element instanceof GrAccessorMethod) {
return GroovyInlineVariableUtil.inlineFieldSettings(((GrAccessorMethod)element).getProperty(), editor, invokedOnReference);
return GrInlineFieldUtil.inlineFieldSettings(((GrAccessorMethod)element).getProperty(), editor, invokedOnReference);
}
else if (element instanceof GrMethod) {
return GroovyInlineMethodUtil.inlineMethodSettings((GrMethod)element, editor, invokedOnReference);
@@ -82,6 +82,11 @@ public class GroovyInlineHandler implements InlineHandler, ReferencesToInlineSea
if (element instanceof GrAccessorMethod) {
element = ((GrAccessorMethod)element).getProperty();
}
if (element instanceof GrVariable && GroovyRefactoringUtil.isLocalVariable((GrVariable)element)) {
GroovyInlineVariableUtil.removeDefinition(element, settings);
return;
}
final PsiElement owner = element.getParent().getParent();
if (element instanceof GrVariable && owner instanceof GrVariableDeclarationOwner) {
((GrVariableDeclarationOwner)owner).removeVariable(((GrVariable)element));
@@ -94,10 +99,10 @@ public class GroovyInlineHandler implements InlineHandler, ReferencesToInlineSea
@Nullable
public Inliner createInliner(PsiElement element, Settings settings) {
if (element instanceof GrVariable) {
return GroovyInlineVariableUtil.createInlinerForVariable(((GrVariable)element));
return GroovyInlineVariableUtil.createInlinerForVariable((GrVariable)element, settings);
}
if (element instanceof GrAccessorMethod) {
return GroovyInlineVariableUtil.createInlinerForVariable(((GrAccessorMethod)element).getProperty());
return GroovyInlineVariableUtil.createInlinerForVariable(((GrAccessorMethod)element).getProperty(), settings);
}
if (element instanceof GrMethod) {
return new GroovyMethodInliner((GrMethod)element);
@@ -106,23 +111,31 @@ public class GroovyInlineHandler implements InlineHandler, ReferencesToInlineSea
}
@Override
public Collection<PsiReference> findReferences(PsiElement element) {
if (element instanceof GrAccessorMethod) element = ((GrAccessorMethod)element).getProperty();
if (!(element instanceof GrField)) {
return ReferencesSearch.search(element).findAll();
public Collection<? extends PsiReference> findReferences(PsiElement element, Settings settings) {
if (element instanceof GrVariable && GroovyRefactoringUtil.isLocalVariable((GrVariable)element)) {
if (settings instanceof InlineLocalVarSettings) {
return ((InlineLocalVarSettings)settings).getRefs();
}
}
LOG.assertTrue(((GrField)element).getSetter() == null);
else if (element instanceof GrAccessorMethod || element instanceof GrField) {
if (element instanceof GrAccessorMethod) {
element = ((GrAccessorMethod)element).getProperty();
}
Collection<PsiReference> result = new ArrayList<PsiReference>();
result.addAll(ReferencesSearch.search(element).findAll());
LOG.assertTrue(((GrField)element).getSetter() == null);
for (GrAccessorMethod getter : ((GrField)element).getGetters()) {
result.addAll(MethodReferencesSearch.search(getter).findAll());
Collection<PsiReference> result = new ArrayList<PsiReference>();
result.addAll(ReferencesSearch.search(element).findAll());
for (GrAccessorMethod getter : ((GrField)element).getGetters()) {
result.addAll(MethodReferencesSearch.search(getter).findAll());
}
return result;
}
return result;
return ReferencesSearch.search(element).findAll();
}
}
@@ -16,64 +16,218 @@
package org.jetbrains.plugins.groovy.refactoring.inline;
import com.intellij.codeInsight.TargetElementUtilBase;
import com.intellij.codeInsight.highlighting.HighlightManager;
import com.intellij.lang.refactoring.InlineHandler;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.colors.EditorColors;
import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.editor.markup.RangeHighlighter;
import com.intellij.openapi.editor.markup.TextAttributes;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.wm.WindowManager;
import com.intellij.psi.PsiModifier;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.PsiUtilBase;
import com.intellij.refactoring.HelpID;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.util.CommonRefactoringUtil;
import com.intellij.refactoring.util.RefactoringMessageDialog;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField;
import org.jetbrains.plugins.groovy.codeInspection.utils.ControlFlowUtils;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrAssignmentExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.util.GrVariableDeclarationOwner;
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.Instruction;
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.ReadWriteVariableInstruction;
import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringBundle;
import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringUtil;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collection;
/**
* @author ilyas
*/
public class GroovyInlineVariableUtil {
private static final Logger LOG = Logger.getInstance(GroovyInlineVariableUtil.class);
public static final String INLINE_VARIABLE = RefactoringBundle.message("inline.variable.title");
public static final String INLINE_FIELD = RefactoringBundle.message("inline.field.title");
private GroovyInlineVariableUtil() {
}
public static void removeDefinition(PsiElement element, InlineHandler.Settings settings) {
LOG.assertTrue(element instanceof GrVariable && GroovyRefactoringUtil.isLocalVariable((GrVariable)element), element.getClass());
if (settings instanceof InlineLocalVarSettings) {
final GrExpression initializer = ((InlineLocalVarSettings)settings).getInitializer();
final PsiElement parent = initializer.getParent();
if (parent instanceof GrAssignmentExpression) {
parent.delete();
return;
}
else if (parent instanceof GrVariable) {
if (!((InlineLocalVarSettings)settings).isRemoveDeclaration()) {
initializer.delete();
return;
}
}
}
final PsiElement owner = element.getParent().getParent();
if (owner instanceof GrVariableDeclarationOwner) {
((GrVariableDeclarationOwner)owner).removeVariable((GrVariable)element);
}
else {
element.delete();
}
}
/**
* Creates new inliner for local variable occurences
* Creates new inliner for local variable occurrences
*/
static InlineHandler.Inliner createInlinerForVariable(final GrVariable variable) {
return new GrVariableInliner(variable);
static InlineHandler.Inliner createInlinerForVariable(final GrVariable variable, InlineHandler.Settings settings) {
return new GrVariableInliner(variable, settings);
}
/**
* Returns Settings object for referenced definition in case of local variable
*/
@Nullable
static InlineHandler.Settings inlineLocalVariableSettings(final GrVariable variable, Editor editor) {
static InlineHandler.Settings inlineLocalVariableSettings(final GrVariable variable, @Nullable Editor editor, boolean invokedOnReference) {
final String localName = variable.getName();
final Project project = variable.getProject();
if (variable.getInitializerGroovy() == null) {
final Collection<PsiReference> refs = ReferencesSearch.search(variable).findAll();
GrExpression initializer = null;
Instruction writeInstr = null;
final Instruction[] flow = ControlFlowUtils.findControlFlowOwner(variable).getControlFlow();
final ArrayList<BitSet> writes = ControlFlowUtils.inferWriteAccessMap(flow, variable);
GrReferenceExpression refExpr = null;
if (invokedOnReference) {
LOG.assertTrue(editor != null, "null editor but invokedOnReference==true");
final PsiReference ref = TargetElementUtilBase.findReference(editor);
LOG.assertTrue(ref != null);
final PsiElement cur = ref.getElement();
if (cur instanceof GrReferenceExpression) {
refExpr = (GrReferenceExpression)cur;
final Instruction instruction = ContainerUtil.find(flow, new Condition<Instruction>() {
@Override
public boolean value(Instruction instruction) {
return instruction.getElement() == cur;
}
});
LOG.assertTrue(instruction != null);
final BitSet prev = writes.get(instruction.num());
if (prev.cardinality() == 1) {
writeInstr = flow[prev.nextSetBit(0)];
final PsiElement element = writeInstr.getElement();
if (element instanceof GrVariable) {
initializer = ((GrVariable)element).getInitializerGroovy();
}
else if (element instanceof GrReferenceExpression) {
initializer = PsiUtil.getInitializerFor((GrReferenceExpression)element);
}
}
}
}
else {
initializer = variable.getInitializerGroovy();
writeInstr = ContainerUtil.find(flow, new Condition<Instruction>() {
@Override
public boolean value(Instruction instruction) {
return instruction.getElement() == variable;
}
});
}
if (initializer == null || writeInstr == null) {
String message = GroovyRefactoringBundle.message("cannot.find.a.single.definition.to.inline.local.var");
CommonRefactoringUtil.showErrorHint(variable.getProject(), editor, message, INLINE_VARIABLE, HelpID.INLINE_VARIABLE);
return InlineHandler.Settings.CANNOT_INLINE_SETTINGS;
}
return inlineLocalVarDialogResult(localName, project);
ArrayList<GrReferenceExpression> toInline = new ArrayList<GrReferenceExpression>();
for (Instruction instruction : flow) {
if (!(instruction instanceof ReadWriteVariableInstruction)) continue;
if (((ReadWriteVariableInstruction)instruction).isWrite()) continue;
final PsiElement element = instruction.getElement();
if (element instanceof GrVariable && element != variable) continue;
if (!(element instanceof GrReferenceExpression)) continue;
final GrReferenceExpression ref = (GrReferenceExpression)element;
if (ref.isQualified() || ref.resolve() != variable) continue;
final BitSet prev = writes.get(instruction.num());
if (prev.cardinality() == 1 && prev.get(writeInstr.num())) {
toInline.add(ref);
}
}
if (toInline.size()==0) {
CommonRefactoringUtil.showErrorHint(project, editor, GroovyRefactoringBundle.message("variable.is.never.used.0", localName), INLINE_VARIABLE, HelpID.INLINE_VARIABLE);
return InlineHandler.Settings.CANNOT_INLINE_SETTINGS;
}
ArrayList<RangeHighlighter> highlighters = new ArrayList<RangeHighlighter>();
HighlightManager highlightManager = HighlightManager.getInstance(project);
EditorColorsManager colorsManager = EditorColorsManager.getInstance();
TextAttributes attributes = colorsManager.getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES);
final TextAttributes writeAttributes = colorsManager.getGlobalScheme().getAttributes(EditorColors.WRITE_SEARCH_RESULT_ATTRIBUTES);
if (refExpr != null && PsiUtil.isAccessedForReading(refExpr) && !toInline.contains(refExpr)) {
highlightManager.addOccurrenceHighlights(editor, new PsiElement[]{refExpr}, attributes, true, null);
String message = RefactoringBundle.getCannotRefactorMessage(GroovyRefactoringBundle.message("variable.is.accessed.for.writing", localName));
CommonRefactoringUtil.showErrorHint(project, editor, message, INLINE_VARIABLE, HelpID.INLINE_VARIABLE);
WindowManager.getInstance().getStatusBar(project).setInfo(RefactoringBundle.message("press.escape.to.remove.the.highlighting"));
return InlineHandler.Settings.CANNOT_INLINE_SETTINGS;
}
for (GrReferenceExpression ref : toInline) {
if (PsiUtil.isAccessedForWriting(ref)) {
final String message = GroovyRefactoringBundle.message("variable.is.accessed.for.writing", localName);
HighlightManager.getInstance(project).addOccurrenceHighlights(editor, new PsiElement[]{ref}, writeAttributes, true, null);
CommonRefactoringUtil.showErrorHint(project, editor, message, INLINE_VARIABLE, HelpID.INLINE_VARIABLE);
WindowManager.getInstance().getStatusBar(project).setInfo(RefactoringBundle.message("press.escape.to.remove.the.highlighting"));
return InlineHandler.Settings.CANNOT_INLINE_SETTINGS;
}
}
highlightManager.addOccurrenceHighlights(editor, PsiUtilBase.toPsiElementArray(toInline), attributes, false, highlighters);
return inlineLocalVarDialogResult(localName, project, toInline, initializer, toInline.size() == refs.size());
}
/**
* Shows dialog with question to inline
*/
@Nullable
private static InlineHandler.Settings inlineLocalVarDialogResult(String localName, Project project) {
private static InlineHandler.Settings inlineLocalVarDialogResult(String localName,
Project project,
Collection<GrReferenceExpression> refs,
GrExpression initializer,
boolean removeDeclaration) {
if (!ApplicationManager.getApplication().isUnitTestMode()) {
final String question = GroovyRefactoringBundle.message("inline.local.variable.prompt", localName);
final String question = GroovyRefactoringBundle.message("inline.local.variable.prompt.0.1", localName, refs.size());
RefactoringMessageDialog dialog = new RefactoringMessageDialog(INLINE_VARIABLE, question, HelpID.INLINE_VARIABLE, "OptionPane.questionIcon", true, project);
dialog.show();
if (!dialog.isOK()) {
@@ -82,55 +236,6 @@ public class GroovyInlineVariableUtil {
}
}
return new InlineHandler.Settings() {
public boolean isOnlyOneReferenceToInline() {
return false;
}
};
}
@Nullable
static InlineHandler.Settings inlineFieldSettings(final GrField field, Editor editor, boolean invokedOnReference) {
final Project project = field.getProject();
if (!field.hasModifierProperty(PsiModifier.FINAL)) {
String message = RefactoringBundle.message("0.refactoring.is.supported.only.for.final.fields", INLINE_FIELD);
CommonRefactoringUtil.showErrorHint(project, editor, message, INLINE_FIELD, HelpID.INLINE_FIELD);
return InlineHandler.Settings.CANNOT_INLINE_SETTINGS;
}
if (field.getInitializerGroovy() == null) {
String message = GroovyRefactoringBundle.message("cannot.find.a.single.definition.to.inline.field");
CommonRefactoringUtil.showErrorHint(project, editor, message, INLINE_FIELD, HelpID.INLINE_FIELD);
return InlineHandler.Settings.CANNOT_INLINE_SETTINGS;
}
return inlineFieldDialogResult(project, field, invokedOnReference);
}
private static InlineHandler.Settings inlineFieldDialogResult(Project project, GrField field, final boolean invokedOnReference) {
if (ApplicationManager.getApplication().isUnitTestMode()) {
return new InlineHandler.Settings() {
@Override
public boolean isOnlyOneReferenceToInline() {
return invokedOnReference;
}
};
}
final InlineGroovyFieldDialog dialog = new InlineGroovyFieldDialog(project, field, invokedOnReference);
dialog.show();
if (dialog.isOK()) {
return new InlineHandler.Settings() {
@Override
public boolean isOnlyOneReferenceToInline() {
return dialog.isInlineThisOnly();
}
};
}
else {
WindowManager.getInstance().getStatusBar(project).setInfo(GroovyRefactoringBundle.message("press.escape.to.remove.the.highlighting"));
return InlineHandler.Settings.CANNOT_INLINE_SETTINGS;
}
return new InlineLocalVarSettings(initializer, refs, removeDeclaration);
}
}
@@ -0,0 +1,57 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.plugins.groovy.refactoring.inline;
import com.intellij.lang.refactoring.InlineHandler;
import com.intellij.psi.PsiReference;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression;
import java.util.Collection;
/**
* @author Max Medvedev
*/
public class InlineLocalVarSettings implements InlineHandler.Settings {
private GrExpression myInitializer;
private Collection<GrReferenceExpression> myRefs;
private boolean myRemoveDeclaration;
public InlineLocalVarSettings(GrExpression initializer, Collection<GrReferenceExpression> refs, boolean removeDeclaration) {
myInitializer = initializer;
myRefs = refs;
myRemoveDeclaration = removeDeclaration;
}
@Override
public boolean isOnlyOneReferenceToInline() {
return false;
}
public GrExpression getInitializer() {
return myInitializer;
}
public Collection<? extends PsiReference> getRefs() {
return myRefs;
}
public boolean isRemoveDeclaration() {
return myRemoveDeclaration;
}
}
@@ -40,6 +40,11 @@ public class InlineVariableTest extends LightCodeInsightFixtureTestCase {
public void testField() {doTest();}
public void testPartial1() {doTest();}
public void testPartial2() {doTest();}
public void testPartial3() {doTest();}
public void testPartial4() {doTest();}
protected void doTest() {
InlineMethodTest.doInlineTest(myFixture, getTestDataPath() + getTestName(true) + ".test", new GroovyInlineHandler());
}
@@ -0,0 +1,21 @@
def a = 2
if (b) {
print <selection>a</selection>
}
else {
a = foo(a)
}
print a
-----
def a
if (b) {
print 2
}
else {
a = foo(2)
}
print a
@@ -0,0 +1,9 @@
def a = 4
print <selection>a</selection>
a = a+2+a
print a
-----
def a
print 4
a = 4+2+4
print a
@@ -0,0 +1,5 @@
def <selection>a</selection> = 3
a=3
-----
FAIL: Variable 'a' is never used
@@ -0,0 +1,28 @@
def a = 2
print a
a = 4
if (b) {
print <selection>a</selection>
}
else {
print a
a = 5
print a
}
print a
-----
def a = 2
print a
if (b) {
print 4
}
else {
print 4
a = 5
print a
}
print a