From 0f598b0e7b21bebb76306a189df9626630df4378 Mon Sep 17 00:00:00 2001 From: Eugene Vigdorchik Date: Mon, 7 Mar 2005 17:27:03 +0300 Subject: [PATCH] (no message) --- .../ex/StandardInspectionToolsProvider.java | 7 +- .../FieldCanBeLocalInspection.java | 175 ++++++++++++++++++ .../AllVariablesControlFlowPolicy.java | 2 +- 3 files changed, 180 insertions(+), 4 deletions(-) create mode 100644 source/com/intellij/codeInspection/varScopeCanBeNarrowed/FieldCanBeLocalInspection.java diff --git a/source/com/intellij/codeInspection/ex/StandardInspectionToolsProvider.java b/source/com/intellij/codeInspection/ex/StandardInspectionToolsProvider.java index f56d46e525aa..d7280c87fb4e 100644 --- a/source/com/intellij/codeInspection/ex/StandardInspectionToolsProvider.java +++ b/source/com/intellij/codeInspection/ex/StandardInspectionToolsProvider.java @@ -1,6 +1,7 @@ package com.intellij.codeInspection.ex; import com.intellij.codeInspection.InspectionToolProvider; +import com.intellij.codeInspection.varScopeCanBeNarrowed.FieldCanBeLocalInspection; import com.intellij.codeInspection.dependencyViolation.DependencyViolationInspection; import com.intellij.openapi.components.ApplicationComponent; import com.intellij.codeInsight.i18n.StringI18nInspection; @@ -46,8 +47,8 @@ public class StandardInspectionToolsProvider implements InspectionToolProvider, StringI18nInspection.class, - DependencyViolationInspection.class - - }; + DependencyViolationInspection.class, + FieldCanBeLocalInspection.class + }; } } diff --git a/source/com/intellij/codeInspection/varScopeCanBeNarrowed/FieldCanBeLocalInspection.java b/source/com/intellij/codeInspection/varScopeCanBeNarrowed/FieldCanBeLocalInspection.java new file mode 100644 index 000000000000..5a719adde734 --- /dev/null +++ b/source/com/intellij/codeInspection/varScopeCanBeNarrowed/FieldCanBeLocalInspection.java @@ -0,0 +1,175 @@ +package com.intellij.codeInspection.varScopeCanBeNarrowed; + +import com.intellij.codeInspection.InspectionManager; +import com.intellij.codeInspection.LocalQuickFix; +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.codeInspection.ProblemHighlightType; +import com.intellij.codeInspection.ex.BaseLocalInspectionTool; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.psi.*; +import com.intellij.psi.codeStyle.CodeStyleManager; +import com.intellij.psi.codeStyle.VariableKind; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.search.PsiSearchHelper; +import com.intellij.psi.search.LocalSearchScope; +import com.intellij.psi.controlFlow.*; +import com.intellij.psi.util.PsiUtil; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.IncorrectOperationException; +import com.intellij.util.containers.HashSet; + +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +/** + * @author ven + */ +public class FieldCanBeLocalInspection extends BaseLocalInspectionTool { + private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.varScopeCanBeNarrowed.FieldCanBeLocalInspection"); + public String getGroupDisplayName() { + return "Local Code Analysis"; + } + + public String getDisplayName() { + return "Field can be local"; + } + + public String getShortName() { + return "FieldCanBeLocal"; + } + + public ProblemDescriptor[] checkClass(PsiClass aClass, InspectionManager manager, boolean isOnTheFly) { + PsiManager psiManager = aClass.getManager(); + final Set candidates = new LinkedHashSet(); + final PsiClass topLevelClass = PsiUtil.getTopLevelClass(aClass); + if (topLevelClass == null) return null; + final PsiField[] fields = aClass.getFields(); + NextField: + for (int i = 0; i < fields.length; i++) { + PsiField field = fields[i]; + if (field.hasModifierProperty(PsiModifier.PRIVATE)) { + final PsiReference[] refs = psiManager.getSearchHelper().findReferences(field, GlobalSearchScope.allScope(psiManager.getProject()), false); + for (int j = 0; j < refs.length; j++) { + PsiElement element = refs[j].getElement(); + while(element != null) { + if (element instanceof PsiMethod) { + candidates.add(field); + continue NextField; + } + element = PsiTreeUtil.getParentOfType(element, PsiMember.class); + } + continue NextField; + } + } + } + topLevelClass.accept(new PsiRecursiveElementVisitor() { + public void visitElement(PsiElement element) { + if (candidates.size() > 0) super.visitElement(element); + } + + public void visitMethod(PsiMethod method) { + super.visitMethod(method); + + final PsiCodeBlock body = method.getBody(); + if (body != null) { + try { + final ControlFlow controlFlow = ControlFlowFactory.getControlFlow(body, AllVariablesControlFlowPolicy.getInstance()); + final List readBeforeWrite = ControlFlowUtil.getReadBeforeWrite(controlFlow); + for (Iterator iterator = readBeforeWrite.iterator(); iterator.hasNext();) { + final PsiElement resolved = iterator.next().resolve(); + if (resolved instanceof PsiField) { + candidates.remove(resolved); + } + } + } + catch (AnalysisCanceledException e) { + candidates.clear(); + } + } + } + }); + + if (candidates.isEmpty()) return null; + ProblemDescriptor[] result = new ProblemDescriptor[candidates.size()]; + int i = 0; + for (Iterator iterator = candidates.iterator(); iterator.hasNext(); i++) { + PsiField field = iterator.next(); + final String message = "Field can be converted to one or more local variables."; + result[i] = manager.createProblemDescriptor(field, message, new MyQuickFix(field), ProblemHighlightType.GENERIC_ERROR_OR_WARNING); + } + return result; + } + + class MyQuickFix implements LocalQuickFix { + private PsiField myField; + + public MyQuickFix(final PsiField field) { + myField = field; + } + + public String getName() { + return "Convert to local"; + } + + public void applyFix(Project project, ProblemDescriptor descriptor) { + PsiManager manager = PsiManager.getInstance(project); + PsiSearchHelper helper = manager.getSearchHelper(); + Set methodSet = new HashSet(); + final PsiReference[] allRefs = helper.findReferences(myField, GlobalSearchScope.allScope(project), false); + for (int i = 0; i < allRefs.length; i++) { + PsiReference ref = allRefs[i]; + if (ref instanceof PsiReferenceExpression) { + final PsiMethod method = PsiTreeUtil.getParentOfType(((PsiReferenceExpression)ref), PsiMethod.class); + LOG.assertTrue(method != null); + methodSet.add(method); + } + } + + for (Iterator iterator = methodSet.iterator(); iterator.hasNext();) { + PsiMethod method = iterator.next(); + final PsiReference[] refs = helper.findReferences(myField, new LocalSearchScope(method), true); + LOG.assertTrue(refs.length > 0); + PsiCodeBlock anchorBlock = findAnchorBlock(refs); + LOG.assertTrue(anchorBlock != null); + final PsiElementFactory elementFactory = manager.getElementFactory(); + final CodeStyleManager styleManager = manager.getCodeStyleManager(); + final String propertyName = styleManager.variableNameToPropertyName(myField.getName(), VariableKind.FIELD); + final String localName = styleManager.propertyNameToVariableName(propertyName, VariableKind.LOCAL_VARIABLE); + try { + final PsiDeclarationStatement decl = elementFactory.createVariableDeclarationStatement(localName, myField.getType(), null); + anchorBlock.addAfter(decl, anchorBlock.getLBrace()); + final PsiReferenceExpression refExpr = (PsiReferenceExpression)elementFactory.createExpressionFromText(localName, null); + for (int j = 0; j < refs.length; j++) { + PsiReference ref = refs[j]; + if (ref instanceof PsiReferenceExpression) { + ((PsiReferenceExpression)ref).replace(refExpr); + } + } + myField.delete(); + } + catch (IncorrectOperationException e) { + LOG.error(e); + } + } + } + + private PsiCodeBlock findAnchorBlock(final PsiReference[] refs) { + PsiCodeBlock result = null; + for (int i = 0; i < refs.length; i++) { + final PsiElement element = refs[i].getElement(); + PsiCodeBlock block = PsiTreeUtil.getParentOfType(element, PsiCodeBlock.class); + if (result == null) { + result = block; + } + else { + final PsiElement commonParent = PsiTreeUtil.findCommonParent(result, block); + result = PsiTreeUtil.getParentOfType(commonParent, PsiCodeBlock.class, false); + } + } + return result; + } + } +} diff --git a/source/com/intellij/psi/controlFlow/AllVariablesControlFlowPolicy.java b/source/com/intellij/psi/controlFlow/AllVariablesControlFlowPolicy.java index df9e08be6b19..b358efbc1dc6 100644 --- a/source/com/intellij/psi/controlFlow/AllVariablesControlFlowPolicy.java +++ b/source/com/intellij/psi/controlFlow/AllVariablesControlFlowPolicy.java @@ -26,7 +26,7 @@ public class AllVariablesControlFlowPolicy implements ControlFlowPolicy { return true; } - public AllVariablesControlFlowPolicy getInstance() { + public static AllVariablesControlFlowPolicy getInstance() { return INSTANCE; }