diff --git a/java/java-tests/testData/refactoring/safeDelete/usageInGenerated/after/gen/B.java b/java/java-tests/testData/refactoring/safeDelete/usageInGenerated/after/gen/B.java new file mode 100644 index 000000000000..c9060d95e61d --- /dev/null +++ b/java/java-tests/testData/refactoring/safeDelete/usageInGenerated/after/gen/B.java @@ -0,0 +1,5 @@ +class B { + { + A.foo(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/safeDelete/usageInGenerated/after/src/A.java b/java/java-tests/testData/refactoring/safeDelete/usageInGenerated/after/src/A.java new file mode 100644 index 000000000000..61ff2abcc95e --- /dev/null +++ b/java/java-tests/testData/refactoring/safeDelete/usageInGenerated/after/src/A.java @@ -0,0 +1,2 @@ +public class A { +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/safeDelete/usageInGenerated/before/gen/B.java b/java/java-tests/testData/refactoring/safeDelete/usageInGenerated/before/gen/B.java new file mode 100644 index 000000000000..c9060d95e61d --- /dev/null +++ b/java/java-tests/testData/refactoring/safeDelete/usageInGenerated/before/gen/B.java @@ -0,0 +1,5 @@ +class B { + { + A.foo(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/safeDelete/usageInGenerated/before/src/A.java b/java/java-tests/testData/refactoring/safeDelete/usageInGenerated/before/src/A.java new file mode 100644 index 000000000000..9b0dc45cc349 --- /dev/null +++ b/java/java-tests/testData/refactoring/safeDelete/usageInGenerated/before/src/A.java @@ -0,0 +1,3 @@ +public class A { + public static void foo(){} +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/refactoring/SafeDeleteTest.java b/java/java-tests/testSrc/com/intellij/refactoring/SafeDeleteTest.java index 2e3da90b2733..0c534a9c67af 100644 --- a/java/java-tests/testSrc/com/intellij/refactoring/SafeDeleteTest.java +++ b/java/java-tests/testSrc/com/intellij/refactoring/SafeDeleteTest.java @@ -10,9 +10,11 @@ import com.intellij.psi.PsiClass; import com.intellij.psi.PsiElement; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.refactoring.safeDelete.SafeDeleteHandler; -import com.intellij.testFramework.IdeaTestUtil; import com.intellij.testFramework.PlatformTestUtil; +import com.intellij.testFramework.PsiTestUtil; import org.jetbrains.annotations.NonNls; +import org.jetbrains.jps.model.java.JavaSourceRootType; +import org.jetbrains.jps.model.java.JpsJavaExtensionService; import java.io.File; @@ -135,8 +137,7 @@ public class SafeDeleteTest extends MultiFileTestCase { } catch (BaseRefactoringProcessor.ConflictsInTestsException e) { String message = e.getMessage(); - assertTrue(message, message.equals("method Super.foo() has 1 usage that is not safe to delete.\n" + - "Of those 0 usages are in strings, comments, or non-code files.")); + assertEquals("method Super.foo() has 1 usage that is not safe to delete.", message); } } @@ -157,11 +158,15 @@ public class SafeDeleteTest extends MultiFileTestCase { } catch (BaseRefactoringProcessor.ConflictsInTestsException e) { String message = e.getMessage(); - assertTrue(message, message.startsWith("local variable varName has 1 usage that is not safe to delete.\n" + - "Of those 0 usages are in strings, comments, or non-code files.")); + assertEquals("local variable varName has 1 usage that is not safe to delete.", message); } } + public void testUsageInGenerated() throws Exception { + myDoCompare = false; + doTest("A"); + } + public void testLastResourceVariable() throws Exception { LanguageLevelProjectExtension.getInstance(getProject()).setLanguageLevel(LanguageLevel.JDK_1_7); doSingleFileTest(); @@ -196,6 +201,22 @@ public class SafeDeleteTest extends MultiFileTestCase { }); } + @Override + protected void prepareProject(VirtualFile rootDir) { + VirtualFile src = rootDir.findChild("src"); + if (src == null) { + super.prepareProject(rootDir); + } + else { + PsiTestUtil.addContentRoot(myModule, rootDir); + PsiTestUtil.addSourceRoot(myModule, src); + } + VirtualFile gen = rootDir.findChild("gen"); + if (gen != null) { + PsiTestUtil.addSourceRoot(myModule, gen, JavaSourceRootType.SOURCE, JpsJavaExtensionService.getInstance().createSourceRootProperties("", true)); + } + } + private void doSingleFileTest() throws Exception { configureByFile(getTestRoot() + getTestName(false) + ".java"); performAction(); diff --git a/platform/lang-impl/src/com/intellij/refactoring/BaseRefactoringProcessor.java b/platform/lang-impl/src/com/intellij/refactoring/BaseRefactoringProcessor.java index 13f6c9f7f92b..be2d8c3b2e55 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/BaseRefactoringProcessor.java +++ b/platform/lang-impl/src/com/intellij/refactoring/BaseRefactoringProcessor.java @@ -366,10 +366,18 @@ public abstract class BaseRefactoringProcessor implements Runnable { nonCodeFiles.remove(null); dynamicUsagesCodeFiles.remove(null); - presentation.setCodeUsagesString(descriptor.getCodeReferencesText(codeUsageCount, codeFiles.size())); + String codeReferencesText = descriptor.getCodeReferencesText(codeUsageCount, codeFiles.size()); + presentation.setCodeUsagesString(codeReferencesText); presentation.setNonCodeUsagesString(descriptor.getCommentReferencesText(nonCodeUsageCount, nonCodeFiles.size())); presentation.setDynamicUsagesString("Dynamic " + StringUtil.decapitalize(descriptor.getCodeReferencesText(dynamicUsagesCount, dynamicUsagesCodeFiles.size()))); - presentation.setUsagesInGeneratedCodeString(descriptor.getCodeReferencesText(codeUsageCount, codeFiles.size()) + " in generated code"); + String generatedCodeString; + if (codeReferencesText.contains("in code")) { + generatedCodeString = StringUtil.replace(codeReferencesText, "in code", "in generated code"); + } + else { + generatedCodeString = codeReferencesText + " in generated code"; + } + presentation.setUsagesInGeneratedCodeString(generatedCodeString); return presentation; } diff --git a/platform/lang-impl/src/com/intellij/refactoring/safeDelete/SafeDeleteProcessor.java b/platform/lang-impl/src/com/intellij/refactoring/safeDelete/SafeDeleteProcessor.java index 2d641388ba5b..284f3fea5614 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/safeDelete/SafeDeleteProcessor.java +++ b/platform/lang-impl/src/com/intellij/refactoring/safeDelete/SafeDeleteProcessor.java @@ -194,11 +194,8 @@ public class SafeDeleteProcessor extends BaseRefactoringProcessor { final HashMap elementsToUsageHolders = sortUsages(usages); final Collection usageHolders = elementsToUsageHolders.values(); for (UsageHolder usageHolder : usageHolders) { - if (usageHolder.getNonCodeUsagesNumber() != usageHolder.getUnsafeUsagesNumber()) { - final String description = usageHolder.getDescription(); - if (description != null) { - conflicts.add(description); - } + if (usageHolder.hasUnsafeUsagesInCode()) { + conflicts.add(usageHolder.getDescription()); } } @@ -352,9 +349,8 @@ public class SafeDeleteProcessor extends BaseRefactoringProcessor { @Override protected boolean isPreviewUsages(UsageInfo[] usages) { - if(myPreviewNonCodeUsages && UsageViewUtil.hasNonCodeUsages(usages)) { - WindowManager.getInstance().getStatusBar(myProject).setInfo( - RefactoringBundle.message("occurrences.found.in.comments.strings.and.non.java.files")); + if(myPreviewNonCodeUsages && (UsageViewUtil.hasNonCodeUsages(usages) || UsageViewUtil.hasUsagesInGeneratedCode(usages, myProject))) { + WindowManager.getInstance().getStatusBar(myProject).setInfo(RefactoringBundle.message("occurrences.found.in.comments.strings.non.java.files.and.generated.code")); return true; } diff --git a/platform/lang-impl/src/com/intellij/refactoring/safeDelete/UsageHolder.java b/platform/lang-impl/src/com/intellij/refactoring/safeDelete/UsageHolder.java index 78cb60665aa7..501a2599fef3 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/safeDelete/UsageHolder.java +++ b/platform/lang-impl/src/com/intellij/refactoring/safeDelete/UsageHolder.java @@ -16,81 +16,68 @@ package com.intellij.refactoring.safeDelete; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.GeneratedSourcesFilter; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiElement; -import com.intellij.psi.SmartPsiElementPointer; import com.intellij.psi.SmartPointerManager; +import com.intellij.psi.SmartPsiElementPointer; import com.intellij.refactoring.RefactoringBundle; import com.intellij.refactoring.safeDelete.usageInfo.SafeDeleteReferenceUsageInfo; import com.intellij.refactoring.util.RefactoringUIUtil; import com.intellij.usageView.UsageInfo; - -import java.util.ArrayList; +import org.jetbrains.annotations.NotNull; /** * @author dsl */ class UsageHolder { private final SmartPsiElementPointer myElementPointer; - private final SafeDeleteReferenceUsageInfo[] myUsages; - private int myUnsafeUsages = -1; - private int myNonCodeUsages = -1; + private int myUnsafeUsages; + private int myNonCodeUnsafeUsages; public UsageHolder(PsiElement element, UsageInfo[] usageInfos) { - myElementPointer = SmartPointerManager.getInstance(element.getProject()).createSmartPsiElementPointer(element); + Project project = element.getProject(); + myElementPointer = SmartPointerManager.getInstance(project).createSmartPsiElementPointer(element); - ArrayList elementUsages = new ArrayList(); + GeneratedSourcesFilter[] filters = GeneratedSourcesFilter.EP_NAME.getExtensions(); for (UsageInfo usageInfo : usageInfos) { - if (usageInfo instanceof SafeDeleteReferenceUsageInfo) { - final SafeDeleteReferenceUsageInfo referenceUsageInfo = (SafeDeleteReferenceUsageInfo)usageInfo; - if (referenceUsageInfo.getReferencedElement() == element) { - elementUsages.add(referenceUsageInfo); + if (!(usageInfo instanceof SafeDeleteReferenceUsageInfo)) continue; + final SafeDeleteReferenceUsageInfo usage = (SafeDeleteReferenceUsageInfo)usageInfo; + if (usage.getReferencedElement() != element) continue; + + if (!usage.isSafeDelete()) { + myUnsafeUsages++; + if (usage.isNonCodeUsage || isInGeneratedCode(usage, project, filters)) { + myNonCodeUnsafeUsages++; } } } - myUsages = - elementUsages.toArray(new SafeDeleteReferenceUsageInfo[elementUsages.size()]); } - public int getNonCodeUsagesNumber() { - if(myNonCodeUsages < 0) { - int nonCodeUsages = 0; - for (SafeDeleteReferenceUsageInfo usage : myUsages) { - if (usage.isNonCodeUsage) { - nonCodeUsages++; - } + private static boolean isInGeneratedCode(SafeDeleteReferenceUsageInfo usage, Project project, GeneratedSourcesFilter[] filters) { + VirtualFile file = usage.getVirtualFile(); + if (file == null) return false; + + for (GeneratedSourcesFilter filter : filters) { + if (filter.isGeneratedSource(file, project)) { + return true; } - myNonCodeUsages = nonCodeUsages; } - return myNonCodeUsages; - } - - public int getUnsafeUsagesNumber() { - if(myUnsafeUsages < 0) { - int nonSafeUsages = 0; - for (SafeDeleteReferenceUsageInfo usage : myUsages) { - if (!usage.isSafeDelete()) { - nonSafeUsages++; - } - } - myUnsafeUsages = nonSafeUsages; - } - return myUnsafeUsages; + return false; } + @NotNull public String getDescription() { - final int nonCodeUsages = getNonCodeUsagesNumber(); - final int unsafeUsages = getUnsafeUsagesNumber(); - - if(unsafeUsages == 0) return null; - final PsiElement element = myElementPointer.getElement(); - if (unsafeUsages == nonCodeUsages) { - return RefactoringBundle.message("0.has.1.usages.in.comments.and.strings", - RefactoringUIUtil.getDescription(element, true), - unsafeUsages); + String message = RefactoringBundle.message("0.has.1.usages.that.are.not.safe.to.delete", RefactoringUIUtil.getDescription(element, true), myUnsafeUsages); + if (myNonCodeUnsafeUsages > 0) { + message += "
" + RefactoringBundle.message("safe.delete.of.those.0.in.comments.strings.non.code", myNonCodeUnsafeUsages); } + return message; + } - return RefactoringBundle.message("0.has.1.usages.that.are.not.safe.to.delete.of.those.2", - RefactoringUIUtil.getDescription(element, true), unsafeUsages, nonCodeUsages); + public boolean hasUnsafeUsagesInCode() { + return myUnsafeUsages != myNonCodeUnsafeUsages; } } diff --git a/platform/lang-impl/src/com/intellij/usageView/UsageViewUtil.java b/platform/lang-impl/src/com/intellij/usageView/UsageViewUtil.java index 9de0e517320a..de1d7e25803b 100644 --- a/platform/lang-impl/src/com/intellij/usageView/UsageViewUtil.java +++ b/platform/lang-impl/src/com/intellij/usageView/UsageViewUtil.java @@ -18,7 +18,10 @@ package com.intellij.usageView; import com.intellij.lang.injection.InjectedLanguageManager; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.GeneratedSourcesFilter; import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.ElementDescriptionUtil; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; @@ -64,6 +67,22 @@ public class UsageViewUtil { return false; } + public static boolean hasUsagesInGeneratedCode(UsageInfo[] usages, Project project) { + GeneratedSourcesFilter[] filters = GeneratedSourcesFilter.EP_NAME.getExtensions(); + for (UsageInfo usage : usages) { + VirtualFile file = usage.getVirtualFile(); + if (file != null) { + for (GeneratedSourcesFilter filter : filters) { + if (filter.isGeneratedSource(file, project)) { + return true; + } + } + } + } + + return false; + } + public static boolean hasReadOnlyUsages(UsageInfo[] usages) { for (UsageInfo usage : usages) { if (!usage.isWritable()) return true; diff --git a/platform/platform-resources-en/src/messages/RefactoringBundle.properties b/platform/platform-resources-en/src/messages/RefactoringBundle.properties index a84626e5fee5..592d20c9ed9e 100644 --- a/platform/platform-resources-en/src/messages/RefactoringBundle.properties +++ b/platform/platform-resources-en/src/messages/RefactoringBundle.properties @@ -569,6 +569,7 @@ replace.default.constructor.of.0.with.a.factory.method=Replace default construct replace.constructor.with.factory.method=Replace constructor with factory method replace.default.constructor.with.factory.method=Replace default constructor with factory method occurrences.found.in.comments.strings.and.non.java.files=Occurrences found in comments, strings and non-code files +occurrences.found.in.comments.strings.non.java.files.and.generated.code=Occurrences found in comments, strings, non-code files and generated code 0.referenced.in.1.will.not.be.accessible.in.module.2={0}, referenced in {1}, will not be accessible in module {2} 0.referenced.in.1.will.not.be.accessible.from.module.2={0}, referenced in {1}, will not be accessible from module {2} 0.referenced.in.1.will.not.be.accessible.from.production.of.module.2={0}, referenced in {1}, will not be accessible from the production sources of module {2} @@ -681,8 +682,8 @@ directory.0.already.contains.a.file.named.1=Directory {0} \nalready contains a f getter.and.setter.methods.found.for.the.field.0=Getter and setter methods found for the field {0}. \n{1} them as well? getter.method.found.for.the.field.0=Getter method found for the field {0}. \n{1} the getter as well? setter.method.found.for.the.field.0=Setter method found for the field {0}. \n{1} the setter as well? -0.has.1.usages.in.comments.and.strings={0} has {1,choice,1#1 usage|2#{1,number} usages} in strings, comments, or non-code files -0.has.1.usages.that.are.not.safe.to.delete.of.those.2={0} has {1,choice,1#1 usage that is|2#{1,number} usages that are} not safe to delete.\nOf those {2,choice,0#0 usages are|1#1 usage is|2#{2,number} usages are} in strings, comments, or non-code files. +0.has.1.usages.that.are.not.safe.to.delete={0} has {1,choice,1#1 usage that is|2#{1,number} usages that are} not safe to delete. +safe.delete.of.those.0.in.comments.strings.non.code=Of those {0,choice,1#1 usage is|2#{0,number} usages are} in strings, comments, non-code files or generated code. type.cook.drop.obsolete.casts=&Drop obsolete casts type.cook.preserve.raw.arrays=Preserve raw &arrays type.cook.leave.object.parameterized.types.raw=&Leave Object-parameterized types raw diff --git a/platform/testFramework/src/com/intellij/testFramework/PsiTestUtil.java b/platform/testFramework/src/com/intellij/testFramework/PsiTestUtil.java index 9115d7992825..a91ea6f825a3 100644 --- a/platform/testFramework/src/com/intellij/testFramework/PsiTestUtil.java +++ b/platform/testFramework/src/com/intellij/testFramework/PsiTestUtil.java @@ -43,6 +43,7 @@ import junit.framework.Assert; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.jps.model.JpsElement; import org.jetbrains.jps.model.java.JavaSourceRootType; import org.jetbrains.jps.model.module.JpsModuleSourceRootType; @@ -148,14 +149,21 @@ public class PsiTestUtil { addSourceRoot(module, vDir, isTestSource ? JavaSourceRootType.TEST_SOURCE : JavaSourceRootType.SOURCE); } - public static void addSourceRoot(Module module, final VirtualFile vDir, @NotNull final JpsModuleSourceRootType rootType) { + public static

void addSourceRoot(Module module, + final VirtualFile vDir, + @NotNull final JpsModuleSourceRootType

rootType) { + addSourceRoot(module, vDir, rootType, rootType.createDefaultProperties()); + } + + public static

void addSourceRoot(Module module, final VirtualFile vDir, + @NotNull final JpsModuleSourceRootType

rootType, final P properties) { updateModel(module, new Consumer() { @SuppressWarnings("unchecked") @Override public void consume(ModifiableRootModel model) { ContentEntry entry = findContentEntry(model, vDir); if (entry == null) entry = model.addContentEntry(vDir); - entry.addSourceFolder(vDir, rootType); + entry.addSourceFolder(vDir, rootType, properties); } }); }