From 3e9fc08e096ddb212cf40a285887b7ed5dd8897b Mon Sep 17 00:00:00 2001 From: Dmitry Trofimov Date: Thu, 6 Oct 2016 17:45:51 +0300 Subject: [PATCH 01/43] Fix running coverage for nose tests (PY-14869) --- .../helpers/coverage_runner/run_coverage.py | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/python/helpers/coverage_runner/run_coverage.py b/python/helpers/coverage_runner/run_coverage.py index f637d6e8d42c..1c1f69a881c4 100644 --- a/python/helpers/coverage_runner/run_coverage.py +++ b/python/helpers/coverage_runner/run_coverage.py @@ -13,18 +13,22 @@ else: from coverage.cmdline import main coverage_file = os.getenv('PYCHARM_COVERAGE_FILE') + +coverage_file = coverage_file[0:-len(".coverage")] + run_cov = os.getenv('PYCHARM_RUN_COVERAGE') if os.getenv('CREATE_TEMP_COVERAGE_FILE'): line = 'LOG: PyCharm: File mapping:%s\t%s\n' import tempfile (h, new_cov_file) = tempfile.mkstemp(prefix='pycharm-coverage') - print(line%(coverage_file, new_cov_file)) + print(line%(coverage_file + ".coverage", new_cov_file + ".coverage")) print(line%(coverage_file + '.syspath.txt', new_cov_file + '.syspath.txt')) print(line%(coverage_file + '.xml', new_cov_file + '.xml')) - coverage_file = new_cov_file + coverage_file = new_cov_file + ".cov" if coverage_file: - os.environ['COVERAGE_FILE'] = coverage_file + os.environ['COVERAGE_FILE'] = coverage_file + ".coverage" + if run_cov: a_file = open(coverage_file + '.syspath.txt', mode='w') a_file.write(os.getcwd()+"\n") @@ -41,7 +45,10 @@ for arg in sys.argv: sys.argv = argv cwd = os.getcwd() -main() -if run_cov: - os.chdir(cwd) - main(["xml", "-o", coverage_file + ".xml", "--ignore-errors"]) \ No newline at end of file + +try: + main() +finally: + if run_cov: + os.chdir(cwd) + main(["xml", "-o", coverage_file + ".xml", "--ignore-errors"]) \ No newline at end of file From 3a7563c3f0d7d752ce7d3b303243fb1f67d3cee3 Mon Sep 17 00:00:00 2001 From: Sergey Simonchik Date: Thu, 6 Oct 2016 23:58:16 +0300 Subject: [PATCH 02/43] EA-89520 - NPE: SwingHelper.adjustDialogSizeToFitPreferredSize --- platform/platform-impl/src/com/intellij/util/ui/SwingHelper.java | 1 + 1 file changed, 1 insertion(+) diff --git a/platform/platform-impl/src/com/intellij/util/ui/SwingHelper.java b/platform/platform-impl/src/com/intellij/util/ui/SwingHelper.java index 3200bf9f471c..617fce5e2781 100644 --- a/platform/platform-impl/src/com/intellij/util/ui/SwingHelper.java +++ b/platform/platform-impl/src/com/intellij/util/ui/SwingHelper.java @@ -192,6 +192,7 @@ public class SwingHelper { public static void adjustDialogSizeToFitPreferredSize(@NotNull DialogWrapper dialogWrapper) { JRootPane rootPane = dialogWrapper.getRootPane(); + if (rootPane == null) return; Dimension componentSize = rootPane.getSize(); Dimension componentPreferredSize = rootPane.getPreferredSize(); if (componentPreferredSize.width <= componentSize.width && componentPreferredSize.height <= componentSize.height) { From ebf320b288853d74d9fce868e8d18c22440693e4 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Fri, 7 Oct 2016 11:57:53 +0700 Subject: [PATCH 03/43] Stream API migration various fixes 1. findFirst() scenario can pull previous assignment (not declaration) now 2. anyMatch() fix did not work if there's single assignment to non-variable (e.g. array element) 3. if non-adjacent return becomes unreachable after findFirst()/anyMatch(), it returned automatically now --- .../streamMigration/MigrateToStreamFix.java | 13 +++++ .../ReplaceWithFindFirstFix.java | 18 ++++++- .../streamMigration/ReplaceWithMatchFix.java | 49 +++++++++++-------- .../afterAnyMatchArrayAssignment.java | 12 +++++ .../afterAnyMatchUnreachableReturn.java | 13 +++++ .../afterFindFirstReAssignment.java | 20 ++++++++ .../afterFindFirstReturnUnreachable.java | 15 ++++++ .../beforeAnyMatchArrayAssignment.java | 16 ++++++ .../beforeAnyMatchUnreachableReturn.java | 19 +++++++ .../beforeFindFirstReAssignment.java | 25 ++++++++++ .../beforeFindFirstReturnUnreachable.java | 21 ++++++++ 11 files changed, 198 insertions(+), 23 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterAnyMatchArrayAssignment.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterAnyMatchUnreachableReturn.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterFindFirstReAssignment.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterFindFirstReturnUnreachable.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAnyMatchArrayAssignment.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAnyMatchUnreachableReturn.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeFindFirstReAssignment.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeFindFirstReturnUnreachable.java diff --git a/java/java-impl/src/com/intellij/codeInspection/streamMigration/MigrateToStreamFix.java b/java/java-impl/src/com/intellij/codeInspection/streamMigration/MigrateToStreamFix.java index 5a1a4c2d0583..2bdbe16a8973 100644 --- a/java/java-impl/src/com/intellij/codeInspection/streamMigration/MigrateToStreamFix.java +++ b/java/java-impl/src/com/intellij/codeInspection/streamMigration/MigrateToStreamFix.java @@ -24,6 +24,7 @@ import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.codeStyle.JavaCodeStyleManager; +import com.intellij.psi.controlFlow.*; import com.intellij.psi.util.PsiTreeUtil; import com.siyeh.ig.psiutils.ExpressionUtils; import one.util.streamex.StreamEx; @@ -152,4 +153,16 @@ abstract class MigrateToStreamFix implements LocalQuickFix { statement.delete(); } } + + static boolean isReachable(PsiReturnStatement target) { + ControlFlow flow; + try { + flow = ControlFlowFactory.getInstance(target.getProject()) + .getControlFlow(target.getParent(), LocalsOrMyInstanceFieldsControlFlowPolicy.getInstance()); + } + catch (AnalysisCanceledException e) { + return true; + } + return ControlFlowUtil.isInstructionReachable(flow, flow.getStartOffset(target), 0); + } } diff --git a/java/java-impl/src/com/intellij/codeInspection/streamMigration/ReplaceWithFindFirstFix.java b/java/java-impl/src/com/intellij/codeInspection/streamMigration/ReplaceWithFindFirstFix.java index ff9b77733dfe..2a60250cc194 100644 --- a/java/java-impl/src/com/intellij/codeInspection/streamMigration/ReplaceWithFindFirstFix.java +++ b/java/java-impl/src/com/intellij/codeInspection/streamMigration/ReplaceWithFindFirstFix.java @@ -19,6 +19,7 @@ import com.intellij.codeInsight.PsiEquivalenceUtil; import com.intellij.codeInspection.streamMigration.StreamApiMigrationInspection.InitializerUsageStatus; import com.intellij.openapi.project.Project; import com.intellij.psi.*; +import com.intellij.psi.util.PsiTreeUtil; import com.intellij.refactoring.util.RefactoringUtil; import com.siyeh.ig.psiutils.ExpressionUtils; import org.jetbrains.annotations.NotNull; @@ -52,10 +53,12 @@ class ReplaceWithFindFirstFix extends MigrateToStreamFix { if (!ExpressionUtils.isSimpleExpression(orElseExpression)) return null; stream = generateOptionalUnwrap(stream, tb, value, orElseExpression, null); restoreComments(loopStatement, body); - if (nextReturnStatement.getParent() == loopStatement.getParent()) { + boolean sibling = nextReturnStatement.getParent() == loopStatement.getParent(); + PsiElement replacement = loopStatement.replace(elementFactory.createStatementFromText("return " + stream + ";", loopStatement)); + if(sibling || !isReachable(nextReturnStatement)) { nextReturnStatement.delete(); } - return loopStatement.replace(elementFactory.createStatementFromText("return " + stream + ";", loopStatement)); + return replacement; } else { PsiStatement[] statements = tb.getStatements(); @@ -84,6 +87,17 @@ class ReplaceWithFindFirstFix extends MigrateToStreamFix { return replaceInitializer(loopStatement, var, initializer, replacementText, status); } } + PsiAssignmentExpression previousAssignment = + ExpressionUtils.getAssignment(PsiTreeUtil.skipSiblingsBackward(loopStatement, PsiWhiteSpace.class, PsiComment.class)); + if(previousAssignment != null) { + PsiExpression prevRValue = previousAssignment.getRExpression(); + PsiExpression prevLValue = previousAssignment.getLExpression(); + if(prevRValue != null && prevLValue instanceof PsiReferenceExpression && ((PsiReferenceExpression)prevLValue).resolve() == var) { + previousAssignment.delete(); + return loopStatement.replace(elementFactory.createStatementFromText( + var.getName() + " = " + generateOptionalUnwrap(stream, tb, value, prevRValue, var.getType()) + ";", loopStatement)); + } + } return loopStatement.replace(elementFactory.createStatementFromText( var.getName() + " = " + generateOptionalUnwrap(stream, tb, value, lValue, var.getType()) + ";", loopStatement)); } diff --git a/java/java-impl/src/com/intellij/codeInspection/streamMigration/ReplaceWithMatchFix.java b/java/java-impl/src/com/intellij/codeInspection/streamMigration/ReplaceWithMatchFix.java index 1053a306f89c..1d0b0a14bfff 100644 --- a/java/java-impl/src/com/intellij/codeInspection/streamMigration/ReplaceWithMatchFix.java +++ b/java/java-impl/src/com/intellij/codeInspection/streamMigration/ReplaceWithMatchFix.java @@ -69,7 +69,11 @@ class ReplaceWithMatchFix extends MigrateToStreamFix { removeLoop(loopStatement); return returnValue.replace(elementFactory.createExpressionFromText(streamText, nextReturnStatement)); } - return loopStatement.replace(elementFactory.createStatementFromText("return " + streamText + ";", loopStatement)); + PsiElement result = loopStatement.replace(elementFactory.createStatementFromText("return " + streamText + ";", loopStatement)); + if(!isReachable(nextReturnStatement)) { + nextReturnStatement.delete(); + } + return result; } } } @@ -84,27 +88,30 @@ class ReplaceWithMatchFix extends MigrateToStreamFix { if(assignment != null) { PsiExpression lValue = assignment.getLExpression(); PsiExpression rValue = assignment.getRExpression(); - if (!(lValue instanceof PsiReferenceExpression) || rValue == null) return null; - PsiElement maybeVar = ((PsiReferenceExpression)lValue).resolve(); - if(maybeVar instanceof PsiVariable) { - // Simplify single assignments like this: - // boolean flag = false; - // for(....) if(...) {flag = true; break;} - PsiVariable var = (PsiVariable)maybeVar; - PsiExpression initializer = var.getInitializer(); - InitializerUsageStatus status = StreamApiMigrationInspection.getInitializerUsageStatus(var, loopStatement); - if(initializer != null && status != InitializerUsageStatus.UNKNOWN) { - String replacement; - if(ExpressionUtils.isLiteral(initializer, Boolean.FALSE) && - ExpressionUtils.isLiteral(rValue, Boolean.TRUE)) { - replacement = streamText; - } else if(ExpressionUtils.isLiteral(initializer, Boolean.TRUE) && - ExpressionUtils.isLiteral(rValue, Boolean.FALSE)) { - replacement = "!"+streamText; - } else { - replacement = streamText + "?" + rValue.getText() + ":" + initializer.getText(); + if ((lValue instanceof PsiReferenceExpression) && rValue != null) { + PsiElement maybeVar = ((PsiReferenceExpression)lValue).resolve(); + if (maybeVar instanceof PsiVariable) { + // Simplify single assignments like this: + // boolean flag = false; + // for(....) if(...) {flag = true; break;} + PsiVariable var = (PsiVariable)maybeVar; + PsiExpression initializer = var.getInitializer(); + InitializerUsageStatus status = StreamApiMigrationInspection.getInitializerUsageStatus(var, loopStatement); + if (initializer != null && status != InitializerUsageStatus.UNKNOWN) { + String replacement; + if (ExpressionUtils.isLiteral(initializer, Boolean.FALSE) && + ExpressionUtils.isLiteral(rValue, Boolean.TRUE)) { + replacement = streamText; + } + else if (ExpressionUtils.isLiteral(initializer, Boolean.TRUE) && + ExpressionUtils.isLiteral(rValue, Boolean.FALSE)) { + replacement = "!" + streamText; + } + else { + replacement = streamText + "?" + rValue.getText() + ":" + initializer.getText(); + } + return replaceInitializer(loopStatement, var, initializer, replacement, status); } - return replaceInitializer(loopStatement, var, initializer, replacement, status); } } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterAnyMatchArrayAssignment.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterAnyMatchArrayAssignment.java new file mode 100644 index 000000000000..6364503ce775 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterAnyMatchArrayAssignment.java @@ -0,0 +1,12 @@ +// "Replace with anyMatch()" "true" + +import java.util.List; + +public class Main { + public void testAssignment(List data) { + String[] found = {"no"}; + if (data.stream().map(String::trim).anyMatch(trimmed -> !trimmed.isEmpty())) { + found[0] = "yes"; + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterAnyMatchUnreachableReturn.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterAnyMatchUnreachableReturn.java new file mode 100644 index 000000000000..93a1eb0c5706 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterAnyMatchUnreachableReturn.java @@ -0,0 +1,13 @@ +// "Replace with anyMatch()" "true" + +import java.util.List; + +public class Main { + boolean find(List data) { + if(data != null) { + return data.stream().map(String::trim).anyMatch(trimmed -> trimmed.startsWith("xyz")); + } else { + throw new IllegalArgumentException(); + } + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterFindFirstReAssignment.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterFindFirstReAssignment.java new file mode 100644 index 000000000000..4851693aa34b --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterFindFirstReAssignment.java @@ -0,0 +1,20 @@ +// "Replace with findFirst()" "true" + +import java.util.List; +import java.util.Map; +import java.util.Objects; + +public class Main { + private int getInitialSize() {return 0;} + + public void testMap(Map> map) throws Exception { + int firstSize = 10; + + System.out.println(firstSize); + + // loop + // comment + firstSize = map.values().stream().filter(Objects::nonNull).findFirst().map(List::size).orElse(getInitialSize()); + System.out.println(firstSize); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterFindFirstReturnUnreachable.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterFindFirstReturnUnreachable.java new file mode 100644 index 000000000000..45ebed51b091 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterFindFirstReturnUnreachable.java @@ -0,0 +1,15 @@ +// "Replace with findFirst()" "true" + +import java.util.Collection; +import java.util.List; + +public class Main { + public static String find(List> list) { + if(list == null) { + System.out.println("oops"); + return ""; + } else { + return list.stream().flatMap(Collection::stream).filter(string -> string.startsWith("ABC")).findFirst().orElse(null); + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAnyMatchArrayAssignment.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAnyMatchArrayAssignment.java new file mode 100644 index 000000000000..1fe81627f086 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAnyMatchArrayAssignment.java @@ -0,0 +1,16 @@ +// "Replace with anyMatch()" "true" + +import java.util.List; + +public class Main { + public void testAssignment(List data) { + String[] found = {"no"}; + for(String str : data) { + String trimmed = str.trim(); + if(!trimmed.isEmpty()) { + found[0] = "yes"; + break; + } + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAnyMatchUnreachableReturn.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAnyMatchUnreachableReturn.java new file mode 100644 index 000000000000..2fa660e42389 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAnyMatchUnreachableReturn.java @@ -0,0 +1,19 @@ +// "Replace with anyMatch()" "true" + +import java.util.List; + +public class Main { + boolean find(List data) { + if(data != null) { + for (String e : data) { + String trimmed = e.trim(); + if (trimmed.startsWith("xyz")) { + return true; + } + } + } else { + throw new IllegalArgumentException(); + } + return false; + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeFindFirstReAssignment.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeFindFirstReAssignment.java new file mode 100644 index 000000000000..d1cd5f35eff2 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeFindFirstReAssignment.java @@ -0,0 +1,25 @@ +// "Replace with findFirst()" "true" + +import java.util.List; +import java.util.Map; + +public class Main { + private int getInitialSize() {return 0;} + + public void testMap(Map> map) throws Exception { + int firstSize = 10; + + System.out.println(firstSize); + + firstSize = getInitialSize(); + // loop + for(List list : map.values()) { + if(list != null) { + firstSize = list.size(); + // comment + break; + } + } + System.out.println(firstSize); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeFindFirstReturnUnreachable.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeFindFirstReturnUnreachable.java new file mode 100644 index 000000000000..a47317fbd733 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeFindFirstReturnUnreachable.java @@ -0,0 +1,21 @@ +// "Replace with findFirst()" "true" + +import java.util.List; + +public class Main { + public static String find(List> list) { + if(list == null) { + System.out.println("oops"); + return ""; + } else { + for (List innerList : list) { + for (String string : innerList) { + if (string.startsWith("ABC")) { + return string; + } + } + } + } + return null; + } +} \ No newline at end of file From 6a8de729a4a4a17de5d3f4b82585bae9adf1fa34 Mon Sep 17 00:00:00 2001 From: nik Date: Fri, 7 Oct 2016 09:36:11 +0300 Subject: [PATCH 04/43] project model: automatically treat source roots located under excluded roots as unexcluded (IDEA-135096) --- .../roots/impl/DirectoryIndexTest.java | 75 ++++++++++++++++--- .../indices/impl/ModuleExcludeIndexImpl.java | 6 ++ .../sourceRootUnderExcluded/exc/src/B.java | 3 + .../sourceRootUnderExcluded.iml | 14 ++++ .../sourceRootUnderExcluded.ipr | 12 +++ .../output/sourceRootUnderExcluded/src/A.java | 3 + .../jps/builders/rebuild/ModuleRebuildTest.kt | 11 +++ .../jps/indices/ModuleExcludeIndexTest.java | 19 +++++ .../actions/MarkRootActionBase.java | 24 +++--- .../openapi/roots/impl/FileIndexBase.java | 7 ++ .../roots/impl/ModuleFileIndexImpl.java | 22 +++--- .../roots/impl/ProjectFileIndexImpl.java | 31 ++++---- .../openapi/roots/impl/RootIndex.java | 33 ++++++-- .../util/containers/ContainerUtil.java | 6 ++ 14 files changed, 210 insertions(+), 56 deletions(-) create mode 100644 jps/jps-builders/testData/output/sourceRootUnderExcluded/exc/src/B.java create mode 100644 jps/jps-builders/testData/output/sourceRootUnderExcluded/sourceRootUnderExcluded.iml create mode 100644 jps/jps-builders/testData/output/sourceRootUnderExcluded/sourceRootUnderExcluded.ipr create mode 100644 jps/jps-builders/testData/output/sourceRootUnderExcluded/src/A.java diff --git a/java/java-tests/testSrc/com/intellij/openapi/roots/impl/DirectoryIndexTest.java b/java/java-tests/testSrc/com/intellij/openapi/roots/impl/DirectoryIndexTest.java index 20da5e4a3194..8970f16fb4a7 100644 --- a/java/java-tests/testSrc/com/intellij/openapi/roots/impl/DirectoryIndexTest.java +++ b/java/java-tests/testSrc/com/intellij/openapi/roots/impl/DirectoryIndexTest.java @@ -712,16 +712,20 @@ public class DirectoryIndexTest extends IdeaTestCase { assertIteratedContent(myFileIndex, Arrays.asList(fileSourceRoot, fileTestSourceRoot), Collections.singletonList(fileRoot)); } - private static void assertIteratedContent(ProjectFileIndex fileIndex, + private void assertIteratedContent(Module module, @Nullable List contains, @Nullable List doesntContain) { + assertIteratedContent(ModuleRootManager.getInstance(module).getFileIndex(), contains, doesntContain); + assertIteratedContent(myFileIndex, contains, doesntContain); + } + + private static void assertIteratedContent(FileIndex fileIndex, @Nullable List contains, @Nullable List doesntContain) { final Set collected = new THashSet<>(); - fileIndex.iterateContent(new ContentIterator() { - @Override - public boolean processFile(VirtualFile fileOrDir) { - collected.add(fileOrDir); - return true; + fileIndex.iterateContent(fileOrDir -> { + if (!collected.add(fileOrDir)) { + fail(fileOrDir + " visited twice"); } + return true; }); if (contains != null) assertContainsElements(collected, contains); if (doesntContain != null) assertDoesntContain(collected, doesntContain); @@ -856,10 +860,61 @@ public class DirectoryIndexTest extends IdeaTestCase { assertTrue(myFileIndex.isInSource(fileSourceRoot)); } - public void testContentRootUnderExcludedRoot() { - VirtualFile dirUnderExcluded = createChildDirectory(myExcludeDir, "generated"); - PsiTestUtil.addSourceContentToRoots(myModule2, dirUnderExcluded); - checkInfo(dirUnderExcluded, myModule2, false, false, "", JavaSourceRootType.SOURCE, myModule2, myModule3); + public void testSourceContentRootsUnderExcludedRoot() { + VirtualFile contentRoot = createChildDirectory(myExcludeDir, "content"); + PsiTestUtil.addContentRoot(myModule2, contentRoot); + checkInfo(contentRoot, myModule2, false, false, null, null, myModule2, myModule3); + VirtualFile excludedFile = createChildData(myExcludeDir, "excluded.txt"); + + VirtualFile sourceRoot = createChildDirectory(myExcludeDir, "src"); + VirtualFile sourceFile = createChildData(sourceRoot, "source.txt"); + PsiTestUtil.addSourceRoot(myModule2, sourceRoot); + assertEquals(myModule2Dir, assertInProject(sourceRoot).getContentRoot()); + checkInfo(sourceRoot, myModule2, false, false, "", JavaSourceRootType.SOURCE, myModule2, myModule3); + + VirtualFile contentSourceRoot = createChildDirectory(myExcludeDir, "content-src"); + VirtualFile contentSourceFile = createChildData(sourceRoot, "content-source.txt"); + PsiTestUtil.addSourceContentToRoots(myModule2, contentSourceRoot); + checkInfo(contentSourceRoot, myModule2, false, false, "", JavaSourceRootType.SOURCE, myModule2, myModule3); + + assertIteratedContent(myModule2, Arrays.asList(sourceFile, contentSourceFile, sourceRoot, contentSourceRoot), + Arrays.asList(excludedFile, myExcludeDir)); + } + + public void testSourceContentRootsUnderExcludedRootUnderSourceRoot() { + VirtualFile excluded = createChildDirectory(myModule2Dir, "excluded"); + PsiTestUtil.addExcludedRoot(myModule2, excluded); + VirtualFile excludedFile = createChildData(excluded, "excluded.txt"); + + VirtualFile contentRoot = createChildDirectory(excluded, "content"); + PsiTestUtil.addContentRoot(myModule2, contentRoot); + checkInfo(contentRoot, myModule2, false, false, null, null); + + VirtualFile sourceRoot = createChildDirectory(excluded, "src"); + PsiTestUtil.addSourceRoot(myModule2, sourceRoot); + VirtualFile sourceFile = createChildData(sourceRoot, "source.txt"); + assertEquals(myModule2Dir, assertInProject(sourceRoot).getContentRoot()); + checkInfo(sourceRoot, myModule2, false, false, "", JavaSourceRootType.SOURCE, myModule2, myModule3); + + VirtualFile contentSourceRoot = createChildDirectory(excluded, "content-src"); + VirtualFile contentSourceFile = createChildData(contentSourceRoot, "content-source.txt"); + PsiTestUtil.addSourceContentToRoots(myModule2, contentSourceRoot); + checkInfo(contentSourceRoot, myModule2, false, false, "", JavaSourceRootType.SOURCE, myModule2, myModule3); + + assertIteratedContent(myModule2, Arrays.asList(sourceFile, contentSourceFile, sourceRoot, contentSourceRoot), + Arrays.asList(excludedFile, myExcludeDir)); + } + + public void testExcludedSourceRootUnderExcluded() { + VirtualFile excluded = createChildDirectory(myModule2Dir, "excluded"); + PsiTestUtil.addExcludedRoot(myModule2, excluded); + + VirtualFile src = createChildDirectory(excluded, "src"); + VirtualFile sourceFile = createChildData(src, "src.txt"); + PsiTestUtil.addSourceRoot(myModule2, src); + PsiTestUtil.addExcludedRoot(myModule2, src); + assertExcluded(src, myModule2); + assertIteratedContent(myModule2, null, Arrays.asList(sourceFile)); } private void checkInfo(VirtualFile file, diff --git a/jps/jps-builders/src/org/jetbrains/jps/indices/impl/ModuleExcludeIndexImpl.java b/jps/jps-builders/src/org/jetbrains/jps/indices/impl/ModuleExcludeIndexImpl.java index bfb234ce59e4..ae17f7860549 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/indices/impl/ModuleExcludeIndexImpl.java +++ b/jps/jps-builders/src/org/jetbrains/jps/indices/impl/ModuleExcludeIndexImpl.java @@ -26,6 +26,7 @@ import org.jetbrains.jps.model.java.JpsJavaExtensionService; import org.jetbrains.jps.model.java.JpsJavaModuleExtension; import org.jetbrains.jps.model.java.JpsJavaProjectExtension; import org.jetbrains.jps.model.module.JpsModule; +import org.jetbrains.jps.model.module.JpsModuleSourceRoot; import org.jetbrains.jps.util.JpsPathUtil; import java.io.File; @@ -67,6 +68,11 @@ public class ModuleExcludeIndexImpl implements ModuleExcludeIndex { moduleContent.add(contentRoot); contentToModule.put(contentRoot, module); } + for (JpsModuleSourceRoot root : module.getSourceRoots()) { + File sourceRoot = root.getFile(); + moduleContent.add(sourceRoot); + contentToModule.put(sourceRoot, module); + } myModuleToExcludesMap.put(module, moduleExcludes); myModuleToContentMap.put(module, moduleContent); myExcludedRoots.addAll(moduleExcludes); diff --git a/jps/jps-builders/testData/output/sourceRootUnderExcluded/exc/src/B.java b/jps/jps-builders/testData/output/sourceRootUnderExcluded/exc/src/B.java new file mode 100644 index 000000000000..ef5f49869300 --- /dev/null +++ b/jps/jps-builders/testData/output/sourceRootUnderExcluded/exc/src/B.java @@ -0,0 +1,3 @@ +class B { + A a; +} \ No newline at end of file diff --git a/jps/jps-builders/testData/output/sourceRootUnderExcluded/sourceRootUnderExcluded.iml b/jps/jps-builders/testData/output/sourceRootUnderExcluded/sourceRootUnderExcluded.iml new file mode 100644 index 000000000000..5752f113727d --- /dev/null +++ b/jps/jps-builders/testData/output/sourceRootUnderExcluded/sourceRootUnderExcluded.iml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/jps/jps-builders/testData/output/sourceRootUnderExcluded/sourceRootUnderExcluded.ipr b/jps/jps-builders/testData/output/sourceRootUnderExcluded/sourceRootUnderExcluded.ipr new file mode 100644 index 000000000000..8d3cc788cfde --- /dev/null +++ b/jps/jps-builders/testData/output/sourceRootUnderExcluded/sourceRootUnderExcluded.ipr @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/jps/jps-builders/testData/output/sourceRootUnderExcluded/src/A.java b/jps/jps-builders/testData/output/sourceRootUnderExcluded/src/A.java new file mode 100644 index 000000000000..398760c55b90 --- /dev/null +++ b/jps/jps-builders/testData/output/sourceRootUnderExcluded/src/A.java @@ -0,0 +1,3 @@ +class A { + B b; +} \ No newline at end of file diff --git a/jps/jps-builders/testSrc/org/jetbrains/jps/builders/rebuild/ModuleRebuildTest.kt b/jps/jps-builders/testSrc/org/jetbrains/jps/builders/rebuild/ModuleRebuildTest.kt index 078a823fe18a..9b0658807a05 100644 --- a/jps/jps-builders/testSrc/org/jetbrains/jps/builders/rebuild/ModuleRebuildTest.kt +++ b/jps/jps-builders/testSrc/org/jetbrains/jps/builders/rebuild/ModuleRebuildTest.kt @@ -69,6 +69,17 @@ class ModuleRebuildTest: JpsRebuildTestCase() { }) } + fun testSourceRootUnderExcluded() { + doTest("sourceRootUnderExcluded/sourceRootUnderExcluded.ipr", fs { + dir("production") { + dir("sourceRootUnderExcluded") { + file("A.class") + file("B.class") + } + } + }) + } + fun testResourceCopying() { doTest("resourceCopying/resourceCopying.ipr", fs { dir("production") { diff --git a/jps/jps-builders/testSrc/org/jetbrains/jps/indices/ModuleExcludeIndexTest.java b/jps/jps-builders/testSrc/org/jetbrains/jps/indices/ModuleExcludeIndexTest.java index a7858910f3a5..abf828ad8195 100644 --- a/jps/jps-builders/testSrc/org/jetbrains/jps/indices/ModuleExcludeIndexTest.java +++ b/jps/jps-builders/testSrc/org/jetbrains/jps/indices/ModuleExcludeIndexTest.java @@ -19,6 +19,7 @@ import com.intellij.openapi.util.io.FileUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.jps.indices.impl.ModuleExcludeIndexImpl; import org.jetbrains.jps.model.JpsJavaModelTestCase; +import org.jetbrains.jps.model.java.JavaSourceRootType; import org.jetbrains.jps.model.java.JpsJavaModuleExtension; import org.jetbrains.jps.model.module.JpsModule; import org.jetbrains.jps.util.JpsPathUtil; @@ -138,6 +139,24 @@ public class ModuleExcludeIndexTest extends JpsJavaModelTestCase { assertFalse(index.isExcludedFromModule(innerRoot, inner)); } + public void testSourceRootUnderExcluded() { + File project = new File(myRoot, "project"); + File exc = new File(project, "exc"); + File src = new File(exc, "src"); + JpsModule module = addModule(); + addContentRoot(module, project); + addExcludedRoot(module, exc); + addSourceRoot(module, src); + assertNotExcluded(src); + + addExcludedRoot(module, src); + assertExcluded(src); + } + + private static void addSourceRoot(JpsModule module, File src) { + module.addSourceRoot(JpsPathUtil.pathToUrl(src.getAbsolutePath()), JavaSourceRootType.SOURCE); + } + private static void addExcludedRoot(JpsModule module, File root) { module.getExcludeRootsList().addUrl(JpsPathUtil.pathToUrl(root.getAbsolutePath())); } diff --git a/platform/lang-impl/src/com/intellij/ide/projectView/actions/MarkRootActionBase.java b/platform/lang-impl/src/com/intellij/ide/projectView/actions/MarkRootActionBase.java index 0c5d26fdaf15..422bc7ba2af1 100644 --- a/platform/lang-impl/src/com/intellij/ide/projectView/actions/MarkRootActionBase.java +++ b/platform/lang-impl/src/com/intellij/ide/projectView/actions/MarkRootActionBase.java @@ -105,7 +105,7 @@ public abstract class MarkRootActionBase extends DumbAwareAction { @Override public void update(AnActionEvent e) { RootsSelection selection = getSelection(e); - doUpdate(e, e.getData(LangDataKeys.MODULE), selection); + doUpdate(e, selection.myModule, selection); } protected void doUpdate(@NotNull AnActionEvent e, @Nullable Module module, @NotNull RootsSelection selection) { @@ -121,21 +121,16 @@ public abstract class MarkRootActionBase extends DumbAwareAction { Module module = getModule(e, files); if (module == null) return RootsSelection.EMPTY; - RootsSelection selection = new RootsSelection(); + RootsSelection selection = new RootsSelection(module); final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(module.getProject()).getFileIndex(); for (VirtualFile file : files) { if (!file.isDirectory()) { return RootsSelection.EMPTY; } - if (!fileIndex.isInContent(file)) { - ExcludeFolder excludeFolder = ProjectRootsUtil.findExcludeFolder(module, file); - if (excludeFolder != null) { - selection.mySelectedExcludeRoots.add(excludeFolder); - continue; - } - else { - return RootsSelection.EMPTY; - } + ExcludeFolder excludeFolder = ProjectRootsUtil.findExcludeFolder(module, file); + if (excludeFolder != null) { + selection.mySelectedExcludeRoots.add(excludeFolder); + continue; } SourceFolder folder = ProjectRootsUtil.findSourceFolder(module, file); if (folder != null) { @@ -180,7 +175,12 @@ public abstract class MarkRootActionBase extends DumbAwareAction { } public static class RootsSelection { - public static final RootsSelection EMPTY = new RootsSelection(); + public static final RootsSelection EMPTY = new RootsSelection(null); + public final Module myModule; + + public RootsSelection(Module module) { + myModule = module; + } public List mySelectedRoots = new ArrayList<>(); public List mySelectedExcludeRoots = new ArrayList<>(); diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/FileIndexBase.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/FileIndexBase.java index c0ca9d99a3bd..25be05c4013f 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/FileIndexBase.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/FileIndexBase.java @@ -2,8 +2,10 @@ package com.intellij.openapi.roots.impl; import com.intellij.injected.editor.VirtualFileWindow; import com.intellij.openapi.fileTypes.FileTypeRegistry; +import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.FileIndex; +import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; @@ -33,4 +35,9 @@ public abstract class FileIndexBase implements FileIndex { !myFileTypeRegistry.isFileIgnored(file) && isInSourceContent(file); } + + @NotNull + protected static VirtualFile[][] getModuleContentAndSourceRoots(Module module) { + return new VirtualFile[][]{ModuleRootManager.getInstance(module).getContentRoots(), ModuleRootManager.getInstance(module).getSourceRoots()}; + } } diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ModuleFileIndexImpl.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ModuleFileIndexImpl.java index 1ea6a212a9c5..829c77a7420a 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ModuleFileIndexImpl.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ModuleFileIndexImpl.java @@ -45,23 +45,23 @@ public class ModuleFileIndexImpl extends FileIndexBase implements ModuleFileInde @Override public boolean iterateContent(@NotNull ContentIterator iterator) { - final List contentRoots = ApplicationManager.getApplication().runReadAction(new Computable>() { - @Override - public List compute() { - if (myModule.isDisposed()) return Collections.emptyList(); - - List result = ContainerUtil.newArrayList(); - for (VirtualFile contentRoot : ModuleRootManager.getInstance(myModule).getContentRoots()) { - VirtualFile parent = contentRoot.getParent(); + final Set contentRoots = ApplicationManager.getApplication().runReadAction((Computable>)() -> { + if (myModule.isDisposed()) return Collections.emptySet(); + + Set result = new LinkedHashSet<>(); + VirtualFile[][] allRoots = getModuleContentAndSourceRoots(myModule); + for (VirtualFile[] roots : allRoots) { + for (VirtualFile root : roots) { + VirtualFile parent = root.getParent(); if (parent != null) { DirectoryInfo parentInfo = myDirectoryIndex.getInfoForFile(parent); if (parentInfo.isInProject() && myModule.equals(parentInfo.getModule())) continue; // inner content - skip it } - result.add(contentRoot); + result.add(root); } - - return result; } + + return result; }); for (VirtualFile contentRoot : contentRoots) { boolean finished = VfsUtilCore.iterateChildrenRecursively(contentRoot, myContentFilter, iterator); diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ProjectFileIndexImpl.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ProjectFileIndexImpl.java index 1fadbde8a58e..f477dc6c04c8 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ProjectFileIndexImpl.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ProjectFileIndexImpl.java @@ -38,6 +38,7 @@ import org.jetbrains.jps.model.java.JavaModuleSourceRootTypes; import org.jetbrains.jps.model.module.JpsModuleSourceRootType; import java.util.Collections; +import java.util.LinkedHashSet; import java.util.List; import java.util.Set; @@ -54,12 +55,7 @@ public class ProjectFileIndexImpl extends FileIndexBase implements ProjectFileIn @Override public boolean iterateContent(@NotNull ContentIterator iterator) { - Module[] modules = ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - public Module[] compute() { - return ModuleManager.getInstance(myProject).getModules(); - } - }); + Module[] modules = ApplicationManager.getApplication().runReadAction((Computable)() -> ModuleManager.getInstance(myProject).getModules()); for (final Module module : modules) { for (VirtualFile contentRoot : getRootsToIterate(module)) { boolean finished = VfsUtilCore.iterateChildrenRecursively(contentRoot, myContentFilter, iterator); @@ -70,28 +66,27 @@ public class ProjectFileIndexImpl extends FileIndexBase implements ProjectFileIn return true; } - private List getRootsToIterate(final Module module) { - return ApplicationManager.getApplication().runReadAction(new Computable>() { - @Override - public List compute() { - if (module.isDisposed()) return Collections.emptyList(); + private Set getRootsToIterate(final Module module) { + return ApplicationManager.getApplication().runReadAction((Computable>)() -> { + if (module.isDisposed()) return Collections.emptySet(); - List result = ContainerUtil.newArrayList(); - for (VirtualFile contentRoot : ModuleRootManager.getInstance(module).getContentRoots()) { - DirectoryInfo info = getInfoForFileOrDirectory(contentRoot); + Set result = new LinkedHashSet<>(); + for (VirtualFile[] roots : getModuleContentAndSourceRoots(module)) { + for (VirtualFile root : roots) { + DirectoryInfo info = getInfoForFileOrDirectory(root); if (!info.isInProject()) continue; // is excluded or ignored if (!module.equals(info.getModule())) continue; // maybe 2 modules have the same content root? - VirtualFile parent = contentRoot.getParent(); + VirtualFile parent = root.getParent(); if (parent != null) { DirectoryInfo parentInfo = getInfoForFileOrDirectory(parent); if (parentInfo.isInProject() && parentInfo.getModule() != null) continue; } - result.add(contentRoot); + result.add(root); } - - return result; } + + return result; }); } diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/RootIndex.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/RootIndex.java index 31831cc1b52e..de0ea2adcf33 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/RootIndex.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/RootIndex.java @@ -367,7 +367,7 @@ public class RootIndex { @Nullable VirtualFile librarySourceRoot = myRootInfo.findLibraryRootInfo(roots, true); result.addAll(myRootInfo.getLibraryOrderEntries(roots, libraryClassRoot, librarySourceRoot, myLibClassRootEntries, myLibSourceRootEntries)); - VirtualFile moduleContentRoot = myRootInfo.findModuleRootInfo(roots); + VirtualFile moduleContentRoot = myRootInfo.findNearestContentRoot(roots); if (moduleContentRoot != null) { ContainerUtil.addIfNotNull(result, myRootInfo.getModuleSourceEntry(roots, moduleContentRoot, myLibClassRootEntries)); } @@ -541,16 +541,39 @@ public class RootIndex { return result; } + /** + * Returns nearest content root for a file by its parent directories hierarchy. If the file is excluded (i.e. located under an excluded + * root and there are no source roots on the path to the excluded root) returns {@code null}. + */ @Nullable - private VirtualFile findModuleRootInfo(@NotNull List hierarchy) { + private VirtualFile findNearestContentRoot(@NotNull List hierarchy) { + Collection sourceRootOwners = null; + boolean underExcludedSourceRoot = false; for (VirtualFile root : hierarchy) { Module module = contentRootOf.get(root); Module excludedFrom = excludedFromModule.get(root); - if (module != null && excludedFrom != module) { + if (module != null && (excludedFrom != module || underExcludedSourceRoot && sourceRootOwners.contains(module))) { return root; } if (excludedFrom != null || excludedFromProject.contains(root)) { - return null; + if (sourceRootOwners != null) { + underExcludedSourceRoot = true; + } + else { + return null; + } + } + + if (!underExcludedSourceRoot && sourceRootOf.containsKey(root)) { + Collection modulesForSourceRoot = sourceRootOf.get(root); + if (!modulesForSourceRoot.isEmpty()) { + if (sourceRootOwners == null) { + sourceRootOwners = modulesForSourceRoot; + } + else { + sourceRootOwners = ContainerUtil.union(sourceRootOwners, modulesForSourceRoot); + } + } } } return null; @@ -662,7 +685,7 @@ public class RootIndex { private static Pair calcDirectoryInfo(@NotNull final VirtualFile root, @NotNull final List hierarchy, @NotNull RootInfo info) { - VirtualFile moduleContentRoot = info.findModuleRootInfo(hierarchy); + VirtualFile moduleContentRoot = info.findNearestContentRoot(hierarchy); VirtualFile libraryClassRoot = info.findLibraryRootInfo(hierarchy, false); VirtualFile librarySourceRoot = info.findLibraryRootInfo(hierarchy, true); boolean inProject = moduleContentRoot != null || libraryClassRoot != null || librarySourceRoot != null; diff --git a/platform/util/src/com/intellij/util/containers/ContainerUtil.java b/platform/util/src/com/intellij/util/containers/ContainerUtil.java index 8e40d738099b..1fed757d77e8 100644 --- a/platform/util/src/com/intellij/util/containers/ContainerUtil.java +++ b/platform/util/src/com/intellij/util/containers/ContainerUtil.java @@ -450,6 +450,12 @@ public class ContainerUtil extends ContainerUtilRt { @NotNull @Contract(pure=true) public static Set union(@NotNull Set set, @NotNull Set set2) { + return union((Collection)set, set2); + } + + @NotNull + @Contract(pure=true) + public static Set union(@NotNull Collection set, @NotNull Collection set2) { Set result = new THashSet(set.size() + set2.size()); result.addAll(set); result.addAll(set2); From e889a3c16dfccab3c31c35ea243b6dd5e0a984b9 Mon Sep 17 00:00:00 2001 From: nik Date: Fri, 7 Oct 2016 09:44:40 +0300 Subject: [PATCH 05/43] corrected action name for MarkAsContentRootAction --- .../platform-resources-en/src/messages/ActionsBundle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/platform-resources-en/src/messages/ActionsBundle.properties b/platform/platform-resources-en/src/messages/ActionsBundle.properties index fcd5d571cb2e..1290f2bf384c 100644 --- a/platform/platform-resources-en/src/messages/ActionsBundle.properties +++ b/platform/platform-resources-en/src/messages/ActionsBundle.properties @@ -1476,7 +1476,7 @@ group.Vcs.Browse.text=Browse VCS Repository action.Graph.print.reset=Reset action.MarkExcludeRoot.text=Excluded action.UnmarkRoot.text=Unmark Root -action.MarkAsContentRoot.text=Cancel Exclusion +action.MarkAsContentRoot.text=Not Excluded action.MarkAsContentRoot.description=Cancel exclusion for the selected directory to make its files processable by IDE's actions action.CreateLibraryFromFile.text=Add as Library... action.ImportModuleFromImlFile.text=Import Module From a244bd1005601b87eb7d0f0b3ca7ab04c7d03783 Mon Sep 17 00:00:00 2001 From: "Vladimir.Orlov" Date: Fri, 7 Oct 2016 09:57:11 +0300 Subject: [PATCH 06/43] WinLauncher: fixed path to launcher in project file. --- native/WinLauncher/WinLauncher/WinLauncher.vcxproj | 2 -- 1 file changed, 2 deletions(-) diff --git a/native/WinLauncher/WinLauncher/WinLauncher.vcxproj b/native/WinLauncher/WinLauncher/WinLauncher.vcxproj index 45f32292dcca..d4ed96a539b6 100644 --- a/native/WinLauncher/WinLauncher/WinLauncher.vcxproj +++ b/native/WinLauncher/WinLauncher/WinLauncher.vcxproj @@ -65,7 +65,6 @@ true $(JdkPath)\include;$(JdkPath)\include\win32;$(IncludePath) - ..\..\..\bin\WinLauncher true @@ -80,7 +79,6 @@ false $(JdkPath)\include;$(JdkPath)\include\win32;$(IncludePath) $(ProjectName)64 - ..\..\..\bin\WinLauncher From 2352e0556049d51d495bd432500fe2997754824e Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Fri, 7 Oct 2016 10:51:22 +0300 Subject: [PATCH 07/43] resource bundle editor: do not update tree nodes if duplicated-key property is appeared IDEA-162113 --- .../editor/ResourceBundleFileStructureViewElement.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/ResourceBundleFileStructureViewElement.java b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/ResourceBundleFileStructureViewElement.java index d2af3f6dcc2e..28c1b63f7b60 100644 --- a/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/ResourceBundleFileStructureViewElement.java +++ b/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/ResourceBundleFileStructureViewElement.java @@ -69,12 +69,13 @@ public class ResourceBundleFileStructureViewElement implements StructureViewTree final HashSet remains = new HashSet<>(myElements.keySet()); for (Map.Entry> entry : propertyNames.entrySet()) { final String propKey = entry.getKey(); - final IProperty representative = entry.getValue().iterator().next(); + Collection properties = entry.getValue(); final ResourceBundlePropertyStructureViewElement oldPropertyNode = myElements.get(propKey); - if (oldPropertyNode != null && oldPropertyNode.getProperty() == representative) { + if (oldPropertyNode != null && properties.contains(oldPropertyNode.getProperty())) { remains.remove(propKey); continue; } + final IProperty representative = properties.iterator().next(); final ResourceBundlePropertyStructureViewElement node = new ResourceBundlePropertyStructureViewElement(representative); myElements.put(propKey, node); } From 7b20b0296702c706540c629b65cfd34195ad0c12 Mon Sep 17 00:00:00 2001 From: nik Date: Fri, 7 Oct 2016 10:07:07 +0300 Subject: [PATCH 08/43] framework detection: @NotNull and assertions added to find problematic plugin which incorrectly returns null from FacetBasedFrameworkDetector#getFacetType (EA-78816) --- .../detection/FacetBasedFrameworkDetector.java | 11 +++++++++-- .../appengine/facet/AppEngineFrameworkDetector.java | 1 + .../com/jetbrains/python/facet/PythonFacetType.java | 1 + .../python/buildout/BuildoutFrameworkDetector.java | 1 + 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/platform/lang-api/src/com/intellij/framework/detection/FacetBasedFrameworkDetector.java b/platform/lang-api/src/com/intellij/framework/detection/FacetBasedFrameworkDetector.java index 0a2db890febe..3b0768415a0a 100644 --- a/platform/lang-api/src/com/intellij/framework/detection/FacetBasedFrameworkDetector.java +++ b/platform/lang-api/src/com/intellij/framework/detection/FacetBasedFrameworkDetector.java @@ -17,6 +17,7 @@ package com.intellij.framework.detection; import com.intellij.facet.*; import com.intellij.framework.FrameworkType; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.roots.ModifiableRootModel; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VirtualFile; @@ -36,6 +37,8 @@ import java.util.Set; * @author nik */ public abstract class FacetBasedFrameworkDetector extends FrameworkDetector { + private static final Logger LOG = Logger.getInstance(FacetBasedFrameworkDetector.class); + protected FacetBasedFrameworkDetector(String detectorId) { super(detectorId); } @@ -44,6 +47,7 @@ public abstract class FacetBasedFrameworkDetector getFacetType(); /** @@ -83,7 +87,10 @@ public abstract class FacetBasedFrameworkDetector type = getFacetType(); + //noinspection ConstantConditions todo[nik] remove later: this is added to find implementations which incorrectly return 'null' from 'getFacetType' + LOG.assertTrue(type != null, "'getFacetType' returns 'null' in " + getClass()); + return createFrameworkType(type); } static FrameworkType createFrameworkType(final FacetType facetType) { @@ -104,7 +111,7 @@ public abstract class FacetBasedFrameworkDetector myFacetType; private final Icon myIcon; - public FacetBasedFrameworkType(FacetType facetType) { + public FacetBasedFrameworkType(@NotNull FacetType facetType) { super(facetType.getStringId()); myFacetType = facetType; final Icon icon = myFacetType.getIcon(); diff --git a/plugins/google-app-engine/source/com/intellij/appengine/facet/AppEngineFrameworkDetector.java b/plugins/google-app-engine/source/com/intellij/appengine/facet/AppEngineFrameworkDetector.java index bb03d9dfe79a..ad0c72a70001 100644 --- a/plugins/google-app-engine/source/com/intellij/appengine/facet/AppEngineFrameworkDetector.java +++ b/plugins/google-app-engine/source/com/intellij/appengine/facet/AppEngineFrameworkDetector.java @@ -39,6 +39,7 @@ public class AppEngineFrameworkDetector extends FacetBasedFrameworkDetector getFacetType() { return FacetType.findInstance(AppEngineFacetType.class); diff --git a/python/pluginJava/com/jetbrains/python/facet/PythonFacetType.java b/python/pluginJava/com/jetbrains/python/facet/PythonFacetType.java index ac6f73c9ec88..c38942079016 100644 --- a/python/pluginJava/com/jetbrains/python/facet/PythonFacetType.java +++ b/python/pluginJava/com/jetbrains/python/facet/PythonFacetType.java @@ -80,6 +80,7 @@ public class PythonFacetType extends FacetType getFacetType() { return PythonFacetType.getInstance(); diff --git a/python/src/com/jetbrains/python/buildout/BuildoutFrameworkDetector.java b/python/src/com/jetbrains/python/buildout/BuildoutFrameworkDetector.java index c2f869ae4853..bd2857d9e23f 100644 --- a/python/src/com/jetbrains/python/buildout/BuildoutFrameworkDetector.java +++ b/python/src/com/jetbrains/python/buildout/BuildoutFrameworkDetector.java @@ -42,6 +42,7 @@ public class BuildoutFrameworkDetector extends FacetBasedFrameworkDetector getFacetType() { return BuildoutFacetType.getInstance(); From ebc0bd42ec1553138d16e058aa4098e2cc3d4be2 Mon Sep 17 00:00:00 2001 From: nik Date: Fri, 7 Oct 2016 10:49:58 +0300 Subject: [PATCH 09/43] root index: ignore roots from unsupported file systems (EA-75058) --- .../intellij/openapi/roots/impl/DirectoryIndexTest.java | 7 +++++++ .../intellij/openapi/roots/impl/ModuleFileIndexImpl.java | 4 ++++ .../src/com/intellij/openapi/roots/impl/RootIndex.java | 5 +++++ 3 files changed, 16 insertions(+) diff --git a/java/java-tests/testSrc/com/intellij/openapi/roots/impl/DirectoryIndexTest.java b/java/java-tests/testSrc/com/intellij/openapi/roots/impl/DirectoryIndexTest.java index 8970f16fb4a7..a631a0eda761 100644 --- a/java/java-tests/testSrc/com/intellij/openapi/roots/impl/DirectoryIndexTest.java +++ b/java/java-tests/testSrc/com/intellij/openapi/roots/impl/DirectoryIndexTest.java @@ -29,6 +29,7 @@ import com.intellij.openapi.roots.ex.ProjectRootManagerEx; import com.intellij.openapi.util.EmptyRunnable; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.*; +import com.intellij.openapi.vfs.ex.http.HttpFileSystem; import com.intellij.testFramework.*; import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; @@ -917,6 +918,12 @@ public class DirectoryIndexTest extends IdeaTestCase { assertIteratedContent(myModule2, null, Arrays.asList(sourceFile)); } + public void testSourceRootFromUnsupportedFileSystem() { + VirtualFile httpFile = HttpFileSystem.getInstance().findFileByPath("example.com"); + PsiTestUtil.addSourceRoot(myModule, httpFile); + assertNotInProject(httpFile); + } + private void checkInfo(VirtualFile file, @Nullable Module module, boolean isInLibraryClasses, diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ModuleFileIndexImpl.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ModuleFileIndexImpl.java index 829c77a7420a..9871077f4f8f 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ModuleFileIndexImpl.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ModuleFileIndexImpl.java @@ -24,6 +24,7 @@ import com.intellij.openapi.util.Computable; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileFilter; +import com.intellij.openapi.vfs.VirtualFileWithId; import com.intellij.util.IncorrectOperationException; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; @@ -52,6 +53,9 @@ public class ModuleFileIndexImpl extends FileIndexBase implements ModuleFileInde VirtualFile[][] allRoots = getModuleContentAndSourceRoots(myModule); for (VirtualFile[] roots : allRoots) { for (VirtualFile root : roots) { + DirectoryInfo info = getInfoForFileOrDirectory(root); + if (!info.isInProject()) continue; + VirtualFile parent = root.getParent(); if (parent != null) { DirectoryInfo parentInfo = myDirectoryIndex.getInfoForFile(parent); diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/RootIndex.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/RootIndex.java index de0ea2adcf33..e0587269baa5 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/RootIndex.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/RootIndex.java @@ -28,6 +28,7 @@ import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.VirtualFileWithId; import com.intellij.openapi.vfs.newvfs.events.VFileEvent; import com.intellij.util.CollectionQuery; import com.intellij.util.Query; @@ -179,6 +180,10 @@ public class RootIndex { } private static boolean ensureValid(@NotNull VirtualFile file, @NotNull Object container) { + if (!(file instanceof VirtualFileWithId)) { + //skip roots from unsupported file systems (e.g. http) + return false; + } if (!file.isValid()) { LOG.error("Invalid root " + file + " in " + container); return false; From a4ed2eab9b214aa03d6c3b8c10c6e42ff1a98ac0 Mon Sep 17 00:00:00 2001 From: nik Date: Fri, 7 Oct 2016 10:55:17 +0300 Subject: [PATCH 10/43] creating project from sources: don't fail if corrupted zip archive found (EA-58250) --- .../src/com/intellij/ide/util/importProject/ModuleInsight.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/java/idea-ui/src/com/intellij/ide/util/importProject/ModuleInsight.java b/java/idea-ui/src/com/intellij/ide/util/importProject/ModuleInsight.java index a52164de461d..992491b52790 100644 --- a/java/idea-ui/src/com/intellij/ide/util/importProject/ModuleInsight.java +++ b/java/idea-ui/src/com/intellij/ide/util/importProject/ModuleInsight.java @@ -459,6 +459,9 @@ public abstract class ModuleInsight { catch (IOException e) { LOG.info(e); } + catch (IllegalArgumentException e) { // may be thrown from java.util.zip.ZipCoder.toString for corrupted archive + LOG.info(e); + } catch (InternalError e) { // indicates that file is somehow damaged and cannot be processed LOG.info(e); } From ddb1c0e0f494e7b714a870f84346aa6b3d7d80e3 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Fri, 7 Oct 2016 15:44:06 +0700 Subject: [PATCH 11/43] SimplifyStreamApiCallChainsInspection: support primitive anyMatch/noneMatch/allMatch conversions --- ...SimplifyStreamApiCallChainsInspection.java | 32 +++++++++++++------ .../afterAllMatchNegatedLambda.java | 2 +- .../afterAllMatchNegatedLambdaTwice.java | 2 +- .../afterAllMatchNegatedPrimitive.java | 10 ++++++ .../afterAnyMatchNegated.java | 2 +- .../afterAnyMatchNegatedLambdaTwice.java | 2 +- .../afterAnyMatchNegatedPrimitive.java | 10 ++++++ .../afterNoneMatchNegated.java | 2 +- .../afterNoneMatchNegatedLambda.java | 2 +- .../afterNoneMatchNegatedPrimitive.java | 10 ++++++ .../beforeAllMatchNegated.java | 2 +- .../beforeAllMatchNegatedLambda.java | 2 +- .../beforeAllMatchNegatedLambdaTwice.java | 2 +- .../beforeAllMatchNegatedPrimitive.java | 10 ++++++ .../beforeAnyMatchNegated.java | 2 +- .../beforeAnyMatchNegatedLambda.java | 2 +- .../beforeAnyMatchNegatedLambdaTwice.java | 2 +- .../beforeAnyMatchNegatedPrimitive.java | 10 ++++++ .../beforeNoneMatchNegated.java | 2 +- .../beforeNoneMatchNegatedLambda.java | 2 +- .../beforeNoneMatchNegatedPrimitive.java | 10 ++++++ 21 files changed, 96 insertions(+), 24 deletions(-) create mode 100644 java/java-tests/testData/inspection/streamApiCallChains/afterAllMatchNegatedPrimitive.java create mode 100644 java/java-tests/testData/inspection/streamApiCallChains/afterAnyMatchNegatedPrimitive.java create mode 100644 java/java-tests/testData/inspection/streamApiCallChains/afterNoneMatchNegatedPrimitive.java create mode 100644 java/java-tests/testData/inspection/streamApiCallChains/beforeAllMatchNegatedPrimitive.java create mode 100644 java/java-tests/testData/inspection/streamApiCallChains/beforeAnyMatchNegatedPrimitive.java create mode 100644 java/java-tests/testData/inspection/streamApiCallChains/beforeNoneMatchNegatedPrimitive.java diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/SimplifyStreamApiCallChainsInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/SimplifyStreamApiCallChainsInspection.java index dcb6b192a3af..e29355b85b9c 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/SimplifyStreamApiCallChainsInspection.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/SimplifyStreamApiCallChainsInspection.java @@ -80,7 +80,10 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns return new JavaElementVisitor() { @Override public void visitMethodCallExpression(PsiMethodCallExpression methodCall) { - final PsiMethod method = methodCall.resolveMethod(); + PsiMethod method = methodCall.resolveMethod(); + if(method == null) return; + PsiClass psiClass = method.getContainingClass(); + if(psiClass == null) return; if (isCallOf(method, CommonClassNames.JAVA_UTIL_COLLECTION, STREAM_METHOD, 0)) { handleCollectionStream(methodCall); } @@ -90,27 +93,28 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns else if (isCallOf(method, CommonClassNames.JAVA_UTIL_OPTIONAL, IS_PRESENT_METHOD, 0)) { handleOptionalIsPresent(methodCall); } - else if (isCallOf(method, CommonClassNames.JAVA_UTIL_STREAM_STREAM, ANY_MATCH_METHOD, 1)) { + else if (isStreamCall(method, ANY_MATCH_METHOD)) { if(isParentNegated(methodCall)) { boolean argNegated = isArgumentLambdaNegated(methodCall); registerMatchFix(methodCall, - new SimplifyMatchNegationFix(argNegated ? "!Stream.anyMatch(x -> !(...))" : "!Stream.anyMatch(...)", + new SimplifyMatchNegationFix( + "!" + psiClass.getName() + (argNegated ? ".anyMatch(x -> !(...))" : ".anyMatch(...)"), argNegated ? ALL_MATCH_METHOD : NONE_MATCH_METHOD)); } } - else if (isCallOf(method, CommonClassNames.JAVA_UTIL_STREAM_STREAM, NONE_MATCH_METHOD, 1)) { + else if (isStreamCall(method, NONE_MATCH_METHOD)) { if(isParentNegated(methodCall)) { - registerMatchFix(methodCall, new SimplifyMatchNegationFix("!Stream.noneMatch(...)", ANY_MATCH_METHOD)); + registerMatchFix(methodCall, new SimplifyMatchNegationFix("!"+psiClass.getName()+".noneMatch(...)", ANY_MATCH_METHOD)); } if(isArgumentLambdaNegated(methodCall)) { - registerMatchFix(methodCall, new SimplifyMatchNegationFix("Stream.noneMatch(x -> !(...))", ALL_MATCH_METHOD)); + registerMatchFix(methodCall, new SimplifyMatchNegationFix(psiClass.getName()+".noneMatch(x -> !(...))", ALL_MATCH_METHOD)); } } - else if (isCallOf(method, CommonClassNames.JAVA_UTIL_STREAM_STREAM, ALL_MATCH_METHOD, 1)) { + else if (isStreamCall(method, ALL_MATCH_METHOD)) { if(isArgumentLambdaNegated(methodCall)) { boolean parentNegated = isParentNegated(methodCall); registerMatchFix(methodCall, - new SimplifyMatchNegationFix(parentNegated ? "!Stream.allMatch(x -> !(...))" : "Stream.allMatch(x -> !(...))", + new SimplifyMatchNegationFix((parentNegated ? "!" : "") + psiClass.getName() + ".allMatch(x -> !(...))", parentNegated ? ANY_MATCH_METHOD : NONE_MATCH_METHOD)); } } @@ -311,6 +315,14 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns return false; } + static boolean isStreamCall(@Nullable PsiMethod method, @NotNull String methodName) { + if (method == null || !methodName.equals(method.getName()) || method.getParameterList().getParametersCount() != 1) { + return false; + } + final PsiClass containingClass = method.getContainingClass(); + return containingClass != null && InheritanceUtil.isInheritor(containingClass, CommonClassNames.JAVA_UTIL_STREAM_BASE_STREAM); + } + interface CallChainFix { String getName(); void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor); @@ -627,11 +639,11 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns @Override public String getName() { - return "Replace "+myFrom+" with Stream."+myTo+"(...)"; + return "Replace "+myFrom+" with "+myTo+"(...)"; } public String getMessage() { - return myFrom+" can be replaced with Stream."+myTo+"(...)"; + return myFrom+" can be replaced with "+myTo+"(...)"; } @Override diff --git a/java/java-tests/testData/inspection/streamApiCallChains/afterAllMatchNegatedLambda.java b/java/java-tests/testData/inspection/streamApiCallChains/afterAllMatchNegatedLambda.java index 62a5fb1f6d4f..3f34513e4cab 100644 --- a/java/java-tests/testData/inspection/streamApiCallChains/afterAllMatchNegatedLambda.java +++ b/java/java-tests/testData/inspection/streamApiCallChains/afterAllMatchNegatedLambda.java @@ -1,4 +1,4 @@ -// "Replace Stream.allMatch(x -> !(...)) with Stream.noneMatch(...)" "true" +// "Replace Stream.allMatch(x -> !(...)) with noneMatch(...)" "true" import java.util.*; diff --git a/java/java-tests/testData/inspection/streamApiCallChains/afterAllMatchNegatedLambdaTwice.java b/java/java-tests/testData/inspection/streamApiCallChains/afterAllMatchNegatedLambdaTwice.java index 1b682b22e4a0..960555bfce88 100644 --- a/java/java-tests/testData/inspection/streamApiCallChains/afterAllMatchNegatedLambdaTwice.java +++ b/java/java-tests/testData/inspection/streamApiCallChains/afterAllMatchNegatedLambdaTwice.java @@ -1,4 +1,4 @@ -// "Replace !Stream.allMatch(x -> !(...)) with Stream.anyMatch(...)" "true" +// "Replace !Stream.allMatch(x -> !(...)) with anyMatch(...)" "true" import java.util.*; diff --git a/java/java-tests/testData/inspection/streamApiCallChains/afterAllMatchNegatedPrimitive.java b/java/java-tests/testData/inspection/streamApiCallChains/afterAllMatchNegatedPrimitive.java new file mode 100644 index 000000000000..9e50a197bf65 --- /dev/null +++ b/java/java-tests/testData/inspection/streamApiCallChains/afterAllMatchNegatedPrimitive.java @@ -0,0 +1,10 @@ +// "Replace DoubleStream.allMatch(x -> !(...)) with noneMatch(...)" "true" + +import java.util.stream.*; + +class Test { + public boolean testAllMatch(double[] data) { + if(DoubleStream.of(data).noneMatch(d -> Double.isNaN(d))) + return true; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/streamApiCallChains/afterAnyMatchNegated.java b/java/java-tests/testData/inspection/streamApiCallChains/afterAnyMatchNegated.java index 5d8217290911..fc0d0a971116 100644 --- a/java/java-tests/testData/inspection/streamApiCallChains/afterAnyMatchNegated.java +++ b/java/java-tests/testData/inspection/streamApiCallChains/afterAnyMatchNegated.java @@ -1,4 +1,4 @@ -// "Replace !Stream.anyMatch(...) with Stream.noneMatch(...)" "true" +// "Replace !Stream.anyMatch(...) with noneMatch(...)" "true" import java.util.*; diff --git a/java/java-tests/testData/inspection/streamApiCallChains/afterAnyMatchNegatedLambdaTwice.java b/java/java-tests/testData/inspection/streamApiCallChains/afterAnyMatchNegatedLambdaTwice.java index d51ced9ee88f..fe678b9ee0ff 100644 --- a/java/java-tests/testData/inspection/streamApiCallChains/afterAnyMatchNegatedLambdaTwice.java +++ b/java/java-tests/testData/inspection/streamApiCallChains/afterAnyMatchNegatedLambdaTwice.java @@ -1,4 +1,4 @@ -// "Replace !Stream.anyMatch(x -> !(...)) with Stream.allMatch(...)" "true" +// "Replace !Stream.anyMatch(x -> !(...)) with allMatch(...)" "true" import java.util.*; diff --git a/java/java-tests/testData/inspection/streamApiCallChains/afterAnyMatchNegatedPrimitive.java b/java/java-tests/testData/inspection/streamApiCallChains/afterAnyMatchNegatedPrimitive.java new file mode 100644 index 000000000000..c488190c82b3 --- /dev/null +++ b/java/java-tests/testData/inspection/streamApiCallChains/afterAnyMatchNegatedPrimitive.java @@ -0,0 +1,10 @@ +// "Replace !LongStream.anyMatch(...) with noneMatch(...)" "true" + +import java.util.stream.*; + +class Test { + public boolean testAnyMatch(long[] data) { + if(LongStream.of(data).noneMatch(i -> i > 0)) + return true; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/streamApiCallChains/afterNoneMatchNegated.java b/java/java-tests/testData/inspection/streamApiCallChains/afterNoneMatchNegated.java index 51b4c0b74601..f2c92cf99890 100644 --- a/java/java-tests/testData/inspection/streamApiCallChains/afterNoneMatchNegated.java +++ b/java/java-tests/testData/inspection/streamApiCallChains/afterNoneMatchNegated.java @@ -1,4 +1,4 @@ -// "Replace !Stream.noneMatch(...) with Stream.anyMatch(...)" "true" +// "Replace !Stream.noneMatch(...) with anyMatch(...)" "true" import java.util.*; diff --git a/java/java-tests/testData/inspection/streamApiCallChains/afterNoneMatchNegatedLambda.java b/java/java-tests/testData/inspection/streamApiCallChains/afterNoneMatchNegatedLambda.java index e11a37fed210..b0e0724163e4 100644 --- a/java/java-tests/testData/inspection/streamApiCallChains/afterNoneMatchNegatedLambda.java +++ b/java/java-tests/testData/inspection/streamApiCallChains/afterNoneMatchNegatedLambda.java @@ -1,4 +1,4 @@ -// "Replace Stream.noneMatch(x -> !(...)) with Stream.allMatch(...)" "true" +// "Replace Stream.noneMatch(x -> !(...)) with allMatch(...)" "true" import java.util.*; diff --git a/java/java-tests/testData/inspection/streamApiCallChains/afterNoneMatchNegatedPrimitive.java b/java/java-tests/testData/inspection/streamApiCallChains/afterNoneMatchNegatedPrimitive.java new file mode 100644 index 000000000000..b1b22f847632 --- /dev/null +++ b/java/java-tests/testData/inspection/streamApiCallChains/afterNoneMatchNegatedPrimitive.java @@ -0,0 +1,10 @@ +// "Replace !IntStream.noneMatch(...) with anyMatch(...)" "true" + +import java.util.stream.*; + +class Test { + public boolean testNoneMatch(int[] data) { + if(IntStream.of(data).anyMatch(i -> i > 0)) + return true; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/streamApiCallChains/beforeAllMatchNegated.java b/java/java-tests/testData/inspection/streamApiCallChains/beforeAllMatchNegated.java index df19ee9bf6fa..a347338761d7 100644 --- a/java/java-tests/testData/inspection/streamApiCallChains/beforeAllMatchNegated.java +++ b/java/java-tests/testData/inspection/streamApiCallChains/beforeAllMatchNegated.java @@ -1,4 +1,4 @@ -// "Replace Stream.allMatch(x -> !(...)) with Stream.noneMatch(...)" "false" +// "Replace Stream.allMatch(x -> !(...)) with noneMatch(...)" "false" import java.util.*; diff --git a/java/java-tests/testData/inspection/streamApiCallChains/beforeAllMatchNegatedLambda.java b/java/java-tests/testData/inspection/streamApiCallChains/beforeAllMatchNegatedLambda.java index d6c2fe534a35..0b037b427bc5 100644 --- a/java/java-tests/testData/inspection/streamApiCallChains/beforeAllMatchNegatedLambda.java +++ b/java/java-tests/testData/inspection/streamApiCallChains/beforeAllMatchNegatedLambda.java @@ -1,4 +1,4 @@ -// "Replace Stream.allMatch(x -> !(...)) with Stream.noneMatch(...)" "true" +// "Replace Stream.allMatch(x -> !(...)) with noneMatch(...)" "true" import java.util.*; diff --git a/java/java-tests/testData/inspection/streamApiCallChains/beforeAllMatchNegatedLambdaTwice.java b/java/java-tests/testData/inspection/streamApiCallChains/beforeAllMatchNegatedLambdaTwice.java index 7a6514bd79c7..82c4327b9723 100644 --- a/java/java-tests/testData/inspection/streamApiCallChains/beforeAllMatchNegatedLambdaTwice.java +++ b/java/java-tests/testData/inspection/streamApiCallChains/beforeAllMatchNegatedLambdaTwice.java @@ -1,4 +1,4 @@ -// "Replace !Stream.allMatch(x -> !(...)) with Stream.anyMatch(...)" "true" +// "Replace !Stream.allMatch(x -> !(...)) with anyMatch(...)" "true" import java.util.*; diff --git a/java/java-tests/testData/inspection/streamApiCallChains/beforeAllMatchNegatedPrimitive.java b/java/java-tests/testData/inspection/streamApiCallChains/beforeAllMatchNegatedPrimitive.java new file mode 100644 index 000000000000..df7b88d3ec00 --- /dev/null +++ b/java/java-tests/testData/inspection/streamApiCallChains/beforeAllMatchNegatedPrimitive.java @@ -0,0 +1,10 @@ +// "Replace DoubleStream.allMatch(x -> !(...)) with noneMatch(...)" "true" + +import java.util.stream.*; + +class Test { + public boolean testAllMatch(double[] data) { + if(DoubleStream.of(data).allMatch(d -> !Double.isNaN(d))) + return true; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/streamApiCallChains/beforeAnyMatchNegated.java b/java/java-tests/testData/inspection/streamApiCallChains/beforeAnyMatchNegated.java index 204e69b28f30..25862a73e816 100644 --- a/java/java-tests/testData/inspection/streamApiCallChains/beforeAnyMatchNegated.java +++ b/java/java-tests/testData/inspection/streamApiCallChains/beforeAnyMatchNegated.java @@ -1,4 +1,4 @@ -// "Replace !Stream.anyMatch(...) with Stream.noneMatch(...)" "true" +// "Replace !Stream.anyMatch(...) with noneMatch(...)" "true" import java.util.*; diff --git a/java/java-tests/testData/inspection/streamApiCallChains/beforeAnyMatchNegatedLambda.java b/java/java-tests/testData/inspection/streamApiCallChains/beforeAnyMatchNegatedLambda.java index 32ce1a0d7618..578e2993cdf4 100644 --- a/java/java-tests/testData/inspection/streamApiCallChains/beforeAnyMatchNegatedLambda.java +++ b/java/java-tests/testData/inspection/streamApiCallChains/beforeAnyMatchNegatedLambda.java @@ -1,4 +1,4 @@ -// "Replace !Stream.anyMatch(x -> !(...)) with Stream.allMatch(...)" "false" +// "Replace !Stream.anyMatch(x -> !(...)) with allMatch(...)" "false" import java.util.*; diff --git a/java/java-tests/testData/inspection/streamApiCallChains/beforeAnyMatchNegatedLambdaTwice.java b/java/java-tests/testData/inspection/streamApiCallChains/beforeAnyMatchNegatedLambdaTwice.java index b2dfcd99b7ec..19c55ab6cf3a 100644 --- a/java/java-tests/testData/inspection/streamApiCallChains/beforeAnyMatchNegatedLambdaTwice.java +++ b/java/java-tests/testData/inspection/streamApiCallChains/beforeAnyMatchNegatedLambdaTwice.java @@ -1,4 +1,4 @@ -// "Replace !Stream.anyMatch(x -> !(...)) with Stream.allMatch(...)" "true" +// "Replace !Stream.anyMatch(x -> !(...)) with allMatch(...)" "true" import java.util.*; diff --git a/java/java-tests/testData/inspection/streamApiCallChains/beforeAnyMatchNegatedPrimitive.java b/java/java-tests/testData/inspection/streamApiCallChains/beforeAnyMatchNegatedPrimitive.java new file mode 100644 index 000000000000..69a1848bd26d --- /dev/null +++ b/java/java-tests/testData/inspection/streamApiCallChains/beforeAnyMatchNegatedPrimitive.java @@ -0,0 +1,10 @@ +// "Replace !LongStream.anyMatch(...) with noneMatch(...)" "true" + +import java.util.stream.*; + +class Test { + public boolean testAnyMatch(long[] data) { + if(!LongStream.of(data).anyMatch(i -> i > 0)) + return true; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/streamApiCallChains/beforeNoneMatchNegated.java b/java/java-tests/testData/inspection/streamApiCallChains/beforeNoneMatchNegated.java index 2b01f91af7ac..f4133a02a7eb 100644 --- a/java/java-tests/testData/inspection/streamApiCallChains/beforeNoneMatchNegated.java +++ b/java/java-tests/testData/inspection/streamApiCallChains/beforeNoneMatchNegated.java @@ -1,4 +1,4 @@ -// "Replace !Stream.noneMatch(...) with Stream.anyMatch(...)" "true" +// "Replace !Stream.noneMatch(...) with anyMatch(...)" "true" import java.util.*; diff --git a/java/java-tests/testData/inspection/streamApiCallChains/beforeNoneMatchNegatedLambda.java b/java/java-tests/testData/inspection/streamApiCallChains/beforeNoneMatchNegatedLambda.java index a37b825badfe..b6de9b267794 100644 --- a/java/java-tests/testData/inspection/streamApiCallChains/beforeNoneMatchNegatedLambda.java +++ b/java/java-tests/testData/inspection/streamApiCallChains/beforeNoneMatchNegatedLambda.java @@ -1,4 +1,4 @@ -// "Replace Stream.noneMatch(x -> !(...)) with Stream.allMatch(...)" "true" +// "Replace Stream.noneMatch(x -> !(...)) with allMatch(...)" "true" import java.util.*; diff --git a/java/java-tests/testData/inspection/streamApiCallChains/beforeNoneMatchNegatedPrimitive.java b/java/java-tests/testData/inspection/streamApiCallChains/beforeNoneMatchNegatedPrimitive.java new file mode 100644 index 000000000000..6791ed98ec2a --- /dev/null +++ b/java/java-tests/testData/inspection/streamApiCallChains/beforeNoneMatchNegatedPrimitive.java @@ -0,0 +1,10 @@ +// "Replace !IntStream.noneMatch(...) with anyMatch(...)" "true" + +import java.util.stream.*; + +class Test { + public boolean testNoneMatch(int[] data) { + if(!IntStream.of(data).noneMatch(i -> i > 0)) + return true; + } +} \ No newline at end of file From bd632e61f4042b89f6a487556cc5ad968894514c Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Fri, 7 Oct 2016 16:18:12 +0700 Subject: [PATCH 12/43] PsiReference.isReferenceTo() used (IDEA-CR-14288) --- .../codeInspection/OptionalIsPresentInspection.java | 2 +- .../java18api/Java8CollectionRemoveIfInspection.java | 2 +- .../java18api/Java8ReplaceMapGetInspection.java | 2 +- .../streamMigration/ReplaceWithFindFirstFix.java | 2 +- .../streamMigration/StreamApiMigrationInspection.java | 10 +++++----- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/OptionalIsPresentInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/OptionalIsPresentInspection.java index 73d43330d3b8..93f360530609 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/OptionalIsPresentInspection.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/OptionalIsPresentInspection.java @@ -134,7 +134,7 @@ public class OptionalIsPresentInspection extends BaseJavaBatchLocalInspectionToo if(!"get".equals(call.getMethodExpression().getReferenceName())) return false; PsiExpression qualifier = call.getMethodExpression().getQualifierExpression(); if(!(qualifier instanceof PsiReferenceExpression)) return false; - return ((PsiReferenceExpression)qualifier).resolve() == variable; + return ((PsiReferenceExpression)qualifier).isReferenceTo(variable); } @Contract("null, _ -> false") diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/java18api/Java8CollectionRemoveIfInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/java18api/Java8CollectionRemoveIfInspection.java index 682ed7749a31..2060327bd3dd 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/java18api/Java8CollectionRemoveIfInspection.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/java18api/Java8CollectionRemoveIfInspection.java @@ -239,7 +239,7 @@ public class Java8CollectionRemoveIfInspection extends BaseJavaBatchLocalInspect if(!method.equals(expression.getReferenceName())) return false; PsiExpression qualifier = expression.getQualifierExpression(); if(!(qualifier instanceof PsiReferenceExpression)) return false; - return ((PsiReferenceExpression)qualifier).resolve() == myIterator; + return ((PsiReferenceExpression)qualifier).isReferenceTo(myIterator); } public PsiVariable getNextElementVariable(PsiStatement statement) { diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/java18api/Java8ReplaceMapGetInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/java18api/Java8ReplaceMapGetInspection.java index 030ce25d8654..ef8f4986aa16 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/java18api/Java8ReplaceMapGetInspection.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/java18api/Java8ReplaceMapGetInspection.java @@ -159,7 +159,7 @@ public class Java8ReplaceMapGetInspection extends BaseJavaBatchLocalInspectionTo PsiElement[] elements = declaration.getDeclaredElements(); if(elements.length > 0) { PsiElement lastDeclaration = elements[elements.length - 1]; - if(lastDeclaration instanceof PsiLocalVariable && lastDeclaration == target.resolve()) { + if(lastDeclaration instanceof PsiLocalVariable && target.isReferenceTo(lastDeclaration)) { PsiLocalVariable var = (PsiLocalVariable)lastDeclaration; PsiExpression initializer = PsiUtil.skipParenthesizedExprDown(var.getInitializer()); if (initializer instanceof PsiMethodCallExpression && diff --git a/java/java-impl/src/com/intellij/codeInspection/streamMigration/ReplaceWithFindFirstFix.java b/java/java-impl/src/com/intellij/codeInspection/streamMigration/ReplaceWithFindFirstFix.java index 2a60250cc194..cca1d5ce74ee 100644 --- a/java/java-impl/src/com/intellij/codeInspection/streamMigration/ReplaceWithFindFirstFix.java +++ b/java/java-impl/src/com/intellij/codeInspection/streamMigration/ReplaceWithFindFirstFix.java @@ -92,7 +92,7 @@ class ReplaceWithFindFirstFix extends MigrateToStreamFix { if(previousAssignment != null) { PsiExpression prevRValue = previousAssignment.getRExpression(); PsiExpression prevLValue = previousAssignment.getLExpression(); - if(prevRValue != null && prevLValue instanceof PsiReferenceExpression && ((PsiReferenceExpression)prevLValue).resolve() == var) { + if(prevRValue != null && prevLValue instanceof PsiReferenceExpression && ((PsiReferenceExpression)prevLValue).isReferenceTo(var)) { previousAssignment.delete(); return loopStatement.replace(elementFactory.createStatementFromText( var.getName() + " = " + generateOptionalUnwrap(stream, tb, value, prevRValue, var.getType()) + ";", loopStatement)); diff --git a/java/java-impl/src/com/intellij/codeInspection/streamMigration/StreamApiMigrationInspection.java b/java/java-impl/src/com/intellij/codeInspection/streamMigration/StreamApiMigrationInspection.java index d84181e06202..13281bd34db4 100644 --- a/java/java-impl/src/com/intellij/codeInspection/streamMigration/StreamApiMigrationInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/streamMigration/StreamApiMigrationInspection.java @@ -380,7 +380,7 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo @Contract("_, null -> false") static boolean isIdentityMapping(PsiVariable variable, PsiExpression mapperCall) { - return mapperCall instanceof PsiReferenceExpression && ((PsiReferenceExpression)mapperCall).resolve() == variable; + return mapperCall instanceof PsiReferenceExpression && ((PsiReferenceExpression)mapperCall).isReferenceTo(variable); } @Nullable @@ -724,7 +724,7 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo if(args.length != 1) return null; comparatorExpression = args[0]; } - if(!(listExpression instanceof PsiReferenceExpression) || ((PsiReferenceExpression)listExpression).resolve() != list) return null; + if(!(listExpression instanceof PsiReferenceExpression) || !((PsiReferenceExpression)listExpression).isReferenceTo(list)) return null; if(comparatorExpression == null || ExpressionUtils.isNullLiteral(comparatorExpression)) return ""; return comparatorExpression.getText(); } @@ -762,7 +762,7 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo PsiExpression qualifierExpression = methodExpression.getQualifierExpression(); if (!(qualifierExpression instanceof PsiReferenceExpression)) return null; PsiLocalVariable collectionVariable = extractCollectionVariable(expression.getMethodExpression().getQualifierExpression()); - if (collectionVariable == null || ((PsiReferenceExpression)qualifierExpression).resolve() != collectionVariable) return null; + if (collectionVariable == null || !((PsiReferenceExpression)qualifierExpression).isReferenceTo(collectionVariable)) return null; PsiExpression initializer = collectionVariable.getInitializer(); if (initializer == null) return null; PsiType type = initializer.getType(); @@ -1080,7 +1080,7 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo // check that increment is like for(...;...;i++) if(!(forStatement.getUpdate() instanceof PsiExpressionStatement)) return null; PsiExpression lValue = extractIncrementedLValue(((PsiExpressionStatement)forStatement.getUpdate()).getExpression()); - if(!(lValue instanceof PsiReferenceExpression) || ((PsiReferenceExpression)lValue).resolve() != counter) return null; + if(!(lValue instanceof PsiReferenceExpression) || !((PsiReferenceExpression)lValue).isReferenceTo(counter)) return null; // check that condition is like for(...;i Date: Wed, 28 Sep 2016 22:11:20 +0300 Subject: [PATCH 13/43] PY-16098 Fixed: Warn that async and await will become keywords in Python 3.7 Warn about variables, classes and functions called "await" or "async" in Pythons 3.5 and 3.6. Suggest quick fix to rename such nodes --- .../PyCompatibilityInspection.java | 37 +++++++++-- .../warningAboutAsyncAndAwaitInPy35.py | 14 +++++ .../warningAboutAsyncAndAwaitInPy36.py | 14 +++++ .../renameAsyncClassInPy35.py | 2 + .../renameAsyncClassInPy35_after.py | 2 + .../renameAsyncClassInPy36.py | 2 + .../renameAsyncClassInPy36_after.py | 2 + .../renameAsyncFunctionInPy35.py | 2 + .../renameAsyncFunctionInPy35_after.py | 2 + .../renameAsyncFunctionInPy36.py | 2 + .../renameAsyncFunctionInPy36_after.py | 2 + .../renameAsyncVariableInPy35.py | 1 + .../renameAsyncVariableInPy35_after.py | 1 + .../renameAsyncVariableInPy36.py | 1 + .../renameAsyncVariableInPy36_after.py | 1 + .../renameAwaitClassInPy35.py | 2 + .../renameAwaitClassInPy35_after.py | 2 + .../renameAwaitClassInPy36.py | 2 + .../renameAwaitClassInPy36_after.py | 2 + .../renameAwaitFunctionInPy35.py | 2 + .../renameAwaitFunctionInPy35_after.py | 2 + .../renameAwaitFunctionInPy36.py | 2 + .../renameAwaitFunctionInPy36_after.py | 2 + .../renameAwaitVariableInPy35.py | 1 + .../renameAwaitVariableInPy35_after.py | 1 + .../renameAwaitVariableInPy36.py | 1 + .../renameAwaitVariableInPy36_after.py | 1 + .../PyCompatibilityInspectionTest.java | 10 +++ .../quickFixes/RenameElementQuickFixTest.java | 62 ++++++++++++++++++- 29 files changed, 172 insertions(+), 5 deletions(-) create mode 100644 python/testData/inspections/PyCompatibilityInspection/warningAboutAsyncAndAwaitInPy35.py create mode 100644 python/testData/inspections/PyCompatibilityInspection/warningAboutAsyncAndAwaitInPy36.py create mode 100644 python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncClassInPy35.py create mode 100644 python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncClassInPy35_after.py create mode 100644 python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncClassInPy36.py create mode 100644 python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncClassInPy36_after.py create mode 100644 python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncFunctionInPy35.py create mode 100644 python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncFunctionInPy35_after.py create mode 100644 python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncFunctionInPy36.py create mode 100644 python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncFunctionInPy36_after.py create mode 100644 python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncVariableInPy35.py create mode 100644 python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncVariableInPy35_after.py create mode 100644 python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncVariableInPy36.py create mode 100644 python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncVariableInPy36_after.py create mode 100644 python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitClassInPy35.py create mode 100644 python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitClassInPy35_after.py create mode 100644 python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitClassInPy36.py create mode 100644 python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitClassInPy36_after.py create mode 100644 python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitFunctionInPy35.py create mode 100644 python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitFunctionInPy35_after.py create mode 100644 python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitFunctionInPy36.py create mode 100644 python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitFunctionInPy36_after.py create mode 100644 python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitVariableInPy35.py create mode 100644 python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitVariableInPy35_after.py create mode 100644 python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitVariableInPy36.py create mode 100644 python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitVariableInPy36_after.py diff --git a/python/src/com/jetbrains/python/inspections/PyCompatibilityInspection.java b/python/src/com/jetbrains/python/inspections/PyCompatibilityInspection.java index 4ee578511eec..8b2d0ec7dac7 100644 --- a/python/src/com/jetbrains/python/inspections/PyCompatibilityInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyCompatibilityInspection.java @@ -27,15 +27,14 @@ import com.intellij.openapi.util.JDOMExternalizableStringList; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.profile.codeInspection.InspectionProjectProfileManager; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiElementVisitor; -import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiReference; +import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.QualifiedName; +import com.intellij.util.ArrayUtil; import com.intellij.util.containers.ContainerUtil; import com.jetbrains.python.PyBundle; import com.jetbrains.python.PyNames; +import com.jetbrains.python.inspections.quickfix.PyRenameElementQuickFix; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.impl.PyBuiltinCache; import com.jetbrains.python.psi.types.PyClassType; @@ -325,5 +324,35 @@ public class PyCompatibilityInspection extends PyInspection { } } } + + @Override + public void visitPyTargetExpression(PyTargetExpression node) { + super.visitPyTargetExpression(node); + warnAboutAsyncAndAwaitInPy35AndPy36(node); + } + + @Override + public void visitPyClass(PyClass node) { + super.visitPyClass(node); + warnAboutAsyncAndAwaitInPy35AndPy36(node); + } + + @Override + public void visitPyFunction(PyFunction node) { + super.visitPyFunction(node); + warnAboutAsyncAndAwaitInPy35AndPy36(node); + } + + private void warnAboutAsyncAndAwaitInPy35AndPy36(@NotNull PsiNameIdentifierOwner nameIdentifierOwner) { + final PsiElement nameIdentifier = nameIdentifierOwner.getNameIdentifier(); + + if (nameIdentifier != null && ArrayUtil.contains(nameIdentifierOwner.getName(), PyNames.AWAIT, PyNames.ASYNC)) { + registerOnFirstMatchingVersion(level -> LanguageLevel.PYTHON35.equals(level) || LanguageLevel.PYTHON36.equals(level), + "'async' and 'await' are not recommended to be used as variable, class, function or module names. " + + "They will become proper keywords in Python 3.7.", + nameIdentifier, + new PyRenameElementQuickFix()); + } + } } } \ No newline at end of file diff --git a/python/testData/inspections/PyCompatibilityInspection/warningAboutAsyncAndAwaitInPy35.py b/python/testData/inspections/PyCompatibilityInspection/warningAboutAsyncAndAwaitInPy35.py new file mode 100644 index 000000000000..eea16c40ef25 --- /dev/null +++ b/python/testData/inspections/PyCompatibilityInspection/warningAboutAsyncAndAwaitInPy35.py @@ -0,0 +1,14 @@ +class async(object): + pass + +class await(object): + pass + +def async(): + pass + +def await(): + pass + +async = 1 +await = 2 \ No newline at end of file diff --git a/python/testData/inspections/PyCompatibilityInspection/warningAboutAsyncAndAwaitInPy36.py b/python/testData/inspections/PyCompatibilityInspection/warningAboutAsyncAndAwaitInPy36.py new file mode 100644 index 000000000000..eea16c40ef25 --- /dev/null +++ b/python/testData/inspections/PyCompatibilityInspection/warningAboutAsyncAndAwaitInPy36.py @@ -0,0 +1,14 @@ +class async(object): + pass + +class await(object): + pass + +def async(): + pass + +def await(): + pass + +async = 1 +await = 2 \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncClassInPy35.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncClassInPy35.py new file mode 100644 index 000000000000..bc25733ddf5d --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncClassInPy35.py @@ -0,0 +1,2 @@ +class async(object): + pass \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncClassInPy35_after.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncClassInPy35_after.py new file mode 100644 index 000000000000..1a98a9cfc6c4 --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncClassInPy35_after.py @@ -0,0 +1,2 @@ +class a(object): + pass \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncClassInPy36.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncClassInPy36.py new file mode 100644 index 000000000000..bc25733ddf5d --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncClassInPy36.py @@ -0,0 +1,2 @@ +class async(object): + pass \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncClassInPy36_after.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncClassInPy36_after.py new file mode 100644 index 000000000000..1a98a9cfc6c4 --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncClassInPy36_after.py @@ -0,0 +1,2 @@ +class a(object): + pass \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncFunctionInPy35.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncFunctionInPy35.py new file mode 100644 index 000000000000..f48d631547da --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncFunctionInPy35.py @@ -0,0 +1,2 @@ +def async(): + pass \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncFunctionInPy35_after.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncFunctionInPy35_after.py new file mode 100644 index 000000000000..690f3c163030 --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncFunctionInPy35_after.py @@ -0,0 +1,2 @@ +def a(): + pass \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncFunctionInPy36.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncFunctionInPy36.py new file mode 100644 index 000000000000..f48d631547da --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncFunctionInPy36.py @@ -0,0 +1,2 @@ +def async(): + pass \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncFunctionInPy36_after.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncFunctionInPy36_after.py new file mode 100644 index 000000000000..690f3c163030 --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncFunctionInPy36_after.py @@ -0,0 +1,2 @@ +def a(): + pass \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncVariableInPy35.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncVariableInPy35.py new file mode 100644 index 000000000000..96bae926eb40 --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncVariableInPy35.py @@ -0,0 +1 @@ +async = 1 \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncVariableInPy35_after.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncVariableInPy35_after.py new file mode 100644 index 000000000000..d25d49e0ffc3 --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncVariableInPy35_after.py @@ -0,0 +1 @@ +a = 1 \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncVariableInPy36.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncVariableInPy36.py new file mode 100644 index 000000000000..96bae926eb40 --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncVariableInPy36.py @@ -0,0 +1 @@ +async = 1 \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncVariableInPy36_after.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncVariableInPy36_after.py new file mode 100644 index 000000000000..d25d49e0ffc3 --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncVariableInPy36_after.py @@ -0,0 +1 @@ +a = 1 \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitClassInPy35.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitClassInPy35.py new file mode 100644 index 000000000000..c531a434721e --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitClassInPy35.py @@ -0,0 +1,2 @@ +class await(object): + pass \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitClassInPy35_after.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitClassInPy35_after.py new file mode 100644 index 000000000000..1a98a9cfc6c4 --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitClassInPy35_after.py @@ -0,0 +1,2 @@ +class a(object): + pass \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitClassInPy36.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitClassInPy36.py new file mode 100644 index 000000000000..c531a434721e --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitClassInPy36.py @@ -0,0 +1,2 @@ +class await(object): + pass \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitClassInPy36_after.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitClassInPy36_after.py new file mode 100644 index 000000000000..1a98a9cfc6c4 --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitClassInPy36_after.py @@ -0,0 +1,2 @@ +class a(object): + pass \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitFunctionInPy35.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitFunctionInPy35.py new file mode 100644 index 000000000000..c1ef9d6ccfae --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitFunctionInPy35.py @@ -0,0 +1,2 @@ +def await(): + pass \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitFunctionInPy35_after.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitFunctionInPy35_after.py new file mode 100644 index 000000000000..690f3c163030 --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitFunctionInPy35_after.py @@ -0,0 +1,2 @@ +def a(): + pass \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitFunctionInPy36.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitFunctionInPy36.py new file mode 100644 index 000000000000..c1ef9d6ccfae --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitFunctionInPy36.py @@ -0,0 +1,2 @@ +def await(): + pass \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitFunctionInPy36_after.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitFunctionInPy36_after.py new file mode 100644 index 000000000000..690f3c163030 --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitFunctionInPy36_after.py @@ -0,0 +1,2 @@ +def a(): + pass \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitVariableInPy35.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitVariableInPy35.py new file mode 100644 index 000000000000..1a28415ce838 --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitVariableInPy35.py @@ -0,0 +1 @@ +await = 1 \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitVariableInPy35_after.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitVariableInPy35_after.py new file mode 100644 index 000000000000..d25d49e0ffc3 --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitVariableInPy35_after.py @@ -0,0 +1 @@ +a = 1 \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitVariableInPy36.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitVariableInPy36.py new file mode 100644 index 000000000000..1a28415ce838 --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitVariableInPy36.py @@ -0,0 +1 @@ +await = 1 \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitVariableInPy36_after.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitVariableInPy36_after.py new file mode 100644 index 000000000000..d25d49e0ffc3 --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitVariableInPy36_after.py @@ -0,0 +1 @@ +a = 1 \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/inspections/PyCompatibilityInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyCompatibilityInspectionTest.java index 4bc87459c36f..7afe19f1f695 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyCompatibilityInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyCompatibilityInspectionTest.java @@ -209,6 +209,16 @@ public class PyCompatibilityInspectionTest extends PyTestCase { doTest(LanguageLevel.PYTHON36); } + // PY-16098 + public void testWarningAboutAsyncAndAwaitInPy35() { + doTest(LanguageLevel.PYTHON35); + } + + // PY-16098 + public void testWarningAboutAsyncAndAwaitInPy36() { + doTest(LanguageLevel.PYTHON36); + } + private void doTest(@NotNull LanguageLevel level) { runWithLanguageLevel(level, this::doTest); } diff --git a/python/testSrc/com/jetbrains/python/quickFixes/RenameElementQuickFixTest.java b/python/testSrc/com/jetbrains/python/quickFixes/RenameElementQuickFixTest.java index 5a63b49b66e6..66a01ae20e1f 100644 --- a/python/testSrc/com/jetbrains/python/quickFixes/RenameElementQuickFixTest.java +++ b/python/testSrc/com/jetbrains/python/quickFixes/RenameElementQuickFixTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -17,6 +17,7 @@ package com.jetbrains.python.quickFixes; import com.intellij.testFramework.TestDataPath; import com.jetbrains.python.PyQuickFixTestCase; +import com.jetbrains.python.inspections.PyCompatibilityInspection; import com.jetbrains.python.inspections.PyPep8NamingInspection; import com.jetbrains.python.inspections.PyProtectedMemberInspection; import com.jetbrains.python.inspections.PyShadowingBuiltinsInspection; @@ -47,4 +48,63 @@ public class RenameElementQuickFixTest extends PyQuickFixTestCase { doQuickFixTest(PyShadowingBuiltinsInspection.class, "Rename element"); } + // PY-16098 + public void testRenameAsyncClassInPy35() { + doQuickFixTest(PyCompatibilityInspection.class, "Rename element"); + } + + // PY-16098 + public void testRenameAsyncClassInPy36() { + doQuickFixTest(PyCompatibilityInspection.class, "Rename element"); + } + + // PY-16098 + public void testRenameAwaitClassInPy35() { + doQuickFixTest(PyCompatibilityInspection.class, "Rename element"); + } + + // PY-16098 + public void testRenameAwaitClassInPy36() { + doQuickFixTest(PyCompatibilityInspection.class, "Rename element"); + } + + // PY-16098 + public void testRenameAsyncFunctionInPy35() { + doQuickFixTest(PyCompatibilityInspection.class, "Rename element"); + } + + // PY-16098 + public void testRenameAsyncFunctionInPy36() { + doQuickFixTest(PyCompatibilityInspection.class, "Rename element"); + } + + // PY-16098 + public void testRenameAwaitFunctionInPy35() { + doQuickFixTest(PyCompatibilityInspection.class, "Rename element"); + } + + // PY-16098 + public void testRenameAwaitFunctionInPy36() { + doQuickFixTest(PyCompatibilityInspection.class, "Rename element"); + } + + // PY-16098 + public void testRenameAsyncVariableInPy35() { + doQuickFixTest(PyCompatibilityInspection.class, "Rename element"); + } + + // PY-16098 + public void testRenameAsyncVariableInPy36() { + doQuickFixTest(PyCompatibilityInspection.class, "Rename element"); + } + + // PY-16098 + public void testRenameAwaitVariableInPy35() { + doQuickFixTest(PyCompatibilityInspection.class, "Rename element"); + } + + // PY-16098 + public void testRenameAwaitVariableInPy36() { + doQuickFixTest(PyCompatibilityInspection.class, "Rename element"); + } } From 0ea03b8acb72542cf637008441464533618e52a3 Mon Sep 17 00:00:00 2001 From: Semyon Proshev Date: Wed, 28 Sep 2016 22:25:47 +0300 Subject: [PATCH 14/43] Create bundle constant for PyRenameElementQuickFix name --- .../com/jetbrains/python/PyBundle.properties | 1 + .../quickfix/PyRenameElementQuickFix.java | 5 +-- .../com/jetbrains/python/PyQuickFixTest.java | 4 +-- .../quickFixes/RenameElementQuickFixTest.java | 35 ++++++++++--------- 4 files changed, 24 insertions(+), 21 deletions(-) diff --git a/python/src/com/jetbrains/python/PyBundle.properties b/python/src/com/jetbrains/python/PyBundle.properties index 1db11b782e1f..4bc626a4fb80 100644 --- a/python/src/com/jetbrains/python/PyBundle.properties +++ b/python/src/com/jetbrains/python/PyBundle.properties @@ -154,6 +154,7 @@ QFIX.NAME.remove.argument=Remove argument QFIX.NAME.remove.parameter=Remove parameter QFIX.NAME.rename.argument=Rename argument +QFIX.NAME.rename.element=Rename element QFIX.NAME.wrap.in.exception=Wrap with Exception call diff --git a/python/src/com/jetbrains/python/inspections/quickfix/PyRenameElementQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/PyRenameElementQuickFix.java index 103b404bec70..02a760247f67 100644 --- a/python/src/com/jetbrains/python/inspections/quickfix/PyRenameElementQuickFix.java +++ b/python/src/com/jetbrains/python/inspections/quickfix/PyRenameElementQuickFix.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -34,6 +34,7 @@ import com.intellij.refactoring.rename.PsiElementRenameHandler; import com.intellij.refactoring.rename.RenameProcessor; import com.intellij.refactoring.rename.RenamePsiElementProcessor; import com.intellij.refactoring.rename.inplace.VariableInplaceRenamer; +import com.jetbrains.python.PyBundle; import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil; import com.jetbrains.python.psi.PyNamedParameter; import com.jetbrains.python.psi.PyReferenceExpression; @@ -49,7 +50,7 @@ public class PyRenameElementQuickFix implements LocalQuickFix { @NotNull @Override public String getFamilyName() { - return "Rename element"; + return PyBundle.message("QFIX.NAME.rename.element"); } @Override diff --git a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java index 7d152a5520c7..ccea524a9b8f 100644 --- a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java +++ b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java @@ -551,7 +551,7 @@ public class PyQuickFixTest extends PyTestCase { myFixture.configureByFile(fileName); myFixture.enableInspections(PyShadowingBuiltinsInspection.class); myFixture.checkHighlighting(true, false, true); - final IntentionAction intentionAction = myFixture.getAvailableIntention("Rename element"); + final IntentionAction intentionAction = myFixture.getAvailableIntention(PyBundle.message("QFIX.NAME.rename.element")); assertNotNull(intentionAction); myFixture.launchAction(intentionAction); myFixture.checkResultByFile(graftBeforeExt(fileName, "_after")); @@ -563,7 +563,7 @@ public class PyQuickFixTest extends PyTestCase { myFixture.configureByFile(fileName); myFixture.enableInspections(PyShadowingBuiltinsInspection.class); myFixture.checkHighlighting(true, false, true); - final IntentionAction intentionAction = myFixture.getAvailableIntention("Rename element"); + final IntentionAction intentionAction = myFixture.getAvailableIntention(PyBundle.message("QFIX.NAME.rename.element")); assertNotNull(intentionAction); myFixture.launchAction(intentionAction); myFixture.checkResultByFile(graftBeforeExt(fileName, "_after")); diff --git a/python/testSrc/com/jetbrains/python/quickFixes/RenameElementQuickFixTest.java b/python/testSrc/com/jetbrains/python/quickFixes/RenameElementQuickFixTest.java index 66a01ae20e1f..16d5a9e75bc5 100644 --- a/python/testSrc/com/jetbrains/python/quickFixes/RenameElementQuickFixTest.java +++ b/python/testSrc/com/jetbrains/python/quickFixes/RenameElementQuickFixTest.java @@ -16,6 +16,7 @@ package com.jetbrains.python.quickFixes; import com.intellij.testFramework.TestDataPath; +import com.jetbrains.python.PyBundle; import com.jetbrains.python.PyQuickFixTestCase; import com.jetbrains.python.inspections.PyCompatibilityInspection; import com.jetbrains.python.inspections.PyPep8NamingInspection; @@ -29,82 +30,82 @@ import com.jetbrains.python.inspections.PyShadowingBuiltinsInspection; public class RenameElementQuickFixTest extends PyQuickFixTestCase { public void testProtectedMember() { - doQuickFixTest(PyProtectedMemberInspection.class, "Rename element"); + doQuickFixTest(PyProtectedMemberInspection.class, PyBundle.message("QFIX.NAME.rename.element")); } public void testPep8() { - doQuickFixTest(PyPep8NamingInspection.class, "Rename element"); + doQuickFixTest(PyPep8NamingInspection.class, PyBundle.message("QFIX.NAME.rename.element")); } public void testPep8Class() { - doQuickFixTest(PyPep8NamingInspection.class, "Rename element"); + doQuickFixTest(PyPep8NamingInspection.class, PyBundle.message("QFIX.NAME.rename.element")); } public void testPep8Function() { - doQuickFixTest(PyPep8NamingInspection.class, "Rename element"); + doQuickFixTest(PyPep8NamingInspection.class, PyBundle.message("QFIX.NAME.rename.element")); } public void testShadowingBuiltins() { - doQuickFixTest(PyShadowingBuiltinsInspection.class, "Rename element"); + doQuickFixTest(PyShadowingBuiltinsInspection.class, PyBundle.message("QFIX.NAME.rename.element")); } // PY-16098 public void testRenameAsyncClassInPy35() { - doQuickFixTest(PyCompatibilityInspection.class, "Rename element"); + doQuickFixTest(PyCompatibilityInspection.class, PyBundle.message("QFIX.NAME.rename.element")); } // PY-16098 public void testRenameAsyncClassInPy36() { - doQuickFixTest(PyCompatibilityInspection.class, "Rename element"); + doQuickFixTest(PyCompatibilityInspection.class, PyBundle.message("QFIX.NAME.rename.element")); } // PY-16098 public void testRenameAwaitClassInPy35() { - doQuickFixTest(PyCompatibilityInspection.class, "Rename element"); + doQuickFixTest(PyCompatibilityInspection.class, PyBundle.message("QFIX.NAME.rename.element")); } // PY-16098 public void testRenameAwaitClassInPy36() { - doQuickFixTest(PyCompatibilityInspection.class, "Rename element"); + doQuickFixTest(PyCompatibilityInspection.class, PyBundle.message("QFIX.NAME.rename.element")); } // PY-16098 public void testRenameAsyncFunctionInPy35() { - doQuickFixTest(PyCompatibilityInspection.class, "Rename element"); + doQuickFixTest(PyCompatibilityInspection.class, PyBundle.message("QFIX.NAME.rename.element")); } // PY-16098 public void testRenameAsyncFunctionInPy36() { - doQuickFixTest(PyCompatibilityInspection.class, "Rename element"); + doQuickFixTest(PyCompatibilityInspection.class, PyBundle.message("QFIX.NAME.rename.element")); } // PY-16098 public void testRenameAwaitFunctionInPy35() { - doQuickFixTest(PyCompatibilityInspection.class, "Rename element"); + doQuickFixTest(PyCompatibilityInspection.class, PyBundle.message("QFIX.NAME.rename.element")); } // PY-16098 public void testRenameAwaitFunctionInPy36() { - doQuickFixTest(PyCompatibilityInspection.class, "Rename element"); + doQuickFixTest(PyCompatibilityInspection.class, PyBundle.message("QFIX.NAME.rename.element")); } // PY-16098 public void testRenameAsyncVariableInPy35() { - doQuickFixTest(PyCompatibilityInspection.class, "Rename element"); + doQuickFixTest(PyCompatibilityInspection.class, PyBundle.message("QFIX.NAME.rename.element")); } // PY-16098 public void testRenameAsyncVariableInPy36() { - doQuickFixTest(PyCompatibilityInspection.class, "Rename element"); + doQuickFixTest(PyCompatibilityInspection.class, PyBundle.message("QFIX.NAME.rename.element")); } // PY-16098 public void testRenameAwaitVariableInPy35() { - doQuickFixTest(PyCompatibilityInspection.class, "Rename element"); + doQuickFixTest(PyCompatibilityInspection.class, PyBundle.message("QFIX.NAME.rename.element")); } // PY-16098 public void testRenameAwaitVariableInPy36() { - doQuickFixTest(PyCompatibilityInspection.class, "Rename element"); + doQuickFixTest(PyCompatibilityInspection.class, PyBundle.message("QFIX.NAME.rename.element")); } } From 3c412457cf3f3cbab2925d3bf3a83cced9051494 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Thu, 6 Oct 2016 12:19:55 +0300 Subject: [PATCH 15/43] cleanup --- platform/util/src/com/intellij/util/PausesStat.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/util/src/com/intellij/util/PausesStat.java b/platform/util/src/com/intellij/util/PausesStat.java index 06070fa8d402..540e17014032 100644 --- a/platform/util/src/com/intellij/util/PausesStat.java +++ b/platform/util/src/com/intellij/util/PausesStat.java @@ -67,7 +67,7 @@ public class PausesStat { long finishStamp = System.currentTimeMillis(); int duration = (int)(finishStamp - startTimeStamp); started = false; - duration = Math.min(duration, (1 << 16) - 1); + duration = Math.min(duration, Short.MAX_VALUE); if (duration > maxDuration) { maxDuration = duration; maxDurationDescription = description; From a05c96972edd4d7554671cb5d2e03537014ba1f1 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Thu, 6 Oct 2016 14:27:40 +0300 Subject: [PATCH 16/43] renamed ambiguous getBom to getPossibleBom --- .../intellij/diff/tools/external/ExternalDiffToolUtil.java | 2 +- .../util/src/com/intellij/openapi/vfs/CharsetToolkit.java | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/platform/diff-impl/src/com/intellij/diff/tools/external/ExternalDiffToolUtil.java b/platform/diff-impl/src/com/intellij/diff/tools/external/ExternalDiffToolUtil.java index 2eccc8d703ed..936ee405660f 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/external/ExternalDiffToolUtil.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/external/ExternalDiffToolUtil.java @@ -129,7 +129,7 @@ public class ExternalDiffToolUtil { byte[] bytes = contentData.getBytes(charset); - byte[] bom = hasBom ? CharsetToolkit.getBom(charset) : null; + byte[] bom = hasBom ? CharsetToolkit.getPossibleBom(charset) : null; if (bom != null) { bytes = ArrayUtil.mergeArrays(bom, bytes); } diff --git a/platform/util/src/com/intellij/openapi/vfs/CharsetToolkit.java b/platform/util/src/com/intellij/openapi/vfs/CharsetToolkit.java index 178936acf6ec..b8f94ab9e12e 100644 --- a/platform/util/src/com/intellij/openapi/vfs/CharsetToolkit.java +++ b/platform/util/src/com/intellij/openapi/vfs/CharsetToolkit.java @@ -624,8 +624,12 @@ public class CharsetToolkit { return CHARSET_TO_MANDATORY_BOM.get(charset); } + /** + * @return BOM which can be associated with this charset, or null otherwise. + * Currently these are UTF-16xx, UTF-32xx and UTF-8. + */ @Nullable - public static byte[] getBom(@NotNull Charset charset) { + public static byte[] getPossibleBom(@NotNull Charset charset) { if (charset.equals(UTF8_CHARSET)) return UTF8_BOM; return CHARSET_TO_MANDATORY_BOM.get(charset); } From 1966a7d8f9493786b710bd93a396b8e08c247da2 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Thu, 6 Oct 2016 14:31:42 +0300 Subject: [PATCH 17/43] EA-87865 - Throwable: ExceptionUtil.currentStackTrace --- .../openapi/wm/impl/status/TogglePopupHintsPanel.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/openapi/wm/impl/status/TogglePopupHintsPanel.java b/platform/lang-impl/src/com/intellij/openapi/wm/impl/status/TogglePopupHintsPanel.java index 0376ef6b2a48..126765c510a6 100644 --- a/platform/lang-impl/src/com/intellij/openapi/wm/impl/status/TogglePopupHintsPanel.java +++ b/platform/lang-impl/src/com/intellij/openapi/wm/impl/status/TogglePopupHintsPanel.java @@ -33,6 +33,7 @@ import com.intellij.psi.PsiManager; import com.intellij.ui.UIBundle; import com.intellij.ui.awt.RelativePoint; import com.intellij.util.Consumer; +import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -47,12 +48,7 @@ public class TogglePopupHintsPanel extends EditorBasedWidget implements StatusBa public TogglePopupHintsPanel(@NotNull final Project project) { super(project); myCurrentIcon = AllIcons.Ide.HectorNo; - myConnection.subscribe(PowerSaveMode.TOPIC, new PowerSaveMode.Listener() { - @Override - public void powerSaveStateChanged() { - updateStatus(); - } - }); + myConnection.subscribe(PowerSaveMode.TOPIC, this::updateStatus); } @Override @@ -115,10 +111,11 @@ public class TogglePopupHintsPanel extends EditorBasedWidget implements StatusBa } public void updateStatus() { - updateStatus(getCurrentFile()); + UIUtil.invokeLaterIfNeeded(() -> updateStatus(getCurrentFile())); } private void updateStatus(PsiFile file) { + if (isDisposed()) return; if (isStateChangeable(file)) { if (PowerSaveMode.isEnabled()) { myCurrentIcon = AllIcons.Ide.HectorNo; From 40039c902496eae6acc38ceb56ea815051d6345b Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Thu, 6 Oct 2016 16:08:34 +0300 Subject: [PATCH 18/43] suppressed "can be private" because it's called via reflection --- .../intellij/execution/testDiscovery/TestDiscoveryExtension.java | 1 + 1 file changed, 1 insertion(+) diff --git a/java/execution/impl/src/com/intellij/execution/testDiscovery/TestDiscoveryExtension.java b/java/execution/impl/src/com/intellij/execution/testDiscovery/TestDiscoveryExtension.java index 6868023872b8..23c95d517bd6 100644 --- a/java/execution/impl/src/com/intellij/execution/testDiscovery/TestDiscoveryExtension.java +++ b/java/execution/impl/src/com/intellij/execution/testDiscovery/TestDiscoveryExtension.java @@ -184,6 +184,7 @@ public class TestDiscoveryExtension extends RunConfigurationExtension { } } + @SuppressWarnings("WeakerAccess") // called via reflection from com.intellij.InternalTestDiscoveryListener.flushCurrentTraces() public static void processAvailableTraces(final String[] fullTestNames, final String tracesDirectory, final String moduleName, From be45d07c832f17b59e31659944721d6748033db7 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Thu, 6 Oct 2016 18:17:37 +0300 Subject: [PATCH 19/43] renames for clarity --- .../BaseExternalAnnotationsManager.java | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/java/java-psi-impl/src/com/intellij/codeInsight/BaseExternalAnnotationsManager.java b/java/java-psi-impl/src/com/intellij/codeInsight/BaseExternalAnnotationsManager.java index 13ca286944bf..c791ba4574b8 100644 --- a/java/java-psi-impl/src/com/intellij/codeInsight/BaseExternalAnnotationsManager.java +++ b/java/java-psi-impl/src/com/intellij/codeInsight/BaseExternalAnnotationsManager.java @@ -52,8 +52,6 @@ import java.io.IOException; import java.util.*; import java.util.concurrent.ConcurrentMap; -import static com.intellij.openapi.util.Pair.pair; - public abstract class BaseExternalAnnotationsManager extends ExternalAnnotationsManager { private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.BaseExternalAnnotationsManager"); private static final Key EXTERNAL_ANNO_MARKER = Key.create("EXTERNAL_ANNO_MARKER"); @@ -61,9 +59,9 @@ public abstract class BaseExternalAnnotationsManager extends ExternalAnnotations protected final PsiManager myPsiManager; - private final ConcurrentMap> myExternalAnnotations = ContainerUtil.createConcurrentWeakKeySoftValueMap(); + private final ConcurrentMap> myExternalAnnotationsCache = ContainerUtil.createConcurrentWeakKeySoftValueMap(); private final Map myAnnotationDataCache = new WeakKeyWeakValueHashMap(); - private final ConcurrentMap, Long>> myAnnotationFileToDataAndModStamp = ContainerUtil.createConcurrentSoftMap(); + private final ConcurrentMap, Long>> myAnnotationFileToDataAndModStampCache = ContainerUtil.createConcurrentSoftMap(); public BaseExternalAnnotationsManager(@NotNull PsiManager psiManager) { myPsiManager = psiManager; @@ -166,7 +164,7 @@ public abstract class BaseExternalAnnotationsManager extends ExternalAnnotations @NotNull private MostlySingularMultiMap getDataFromFile(@NotNull PsiFile file) { - Pair, Long> cached = myAnnotationFileToDataAndModStamp.get(file); + Pair, Long> cached = myAnnotationFileToDataAndModStampCache.get(file); long fileModificationStamp = file.getModificationStamp(); if (cached != null && cached.getSecond() == fileModificationStamp) { return cached.getFirst(); @@ -188,7 +186,7 @@ public abstract class BaseExternalAnnotationsManager extends ExternalAnnotations } MostlySingularMultiMap result = handler.getResult(); - myAnnotationFileToDataAndModStamp.put(file, pair(result, fileModificationStamp)); + myAnnotationFileToDataAndModStampCache.put(file, Pair.create(result, fileModificationStamp)); return result; } @@ -234,7 +232,7 @@ public abstract class BaseExternalAnnotationsManager extends ExternalAnnotations final VirtualFile virtualFile = containingFile.getVirtualFile(); if (virtualFile == null) return null; - final List files = myExternalAnnotations.get(virtualFile); + final List files = myExternalAnnotationsCache.get(virtualFile); if (files == NULL_LIST) return null; if (files != null) { @@ -263,7 +261,7 @@ public abstract class BaseExternalAnnotationsManager extends ExternalAnnotations } if (possibleAnnotations.isEmpty()) { - myExternalAnnotations.put(virtualFile, NULL_LIST); + myExternalAnnotationsCache.put(virtualFile, NULL_LIST); return null; } @@ -277,7 +275,7 @@ public abstract class BaseExternalAnnotationsManager extends ExternalAnnotations return w1 == w2 ? 0 : w1 ? -1 : 1; } }); - myExternalAnnotations.put(virtualFile, result); + myExternalAnnotationsCache.put(virtualFile, result); return result; } @@ -285,8 +283,8 @@ public abstract class BaseExternalAnnotationsManager extends ExternalAnnotations protected abstract List getExternalAnnotationsRoots(@NotNull VirtualFile libraryFile); protected void dropCache() { - myExternalAnnotations.clear(); - myAnnotationFileToDataAndModStamp.clear(); + myExternalAnnotationsCache.clear(); + myAnnotationFileToDataAndModStampCache.clear(); cache.clear(); } @@ -348,7 +346,7 @@ public abstract class BaseExternalAnnotationsManager extends ExternalAnnotations @NotNull List annotationFiles) { VirtualFile virtualFile = fromFile.getVirtualFile(); if (virtualFile != null) { - myExternalAnnotations.put(virtualFile, annotationFiles); + myExternalAnnotationsCache.put(virtualFile, annotationFiles); } } From 4f11cff6d03c935a141d143c88d5803d3ac53d2a Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Fri, 7 Oct 2016 12:25:18 +0300 Subject: [PATCH 20/43] diag: fold by default the DEBUG logging of the failed tests --- .../FailedTestDebugLogConsoleFolding.java | 32 +++++++++++++++++++ .../testFramework/TestLoggerFactory.java | 19 +++++++++-- resources/src/META-INF/IdeaPlugin.xml | 1 + 3 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 platform/testFramework/src/com/intellij/testFramework/FailedTestDebugLogConsoleFolding.java diff --git a/platform/testFramework/src/com/intellij/testFramework/FailedTestDebugLogConsoleFolding.java b/platform/testFramework/src/com/intellij/testFramework/FailedTestDebugLogConsoleFolding.java new file mode 100644 index 000000000000..a9517fe76b57 --- /dev/null +++ b/platform/testFramework/src/com/intellij/testFramework/FailedTestDebugLogConsoleFolding.java @@ -0,0 +1,32 @@ +/* + * Copyright 2000-2016 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 com.intellij.testFramework; + +import com.intellij.execution.ConsoleFolding; + +import java.util.List; + +public class FailedTestDebugLogConsoleFolding extends ConsoleFolding { + @Override + public boolean shouldFoldLine(String line) { + return line.indexOf(TestLoggerFactory.FAILED_TEST_DEBUG_OUTPUT_MARKER) != -1; + } + + @Override + public String getPlaceholderText(List lines) { + return " "; + } +} diff --git a/platform/testFramework/src/com/intellij/testFramework/TestLoggerFactory.java b/platform/testFramework/src/com/intellij/testFramework/TestLoggerFactory.java index 7f9752c9d06a..75f22b2e4e5f 100644 --- a/platform/testFramework/src/com/intellij/testFramework/TestLoggerFactory.java +++ b/platform/testFramework/src/com/intellij/testFramework/TestLoggerFactory.java @@ -20,6 +20,7 @@ import com.intellij.openapi.application.PathManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.LineTokenizer; import com.intellij.openapi.util.text.StringUtil; import org.apache.log4j.*; import org.apache.log4j.spi.LoggingEvent; @@ -145,6 +146,8 @@ public class TestLoggerFactory implements Logger.Factory { private static final StringWriter STRING_WRITER = new StringWriter(); private static final StringBuffer BUFFER = STRING_WRITER.getBuffer(); + static final char FAILED_TEST_DEBUG_OUTPUT_MARKER = '\u2003'; + // inserted unicode whitespace to be able to tell these failed tests log lines from the others and fold them private static final WriterAppender APPENDER = new WriterAppender(new PatternLayout("%d{HH:mm:ss,SSS} %p %.30c - %m%n"), STRING_WRITER); private static final int MAX_BUFFER_LENGTH = 100000; private static final String CFQN = Category.class.getName(); @@ -165,8 +168,20 @@ public class TestLoggerFactory implements Logger.Factory { } public static void onTestFinished(boolean success) { - if (!success) { - System.err.println(BUFFER); + if (!success && BUFFER.length() != 0) { + if (UsefulTestCase.IS_UNDER_TEAMCITY) { + // print in one statement to avoid exception text cutting in between causing this fold to expand + BUFFER.insert(0, "##teamcity[blockOpened name='DEBUG log']\n"); + BUFFER.append( "\n##teamcity[blockClosed name='DEBUG log']\n"); + System.err.println(BUFFER); + } + else { + // mark each line in IDEA console with this hidden mark to be able to fold it automatically + String[] lines = LineTokenizer.tokenize(BUFFER, false, false); + String text = StringUtil.join(lines, FAILED_TEST_DEBUG_OUTPUT_MARKER + "\n"); + if (!text.startsWith("\n")) text = "\n" + text; + System.err.println(text); + } } BUFFER.setLength(0); } diff --git a/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml index 2e0249035038..4de90ce9f8e3 100644 --- a/resources/src/META-INF/IdeaPlugin.xml +++ b/resources/src/META-INF/IdeaPlugin.xml @@ -1244,6 +1244,7 @@ + From aa605118de5a6bd119f8d7bb9dda471d7ed60111 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Fri, 7 Oct 2016 16:33:18 +0700 Subject: [PATCH 21/43] LightQuickFixTestCase: Check whether is present in "before" files; wrong tests fixed --- .../beforeLastSlashN.java | 2 +- .../beforeLastSlashNSlashR.java | 2 +- .../beforeVarargsCallMoreArgs.java | 2 +- .../redundantUncheckedVarargs/before7.java | 2 +- .../suppressLocalInspection/before6.java | 10 +++++++++- .../suppressNonInspections/before10.java | 2 +- .../suppressNonInspections/before8.java | 2 +- .../beforeInvalidTypeParameter.java | 2 +- .../beforeConstructorNoDuplicates.java | 2 +- .../quickFix/SuppressLocalInspectionTest.java | 19 +++++++++++++++++-- .../quickFix/LightQuickFixTestCase.java | 2 +- .../testFramework/EditorTestUtil.java | 14 +++++++++++++- .../LightPlatformCodeInsightTestCase.java | 16 ++++++++++++++++ 13 files changed, 64 insertions(+), 13 deletions(-) diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/breakStringOnLineBreaks/beforeLastSlashN.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/breakStringOnLineBreaks/beforeLastSlashN.java index 0de2f83afa6a..065d55f3b63f 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/breakStringOnLineBreaks/beforeLastSlashN.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/breakStringOnLineBreaks/beforeLastSlashN.java @@ -1,5 +1,5 @@ // "Break string on '\n'" "false" class A { - String s = "Hello!\n"; + String s = "Hello!\n"; } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/breakStringOnLineBreaks/beforeLastSlashNSlashR.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/breakStringOnLineBreaks/beforeLastSlashNSlashR.java index c18a560abff9..220a365a43a6 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/breakStringOnLineBreaks/beforeLastSlashNSlashR.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/breakStringOnLineBreaks/beforeLastSlashNSlashR.java @@ -1,5 +1,5 @@ // "Break string on '\n'" "false" class A { - String s = "Hello!\n\r"; + String s = "Hello!\n\r"; } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/lambda2methodReference/beforeVarargsCallMoreArgs.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/lambda2methodReference/beforeVarargsCallMoreArgs.java index 1d1261adfdb2..2e9edf0cdcee 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/lambda2methodReference/beforeVarargsCallMoreArgs.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/lambda2methodReference/beforeVarargsCallMoreArgs.java @@ -7,6 +7,6 @@ class Example { } { - Function r = (s) -> m(s, s); + Function r = (s) -> m(s, s); } } \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantUncheckedVarargs/before7.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantUncheckedVarargs/before7.java index da98e4655f5b..a9d8079d459d 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantUncheckedVarargs/before7.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantUncheckedVarargs/before7.java @@ -12,7 +12,7 @@ public class Test { } @SuppressWarnings("unchecked") - void foo() { + void foo() { foo(new ArrayList()).addAll(foo1(new ArrayList); } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/suppressLocalInspection/before6.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/suppressLocalInspection/before6.java index 36fd61a74e52..8f54bf7ff7a1 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/suppressLocalInspection/before6.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/suppressLocalInspection/before6.java @@ -1,5 +1,13 @@ // "Suppress for field" "false" /** @noinspection ALL*/ class a { - static private String mm = "00"; + static private String mm = "00"; + // The "Convert to local" inspection should be reported here if not suppressed + + static void test() { + mm = "1"; + if(mm == "1") { + mm = "2"; + } + } } \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/suppressNonInspections/before10.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/suppressNonInspections/before10.java index c263cb5fcae7..f8b32ba0108d 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/suppressNonInspections/before10.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/suppressNonInspections/before10.java @@ -4,6 +4,6 @@ public class Test { { int i = 0; //noinspection SillyAssignment my very long comment - i = i; + i = i; } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/suppressNonInspections/before8.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/suppressNonInspections/before8.java index 0897593fad70..4532b8121308 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/suppressNonInspections/before8.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/suppressNonInspections/before8.java @@ -1,6 +1,6 @@ // "Suppress for method" "false" /** @noinspection UNUSED_SYMBOL*/ class a { -private void run() { +private void run() { } } \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/wrapObjectWithOptional/beforeInvalidTypeParameter.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/wrapObjectWithOptional/beforeInvalidTypeParameter.java index 15519b887d03..4e5ee1757301 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/wrapObjectWithOptional/beforeInvalidTypeParameter.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/wrapObjectWithOptional/beforeInvalidTypeParameter.java @@ -4,7 +4,7 @@ import java.util.Optional; public class Test { void m(String ss) { - f(ss); + f(ss); } diff --git a/java/java-tests/testData/inspection/stringTokenizerDelimiter/beforeConstructorNoDuplicates.java b/java/java-tests/testData/inspection/stringTokenizerDelimiter/beforeConstructorNoDuplicates.java index 69d4839095d8..c435409bbb78 100644 --- a/java/java-tests/testData/inspection/stringTokenizerDelimiter/beforeConstructorNoDuplicates.java +++ b/java/java-tests/testData/inspection/stringTokenizerDelimiter/beforeConstructorNoDuplicates.java @@ -5,7 +5,7 @@ class A { void m() { - new StringTokenizer("asd", "\\\t\nqwerty!#2@$") + new StringTokenizer("asd", "\\\t\nqwerty!#2@$") } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/SuppressLocalInspectionTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/SuppressLocalInspectionTest.java index 8a992b117ff3..2e6c19f0a67f 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/SuppressLocalInspectionTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/SuppressLocalInspectionTest.java @@ -1,8 +1,23 @@ +/* + * Copyright 2000-2016 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 com.intellij.codeInsight.daemon.quickFix; import com.intellij.codeInspection.LocalInspectionTool; import com.intellij.codeInspection.localCanBeFinal.LocalCanBeFinal; -import com.intellij.openapi.roots.LanguageLevelProjectExtension; +import com.intellij.codeInspection.varScopeCanBeNarrowed.FieldCanBeLocalInspection; import com.intellij.pom.java.LanguageLevel; import org.jetbrains.annotations.NotNull; @@ -15,7 +30,7 @@ public class SuppressLocalInspectionTest extends LightQuickFixParameterizedTestC @NotNull @Override protected LocalInspectionTool[] configureLocalInspectionTools() { - return new LocalInspectionTool[]{new LocalCanBeFinal()}; + return new LocalInspectionTool[]{new LocalCanBeFinal(), new FieldCanBeLocalInspection()}; } public void test() throws Exception { doAllTests(); } diff --git a/java/testFramework/src/com/intellij/codeInsight/daemon/quickFix/LightQuickFixTestCase.java b/java/testFramework/src/com/intellij/codeInsight/daemon/quickFix/LightQuickFixTestCase.java index db33c980ab61..60456ec77ef9 100644 --- a/java/testFramework/src/com/intellij/codeInsight/daemon/quickFix/LightQuickFixTestCase.java +++ b/java/testFramework/src/com/intellij/codeInsight/daemon/quickFix/LightQuickFixTestCase.java @@ -336,7 +336,7 @@ public abstract class LightQuickFixTestCase extends LightDaemonAnalyzerTestCase @Override public void configureFromFileText(String name, String contents) throws IOException { - LightPlatformCodeInsightTestCase.configureFromFileText(name, contents); + LightPlatformCodeInsightTestCase.configureFromFileText(name, contents, true); } @Override diff --git a/platform/testFramework/src/com/intellij/testFramework/EditorTestUtil.java b/platform/testFramework/src/com/intellij/testFramework/EditorTestUtil.java index 8005327e8149..5f8310e103b7 100644 --- a/platform/testFramework/src/com/intellij/testFramework/EditorTestUtil.java +++ b/platform/testFramework/src/com/intellij/testFramework/EditorTestUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -474,6 +474,18 @@ public class EditorTestUtil { this.carets = carets; this.blockSelection = blockSelection; } + + /** + * Returns true if current CaretAndSelectionState contains at least one caret or selection explicitly specified + */ + public boolean hasExplicitCaret() { + if(carets.isEmpty()) return false; + if(blockSelection == null && carets.size() == 1) { + CaretInfo caret = carets.get(0); + return caret.position != null || caret.selection != null; + } + return true; + } } public static class CaretInfo { diff --git a/platform/testFramework/src/com/intellij/testFramework/LightPlatformCodeInsightTestCase.java b/platform/testFramework/src/com/intellij/testFramework/LightPlatformCodeInsightTestCase.java index e133b0146a59..2a35ff76b0e6 100644 --- a/platform/testFramework/src/com/intellij/testFramework/LightPlatformCodeInsightTestCase.java +++ b/platform/testFramework/src/com/intellij/testFramework/LightPlatformCodeInsightTestCase.java @@ -146,12 +146,28 @@ public abstract class LightPlatformCodeInsightTestCase extends LightPlatformTest */ @NotNull protected static Document configureFromFileText(@NonNls @NotNull final String fileName, @NonNls @NotNull final String fileText) { + return configureFromFileText(fileName, fileText, false); + } + + /** + * Same as configureByFile but text is provided directly. + * @param fileName - name of the file. + * @param fileText - data file text. + * @param checkCaret - if true, if will be verified that file contains at least one caret or selection marker + */ + @NotNull + protected static Document configureFromFileText(@NonNls @NotNull final String fileName, + @NonNls @NotNull final String fileText, + boolean checkCaret) { return new WriteCommandAction(null) { @Override protected void run(@NotNull Result result) throws Throwable { final Document fakeDocument = new DocumentImpl(fileText); EditorTestUtil.CaretAndSelectionState caretsState = EditorTestUtil.extractCaretAndSelectionMarkers(fakeDocument); + if(checkCaret) { + assertTrue("No caret specified in " + fileName, caretsState.hasExplicitCaret()); + } String newFileText = fakeDocument.getText(); Document document; From e4486ad1d7590828bf6d67c421f2cf4df68a319e Mon Sep 17 00:00:00 2001 From: Sergey Simonchik Date: Fri, 7 Oct 2016 13:04:59 +0300 Subject: [PATCH 22/43] revert "hidpi: scale StyleSheet font sizes for better rendering of "font-size: medium|xx-small|x-small|small|large|x-large|xx-large"" as it messes with quick doc --- .../util/src/com/intellij/util/ui/UIUtil.java | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/platform/util/src/com/intellij/util/ui/UIUtil.java b/platform/util/src/com/intellij/util/ui/UIUtil.java index 4390e42ba525..93dad49bf6b5 100644 --- a/platform/util/src/com/intellij/util/ui/UIUtil.java +++ b/platform/util/src/com/intellij/util/ui/UIUtil.java @@ -54,7 +54,6 @@ import javax.swing.plaf.basic.BasicComboBoxUI; import javax.swing.plaf.basic.BasicRadioButtonUI; import javax.swing.plaf.basic.ComboPopup; import javax.swing.text.*; -import javax.swing.text.html.HTMLDocument; import javax.swing.text.html.HTMLEditorKit; import javax.swing.text.html.StyleSheet; import javax.swing.undo.UndoManager; @@ -2370,19 +2369,8 @@ public class UIUtil { final StyleSheet style = new StyleSheet(); style.addStyleSheet(isUnderDarcula() ? (StyleSheet)UIManager.getDefaults().get("StyledEditorKit.JBDefaultStyle") : DEFAULT_HTML_KIT_CSS); style.addRule(customCss); - scaleStyleSheetFontSize(style, size); return new HTMLEditorKit() { - - @Override - public Document createDefaultDocument() { - Document document = super.createDefaultDocument(); - if (document instanceof HTMLDocument) { - scaleStyleSheetFontSize(((HTMLDocument)document).getStyleSheet(), size); - } - return document; - } - @Override public StyleSheet getStyleSheet() { return style; @@ -2390,14 +2378,6 @@ public class UIUtil { }; } - private static void scaleStyleSheetFontSize(@Nullable StyleSheet styleSheet, int bodyFontSize) { - // In compliance with javax.swing.text.html.StyleSheet logic, where 14pt font size is specified in - // javax/swing/text/html/default.css and javax.swing.text.html.StyleSheet.sizeMapDefault[3]. - if (styleSheet != null) { - styleSheet.addRule("BASE_SIZE " + bodyFontSize); - } - } - public static void removeScrollBorder(final Component c) { for (JScrollPane scrollPane : uiTraverser(c).filter(JScrollPane.class)) { if (!uiParents(scrollPane, true) From 0cd1407e8eb282e3b08977a2b030a2bf6e0c2f42 Mon Sep 17 00:00:00 2001 From: Eugene Zhuravlev Date: Fri, 7 Oct 2016 12:07:18 +0200 Subject: [PATCH 23/43] unexpected errors logging --- .../org/jetbrains/jps/cmdline/BuildMain.java | 222 +++++++++--------- 1 file changed, 114 insertions(+), 108 deletions(-) diff --git a/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildMain.java b/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildMain.java index c1020cadaa01..610290944fbe 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildMain.java +++ b/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildMain.java @@ -67,128 +67,134 @@ public class BuildMain { @Nullable private static PreloadedData ourPreloadedData; - public static void main(String[] args){ - final long processStart = System.currentTimeMillis(); - final String startMessage = "Build process started. Classpath: " + System.getProperty("java.class.path"); - System.out.println(startMessage); - LOG.info(startMessage); - - final String host = args[HOST_ARG]; - final int port = Integer.parseInt(args[PORT_ARG]); - final UUID sessionId = UUID.fromString(args[SESSION_ID_ARG]); - @SuppressWarnings("ConstantConditions") - final File systemDir = new File(FileUtil.toCanonicalPath(args[SYSTEM_DIR_ARG])); - Utils.setSystemRoot(systemDir); + public static void main(String[] args) throws Throwable{ + try { + final long processStart = System.currentTimeMillis(); + final String startMessage = "Build process started. Classpath: " + System.getProperty("java.class.path"); + System.out.println(startMessage); + LOG.info(startMessage); - final long connectStart = System.currentTimeMillis(); - // IDEA-123132, let's try again - for (int attempt = 0; attempt < 3; attempt++) { - try { - ourEventLoopGroup = new NioEventLoopGroup(1, SharedThreadPool.getInstance()); - break; - } - catch (IllegalStateException e) { - if (attempt == 2) { - printErrorAndExit(host, port, e); - return; - } - else { - LOG.warn("Cannot create event loop, attempt #" + attempt, e); - try { - //noinspection BusyWait - Thread.sleep(10 * (attempt + 1)); - } - catch (InterruptedException ignored) { - } - } - } - } + final String host = args[HOST_ARG]; + final int port = Integer.parseInt(args[PORT_ARG]); + final UUID sessionId = UUID.fromString(args[SESSION_ID_ARG]); + @SuppressWarnings("ConstantConditions") + final File systemDir = new File(FileUtil.toCanonicalPath(args[SYSTEM_DIR_ARG])); + Utils.setSystemRoot(systemDir); - final Bootstrap bootstrap = new Bootstrap().group(ourEventLoopGroup).channel(NioSocketChannel.class).handler(new ChannelInitializer() { - @Override - protected void initChannel(Channel channel) throws Exception { - channel.pipeline().addLast(new ProtobufVarint32FrameDecoder(), - new ProtobufDecoder(CmdlineRemoteProto.Message.getDefaultInstance()), - new ProtobufVarint32LengthFieldPrepender(), - new ProtobufEncoder(), - new MyMessageHandler(sessionId)); - } - }).option(ChannelOption.TCP_NODELAY, true).option(ChannelOption.SO_KEEPALIVE, true); - - final ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port)).awaitUninterruptibly(); - - - final boolean success = future.isSuccess(); - if (success) { - LOG.info("Connection to IDE established in " + (System.currentTimeMillis() - connectStart) + " ms"); - - final String projectPathToPreload = System.getProperty(PRELOAD_PROJECT_PATH, null); - final String globalsPathToPreload = System.getProperty(PRELOAD_CONFIG_PATH, null); - if (projectPathToPreload != null && globalsPathToPreload != null) { - final PreloadedData data = new PreloadedData(); - ourPreloadedData = data; + final long connectStart = System.currentTimeMillis(); + // IDEA-123132, let's try again + for (int attempt = 0; attempt < 3; attempt++) { try { - FileSystemUtil.getAttributes(projectPathToPreload); // this will pre-load all FS optimizations - - final BuildRunner runner = new BuildRunner(new JpsModelLoaderImpl(projectPathToPreload, globalsPathToPreload, null)); - data.setRunner(runner); - - final File dataStorageRoot = Utils.getDataStorageRoot(projectPathToPreload); - final BuildFSState fsState = new BuildFSState(false); - final ProjectDescriptor pd = runner.load(new MessageHandler() { - @Override - public void processMessage(BuildMessage msg) { - data.addMessage(msg); - } - }, dataStorageRoot, fsState); - data.setProjectDescriptor(pd); - - try { - final File fsStateFile = new File(dataStorageRoot, BuildSession.FS_STATE_FILE); - final DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream(fsStateFile))); + ourEventLoopGroup = new NioEventLoopGroup(1, SharedThreadPool.getInstance()); + break; + } + catch (IllegalStateException e) { + if (attempt == 2) { + printErrorAndExit(host, port, e); + return; + } + else { + LOG.warn("Cannot create event loop, attempt #" + attempt, e); try { - final int version = in.readInt(); - if (version == BuildFSState.VERSION) { - final long savedOrdinal = in.readLong(); - final boolean hasWorkToDo = in.readBoolean();// must skip "has-work-to-do" flag - fsState.load(in, pd.getModel(), pd.getBuildRootIndex()); - data.setFsEventOrdinal(savedOrdinal); - data.setHasHasWorkToDo(hasWorkToDo); + //noinspection BusyWait + Thread.sleep(10 * (attempt + 1)); + } + catch (InterruptedException ignored) { + } + } + } + } + + final Bootstrap bootstrap = new Bootstrap().group(ourEventLoopGroup).channel(NioSocketChannel.class).handler(new ChannelInitializer() { + @Override + protected void initChannel(Channel channel) throws Exception { + channel.pipeline().addLast(new ProtobufVarint32FrameDecoder(), + new ProtobufDecoder(CmdlineRemoteProto.Message.getDefaultInstance()), + new ProtobufVarint32LengthFieldPrepender(), + new ProtobufEncoder(), + new MyMessageHandler(sessionId)); + } + }).option(ChannelOption.TCP_NODELAY, true).option(ChannelOption.SO_KEEPALIVE, true); + + final ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port)).awaitUninterruptibly(); + + + final boolean success = future.isSuccess(); + if (success) { + LOG.info("Connection to IDE established in " + (System.currentTimeMillis() - connectStart) + " ms"); + + final String projectPathToPreload = System.getProperty(PRELOAD_PROJECT_PATH, null); + final String globalsPathToPreload = System.getProperty(PRELOAD_CONFIG_PATH, null); + if (projectPathToPreload != null && globalsPathToPreload != null) { + final PreloadedData data = new PreloadedData(); + ourPreloadedData = data; + try { + FileSystemUtil.getAttributes(projectPathToPreload); // this will pre-load all FS optimizations + + final BuildRunner runner = new BuildRunner(new JpsModelLoaderImpl(projectPathToPreload, globalsPathToPreload, null)); + data.setRunner(runner); + + final File dataStorageRoot = Utils.getDataStorageRoot(projectPathToPreload); + final BuildFSState fsState = new BuildFSState(false); + final ProjectDescriptor pd = runner.load(new MessageHandler() { + @Override + public void processMessage(BuildMessage msg) { + data.addMessage(msg); + } + }, dataStorageRoot, fsState); + data.setProjectDescriptor(pd); + + try { + final File fsStateFile = new File(dataStorageRoot, BuildSession.FS_STATE_FILE); + final DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream(fsStateFile))); + try { + final int version = in.readInt(); + if (version == BuildFSState.VERSION) { + final long savedOrdinal = in.readLong(); + final boolean hasWorkToDo = in.readBoolean();// must skip "has-work-to-do" flag + fsState.load(in, pd.getModel(), pd.getBuildRootIndex()); + data.setFsEventOrdinal(savedOrdinal); + data.setHasHasWorkToDo(hasWorkToDo); + } + } + finally { + in.close(); } } - finally { - in.close(); + catch (FileNotFoundException ignored) { + } + catch (IOException e) { + LOG.info("Error pre-loading FS state", e); + fsState.clearAll(); } - } - catch (FileNotFoundException ignored) { - } - catch (IOException e) { - LOG.info("Error pre-loading FS state", e); - fsState.clearAll(); - } - // preloading target configurations - final BuildTargetsState targetsState = pd.getTargetsState(); - for (BuildTarget target : pd.getBuildTargetIndex().getAllTargets()) { - targetsState.getTargetConfiguration(target); + // preloading target configurations + final BuildTargetsState targetsState = pd.getTargetsState(); + for (BuildTarget target : pd.getBuildTargetIndex().getAllTargets()) { + targetsState.getTargetConfiguration(target); + } + + BuilderRegistry.getInstance(); + + LOG.info("Pre-loaded process ready in " + (System.currentTimeMillis() - processStart) + " ms"); + } + catch (Throwable e) { + LOG.info("Failed to pre-load project " + projectPathToPreload, e); + // just failed to preload the project, the situation will be handled later, when real build starts } - - BuilderRegistry.getInstance(); - - LOG.info("Pre-loaded process ready in " + (System.currentTimeMillis() - processStart) + " ms"); } - catch (Throwable e) { - LOG.info("Failed to pre-load project " + projectPathToPreload, e); - // just failed to preload the project, the situation will be handled later, when real build starts + else if (projectPathToPreload != null || globalsPathToPreload != null){ + LOG.info("Skipping project pre-loading step: both paths to project configuration files and path to global settings must be specified"); } + future.channel().writeAndFlush(CmdlineProtoUtil.toMessage(sessionId, CmdlineProtoUtil.createParamRequest())); } - else if (projectPathToPreload != null || globalsPathToPreload != null){ - LOG.info("Skipping project pre-loading step: both paths to project configuration files and path to global settings must be specified"); + else { + printErrorAndExit(host, port, future.cause()); } - future.channel().writeAndFlush(CmdlineProtoUtil.toMessage(sessionId, CmdlineProtoUtil.createParamRequest())); } - else { - printErrorAndExit(host, port, future.cause()); + catch (Throwable e) { + LOG.error(e); + throw e; } } From 2c3307e2b9bffc12cb3b9e0bc3a87916a7a26129 Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 7 Oct 2016 11:12:02 +0200 Subject: [PATCH 24/43] IDEA-161588 If you paste code with statically imported ambiguous methods copied from another Java file, IDEA should remember the "static imports" and add them automatically like it does for classes honor even more settings (IDEA-CR-14253) --- .../daemon/impl/quickfix/ImportClassFixBase.java | 12 +++++++----- .../daemon/impl/quickfix/StaticImportMemberFix.java | 10 +++++----- .../JavaCopyPasteReferenceProcessor.java | 7 +++---- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ImportClassFixBase.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ImportClassFixBase.java index 12d6646f9a2f..f32fc4a2a012 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ImportClassFixBase.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ImportClassFixBase.java @@ -34,7 +34,6 @@ import com.intellij.openapi.application.impl.LaterInvocator; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.text.StringUtil; @@ -46,7 +45,6 @@ import com.intellij.psi.search.PsiShortNamesCache; import com.intellij.psi.util.FileTypeUtils; import com.intellij.psi.util.InheritanceUtil; import com.intellij.psi.util.PsiUtil; -import com.intellij.util.Processor; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.HashSet; import gnu.trove.THashSet; @@ -326,9 +324,7 @@ public abstract class ImportClassFixBase implements Inte final QuestionAction action = createQuestionAction(candidates, element.getProject(), editor); PsiFile psiFile = element.getContainingFile(); if (candidates.size() == 1 && - (FileTypeUtils.isInServerPageFile(psiFile) ? - CodeInsightSettings.getInstance().JSP_ADD_UNAMBIGIOUS_IMPORTS_ON_THE_FLY : - CodeInsightSettings.getInstance().ADD_UNAMBIGIOUS_IMPORTS_ON_THE_FLY) && + ImportClassFixBase.isAddUnambiguousImportsOnTheFlyEnabled(psiFile) && (ApplicationManager.getApplication().isUnitTestMode() || DaemonListeners.canChangeFileSilently(psiFile)) && !LaterInvocator.isInModalContext()) { CommandProcessor.getInstance().runUndoTransparentAction(() -> action.execute()); diff --git a/java/java-impl/src/com/intellij/codeInsight/editorActions/JavaCopyPasteReferenceProcessor.java b/java/java-impl/src/com/intellij/codeInsight/editorActions/JavaCopyPasteReferenceProcessor.java index d7dea455820a..98689fab9dd9 100644 --- a/java/java-impl/src/com/intellij/codeInsight/editorActions/JavaCopyPasteReferenceProcessor.java +++ b/java/java-impl/src/com/intellij/codeInsight/editorActions/JavaCopyPasteReferenceProcessor.java @@ -17,6 +17,7 @@ package com.intellij.codeInsight.editorActions; import com.intellij.codeInsight.CodeInsightSettings; import com.intellij.codeInsight.daemon.impl.quickfix.ImportClassFix; +import com.intellij.codeInsight.daemon.impl.quickfix.ImportClassFixBase; import com.intellij.codeInsight.daemon.impl.quickfix.StaticImportConstantFix; import com.intellij.codeInsight.daemon.impl.quickfix.StaticImportMethodFix; import com.intellij.openapi.diagnostic.Logger; @@ -106,8 +107,7 @@ public class JavaCopyPasteReferenceProcessor extends CopyPasteReferenceProcessor } } - if (CodeInsightSettings.getInstance().ADD_UNAMBIGIOUS_IMPORTS_ON_THE_FLY || - CodeInsightSettings.getInstance().ADD_MEMBER_IMPORTS_ON_THE_FLY) { + if (ImportClassFixBase.isAddUnambiguousImportsOnTheFlyEnabled(file)) { for (int i = 0; i < refs.length; i++) { if (isUnambiguous(refs[i])) { refs[i] = null; @@ -131,8 +131,7 @@ public class JavaCopyPasteReferenceProcessor extends CopyPasteReferenceProcessor int classCount = new ImportClassFix(ref).getClassesToImport().size(); if (constCount + classCount > 1) return false; if (constCount + classCount == 0) return true; - return constCount == 1 ? CodeInsightSettings.getInstance().ADD_MEMBER_IMPORTS_ON_THE_FLY - : CodeInsightSettings.getInstance().ADD_UNAMBIGIOUS_IMPORTS_ON_THE_FLY; + return constCount == 0 || CodeInsightSettings.getInstance().ADD_MEMBER_IMPORTS_ON_THE_FLY; } @Override From 6b3e2fc6db1b3f96fdf52dcb8a32bfb87fee9e77 Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 7 Oct 2016 12:31:11 +0200 Subject: [PATCH 25/43] java dfa: rewrite "try "treatment to visit finally on break/continue control transfers (IDEA-55394, IDEA-156394) --- .../dataFlow/ControlFlowAnalyzer.java | 355 ++++-------------- .../dataFlow/DfaMemoryStateImpl.java | 6 +- .../dataFlow/controlTransfer.kt | 125 ++++++ .../instructions/ReturnInstruction.java | 19 +- .../dataFlow/value/DfaValueFactory.java | 24 +- .../fixture/BreakContinueViaFinally.java | 31 ++ .../DataFlowInspectionTest.java | 1 + .../com/intellij/util/containers/FList.java | 11 + 8 files changed, 267 insertions(+), 305 deletions(-) create mode 100644 java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/controlTransfer.kt create mode 100644 java/java-tests/testData/inspection/dataFlow/fixture/BreakContinueViaFinally.java diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java index 2a356bd66862..79cb175e2668 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java @@ -28,8 +28,7 @@ import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.*; import com.intellij.util.IncorrectOperationException; import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.containers.FactoryMap; -import com.intellij.util.containers.Stack; +import com.intellij.util.containers.FList; import com.siyeh.ig.numeric.UnnecessaryExplicitNumericCastInspection; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; @@ -50,18 +49,11 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { private final DfaValueFactory myFactory; private ControlFlow myCurrentFlow; - private Stack myCatchStack; - private final DfaValue myRuntimeException; - private final DfaValue myError; - private final DfaValue myString; + private FList myTrapStack = FList.emptyList(); + private final ExceptionTransfer myRuntimeException; + private final ExceptionTransfer myError; private final PsiType myNpe; private final PsiType myAssertionError; - private final Stack myElementStack = new Stack<>(); - - /** - * Variables for try-related control transfers. Contain exceptions or an (Throwable-inconvertible) string to indicate return inside finally - */ - private FactoryMap myExceptionHolders; ControlFlowAnalyzer(final DfaValueFactory valueFactory, @NotNull PsiElement codeFragment, boolean ignoreAssertions) { myFactory = valueFactory; @@ -69,26 +61,14 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { myProject = codeFragment.getProject(); myIgnoreAssertions = ignoreAssertions; GlobalSearchScope scope = codeFragment.getResolveScope(); - myRuntimeException = myFactory.createTypeValue(createClassType(scope, JAVA_LANG_RUNTIME_EXCEPTION), Nullness.NOT_NULL); - myError = myFactory.createTypeValue(createClassType(scope, JAVA_LANG_ERROR), Nullness.NOT_NULL); + myRuntimeException = new ExceptionTransfer(myFactory.createTypeValue(createClassType(scope, JAVA_LANG_RUNTIME_EXCEPTION), Nullness.NOT_NULL)); + myError = new ExceptionTransfer(myFactory.createTypeValue(createClassType(scope, JAVA_LANG_ERROR), Nullness.NOT_NULL)); myNpe = createClassType(scope, JAVA_LANG_NULL_POINTER_EXCEPTION); myAssertionError = createClassType(scope, JAVA_LANG_ASSERTION_ERROR); - myString = myFactory.createTypeValue(createClassType(scope, JAVA_LANG_STRING), Nullness.NOT_NULL); - - myExceptionHolders = new FactoryMap() { - @Nullable - @Override - protected DfaVariableValue create(PsiTryStatement key) { - String text = "java.lang.Object $exception" + myExceptionHolders.size() + "$"; - PsiParameter mockVar = JavaPsiFacade.getElementFactory(myProject).createParameterFromText(text, null); - return myFactory.getVarFactory().createVariableValue(mockVar, false); - } - }; } @Nullable public ControlFlow buildControlFlow() { - myCatchStack = new Stack<>(); myCurrentFlow = new ControlFlow(myFactory); try { myCodeFragment.accept(this); @@ -104,7 +84,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { addInstruction(new CheckReturnValueInstruction(myCodeFragment)); } - addInstruction(new ReturnInstruction(false, null)); + addInstruction(new ReturnInstruction(myFactory.controlTransfer(ReturnTransfer.INSTANCE, FList.emptyList()), null)); if (Registry.is("idea.dfa.live.variables.analysis")) { new LiveVariablesAnalyzer(myCurrentFlow, myFactory).flushDeadVariablesOnStatementFinish(); @@ -135,15 +115,10 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { private void startElement(PsiElement element) { myCurrentFlow.startElement(element); - myElementStack.push(element); } private void finishElement(PsiElement element) { myCurrentFlow.finishElement(element); - PsiElement popped = myElementStack.pop(); - if (element != popped) { - throw new AssertionError("Expected " + element + ", popped " + popped); - } if (element instanceof PsiStatement && !(element instanceof PsiReturnStatement)) { addInstruction(new FinishElementInstruction(element)); } @@ -250,9 +225,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { description.accept(this); } - CatchDescriptor cd = findNextCatch(false); - initException(myAssertionError, cd); - addThrowCode(cd, statement); + throwException(myAssertionError, statement); } finishElement(statement); } @@ -369,20 +342,36 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { PsiStatement exitedStatement = statement.findExitedStatement(); if (exitedStatement != null) { - flushVariablesOnControlTransfer(exitedStatement); - addInstruction(new GotoInstruction(getEndOffset(exitedStatement))); + controlTransfer(new InstructionTransfer(getEndOffset(exitedStatement), getVariablesInside(exitedStatement)), + getTrapsInsideStatement(exitedStatement)); } finishElement(statement); } + private void controlTransfer(InstructionTransfer target, FList traps) { + addInstruction(new ControlTransferInstruction(myFactory.controlTransfer(target, traps))); + } + + @NotNull + private FList getTrapsInsideStatement(PsiStatement statement) { + return FList.createFromReversed(ContainerUtil.reverse( + ContainerUtil.findAll(myTrapStack, cd -> PsiTreeUtil.isAncestor(statement, cd.getAnchor(), true)))); + } + + @NotNull + private List getVariablesInside(PsiElement exitedStatement) { + return ContainerUtil.map(PsiTreeUtil.findChildrenOfType(exitedStatement, PsiVariable.class), + var -> myFactory.getVarFactory().createVariableValue(var, false)); + } + @Override public void visitContinueStatement(PsiContinueStatement statement) { startElement(statement); PsiStatement continuedStatement = statement.findContinuedStatement(); if (continuedStatement instanceof PsiLoopStatement) { PsiStatement body = ((PsiLoopStatement)continuedStatement).getBody(); - flushVariablesOnControlTransfer(body); - addInstruction(new GotoInstruction(getEndOffset(body))); + controlTransfer(new InstructionTransfer(getEndOffset(body), getVariablesInside(body)), getTrapsInsideStatement(body)); + } else { addInstruction(new EmptyInstruction(null)); } @@ -590,24 +579,10 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { addInstruction(new CheckReturnValueInstruction(returnValue)); } - returnCheckingFinally(false, statement); + addInstruction(new ReturnInstruction(myFactory.controlTransfer(ReturnTransfer.INSTANCE, myTrapStack), statement)); finishElement(statement); } - private void returnCheckingFinally(boolean viaException, @NotNull PsiElement anchor) { - CatchDescriptor finallyDescriptor = findFinally(); - if (finallyDescriptor != null) { - addInstruction(new PushInstruction(getExceptionHolder(finallyDescriptor), null)); - addInstruction(new PushInstruction(myString, null)); - addInstruction(new AssignInstruction(null, null)); - addInstruction(new PopInstruction()); - - addInstruction(new GotoInstruction(finallyDescriptor.getJumpOffset(this))); - } else { - addInstruction(new ReturnInstruction(viaException, anchor)); - } - } - @Override public void visitSwitchLabelStatement(PsiSwitchLabelStatement statement) { startElement(statement); finishElement(statement); @@ -737,13 +712,6 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { if (exception != null) { exception.accept(this); - CatchDescriptor cd = findNextCatch(false); - if (cd == null) { - addInstruction(new FieldReferenceInstruction(exception, "thrown exception")); - addInstruction(new ReturnInstruction(true, statement)); - finishElement(statement); - return; - } addConditionalRuntimeThrow(); addInstruction(new DupInstruction()); @@ -753,105 +721,32 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { addInstruction(gotoInstruction); addInstruction(new FieldReferenceInstruction(exception, "thrown exception")); - initException(myNpe, cd); - addThrowCode(cd, statement); + throwException(myNpe, statement); gotoInstruction.setOffset(myCurrentFlow.getInstructionCount()); - addInstruction(new PushInstruction(getExceptionHolder(cd), null)); - addInstruction(new SwapInstruction()); - addInstruction(new AssignInstruction(null, null)); - addInstruction(new PopInstruction()); - addThrowCode(cd, statement); + throwException(exception.getType(), statement); } finishElement(statement); } private void addConditionalRuntimeThrow() { - CatchDescriptor cd = findNextCatch(false); - if (cd == null) { + if (myTrapStack.isEmpty()) { return; } pushUnknown(); final ConditionalGotoInstruction ifNoException = addInstruction(new ConditionalGotoInstruction(null, false, null)); - addInstruction(new EmptyStackInstruction()); - - addInstruction(new PushInstruction(getExceptionHolder(cd), null)); pushUnknown(); final ConditionalGotoInstruction ifError = addInstruction(new ConditionalGotoInstruction(null, false, null)); - addInstruction(new PushInstruction(myRuntimeException, null)); - GotoInstruction ifRuntime = addInstruction(new GotoInstruction(null)); + throwException(myRuntimeException, null); ifError.setOffset(myCurrentFlow.getInstructionCount()); - addInstruction(new PushInstruction(myError, null)); - ifRuntime.setOffset(myCurrentFlow.getInstructionCount()); - - addInstruction(new AssignInstruction(null, null)); - addInstruction(new PopInstruction()); - - addThrowCode(cd, null); + throwException(myError, null); ifNoException.setOffset(myCurrentFlow.getInstructionCount()); } - private void flushVariablesOnControlTransfer(PsiElement stopWhenAncestorOf) { - for (int i = myElementStack.size() - 1; i >= 0; i--) { - PsiElement scope = myElementStack.get(i); - if (PsiTreeUtil.isAncestor(scope, stopWhenAncestorOf, true)) { - break; - } - if (scope instanceof PsiCodeBlock) { - flushCodeBlockVariables((PsiCodeBlock)scope); - } - } - } - - // the exception object should be in $exception$ variable - private void addThrowCode(@Nullable CatchDescriptor cd, @Nullable PsiElement explicitThrower) { - if (cd == null) { - addInstruction(new ReturnInstruction(true, explicitThrower)); - return; - } - - flushVariablesOnControlTransfer(cd.getBlock()); - addInstruction(new GotoInstruction(cd.getJumpOffset(this))); - } - - @Nullable - private CatchDescriptor findNextCatch(boolean catchRethrow) { - if (myCatchStack.isEmpty()) { - return null; - } - - PsiElement currentElement = myElementStack.peek(); - - CatchDescriptor cd = myCatchStack.get(myCatchStack.size() - 1); - if (!cd.isFinally() && PsiTreeUtil.isAncestor(cd.getBlock().getParent(), currentElement, false)) { - int i = myCatchStack.size() - 2; - while (!catchRethrow && i >= 0 && !myCatchStack.get(i).isFinally() && myCatchStack.get(i).getTryStatement() == cd.getTryStatement()) { - i--; - } - if (i < 0) { - return null; - } - cd = myCatchStack.get(i); - } - - return cd; - } - - - @Nullable - private CatchDescriptor findFinally() { - for (int i = myCatchStack.size() - 1; i >= 0; i--) { - CatchDescriptor cd = myCatchStack.get(i); - if (cd.isFinally()) return cd; - } - - return null; - } - private static class ApplyNotNullInstruction extends Instruction { private final PsiMethodCallExpression myCall; @@ -874,50 +769,6 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { } } - private static class CatchDescriptor { - private final PsiType myType; - private final PsiParameter myParameter; - private final PsiCodeBlock myBlock; - private final boolean myIsFinally; - - public CatchDescriptor(PsiCodeBlock finallyBlock) { - myType = null; - myParameter = null; - myBlock = finallyBlock; - myIsFinally = true; - } - - public CatchDescriptor(PsiParameter parameter, PsiCodeBlock catchBlock) { - myType = parameter.getType(); - myParameter = parameter; - myBlock = catchBlock; - myIsFinally = false; - } - - public PsiCodeBlock getBlock() { - return myBlock; - } - public PsiTryStatement getTryStatement() { - return (PsiTryStatement) (isFinally() ? myBlock.getParent() : myBlock.getParent().getParent()); - } - - public PsiType getType() { - return myType; - } - - public boolean isFinally() { - return myIsFinally; - } - - public ControlFlow.ControlFlowOffset getJumpOffset(ControlFlowAnalyzer analyzer) { - return analyzer.getStartOffset(isFinally() ? myBlock : myBlock.getParent()); - } - - public PsiParameter getParameter() { - return myParameter; - } - } - @Override public void visitTryStatement(PsiTryStatement statement) { startElement(statement); @@ -926,27 +777,23 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { PsiCodeBlock tryBlock = statement.getTryBlock(); PsiCodeBlock finallyBlock = statement.getFinallyBlock(); - if (finallyBlock != null) { - myCatchStack.push(new CatchDescriptor(finallyBlock)); + Trap.TryFinally finallyDescriptor = finallyBlock != null ? new Trap.TryFinally(finallyBlock, getStartOffset(finallyBlock)) : null; + if (finallyDescriptor != null) { + myTrapStack = myTrapStack.prepend(finallyDescriptor); } PsiCatchSection[] sections = statement.getCatchSections(); - for (int i = sections.length - 1; i >= 0; i--) { - PsiCatchSection section = sections[i]; - PsiCodeBlock catchBlock = section.getCatchBlock(); - PsiParameter parameter = section.getParameter(); - if (parameter != null && catchBlock != null) { - PsiType type = parameter.getType(); - if (type instanceof PsiClassType || type instanceof PsiDisjunctionType) { - myCatchStack.push(new CatchDescriptor(parameter, catchBlock)); - continue; + if (sections.length > 0) { + LinkedHashMap clauses = new LinkedHashMap<>(); + for (PsiCatchSection section : sections) { + PsiCodeBlock catchBlock = section.getCatchBlock(); + if (catchBlock != null) { + clauses.put(section, getStartOffset(catchBlock)); } } - throw new CannotAnalyzeException(); + myTrapStack = myTrapStack.prepend(new Trap.TryCatch(statement, clauses)); } - ControlFlow.ControlFlowOffset endOffset = finallyBlock == null ? getEndOffset(statement) : getStartOffset(finallyBlock); - if (resourceList != null) { resourceList.accept(this); } @@ -955,79 +802,37 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { tryBlock.accept(this); } - addInstruction(new GotoInstruction(endOffset)); + InstructionTransfer gotoEnd = new InstructionTransfer(getEndOffset(statement), getVariablesInside(tryBlock)); + FList singleFinally = FList.createFromReversed(ContainerUtil.createMaybeSingletonList(finallyDescriptor)); + controlTransfer(gotoEnd, singleFinally); + + if (sections.length > 0) { + assert myTrapStack.getHead() instanceof Trap.TryCatch; + myTrapStack = myTrapStack.getTail(); + } for (PsiCatchSection section : sections) { - section.accept(this); - addInstruction(new GotoInstruction(endOffset)); - myCatchStack.pop(); + PsiCodeBlock catchBlock = section.getCatchBlock(); + if (catchBlock != null) { + visitCodeBlock(catchBlock); + } + controlTransfer(gotoEnd, singleFinally); } if (finallyBlock != null) { - CatchDescriptor finallyDescriptor = myCatchStack.pop(); + assert myTrapStack.getHead() instanceof Trap.TryFinally; + myTrapStack = myTrapStack.getTail().prepend(new Trap.InsideFinally(finallyBlock)); + finallyBlock.accept(this); - - //if $exception$==null => continue normal execution - addInstruction(new PushInstruction(getExceptionHolder(finallyDescriptor), null)); - addInstruction(new PushInstruction(myFactory.getConstFactory().getNull(), null)); - addInstruction(new BinopInstruction(JavaTokenType.EQEQ, null, myProject)); - addInstruction(new ConditionalGotoInstruction(getEndOffset(statement), false, null)); - - // else throw $exception$ - rethrowException(finallyDescriptor, false); + addInstruction(new ControlTransferInstruction(null)); // DfaControlTransferValue is on stack + + assert myTrapStack.getHead() instanceof Trap.InsideFinally; + myTrapStack = myTrapStack.getTail(); } finishElement(statement); } - @Override - public void visitCatchSection(PsiCatchSection section) { - startElement(section); - PsiCodeBlock catchBlock = section.getCatchBlock(); - if (catchBlock != null) { - CatchDescriptor currentDescriptor = new CatchDescriptor(section.getParameter(), catchBlock); - DfaVariableValue exceptionHolder = getExceptionHolder(currentDescriptor); - - // exception is in exceptionHolder mock variable - // check if it's assignable to catch parameter type - PsiType declaredType = section.getCatchType(); - List flattened = declaredType instanceof PsiDisjunctionType ? - ((PsiDisjunctionType)declaredType).getDisjunctions() : - ContainerUtil.createMaybeSingletonList(declaredType); - for (PsiType catchType : flattened) { - addInstruction(new PushInstruction(exceptionHolder, null)); - addInstruction(new PushInstruction(myFactory.createTypeValue(catchType, Nullness.UNKNOWN), null)); - addInstruction(new BinopInstruction(JavaTokenType.INSTANCEOF_KEYWORD, null, myProject)); - addInstruction(new ConditionalGotoInstruction(ControlFlow.deltaOffset(getStartOffset(catchBlock), -5), false, null)); - } - - // not assignable => rethrow - rethrowException(currentDescriptor, true); - - // e = $exception$ - addInstruction(new PushInstruction(myFactory.getVarFactory().createVariableValue(section.getParameter(), false), null)); - addInstruction(new PushInstruction(exceptionHolder, null)); - addInstruction(new AssignInstruction(null, null)); - addInstruction(new PopInstruction()); - - addInstruction(new FlushVariableInstruction(exceptionHolder)); - - catchBlock.accept(this); - } - finishElement(section); - } - - private void rethrowException(CatchDescriptor currentDescriptor, boolean catchRethrow) { - CatchDescriptor nextCatch = findNextCatch(catchRethrow); - if (nextCatch != null) { - addInstruction(new PushInstruction(getExceptionHolder(nextCatch), null, false)); - addInstruction(new PushInstruction(getExceptionHolder(currentDescriptor), null, true)); - addInstruction(new AssignInstruction(null, null)); - addInstruction(new PopInstruction()); - } - addThrowCode(nextCatch, null); - } - @Override public void visitResourceList(PsiResourceList resourceList) { for (PsiResourceListElement resource : resourceList) { @@ -1044,7 +849,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { final List closerExceptions = ExceptionUtil.getCloserExceptions(resource); if (!closerExceptions.isEmpty()) { - addThrows(null, findNextCatch(false), closerExceptions.toArray(new PsiClassType[closerExceptions.size()])); + addThrows(null, closerExceptions.toArray(new PsiClassType[closerExceptions.size()])); } } } @@ -1409,35 +1214,28 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { } private void addMethodThrows(PsiMethod method, @Nullable PsiElement explicitCall) { - CatchDescriptor cd = findNextCatch(false); if (method != null) { - PsiClassType[] refs = method.getThrowsList().getReferencedTypes(); - addThrows(explicitCall, cd, refs); + addThrows(explicitCall, method.getThrowsList().getReferencedTypes()); } } - private void addThrows(@Nullable PsiElement explicitCall, CatchDescriptor cd, PsiClassType[] refs) { + private void addThrows(@Nullable PsiElement explicitCall, PsiClassType[] refs) { for (PsiClassType ref : refs) { pushUnknown(); ConditionalGotoInstruction cond = new ConditionalGotoInstruction(null, false, null); addInstruction(cond); - addInstruction(new EmptyStackInstruction()); - initException(ref, cd); - addThrowCode(cd, explicitCall); + throwException(ref, explicitCall); cond.setOffset(myCurrentFlow.getInstructionCount()); } } - private void initException(PsiType ref, @Nullable CatchDescriptor cd) { - if (cd == null) return; - addInstruction(new PushInstruction(getExceptionHolder(cd), null)); - addInstruction(new PushInstruction(myFactory.createTypeValue(ref, Nullness.NOT_NULL), null)); - addInstruction(new AssignInstruction(null, null)); - addInstruction(new PopInstruction()); + private void throwException(PsiType ref, @Nullable PsiElement anchor) { + throwException(new ExceptionTransfer(myFactory.createTypeValue(ref, Nullness.NOT_NULL)), anchor); } - private DfaVariableValue getExceptionHolder(CatchDescriptor cd) { - return myExceptionHolders.get(cd.getTryStatement()); + private void throwException(ExceptionTransfer kind, @Nullable PsiElement anchor) { + addInstruction(new EmptyStackInstruction()); + addInstruction(new ReturnInstruction(myFactory.controlTransfer(kind, myTrapStack), anchor)); } @Override public void visitMethodCallExpression(PsiMethodCallExpression expression) { @@ -1488,11 +1286,12 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { addInstruction(new BinopInstruction(JavaTokenType.EQEQ, null, myProject)); ConditionalGotoInstruction ifNotFail = new ConditionalGotoInstruction(null, true, null); addInstruction(ifNotFail); - returnCheckingFinally(true, expression); + addInstruction(new ReturnInstruction(myFactory.controlTransfer(new ExceptionTransfer(DfaUnknownValue.getInstance()), myTrapStack), expression)); + ifNotFail.setOffset(myCurrentFlow.getInstructionCount()); } - if (!myCatchStack.isEmpty()) { + if (!myTrapStack.isEmpty()) { addMethodThrows(expression.resolveMethod(), expression); } @@ -1588,7 +1387,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { addConditionalRuntimeThrow(); addInstruction(new MethodCallInstruction(expression, null, constructor == null ? Collections.emptyList() : getMethodContracts(constructor))); - if (!myCatchStack.isEmpty()) { + if (!myTrapStack.isEmpty()) { addMethodThrows(constructor, expression); } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java index 3f8fb818a3fb..6dd56df706fd 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java @@ -237,7 +237,9 @@ public class DfaMemoryStateImpl implements DfaMemoryState { @Override public void emptyStack() { myCachedHash = null; - myStack.clear(); + while (!myStack.isEmpty() && !(myStack.peek() instanceof DfaControlTransferValue)) { + myStack.pop(); + } } @Override @@ -677,7 +679,7 @@ public class DfaMemoryStateImpl implements DfaMemoryState { setVariableState(dfaVar, newState); return true; } - return applyRelation(dfaVar, myFactory.getConstFactory().getNull(), false); + return !getVariableState(dfaVar).isNotNull() && applyRelation(dfaVar, myFactory.getConstFactory().getNull(), false); } if (applyRelation(dfaVar, myFactory.getConstFactory().getNull(), true)) { DfaVariableState newState = getVariableState(dfaVar).withInstanceofValue((DfaTypeValue)dfaRight); diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/controlTransfer.kt b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/controlTransfer.kt new file mode 100644 index 000000000000..c19d8bcb16df --- /dev/null +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/controlTransfer.kt @@ -0,0 +1,125 @@ +/* + * Copyright 2000-2016 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 com.intellij.codeInspection.dataFlow + +import com.intellij.codeInspection.dataFlow.instructions.Instruction +import com.intellij.codeInspection.dataFlow.value.DfaTypeValue +import com.intellij.codeInspection.dataFlow.value.DfaValue +import com.intellij.codeInspection.dataFlow.value.DfaValueFactory +import com.intellij.codeInspection.dataFlow.value.DfaVariableValue +import com.intellij.psi.* +import com.intellij.util.containers.FList +import java.util.* + +/** + * @author peter + */ +class DfaControlTransferValue(factory: DfaValueFactory, + val target: TransferTarget, + val traps: FList) : DfaValue(factory) { + override fun toString() = target.toString() + " " + traps.toString() +} + +interface TransferTarget +data class ExceptionTransfer(val throwable: DfaValue) : TransferTarget +data class InstructionTransfer(val offset: ControlFlow.ControlFlowOffset, val toFlush: List) : TransferTarget +object ReturnTransfer : TransferTarget + +open class ControlTransferInstruction(val transfer: DfaControlTransferValue?) : Instruction() { + override fun accept(runner: DataFlowRunner, state: DfaMemoryState, visitor: InstructionVisitor): Array { + val transferValue = transfer ?: state.pop() as DfaControlTransferValue + return ControlTransferHandler(state, runner, transferValue.target).iteration(transferValue.traps).toTypedArray() + } + + override fun toString() = transfer.toString() +} + +sealed class Trap(val anchor: PsiElement) { + class TryCatch(tryStatement : PsiTryStatement, val clauses: LinkedHashMap): Trap(tryStatement) + class TryFinally(val finallyBlock: PsiCodeBlock, val jumpOffset: ControlFlow.ControlFlowOffset): Trap(finallyBlock) + class InsideFinally(val finallyBlock: PsiCodeBlock): Trap(finallyBlock) +} + +private class ControlTransferHandler(val state: DfaMemoryState, val runner: DataFlowRunner, val target: TransferTarget) { + var throwableState: DfaVariableState? = null + + fun iteration(traps: FList): List { + val (head, tail) = traps.head to traps.tail + return when (head) { + null -> transferToTarget() + is Trap.TryCatch -> if (target is ExceptionTransfer) processCatches(head, target.throwable, tail) else iteration(tail) + is Trap.TryFinally -> goToFinally(head.jumpOffset.instructionOffset, tail) + is Trap.InsideFinally -> leaveFinally(tail) + } + } + + private fun transferToTarget(): List { + return when (target) { + is InstructionTransfer -> { + target.toFlush.forEach { state.flushVariable(it) } + listOf(DfaInstructionState(runner.getInstruction(target.offset.instructionOffset), state)) + } + else -> emptyList() + } + } + + private fun goToFinally(offset: Int, traps: FList): List { + state.push(runner.factory.controlTransfer(target, traps)) + return listOf(DfaInstructionState(runner.getInstruction(offset), state)) + } + + private fun leaveFinally(traps: FList): List { + state.pop() as DfaControlTransferValue + return iteration(traps) + } + + private fun processCatches(tryCatch: Trap.TryCatch, thrownValue: DfaValue, traps: FList): List { + val result = arrayListOf() + for ((catchSection, jumpOffset) in tryCatch.clauses) { + val param = catchSection.parameter ?: continue + if (throwableState == null) throwableState = initVariableState(param, thrownValue) + + for (caughtType in allCaughtTypes(param)) { + throwableState?.withInstanceofValue(caughtType)?.let { varState -> + result.add(DfaInstructionState(runner.getInstruction(jumpOffset.instructionOffset), stateForCatchClause(param, varState))) + } + + throwableState = throwableState?.withNotInstanceofValue(caughtType) ?: return result + } + } + return result + iteration(traps) + } + + private fun allCaughtTypes(param: PsiParameter): List { + val psiTypes = param.type.let { if (it is PsiDisjunctionType) it.disjunctions else listOfNotNull(it) } + return psiTypes.map { runner.factory.createTypeValue(it, Nullness.NOT_NULL) }.filterIsInstance() + } + + private fun stateForCatchClause(param: PsiParameter, varState: DfaVariableState): DfaMemoryState { + val catchingCopy = state.createCopy() as DfaMemoryStateImpl + catchingCopy.setVariableState(catchingCopy.factory.varFactory.createVariableValue(param, false), varState) + return catchingCopy + } + + private fun initVariableState(param: PsiParameter, throwable: DfaValue): DfaVariableState { + val sampleVar = (state as DfaMemoryStateImpl).factory.varFactory.createVariableValue(param, false) + val varState = state.createVariableState(sampleVar).withNullability(Nullness.NOT_NULL) + return if (throwable is DfaTypeValue) varState.withInstanceofValue(throwable)!! else varState + } + +} diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/ReturnInstruction.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/ReturnInstruction.java index 9c67fbe9f5e1..4c84e46ea1fb 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/ReturnInstruction.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/ReturnInstruction.java @@ -26,14 +26,14 @@ package com.intellij.codeInspection.dataFlow.instructions; import com.intellij.codeInspection.dataFlow.*; import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public class ReturnInstruction extends Instruction { - private final boolean isViaException; +public class ReturnInstruction extends ControlTransferInstruction { private final PsiElement myAnchor; - public ReturnInstruction(boolean isViaException, @Nullable PsiElement anchor) { - this.isViaException = isViaException; + public ReturnInstruction(@NotNull DfaControlTransferValue transfer, @Nullable PsiElement anchor) { + super(transfer); myAnchor = anchor; } @@ -43,15 +43,8 @@ public class ReturnInstruction extends Instruction { } public boolean isViaException() { - return isViaException; + DfaControlTransferValue transfer = getTransfer(); + return transfer != null && transfer.getTarget() instanceof ExceptionTransfer; } - @Override - public DfaInstructionState[] accept(DataFlowRunner runner, DfaMemoryState stateBefore, InstructionVisitor visitor) { - return DfaInstructionState.EMPTY_ARRAY; - } - - public String toString() { - return "RETURN"; - } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaValueFactory.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaValueFactory.java index 9a20ae691695..c8e4d60f3d24 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaValueFactory.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaValueFactory.java @@ -24,12 +24,17 @@ */ package com.intellij.codeInspection.dataFlow.value; +import com.intellij.codeInspection.dataFlow.DfaControlTransferValue; import com.intellij.codeInspection.dataFlow.Nullness; +import com.intellij.codeInspection.dataFlow.TransferTarget; +import com.intellij.codeInspection.dataFlow.Trap; import com.intellij.openapi.util.Pair; import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.TypeConversionUtil; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.FList; +import com.intellij.util.containers.FactoryMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -101,18 +106,6 @@ public class DfaValueFactory { return getConstFactory().create(literal); } - @Nullable - public static PsiVariable resolveUnqualifiedVariable(PsiReferenceExpression refExpression) { - if (isEffectivelyUnqualified(refExpression)) { - PsiElement resolved = refExpression.resolve(); - if (resolved instanceof PsiVariable) { - return (PsiVariable)resolved; - } - } - - return null; - } - public static boolean isEffectivelyUnqualified(PsiReferenceExpression refExpression) { PsiExpression qualifier = refExpression.getQualifierExpression(); if (qualifier == null) { @@ -129,6 +122,13 @@ public class DfaValueFactory { return false; } + public DfaControlTransferValue controlTransfer(TransferTarget kind, FList traps) { + return myControlTransfers.get(Pair.create(kind, traps)); + } + + private final Map>, DfaControlTransferValue> myControlTransfers = + FactoryMap.createMap(p -> new DfaControlTransferValue(this, p.first, p.second)); + private final DfaVariableValue.Factory myVarFactory; private final DfaConstValue.Factory myConstFactory; private final DfaBoxedValue.Factory myBoxedFactory; diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/BreakContinueViaFinally.java b/java/java-tests/testData/inspection/dataFlow/fixture/BreakContinueViaFinally.java new file mode 100644 index 000000000000..4fa7915e56b1 --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/BreakContinueViaFinally.java @@ -0,0 +1,31 @@ +class Test { + public void testContinue() { + Object o = null; + for (int i = 0; i < 4; i++) { + try { + if (o == null) { + System.out.println("hello"); + continue; + } + System.out.println("fred"); + } finally { + o = ""; + } + } + } + + public void testBreak() { + Object o = null; + while (true) { + try { + System.out.println("hello"); + break; + } finally { + o = ""; + } + } + if (o != null) { + System.out.println("fred"); + } + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java index 7128b952ae18..e4ad15969725 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java @@ -60,6 +60,7 @@ public class DataFlowInspectionTest extends DataFlowInspectionTestCase { public void testExceptionFromFinallyNesting() throws Throwable { doTest(); } public void testNestedFinally() { doTest(); } public void testTryFinallyInsideFinally() { doTest(); } + public void testBreakContinueViaFinally() { doTest(); } public void testFieldChangedBetweenSynchronizedBlocks() throws Throwable { doTest(); } public void testGeneratedEquals() throws Throwable { doTest(); } diff --git a/platform/util/src/com/intellij/util/containers/FList.java b/platform/util/src/com/intellij/util/containers/FList.java index dde6dcf3fcba..4ddb009715dd 100644 --- a/platform/util/src/com/intellij/util/containers/FList.java +++ b/platform/util/src/com/intellij/util/containers/FList.java @@ -153,4 +153,15 @@ public class FList extends AbstractList { //noinspection unchecked return (FList)EMPTY_LIST; } + + /** + * Creates an FList object with the elements of the given sequence in the reversed order, i.e. the last element of from will be the result's {@link #getHead()} + */ + public static FList createFromReversed(Iterable from) { + FList result = emptyList(); + for (E e : from) { + result = result.prepend(e); + } + return result; + } } From 5baf72fb3d36a4816f9e76caa136dbf56f967895 Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin Date: Fri, 7 Oct 2016 12:30:35 +0300 Subject: [PATCH 26/43] [Parameter Name Hints] minor --- .../codeInsight/daemon/impl/ParameterNameHintsManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/ParameterNameHintsManager.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/ParameterNameHintsManager.java index 6cfe9cd1593d..b50cd520cf8f 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/ParameterNameHintsManager.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/ParameterNameHintsManager.java @@ -42,7 +42,7 @@ public class ParameterNameHintsManager { "(from*, to*)", "(min*, max*)", "(key, value)", - "(format, arg)" + "(format, arg*)" ).map((s) -> MatcherConstructor.INSTANCE.createMatcher(s)) .collect(Collectors.toList()); From 111834f86f04b812874824812ea96818d8cb1e18 Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin Date: Fri, 7 Oct 2016 12:40:36 +0300 Subject: [PATCH 27/43] [Parameter Name Hints] set is also setter --- .../daemon/impl/ParameterNameHintsManager.java | 6 +++--- .../daemon/inlays/InlayParameterHintsTest.kt | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/ParameterNameHintsManager.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/ParameterNameHintsManager.java index b50cd520cf8f..90092aab30dc 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/ParameterNameHintsManager.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/ParameterNameHintsManager.java @@ -122,9 +122,9 @@ public class ParameterNameHintsManager { private static boolean isSetter(PsiMethod method) { String methodName = method.getName(); - if (hasSingleParameter(method) - && methodName.startsWith("set") - && methodName.length() > 3 && Character.isUpperCase(methodName.charAt(3))) { + if (hasSingleParameter(method) && methodName.startsWith("set") + && (methodName.length() == 3 + || methodName.length() > 3 && Character.isUpperCase(methodName.charAt(3)))) { return true; } return false; diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/inlays/InlayParameterHintsTest.kt b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/inlays/InlayParameterHintsTest.kt index e49fb2e6f845..bba79b121692 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/inlays/InlayParameterHintsTest.kt +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/inlays/InlayParameterHintsTest.kt @@ -633,6 +633,22 @@ class Test { onLineStartingWith("draw").assertInlays("x->10", "y->20") onLineStartingWith("drawRect").assertInlays("w->10", "h->12") } + + fun `test do not show for setters`() { + setup(""" +class Test { + + void main() { + set(10); + } + + void set(int newValue) {} + +} +""") + + onLineStartingWith("set(").assertNoInlays() + } fun `test show for method with boolean param and return value`() { setup(""" From 67fb750554898a8c0a1563fd7985c9f576acb346 Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin Date: Fri, 7 Oct 2016 13:26:15 +0300 Subject: [PATCH 28/43] [FreezeLogger] check modality state under invokeLater --- .../intellij/reporting/FreezeLoggerImpl.java | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/reporting/FreezeLoggerImpl.java b/platform/platform-impl/src/com/intellij/reporting/FreezeLoggerImpl.java index 079b5317dd64..95e0b33cf5a1 100644 --- a/platform/platform-impl/src/com/intellij/reporting/FreezeLoggerImpl.java +++ b/platform/platform-impl/src/com/intellij/reporting/FreezeLoggerImpl.java @@ -38,7 +38,7 @@ public class FreezeLoggerImpl extends FreezeLogger { @Override public void runUnderPerformanceMonitor(@Nullable Project project, @NotNull Runnable action) { - if (isUnderDebug() || ApplicationManager.getApplication().isUnitTestMode()) { + if (!shouldReport() || isUnderDebug() || ApplicationManager.getApplication().isUnitTestMode()) { action.run(); return; } @@ -54,12 +54,12 @@ public class FreezeLoggerImpl extends FreezeLogger { ALARM.cancelAllRequests(); } } - + + private static boolean shouldReport() { + return Registry.is("typing.freeze.report.dumps"); + } + private static void dumpThreads(@Nullable Project project, @NotNull ModalityState initialState) { - if (!initialState.equals(ModalityState.current())) { - return; - } - final ThreadInfo[] infos = ThreadDumper.getThreadInfos(); final String edtTrace = ThreadDumper.dumpEdtStackTrace(infos); if (edtTrace.contains("java.lang.ClassLoader.loadClass")) { @@ -67,23 +67,23 @@ public class FreezeLoggerImpl extends FreezeLogger { } final boolean isInDumbMode = project != null && !project.isDisposed() && DumbService.isDumb(project); - final String dumps = ThreadDumper.dumpThreadsToString(); - final String msg = "Typing freeze report, (DumbMode=" + isInDumbMode + ") thread dumps attached. EDT stacktrace:\n" - + edtTrace - + "\n\n\n"; - if (Registry.is("typing.freeze.report.dumps")) { + ApplicationManager.getApplication().invokeLater(() -> { + if (!initialState.equals(ModalityState.current())) return; + sendDumpsInBackground(infos, isInDumbMode); + }, ModalityState.any()); + } + + private static void sendDumpsInBackground(ThreadInfo[] infos, boolean isInDumbMode) { + ApplicationManager.getApplication().executeOnPooledThread(() -> { ThreadDumpInfo info = new ThreadDumpInfo(infos, isInDumbMode); String report = ReporterKt.createReportLine("typing-freeze-dumps", info); if (!StatsSender.INSTANCE.send(report, true)) { LOG.debug("Error while reporting thread dump"); } - } - else { - LOG.error(msg, dumps); - } + }); } - + private static boolean isUnderDebug() { return ManagementFactory.getRuntimeMXBean().getInputArguments().toString().contains("jdwp"); } From 99b09404744387667f968a5421d8e3ac6afa83ce Mon Sep 17 00:00:00 2001 From: "Anna.Kozlova" Date: Fri, 7 Oct 2016 10:21:19 +0200 Subject: [PATCH 29/43] javafx: ensure valid type & logging EA-89519 - IAE: PsiElementFactoryImpl.createTypeElement --- .../codeInsight/JavaFxGetterSetterPrototypeProvider.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/codeInsight/JavaFxGetterSetterPrototypeProvider.java b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/codeInsight/JavaFxGetterSetterPrototypeProvider.java index 7df76d7e5436..8ebccb19b934 100644 --- a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/codeInsight/JavaFxGetterSetterPrototypeProvider.java +++ b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/codeInsight/JavaFxGetterSetterPrototypeProvider.java @@ -36,7 +36,8 @@ public class JavaFxGetterSetterPrototypeProvider extends GetterSetterPrototypePr @Override public boolean canGeneratePrototypeFor(PsiField field) { - return InheritanceUtil.isInheritor(field.getType(), JavaFxCommonNames.JAVAFX_BEANS_VALUE_OBSERVABLE_VALUE); + return InheritanceUtil.isInheritor(field.getType(), JavaFxCommonNames.JAVAFX_BEANS_VALUE_OBSERVABLE_VALUE) && + JavaFxPsiUtil.getWrappedPropertyType(field, field.getProject(), JavaFxCommonNames.ourReadOnlyMap) != null; } @Override @@ -46,6 +47,7 @@ public class JavaFxGetterSetterPrototypeProvider extends GetterSetterPrototypePr final PsiMethod getter = GenerateMembersUtil.generateSimpleGetterPrototype(field); final PsiType wrappedType = JavaFxPsiUtil.getWrappedPropertyType(field, project, JavaFxCommonNames.ourReadOnlyMap); + LOG.assertTrue(wrappedType != null, field.getType()); getter.setName(PropertyUtil.suggestGetterName(PropertyUtil.suggestPropertyName(field), wrappedType)); final PsiTypeElement returnTypeElement = getter.getReturnTypeElement(); @@ -70,7 +72,7 @@ public class JavaFxGetterSetterPrototypeProvider extends GetterSetterPrototypePr final Project project = field.getProject(); final PsiType wrappedType = JavaFxPsiUtil.getWrappedPropertyType(field, project, JavaFxCommonNames.ourWritableMap); - + LOG.assertTrue(wrappedType != null, field.getType()); final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project); final PsiTypeElement newTypeElement = elementFactory.createTypeElement(wrappedType); final PsiParameter[] parameters = setter.getParameterList().getParameters(); From f6821d7fe881907bf8922a6d5b0fca4aec5f754e Mon Sep 17 00:00:00 2001 From: "Anna.Kozlova" Date: Fri, 7 Oct 2016 10:23:58 +0200 Subject: [PATCH 30/43] EA-89456 - IAE: CleanupInspectionIntention.applyFixes --- .../codeInspection/actions/CleanupInspectionIntention.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/platform/lang-impl/src/com/intellij/codeInspection/actions/CleanupInspectionIntention.java b/platform/lang-impl/src/com/intellij/codeInspection/actions/CleanupInspectionIntention.java index 0a4e9104df61..f3d465ab3032 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/actions/CleanupInspectionIntention.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/actions/CleanupInspectionIntention.java @@ -31,6 +31,7 @@ import com.intellij.openapi.editor.Editor; import com.intellij.openapi.progress.EmptyProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Comparing; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.util.PsiUtilCore; @@ -97,7 +98,11 @@ public class CleanupInspectionIntention implements IntentionAction, HighPriority Collections.sort(descriptions, (o1, o2) -> { final ProblemDescriptorBase d1 = (ProblemDescriptorBase)o1; final ProblemDescriptorBase d2 = (ProblemDescriptorBase)o2; - return -PsiUtilCore.compareElementsByPosition(d1.getPsiElement(), d2.getPsiElement()); + final int elementsDiff = PsiUtilCore.compareElementsByPosition(d1.getPsiElement(), d2.getPsiElement()); + if (elementsDiff == 0) { + return Comparing.compare(d1.getDescriptionTemplate(), d2.getDescriptionTemplate()); + } + return -elementsDiff; }); final SequentialModalProgressTask progressTask = From c0de6f3d369a575e5f691bc1442e73241fa8b24b Mon Sep 17 00:00:00 2001 From: "Anna.Kozlova" Date: Fri, 7 Oct 2016 10:26:15 +0200 Subject: [PATCH 31/43] EA-89400 - assert: PsiPrimitiveType.getUnboxedType --- java/java-psi-api/src/com/intellij/psi/PsiPrimitiveType.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/java-psi-api/src/com/intellij/psi/PsiPrimitiveType.java b/java/java-psi-api/src/com/intellij/psi/PsiPrimitiveType.java index a2d3e817c218..50d378c78ba3 100644 --- a/java/java-psi-api/src/com/intellij/psi/PsiPrimitiveType.java +++ b/java/java-psi-api/src/com/intellij/psi/PsiPrimitiveType.java @@ -126,7 +126,7 @@ public class PsiPrimitiveType extends PsiType.Stub { public static PsiPrimitiveType getUnboxedType(PsiType type) { if (!(type instanceof PsiClassType)) return null; - assert type.isValid() : type; + PsiUtil.ensureValidType(type); LanguageLevel languageLevel = ((PsiClassType)type).getLanguageLevel(); if (!languageLevel.isAtLeast(LanguageLevel.JDK_1_5)) return null; From b560ee5910723f63575da47f2f5d0cde17626632 Mon Sep 17 00:00:00 2001 From: "Anna.Kozlova" Date: Fri, 7 Oct 2016 11:34:59 +0200 Subject: [PATCH 32/43] lambda highlighting: if return statement has problem, try to highlight only corresponding return even if containing inference failed --- .../impl/analysis/HighlightVisitorImpl.java | 114 ++++++++---------- .../highlighting/TypeArgsConsistency.java | 2 +- .../TypeArgsConsistencyMisc1.java | 6 +- ...HighlightFaultyLambdaReturnExpression.java | 13 +- .../newLambda/LiftedIntersectionType.java | 2 +- 5 files changed, 68 insertions(+), 69 deletions(-) diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java index d9d81f92a0c5..b643e14a85ad 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java @@ -325,77 +325,65 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh myHolder.add(checkFeature(expression, Feature.LAMBDA_EXPRESSIONS)); final PsiElement parent = PsiUtil.skipParenthesizedExprUp(expression.getParent()); if (parent instanceof PsiExpressionStatement) return; + if (!myHolder.hasErrorResults() && !LambdaUtil.isValidLambdaContext(parent)) { + myHolder.add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression) + .descriptionAndTooltip("Lambda expression not expected here").create()); + } + + PsiType functionalInterfaceType = null; if (!myHolder.hasErrorResults()) { - if (LambdaUtil.isValidLambdaContext(parent)) { - final PsiType functionalInterfaceType = expression.getFunctionalInterfaceType(); - if (functionalInterfaceType != null) { - final String notFunctionalMessage = LambdaHighlightingUtil.checkInterfaceFunctional(functionalInterfaceType); - if (notFunctionalMessage != null) { - HighlightInfo result = - HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression).descriptionAndTooltip(notFunctionalMessage) - .create(); - myHolder.add(result); - } - else { - final PsiCallExpression callExpression = parent instanceof PsiExpressionList && parent.getParent() instanceof PsiCallExpression ? - (PsiCallExpression)parent.getParent() : null; - final JavaResolveResult containingCallResolveResult = callExpression != null ? callExpression.resolveMethodGenerics() : null; - final String errorMessage; - if (containingCallResolveResult instanceof MethodCandidateInfo) { - errorMessage = ((MethodCandidateInfo)containingCallResolveResult).getParentInferenceErrorMessage((PsiExpressionList)parent); - } - else { - errorMessage = null; - } - if (errorMessage != null) { - HighlightInfo result = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) - .range(expression).descriptionAndTooltip(errorMessage).create(); - myHolder.add(result); - } - else { - final Map returnErrors = LambdaUtil - .checkReturnTypeCompatible(expression, LambdaUtil.getFunctionalInterfaceReturnType(functionalInterfaceType)); - if (returnErrors != null) { - for (Map.Entry entry : returnErrors.entrySet()) { - myHolder.add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) - .range(entry.getKey()) - .descriptionAndTooltip(entry.getValue()).create()); - } - } - else { - final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType); - final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(resolveResult); - if (interfaceMethod != null) { - final PsiParameter[] parameters = interfaceMethod.getParameterList().getParameters(); - HighlightInfo result = LambdaHighlightingUtil - .checkParametersCompatible(expression, parameters, LambdaUtil.getSubstitutor(interfaceMethod, resolveResult)); - if (result != null) { - myHolder.add(result); - } - else { - checkFunctionalInterfaceTypeAccessible(expression, functionalInterfaceType); - } - } - } - } - } + functionalInterfaceType = expression.getFunctionalInterfaceType(); + if (functionalInterfaceType != null) { + final String notFunctionalMessage = LambdaHighlightingUtil.checkInterfaceFunctional(functionalInterfaceType); + if (notFunctionalMessage != null) { + myHolder.add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression) + .descriptionAndTooltip(notFunctionalMessage).create()); } - else if (LambdaUtil.getFunctionalInterfaceType(expression, true) != null) { - myHolder.add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression).descriptionAndTooltip("Cannot infer functional interface type").create()); + else { + checkFunctionalInterfaceTypeAccessible(expression, functionalInterfaceType); } } - else { - HighlightInfo result = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression) - .descriptionAndTooltip("Lambda expression not expected here").create(); - myHolder.add(result); + else if (LambdaUtil.getFunctionalInterfaceType(expression, true) != null) { + myHolder.add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression).descriptionAndTooltip("Cannot infer functional interface type").create()); } - if (!myHolder.hasErrorResults()) { - final PsiElement body = expression.getBody(); - if (body instanceof PsiCodeBlock) { - myHolder.add(HighlightControlFlowUtil.checkUnreachableStatement((PsiCodeBlock)body)); + } + + if (!myHolder.hasErrorResults() && functionalInterfaceType != null) { + String parentInferenceErrorMessage = null; + final PsiCallExpression callExpression = parent instanceof PsiExpressionList && parent.getParent() instanceof PsiCallExpression ? + (PsiCallExpression)parent.getParent() : null; + final JavaResolveResult containingCallResolveResult = callExpression != null ? callExpression.resolveMethodGenerics() : null; + if (containingCallResolveResult instanceof MethodCandidateInfo) { + parentInferenceErrorMessage = ((MethodCandidateInfo)containingCallResolveResult).getParentInferenceErrorMessage((PsiExpressionList)parent); + } + final Map returnErrors = LambdaUtil.checkReturnTypeCompatible(expression, LambdaUtil.getFunctionalInterfaceReturnType(functionalInterfaceType)); + if (parentInferenceErrorMessage != null && (returnErrors == null || !returnErrors.containsValue(parentInferenceErrorMessage))) { + myHolder.add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression).descriptionAndTooltip(parentInferenceErrorMessage).create()); + } + else if (returnErrors != null) { + for (Map.Entry entry : returnErrors.entrySet()) { + myHolder.add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) + .range(entry.getKey()) + .descriptionAndTooltip(entry.getValue()).create()); } } } + + if (!myHolder.hasErrorResults() && functionalInterfaceType != null) { + final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType); + final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(resolveResult); + if (interfaceMethod != null) { + final PsiParameter[] parameters = interfaceMethod.getParameterList().getParameters(); + myHolder.add(LambdaHighlightingUtil.checkParametersCompatible(expression, parameters, LambdaUtil.getSubstitutor(interfaceMethod, resolveResult))); + } + } + + if (!myHolder.hasErrorResults()) { + final PsiElement body = expression.getBody(); + if (body instanceof PsiCodeBlock) { + myHolder.add(HighlightControlFlowUtil.checkUnreachableStatement((PsiCodeBlock)body)); + } + } } @Override diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/TypeArgsConsistency.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/TypeArgsConsistency.java index 5d81e850e679..76cddf5e33e4 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/TypeArgsConsistency.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/TypeArgsConsistency.java @@ -33,7 +33,7 @@ class TypeArgsConsistency1 { I i1 = (i, j) -> i + j; foo((i, j) -> i + j); I i2 =bar((i, j) -> i) ; - I i3 = bar((i, j) -> "" + i + j); + I i3 = bar((i, j) -> "" + i + j); } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/TypeArgsConsistencyMisc1.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/TypeArgsConsistencyMisc1.java index 14facbd197a5..a27b804feafe 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/TypeArgsConsistencyMisc1.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/TypeArgsConsistencyMisc1.java @@ -66,9 +66,9 @@ class Test2 { { bar(x -> x); bar1(x -> x); - bar2(1, x -> x); - bar2("", x -> x); - bar3(x -> x, ""); + bar2(1, x -> x); + bar2("", x -> x); + bar3(x -> x, ""); } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/HighlightFaultyLambdaReturnExpression.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/HighlightFaultyLambdaReturnExpression.java index fdd1faa3c54a..06c5c61c90d7 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/HighlightFaultyLambdaReturnExpression.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/HighlightFaultyLambdaReturnExpression.java @@ -1,4 +1,5 @@ - +import java.util.Collections; +import java.util.List; import java.util.function.Supplier; class Test { @@ -12,4 +13,14 @@ class Test { } public static void foo(Supplier consumer) {} + + private void foo(List descriptions) { + Collections.sort(descriptions, (o1, o2) -> { + final int elementsDiff = o1.length() - o2.length(); + if (elementsDiff == 0) { + return o1.equals(o2); + } + return -elementsDiff; + }); + } } \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/LiftedIntersectionType.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/LiftedIntersectionType.java index e53f227b1950..f7d4fdd37967 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/LiftedIntersectionType.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/LiftedIntersectionType.java @@ -13,7 +13,7 @@ class Test { } void bar(C c) { - foo(c, x -> x.f()); + foo(c, x -> x.f()); foo(c, x -> x); } From 98094c72de67a7b019cb4c84127fa549f66df871 Mon Sep 17 00:00:00 2001 From: "Anna.Kozlova" Date: Fri, 7 Oct 2016 13:53:09 +0200 Subject: [PATCH 33/43] lambda: ensure deep nested lambdas get target types from top level inference (IDEA-157314) --- .../src/com/intellij/psi/LambdaUtil.java | 10 +++ .../InferenceSessionContainer.java | 90 ++++++++++++++++--- ...ReturnExpressionsWithProperTargetType.java | 24 +++++ ...ceCollectingAdditionalConstraintsTest.java | 8 +- 4 files changed, 116 insertions(+), 16 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/additionalConstraints/DeepLambdaReturnExpressionsWithProperTargetType.java diff --git a/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java b/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java index 8535e5756a0c..55c581f13105 100644 --- a/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java @@ -906,6 +906,16 @@ public class LambdaUtil { } } + public static T performWithLambdaTargetType(PsiLambdaExpression lambdaExpression, PsiType targetType, Producer producer) { + try { + getFunctionalTypeMap().put(lambdaExpression, targetType); + return producer.produce(); + } + finally { + getFunctionalTypeMap().remove(lambdaExpression); + } + } + /** * Generate lambda text for single argument expression lambda * diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSessionContainer.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSessionContainer.java index fcf4496e1e17..4301e330b2ac 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSessionContainer.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSessionContainer.java @@ -17,11 +17,13 @@ package com.intellij.psi.impl.source.resolve.graphInference; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Computable; +import com.intellij.openapi.util.Ref; import com.intellij.psi.*; import com.intellij.psi.impl.source.resolve.ParameterTypeInferencePolicy; import com.intellij.psi.impl.source.resolve.graphInference.constraints.ExpressionCompatibilityConstraint; import com.intellij.psi.infos.MethodCandidateInfo; import com.intellij.psi.util.*; +import com.intellij.util.Producer; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -118,17 +120,8 @@ public class InferenceSessionContainer { } if (session != null) { - final CompoundInitialState compoundInitialState = createState(session); - final InitialInferenceState initialInferenceState = compoundInitialState.getInitialState(PsiTreeUtil.getParentOfType(argumentList, PsiCall.class)); - if (initialInferenceState != null) { - InferenceSession childSession = new InferenceSession(initialInferenceState); - final List errorMessages = session.getIncompatibleErrorMessages(); - if (errorMessages != null) { - return childSession.prepareSubstitution(); - } - return childSession - .collectAdditionalAndInfer(parameters, arguments, properties, compoundInitialState.getInitialSubstitutor()); - } + final PsiSubstitutor childSubstitutor = inferNested(typeParameters, parameters, arguments, partialSubstitutor, (PsiCall)parent, policy, properties, session); + if (childSubstitutor != null) return childSubstitutor; } else if (topLevelCall instanceof PsiMethodCallExpression) { return new InferenceSession(typeParameters, partialSubstitutor, parent.getManager(), parent, policy).prepareSubstitution(); @@ -141,7 +134,80 @@ public class InferenceSessionContainer { inferenceSession.initExpressionConstraints(parameters, arguments, parent); return inferenceSession.infer(parameters, arguments, parent); } - + + private static PsiSubstitutor inferNested(final PsiTypeParameter[] typeParameters, + @NotNull final PsiParameter[] parameters, + @NotNull final PsiExpression[] arguments, + final PsiSubstitutor partialSubstitutor, + @NotNull final PsiCall parent, + @NotNull final ParameterTypeInferencePolicy policy, + final MethodCandidateInfo.CurrentCandidateProperties properties, + final InferenceSession parentSession) { + final CompoundInitialState compoundInitialState = createState(parentSession); + InitialInferenceState initialInferenceState = compoundInitialState.getInitialState(parent); + if (initialInferenceState != null) { + final InferenceSession childSession = new InferenceSession(initialInferenceState); + final List errorMessages = parentSession.getIncompatibleErrorMessages(); + if (errorMessages != null) { + return childSession.prepareSubstitution(); + } + return childSession.collectAdditionalAndInfer(parameters, arguments, properties, compoundInitialState.getInitialSubstitutor()); + } + + //we do not investigate lambda return expressions when lambda's return type is already inferred (proper) + //this way all calls from lambda's return expressions won't appear in nested sessions + else { + PsiElement gParent = PsiUtil.skipParenthesizedExprUp(parent.getParent()); + //find the nearest parent which appears in the map and start inference with a provided target type for a nested lambda + while (true) { + if (gParent instanceof PsiReturnStatement) { //process code block lambda + final PsiElement returnContainer = gParent.getParent(); + if (returnContainer instanceof PsiCodeBlock) { + gParent = returnContainer.getParent(); + } + } + if (gParent instanceof PsiLambdaExpression) { + final PsiCall call = PsiTreeUtil.getParentOfType(gParent, PsiCall.class); + if (call != null) { + initialInferenceState = compoundInitialState.getInitialState(call); + if (initialInferenceState != null) { + final int idx = LambdaUtil.getLambdaIdx(call.getArgumentList(), gParent); + final PsiMethod method = call.resolveMethod(); + if (method != null && idx > -1) { + final PsiType parameterType = PsiTypesUtil.getParameterType(method.getParameterList().getParameters(), idx, true); + final PsiType parameterTypeInTermsOfSession = initialInferenceState.getInferenceSubstitutor().substitute(parameterType); + final PsiType lambdaTargetType = compoundInitialState.getInitialSubstitutor().substitute(parameterTypeInTermsOfSession); + return LambdaUtil.performWithLambdaTargetType((PsiLambdaExpression)gParent, lambdaTargetType, new Producer() { + @Nullable + @Override + public PsiSubstitutor produce() { + if (call.equals(PsiTreeUtil.getParentOfType(parent, PsiCall.class, true))) { + //parent was mentioned in the top inference session + //just proceed with the target type + final InferenceSession inferenceSession = new InferenceSession(typeParameters, partialSubstitutor, parent.getManager(), parent, policy); + inferenceSession.initExpressionConstraints(parameters, arguments, parent); + return inferenceSession.infer(parameters, arguments, parent); + } + //one of the grand parents were found in the top inference session + //start from it as it is the top level call + final InferenceSession sessionInsideLambda = startTopLevelInference(call, policy); + return inferNested(typeParameters, parameters, arguments, partialSubstitutor, parent, policy, properties, sessionInsideLambda); + } + }); + } + } + else { + gParent = PsiUtil.skipParenthesizedExprUp(call.getParent()); + continue; + } + } + } + break; + } + } + return null; + } + private static CompoundInitialState createState(InferenceSession topLevelSession) { final PsiSubstitutor topInferenceSubstitutor = replaceVariables(topLevelSession.getInferenceVariables()); final Map nestedStates = new LinkedHashMap(); diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/additionalConstraints/DeepLambdaReturnExpressionsWithProperTargetType.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/additionalConstraints/DeepLambdaReturnExpressionsWithProperTargetType.java new file mode 100644 index 000000000000..f6ccbee6a259 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/additionalConstraints/DeepLambdaReturnExpressionsWithProperTargetType.java @@ -0,0 +1,24 @@ + +import java.util.Optional; +import java.util.function.UnaryOperator; + +class Test { + + private void example() { + update(x -> x.flatMap(y -> Optional.empty())); + update(x -> x.flatMap(y -> x.flatMap(z -> Optional.empty()))); + update(x -> x.flatMap(y -> x.flatMap(z -> x.flatMap(w -> Optional.empty())))); + + update(x -> { + return x.flatMap(y -> { + return x.flatMap(z -> { + return x.flatMap(w -> { + return Optional.empty(); + }); + }); + }); + }); + } + + void update(UnaryOperator> u) {} +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewInferenceCollectingAdditionalConstraintsTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewInferenceCollectingAdditionalConstraintsTest.java index 3cfeaa32fc5e..22c993a3dee6 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewInferenceCollectingAdditionalConstraintsTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewInferenceCollectingAdditionalConstraintsTest.java @@ -31,12 +31,12 @@ public class NewInferenceCollectingAdditionalConstraintsTest extends LightDaemon doTest(); } - private void doTest() { - doTest(true); + public void testDeepLambdaReturnExpressionsWithProperTargetType() throws Exception { + doTest(); } - private void doTest(boolean warnings) { + private void doTest() { IdeaTestUtil.setTestVersion(JavaSdkVersion.JDK_1_8, getModule(), getTestRootDisposable()); - doTest(BASE_PATH + "/" + getTestName(false) + ".java", warnings, false); + doTest(BASE_PATH + "/" + getTestName(false) + ".java", true, false); } } From cbd3e9198ccb9156f716ccf4e7fd1fc5de3d30db Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Fri, 7 Oct 2016 13:04:43 +0300 Subject: [PATCH 34/43] cleanup --- platform/util-rt/src/com/intellij/openapi/util/Pair.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/util-rt/src/com/intellij/openapi/util/Pair.java b/platform/util-rt/src/com/intellij/openapi/util/Pair.java index 84b870010429..b2518ddce1f3 100644 --- a/platform/util-rt/src/com/intellij/openapi/util/Pair.java +++ b/platform/util-rt/src/com/intellij/openapi/util/Pair.java @@ -37,7 +37,7 @@ public class Pair { @SuppressWarnings("MethodNamesDifferingOnlyByCase") public static Pair pair(A first, B second) { //noinspection DontUsePairConstructor - return new Pair(first, second); + return create(first, second); } @NotNull From b6d3b18ace67b25715eaa242196cd2665755b0c6 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Fri, 7 Oct 2016 15:15:06 +0300 Subject: [PATCH 35/43] reverted to use Alarm to avoid thread multiplication when checkTextAvailable() hangs --- .../xpath/xslt/run/ReadProcessThread.java | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/run/ReadProcessThread.java b/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/run/ReadProcessThread.java index 3dff2d37104d..62c873e39ce3 100644 --- a/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/run/ReadProcessThread.java +++ b/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/run/ReadProcessThread.java @@ -15,15 +15,16 @@ */ package org.intellij.lang.xpath.xslt.run; -import com.intellij.util.concurrency.AppExecutorUtil; +import com.intellij.openapi.Disposable; +import com.intellij.openapi.util.Disposer; +import com.intellij.util.Alarm; import java.io.IOException; import java.io.Reader; -import java.util.concurrent.TimeUnit; /* Copied from com.intellij.execution.process.OSProcessHandler.ReadProcessThread */ @SuppressWarnings({"ALL"}) -abstract class ReadProcessThread extends Thread { +abstract class ReadProcessThread extends Thread implements Disposable { private static final int NOTIFY_TEXT_DELAY = 300; private final Reader myReader; @@ -40,15 +41,15 @@ abstract class ReadProcessThread extends Thread { } public void run() { - AppExecutorUtil.getAppScheduledExecutorService().schedule( - new Runnable() { + final Alarm myAlarm = new Alarm(Alarm.ThreadToUse.POOLED_THREAD, this); + myAlarm.addRequest(new Runnable() { public void run() { if(!isClosed()) { - AppExecutorUtil.getAppScheduledExecutorService().schedule(this, NOTIFY_TEXT_DELAY,TimeUnit.MILLISECONDS); + myAlarm.addRequest(this, NOTIFY_TEXT_DELAY); checkTextAvailable(); } } - }, NOTIFY_TEXT_DELAY, TimeUnit.MILLISECONDS); + }, NOTIFY_TEXT_DELAY); try { while (!isClosed()) { @@ -67,8 +68,14 @@ abstract class ReadProcessThread extends Thread { catch (Exception e) { e.printStackTrace(); } - - close(); + finally { + try { + close(); + } + finally { + Disposer.dispose(this); + } + } } private int readNextByte() { @@ -122,4 +129,7 @@ abstract class ReadProcessThread extends Thread { return myIsClosed; } + @Override + public void dispose() { + } } From b31ec2fbdb369621cb4360268153c65b16ddb0d7 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Fri, 7 Oct 2016 15:26:09 +0300 Subject: [PATCH 36/43] removed unnecessary lazy value --- .../openapi/util/LowMemoryWatcherManager.java | 15 ++++----------- .../concurrency/AppScheduledExecutorService.java | 3 ++- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/platform/util/src/com/intellij/openapi/util/LowMemoryWatcherManager.java b/platform/util/src/com/intellij/openapi/util/LowMemoryWatcherManager.java index d38d02df4631..6a3f5e3ddcee 100644 --- a/platform/util/src/com/intellij/openapi/util/LowMemoryWatcherManager.java +++ b/platform/util/src/com/intellij/openapi/util/LowMemoryWatcherManager.java @@ -18,7 +18,6 @@ package com.intellij.openapi.util; import com.intellij.openapi.Disposable; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.registry.Registry; -import com.intellij.util.concurrency.AppExecutorUtil; import org.jetbrains.annotations.NotNull; import javax.management.ListenerNotFoundException; @@ -37,6 +36,7 @@ public class LowMemoryWatcherManager implements Disposable { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.util.LowMemoryWatcherManager"); private static final long MEM_THRESHOLD = 5 /*MB*/ * 1024 * 1024; + @NotNull private final ExecutorService myExecutorService; private Future mySubmitted; // guarded by ourJanitor private final AtomicBoolean myProcessing = new AtomicBoolean(); @@ -52,7 +52,8 @@ public class LowMemoryWatcherManager implements Disposable { } }; - public LowMemoryWatcherManager() { + public LowMemoryWatcherManager(@NotNull ExecutorService executorService) { + myExecutorService = executorService; try { for (MemoryPoolMXBean bean : ManagementFactory.getMemoryPoolMXBeans()) { if (bean.getType() == MemoryType.HEAP && bean.isUsageThresholdSupported()) { @@ -83,7 +84,7 @@ public class LowMemoryWatcherManager implements Disposable { synchronized (myJanitor) { if (mySubmitted == null) { - mySubmitted = myExecutor.getValue().submit(myJanitor); + mySubmitted = myExecutorService.submit(myJanitor); } } } @@ -101,14 +102,6 @@ public class LowMemoryWatcherManager implements Disposable { } } - private final NotNullLazyValue myExecutor = new NotNullLazyValue() { - @NotNull - @Override - protected ExecutorService compute() { - return AppExecutorUtil.createBoundedApplicationPoolExecutor("lowMemoryWatcher", 1); - } - }; - @Override public void dispose() { try { diff --git a/platform/util/src/com/intellij/util/concurrency/AppScheduledExecutorService.java b/platform/util/src/com/intellij/util/concurrency/AppScheduledExecutorService.java index fed13f11e2f6..d0af88715a32 100644 --- a/platform/util/src/com/intellij/util/concurrency/AppScheduledExecutorService.java +++ b/platform/util/src/com/intellij/util/concurrency/AppScheduledExecutorService.java @@ -37,7 +37,7 @@ public class AppScheduledExecutorService extends SchedulingWrapper { private static final Logger LOG = Logger.getInstance("#org.jetbrains.ide.PooledThreadExecutor"); static final String POOLED_THREAD_PREFIX = "ApplicationImpl pooled thread "; @NotNull private final String myName; - private final LowMemoryWatcherManager myLowMemoryWatcherManager = new LowMemoryWatcherManager(); + private final LowMemoryWatcherManager myLowMemoryWatcherManager; private Consumer newThreadListener; private final AtomicInteger counter = new AtomicInteger(); @@ -68,6 +68,7 @@ public class AppScheduledExecutorService extends SchedulingWrapper { return thread; } }); + myLowMemoryWatcherManager = new LowMemoryWatcherManager(this); } public void setNewThreadListener(@NotNull Consumer threadListener) { From 266379005ba83e8a379d4813e6b01ffa128fce78 Mon Sep 17 00:00:00 2001 From: "Anna.Kozlova" Date: Fri, 7 Oct 2016 14:40:57 +0200 Subject: [PATCH 37/43] strip nullable annotations which come from method return types on introduce local --- .../IntroduceVariableBase.java | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java b/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java index 72c24b5798ca..614f204c6a95 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java @@ -16,6 +16,7 @@ package com.intellij.refactoring.introduceVariable; import com.intellij.codeInsight.CodeInsightUtil; +import com.intellij.codeInsight.NullableNotNullManager; import com.intellij.codeInsight.completion.JavaCompletionUtil; import com.intellij.codeInsight.highlighting.HighlightManager; import com.intellij.codeInsight.lookup.LookupManager; @@ -855,8 +856,9 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase { settings.getSelectedType()); initializer = simplifyVariableInitializer(initializer, selectedType.getType()); + PsiType type = stripNullabilityAnnotationsFromTargetType(selectedType, project); PsiDeclarationStatement declaration = JavaPsiFacade.getInstance(project).getElementFactory() - .createVariableDeclarationStatement(settings.getEnteredName(), selectedType.getType(), initializer, container); + .createVariableDeclarationStatement(settings.getEnteredName(), type, initializer, container); if (!isInsideLoop) { declaration = addDeclaration(declaration, initializer); LOG.assertTrue(expr1.isValid()); @@ -946,6 +948,26 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase { }; } + private static PsiType stripNullabilityAnnotationsFromTargetType(SmartTypePointer selectedType, final Project project) { + PsiType type = selectedType.getType(); + if (type == null) return null; + final PsiAnnotation[] annotations = type.getAnnotations(); + type = type.annotate(new TypeAnnotationProvider() { + @NotNull + @Override + public PsiAnnotation[] getAnnotations() { + final NullableNotNullManager manager = NullableNotNullManager.getInstance(project); + final Set nullables = new HashSet<>(); + nullables.addAll(manager.getNotNulls()); + nullables.addAll(manager.getNullables()); + return Arrays.stream(annotations) + .filter(annotation -> !nullables.contains(annotation.getQualifiedName())) + .toArray(PsiAnnotation[]::new); + } + }); + return type; + } + private static boolean isFinalVariableOnLHS(PsiExpression expr) { if (expr instanceof PsiReferenceExpression && RefactoringUtil.isAssignmentLHS(expr)) { final PsiElement resolve = ((PsiReferenceExpression)expr).resolve(); From 9019fe01b7addac855987e2cd21c657a3d050a6d Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 7 Oct 2016 13:27:37 +0200 Subject: [PATCH 38/43] add MergingLexerAdapterBase.toString for better EA-89515 diagnostics (assert: TemplateDataElementType.createOuterLanguageElement) --- .../src/com/intellij/lexer/MergingLexerAdapterBase.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/platform/core-api/src/com/intellij/lexer/MergingLexerAdapterBase.java b/platform/core-api/src/com/intellij/lexer/MergingLexerAdapterBase.java index 135ace049cf5..b8a5c3d5f056 100644 --- a/platform/core-api/src/com/intellij/lexer/MergingLexerAdapterBase.java +++ b/platform/core-api/src/com/intellij/lexer/MergingLexerAdapterBase.java @@ -95,6 +95,11 @@ public abstract class MergingLexerAdapterBase extends DelegateLexer { myState = pos.getOldState(); } + @Override + public String toString() { + return getClass().getName() + "[" + getDelegate() + "]"; + } + @NotNull @Override public LexerPosition getCurrentPosition() { From 88345c2060bf3d771a73aee278f52ff99e221e4d Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 7 Oct 2016 13:50:37 +0200 Subject: [PATCH 39/43] add notnull to BulkOccurrenceProcessor (IDEA-CR-13878) --- .../com/intellij/psi/impl/search/BulkOccurrenceProcessor.java | 2 +- .../src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/indexing-impl/src/com/intellij/psi/impl/search/BulkOccurrenceProcessor.java b/platform/indexing-impl/src/com/intellij/psi/impl/search/BulkOccurrenceProcessor.java index 2f7b36b9c2f7..dd346b2b5295 100644 --- a/platform/indexing-impl/src/com/intellij/psi/impl/search/BulkOccurrenceProcessor.java +++ b/platform/indexing-impl/src/com/intellij/psi/impl/search/BulkOccurrenceProcessor.java @@ -21,5 +21,5 @@ import org.jetbrains.annotations.NotNull; @FunctionalInterface interface BulkOccurrenceProcessor { - boolean execute(@NotNull PsiElement scope, int[] offsetsInScope, @NotNull StringSearcher searcher); + boolean execute(@NotNull PsiElement scope, @NotNull int[] offsetsInScope, @NotNull StringSearcher searcher); } diff --git a/platform/indexing-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java b/platform/indexing-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java index 3be5d14d704f..a402e800b0e8 100644 --- a/platform/indexing-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java +++ b/platform/indexing-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java @@ -721,7 +721,7 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { final RequestResultProcessor wrapped = singleRequest.processor; return new BulkOccurrenceProcessor() { @Override - public boolean execute(@NotNull PsiElement scope, int[] offsetsInScope, @NotNull StringSearcher searcher) { + public boolean execute(@NotNull PsiElement scope, @NotNull int[] offsetsInScope, @NotNull StringSearcher searcher) { try { if (wrapped instanceof RequestResultProcessor.BulkResultProcessor) { return ((RequestResultProcessor.BulkResultProcessor)wrapped).processTextOccurrences(scope, offsetsInScope, consumer); From 380c3ea3b03df9fa3ebbf2580fb557c7d9f91227 Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 7 Oct 2016 14:07:47 +0200 Subject: [PATCH 40/43] don't startCacheUpdate for disposed project (EA-88839 - assert: MessageBusImpl.checkNotDisposed) --- .../src/com/intellij/ide/startup/impl/StartupManagerImpl.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/platform/platform-impl/src/com/intellij/ide/startup/impl/StartupManagerImpl.java b/platform/platform-impl/src/com/intellij/ide/startup/impl/StartupManagerImpl.java index f882ad127cea..9fa2d24ef681 100644 --- a/platform/platform-impl/src/com/intellij/ide/startup/impl/StartupManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/ide/startup/impl/StartupManagerImpl.java @@ -302,6 +302,8 @@ public class StartupManagerImpl extends StartupManagerEx { } public void startCacheUpdate() { + if (myProject.isDisposed()) return; + try { DumbServiceImpl dumbService = DumbServiceImpl.getInstance(myProject); From 2d5ebdd0d81c72e1a0b6c0b2090431b1ced0228c Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 7 Oct 2016 14:17:40 +0200 Subject: [PATCH 41/43] FlatWelcomeFrame: handle jb protocol commands in write-safe context (EA-89533 - assert: FileDocumentManagerImpl.saveAllDocuments) --- .../openapi/wm/impl/welcomeScreen/FlatWelcomeFrame.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/FlatWelcomeFrame.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/FlatWelcomeFrame.java index ef6f3e0b5df3..950cbca61896 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/FlatWelcomeFrame.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/FlatWelcomeFrame.java @@ -28,6 +28,7 @@ import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ApplicationNamesInfo; import com.intellij.openapi.application.JBProtocolCommand; +import com.intellij.openapi.application.TransactionGuard; import com.intellij.openapi.application.ex.ApplicationInfoEx; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.DumbAwareAction; @@ -96,8 +97,7 @@ public class FlatWelcomeFrame extends JFrame implements IdeFrame, Disposable, Ac @Override public void addNotify() { super.addNotify(); - //noinspection SSBasedInspection - SwingUtilities.invokeLater(() -> JBProtocolCommand.handleCurrentCommand()); + TransactionGuard.submitTransaction(FlatWelcomeFrame.this, () -> JBProtocolCommand.handleCurrentCommand()); } }; From 3bf9743d5954781d5b10132059029a48648fde13 Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 7 Oct 2016 14:20:17 +0200 Subject: [PATCH 42/43] show cloudgit commit dialog in a write-safe context (EA-89532 - assert: FileDocumentManagerImpl.saveAllDocuments) --- .../intellij/remoteServer/util/CloudGitDeploymentRuntime.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/git4idea/remote-servers-git/src/com/intellij/remoteServer/util/CloudGitDeploymentRuntime.java b/plugins/git4idea/remote-servers-git/src/com/intellij/remoteServer/util/CloudGitDeploymentRuntime.java index d49df174fb43..d4553b074dab 100644 --- a/plugins/git4idea/remote-servers-git/src/com/intellij/remoteServer/util/CloudGitDeploymentRuntime.java +++ b/plugins/git4idea/remote-servers-git/src/com/intellij/remoteServer/util/CloudGitDeploymentRuntime.java @@ -1,7 +1,6 @@ package com.intellij.remoteServer.util; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressIndicator; @@ -247,7 +246,7 @@ public class CloudGitDeploymentRuntime extends CloudDeploymentRuntime { private static T runOnEdt(final Computable computable) { final Ref result = new Ref<>(); - ApplicationManager.getApplication().invokeAndWait(() -> result.set(computable.compute()), ModalityState.any()); + ApplicationManager.getApplication().invokeAndWait(() -> result.set(computable.compute())); return result.get(); } From 8f72edb456f408cdff164e359343fae8e90d4cd3 Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 7 Oct 2016 14:28:59 +0200 Subject: [PATCH 43/43] execute PydevConsoleRunner in a write-safe context (EA-89529 - assert: FileDocumentManagerImpl.saveAllDocuments) --- .../src/com/jetbrains/python/console/PydevConsoleRunner.java | 4 +++- .../python/console/PythonConsoleToolWindowFactory.java | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/python/src/com/jetbrains/python/console/PydevConsoleRunner.java b/python/src/com/jetbrains/python/console/PydevConsoleRunner.java index 938dff0b3344..2476bdc11f30 100644 --- a/python/src/com/jetbrains/python/console/PydevConsoleRunner.java +++ b/python/src/com/jetbrains/python/console/PydevConsoleRunner.java @@ -43,6 +43,7 @@ import com.intellij.lang.ASTNode; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.actionSystem.ex.ActionUtil; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.application.Result; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.diagnostic.Logger; @@ -75,6 +76,7 @@ import com.intellij.psi.PsiFile; import com.intellij.remote.RemoteProcess; import com.intellij.remote.Tunnelable; import com.intellij.testFramework.LightVirtualFile; +import com.intellij.ui.GuiUtils; import com.intellij.util.ArrayUtil; import com.intellij.util.IJSwingUtilities; import com.intellij.util.PathMappingSettings; @@ -1048,7 +1050,7 @@ public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory PydevConsoleRunner.this.run()); + GuiUtils.invokeLaterIfNeeded(() -> PydevConsoleRunner.this.run(), ModalityState.defaultModalityState()); } }.queue(); } diff --git a/python/src/com/jetbrains/python/console/PythonConsoleToolWindowFactory.java b/python/src/com/jetbrains/python/console/PythonConsoleToolWindowFactory.java index 2a49d5efedd3..8ae23b99acb2 100644 --- a/python/src/com/jetbrains/python/console/PythonConsoleToolWindowFactory.java +++ b/python/src/com/jetbrains/python/console/PythonConsoleToolWindowFactory.java @@ -16,6 +16,7 @@ package com.jetbrains.python.console; import com.intellij.execution.console.LanguageConsoleView; +import com.intellij.openapi.application.TransactionGuard; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; import com.intellij.openapi.wm.ToolWindow; @@ -37,6 +38,6 @@ public class PythonConsoleToolWindowFactory implements ToolWindowFactory, DumbAw PythonConsoleToolWindow.getInstance(project).initialized(); } }); - runner.run(); + TransactionGuard.submitTransaction(project, () -> runner.run()); } }