inline parameter: conflict if replacement would be inaccessible
authoranna <anna.kozlova@jetbrains.com>
Fri, 12 Feb 2010 13:01:21 +0000 (12 16:01 +0300)
committeranna <anna.kozlova@jetbrains.com>
Fri, 12 Feb 2010 15:57:10 +0000 (12 18:57 +0300)
java/java-impl/src/com/intellij/refactoring/inline/InlineParameterExpressionProcessor.java
java/java-tests/testData/refactoring/inlineParameter/RefCallerParameterInCallChain.java [new file with mode: 0644]
java/java-tests/testSrc/com/intellij/refactoring/inline/InlineParameterTest.java

index 28d8ea1..a360bed 100644 (file)
@@ -175,94 +175,18 @@ public class InlineParameterExpressionProcessor extends BaseRefactoringProcessor
   @Override
   protected boolean preprocessUsages(Ref<UsageInfo[]> refUsages) {
     final MultiMap<PsiElement, String> conflicts = new MultiMap<PsiElement, String>();
-    myInitializer.accept(new JavaRecursiveElementWalkingVisitor() {
-      @Override
-      public void visitReferenceExpression(final PsiReferenceExpression expression) {
-        super.visitReferenceExpression(expression);
-        final PsiElement element = expression.resolve();
-        if (element instanceof PsiMember && !((PsiModifierListOwner)element).hasModifierProperty(PsiModifier.STATIC)) {
-          if (myMethod.hasModifierProperty(PsiModifier.STATIC)) {
-            conflicts.putValue(expression, "Parameter initializer depends on " + RefactoringUIUtil.getDescription(element, false) + " which is not available inside the static method");
-          }
-        }
-        if (element instanceof PsiMethod || element instanceof PsiField) {
-          if (!mySameClass && !((PsiModifierListOwner)element).hasModifierProperty(PsiModifier.STATIC)) {
-            conflicts.putValue(expression, "Parameter initializer depend on non static member from some other class");
-          } else if (!PsiUtil.isAccessible((PsiMember)element, myMethod, null)) {
-            conflicts.putValue(expression, "Parameter initializer depends on value which is not available inside method");
-          }
-        } else if (element instanceof PsiParameter) {
-          conflicts.putValue(expression, "Parameter initializer depends on callers parameter");
-        }
-      }
-
-      @Override
-      public void visitThisExpression(PsiThisExpression thisExpression) {
-        super.visitThisExpression(thisExpression);
-        final PsiJavaCodeReferenceElement qualifier = thisExpression.getQualifier();
-        PsiElement containingClass;
-        if (qualifier != null) {
-          containingClass = qualifier.resolve();
-        }
-        else {
-          containingClass = PsiTreeUtil.getParentOfType(myMethodCall, PsiClass.class);
-        }
-        final PsiClass methodContainingClass = myMethod.getContainingClass();
-        LOG.assertTrue(methodContainingClass != null);
-        if (!PsiTreeUtil.isAncestor(containingClass, methodContainingClass, false)) {
-          conflicts.putValue(thisExpression,
-                             "Parameter initializer depends on this which is not available inside the method and cannot be inlined");
-        } else if (myMethod.hasModifierProperty(PsiModifier.STATIC)) {
-          conflicts.putValue(thisExpression, "Parameter initializer depends on this which is not available inside the static method");
-        }
-      }
-
-      @Override
-      public void visitReferenceElement(PsiJavaCodeReferenceElement reference) {
-        super.visitReferenceElement(reference);
-        if (myMethod.hasModifierProperty(PsiModifier.STATIC)) {
-          final PsiElement resolved = reference.resolve();
-          if (resolved instanceof PsiClass && !((PsiClass)resolved).hasModifierProperty(PsiModifier.STATIC)) {
-            conflicts.putValue(reference, "Parameter initializer depends on non static class which is not available inside static method");
-          }
-        }
-      }
-
-      @Override
-      public void visitNewExpression(PsiNewExpression expression) {
-        super.visitNewExpression(expression);
-        final PsiJavaCodeReferenceElement reference = expression.getClassOrAnonymousClassReference();
-        if (reference != null) {
-          final PsiElement resolved = reference.resolve();
-          if (resolved instanceof PsiClass) {
-            final PsiClass refClass = (PsiClass)resolved;
-            final String classUnavailableMessage = "Parameter initializer depends on " +
-                                                   RefactoringUIUtil.getDescription(refClass, true) +
-                                                   " which is not available inside method and cannot be inlined";
-            if (!PsiUtil.isAccessible(refClass, myMethod, null)) {
-              conflicts.putValue(expression, classUnavailableMessage);
-            }
-            else {
-              final PsiClass methodContainingClass = myMethod.getContainingClass();
-              LOG.assertTrue(methodContainingClass != null);
-              if (!PsiTreeUtil.isAncestor(myMethod, refClass, false)) {
-                PsiElement parent = refClass;
-                while ((parent = parent.getParent()) instanceof PsiClass) {
-                  if (!PsiUtil.isAccessible((PsiClass)parent, myMethod, null)) {
-                    break;
-                  }
-                }
-                if (!(parent instanceof PsiFile)) {
-                  conflicts.putValue(expression, classUnavailableMessage);
-                }
-              }
-            }
-          }
+    final UsageInfo[] usages = refUsages.get();
+    final InaccessibleExpressionsDetector detector = new InaccessibleExpressionsDetector(conflicts);
+    myInitializer.accept(detector);
+    for (UsageInfo usage : usages) {
+      if (usage instanceof LocalReplacementUsageInfo) {
+        final PsiElement replacement = ((LocalReplacementUsageInfo)usage).getReplacement();
+        if (replacement != null) {
+          replacement.accept(detector);
         }
       }
-    });
+    }
 
-    final UsageInfo[] usages = refUsages.get();
     final Set<PsiVariable> vars = new HashSet<PsiVariable>();
     for (UsageInfo usageInfo : usages) {
       if (usageInfo instanceof LocalReplacementUsageInfo) {
@@ -377,4 +301,97 @@ public class InlineParameterExpressionProcessor extends BaseRefactoringProcessor
       return myVariable != null && myVariable.isValid() ? myVariable : null;
     }
   }
+
+  private class InaccessibleExpressionsDetector extends JavaRecursiveElementWalkingVisitor {
+    private final MultiMap<PsiElement, String> myConflicts;
+
+    public InaccessibleExpressionsDetector(MultiMap<PsiElement, String> conflicts) {
+      myConflicts = conflicts;
+    }
+
+    @Override
+    public void visitReferenceExpression(final PsiReferenceExpression expression) {
+      super.visitReferenceExpression(expression);
+      final PsiElement element = expression.resolve();
+      if (element instanceof PsiMember && !((PsiModifierListOwner)element).hasModifierProperty(PsiModifier.STATIC)) {
+        if (myMethod.hasModifierProperty(PsiModifier.STATIC)) {
+          myConflicts.putValue(expression, "Parameter initializer depends on " + RefactoringUIUtil.getDescription(element, false) + " which is not available inside the static method");
+        }
+      }
+      if (element instanceof PsiMethod || element instanceof PsiField) {
+        if (!mySameClass && !((PsiModifierListOwner)element).hasModifierProperty(PsiModifier.STATIC)) {
+          myConflicts.putValue(expression, "Parameter initializer depend on non static member from some other class");
+        } else if (!PsiUtil.isAccessible((PsiMember)element, myMethod, null)) {
+          myConflicts.putValue(expression, "Parameter initializer depends on value which is not available inside method");
+        }
+      } else if (element instanceof PsiParameter) {
+        myConflicts.putValue(expression, "Parameter initializer depends on callers parameter");
+      }
+    }
+
+    @Override
+    public void visitThisExpression(PsiThisExpression thisExpression) {
+      super.visitThisExpression(thisExpression);
+      final PsiJavaCodeReferenceElement qualifier = thisExpression.getQualifier();
+      PsiElement containingClass;
+      if (qualifier != null) {
+        containingClass = qualifier.resolve();
+      }
+      else {
+        containingClass = PsiTreeUtil.getParentOfType(myMethodCall, PsiClass.class);
+      }
+      final PsiClass methodContainingClass = myMethod.getContainingClass();
+      LOG.assertTrue(methodContainingClass != null);
+      if (!PsiTreeUtil.isAncestor(containingClass, methodContainingClass, false)) {
+        myConflicts.putValue(thisExpression,
+                           "Parameter initializer depends on this which is not available inside the method and cannot be inlined");
+      } else if (myMethod.hasModifierProperty(PsiModifier.STATIC)) {
+        myConflicts.putValue(thisExpression, "Parameter initializer depends on this which is not available inside the static method");
+      }
+    }
+
+    @Override
+    public void visitReferenceElement(PsiJavaCodeReferenceElement reference) {
+      super.visitReferenceElement(reference);
+      if (myMethod.hasModifierProperty(PsiModifier.STATIC)) {
+        final PsiElement resolved = reference.resolve();
+        if (resolved instanceof PsiClass && !((PsiClass)resolved).hasModifierProperty(PsiModifier.STATIC)) {
+          myConflicts.putValue(reference, "Parameter initializer depends on non static class which is not available inside static method");
+        }
+      }
+    }
+
+    @Override
+    public void visitNewExpression(PsiNewExpression expression) {
+      super.visitNewExpression(expression);
+      final PsiJavaCodeReferenceElement reference = expression.getClassOrAnonymousClassReference();
+      if (reference != null) {
+        final PsiElement resolved = reference.resolve();
+        if (resolved instanceof PsiClass) {
+          final PsiClass refClass = (PsiClass)resolved;
+          final String classUnavailableMessage = "Parameter initializer depends on " +
+                                                 RefactoringUIUtil.getDescription(refClass, true) +
+                                                 " which is not available inside method and cannot be inlined";
+          if (!PsiUtil.isAccessible(refClass, myMethod, null)) {
+            myConflicts.putValue(expression, classUnavailableMessage);
+          }
+          else {
+            final PsiClass methodContainingClass = myMethod.getContainingClass();
+            LOG.assertTrue(methodContainingClass != null);
+            if (!PsiTreeUtil.isAncestor(myMethod, refClass, false)) {
+              PsiElement parent = refClass;
+              while ((parent = parent.getParent()) instanceof PsiClass) {
+                if (!PsiUtil.isAccessible((PsiClass)parent, myMethod, null)) {
+                  break;
+                }
+              }
+              if (!(parent instanceof PsiFile)) {
+                myConflicts.putValue(expression, classUnavailableMessage);
+              }
+            }
+          }
+        }
+      }
+    }
+  }
 }
\ No newline at end of file
diff --git a/java/java-tests/testData/refactoring/inlineParameter/RefCallerParameterInCallChain.java b/java/java-tests/testData/refactoring/inlineParameter/RefCallerParameterInCallChain.java
new file mode 100644 (file)
index 0000000..62dc4ce
--- /dev/null
@@ -0,0 +1,21 @@
+class Foo {
+    void f(boolean b) {
+        String project = project(b);
+        if (b) {
+            barrrr(project.substring(0));
+        } else {
+            if (true) {
+                barrrr(project.substring(0));
+            }
+        }
+
+    }
+
+    private void barrrr(String <caret>pProject) {
+        System.out.println(pProject);
+    }
+
+    String project (boolean b) {
+        return null;
+    }
+}
\ No newline at end of file
index 1e3a752..65a9b51 100644 (file)
@@ -237,6 +237,15 @@ public class InlineParameterTest extends LightCodeInsightTestCase {
     }
   }
 
+  public void testRefCallerParameterInCallChain() throws Exception {
+    try {
+      doTest(false);
+    }
+    catch (BaseRefactoringProcessor.ConflictsInTestsException e) {
+      assertEquals("Parameter initializer depends on callers parameter", e.getMessage());
+    }
+  }
+
   private void doTest(final boolean createLocal) throws Exception {
     getProject().putUserData(InlineParameterExpressionProcessor.CREATE_LOCAL_FOR_TESTS,createLocal);