From d1d5dc8580cf9d98c94d6e7b5258724af12742c9 Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Wed, 9 Mar 2016 13:01:55 +0300 Subject: [PATCH 01/83] Inspesction tool window: unified html for html export and dead code preview is shown (not yet finished) --- .../deadCode/DeadHTMLComposer.java | 18 +++++++++++++----- .../DummyEntryPointsPresentation.java | 2 +- .../UnusedDeclarationPresentation.java | 19 +++++++++++++++---- .../intellij/codeInspection/HTMLComposer.java | 11 +++++++---- .../codeInspection/ex/HTMLComposerImpl.java | 2 ++ .../codeInspection/export/HTMLExporter.java | 6 +++++- .../codeInspection/ex/DescriptorComposer.java | 3 ++- .../ui/actions/ExportHTMLAction.java | 14 +++++++++++++- 8 files changed, 58 insertions(+), 17 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInspection/deadCode/DeadHTMLComposer.java b/java/java-impl/src/com/intellij/codeInspection/deadCode/DeadHTMLComposer.java index 88f56186fc8e..9f6f534e58ff 100644 --- a/java/java-impl/src/com/intellij/codeInspection/deadCode/DeadHTMLComposer.java +++ b/java/java-impl/src/com/intellij/codeInspection/deadCode/DeadHTMLComposer.java @@ -51,7 +51,13 @@ public class DeadHTMLComposer extends HTMLComposerImpl { @Override public void compose(final StringBuffer buf, RefEntity refEntity) { - genPageHeader(buf, refEntity); + compose(buf, refEntity, true); + } + + public void compose(final StringBuffer buf, RefEntity refEntity, boolean toExternalHtml) { + if (toExternalHtml) { + genPageHeader(buf, refEntity); + } if (refEntity instanceof RefElement) { RefElementImpl refElement = (RefElementImpl)refEntity; @@ -59,12 +65,12 @@ public class DeadHTMLComposer extends HTMLComposerImpl { appendHeading(buf, InspectionsBundle.message("inspection.problem.synopsis")); //noinspection HardCodedStringLiteral buf.append("
"); - appendAfterHeaderIndention(buf); appendProblemSynopsis(refElement, buf); - //noinspection HardCodedStringLiteral - buf.append("

"); - appendResolution(buf, refElement, DescriptorComposer.quickFixTexts(refElement, myToolPresentation)); + if (toExternalHtml) { + buf.append("

"); + appendResolution(buf, refElement, DescriptorComposer.quickFixTexts(refElement, myToolPresentation)); + } refElement.accept(new RefJavaVisitor() { @Override public void visitClass(@NotNull RefClass aClass) { appendClassInstantiations(buf, aClass); @@ -94,6 +100,7 @@ public class DeadHTMLComposer extends HTMLComposerImpl { } public static void appendProblemSynopsis(final RefElement refElement, final StringBuffer buf) { + buf.append("
"); refElement.accept(new RefJavaVisitor() { @Override public void visitField(@NotNull RefField field) { if (field.isUsedForReading() && !field.isUsedForWriting()) { @@ -210,6 +217,7 @@ public class DeadHTMLComposer extends HTMLComposerImpl { } } }); + buf.append("
"); } @Override diff --git a/java/java-impl/src/com/intellij/codeInspection/deadCode/DummyEntryPointsPresentation.java b/java/java-impl/src/com/intellij/codeInspection/deadCode/DummyEntryPointsPresentation.java index c9be5d934b3d..f5caa71e452e 100644 --- a/java/java-impl/src/com/intellij/codeInspection/deadCode/DummyEntryPointsPresentation.java +++ b/java/java-impl/src/com/intellij/codeInspection/deadCode/DummyEntryPointsPresentation.java @@ -82,7 +82,7 @@ public class DummyEntryPointsPresentation extends UnusedDeclarationPresentation @Override @NotNull - public HTMLComposerImpl getComposer() { + public DeadHTMLComposer getComposer() { return new DeadHTMLComposer(this); } } diff --git a/java/java-impl/src/com/intellij/codeInspection/deadCode/UnusedDeclarationPresentation.java b/java/java-impl/src/com/intellij/codeInspection/deadCode/UnusedDeclarationPresentation.java index 963d55c6d342..dc9d15de6250 100644 --- a/java/java-impl/src/com/intellij/codeInspection/deadCode/UnusedDeclarationPresentation.java +++ b/java/java-impl/src/com/intellij/codeInspection/deadCode/UnusedDeclarationPresentation.java @@ -31,6 +31,7 @@ import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vcs.FileStatus; +import com.intellij.profile.codeInspection.ui.SingleInspectionProfilePanel; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; @@ -38,11 +39,14 @@ import com.intellij.psi.PsiModifierListOwner; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtilCore; import com.intellij.refactoring.safeDelete.SafeDeleteHandler; +import com.intellij.ui.ScrollPaneFactory; +import com.intellij.ui.components.JBScrollPane; import com.intellij.util.IncorrectOperationException; import com.intellij.util.containers.HashMap; import com.intellij.util.containers.HashSet; import com.intellij.util.text.CharArrayUtil; import com.intellij.util.text.DateFormatUtil; +import com.intellij.util.ui.JBUI; import com.intellij.util.ui.UIUtil; import org.jdom.Element; import org.jetbrains.annotations.NonNls; @@ -50,6 +54,8 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; +import javax.swing.text.html.HTMLEditorKit; +import javax.swing.text.html.StyleSheet; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.util.*; @@ -100,7 +106,7 @@ public class UnusedDeclarationPresentation extends DefaultInspectionToolPresenta @Override @NotNull - public HTMLComposerImpl getComposer() { + public DeadHTMLComposer getComposer() { if (myComposer == null) { myComposer = new DeadHTMLComposer(this); } @@ -527,9 +533,14 @@ public class UnusedDeclarationPresentation extends DefaultInspectionToolPresenta htmlView.setContentType(UIUtil.HTML_MIME); htmlView.setEditable(false); htmlView.setOpaque(false); + final StyleSheet css = ((HTMLEditorKit)htmlView.getEditorKit()).getStyleSheet(); + css.addRule("p.problem-description-group {text-indent: " + JBUI.scale(12) + "px;font-weight:bold;}"); + css.addRule("div.problem-description {margin-left: 0;}"); + css.addRule("ul {margin-left:" + JBUI.scale(22) + "px;text-indent: 0}"); final StringBuffer buf = new StringBuffer(); - getComposer().compose(buf, entity); - htmlView.setText(buf.toString()); - return htmlView; + getComposer().compose(buf, entity, false); + final String text = buf.toString(); + SingleInspectionProfilePanel.readHTML(htmlView, SingleInspectionProfilePanel.toHTML(htmlView, text, false)); + return ScrollPaneFactory.createScrollPane(htmlView, true); } } diff --git a/platform/analysis-api/src/com/intellij/codeInspection/HTMLComposer.java b/platform/analysis-api/src/com/intellij/codeInspection/HTMLComposer.java index 59c2683e5c19..19fe283630ed 100644 --- a/platform/analysis-api/src/com/intellij/codeInspection/HTMLComposer.java +++ b/platform/analysis-api/src/com/intellij/codeInspection/HTMLComposer.java @@ -39,10 +39,9 @@ public abstract class HTMLComposer { public abstract void appendListItem(StringBuffer buf, RefElement refElement); public static void appendHeading(@NonNls StringBuffer buf, String name){ - buf.append("  ") - .append(name) - .append(""); + buf.append("

") + .append(name) + .append("

"); } public abstract void appendElementReference(StringBuffer buf, RefElement refElement, boolean isPackageIncluded); @@ -55,6 +54,10 @@ public abstract class HTMLComposer { public abstract void startListItem(@NonNls StringBuffer buf); + /** + * Use css for indentations + */ + @Deprecated public static void appendAfterHeaderIndention(@NonNls StringBuffer buf) { buf.append("     "); } diff --git a/platform/analysis-impl/src/com/intellij/codeInspection/ex/HTMLComposerImpl.java b/platform/analysis-impl/src/com/intellij/codeInspection/ex/HTMLComposerImpl.java index a00ffe6c7f4d..dbc5bbde30a6 100644 --- a/platform/analysis-impl/src/com/intellij/codeInspection/ex/HTMLComposerImpl.java +++ b/platform/analysis-impl/src/com/intellij/codeInspection/ex/HTMLComposerImpl.java @@ -322,6 +322,7 @@ public abstract class HTMLComposerImpl extends HTMLComposer { @Override public void startList(@NonNls final StringBuffer buf) { + buf.append("
"); buf.append("
"); } @Override diff --git a/platform/analysis-impl/src/com/intellij/codeInspection/export/HTMLExporter.java b/platform/analysis-impl/src/com/intellij/codeInspection/export/HTMLExporter.java index 896d22588afe..80a7eada57cf 100644 --- a/platform/analysis-impl/src/com/intellij/codeInspection/export/HTMLExporter.java +++ b/platform/analysis-impl/src/com/intellij/codeInspection/export/HTMLExporter.java @@ -59,7 +59,11 @@ public class HTMLExporter { public void createPage(RefEntity element) throws IOException { final String currentFileName = fileNameForElement(element); - StringBuffer buf = new StringBuffer(""); + StringBuffer buf = new StringBuffer("" + + "\n" + + "\n" + + "" + + ""); appendNavBar(buf, element); myComposer.composeWithExporter(buf, element, this); buf.append(""); diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ex/DescriptorComposer.java b/platform/lang-impl/src/com/intellij/codeInspection/ex/DescriptorComposer.java index c94884f1d527..f45090991052 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ex/DescriptorComposer.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ex/DescriptorComposer.java @@ -51,7 +51,7 @@ public class DescriptorComposer extends HTMLComposerImpl { genPageHeader(buf, refEntity); if (myTool.getDescriptions(refEntity) != null) { appendHeading(buf, InspectionsBundle.message("inspection.problem.synopsis")); - + buf.append("
"); CommonProblemDescriptor[] descriptions = myTool.getDescriptions(refEntity); LOG.assertTrue(descriptions != null); @@ -66,6 +66,7 @@ public class DescriptorComposer extends HTMLComposerImpl { } doneList(buf); + buf.append("
"); appendResolution(buf,refEntity, quickFixTexts(refEntity, myTool)); } diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ui/actions/ExportHTMLAction.java b/platform/lang-impl/src/com/intellij/codeInspection/ui/actions/ExportHTMLAction.java index 0931387fcf8a..13025b6ac304 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ui/actions/ExportHTMLAction.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ui/actions/ExportHTMLAction.java @@ -51,7 +51,9 @@ import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.JDOMUtil; import com.intellij.psi.codeStyle.CodeStyleSettingsManager; import com.intellij.util.ThrowableRunnable; +import com.intellij.util.ui.UIUtil; import com.intellij.util.ui.tree.TreeUtil; +import org.intellij.lang.annotations.Language; import org.jdom.Document; import org.jdom.Element; import org.jetbrains.annotations.NonNls; @@ -71,6 +73,9 @@ public class ExportHTMLAction extends AnAction implements DumbAware { @NonNls private static final String PROBLEMS = "problems"; @NonNls private static final String HTML = "HTML"; @NonNls private static final String XML = "XML"; + @NonNls + private static String CSS = ""; + public ExportHTMLAction(final InspectionResultsView view) { super(InspectionsBundle.message("inspection.action.export.html"), null, AllIcons.Actions.Export); @@ -293,7 +298,11 @@ public class ExportHTMLAction extends AnAction implements DumbAware { List packageContent = new ArrayList(content.get(packageName)); Collections.sort(packageContent, RefEntityAlphabeticalComparator.getInstance()); StringBuffer contentIndex = new StringBuffer(); - contentIndex.append(""); + contentIndex.append("" + + "\n" + + "\n" + + "" + + ""); for (RefEntity refElement : packageContent) { refElement = refElement.getRefManager().getRefinedElement(refElement); contentIndex.append(""); HTMLExportUtil.writeFile(exporter.getRootFolder(), packageName + "-index.html", contentIndex, myView.getProject()); + CSS = "p.problem-description-group {color: %s; font-weight:bold;}\n" + + "."; + HTMLExportUtil.writeFile(exporter.getRootFolder(), "inspection-report-style.css", String.format(CSS, UIUtil.isUnderDarcula() ? "#A5C25C" : "#005555"), myView.getProject()); } final Set modules = new HashSet(); From bd395499ed2959287f328b55701fba4ad8ac0bce Mon Sep 17 00:00:00 2001 From: Valentina Kiryushkina Date: Thu, 14 Jan 2016 19:46:37 +0300 Subject: [PATCH 02/83] For #PY-2748 Resolve to argument with redundant parentheses and to binary expression argument in percent string --- .../PySubstitutionChunkReference.java | 85 +++++++++++++++---- ...ercentStringArgWithRedundantParentheses.py | 1 + .../PercentStringBinaryStatementArg.py | 1 + .../com/jetbrains/python/PyResolveTest.java | 13 +++ 4 files changed, 84 insertions(+), 16 deletions(-) create mode 100644 python/testData/resolve/PercentStringArgWithRedundantParentheses.py create mode 100644 python/testData/resolve/PercentStringBinaryStatementArg.py diff --git a/python/src/com/jetbrains/python/codeInsight/PySubstitutionChunkReference.java b/python/src/com/jetbrains/python/codeInsight/PySubstitutionChunkReference.java index 8dedf328f645..ddbe2fc3e24d 100644 --- a/python/src/com/jetbrains/python/codeInsight/PySubstitutionChunkReference.java +++ b/python/src/com/jetbrains/python/codeInsight/PySubstitutionChunkReference.java @@ -91,29 +91,73 @@ public class PySubstitutionChunkReference extends PsiReferenceBase myPosition) { - return elements[myPosition]; - } - } + result = resolvePositional((PyParenthesizedExpression)rightExpression); } else if (rightExpression instanceof PyDictLiteralExpression) { - if (myChunk.getMappingKey() != null) { - final PyKeyValueExpression[] keyValueExpressions = ((PyDictLiteralExpression)rightExpression).getElements(); - for (PyKeyValueExpression keyValueExpression: keyValueExpressions) { - final PyStringLiteralExpression key = (PyStringLiteralExpression)keyValueExpression.getKey(); - if (key.getStringValue().equals(myChunk.getMappingKey())) { - return key; - } + result = resolveKeyword((PyDictLiteralExpression)rightExpression); + } + } + return result == null ? getElement() : result; + } + + @Nullable + private PsiElement resolveKeyword(PyDictLiteralExpression rightExpression) { + if (myChunk.getMappingKey() != null) { + final PyKeyValueExpression[] keyValueExpressions = rightExpression.getElements(); + for (PyKeyValueExpression keyValueExpression: keyValueExpressions) { + final PyStringLiteralExpression key = (PyStringLiteralExpression)keyValueExpression.getKey(); + if (key.getStringValue().equals(myChunk.getMappingKey())) { + return key; + } + } + } + return null; + } + + @Nullable + private PsiElement resolvePositional(PyParenthesizedExpression rightExpression) { + PsiElement result = null; + + final PyExpression containedExpression = getContainedExpression(rightExpression); + if (containedExpression instanceof PyTupleExpression) { + final PyExpression[] elements = ((PySequenceExpression)containedExpression).getElements(); + if (elements.length > myPosition) { + result = elements[myPosition]; + } + } + else if (containedExpression instanceof PyBinaryExpression && ((PyBinaryExpression)containedExpression).isOperator("+")) { + result = processNotNestedBinaryExpression((PyBinaryExpression)containedExpression); + } + return result; + } + + @Nullable + private PsiElement processNotNestedBinaryExpression(PyBinaryExpression containedExpression) { + PyExpression left = containedExpression.getLeftExpression(); + PyExpression right = containedExpression.getRightExpression(); + if (left instanceof PyParenthesizedExpression) { + PyExpression leftTuple = getContainedExpression((PyParenthesizedExpression)left); + if (leftTuple instanceof PyTupleExpression) { + PyExpression[] leftTupleElements = ((PyTupleExpression)leftTuple).getElements(); + int leftTupleLength = leftTupleElements.length; + if (leftTupleLength > myPosition) { + return leftTupleElements[myPosition]; + } + if (right instanceof PyParenthesizedExpression) { + PyExpression rightTuple = ((PyParenthesizedExpression)right).getContainedExpression(); + if (rightTuple instanceof PyTupleExpression) { + PyExpression[] rigthTupleElements = ((PyTupleExpression)rightTuple).getElements(); + int rightLength = rigthTupleElements.length; + if (leftTupleLength + rightLength > myPosition) + return rigthTupleElements[myPosition - leftTupleLength]; } } } @@ -121,6 +165,15 @@ public class PySubstitutionChunkReference extends PsiReferenceBase%d%s" % ((("1", " ")))) \ No newline at end of file diff --git a/python/testData/resolve/PercentStringBinaryStatementArg.py b/python/testData/resolve/PercentStringBinaryStatementArg.py new file mode 100644 index 000000000000..44e33d49b0ee --- /dev/null +++ b/python/testData/resolve/PercentStringBinaryStatementArg.py @@ -0,0 +1 @@ +print("%d%s" % (("1",) + (" "))) \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PyResolveTest.java b/python/testSrc/com/jetbrains/python/PyResolveTest.java index 488ed1b2da8f..b18a7ee770b2 100644 --- a/python/testSrc/com/jetbrains/python/PyResolveTest.java +++ b/python/testSrc/com/jetbrains/python/PyResolveTest.java @@ -659,4 +659,17 @@ public class PyResolveTest extends PyResolveTestCase { public void testGlobalNotDefinedAtTopLevel() { assertResolvesTo(PyTargetExpression.class, "foo"); } + + //PY-2478 + public void testPercentStringBinaryStatementArg() { + PsiElement target = resolve(); + assertTrue(target instanceof PyStringLiteralExpression); + assertTrue(((PyStringLiteralExpression)target).getStringValue().equals("1")); + } + + public void testPercentStringArgWithRedundantParentheses() { + PsiElement target = resolve(); + assertTrue(target instanceof PyStringLiteralExpression); + assertTrue(((PyStringLiteralExpression)target).getStringValue().equals("1")); + } } From 8a980feda7f1ef23629ad512bef578272138fb3e Mon Sep 17 00:00:00 2001 From: Valentina Kiryushkina Date: Tue, 9 Feb 2016 18:49:33 +0300 Subject: [PATCH 03/83] Fix #PY-18115, #PY-18401 and rename problems * Stop show unresolved reference warning if argument of format string is PyReferenceExpression * Fix exception caused by trying to rename not PsiNamedElement: register rename processor, that accepts all literal expressions, but do rename for string only and throw UnsupportedOperationException for others * Add resolve for packed dicts, lists and tuples --- python/src/META-INF/python-core-common.xml | 1 + .../PySubstitutionChunkReference.java | 140 +++++++++++++----- .../PyUnresolvedReferencesInspection.java | 5 + .../RenamePyLiteralExpressionProcessor.java | 74 +++++++++ 4 files changed, 184 insertions(+), 36 deletions(-) create mode 100644 python/src/com/jetbrains/python/refactoring/rename/RenamePyLiteralExpressionProcessor.java diff --git a/python/src/META-INF/python-core-common.xml b/python/src/META-INF/python-core-common.xml index 6cdbf4fa9090..40b3e80d0b29 100644 --- a/python/src/META-INF/python-core-common.xml +++ b/python/src/META-INF/python-core-common.xml @@ -452,6 +452,7 @@ + diff --git a/python/src/com/jetbrains/python/codeInsight/PySubstitutionChunkReference.java b/python/src/com/jetbrains/python/codeInsight/PySubstitutionChunkReference.java index ddbe2fc3e24d..898c1f9aa335 100644 --- a/python/src/com/jetbrains/python/codeInsight/PySubstitutionChunkReference.java +++ b/python/src/com/jetbrains/python/codeInsight/PySubstitutionChunkReference.java @@ -30,6 +30,7 @@ import org.jetbrains.annotations.Nullable; public class PySubstitutionChunkReference extends PsiReferenceBase implements PsiReferenceEx{ private final int myPosition; private final PyStringFormatParser.SubstitutionChunk myChunk; + private boolean myIgnoreUnresolved = false; public PySubstitutionChunkReference(@NotNull final PyStringLiteralExpression element, @NotNull final PyStringFormatParser.SubstitutionChunk chunk, final int position) { @@ -37,10 +38,11 @@ public class PySubstitutionChunkReference extends PsiReferenceBase 0) { final PyExpression[] arguments = argumentList.getArguments(); - if (myChunk.getMappingKey() != null) { + + boolean isStarArgument = arguments.length == 1 && arguments[0] instanceof PyStarArgument; + if (isStarArgument) return getUnderStarExpression(arguments); + + boolean isKeywordSubstitution = myChunk.getMappingKey() != null; + if (isKeywordSubstitution) { return argumentList.getKeywordArgument(myChunk.getMappingKey()); } else { final int position = myChunk.getPosition() == null ? myPosition : myChunk.getPosition(); - if (arguments.length == 1 && arguments[0] instanceof PyStarArgument) { - return arguments[0]; - } - else if (position < arguments.length) { - return arguments[position]; - } + if (position < arguments.length) return arguments[position]; + + if (arguments[0] instanceof PyBinaryExpression && ((PyBinaryExpression)arguments[0]).isOperator("+")) { + return processNotNestedBinaryExpression((PyBinaryExpression)arguments[0]); } } + } return null; } @@ -94,24 +102,27 @@ public class PySubstitutionChunkReference extends PsiReferenceBase myPosition) { - result = elements[myPosition]; + if (expression instanceof PyParenthesizedExpression) { + final PyExpression containedExpression = getContainedExpression((PyParenthesizedExpression)expression); + + if (containedExpression instanceof PyTupleExpression) { + final PyExpression[] elements = ((PySequenceExpression)containedExpression).getElements(); + if (elements.length > myPosition) { + result = elements[myPosition]; + } + } + else if (containedExpression instanceof PyBinaryExpression && ((PyBinaryExpression)containedExpression).isOperator("+")) { + result = processNotNestedBinaryExpression((PyBinaryExpression)containedExpression); + } + else if (containedExpression instanceof PyReferenceExpression) { + myIgnoreUnresolved = true; } } - else if (containedExpression instanceof PyBinaryExpression && ((PyBinaryExpression)containedExpression).isOperator("+")) { - result = processNotNestedBinaryExpression((PyBinaryExpression)containedExpression); + else if (expression instanceof PyReferenceExpression) { + myIgnoreUnresolved = true; } return result; } @@ -154,10 +174,10 @@ public class PySubstitutionChunkReference extends PsiReferenceBase myPosition) - return rigthTupleElements[myPosition - leftTupleLength]; + return rightTupleElements[myPosition - leftTupleLength]; } } } @@ -180,9 +200,57 @@ public class PySubstitutionChunkReference extends PsiReferenceBase Date: Tue, 9 Feb 2016 18:52:01 +0300 Subject: [PATCH 04/83] Add test for #PY-2748 --- .../resolve/FormatDoubleStarArgument.py | 1 + .../resolve/FormatStringWithBinExprAsArg.py | 1 + .../FormatStringWithPackedDictAsArgument.py | 1 + .../FormatStringWithPackedListAsArgument.py | 1 + .../FormatStringWithPackedTupleAsArgument.py | 1 + .../resolve/FormatStringWithRefAsArgument.py | 2 + python/testData/resolve/PercentKeyWordArgs.py | 2 +- ...ercentStringArgWithRedundantParentheses.py | 2 +- .../PercentStringKeyWordArgWithParentheses.py | 1 + .../resolve/PercentStringWithRefAsArgument.py | 2 + .../com/jetbrains/python/PyResolveTest.java | 56 +++++++++++++++++-- 11 files changed, 62 insertions(+), 8 deletions(-) create mode 100644 python/testData/resolve/FormatDoubleStarArgument.py create mode 100644 python/testData/resolve/FormatStringWithBinExprAsArg.py create mode 100644 python/testData/resolve/FormatStringWithPackedDictAsArgument.py create mode 100644 python/testData/resolve/FormatStringWithPackedListAsArgument.py create mode 100644 python/testData/resolve/FormatStringWithPackedTupleAsArgument.py create mode 100644 python/testData/resolve/FormatStringWithRefAsArgument.py create mode 100644 python/testData/resolve/PercentStringKeyWordArgWithParentheses.py create mode 100644 python/testData/resolve/PercentStringWithRefAsArgument.py diff --git a/python/testData/resolve/FormatDoubleStarArgument.py b/python/testData/resolve/FormatDoubleStarArgument.py new file mode 100644 index 000000000000..7d836c3ea1dd --- /dev/null +++ b/python/testData/resolve/FormatDoubleStarArgument.py @@ -0,0 +1 @@ +print "first is {fst}, second is {snd}".format(**{"fst": "f", "snd": "s"}) \ No newline at end of file diff --git a/python/testData/resolve/FormatStringWithBinExprAsArg.py b/python/testData/resolve/FormatStringWithBinExprAsArg.py new file mode 100644 index 000000000000..f2dd1d78671d --- /dev/null +++ b/python/testData/resolve/FormatStringWithBinExprAsArg.py @@ -0,0 +1 @@ +v = "first is {}, second is""{}".format(("fst", ) + ("snd", )) \ No newline at end of file diff --git a/python/testData/resolve/FormatStringWithPackedDictAsArgument.py b/python/testData/resolve/FormatStringWithPackedDictAsArgument.py new file mode 100644 index 000000000000..fc67dfaf2728 --- /dev/null +++ b/python/testData/resolve/FormatStringWithPackedDictAsArgument.py @@ -0,0 +1 @@ +v = "first is" "{fst}, second is {snd}".format(**{"fst": "f", "snd": "s"}) \ No newline at end of file diff --git a/python/testData/resolve/FormatStringWithPackedListAsArgument.py b/python/testData/resolve/FormatStringWithPackedListAsArgument.py new file mode 100644 index 000000000000..87b9dc8ee7a7 --- /dev/null +++ b/python/testData/resolve/FormatStringWithPackedListAsArgument.py @@ -0,0 +1 @@ +print "first is {}, second is {}".format(*[1, 2]) \ No newline at end of file diff --git a/python/testData/resolve/FormatStringWithPackedTupleAsArgument.py b/python/testData/resolve/FormatStringWithPackedTupleAsArgument.py new file mode 100644 index 000000000000..fb3414b5d71e --- /dev/null +++ b/python/testData/resolve/FormatStringWithPackedTupleAsArgument.py @@ -0,0 +1 @@ +v = "first is {}, second is {}".format(*("fst", "snd")) \ No newline at end of file diff --git a/python/testData/resolve/FormatStringWithRefAsArgument.py b/python/testData/resolve/FormatStringWithRefAsArgument.py new file mode 100644 index 000000000000..b5da9c3a5e87 --- /dev/null +++ b/python/testData/resolve/FormatStringWithRefAsArgument.py @@ -0,0 +1,2 @@ +reference = (1, 2) +v = "first is {}, second is {}".format(*reference) \ No newline at end of file diff --git a/python/testData/resolve/PercentKeyWordArgs.py b/python/testData/resolve/PercentKeyWordArgs.py index e7345375c532..97188523141a 100644 --- a/python/testData/resolve/PercentKeyWordArgs.py +++ b/python/testData/resolve/PercentKeyWordArgs.py @@ -1 +1 @@ -"This is my favourite number%(""kwg)d!" % {'kwg': 4181} \ No newline at end of file +"This is my favourite number%(""kwg)d!" % {'kwg': 4181} diff --git a/python/testData/resolve/PercentStringArgWithRedundantParentheses.py b/python/testData/resolve/PercentStringArgWithRedundantParentheses.py index 351908048735..eaad91f7d66e 100644 --- a/python/testData/resolve/PercentStringArgWithRedundantParentheses.py +++ b/python/testData/resolve/PercentStringArgWithRedundantParentheses.py @@ -1 +1 @@ -print("%d%s" % ((("1", " ")))) \ No newline at end of file +print("%s%s" % ((("1", " ")))) \ No newline at end of file diff --git a/python/testData/resolve/PercentStringKeyWordArgWithParentheses.py b/python/testData/resolve/PercentStringKeyWordArgWithParentheses.py new file mode 100644 index 000000000000..f869a7fad425 --- /dev/null +++ b/python/testData/resolve/PercentStringKeyWordArgWithParentheses.py @@ -0,0 +1 @@ +v = "first is %(fst)s, second is" "%(snd)s" % ({"fst": "f", "snd": "s"}) \ No newline at end of file diff --git a/python/testData/resolve/PercentStringWithRefAsArgument.py b/python/testData/resolve/PercentStringWithRefAsArgument.py new file mode 100644 index 000000000000..5f884037a27d --- /dev/null +++ b/python/testData/resolve/PercentStringWithRefAsArgument.py @@ -0,0 +1,2 @@ +tuple = (1, 2) +v = "first is" "%s, second is %s" % tuple \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PyResolveTest.java b/python/testSrc/com/jetbrains/python/PyResolveTest.java index b18a7ee770b2..126892f9af5e 100644 --- a/python/testSrc/com/jetbrains/python/PyResolveTest.java +++ b/python/testSrc/com/jetbrains/python/PyResolveTest.java @@ -637,6 +637,36 @@ public class PyResolveTest extends PyResolveTestCase { assertTrue(target instanceof PyKeywordArgument); assertEquals("kwd", ((PyKeywordArgument)target).getKeyword()); } + + public void testFormatStringWithPackedDictAsArgument() { + PsiElement target = resolve(); + assertTrue(target instanceof PyStringLiteralExpression); + assertEquals("\"fst\"", target.getText()); + } + + public void testFormatStringWithPackedListAsArgument() { + PsiElement target = resolve(); + assertTrue(target instanceof PyNumericLiteralExpression); + assertEquals("1", target.getText()); + } + + public void testFormatStringWithPackedTupleAsArgument() { + PsiElement target = resolve(); + assertTrue(target instanceof PyStringLiteralExpression); + assertEquals("\"snd\"", target.getText()); + } + + public void testFormatStringWithBinExprAsArg() { + PsiElement target = resolve(); + assertTrue(target instanceof PyStringLiteralExpression); + assertEquals("\"snd\"", target.getText()); + } + + public void testFormatStringWithRefAsArgument() { + PsiElement target = resolve(); + assertEquals(null, target); + } + //PY-2478 public void testPercentPositionalArgs() { @@ -654,22 +684,36 @@ public class PyResolveTest extends PyResolveTestCase { // PY-18254 public void testFunctionTypeComment() { assertResolvesTo(PyClass.class, "MyClass"); - } - - public void testGlobalNotDefinedAtTopLevel() { - assertResolvesTo(PyTargetExpression.class, "foo"); + } + + public void testPercentStringKeyWordArgWithParentheses() { + PsiElement target = resolve(); + assertTrue(target instanceof PyStringLiteralExpression); + assertEquals("snd", ((PyStringLiteralExpression)target).getStringValue()); } //PY-2478 public void testPercentStringBinaryStatementArg() { PsiElement target = resolve(); assertTrue(target instanceof PyStringLiteralExpression); - assertTrue(((PyStringLiteralExpression)target).getStringValue().equals("1")); + assertEquals("1", ((PyStringLiteralExpression)target).getStringValue()); } + //PY-2478 public void testPercentStringArgWithRedundantParentheses() { PsiElement target = resolve(); assertTrue(target instanceof PyStringLiteralExpression); - assertTrue(((PyStringLiteralExpression)target).getStringValue().equals("1")); + assertEquals("1", ((PyStringLiteralExpression)target).getStringValue()); } + + public void testPercentStringWithRefAsArgument() { + PsiElement target = resolve(); + assertEquals(null, target); + } + + + public void testGlobalNotDefinedAtTopLevel() { + assertResolvesTo(PyTargetExpression.class, "foo"); + } + } From baee11599d14f8b69bc782769350888edd99248f Mon Sep 17 00:00:00 2001 From: Valentina Kiryushkina Date: Mon, 15 Feb 2016 20:20:29 +0300 Subject: [PATCH 05/83] For #PY-2748 Fix false positives, rename issues, add tests and minor fixes according to review * Don't process renaming string literal expression if it's PyKeyValueExpression: it corrupts user's code by renaming only key-value expression and leaving substitution key the same * Pass isPercentString as constructor parameter, use PyPsiUtil.flattenParens(), rename method(process...-> resolve...) * Fix false positive with function call and add tests * In percent string resolve to all literal expressions, not only string literal * In RenamePyLiteralExpressionProcessor always throw IncorrectOperationException --- .../PySubstitutionChunkReference.java | 90 ++++++++++++------- ...ythonFormattedStringReferenceProvider.java | 9 +- .../RenamePyLiteralExpressionProcessor.java | 12 +-- .../formatStringKeyword.py | 1 + .../formatStringPackedDict.py | 1 + .../formatStringPackedDictCall.py | 1 + .../formatStringPackedFunctionCall.py | 5 ++ .../formatStringPackedReference.py | 2 + .../formatStringPositional.py | 1 + .../percentStringFunctionCall.py | 4 + .../percentStringKeyword.py | 1 + .../percentStringPositional.py | 1 + .../percentStringReference.py | 2 + .../rename/formatStringDictLiteral.py | 1 + .../formatStringNumericLiteralExpression.py | 1 + .../resolve/FormatDoubleStarArgument.py | 1 - .../resolve/FormatStringPackedDictCall.py | 1 + .../testData/resolve/PercentStringDictCall.py | 1 + .../PercentStringWithOneStringArgument.py | 1 + .../com/jetbrains/python/PyResolveTest.java | 50 ++++++++--- .../PyUnresolvedReferencesInspectionTest.java | 50 +++++++++++ .../python/refactoring/PyRenameTest.java | 30 ++++++- 22 files changed, 205 insertions(+), 61 deletions(-) create mode 100644 python/testData/inspections/PyUnresolvedReferencesInspection/formatStringKeyword.py create mode 100644 python/testData/inspections/PyUnresolvedReferencesInspection/formatStringPackedDict.py create mode 100644 python/testData/inspections/PyUnresolvedReferencesInspection/formatStringPackedDictCall.py create mode 100644 python/testData/inspections/PyUnresolvedReferencesInspection/formatStringPackedFunctionCall.py create mode 100644 python/testData/inspections/PyUnresolvedReferencesInspection/formatStringPackedReference.py create mode 100644 python/testData/inspections/PyUnresolvedReferencesInspection/formatStringPositional.py create mode 100644 python/testData/inspections/PyUnresolvedReferencesInspection/percentStringFunctionCall.py create mode 100644 python/testData/inspections/PyUnresolvedReferencesInspection/percentStringKeyword.py create mode 100644 python/testData/inspections/PyUnresolvedReferencesInspection/percentStringPositional.py create mode 100644 python/testData/inspections/PyUnresolvedReferencesInspection/percentStringReference.py create mode 100644 python/testData/refactoring/rename/formatStringDictLiteral.py create mode 100644 python/testData/refactoring/rename/formatStringNumericLiteralExpression.py delete mode 100644 python/testData/resolve/FormatDoubleStarArgument.py create mode 100644 python/testData/resolve/FormatStringPackedDictCall.py create mode 100644 python/testData/resolve/PercentStringDictCall.py create mode 100644 python/testData/resolve/PercentStringWithOneStringArgument.py diff --git a/python/src/com/jetbrains/python/codeInsight/PySubstitutionChunkReference.java b/python/src/com/jetbrains/python/codeInsight/PySubstitutionChunkReference.java index 898c1f9aa335..0dbd8765dd47 100644 --- a/python/src/com/jetbrains/python/codeInsight/PySubstitutionChunkReference.java +++ b/python/src/com/jetbrains/python/codeInsight/PySubstitutionChunkReference.java @@ -23,6 +23,7 @@ import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.ArrayUtil; import com.jetbrains.python.inspections.PyStringFormatParser; import com.jetbrains.python.psi.*; +import com.jetbrains.python.psi.impl.PyPsiUtils; import com.jetbrains.python.psi.types.TypeEvalContext; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -30,13 +31,15 @@ import org.jetbrains.annotations.Nullable; public class PySubstitutionChunkReference extends PsiReferenceBase implements PsiReferenceEx{ private final int myPosition; private final PyStringFormatParser.SubstitutionChunk myChunk; + private final boolean myIsPercent; private boolean myIgnoreUnresolved = false; public PySubstitutionChunkReference(@NotNull final PyStringLiteralExpression element, - @NotNull final PyStringFormatParser.SubstitutionChunk chunk, final int position) { + @NotNull final PyStringFormatParser.SubstitutionChunk chunk, final int position, boolean isPercent) { super(element, getKeyWordRange(element, chunk)); myChunk = chunk; myPosition = position; + myIsPercent = isPercent; } @Nullable @@ -64,8 +67,7 @@ public class PySubstitutionChunkReference extends PsiReferenceBase myPosition) { - result = elements[myPosition]; - } - } - else if (containedExpression instanceof PyBinaryExpression && ((PyBinaryExpression)containedExpression).isOperator("+")) { - result = processNotNestedBinaryExpression((PyBinaryExpression)containedExpression); - } - else if (containedExpression instanceof PyReferenceExpression) { - myIgnoreUnresolved = true; + containedExpression = PyPsiUtils.flattenParens(expression); + } + if (containedExpression instanceof PyTupleExpression) { + final PyExpression[] elements = ((PySequenceExpression)containedExpression).getElements(); + if (elements.length > myPosition) { + result = elements[myPosition]; } } - else if (expression instanceof PyReferenceExpression) { + else if (containedExpression instanceof PyBinaryExpression && ((PyBinaryExpression)containedExpression).isOperator("+")) { + result = resolveNotNestedBinaryExpression((PyBinaryExpression)containedExpression); + } + else if (containedExpression instanceof PyLiteralExpression && myPosition == 0) { + return expression; + } + else if (containedExpression instanceof PyCallExpression) { + return resolveCallExpression((PyCallExpression)expression); + } + else if (containedExpression instanceof PyReferenceExpression) { myIgnoreUnresolved = true; } return result; } @Nullable - private PsiElement processNotNestedBinaryExpression(PyBinaryExpression containedExpression) { + private PsiElement resolveNotNestedBinaryExpression(PyBinaryExpression containedExpression) { PyExpression left = containedExpression.getLeftExpression(); PyExpression right = containedExpression.getRightExpression(); if (left instanceof PyParenthesizedExpression) { - PyExpression leftTuple = getContainedExpression((PyParenthesizedExpression)left); + PyExpression leftTuple = PyPsiUtils.flattenParens(left); if (leftTuple instanceof PyTupleExpression) { PyExpression[] leftTupleElements = ((PyTupleExpression)leftTuple).getElements(); int leftTupleLength = leftTupleElements.length; @@ -172,7 +180,7 @@ public class PySubstitutionChunkReference extends PsiReferenceBase chunks = PyStringFormatParser.filterSubstitutions( PyStringFormatParser.parseNewStyleFormat(element.getStringValue())); - return getReferencesFromChunks(element, chunks); + return getReferencesFromChunks(element, chunks, false); } private static PsiReference[] getReferencesFromPercentString(@NotNull final PyStringLiteralExpression element) { final List chunks = PyStringFormatParser.filterSubstitutions(PyStringFormatParser.parsePercentFormat(element.getStringValue())); - return getReferencesFromChunks(element, chunks); + return getReferencesFromChunks(element, chunks, true); } @NotNull private static PsiReference[] getReferencesFromChunks(@NotNull final PyStringLiteralExpression element, - @NotNull final List chunks) { + @NotNull final List chunks, + boolean isPercent) { final PsiReference[] result = new PsiReference[chunks.size()]; if (!element.isDocString()) { for (int i = 0; i < chunks.size(); i++) { final PyStringFormatParser.SubstitutionChunk chunk = chunks.get(i); - result[i] = new PySubstitutionChunkReference(element, chunk, i); + result[i] = new PySubstitutionChunkReference(element, chunk, i, isPercent); } } return result; diff --git a/python/src/com/jetbrains/python/refactoring/rename/RenamePyLiteralExpressionProcessor.java b/python/src/com/jetbrains/python/refactoring/rename/RenamePyLiteralExpressionProcessor.java index c4fed0238705..8910129755b5 100644 --- a/python/src/com/jetbrains/python/refactoring/rename/RenamePyLiteralExpressionProcessor.java +++ b/python/src/com/jetbrains/python/refactoring/rename/RenamePyLiteralExpressionProcessor.java @@ -17,18 +17,16 @@ package com.jetbrains.python.refactoring.rename; import com.intellij.openapi.editor.Editor; import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiReference; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.refactoring.listeners.RefactoringElementListener; import com.intellij.usageView.UsageInfo; import com.intellij.util.IncorrectOperationException; import com.jetbrains.python.codeInsight.PyCodeInsightSettings; -import com.jetbrains.python.psi.*; +import com.jetbrains.python.psi.PyLiteralExpression; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class RenamePyLiteralExpressionProcessor extends RenamePyElementProcessor { - private static final Class[] UNSUPPORTED = {PyNumericLiteralExpression.class, PyNoneLiteralExpression.class, PyBoolLiteralExpression.class}; @Override public boolean canProcessElement(@NotNull PsiElement element) { return PsiTreeUtil.instanceOf(element, PyLiteralExpression.class); @@ -37,13 +35,7 @@ public class RenamePyLiteralExpressionProcessor extends RenamePyElementProcessor @Override public void renameElement(PsiElement element, String newName, UsageInfo[] usages, @Nullable RefactoringElementListener listener) throws IncorrectOperationException { - if (PsiTreeUtil.instanceOf(element, UNSUPPORTED)) throw new IncorrectOperationException(); - ((PyStringLiteralExpression)element).updateText("\"" + newName + "\""); - for (UsageInfo usageInfo: usages) { - PsiReference reference = usageInfo.getReference(); - if (reference == null) return; - reference.handleElementRename("\"" + newName + "\""); - } + throw new IncorrectOperationException(); } @Override diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/formatStringKeyword.py b/python/testData/inspections/PyUnresolvedReferencesInspection/formatStringKeyword.py new file mode 100644 index 000000000000..454a39f84094 --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection/formatStringKeyword.py @@ -0,0 +1 @@ +'{foo}'.format(boo=1) \ No newline at end of file diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/formatStringPackedDict.py b/python/testData/inspections/PyUnresolvedReferencesInspection/formatStringPackedDict.py new file mode 100644 index 000000000000..6248dd51403d --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection/formatStringPackedDict.py @@ -0,0 +1 @@ +'{foo}'.format(**{"boo": 1}) \ No newline at end of file diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/formatStringPackedDictCall.py b/python/testData/inspections/PyUnresolvedReferencesInspection/formatStringPackedDictCall.py new file mode 100644 index 000000000000..dfbb03d2a4f1 --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection/formatStringPackedDictCall.py @@ -0,0 +1 @@ +'{foo}'.format(**dict(t=1)) \ No newline at end of file diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/formatStringPackedFunctionCall.py b/python/testData/inspections/PyUnresolvedReferencesInspection/formatStringPackedFunctionCall.py new file mode 100644 index 000000000000..8aafa5a24125 --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection/formatStringPackedFunctionCall.py @@ -0,0 +1,5 @@ +def f(): + return dict(foo=0) + + +'{foo}'.format(**f()) \ No newline at end of file diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/formatStringPackedReference.py b/python/testData/inspections/PyUnresolvedReferencesInspection/formatStringPackedReference.py new file mode 100644 index 000000000000..c3e726eb7ab4 --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection/formatStringPackedReference.py @@ -0,0 +1,2 @@ +ref = {"fst": 1, "snd": 2} +print "first is {fst}, second is {snd}".format(**ref) \ No newline at end of file diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/formatStringPositional.py b/python/testData/inspections/PyUnresolvedReferencesInspection/formatStringPositional.py new file mode 100644 index 000000000000..fa8faafa8734 --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection/formatStringPositional.py @@ -0,0 +1 @@ +v = '{}'.format() \ No newline at end of file diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/percentStringFunctionCall.py b/python/testData/inspections/PyUnresolvedReferencesInspection/percentStringFunctionCall.py new file mode 100644 index 000000000000..c7dc76fd26dc --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection/percentStringFunctionCall.py @@ -0,0 +1,4 @@ +def f(): + return [1] + +"%s" % f() \ No newline at end of file diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/percentStringKeyword.py b/python/testData/inspections/PyUnresolvedReferencesInspection/percentStringKeyword.py new file mode 100644 index 000000000000..f4152f4c7ce3 --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection/percentStringKeyword.py @@ -0,0 +1 @@ +v = "first is %(fst)s" % {"snd": 2} \ No newline at end of file diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/percentStringPositional.py b/python/testData/inspections/PyUnresolvedReferencesInspection/percentStringPositional.py new file mode 100644 index 000000000000..36ad546c900a --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection/percentStringPositional.py @@ -0,0 +1 @@ +v = '%s' % (1) \ No newline at end of file diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/percentStringReference.py b/python/testData/inspections/PyUnresolvedReferencesInspection/percentStringReference.py new file mode 100644 index 000000000000..6f3a61ff0013 --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection/percentStringReference.py @@ -0,0 +1,2 @@ +d = {"fst": 1, "snd": 2} +print "first is %(fst)s, second is %(snd)s" % d \ No newline at end of file diff --git a/python/testData/refactoring/rename/formatStringDictLiteral.py b/python/testData/refactoring/rename/formatStringDictLiteral.py new file mode 100644 index 000000000000..7431aef24f14 --- /dev/null +++ b/python/testData/refactoring/rename/formatStringDictLiteral.py @@ -0,0 +1 @@ +"I want to rename this{to_be_renamed}".format(**{"to_be_renamed": "value"}) \ No newline at end of file diff --git a/python/testData/refactoring/rename/formatStringNumericLiteralExpression.py b/python/testData/refactoring/rename/formatStringNumericLiteralExpression.py new file mode 100644 index 000000000000..ee028401dae1 --- /dev/null +++ b/python/testData/refactoring/rename/formatStringNumericLiteralExpression.py @@ -0,0 +1 @@ +print "first is {}, second is {}".format(1, 2) \ No newline at end of file diff --git a/python/testData/resolve/FormatDoubleStarArgument.py b/python/testData/resolve/FormatDoubleStarArgument.py deleted file mode 100644 index 7d836c3ea1dd..000000000000 --- a/python/testData/resolve/FormatDoubleStarArgument.py +++ /dev/null @@ -1 +0,0 @@ -print "first is {fst}, second is {snd}".format(**{"fst": "f", "snd": "s"}) \ No newline at end of file diff --git a/python/testData/resolve/FormatStringPackedDictCall.py b/python/testData/resolve/FormatStringPackedDictCall.py new file mode 100644 index 000000000000..674703380784 --- /dev/null +++ b/python/testData/resolve/FormatStringPackedDictCall.py @@ -0,0 +1 @@ +'{foo}'.format(**dict(foo="fo")) diff --git a/python/testData/resolve/PercentStringDictCall.py b/python/testData/resolve/PercentStringDictCall.py new file mode 100644 index 000000000000..f5250ac2e19d --- /dev/null +++ b/python/testData/resolve/PercentStringDictCall.py @@ -0,0 +1 @@ +"first is %(fst)s" % dict(fst="hello") \ No newline at end of file diff --git a/python/testData/resolve/PercentStringWithOneStringArgument.py b/python/testData/resolve/PercentStringWithOneStringArgument.py new file mode 100644 index 000000000000..9ccdf86b06e9 --- /dev/null +++ b/python/testData/resolve/PercentStringWithOneStringArgument.py @@ -0,0 +1 @@ +v = "%s" % "hello" \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PyResolveTest.java b/python/testSrc/com/jetbrains/python/PyResolveTest.java index 126892f9af5e..d7cbfb6c97ac 100644 --- a/python/testSrc/com/jetbrains/python/PyResolveTest.java +++ b/python/testSrc/com/jetbrains/python/PyResolveTest.java @@ -611,70 +611,75 @@ public class PyResolveTest extends PyResolveTestCase { assertResolvesTo(PyFunction.class, "__rmatmul__"); } - //PY-2478 + //PY-2748 public void testFormatStringKWArgs() { PsiElement target = resolve(); assertTrue(target instanceof PyKeywordArgument); assertEquals("fst", ((PyKeywordArgument)target).getKeyword()); } - //PY-2478 + //PY-2748 public void testFormatPositionalArgs() { PsiElement target = resolve(); assertTrue(target instanceof PyReferenceExpression); assertEquals("string", target.getText()); } - //PY-2478 + //PY-2748 public void testFormatArgsAndKWargs() { PsiElement target = resolve(); assertTrue(target instanceof PyStringLiteralExpression); } - //PY-2478 + //PY-2748 public void testFormatArgsAndKWargs1() { PsiElement target = resolve(); assertTrue(target instanceof PyKeywordArgument); assertEquals("kwd", ((PyKeywordArgument)target).getKeyword()); } - + + //PY-2748 public void testFormatStringWithPackedDictAsArgument() { PsiElement target = resolve(); assertTrue(target instanceof PyStringLiteralExpression); assertEquals("\"fst\"", target.getText()); } + //PY-2748 public void testFormatStringWithPackedListAsArgument() { PsiElement target = resolve(); assertTrue(target instanceof PyNumericLiteralExpression); assertEquals("1", target.getText()); } + //PY-2748 public void testFormatStringWithPackedTupleAsArgument() { PsiElement target = resolve(); assertTrue(target instanceof PyStringLiteralExpression); assertEquals("\"snd\"", target.getText()); } - + + //PY-2748 public void testFormatStringWithBinExprAsArg() { PsiElement target = resolve(); assertTrue(target instanceof PyStringLiteralExpression); assertEquals("\"snd\"", target.getText()); } - + + //PY-2748 public void testFormatStringWithRefAsArgument() { PsiElement target = resolve(); assertEquals(null, target); } - //PY-2478 + //PY-2748 public void testPercentPositionalArgs() { PsiElement target = resolve(); assertTrue(target instanceof PyStringLiteralExpression); } - //PY-2478 + //PY-2748 public void testPercentKeyWordArgs() { PsiElement target = resolve(); assertTrue(target instanceof PyStringLiteralExpression); @@ -692,25 +697,44 @@ public class PyResolveTest extends PyResolveTestCase { assertEquals("snd", ((PyStringLiteralExpression)target).getStringValue()); } - //PY-2478 + //PY-2748 public void testPercentStringBinaryStatementArg() { PsiElement target = resolve(); assertTrue(target instanceof PyStringLiteralExpression); assertEquals("1", ((PyStringLiteralExpression)target).getStringValue()); } - //PY-2478 + //PY-2748 public void testPercentStringArgWithRedundantParentheses() { PsiElement target = resolve(); assertTrue(target instanceof PyStringLiteralExpression); assertEquals("1", ((PyStringLiteralExpression)target).getStringValue()); } - + + //PY-2748 public void testPercentStringWithRefAsArgument() { PsiElement target = resolve(); assertEquals(null, target); } - + + //PY-2748 + public void testPercentStringWithOneStringArgument() { + PsiElement target = resolve(); + assertEquals("hello", ((PyStringLiteralExpression)target).getStringValue()); + } + + //PY-2748 + public void testFormatStringPackedDictCall() { + PsiElement target = resolve(); + assertEquals("fo", ((PyStringLiteralExpression)((PyKeywordArgument)target).getValueExpression()).getStringValue()); + } + + //PY-2748 + public void testPercentStringDictCall() { + PsiElement target = resolve(); + assertEquals("hello", ((PyStringLiteralExpression)((PyKeywordArgument)target).getValueExpression()).getStringValue()); + } + public void testGlobalNotDefinedAtTopLevel() { assertResolvesTo(PyTargetExpression.class, "foo"); diff --git a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java index 19b1c24fb1bf..5eb1fe6ca0be 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java @@ -537,6 +537,56 @@ public class PyUnresolvedReferencesInspectionTest extends PyInspectionTestCase { doTest(); } + // PY-2748 + public void testFormatStringPackedDictCall() { + doTest(); + } + + // PY-2748 + public void testFormatStringPackedDict() { + doTest(); + } + + // PY-2748 + public void testFormatStringPositional() { + doTest(); + } + + // PY-2748 + public void testFormatStringKeyword() { + doTest(); + } + + // PY-2748 + public void testPercentStringPositional() { + doTest(); + } + + // PY-2748 + public void testPercentStringKeyword() { + doTest(); + } + + // PY-2748 + public void testFormatStringPackedFunctionCall() { + doTest(); + } + + // PY-2748 + public void testPercentStringFunctionCall() { + doTest(); + } + + // PY-2748 + public void testFormatStringPackedReference() { + doTest(); + } + + // PY-2748 + public void testPercentStringReference() { + doTest(); + } + // PY-18254 public void testVarargsAnnotatedWithFunctionComment() { doTest(); diff --git a/python/testSrc/com/jetbrains/python/refactoring/PyRenameTest.java b/python/testSrc/com/jetbrains/python/refactoring/PyRenameTest.java index 0da795a760f2..88bbd18ca102 100644 --- a/python/testSrc/com/jetbrains/python/refactoring/PyRenameTest.java +++ b/python/testSrc/com/jetbrains/python/refactoring/PyRenameTest.java @@ -236,11 +236,39 @@ public class PyRenameTest extends PyTestCase { renameWithDocStringFormat(DocStringFormat.NUMPY, "bar"); } - //PY-2478 + //PY-2748 public void testFormatStringKeyword() { doTest("renamed"); } + //PY-2748 + public void testFormatStringDictLiteral() { + myFixture.configureByFile(RENAME_DATA_PATH + getTestName(true) + ".py"); + try { + myFixture.renameElementAtCaret("renamed"); + } + catch (RuntimeException e) { + if ("com.intellij.util.IncorrectOperationException".equals(e.getMessage())) { + return; + } + } + fail(); + } + + //PY-2748 + public void testFormatStringNumericLiteralExpression() { + myFixture.configureByFile(RENAME_DATA_PATH + getTestName(true) + ".py"); + try { + myFixture.renameElementAtCaret("renamed"); + } + catch (RuntimeException e) { + if ("com.intellij.util.IncorrectOperationException".equals(e.getMessage())) { + return; + } + } + fail(); + } + private void renameWithDocStringFormat(DocStringFormat format, final String newName) { runWithDocStringFormat(format, new Runnable() { public void run() { From b6baa97f4d75f7cf991827fb17a23b50125dfee1 Mon Sep 17 00:00:00 2001 From: Valentina Kiryushkina Date: Wed, 2 Mar 2016 17:08:24 +0300 Subject: [PATCH 06/83] Fix #PY-18659 ClassCastException in PySubstitutionChunkReference.resolvePercentString --- .../PySubstitutionChunkReference.java | 43 ++++++++++--------- ...gDictLiteralArgumentWithNumericExprKeys.py | 1 + ...ictLiteralArgumentWithReferenceExprKeys.py | 3 ++ ...ictLiteralArgumentWithReferenceExprKeys.py | 3 ++ .../PyUnresolvedReferencesInspectionTest.java | 16 +++++++ 5 files changed, 45 insertions(+), 21 deletions(-) create mode 100644 python/testData/inspections/PyUnresolvedReferencesInspection/formatStringDictLiteralArgumentWithNumericExprKeys.py create mode 100644 python/testData/inspections/PyUnresolvedReferencesInspection/formatStringDictLiteralArgumentWithReferenceExprKeys.py create mode 100644 python/testData/inspections/PyUnresolvedReferencesInspection/percentStringDictLiteralArgumentWithReferenceExprKeys.py diff --git a/python/src/com/jetbrains/python/codeInsight/PySubstitutionChunkReference.java b/python/src/com/jetbrains/python/codeInsight/PySubstitutionChunkReference.java index 0dbd8765dd47..ee5b19984343 100644 --- a/python/src/com/jetbrains/python/codeInsight/PySubstitutionChunkReference.java +++ b/python/src/com/jetbrains/python/codeInsight/PySubstitutionChunkReference.java @@ -124,13 +124,7 @@ public class PySubstitutionChunkReference extends PsiReferenceBasefst)s" % {1: "3"}) \ No newline at end of file diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/formatStringDictLiteralArgumentWithReferenceExprKeys.py b/python/testData/inspections/PyUnresolvedReferencesInspection/formatStringDictLiteralArgumentWithReferenceExprKeys.py new file mode 100644 index 000000000000..82c1ed167b4b --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection/formatStringDictLiteralArgumentWithReferenceExprKeys.py @@ -0,0 +1,3 @@ +f = "fst" +s = "snd" +v = "first {fst}, second {snd}".format(**{s: 221, f: 10}) \ No newline at end of file diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/percentStringDictLiteralArgumentWithReferenceExprKeys.py b/python/testData/inspections/PyUnresolvedReferencesInspection/percentStringDictLiteralArgumentWithReferenceExprKeys.py new file mode 100644 index 000000000000..b1c1a1b61319 --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection/percentStringDictLiteralArgumentWithReferenceExprKeys.py @@ -0,0 +1,3 @@ +f = "fst" +s = "snd" +print ("first is %(fst)s, second is %(snd)s" % {s: "3", f: "1"}) \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java index 5eb1fe6ca0be..9c28683a89c0 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java @@ -587,6 +587,22 @@ public class PyUnresolvedReferencesInspectionTest extends PyInspectionTestCase { doTest(); } + // PY-2748 + public void testFormatStringDictLiteralArgumentWithReferenceExprKeys() { + doTest(); + } + + // PY-2748 + public void testPercentStringDictLiteralArgumentWithReferenceExprKeys() { + doTest(); + } + + // PY-2748 + public void testFormatStringDictLiteralArgumentWithNumericExprKeys() { + doTest(); + } + + // PY-18254 public void testVarargsAnnotatedWithFunctionComment() { doTest(); From e7c6dfa4c0e46ea2e48e62cf6b3d7c81dee30d4c Mon Sep 17 00:00:00 2001 From: Dmitry Batrak Date: Wed, 9 Mar 2016 13:42:38 +0300 Subject: [PATCH 07/83] IDEA-152551 Keep caret position when reaching document top/bottom --- .../intellij/openapi/editor/impl/CaretImpl.java | 4 ---- .../openapi/editor/impl/EditorImplTest.java | 16 ++++++++++++++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretImpl.java index 491086630c5f..6ff0acc26ab3 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretImpl.java @@ -302,8 +302,6 @@ public class CaretImpl extends UserDataHolderBase implements Caret, Dumpable { // We want to move caret to the first column if it's already located at the first line and 'Up' is pressed. newColumnNumber = 0; - desiredX = -1; - lastColumnNumber = -1; } VisualPosition pos = new VisualPosition(newLineNumber, newColumnNumber); @@ -316,8 +314,6 @@ public class CaretImpl extends UserDataHolderBase implements Caret, Dumpable { if (lastOffsetColumn > newColumnNumber) { newColumnNumber = lastOffsetColumn; newLeansRight = true; - desiredX = -1; - lastColumnNumber = -1; } } if (!editorSettings.isCaretInsideTabs()) { diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/editor/impl/EditorImplTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/editor/impl/EditorImplTest.java index 8acfa6cccc5d..441bc2de6908 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/editor/impl/EditorImplTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/editor/impl/EditorImplTest.java @@ -303,4 +303,20 @@ public class EditorImplTest extends AbstractEditorTest { verifySoftWrapPositions(); } + + public void testUpDownNearDocumentTopAndBottom() throws Exception { + initText("abc\ndef\nghi"); + up(); + checkResultByText("abc\ndef\nghi"); + up(); + checkResultByText("abc\ndef\nghi"); + down(); + checkResultByText("abc\ndef\nghi"); + down(); + checkResultByText("abc\ndef\nghi"); + down(); + checkResultByText("abc\ndef\nghi"); + up(); + checkResultByText("abc\ndef\nghi"); + } } From 31f9b8b8c623ee0f32d043eab0c404719a2828d6 Mon Sep 17 00:00:00 2001 From: Semyon Proshev Date: Mon, 15 Feb 2016 18:30:21 +0300 Subject: [PATCH 08/83] PY-18422 Fixed: False positives with __slots__ and class attributes Honour class attributes in the case when there is declared __slots__ --- .../PyUnresolvedReferencesInspection.java | 8 +++++++- .../slotsAndClassAttr.py | 9 +++++++++ .../PyUnresolvedReferencesInspectionTest.java | 5 +++++ 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 python/testData/inspections/PyUnresolvedReferencesInspection/slotsAndClassAttr.py diff --git a/python/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java b/python/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java index 06d058b7e297..326a27f643c3 100644 --- a/python/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java +++ b/python/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java @@ -197,12 +197,18 @@ public class PyUnresolvedReferencesInspection extends PyInspection { } } - private static boolean canHaveAttribute(@NotNull PyClass cls, @Nullable String attrName) { + private boolean canHaveAttribute(@NotNull PyClass cls, @Nullable String attrName) { final List slots = cls.getOwnSlots(); + // Class instance can contain attributes with arbitrary names if (slots == null || slots.contains(PyNames.DICT)) { return true; } + + if (attrName != null && cls.findClassAttribute(attrName, true, myTypeEvalContext) != null) { + return true; + } + return slots.contains(attrName) || cls.getProperties().containsKey(attrName); } diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/slotsAndClassAttr.py b/python/testData/inspections/PyUnresolvedReferencesInspection/slotsAndClassAttr.py new file mode 100644 index 000000000000..24fec7f11835 --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection/slotsAndClassAttr.py @@ -0,0 +1,9 @@ +class Singleton(object): + __slots__ = () + data = {} + + def foo(self): + self.data = {'a': 1} + +Singleton.data = {'a': 1} +Singleton().__class__.data = {'a': 1} \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java index 9c28683a89c0..9f979def0fd4 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java @@ -70,6 +70,11 @@ public class PyUnresolvedReferencesInspectionTest extends PyInspectionTestCase { doTest(); } + // PY-18422 + public void testSlotsAndClassAttr() { + doTest(); + } + public void testSlotsSubclass() { // PY-5939 doTest(); } From 80e2ca8854dc7b7132ddb9988277fa6f10ec5e33 Mon Sep 17 00:00:00 2001 From: Rustam Vishnyakov Date: Wed, 9 Mar 2016 13:46:27 +0300 Subject: [PATCH 09/83] Fixes for Jade/Spy.js --- .../src/colorSchemes/all_hallows_eve.xml | 47 ++++++++++++++++ colorSchemes/src/colorSchemes/blackboard.xml | 55 +++++++++++++++++++ colorSchemes/src/colorSchemes/cobalt.xml | 55 +++++++++++++++++++ colorSchemes/src/colorSchemes/github.xml | 49 +++++++++++++++++ colorSchemes/src/colorSchemes/monokai.xml | 47 ++++++++++++++++ colorSchemes/src/colorSchemes/rails_casts.xml | 47 ++++++++++++++++ colorSchemes/src/colorSchemes/twilight.xml | 55 +++++++++++++++++++ 7 files changed, 355 insertions(+) diff --git a/colorSchemes/src/colorSchemes/all_hallows_eve.xml b/colorSchemes/src/colorSchemes/all_hallows_eve.xml index 4b79491f240d..b78821565e19 100644 --- a/colorSchemes/src/colorSchemes/all_hallows_eve.xml +++ b/colorSchemes/src/colorSchemes/all_hallows_eve.xml @@ -818,6 +818,18 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + , B extends Comparable> { + + class Pair implements Comparable { + A a; + B b; + + + public A getA() { + return a; + } + + + public B getB() { + return b; + } + + + @Override + public int compareTo(Pair other) { + Comparator comparator = Comparator.comparing(Pair::getA).thenComparing(Pair::getB); + + return comparator.compare(this, other); + } + } + +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewMethodRefHighlightingTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewMethodRefHighlightingTest.java index 91d9cf2104ea..2451d8280718 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewMethodRefHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewMethodRefHighlightingTest.java @@ -498,6 +498,10 @@ public class NewMethodRefHighlightingTest extends LightDaemonAnalyzerTestCase { doTest(); } + public void testIDEA152659() throws Exception { + doTest(); + } + private void doTest() { doTest(false); } From cee658173431fdc8d8fa4f0330213f0f0ecc828c Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Wed, 9 Mar 2016 12:39:23 +0100 Subject: [PATCH 46/83] generify: collapse to diamond when applicable --- .../com/intellij/refactoring/typeCook/Util.java | 5 +++++ .../typeCook/convertToDiamond/after/Test.1.items | 1 + .../typeCook/convertToDiamond/after/Test.items | 1 + .../typeCook/convertToDiamond/after/test.java | 6 ++++++ .../typeCook/convertToDiamond/before/test.java | 6 ++++++ .../com/intellij/refactoring/TypeCookTest.java | 14 ++++++++++++++ 6 files changed, 33 insertions(+) create mode 100644 java/java-tests/testData/refactoring/typeCook/convertToDiamond/after/Test.1.items create mode 100644 java/java-tests/testData/refactoring/typeCook/convertToDiamond/after/Test.items create mode 100644 java/java-tests/testData/refactoring/typeCook/convertToDiamond/after/test.java create mode 100644 java/java-tests/testData/refactoring/typeCook/convertToDiamond/before/test.java diff --git a/java/java-impl/src/com/intellij/refactoring/typeCook/Util.java b/java/java-impl/src/com/intellij/refactoring/typeCook/Util.java index 54149613704b..09370d3afe5a 100644 --- a/java/java-impl/src/com/intellij/refactoring/typeCook/Util.java +++ b/java/java-impl/src/com/intellij/refactoring/typeCook/Util.java @@ -17,6 +17,7 @@ package com.intellij.refactoring.typeCook; import com.intellij.openapi.diagnostic.Logger; import com.intellij.psi.*; +import com.intellij.psi.impl.PsiDiamondTypeUtil; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.PsiUtil; import com.intellij.refactoring.typeCook.deductive.PsiTypeVariableFactory; @@ -405,6 +406,10 @@ public class Util { list .add(factory.createTypeElement(aType == null ? PsiType.getJavaLangObject(list.getManager(), list.getResolveScope()) : aType)); } + + if (PsiDiamondTypeUtil.canCollapseToDiamond(newx, newx, newx.getType())) { + PsiDiamondTypeUtil.replaceExplicitWithDiamond(list); + } } } else { diff --git a/java/java-tests/testData/refactoring/typeCook/convertToDiamond/after/Test.1.items b/java/java-tests/testData/refactoring/typeCook/convertToDiamond/after/Test.1.items new file mode 100644 index 000000000000..3b9e27f05d2c --- /dev/null +++ b/java/java-tests/testData/refactoring/typeCook/convertToDiamond/after/Test.1.items @@ -0,0 +1 @@ +java.util.ArrayList\nnew diff --git a/java/java-tests/testData/refactoring/typeCook/convertToDiamond/after/Test.items b/java/java-tests/testData/refactoring/typeCook/convertToDiamond/after/Test.items new file mode 100644 index 000000000000..f994691fb0c5 --- /dev/null +++ b/java/java-tests/testData/refactoring/typeCook/convertToDiamond/after/Test.items @@ -0,0 +1 @@ +java.util.ArrayList\nnew diff --git a/java/java-tests/testData/refactoring/typeCook/convertToDiamond/after/test.java b/java/java-tests/testData/refactoring/typeCook/convertToDiamond/after/test.java new file mode 100644 index 000000000000..89c685c83acb --- /dev/null +++ b/java/java-tests/testData/refactoring/typeCook/convertToDiamond/after/test.java @@ -0,0 +1,6 @@ +import java.util.ArrayList; + +class Test +{ + ArrayList l = new ArrayList<>(); +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/typeCook/convertToDiamond/before/test.java b/java/java-tests/testData/refactoring/typeCook/convertToDiamond/before/test.java new file mode 100644 index 000000000000..e61c977ebe95 --- /dev/null +++ b/java/java-tests/testData/refactoring/typeCook/convertToDiamond/before/test.java @@ -0,0 +1,6 @@ +import java.util.ArrayList; + +class Test +{ + ArrayList l = new ArrayList(); +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/refactoring/TypeCookTest.java b/java/java-tests/testSrc/com/intellij/refactoring/TypeCookTest.java index 861c5ef62b1d..78ac7052b2a4 100644 --- a/java/java-tests/testSrc/com/intellij/refactoring/TypeCookTest.java +++ b/java/java-tests/testSrc/com/intellij/refactoring/TypeCookTest.java @@ -17,9 +17,11 @@ package com.intellij.refactoring; import com.intellij.JavaTestUtil; import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.roots.LanguageLevelProjectExtension; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.PsiClass; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.refactoring.typeCook.Settings; @@ -667,6 +669,18 @@ public class TypeCookTest extends MultiFileTestCase { start(); } + public void testConvertToDiamond() throws Exception { + final LanguageLevelProjectExtension levelProjectExtension = LanguageLevelProjectExtension.getInstance(getProject()); + final LanguageLevel oldLevel = levelProjectExtension.getLanguageLevel(); + try { + levelProjectExtension.setLanguageLevel(LanguageLevel.JDK_1_8); + start(); + } + finally { + levelProjectExtension.setLanguageLevel(oldLevel); + } + } + public void start() throws Exception { start(false); } From 355ee67f277cfb17926f0669802f85ce4ec68843 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Wed, 9 Mar 2016 12:50:55 +0100 Subject: [PATCH 47/83] generify fix for unchecked assignments (IDEA-152658) --- .../UncheckedWarningLocalInspectionBase.java | 16 +++++++++++----- .../quickFix/generifyFile/before2.java | 7 +++++++ 2 files changed, 18 insertions(+), 5 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/generifyFile/before2.java diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/uncheckedWarnings/UncheckedWarningLocalInspectionBase.java b/java/java-analysis-impl/src/com/intellij/codeInspection/uncheckedWarnings/UncheckedWarningLocalInspectionBase.java index 27be172ae58a..accdc0b2b17b 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/uncheckedWarnings/UncheckedWarningLocalInspectionBase.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/uncheckedWarnings/UncheckedWarningLocalInspectionBase.java @@ -42,6 +42,7 @@ import javax.swing.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; +import java.util.Collections; import java.util.List; public class UncheckedWarningLocalInspectionBase extends BaseJavaBatchLocalInspectionTool { @@ -68,8 +69,8 @@ public class UncheckedWarningLocalInspectionBase extends BaseJavaBatchLocalInspe return uncheckedCb; } - public static LocalQuickFix[] getChangeVariableTypeFixes(@NotNull PsiVariable parameter, PsiType itemType) { - if (itemType instanceof PsiMethodReferenceType) return LocalQuickFix.EMPTY_ARRAY; + public static LocalQuickFix[] getChangeVariableTypeFixes(@NotNull PsiVariable parameter, PsiType itemType, LocalQuickFix[] generifyFixes) { + if (itemType instanceof PsiMethodReferenceType) return generifyFixes; final List result = new ArrayList(); LOG.assertTrue(parameter.isValid()); if (itemType != null) { @@ -81,6 +82,10 @@ public class UncheckedWarningLocalInspectionBase extends BaseJavaBatchLocalInspe } } } + + if (generifyFixes.length > 0) { + Collections.addAll(result, generifyFixes); + } return result.toArray(new LocalQuickFix[result.size()]); } @@ -283,7 +288,7 @@ public class UncheckedWarningLocalInspectionBase extends BaseJavaBatchLocalInspe if (initializer == null || initializer instanceof PsiArrayInitializerExpression) return; final PsiType initializerType = initializer.getType(); checkRawToGenericsAssignment(initializer, initializer, variable.getType(), initializerType, true, - myOnTheFly ? getChangeVariableTypeFixes(variable, initializerType) : LocalQuickFix.EMPTY_ARRAY); + myOnTheFly ? getChangeVariableTypeFixes(variable, initializerType, myGenerifyFixes) : LocalQuickFix.EMPTY_ARRAY); } @Override @@ -295,7 +300,8 @@ public class UncheckedWarningLocalInspectionBase extends BaseJavaBatchLocalInspe final PsiExpression iteratedValue = statement.getIteratedValue(); if (iteratedValue == null) return; final PsiType itemType = JavaGenericsUtil.getCollectionItemType(iteratedValue); - checkRawToGenericsAssignment(parameter, iteratedValue, parameterType, itemType, true, myOnTheFly ? getChangeVariableTypeFixes(parameter, itemType) : LocalQuickFix.EMPTY_ARRAY); + checkRawToGenericsAssignment(parameter, iteratedValue, parameterType, itemType, true, myOnTheFly ? getChangeVariableTypeFixes(parameter, itemType, + myGenerifyFixes) : LocalQuickFix.EMPTY_ARRAY); } @Override @@ -316,7 +322,7 @@ public class UncheckedWarningLocalInspectionBase extends BaseJavaBatchLocalInspe leftVar = (PsiVariable)element; } } - checkRawToGenericsAssignment(rExpr, rExpr, lType, rType, true, myOnTheFly && leftVar != null ? getChangeVariableTypeFixes(leftVar, rType) : LocalQuickFix.EMPTY_ARRAY); + checkRawToGenericsAssignment(rExpr, rExpr, lType, rType, true, myOnTheFly && leftVar != null ? getChangeVariableTypeFixes(leftVar, rType, myGenerifyFixes) : LocalQuickFix.EMPTY_ARRAY); } @Override diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/generifyFile/before2.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/generifyFile/before2.java new file mode 100644 index 000000000000..e821781e448e --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/generifyFile/before2.java @@ -0,0 +1,7 @@ +// "Try to generify 'before2.java'" "true" +import java.util.ArrayList; +class Use { + void f() { + ArrayList s = new ArrayList(); + } +} \ No newline at end of file From 0a2ab72163916c9a6589dd8af8be7b0ba52b6af3 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Wed, 9 Mar 2016 14:10:57 +0100 Subject: [PATCH 48/83] use statistics from introduce variable for create local from usage (IDEA-152410) --- .../quickfix/CreateLocalFromUsageFix.java | 13 ++++++++++-- .../AbstractJavaInplaceIntroducer.java | 19 +++++++++++++++++- .../ui/TypeSelectorManagerImpl.java | 20 ++++++++++--------- 3 files changed, 40 insertions(+), 12 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateLocalFromUsageFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateLocalFromUsageFix.java index 919c6b447259..aca399d6b20e 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateLocalFromUsageFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateLocalFromUsageFix.java @@ -31,6 +31,8 @@ import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.codeStyle.CodeStyleSettingsManager; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; +import com.intellij.refactoring.introduceParameter.AbstractJavaInplaceIntroducer; +import com.intellij.refactoring.ui.TypeSelectorManagerImpl; import org.jetbrains.annotations.NotNull; /** @@ -73,7 +75,9 @@ public class CreateLocalFromUsageFix extends CreateVarFromUsageFix { final PsiFile targetFile = targetClass.getContainingFile(); PsiType[] expectedTypes = CreateFromUsageUtils.guessType(myReferenceExpression, false); - PsiType type = expectedTypes[0]; + final SmartTypePointer defaultType = SmartTypePointerManager.getInstance(project).createSmartTypePointer(expectedTypes[0]); + final PsiType preferredType = TypeSelectorManagerImpl.getPreferredType(expectedTypes, expectedTypes[0]); + PsiType type = preferredType != null ? preferredType : expectedTypes[0]; String varName = myReferenceExpression.getReferenceName(); PsiExpression initializer = null; @@ -116,7 +120,10 @@ public class CreateLocalFromUsageFix extends CreateVarFromUsageFix { var = CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(var); if (var == null) return; TemplateBuilderImpl builder = new TemplateBuilderImpl(var); - builder.replaceElement(var.getTypeElement(), expression); + final PsiTypeElement typeElement = var.getTypeElement(); + LOG.assertTrue(typeElement != null); + builder.replaceElement(typeElement, + AbstractJavaInplaceIntroducer.createExpression(expression, typeElement.getText())); builder.setEndVariableAfter(var.getNameIdentifier()); Template template = builder.buildTemplate(); @@ -132,6 +139,8 @@ public class CreateLocalFromUsageFix extends CreateVarFromUsageFix { final int offset = newEditor.getCaretModel().getOffset(); final PsiLocalVariable localVariable = PsiTreeUtil.findElementOfClassAtOffset(targetFile, offset, PsiLocalVariable.class, false); if (localVariable != null) { + TypeSelectorManagerImpl.typeSelected(localVariable.getType(), defaultType.getType()); + ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override public void run() { diff --git a/java/java-impl/src/com/intellij/refactoring/introduceParameter/AbstractJavaInplaceIntroducer.java b/java/java-impl/src/com/intellij/refactoring/introduceParameter/AbstractJavaInplaceIntroducer.java index c1a707718308..6e7cdac9a820 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceParameter/AbstractJavaInplaceIntroducer.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceParameter/AbstractJavaInplaceIntroducer.java @@ -2,6 +2,7 @@ package com.intellij.refactoring.introduceParameter; import com.intellij.codeInsight.intention.impl.TypeExpression; import com.intellij.codeInsight.lookup.LookupElement; +import com.intellij.codeInsight.lookup.PsiTypeLookupItem; import com.intellij.codeInsight.template.Expression; import com.intellij.codeInsight.template.ExpressionContext; import com.intellij.codeInsight.template.Result; @@ -200,7 +201,23 @@ public abstract class AbstractJavaInplaceIntroducer extends AbstractInplaceIntro @Override public LookupElement[] calculateLookupItems(ExpressionContext context) { - return expression.calculateLookupItems(context); + final LookupElement[] elements = expression.calculateLookupItems(context); + if (elements != null) { + LookupElement toBeSelected = null; + for (LookupElement element : elements) { + if (element instanceof PsiTypeLookupItem && ((PsiTypeLookupItem)element).getType().getPresentableText().equals(defaultType)) { + toBeSelected = element; + break; + } + } + if (toBeSelected != null) { + final int idx = ArrayUtil.find(elements, toBeSelected); + if (idx > 0) { + return ArrayUtil.prepend(toBeSelected, ArrayUtil.remove(elements, idx)); + } + } + } + return elements; } @Override diff --git a/java/java-impl/src/com/intellij/refactoring/ui/TypeSelectorManagerImpl.java b/java/java-impl/src/com/intellij/refactoring/ui/TypeSelectorManagerImpl.java index b308a4cdf929..308ee4b175fb 100644 --- a/java/java-impl/src/com/intellij/refactoring/ui/TypeSelectorManagerImpl.java +++ b/java/java-impl/src/com/intellij/refactoring/ui/TypeSelectorManagerImpl.java @@ -312,18 +312,25 @@ public class TypeSelectorManagerImpl implements TypeSelectorManager { private void setTypesAndPreselect(PsiType[] types) { myTypeSelector.setTypes(types); + final PsiType preferredType = getPreferredType(types, getDefaultType()); + if (preferredType != null) { + myTypeSelector.selectType(preferredType); + } + } + + public static PsiType getPreferredType(PsiType[] types, PsiType defaultType) { Map map = new THashMap(); for (final PsiType type : types) { map.put(serialize(type), type); } - for (StatisticsInfo info : StatisticsManager.getInstance().getAllValues(getStatsKey())) { + for (StatisticsInfo info : StatisticsManager.getInstance().getAllValues(getStatsKey(defaultType))) { final PsiType candidate = map.get(info.getValue()); if (candidate != null && StatisticsManager.getInstance().getUseCount(info) > 0) { - myTypeSelector.selectType(candidate); - return; + return candidate; } } + return null; } @Override @@ -353,15 +360,10 @@ public class TypeSelectorManagerImpl implements TypeSelectorManager { StatisticsManager.getInstance().incUseCount(new StatisticsInfo(getStatsKey(defaultType), serialize(type))); } - private String getStatsKey() { - final PsiType defaultType = getDefaultType(); + private static String getStatsKey(final PsiType defaultType) { if (defaultType == null) { return "IntroduceVariable##"; } - return getStatsKey(defaultType); - } - - private static String getStatsKey(final PsiType defaultType) { return "IntroduceVariable##" + serialize(defaultType); } From 76ca6c2f2c1bbe4241c553fbad24e9bad287e86a Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Thu, 3 Mar 2016 17:35:43 +0300 Subject: [PATCH 49/83] got rid of reflection --- .../openapi/application/impl/ApplicationImpl.java | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java b/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java index 2030906c2e9a..448aab7cd286 100644 --- a/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java @@ -17,7 +17,6 @@ package com.intellij.openapi.application.impl; import com.intellij.BundleBase; import com.intellij.CommonBundle; -import com.intellij.Patches; import com.intellij.diagnostic.LogEventException; import com.intellij.diagnostic.PerformanceWatcher; import com.intellij.diagnostic.ThreadDumper; @@ -77,12 +76,12 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; import org.jetbrains.ide.PooledThreadExecutor; import org.picocontainer.MutablePicoContainer; +import sun.awt.AWTAccessor; import javax.swing.*; import java.awt.*; import java.io.File; import java.io.IOException; -import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; @@ -1077,15 +1076,7 @@ public class ApplicationImpl extends PlatformComponentManagerImpl implements App private static Thread getEventQueueThread() { EventQueue eventQueue = Toolkit.getDefaultToolkit().getSystemEventQueue(); - try { - // use sun.awt.AWTAccessor.EventQueueAccessor? - assert Patches.USE_REFLECTION_TO_ACCESS_JDK8; - Method method = ReflectionUtil.getDeclaredMethod(EventQueue.class, "getDispatchThread"); - return (Thread)method.invoke(eventQueue); - } - catch (Exception e) { - throw new RuntimeException(e); - } + return AWTAccessor.getEventQueueAccessor().getDispatchThread(eventQueue); } @Override From 654211ec7324cff601b1cb6214ee50e28820083c Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Thu, 3 Mar 2016 17:51:12 +0300 Subject: [PATCH 50/83] moved to core-impl since it is private API anyway --- .../src/com/intellij/concurrency/Job.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename platform/{core-api => core-impl}/src/com/intellij/concurrency/Job.java (98%) diff --git a/platform/core-api/src/com/intellij/concurrency/Job.java b/platform/core-impl/src/com/intellij/concurrency/Job.java similarity index 98% rename from platform/core-api/src/com/intellij/concurrency/Job.java rename to platform/core-impl/src/com/intellij/concurrency/Job.java index 415dddd1b86c..b99e07aace46 100644 --- a/platform/core-api/src/com/intellij/concurrency/Job.java +++ b/platform/core-impl/src/com/intellij/concurrency/Job.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. From 51f6e09f598cb003a855b16d5382b009a95ddc59 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Fri, 4 Mar 2016 15:04:58 +0300 Subject: [PATCH 51/83] cleanup --- .../ide/projectView/impl/AbstractProjectViewPane.java | 2 +- .../intellij/openapi/components/impl/ServiceManagerImpl.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/ide/projectView/impl/AbstractProjectViewPane.java b/platform/lang-impl/src/com/intellij/ide/projectView/impl/AbstractProjectViewPane.java index a06a2efcee42..64d245d4512e 100644 --- a/platform/lang-impl/src/com/intellij/ide/projectView/impl/AbstractProjectViewPane.java +++ b/platform/lang-impl/src/com/intellij/ide/projectView/impl/AbstractProjectViewPane.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. diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/ServiceManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/ServiceManagerImpl.java index ce4f2dbf54cf..7f4a76a8558a 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/ServiceManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/ServiceManagerImpl.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. @@ -182,7 +182,7 @@ public class ServiceManagerImpl implements BaseComponent { private final ServiceDescriptor myDescriptor; private final PluginDescriptor myPluginDescriptor; private final ComponentManagerEx myComponentManager; - private volatile Object myInitializedComponentInstance = null; + private volatile Object myInitializedComponentInstance; public MyComponentAdapter(final ServiceDescriptor descriptor, final PluginDescriptor pluginDescriptor, ComponentManagerEx componentManager) { myDescriptor = descriptor; From 2c52eb3033fcd57de5086dfc2a5bd25d6abde93e Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Fri, 4 Mar 2016 17:47:40 +0300 Subject: [PATCH 52/83] lambdify --- .../search/JavaDirectInheritorsSearcher.java | 127 ++++-------------- 1 file changed, 27 insertions(+), 100 deletions(-) diff --git a/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaDirectInheritorsSearcher.java b/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaDirectInheritorsSearcher.java index a0ea9b3fd68f..077985db8c34 100644 --- a/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaDirectInheritorsSearcher.java +++ b/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaDirectInheritorsSearcher.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. @@ -51,87 +51,46 @@ public class JavaDirectInheritorsSearcher implements QueryExecutor consumer) { final PsiClass aClass = p.getClassToProcess(); - final SearchScope useScope = ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - public SearchScope compute() { - return aClass.getUseScope(); - } - }); + final SearchScope useScope = ApplicationManager.getApplication().runReadAction((Computable)aClass::getUseScope); - final String qualifiedName = ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - public String compute() { - return aClass.getQualifiedName(); - } - }); + final String qualifiedName = ApplicationManager.getApplication().runReadAction((Computable)aClass::getQualifiedName); final Project project = PsiUtilCore.getProjectInReadAction(aClass); if (CommonClassNames.JAVA_LANG_OBJECT.equals(qualifiedName)) { - //[pasynkov]: WTF? - //final SearchScope scope = useScope.intersectWith(GlobalSearchScope.notScope(GlobalSearchScope.getScopeRestrictedByFileTypes( - // GlobalSearchScope.allScope(psiManager.getProject()), StdFileTypes.JSP, StdFileTypes.JSPX))); - - return AllClassesSearch.search(useScope, project).forEach(new Processor() { - @Override - public boolean process(final PsiClass psiClass) { - ProgressManager.checkCanceled(); - if (psiClass.isInterface()) { - return consumer.process(psiClass); - } - final PsiClass superClass = psiClass.getSuperClass(); - if (superClass != null && CommonClassNames.JAVA_LANG_OBJECT.equals(ApplicationManager.getApplication().runReadAction(new Computable() { - public String compute() { - return superClass.getQualifiedName(); - } - }))) { - return consumer.process(psiClass); - } - return true; + return AllClassesSearch.search(useScope, project).forEach(psiClass -> { + ProgressManager.checkCanceled(); + if (psiClass.isInterface()) { + return consumer.process(psiClass); } + final PsiClass superClass = psiClass.getSuperClass(); + if (superClass != null && + CommonClassNames.JAVA_LANG_OBJECT.equals(ApplicationManager.getApplication().runReadAction((Computable)superClass::getQualifiedName))) { + return consumer.process(psiClass); + } + return true; }); } final GlobalSearchScope scope = useScope instanceof GlobalSearchScope ? (GlobalSearchScope)useScope : new EverythingGlobalScope(project); - final String searchKey = ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - public String compute() { - return aClass.getName(); - } - }); + final String searchKey = ApplicationManager.getApplication().runReadAction((Computable)aClass::getName); if (StringUtil.isEmpty(searchKey)) { return true; } - Collection candidates = MethodUsagesSearcher.resolveInReadAction(project, - new Computable>() { - @Override - public Collection compute() { - return JavaSuperClassNameOccurenceIndex - .getInstance().get(searchKey, project, scope); - } - }); + Collection candidates = + MethodUsagesSearcher.resolveInReadAction(project, () -> JavaSuperClassNameOccurenceIndex.getInstance().get(searchKey, project, scope)); - Map> classes = new HashMap>(); + Map> classes = new HashMap<>(); for (final PsiReferenceList referenceList : candidates) { ProgressManager.checkCanceled(); - final PsiClass candidate = (PsiClass)ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - public PsiElement compute() { - return referenceList.getParent(); - } - }); + final PsiClass candidate = (PsiClass)ApplicationManager.getApplication().runReadAction((Computable)referenceList::getParent); if (!checkInheritance(p, aClass, candidate, project)) continue; - String fqn = ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - public String compute() { - return candidate.getQualifiedName(); - } - }); + String fqn = ApplicationManager.getApplication().runReadAction((Computable)candidate::getQualifiedName); List list = classes.get(fqn); if (list == null) { - list = new ArrayList(); + list = new ArrayList<>(); classes.put(fqn, list); } list.add(candidate); @@ -146,15 +105,8 @@ public class JavaDirectInheritorsSearcher implements QueryExecutor anonymousCandidates = MethodUsagesSearcher.resolveInReadAction(project, - new Computable>() { - @Override - public Collection compute() { - return JavaAnonymousClassBaseRefOccurenceIndex - .getInstance() - .get(searchKey, project, scope); - } - }); + Collection anonymousCandidates = + MethodUsagesSearcher.resolveInReadAction(project, () -> JavaAnonymousClassBaseRefOccurenceIndex.getInstance().get(searchKey, project, scope)); for (PsiAnonymousClass candidate : anonymousCandidates) { ProgressManager.checkCanceled(); @@ -163,30 +115,15 @@ public class JavaDirectInheritorsSearcher implements QueryExecutor() { - @Override - public Boolean compute() { - return aClass.isEnum(); - } - }); + boolean isEnum = ApplicationManager.getApplication().runReadAction((Computable)aClass::isEnum); if (isEnum) { // abstract enum can be subclassed in the body - PsiField[] fields = ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - public PsiField[] compute() { - return aClass.getFields(); - } - }); + PsiField[] fields = ApplicationManager.getApplication().runReadAction((Computable)aClass::getFields); for (final PsiField field : fields) { ProgressManager.checkCanceled(); if (field instanceof PsiEnumConstant) { PsiEnumConstantInitializer initializingClass = - ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - public PsiEnumConstantInitializer compute() { - return ((PsiEnumConstant)field).getInitializingClass(); - } - }); + ApplicationManager.getApplication().runReadAction((Computable)((PsiEnumConstant)field)::getInitializingClass); if (initializingClass != null) { if (!consumer.process(initializingClass)) return false; } @@ -199,12 +136,7 @@ public class JavaDirectInheritorsSearcher implements QueryExecutor() { - @Override - public Boolean compute() { - return !p.isCheckInheritance() || candidate.isInheritor(aClass, false); - } - }); + return MethodUsagesSearcher.resolveInReadAction(project, () -> !p.isCheckInheritance() || candidate.isInheritor(aClass, false)); } private static boolean processSameNamedClasses(Processor consumer, List sameNamedClasses, final VirtualFile jarFile) { @@ -226,11 +158,6 @@ public class JavaDirectInheritorsSearcher implements QueryExecutor() { - @Override - public VirtualFile compute() { - return PsiUtil.getJarFile(aClass); - } - }); + return ApplicationManager.getApplication().runReadAction((Computable)() -> PsiUtil.getJarFile(aClass)); } } From 35dfe0a4d82a6eacb2113d096e53f5f359352109 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Fri, 4 Mar 2016 18:24:34 +0300 Subject: [PATCH 53/83] notnull --- .../impl/search/JavaDirectInheritorsSearcher.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaDirectInheritorsSearcher.java b/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaDirectInheritorsSearcher.java index 077985db8c34..2c3505df7246 100644 --- a/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaDirectInheritorsSearcher.java +++ b/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaDirectInheritorsSearcher.java @@ -37,6 +37,7 @@ import com.intellij.util.QueryExecutor; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.HashMap; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Collection; @@ -100,7 +101,7 @@ public class JavaDirectInheritorsSearcher implements QueryExecutor sameNamedClasses : classes.values()) { ProgressManager.checkCanceled(); - if (!processSameNamedClasses(consumer, sameNamedClasses, jarFile)) return false; + if (!processSameNamedClasses(sameNamedClasses, jarFile, consumer)) return false; } } @@ -135,11 +136,16 @@ public class JavaDirectInheritorsSearcher implements QueryExecutor !p.isCheckInheritance() || candidate.isInheritor(aClass, false)); } - private static boolean processSameNamedClasses(Processor consumer, List sameNamedClasses, final VirtualFile jarFile) { + private static boolean processSameNamedClasses(@NotNull List sameNamedClasses, + @Nullable VirtualFile jarFile, + @NotNull Processor consumer) { // if there is a class from the same jar, prefer it boolean sameJarClassFound = false; @@ -157,7 +163,7 @@ public class JavaDirectInheritorsSearcher implements QueryExecutor)() -> PsiUtil.getJarFile(aClass)); } } From 0714e7a97affa96e45e30e3373324e2bada4a09d Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Fri, 4 Mar 2016 19:45:32 +0300 Subject: [PATCH 54/83] notnull --- .../searches/DirectClassInheritorsSearch.java | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/java/java-indexing-api/src/com/intellij/psi/search/searches/DirectClassInheritorsSearch.java b/java/java-indexing-api/src/com/intellij/psi/search/searches/DirectClassInheritorsSearch.java index 8db0cf827e9b..6cbf5bbe4bed 100644 --- a/java/java-indexing-api/src/com/intellij/psi/search/searches/DirectClassInheritorsSearch.java +++ b/java/java-indexing-api/src/com/intellij/psi/search/searches/DirectClassInheritorsSearch.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. @@ -16,7 +16,6 @@ package com.intellij.psi.search.searches; import com.intellij.openapi.extensions.ExtensionPointName; -import com.intellij.openapi.util.Condition; import com.intellij.psi.PsiAnonymousClass; import com.intellij.psi.PsiClass; import com.intellij.psi.search.GlobalSearchScope; @@ -25,6 +24,7 @@ import com.intellij.psi.util.PsiUtilCore; import com.intellij.util.FilteredQuery; import com.intellij.util.Query; import com.intellij.util.QueryExecutor; +import org.jetbrains.annotations.NotNull; /** * @author max @@ -34,30 +34,32 @@ public class DirectClassInheritorsSearch extends ExtensibleQueryFactory raw = INSTANCE.createUniqueResultsQuery(new SearchParameters(aClass, scope, includeAnonymous, checkInheritance)); if (!includeAnonymous) { - return new FilteredQuery(raw, new Condition() { - @Override - public boolean value(final PsiClass psiClass) { - return !(psiClass instanceof PsiAnonymousClass); - } - }); + return new FilteredQuery<>(raw, psiClass -> !(psiClass instanceof PsiAnonymousClass)); } return raw; From f6820841801da7748b8de41acd55f50f486e2858 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Wed, 9 Mar 2016 16:24:24 +0300 Subject: [PATCH 55/83] ported JobScheduler to ForkJoinPool.commonPool() --- platform/boot/boot.iml | 4 +- .../IdeaForkJoinWorkerThreadFactory.java | 58 ++++++++++++++++++ .../concurrency/ApplierCompleter.java | 57 +++++++----------- .../intellij/concurrency/JobLauncherImpl.java | 60 +++---------------- .../application/impl/ApplicationImpl.java | 5 ++ 5 files changed, 93 insertions(+), 91 deletions(-) create mode 100644 platform/boot/src/com/intellij/concurrency/IdeaForkJoinWorkerThreadFactory.java diff --git a/platform/boot/boot.iml b/platform/boot/boot.iml index b96e3da1edc5..9a0e7ff45829 100644 --- a/platform/boot/boot.iml +++ b/platform/boot/boot.iml @@ -1,12 +1,12 @@ - + - + diff --git a/platform/boot/src/com/intellij/concurrency/IdeaForkJoinWorkerThreadFactory.java b/platform/boot/src/com/intellij/concurrency/IdeaForkJoinWorkerThreadFactory.java new file mode 100644 index 000000000000..83fc93abad5c --- /dev/null +++ b/platform/boot/src/com/intellij/concurrency/IdeaForkJoinWorkerThreadFactory.java @@ -0,0 +1,58 @@ +/* + * 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.concurrency; + +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.ForkJoinWorkerThread; +import java.util.concurrent.atomic.AtomicLong; + +// must be accessible via "ClassLoader.getSystemClassLoader().loadClass(fp).newInstance()" from java.util.concurrent.ForkJoinPool.makeCommonPool() +public class IdeaForkJoinWorkerThreadFactory implements ForkJoinPool.ForkJoinWorkerThreadFactory { + private static final int PARALLELISM = Runtime.getRuntime().availableProcessors(); + + // must be called in the earliest possible moment on startup + public static void setupForkJoinCommonPool() { + System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", String.valueOf(PARALLELISM)); + System.setProperty("java.util.concurrent.ForkJoinPool.common.threadFactory", IdeaForkJoinWorkerThreadFactory.class.getName()); + if (ForkJoinPool.commonPool().getFactory().getClass() != IdeaForkJoinWorkerThreadFactory.class) { + throw new IllegalStateException("Could not set ForkJoinPool thread factory: got "+ForkJoinPool.commonPool().getFactory()); + } + } + + private static final AtomicLong bits = new AtomicLong(); + @Override + public ForkJoinWorkerThread newThread(ForkJoinPool pool) { + final int n = setNextBit(); + ForkJoinWorkerThread thread = new ForkJoinWorkerThread(pool) { + @Override + protected void onTermination(Throwable exception) { + clearBit(n); + super.onTermination(exception); + } + }; + thread.setName("JobScheduler FJ pool " + n + "/" + PARALLELISM); + return thread; + } + + private static int setNextBit() { + long oldValue = bits.getAndUpdate(value -> value + 1 | value); + return Long.numberOfTrailingZeros(oldValue + 1); + } + + private static void clearBit(int n) { + bits.updateAndGet(value -> value & ~(1L << n)); + } +} diff --git a/platform/platform-impl/src/com/intellij/concurrency/ApplierCompleter.java b/platform/platform-impl/src/com/intellij/concurrency/ApplierCompleter.java index d77604988e49..fdb3bd48a5fa 100644 --- a/platform/platform-impl/src/com/intellij/concurrency/ApplierCompleter.java +++ b/platform/platform-impl/src/com/intellij/concurrency/ApplierCompleter.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. @@ -21,12 +21,12 @@ import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.util.Processor; import com.intellij.util.concurrency.AtomicFieldUpdater; -import jsr166e.CountedCompleter; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; +import java.util.Collection; import java.util.List; +import java.util.concurrent.CountedCompleter; /** * Executes processor on array elements in range from lo (inclusive) to hi (exclusive). @@ -38,7 +38,7 @@ import java.util.List; * After that, the task completes itself. * The process of completing traverses task parent hierarchy, decrementing each pending count until it either * decrements not-zero pending count and stops or - * reaches the top, in which case it invokes {@link jsr166e.ForkJoinTask#quietlyComplete()} which causes the top level task to wake up and join successfully. + * reaches the top, in which case it invokes {@link java.util.concurrent.ForkJoinTask#quietlyComplete()} which causes the top level task to wake up and join successfully. * The exceptions from the sub tasks bubble up to the top and saved in {@link #throwable}. */ class ApplierCompleter extends CountedCompleter { @@ -55,7 +55,7 @@ class ApplierCompleter extends CountedCompleter { private static final AtomicFieldUpdater throwableUpdater = AtomicFieldUpdater.forFieldOfType(ApplierCompleter.class, Throwable.class); // if not null, the read action has failed and this list contains unfinished subtasks - private List> failedSubTasks; + private final Collection> failedSubTasks; //private final List children = new ArrayList(); @@ -72,6 +72,7 @@ class ApplierCompleter extends CountedCompleter { @NotNull Processor processor, int lo, int hi, + @NotNull Collection> failedSubTasks, ApplierCompleter next) { super(parent); this.runInReadAction = runInReadAction; @@ -80,28 +81,20 @@ class ApplierCompleter extends CountedCompleter { this.processor = processor; this.lo = lo; this.hi = hi; + this.failedSubTasks = failedSubTasks; this.next = next; } @Override public void compute() { - wrapInReadActionAndIndicator(new Runnable() { - @Override - public void run() { - execAndForkSubTasks(); - } - }); + wrapInReadActionAndIndicator(this::execAndForkSubTasks); } private void wrapInReadActionAndIndicator(@NotNull final Runnable process) { - Runnable toRun = runInReadAction ? new Runnable() { - @Override - public void run() { - if (!ApplicationManagerEx.getApplicationEx().tryRunReadAction(process)) { - failedSubTasks = new ArrayList>(); - failedSubTasks.add(ApplierCompleter.this); - doComplete(throwable); - } + Runnable toRun = runInReadAction ? () -> { + if (!ApplicationManagerEx.getApplicationEx().tryRunReadAction(process)) { + failedSubTasks.add(this); + doComplete(throwable); } } : process; ProgressIndicator existing = ProgressManager.getInstance().getProgressIndicator(); @@ -122,6 +115,7 @@ class ApplierCompleter extends CountedCompleter { long start = System.currentTimeMillis(); ApplierCompleter right = null; Throwable throwable = null; + try { for (int i = lo; i < hi; ++i) { progressIndicator.checkCanceled(); @@ -132,7 +126,7 @@ class ApplierCompleter extends CountedCompleter { long elapsed = finish - start; if (elapsed > 5 && hi - i >= 2 && getSurplusQueuedTaskCount() <= JobSchedulerImpl.CORES_COUNT) { int mid = i + hi >>> 1; - right = new ApplierCompleter(this, runInReadAction, progressIndicator, array, processor, mid, hi, right); + right = new ApplierCompleter<>(this, runInReadAction, progressIndicator, array, processor, mid, hi, failedSubTasks, right); //children.add(right); addToPendingCount(1); right.fork(); @@ -223,34 +217,23 @@ class ApplierCompleter extends CountedCompleter { } boolean completeTaskWhichFailToAcquireReadAction() { - if (failedSubTasks == null) { - return true; - } final boolean[] result = {true}; // these tasks could not be executed in the other thread; do them here for (final ApplierCompleter task : failedSubTasks) { - task.failedSubTasks = null; - task.wrapInReadActionAndIndicator(new Runnable() { - @Override - public void run() { - for (int i = task.lo; i < task.hi; ++i) { - if (!task.processor.process(task.array.get(i))) { - result[0] = false; - break; - } + task.wrapInReadActionAndIndicator(() -> { + for (int i = task.lo; i < task.hi; ++i) { + if (!task.processor.process(task.array.get(i))) { + result[0] = false; + break; } } }); - if (task.failedSubTasks != null) { - result[0] = false; - break; - } } return result[0]; } @Override public String toString() { - return System.identityHashCode(this) + " ("+lo+"-"+hi+")"; + return "("+lo+"-"+hi+")"+(getCompleter() == null ? "" : " parent: "+getCompleter()); } } diff --git a/platform/platform-impl/src/com/intellij/concurrency/JobLauncherImpl.java b/platform/platform-impl/src/com/intellij/concurrency/JobLauncherImpl.java index fc705c315f06..9650b9deb1c1 100644 --- a/platform/platform-impl/src/com/intellij/concurrency/JobLauncherImpl.java +++ b/platform/platform-impl/src/com/intellij/concurrency/JobLauncherImpl.java @@ -15,7 +15,6 @@ */ package com.intellij.concurrency; -import com.intellij.Patches; import com.intellij.openapi.application.ex.ApplicationManagerEx; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicator; @@ -25,64 +24,20 @@ import com.intellij.util.Consumer; import com.intellij.util.IncorrectOperationException; import com.intellij.util.Processor; import com.intellij.util.io.storage.HeavyProcessLatch; -import jsr166e.ForkJoinPool; -import jsr166e.ForkJoinTask; -import jsr166e.ForkJoinWorkerThread; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Queue; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicLong; /** * @author cdr */ public class JobLauncherImpl extends JobLauncher { - private static final AtomicLong bits = new AtomicLong(); - static { - assert Patches.USE_REFLECTION_TO_ACCESS_JDK8 : "Please port to java.util.concurrent.ForkJoinPool"; - } - private static final ForkJoinPool.ForkJoinWorkerThreadFactory FACTORY = new ForkJoinPool.ForkJoinWorkerThreadFactory() { - @Override - public ForkJoinWorkerThread newThread(ForkJoinPool pool) { - final int n = addThread(); - ForkJoinWorkerThread thread = new ForkJoinWorkerThread(pool) { - @Override - protected void onTermination(Throwable exception) { - finishThread(n); - super.onTermination(exception); - } - }; - thread.setName("JobScheduler FJ pool "+ n +"/"+ JobSchedulerImpl.CORES_COUNT); - return thread; - } - - private int addThread() { - boolean set; - int n; - do { - long l = bits.longValue(); - long next = (l + 1) | l; - n = Long.numberOfTrailingZeros(l + 1); - set = bits.compareAndSet(l, next); - } while (!set); - return n; - } - private void finishThread(int n) { - boolean set; - do { - long l = bits.get(); - long next = l & ~(1L << n); - set = bits.compareAndSet(l, next); - } while (!set); - } - }; - - private static final ForkJoinPool pool = new ForkJoinPool(JobSchedulerImpl.CORES_COUNT, FACTORY, null, false); static final int CORES_FORK_THRESHOLD = 1; @Override @@ -99,9 +54,10 @@ public class JobLauncherImpl extends JobLauncher { HeavyProcessLatch.INSTANCE.stopThreadPrioritizing(); - ApplierCompleter applier = new ApplierCompleter<>(null, runInReadAction, wrapper, things, thingProcessor, 0, things.size(), null); + List> failedSubTasks = Collections.synchronizedList(new ArrayList<>()); + ApplierCompleter applier = new ApplierCompleter<>(null, runInReadAction, wrapper, things, thingProcessor, 0, things.size(), failedSubTasks, null); try { - pool.invoke(applier); + ForkJoinPool.commonPool().invoke(applier); if (applier.throwable != null) throw applier.throwable; } catch (ApplierCompleter.ComputationAbortedException e) { @@ -217,7 +173,7 @@ public class JobLauncherImpl extends JobLauncher { } private void submit() { - pool.submit(myForkJoinTask); + ForkJoinPool.commonPool().submit(myForkJoinTask); } //////////////// Job @@ -282,8 +238,8 @@ public class JobLauncherImpl extends JobLauncher { catch (CancellationException e) { // was canceled in the middle of execution // can't do anything but wait. help other tasks in the meantime - if (Thread.currentThread() instanceof ForkJoinWorkerThread) { // if called outside FJP the FJTask.fork() starts up commonPool which is undesirable - pool.awaitQuiescence(millis, TimeUnit.MILLISECONDS); + if (!isDone()) { + ForkJoinPool.commonPool().awaitQuiescence(millis, TimeUnit.MILLISECONDS); } } } @@ -365,7 +321,7 @@ public class JobLauncherImpl extends JobLauncher { List> tasks = new ArrayList<>(); for (int i = 0; i < JobSchedulerImpl.CORES_COUNT; i++) { - tasks.add(pool.submit(new MyTask(i))); + tasks.add(ForkJoinPool.commonPool().submit(new MyTask(i))); } boolean result = true; diff --git a/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java b/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java index 448aab7cd286..1f9f0fcd1ded 100644 --- a/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java @@ -17,6 +17,7 @@ package com.intellij.openapi.application.impl; import com.intellij.BundleBase; import com.intellij.CommonBundle; +import com.intellij.concurrency.IdeaForkJoinWorkerThreadFactory; import com.intellij.diagnostic.LogEventException; import com.intellij.diagnostic.PerformanceWatcher; import com.intellij.diagnostic.ThreadDumper; @@ -167,6 +168,10 @@ public class ApplicationImpl extends PlatformComponentManagerImpl implements App } }; + static { + IdeaForkJoinWorkerThreadFactory.setupForkJoinCommonPool(); + } + public ApplicationImpl(boolean isInternal, boolean isUnitTestMode, boolean isHeadless, From 82417ae5cc196be6f3c68b0b0a79227b77d88dd7 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Wed, 9 Mar 2016 16:26:38 +0300 Subject: [PATCH 56/83] always register balloon in Disposer --- .../com/intellij/notification/EventLog.java | 10 ++++++---- .../impl/NotificationsManagerImpl.java | 19 ++++++++++++------- .../src/com/intellij/ui/BalloonImpl.java | 2 -- .../vcs/actions/AnnotateDiffViewerAction.java | 6 ++---- 4 files changed, 20 insertions(+), 17 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/notification/EventLog.java b/platform/platform-impl/src/com/intellij/notification/EventLog.java index ee8871d590cc..f239624650c3 100644 --- a/platform/platform-impl/src/com/intellij/notification/EventLog.java +++ b/platform/platform-impl/src/com/intellij/notification/EventLog.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 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. @@ -31,7 +31,10 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.startup.StartupManager; import com.intellij.openapi.ui.popup.Balloon; -import com.intellij.openapi.util.*; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.ShutDownTracker; +import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.util.Trinity; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.wm.*; import com.intellij.ui.awt.RelativePoint; @@ -535,8 +538,7 @@ public class EventLog { if (target != null) { IdeFrame frame = WindowManager.getInstance().getIdeFrame(project); assert frame != null; - Balloon balloon = NotificationsManagerImpl.createBalloon(frame, myNotification, true, true, null); - Disposer.register(project, balloon); + Balloon balloon = NotificationsManagerImpl.createBalloon(frame, myNotification, true, true, null, project); balloon.show(target, Balloon.Position.above); } } diff --git a/platform/platform-impl/src/com/intellij/notification/impl/NotificationsManagerImpl.java b/platform/platform-impl/src/com/intellij/notification/impl/NotificationsManagerImpl.java index 7952ad2a6278..e56eca06b402 100644 --- a/platform/platform-impl/src/com/intellij/notification/impl/NotificationsManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/notification/impl/NotificationsManagerImpl.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. @@ -21,6 +21,7 @@ import com.intellij.ide.ui.laf.darcula.ui.DarculaButtonPainter; import com.intellij.ide.ui.laf.darcula.ui.DarculaButtonUI; import com.intellij.notification.*; import com.intellij.notification.impl.ui.NotificationsUtil; +import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; @@ -228,8 +229,7 @@ public class NotificationsManagerImpl extends NotificationsManager { final boolean noProjects = projectManager.getOpenProjects().length == 0; final boolean sticky = NotificationDisplayType.STICKY_BALLOON == displayType || noProjects; Ref layoutDataRef = newEnabled() ? new Ref() : null; - final Balloon balloon = createBalloon((IdeFrame)window, notification, false, false, layoutDataRef); - Disposer.register(project != null ? project : ApplicationManager.getApplication(), balloon); + final Balloon balloon = createBalloon((IdeFrame)window, notification, false, false, layoutDataRef, project != null ? project : ApplicationManager.getApplication()); if (notification.isExpired()) { return null; @@ -295,8 +295,9 @@ public class NotificationsManagerImpl extends NotificationsManager { @NotNull final Notification notification, final boolean showCallout, final boolean hideOnClickOutside, - @Nullable Ref layoutDataRef) { - return createBalloon(window.getComponent(), notification, showCallout, hideOnClickOutside, layoutDataRef); + @Nullable Ref layoutDataRef, + @NotNull Disposable parentDisposable) { + return createBalloon(window.getComponent(), notification, showCallout, hideOnClickOutside, layoutDataRef, parentDisposable); } @NotNull @@ -304,9 +305,12 @@ public class NotificationsManagerImpl extends NotificationsManager { @NotNull final Notification notification, final boolean showCallout, final boolean hideOnClickOutside, - @Nullable Ref layoutDataRef) { + @Nullable Ref layoutDataRef, + @NotNull Disposable parentDisposable) { if (layoutDataRef != null) { - return createNewBalloon(windowComponent, notification, showCallout, hideOnClickOutside, layoutDataRef); + Balloon balloon = createNewBalloon(windowComponent, notification, showCallout, hideOnClickOutside, layoutDataRef); + Disposer.register(parentDisposable, balloon); + return balloon; } final JEditorPane text = new JEditorPane(); @@ -380,6 +384,7 @@ public class NotificationsManagerImpl extends NotificationsManager { final Balloon balloon = builder.createBalloon(); balloon.setAnimationEnabled(false); notification.setBalloon(balloon); + Disposer.register(parentDisposable, balloon); return balloon; } diff --git a/platform/platform-impl/src/com/intellij/ui/BalloonImpl.java b/platform/platform-impl/src/com/intellij/ui/BalloonImpl.java index b478f7f829a6..cf0d89d8a3ee 100644 --- a/platform/platform-impl/src/com/intellij/ui/BalloonImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/BalloonImpl.java @@ -917,8 +917,6 @@ public class BalloonImpl implements Balloon, IdeTooltip.Ui { Toolkit.getDefaultToolkit().removeAWTEventListener(myAwtActivityListener); if (myLayeredPane != null) { myLayeredPane.removeComponentListener(myComponentListener); - Disposer.register(ApplicationManager.getApplication(), - this); // to be safe if Application suddenly exits and animation wouldn't have a chance to complete runAnimation(false, myLayeredPane, disposeRunnable); } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotateDiffViewerAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotateDiffViewerAction.java index 0b8f47d141b5..6af228909dfe 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotateDiffViewerAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotateDiffViewerAction.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. @@ -46,7 +46,6 @@ import com.intellij.openapi.progress.util.BackgroundTaskUtil; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.Balloon; -import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vcs.*; @@ -388,8 +387,7 @@ public class AnnotateDiffViewerAction extends ToggleAction implements DumbAware return; } - Balloon balloon = NotificationsManagerImpl.createBalloon(component, notification, false, true, null); - Disposer.register(viewer, balloon); + Balloon balloon = NotificationsManagerImpl.createBalloon(component, notification, false, true, null, viewer); Dimension componentSize = component.getSize(); Dimension balloonSize = balloon.getPreferredSize(); From 92de3814e464896c0b4eeccaa99165fb970a71ea Mon Sep 17 00:00:00 2001 From: Konstantin Kolosovsky Date: Wed, 9 Mar 2016 16:39:48 +0300 Subject: [PATCH 57/83] svn: Fixed committing case-only renames (on Windows) "ContainerUtil.classify()" uses "Set" internally. This way only one case-only different "FilePath" instance is left after "classify()" on case-insensitive OS and corresponding commit fails. "ContainerUtil.groupBy()" solves this issue. --- .../svn4idea/src/org/jetbrains/idea/svn/SvnUtil.java | 11 +++++++---- .../idea/svn/checkin/SvnCheckinEnvironment.java | 6 ++++-- .../idea/svn/rollback/SvnRollbackEnvironment.java | 7 +++++-- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnUtil.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnUtil.java index 75e4d6c4f1fc..2c1eb879f09a 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnUtil.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnUtil.java @@ -40,8 +40,10 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.openapi.wm.impl.status.StatusBarUtil; import com.intellij.util.ArrayUtil; +import com.intellij.util.NotNullFunction; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.Convertor; +import com.intellij.util.containers.MultiMap; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -308,7 +310,7 @@ public class SvnUtil { } @NotNull - public static Map, Set> splitChangesIntoWc(@NotNull SvnVcs vcs, @NotNull List changes) { + public static MultiMap, Change> splitChangesIntoWc(@NotNull SvnVcs vcs, @NotNull List changes) { return splitIntoRepositoriesMap(vcs, changes, new Convertor() { @Override public FilePath convert(@NotNull Change change) { @@ -318,12 +320,13 @@ public class SvnUtil { } @NotNull - public static Map, Set> splitIntoRepositoriesMap(@NotNull final SvnVcs vcs, + public static MultiMap, T> splitIntoRepositoriesMap(@NotNull final SvnVcs vcs, @NotNull List items, @NotNull final Convertor converter) { - return ContainerUtil.classify(items.iterator(), new Convertor>() { + return ContainerUtil.groupBy(items, new NotNullFunction>() { + @NotNull @Override - public Pair convert(@NotNull T item) { + public Pair fun(@NotNull T item) { RootUrlInfo path = vcs.getSvnFileUrlMapping().getWcRootForFilePath(converter.convert(item).getIOFile()); return path == null ? UNKNOWN_REPOSITORY_AND_FORMAT : Pair.create(path.getRepositoryUrlUrl(), path.getFormat()); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/SvnCheckinEnvironment.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/SvnCheckinEnvironment.java index 588e218c0167..63bb448b4a44 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/SvnCheckinEnvironment.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/SvnCheckinEnvironment.java @@ -40,6 +40,7 @@ import com.intellij.util.NullableFunction; import com.intellij.util.PairConsumer; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.Convertor; +import com.intellij.util.containers.MultiMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.*; @@ -84,8 +85,9 @@ public class SvnCheckinEnvironment implements CheckinEnvironment { private void doCommit(@NotNull List committables, String comment, List exception, final Set feedback) { //noinspection unchecked - Map, Set> map = SvnUtil.splitIntoRepositoriesMap(mySvnVcs, committables, Convertor.SELF); - for (Map.Entry, Set> entry : map.entrySet()) { + MultiMap, FilePath> map = SvnUtil.splitIntoRepositoriesMap(mySvnVcs, committables, Convertor.SELF); + + for (Map.Entry, Collection> entry : map.entrySet()) { try { doCommitOneRepo(entry.getValue(), comment, exception, feedback, entry.getKey().getSecond()); } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/rollback/SvnRollbackEnvironment.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/rollback/SvnRollbackEnvironment.java index ce9ce271ffa6..272e90bc1ae6 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/rollback/SvnRollbackEnvironment.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/rollback/SvnRollbackEnvironment.java @@ -17,6 +17,7 @@ package org.jetbrains.idea.svn.rollback; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Couple; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vcs.VcsException; @@ -29,12 +30,14 @@ import org.jetbrains.idea.svn.*; import org.jetbrains.idea.svn.api.Depth; import org.jetbrains.idea.svn.info.Info; import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.SVNURL; import org.tmatesoft.svn.core.wc.SVNRevision; import java.io.File; import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Map; /** * @author yole @@ -56,9 +59,9 @@ public class SvnRollbackEnvironment extends DefaultRollbackEnvironment { @NotNull RollbackProgressListener listener) { listener.indeterminate(); - for (Collection collection : SvnUtil.splitChangesIntoWc(mySvnVcs, changes).values()) { + for (Map.Entry, Collection> entry : SvnUtil.splitChangesIntoWc(mySvnVcs, changes).entrySet()) { // to be more sure about nested changes, being or being not reverted - List sortedChanges = ContainerUtil.sorted(collection, ChangesAfterPathComparator.getInstance()); + List sortedChanges = ContainerUtil.sorted(entry.getValue(), ChangesAfterPathComparator.getInstance()); rollbackGroupForWc(sortedChanges, exceptions, listener); } From 9c0ecff67e32fcf930aa73d277949c68e8642a9d Mon Sep 17 00:00:00 2001 From: Semyon Proshev Date: Wed, 17 Feb 2016 15:27:56 +0300 Subject: [PATCH 58/83] PY-18322 Fixed: PyCharm cannot detect obvious unresolved method Correctly determine openFunctionType in Python 2. Update read and write types of builtin file in skeletons --- .../helpers/python-skeletons/__builtin__.py | 14 +++++----- .../stdlib/PyStdlibTypeProvider.java | 27 +++++++++---------- .../{bytesIORead.py => bytesIOMethods.py} | 0 .../fileMethods.py | 7 +++++ .../com/jetbrains/python/PyTypeTest.java | 6 ++--- .../PyUnresolvedReferencesInspectionTest.java | 7 ++++- 6 files changed, 36 insertions(+), 25 deletions(-) rename python/testData/inspections/PyUnresolvedReferencesInspection/{bytesIORead.py => bytesIOMethods.py} (100%) create mode 100644 python/testData/inspections/PyUnresolvedReferencesInspection/fileMethods.py diff --git a/python/helpers/python-skeletons/__builtin__.py b/python/helpers/python-skeletons/__builtin__.py index d9f63538c4d9..95ace39a878a 100644 --- a/python/helpers/python-skeletons/__builtin__.py +++ b/python/helpers/python-skeletons/__builtin__.py @@ -2390,7 +2390,7 @@ class file(object): def next(self): """Returns the next input line. - :rtype: bytes | unicode + :rtype: bytes """ return '' @@ -2399,7 +2399,7 @@ class file(object): before obtaining size bytes). :type size: numbers.Integral - :rtype: bytes | unicode + :rtype: bytes """ return '' @@ -2407,7 +2407,7 @@ class file(object): """Read one entire line from the file. :type size: numbers.Integral - :rtype: bytes | unicode + :rtype: bytes """ return '' @@ -2416,14 +2416,14 @@ class file(object): lines thus read. :type sizehint: numbers.Integral - :rtype: list[bytes | unicode] + :rtype: list[bytes] """ return [] def xreadlines(self): """This method returns the same thing as iter(f). - :rtype: collections.Iterable[bytes | unicode] + :rtype: collections.Iterable[bytes] """ return [] @@ -2454,7 +2454,7 @@ class file(object): def write(self, str): """"Write a string to the file. - :type str: bytes | unicode + :type str: bytes :rtype: None """ pass @@ -2462,7 +2462,7 @@ class file(object): def writelines(self, sequence): """Write a sequence of strings to the file. - :type sequence: collections.Iterable[bytes | unicode] + :type sequence: collections.Iterable[bytes] :rtype: None """ pass diff --git a/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java b/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java index b7a3c49c3bf4..ec08ba2ab102 100644 --- a/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java +++ b/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.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. @@ -44,8 +44,10 @@ import static com.jetbrains.python.psi.PyUtil.as; public class PyStdlibTypeProvider extends PyTypeProviderBase { private static final Set OPEN_FUNCTIONS = ImmutableSet.of("__builtin__.open", "io.open", "os.fdopen", "pathlib.Path.open"); - private static final String BINARY_FILE_TYPE = "io.FileIO[bytes]"; - private static final String TEXT_FILE_TYPE = "io.TextIOWrapper[unicode]"; + + private static final String PY2K_FILE_TYPE = "file"; + private static final String PY3K_BINARY_FILE_TYPE = "io.FileIO[bytes]"; + private static final String PY3K_TEXT_FILE_TYPE = "io.TextIOWrapper[unicode]"; @Nullable public static PyStdlibTypeProvider getInstance() { @@ -275,19 +277,16 @@ public class PyStdlibTypeProvider extends PyTypeProviderBase { } } final LanguageLevel level = LanguageLevel.forElement(anchor); - // Binary mode - if (mode.contains("b")) { - return PyTypeParser.getTypeByName(anchor, BINARY_FILE_TYPE); - } - // Text mode - else { - if (level.isPy3K() || "io.open".equals(callQName)) { - return PyTypeParser.getTypeByName(anchor, TEXT_FILE_TYPE); - } - else { - return PyTypeParser.getTypeByName(anchor, BINARY_FILE_TYPE); + + if (level.isPy3K() || "io.open".equals(callQName)) { + if (mode.contains("b")) { + return PyTypeParser.getTypeByName(anchor, PY3K_BINARY_FILE_TYPE); + } else { + return PyTypeParser.getTypeByName(anchor, PY3K_TEXT_FILE_TYPE); } } + + return PyTypeParser.getTypeByName(anchor, PY2K_FILE_TYPE); } @Nullable diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/bytesIORead.py b/python/testData/inspections/PyUnresolvedReferencesInspection/bytesIOMethods.py similarity index 100% rename from python/testData/inspections/PyUnresolvedReferencesInspection/bytesIORead.py rename to python/testData/inspections/PyUnresolvedReferencesInspection/bytesIOMethods.py diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/fileMethods.py b/python/testData/inspections/PyUnresolvedReferencesInspection/fileMethods.py new file mode 100644 index 000000000000..8f69c667fa99 --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection/fileMethods.py @@ -0,0 +1,7 @@ +f = open("file.txt") +f.writelines("a") # OK +f.writeliness("a") + +f = open("file.txt", "rb") +f.writelines("a") # OK +f.writeliness("a") \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PyTypeTest.java b/python/testSrc/com/jetbrains/python/PyTypeTest.java index 93e2973b1933..247e1da55543 100644 --- a/python/testSrc/com/jetbrains/python/PyTypeTest.java +++ b/python/testSrc/com/jetbrains/python/PyTypeTest.java @@ -694,17 +694,17 @@ public class PyTypeTest extends PyTestCase { } public void testOpenDefault() { - doTest("FileIO[str]", + doTest("file", "expr = open('foo')\n"); } public void testOpenText() { - doTest("FileIO[str]", + doTest("file", "expr = open('foo', 'r')\n"); } public void testOpenBinary() { - doTest("FileIO[str]", + doTest("file", "expr = open('foo', 'rb')\n"); } diff --git a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java index 9f979def0fd4..a79347e74771 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java @@ -348,7 +348,12 @@ public class PyUnresolvedReferencesInspectionTest extends PyInspectionTestCase { doMultiFileTest("a.py"); } - public void testBytesIORead() { + public void testBytesIOMethods() { + doTest(); + } + + // PY-18322 + public void testFileMethods() { doTest(); } From 9cb08375e6bd6c471abfe9517891a51d6c0429b8 Mon Sep 17 00:00:00 2001 From: Semyon Proshev Date: Wed, 9 Mar 2016 17:07:48 +0300 Subject: [PATCH 59/83] Tests for open function type in Python 3 --- .../testSrc/com/jetbrains/python/Py3TypeTest.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/python/testSrc/com/jetbrains/python/Py3TypeTest.java b/python/testSrc/com/jetbrains/python/Py3TypeTest.java index b5d612a99171..a70fead0608a 100644 --- a/python/testSrc/com/jetbrains/python/Py3TypeTest.java +++ b/python/testSrc/com/jetbrains/python/Py3TypeTest.java @@ -195,6 +195,21 @@ public class Py3TypeTest extends PyTestCase { }); } + public void testOpenDefault() { + doTest("TextIOWrapper[str]", + "expr = open('foo')\n"); + } + + public void testOpenText() { + doTest("TextIOWrapper[str]", + "expr = open('foo', 'r')\n"); + } + + public void testOpenBinary() { + doTest("FileIO[bytes]", + "expr = open('foo', 'rb')\n"); + } + private void doTest(final String expectedType, final String text) { myFixture.configureByText(PythonFileType.INSTANCE, text); final PyExpression expr = myFixture.findElementByText("expr", PyExpression.class); From 2c2cbf3adbf103beac78c32ce9149fb148b4f9ec Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Fri, 4 Mar 2016 15:03:34 +0300 Subject: [PATCH 60/83] PY-18386 Add recursion guard to handle recursive types via PEP 484 type aliases Recursive type are not fully supported yet. It only prevents SOE for them. --- .../codeInsight/PyTypingTypeProvider.java | 167 +++++++++++------- .../com/jetbrains/python/PyTypingTest.java | 32 ++++ 2 files changed, 133 insertions(+), 66 deletions(-) diff --git a/python/src/com/jetbrains/python/codeInsight/PyTypingTypeProvider.java b/python/src/com/jetbrains/python/codeInsight/PyTypingTypeProvider.java index 8d267245eb8f..f37048a1c21d 100644 --- a/python/src/com/jetbrains/python/codeInsight/PyTypingTypeProvider.java +++ b/python/src/com/jetbrains/python/codeInsight/PyTypingTypeProvider.java @@ -25,6 +25,7 @@ import com.intellij.psi.PsiComment; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiPolyVariantReference; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.containers.HashSet; import com.jetbrains.python.PyNames; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.impl.PyExpressionCodeFragmentImpl; @@ -82,7 +83,7 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { // XXX: Requires switching from stub to AST final PyExpression value = annotation.getValue(); if (value != null) { - final PyType type = getType(value, context); + final PyType type = getType(value, context, new HashSet<>()); if (type != null) { final PyType optionalType = getOptionalTypeFromDefaultNone(param, type, context); return Ref.create(optionalType != null ? optionalType : type); @@ -126,11 +127,11 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { // XXX: Requires switching from stub to AST final PyExpression value = annotation.getValue(); if (value != null) { - final PyType type = getType(value, context); + final PyType type = getType(value, context, new HashSet<>()); return type != null ? Ref.create(type) : null; } } - final PyType constructorType = getGenericConstructorType(function, context); + final PyType constructorType = getGenericConstructorType(function, context, new HashSet<>()); if (constructorType != null) { return Ref.create(constructorType); } @@ -154,7 +155,7 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { final PyExpression[] args = callExpr.getArguments(); if (args.length > 0) { final PyExpression typeExpr = args[0]; - return getType(typeExpr, context); + return getType(typeExpr, context, new HashSet<>()); } } return null; @@ -166,7 +167,7 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { final PyTargetExpression target = (PyTargetExpression)referenceTarget; final String comment = getTypeComment(target); if (comment != null) { - final PyType type = getStringBasedType(comment, referenceTarget, context); + final PyType type = getStringBasedType(comment, referenceTarget, context, new HashSet<>()); if (type instanceof PyTupleType) { final PyTupleExpression tupleExpr = PsiTreeUtil.getParentOfType(target, PyTupleExpression.class); if (tupleExpr != null) { @@ -243,11 +244,11 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { } @Nullable - private static PyType getGenericConstructorType(@NotNull PyFunction function, @NotNull TypeEvalContext context) { + private static PyType getGenericConstructorType(@NotNull PyFunction function, @NotNull TypeEvalContext context, @NotNull Set cache) { if (PyUtil.isInit(function)) { final PyClass cls = function.getContainingClass(); if (cls != null) { - final List genericTypes = collectGenericTypes(cls, context); + final List genericTypes = collectGenericTypes(cls, context, cache); final List elementTypes = new ArrayList(genericTypes); if (!elementTypes.isEmpty()) { return new PyCollectionTypeImpl(cls, false, elementTypes); @@ -258,7 +259,7 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { } @NotNull - private static List collectGenericTypes(@NotNull PyClass cls, @NotNull TypeEvalContext context) { + private static List collectGenericTypes(@NotNull PyClass cls, @NotNull TypeEvalContext context, @NotNull Set cache) { boolean isGeneric = false; for (PyClass ancestor : cls.getAncestorClasses(context)) { if (GENERIC_CLASSES.contains(ancestor.getQualifiedName())) { @@ -274,7 +275,7 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { final PyExpression indexExpr = ((PySubscriptionExpression)expr).getIndexExpression(); if (indexExpr != null) { for (PsiElement resolved : tryResolving(indexExpr, context)) { - final PyGenericType genericType = getGenericType(resolved, context); + final PyGenericType genericType = getGenericType(resolved, context, cache); if (genericType != null) { results.add(genericType); } @@ -288,49 +289,62 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { } @Nullable - private static PyType getType(@NotNull PyExpression expression, @NotNull TypeEvalContext context) { + private static PyType getType(@NotNull PyExpression expression, @NotNull TypeEvalContext context, @NotNull Set cache) { final List members = Lists.newArrayList(); for (PsiElement resolved : tryResolving(expression, context)) { - members.add(getTypeForResolvedElement(resolved, context)); + members.add(getTypeForResolvedElement(resolved, context, cache)); } return PyUnionType.union(members); } @Nullable - private static PyType getTypeForResolvedElement(@NotNull PsiElement resolved, @NotNull TypeEvalContext context) { - final PyType unionType = getUnionType(resolved, context); - if (unionType != null) { - return unionType; + private static PyType getTypeForResolvedElement(@NotNull PsiElement resolved, + @NotNull TypeEvalContext context, + @NotNull Set cache) { + if (cache.contains(resolved)) { + // Recursive types are not yet supported + return null; } - final Ref optionalType = getOptionalType(resolved, context); - if (optionalType != null) { - return optionalType.get(); + + cache.add(resolved); + try { + final PyType unionType = getUnionType(resolved, context, cache); + if (unionType != null) { + return unionType; + } + final Ref optionalType = getOptionalType(resolved, context, cache); + if (optionalType != null) { + return optionalType.get(); + } + final PyType callableType = getCallableType(resolved, context, cache); + if (callableType != null) { + return callableType; + } + final PyType parameterizedType = getParameterizedType(resolved, context, cache); + if (parameterizedType != null) { + return parameterizedType; + } + final PyType builtinCollection = getBuiltinCollection(resolved); + if (builtinCollection != null) { + return builtinCollection; + } + final PyType genericType = getGenericType(resolved, context, cache); + if (genericType != null) { + return genericType; + } + final Ref classType = getClassType(resolved, context); + if (classType != null) { + return classType.get(); + } + final PyType stringBasedType = getStringBasedType(resolved, context, cache); + if (stringBasedType != null) { + return stringBasedType; + } + return null; } - final PyType callableType = getCallableType(resolved, context); - if (callableType != null) { - return callableType; + finally { + cache.remove(resolved); } - final PyType parameterizedType = getParameterizedType(resolved, context); - if (parameterizedType != null) { - return parameterizedType; - } - final PyType builtinCollection = getBuiltinCollection(resolved); - if (builtinCollection != null) { - return builtinCollection; - } - final PyType genericType = getGenericType(resolved, context); - if (genericType != null) { - return genericType; - } - final Ref classType = getClassType(resolved, context); - if (classType != null) { - return classType.get(); - } - final PyType stringBasedType = getStringBasedType(resolved, context); - if (stringBasedType != null) { - return stringBasedType; - } - return null; } @Nullable @@ -357,10 +371,18 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { } @Nullable - public static PyType getTypeFromTargetExpression(@NotNull PyTargetExpression expression, @NotNull TypeEvalContext context) { + public static PyType getTypeFromTargetExpression(@NotNull PyTargetExpression expression, + @NotNull TypeEvalContext context) { + return getTypeFromTargetExpression(expression, context, new HashSet<>()); + } + + @Nullable + private static PyType getTypeFromTargetExpression(@NotNull PyTargetExpression expression, + @NotNull TypeEvalContext context, + @NotNull Set cache) { // XXX: Requires switching from stub to AST final PyExpression assignedValue = expression.findAssignedValue(); - return assignedValue != null ? getTypeForResolvedElement(assignedValue, context) : null; + return assignedValue != null ? getTypeForResolvedElement(assignedValue, context, cache) : null; } @Nullable @@ -385,7 +407,9 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { } @Nullable - private static Ref getOptionalType(@NotNull PsiElement element, @NotNull TypeEvalContext context) { + private static Ref getOptionalType(@NotNull PsiElement element, + @NotNull TypeEvalContext context, + @NotNull Set cache) { if (element instanceof PySubscriptionExpression) { final PySubscriptionExpression subscriptionExpr = (PySubscriptionExpression)element; final PyExpression operand = subscriptionExpr.getOperand(); @@ -393,7 +417,7 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { if (operandNames.contains("typing.Optional")) { final PyExpression indexExpr = subscriptionExpr.getIndexExpression(); if (indexExpr != null) { - final PyType type = getType(indexExpr, context); + final PyType type = getType(indexExpr, context, cache); if (type != null) { return Ref.create(PyUnionType.union(type, PyNoneType.INSTANCE)); } @@ -405,17 +429,20 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { } @Nullable - private static PyType getStringBasedType(@NotNull PsiElement element, @NotNull TypeEvalContext context) { + private static PyType getStringBasedType(@NotNull PsiElement element, @NotNull TypeEvalContext context, @NotNull Set cache) { if (element instanceof PyStringLiteralExpression) { // XXX: Requires switching from stub to AST final String contents = ((PyStringLiteralExpression)element).getStringValue(); - return getStringBasedType(contents, element, context); + return getStringBasedType(contents, element, context, cache); } return null; } @Nullable - private static PyType getStringBasedType(@NotNull String contents, @NotNull PsiElement anchor, @NotNull TypeEvalContext context) { + private static PyType getStringBasedType(@NotNull String contents, + @NotNull PsiElement anchor, + @NotNull TypeEvalContext context, + @NotNull Set cache) { final Project project = anchor.getProject(); final PyExpressionCodeFragmentImpl codeFragment = new PyExpressionCodeFragmentImpl(project, "dummy.py", contents, false); codeFragment.setContext(anchor.getContainingFile()); @@ -426,17 +453,17 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { final PyTupleExpression tupleExpr = (PyTupleExpression)expr; final List elementTypes = new ArrayList(); for (PyExpression elementExpr : tupleExpr.getElements()) { - elementTypes.add(getType(elementExpr, context)); + elementTypes.add(getType(elementExpr, context, cache)); } return PyTupleType.create(anchor, elementTypes.toArray(new PyType[elementTypes.size()])); } - return getType(expr, context); + return getType(expr, context, cache); } return null; } @Nullable - private static PyType getCallableType(@NotNull PsiElement resolved, @NotNull TypeEvalContext context) { + private static PyType getCallableType(@NotNull PsiElement resolved, @NotNull TypeEvalContext context, @NotNull Set cache) { if (resolved instanceof PySubscriptionExpression) { final PySubscriptionExpression subscriptionExpr = (PySubscriptionExpression)resolved; final PyExpression operand = subscriptionExpr.getOperand(); @@ -452,10 +479,10 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { final List parameters = new ArrayList(); final PyListLiteralExpression listExpr = (PyListLiteralExpression)parametersExpr; for (PyExpression argExpr : listExpr.getElements()) { - parameters.add(new PyCallableParameterImpl(null, getType(argExpr, context))); + parameters.add(new PyCallableParameterImpl(null, getType(argExpr, context, cache))); } final PyExpression returnTypeExpr = elements[1]; - final PyType returnType = getType(returnTypeExpr, context); + final PyType returnType = getType(returnTypeExpr, context, cache); return new PyCallableTypeImpl(parameters, returnType); } } @@ -466,20 +493,22 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { } @Nullable - private static PyType getUnionType(@NotNull PsiElement element, @NotNull TypeEvalContext context) { + private static PyType getUnionType(@NotNull PsiElement element, @NotNull TypeEvalContext context, @NotNull Set cache) { if (element instanceof PySubscriptionExpression) { final PySubscriptionExpression subscriptionExpr = (PySubscriptionExpression)element; final PyExpression operand = subscriptionExpr.getOperand(); final Collection operandNames = resolveToQualifiedNames(operand, context); if (operandNames.contains("typing.Union")) { - return PyUnionType.union(getIndexTypes(subscriptionExpr, context)); + return PyUnionType.union(getIndexTypes(subscriptionExpr, context, cache)); } } return null; } @Nullable - private static PyGenericType getGenericType(@NotNull PsiElement element, @NotNull TypeEvalContext context) { + private static PyGenericType getGenericType(@NotNull PsiElement element, + @NotNull TypeEvalContext context, + @NotNull Set cache) { if (element instanceof PyCallExpression) { final PyCallExpression assignedCall = (PyCallExpression)element; final PyExpression callee = assignedCall.getCallee(); @@ -492,7 +521,7 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { if (firstArgument instanceof PyStringLiteralExpression) { final String name = ((PyStringLiteralExpression)firstArgument).getStringValue(); if (name != null) { - return new PyGenericType(name, getGenericTypeBound(arguments, context)); + return new PyGenericType(name, getGenericTypeBound(arguments, context, cache)); } } } @@ -503,40 +532,46 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { } @Nullable - private static PyType getGenericTypeBound(@NotNull PyExpression[] typeVarArguments, @NotNull TypeEvalContext context) { + private static PyType getGenericTypeBound(@NotNull PyExpression[] typeVarArguments, + @NotNull TypeEvalContext context, + @NotNull Set cache) { final List types = new ArrayList(); for (int i = 1; i < typeVarArguments.length; i++) { - types.add(getType(typeVarArguments[i], context)); + types.add(getType(typeVarArguments[i], context, cache)); } return PyUnionType.union(types); } @NotNull - private static List getIndexTypes(@NotNull PySubscriptionExpression expression, @NotNull TypeEvalContext context) { + private static List getIndexTypes(@NotNull PySubscriptionExpression expression, + @NotNull TypeEvalContext context, + @NotNull Set cache) { final List types = new ArrayList(); final PyExpression indexExpr = expression.getIndexExpression(); if (indexExpr instanceof PyTupleExpression) { final PyTupleExpression tupleExpr = (PyTupleExpression)indexExpr; for (PyExpression expr : tupleExpr.getElements()) { - types.add(getType(expr, context)); + types.add(getType(expr, context, cache)); } } else if (indexExpr != null) { - types.add(getType(indexExpr, context)); + types.add(getType(indexExpr, context, cache)); } return types; } @Nullable - private static PyType getParameterizedType(@NotNull PsiElement element, @NotNull TypeEvalContext context) { + private static PyType getParameterizedType(@NotNull PsiElement element, + @NotNull TypeEvalContext context, + @NotNull Set cache) { if (element instanceof PySubscriptionExpression) { final PySubscriptionExpression subscriptionExpr = (PySubscriptionExpression)element; final PyExpression operand = subscriptionExpr.getOperand(); final PyExpression indexExpr = subscriptionExpr.getIndexExpression(); - final PyType operandType = getType(operand, context); + final PyType operandType = getType(operand, context, cache); if (operandType instanceof PyClassType) { final PyClass cls = ((PyClassType)operandType).getPyClass(); - final List indexTypes = getIndexTypes(subscriptionExpr, context); + final List indexTypes = getIndexTypes(subscriptionExpr, context, cache); if (PyNames.TUPLE.equals(cls.getQualifiedName())) { return PyTupleType.create(element, indexTypes.toArray(new PyType[indexTypes.size()])); } diff --git a/python/testSrc/com/jetbrains/python/PyTypingTest.java b/python/testSrc/com/jetbrains/python/PyTypingTest.java index 8659fd088d1a..889f675ab65c 100644 --- a/python/testSrc/com/jetbrains/python/PyTypingTest.java +++ b/python/testSrc/com/jetbrains/python/PyTypingTest.java @@ -472,6 +472,38 @@ public class PyTypingTest extends PyTestCase { } + // PY-18386 + public void testRecursiveType() { + doTest("Union[int, Any]", + "from typing import Union\n" + + "\n" + + "Type = Union[int, 'Type']\n" + + "expr = 42 # type: Type"); + } + + // PY-18386 + public void testRecursiveType2() { + doTest("Dict[str, Union[Union[str, int, float], Any]]", + "from typing import Dict, Union\n" + + "\n" + + "JsonDict = Dict[str, Union[str, int, float, 'JsonDict']]\n" + + "\n" + + "def f(x: JsonDict):\n" + + " expr = x"); + } + + // PY-18386 + public void testRecursiveType3() { + doTest("Union[Union[str, int], Any]", + "from typing import Union\n" + + "\n" + + "Type1 = Union[str, 'Type2']\n" + + "Type2 = Union[int, Type1]\n" + + "\n" + + "expr = None # type: Type1"); + + } + private void doTestNoInjectedText(@NotNull String text) { myFixture.configureByText(PythonFileType.INSTANCE, text); final InjectedLanguageManager languageManager = InjectedLanguageManager.getInstance(myFixture.getProject()); From 3cdc8c9cf8dba256da64290fade0ab99f1442c6e Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Fri, 4 Mar 2016 15:25:25 +0300 Subject: [PATCH 61/83] PY-18386 Replace cache and type eval context parameters with single parameter object --- .../codeInsight/PyTypingTypeProvider.java | 148 +++++++++--------- 1 file changed, 75 insertions(+), 73 deletions(-) diff --git a/python/src/com/jetbrains/python/codeInsight/PyTypingTypeProvider.java b/python/src/com/jetbrains/python/codeInsight/PyTypingTypeProvider.java index f37048a1c21d..d414e9abead1 100644 --- a/python/src/com/jetbrains/python/codeInsight/PyTypingTypeProvider.java +++ b/python/src/com/jetbrains/python/codeInsight/PyTypingTypeProvider.java @@ -83,7 +83,7 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { // XXX: Requires switching from stub to AST final PyExpression value = annotation.getValue(); if (value != null) { - final PyType type = getType(value, context, new HashSet<>()); + final PyType type = getType(value, new Context(context)); if (type != null) { final PyType optionalType = getOptionalTypeFromDefaultNone(param, type, context); return Ref.create(optionalType != null ? optionalType : type); @@ -127,11 +127,11 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { // XXX: Requires switching from stub to AST final PyExpression value = annotation.getValue(); if (value != null) { - final PyType type = getType(value, context, new HashSet<>()); + final PyType type = getType(value, new Context(context)); return type != null ? Ref.create(type) : null; } } - final PyType constructorType = getGenericConstructorType(function, context, new HashSet<>()); + final PyType constructorType = getGenericConstructorType(function, new Context(context)); if (constructorType != null) { return Ref.create(constructorType); } @@ -155,7 +155,7 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { final PyExpression[] args = callExpr.getArguments(); if (args.length > 0) { final PyExpression typeExpr = args[0]; - return getType(typeExpr, context, new HashSet<>()); + return getType(typeExpr, new Context(context)); } } return null; @@ -167,7 +167,7 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { final PyTargetExpression target = (PyTargetExpression)referenceTarget; final String comment = getTypeComment(target); if (comment != null) { - final PyType type = getStringBasedType(comment, referenceTarget, context, new HashSet<>()); + final PyType type = getStringBasedType(comment, referenceTarget, new Context(context)); if (type instanceof PyTupleType) { final PyTupleExpression tupleExpr = PsiTreeUtil.getParentOfType(target, PyTupleExpression.class); if (tupleExpr != null) { @@ -244,11 +244,11 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { } @Nullable - private static PyType getGenericConstructorType(@NotNull PyFunction function, @NotNull TypeEvalContext context, @NotNull Set cache) { + private static PyType getGenericConstructorType(@NotNull PyFunction function, @NotNull Context context) { if (PyUtil.isInit(function)) { final PyClass cls = function.getContainingClass(); if (cls != null) { - final List genericTypes = collectGenericTypes(cls, context, cache); + final List genericTypes = collectGenericTypes(cls, context); final List elementTypes = new ArrayList(genericTypes); if (!elementTypes.isEmpty()) { return new PyCollectionTypeImpl(cls, false, elementTypes); @@ -259,9 +259,9 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { } @NotNull - private static List collectGenericTypes(@NotNull PyClass cls, @NotNull TypeEvalContext context, @NotNull Set cache) { + private static List collectGenericTypes(@NotNull PyClass cls, @NotNull Context context) { boolean isGeneric = false; - for (PyClass ancestor : cls.getAncestorClasses(context)) { + for (PyClass ancestor : cls.getAncestorClasses(context.getTypeContext())) { if (GENERIC_CLASSES.contains(ancestor.getQualifiedName())) { isGeneric = true; break; @@ -274,8 +274,8 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { if (expr instanceof PySubscriptionExpression) { final PyExpression indexExpr = ((PySubscriptionExpression)expr).getIndexExpression(); if (indexExpr != null) { - for (PsiElement resolved : tryResolving(indexExpr, context)) { - final PyGenericType genericType = getGenericType(resolved, context, cache); + for (PsiElement resolved : tryResolving(indexExpr, context.getTypeContext())) { + final PyGenericType genericType = getGenericType(resolved, context); if (genericType != null) { results.add(genericType); } @@ -289,38 +289,36 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { } @Nullable - private static PyType getType(@NotNull PyExpression expression, @NotNull TypeEvalContext context, @NotNull Set cache) { + private static PyType getType(@NotNull PyExpression expression, @NotNull Context context) { final List members = Lists.newArrayList(); - for (PsiElement resolved : tryResolving(expression, context)) { - members.add(getTypeForResolvedElement(resolved, context, cache)); + for (PsiElement resolved : tryResolving(expression, context.getTypeContext())) { + members.add(getTypeForResolvedElement(resolved, context)); } return PyUnionType.union(members); } @Nullable - private static PyType getTypeForResolvedElement(@NotNull PsiElement resolved, - @NotNull TypeEvalContext context, - @NotNull Set cache) { - if (cache.contains(resolved)) { + private static PyType getTypeForResolvedElement(@NotNull PsiElement resolved, @NotNull Context context) { + if (context.getExpressionCache().contains(resolved)) { // Recursive types are not yet supported return null; } - cache.add(resolved); + context.getExpressionCache().add(resolved); try { - final PyType unionType = getUnionType(resolved, context, cache); + final PyType unionType = getUnionType(resolved, context); if (unionType != null) { return unionType; } - final Ref optionalType = getOptionalType(resolved, context, cache); + final Ref optionalType = getOptionalType(resolved, context); if (optionalType != null) { return optionalType.get(); } - final PyType callableType = getCallableType(resolved, context, cache); + final PyType callableType = getCallableType(resolved, context); if (callableType != null) { return callableType; } - final PyType parameterizedType = getParameterizedType(resolved, context, cache); + final PyType parameterizedType = getParameterizedType(resolved, context); if (parameterizedType != null) { return parameterizedType; } @@ -328,22 +326,22 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { if (builtinCollection != null) { return builtinCollection; } - final PyType genericType = getGenericType(resolved, context, cache); + final PyType genericType = getGenericType(resolved, context); if (genericType != null) { return genericType; } - final Ref classType = getClassType(resolved, context); + final Ref classType = getClassType(resolved, context.getTypeContext()); if (classType != null) { return classType.get(); } - final PyType stringBasedType = getStringBasedType(resolved, context, cache); + final PyType stringBasedType = getStringBasedType(resolved, context); if (stringBasedType != null) { return stringBasedType; } return null; } finally { - cache.remove(resolved); + context.getExpressionCache().remove(resolved); } } @@ -371,18 +369,16 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { } @Nullable - public static PyType getTypeFromTargetExpression(@NotNull PyTargetExpression expression, - @NotNull TypeEvalContext context) { - return getTypeFromTargetExpression(expression, context, new HashSet<>()); + public static PyType getTypeFromTargetExpression(@NotNull PyTargetExpression expression, @NotNull TypeEvalContext context) { + return getTypeFromTargetExpression(expression, new Context(context)); } @Nullable private static PyType getTypeFromTargetExpression(@NotNull PyTargetExpression expression, - @NotNull TypeEvalContext context, - @NotNull Set cache) { + @NotNull Context context) { // XXX: Requires switching from stub to AST final PyExpression assignedValue = expression.findAssignedValue(); - return assignedValue != null ? getTypeForResolvedElement(assignedValue, context, cache) : null; + return assignedValue != null ? getTypeForResolvedElement(assignedValue, context) : null; } @Nullable @@ -407,17 +403,15 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { } @Nullable - private static Ref getOptionalType(@NotNull PsiElement element, - @NotNull TypeEvalContext context, - @NotNull Set cache) { + private static Ref getOptionalType(@NotNull PsiElement element, @NotNull Context context) { if (element instanceof PySubscriptionExpression) { final PySubscriptionExpression subscriptionExpr = (PySubscriptionExpression)element; final PyExpression operand = subscriptionExpr.getOperand(); - final Collection operandNames = resolveToQualifiedNames(operand, context); + final Collection operandNames = resolveToQualifiedNames(operand, context.getTypeContext()); if (operandNames.contains("typing.Optional")) { final PyExpression indexExpr = subscriptionExpr.getIndexExpression(); if (indexExpr != null) { - final PyType type = getType(indexExpr, context, cache); + final PyType type = getType(indexExpr, context); if (type != null) { return Ref.create(PyUnionType.union(type, PyNoneType.INSTANCE)); } @@ -429,20 +423,17 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { } @Nullable - private static PyType getStringBasedType(@NotNull PsiElement element, @NotNull TypeEvalContext context, @NotNull Set cache) { + private static PyType getStringBasedType(@NotNull PsiElement element, @NotNull Context context) { if (element instanceof PyStringLiteralExpression) { // XXX: Requires switching from stub to AST final String contents = ((PyStringLiteralExpression)element).getStringValue(); - return getStringBasedType(contents, element, context, cache); + return getStringBasedType(contents, element, context); } return null; } @Nullable - private static PyType getStringBasedType(@NotNull String contents, - @NotNull PsiElement anchor, - @NotNull TypeEvalContext context, - @NotNull Set cache) { + private static PyType getStringBasedType(@NotNull String contents, @NotNull PsiElement anchor, @NotNull Context context) { final Project project = anchor.getProject(); final PyExpressionCodeFragmentImpl codeFragment = new PyExpressionCodeFragmentImpl(project, "dummy.py", contents, false); codeFragment.setContext(anchor.getContainingFile()); @@ -453,21 +444,21 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { final PyTupleExpression tupleExpr = (PyTupleExpression)expr; final List elementTypes = new ArrayList(); for (PyExpression elementExpr : tupleExpr.getElements()) { - elementTypes.add(getType(elementExpr, context, cache)); + elementTypes.add(getType(elementExpr, context)); } return PyTupleType.create(anchor, elementTypes.toArray(new PyType[elementTypes.size()])); } - return getType(expr, context, cache); + return getType(expr, context); } return null; } @Nullable - private static PyType getCallableType(@NotNull PsiElement resolved, @NotNull TypeEvalContext context, @NotNull Set cache) { + private static PyType getCallableType(@NotNull PsiElement resolved, @NotNull Context context) { if (resolved instanceof PySubscriptionExpression) { final PySubscriptionExpression subscriptionExpr = (PySubscriptionExpression)resolved; final PyExpression operand = subscriptionExpr.getOperand(); - final Collection operandNames = resolveToQualifiedNames(operand, context); + final Collection operandNames = resolveToQualifiedNames(operand, context.getTypeContext()); if (operandNames.contains("typing.Callable")) { final PyExpression indexExpr = subscriptionExpr.getIndexExpression(); if (indexExpr instanceof PyTupleExpression) { @@ -479,10 +470,10 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { final List parameters = new ArrayList(); final PyListLiteralExpression listExpr = (PyListLiteralExpression)parametersExpr; for (PyExpression argExpr : listExpr.getElements()) { - parameters.add(new PyCallableParameterImpl(null, getType(argExpr, context, cache))); + parameters.add(new PyCallableParameterImpl(null, getType(argExpr, context))); } final PyExpression returnTypeExpr = elements[1]; - final PyType returnType = getType(returnTypeExpr, context, cache); + final PyType returnType = getType(returnTypeExpr, context); return new PyCallableTypeImpl(parameters, returnType); } } @@ -493,27 +484,25 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { } @Nullable - private static PyType getUnionType(@NotNull PsiElement element, @NotNull TypeEvalContext context, @NotNull Set cache) { + private static PyType getUnionType(@NotNull PsiElement element, @NotNull Context context) { if (element instanceof PySubscriptionExpression) { final PySubscriptionExpression subscriptionExpr = (PySubscriptionExpression)element; final PyExpression operand = subscriptionExpr.getOperand(); - final Collection operandNames = resolveToQualifiedNames(operand, context); + final Collection operandNames = resolveToQualifiedNames(operand, context.getTypeContext()); if (operandNames.contains("typing.Union")) { - return PyUnionType.union(getIndexTypes(subscriptionExpr, context, cache)); + return PyUnionType.union(getIndexTypes(subscriptionExpr, context)); } } return null; } @Nullable - private static PyGenericType getGenericType(@NotNull PsiElement element, - @NotNull TypeEvalContext context, - @NotNull Set cache) { + private static PyGenericType getGenericType(@NotNull PsiElement element, @NotNull Context context) { if (element instanceof PyCallExpression) { final PyCallExpression assignedCall = (PyCallExpression)element; final PyExpression callee = assignedCall.getCallee(); if (callee != null) { - final Collection calleeQNames = resolveToQualifiedNames(callee, context); + final Collection calleeQNames = resolveToQualifiedNames(callee, context.getTypeContext()); if (calleeQNames.contains("typing.TypeVar")) { final PyExpression[] arguments = assignedCall.getArguments(); if (arguments.length > 0) { @@ -521,7 +510,7 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { if (firstArgument instanceof PyStringLiteralExpression) { final String name = ((PyStringLiteralExpression)firstArgument).getStringValue(); if (name != null) { - return new PyGenericType(name, getGenericTypeBound(arguments, context, cache)); + return new PyGenericType(name, getGenericTypeBound(arguments, context)); } } } @@ -532,46 +521,40 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { } @Nullable - private static PyType getGenericTypeBound(@NotNull PyExpression[] typeVarArguments, - @NotNull TypeEvalContext context, - @NotNull Set cache) { - final List types = new ArrayList(); + private static PyType getGenericTypeBound(@NotNull PyExpression[] typeVarArguments, @NotNull Context context) { + final List types = new ArrayList<>(); for (int i = 1; i < typeVarArguments.length; i++) { - types.add(getType(typeVarArguments[i], context, cache)); + types.add(getType(typeVarArguments[i], context)); } return PyUnionType.union(types); } @NotNull - private static List getIndexTypes(@NotNull PySubscriptionExpression expression, - @NotNull TypeEvalContext context, - @NotNull Set cache) { + private static List getIndexTypes(@NotNull PySubscriptionExpression expression, @NotNull Context context) { final List types = new ArrayList(); final PyExpression indexExpr = expression.getIndexExpression(); if (indexExpr instanceof PyTupleExpression) { final PyTupleExpression tupleExpr = (PyTupleExpression)indexExpr; for (PyExpression expr : tupleExpr.getElements()) { - types.add(getType(expr, context, cache)); + types.add(getType(expr, context)); } } else if (indexExpr != null) { - types.add(getType(indexExpr, context, cache)); + types.add(getType(indexExpr, context)); } return types; } @Nullable - private static PyType getParameterizedType(@NotNull PsiElement element, - @NotNull TypeEvalContext context, - @NotNull Set cache) { + private static PyType getParameterizedType(@NotNull PsiElement element, @NotNull Context context) { if (element instanceof PySubscriptionExpression) { final PySubscriptionExpression subscriptionExpr = (PySubscriptionExpression)element; final PyExpression operand = subscriptionExpr.getOperand(); final PyExpression indexExpr = subscriptionExpr.getIndexExpression(); - final PyType operandType = getType(operand, context, cache); + final PyType operandType = getType(operand, context); if (operandType instanceof PyClassType) { final PyClass cls = ((PyClassType)operandType).getPyClass(); - final List indexTypes = getIndexTypes(subscriptionExpr, context, cache); + final List indexTypes = getIndexTypes(subscriptionExpr, context); if (PyNames.TUPLE.equals(cls.getQualifiedName())) { return PyTupleType.create(element, indexTypes.toArray(new PyType[indexTypes.size()])); } @@ -646,4 +629,23 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { } return null; } + + private static class Context { + @NotNull private final TypeEvalContext myContext; + @NotNull private final Set myCache = new HashSet<>(); + + private Context(@NotNull TypeEvalContext context) { + myContext = context; + } + + @NotNull + public TypeEvalContext getTypeContext() { + return myContext; + } + + @NotNull + public Set getExpressionCache() { + return myCache; + } + } } From 32cc8e10938631efb34d42427674f9e53a490295 Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 9 Mar 2016 15:35:32 +0100 Subject: [PATCH 62/83] don't drop PsiCacheKey cache on any change in a physical file --- platform/core-api/src/com/intellij/psi/util/PsiCacheKey.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/core-api/src/com/intellij/psi/util/PsiCacheKey.java b/platform/core-api/src/com/intellij/psi/util/PsiCacheKey.java index d91e4823c5c0..bd57a08c6ba6 100644 --- a/platform/core-api/src/com/intellij/psi/util/PsiCacheKey.java +++ b/platform/core-api/src/com/intellij/psi/util/PsiCacheKey.java @@ -76,7 +76,7 @@ public class PsiCacheKey extends Key Date: Wed, 9 Mar 2016 18:06:36 +0300 Subject: [PATCH 63/83] fixed weird different classloaders in community? --- .../intellij/concurrency/IdeaForkJoinWorkerThreadFactory.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/platform/boot/src/com/intellij/concurrency/IdeaForkJoinWorkerThreadFactory.java b/platform/boot/src/com/intellij/concurrency/IdeaForkJoinWorkerThreadFactory.java index 83fc93abad5c..1d741f87abe1 100644 --- a/platform/boot/src/com/intellij/concurrency/IdeaForkJoinWorkerThreadFactory.java +++ b/platform/boot/src/com/intellij/concurrency/IdeaForkJoinWorkerThreadFactory.java @@ -27,7 +27,8 @@ public class IdeaForkJoinWorkerThreadFactory implements ForkJoinPool.ForkJoinWor public static void setupForkJoinCommonPool() { System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", String.valueOf(PARALLELISM)); System.setProperty("java.util.concurrent.ForkJoinPool.common.threadFactory", IdeaForkJoinWorkerThreadFactory.class.getName()); - if (ForkJoinPool.commonPool().getFactory().getClass() != IdeaForkJoinWorkerThreadFactory.class) { + + if (!ForkJoinPool.commonPool().getFactory().getClass().getName().equals(IdeaForkJoinWorkerThreadFactory.class.getName())) { throw new IllegalStateException("Could not set ForkJoinPool thread factory: got "+ForkJoinPool.commonPool().getFactory()); } } From 3d8b7726271170beb4dced8e3f1d0b67f422638e Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Wed, 9 Mar 2016 16:08:02 +0100 Subject: [PATCH 64/83] WEB-19117 Debugger is extremely slow to start w/ Node.js 5.0.0 --- .../debugger-ui/src/DebugProcessImpl.kt | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/platform/script-debugger/debugger-ui/src/DebugProcessImpl.kt b/platform/script-debugger/debugger-ui/src/DebugProcessImpl.kt index 5a5ed158d4cb..5ef66a6360e8 100644 --- a/platform/script-debugger/debugger-ui/src/DebugProcessImpl.kt +++ b/platform/script-debugger/debugger-ui/src/DebugProcessImpl.kt @@ -256,15 +256,8 @@ abstract class DebugProcessImpl>(session: XDebugSession, protected fun addChildVm(vm: Vm) { beforeInitBreakpoints(vm) - val breakpointManager = XDebuggerManager.getInstance(session.project).breakpointManager - @Suppress("UNCHECKED_CAST") - for (breakpointHandler in breakpointHandlers) { - if (breakpointHandler is LineBreakpointHandler) { - val breakpoints = runReadAction { breakpointManager.getBreakpoints(breakpointHandler.breakpointTypeClass) } - for (breakpoint in breakpoints) { - breakpointHandler.manager.setBreakpoint(vm, breakpoint) - } - } + processBreakpoints { handler, breakpoint -> + handler.manager.setBreakpoint(vm, breakpoint) } } @@ -276,6 +269,18 @@ abstract class DebugProcessImpl>(session: XDebugSession, } } } + + protected inline fun processBreakpoints(processor: (handler: LineBreakpointHandler, breakpoint: XLineBreakpoint<*>) -> Unit) { + val breakpointManager = XDebuggerManager.getInstance(session.project).breakpointManager + for (breakpointHandler in breakpointHandlers) { + if (breakpointHandler is LineBreakpointHandler) { + val breakpoints = runReadAction { breakpointManager.getBreakpoints(breakpointHandler.breakpointTypeClass) } + for (breakpoint in breakpoints) { + processor(breakpointHandler, breakpoint) + } + } + } + } } @Suppress("UNCHECKED_CAST") From 5948d464114c8ef917c939be85a3f1c0877ebb2a Mon Sep 17 00:00:00 2001 From: Konstantin Ulitin Date: Wed, 9 Mar 2016 18:01:05 +0300 Subject: [PATCH 65/83] WEB-20680 ES6 unicode code points escape sequences are not supported in strings --- .../intellij/lexer/StringLiteralLexer.java | 41 +++++++++++-------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/platform/core-api/src/com/intellij/lexer/StringLiteralLexer.java b/platform/core-api/src/com/intellij/lexer/StringLiteralLexer.java index 3b943b9e6018..3703409fcf23 100644 --- a/platform/core-api/src/com/intellij/lexer/StringLiteralLexer.java +++ b/platform/core-api/src/com/intellij/lexer/StringLiteralLexer.java @@ -32,13 +32,13 @@ public class StringLiteralLexer extends LexerBase { public static final char NO_QUOTE_CHAR = (char)-1; - private CharSequence myBuffer; - private int myStart; - private int myEnd; + protected CharSequence myBuffer; + protected int myStart; + protected int myEnd; private int myState; private int myLastState; - private int myBufferEnd; - private final char myQuoteChar; + protected int myBufferEnd; + protected final char myQuoteChar; private final IElementType myOriginalLiteralToken; private final boolean myCanEscapeEolOrFramingSpaces; private final String myAdditionalValidEscapes; @@ -114,10 +114,7 @@ public class StringLiteralLexer extends LexerBase { return StringEscapesTokenTypes.VALID_STRING_ESCAPE_TOKEN; } if (nextChar == 'u') { - for(int i = myStart + 2; i < myStart + 6; i++) { - if (i >= myEnd || !StringUtil.isHexDigit(myBuffer.charAt(i))) return StringEscapesTokenTypes.INVALID_UNICODE_ESCAPE_TOKEN; - } - return StringEscapesTokenTypes.VALID_STRING_ESCAPE_TOKEN; + return getUnicodeEscapeSequenceType(); } if (nextChar == 'x' && myAllowHex) { @@ -155,6 +152,14 @@ public class StringLiteralLexer extends LexerBase { return StringEscapesTokenTypes.INVALID_CHARACTER_ESCAPE_TOKEN; } + @NotNull + protected IElementType getUnicodeEscapeSequenceType() { + for (int i = myStart + 2; i < myStart + 6; i++) { + if (i >= myEnd || !StringUtil.isHexDigit(myBuffer.charAt(i))) return StringEscapesTokenTypes.INVALID_UNICODE_ESCAPE_TOKEN; + } + return StringEscapesTokenTypes.VALID_STRING_ESCAPE_TOKEN; + } + // all subsequent chars are escaped spaces private boolean isTrailingSpace(final int start) { for (int i=start;i Date: Wed, 2 Mar 2016 15:25:37 +0300 Subject: [PATCH 66/83] PY-18560 Fixed: wrong inferred type for result of __getitem__ by square brackets with slice Fix calculating type of slice expression: keep tuples and collections as is and resolve __getitem__ method for other class types --- .../psi/impl/PySliceExpressionImpl.java | 75 +++++++++++++++++-- .../com/jetbrains/python/PyTypeTest.java | 19 ++++- 2 files changed, 85 insertions(+), 9 deletions(-) diff --git a/python/src/com/jetbrains/python/psi/impl/PySliceExpressionImpl.java b/python/src/com/jetbrains/python/psi/impl/PySliceExpressionImpl.java index 5fde15256d40..8a28b9dc5dda 100644 --- a/python/src/com/jetbrains/python/psi/impl/PySliceExpressionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PySliceExpressionImpl.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. @@ -16,17 +16,21 @@ package com.jetbrains.python.psi.impl; import com.intellij.lang.ASTNode; +import com.intellij.psi.PsiElement; import com.intellij.psi.util.PsiTreeUtil; +import com.jetbrains.python.PyNames; import com.jetbrains.python.PythonDialectsTokenSetProvider; -import com.jetbrains.python.psi.PyExpression; -import com.jetbrains.python.psi.PySliceExpression; -import com.jetbrains.python.psi.PySliceItem; -import com.jetbrains.python.psi.types.PyTupleType; -import com.jetbrains.python.psi.types.PyType; -import com.jetbrains.python.psi.types.TypeEvalContext; +import com.jetbrains.python.psi.*; +import com.jetbrains.python.psi.resolve.PyResolveContext; +import com.jetbrains.python.psi.resolve.RatedResolveResult; +import com.jetbrains.python.psi.types.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + /** * @author yole */ @@ -39,11 +43,38 @@ public class PySliceExpressionImpl extends PyElementImpl implements PySliceExpre @Override public PyType getType(@NotNull TypeEvalContext context, @NotNull TypeEvalContext.Key key) { final PyType type = context.getType(getOperand()); + // TODO: Currently we don't evaluate the static range of the slice, so we have to return a generic tuple type without elements if (type instanceof PyTupleType) { return PyBuiltinCache.getInstance(this).getTupleType(); } - return type; + + if (type instanceof PyCollectionType) { + return type; + } + + if (type instanceof PyClassType) { + final List resolveResults = type.resolveMember( + PyNames.GETITEM, + null, + AccessDirection.READ, + PyResolveContext.noImplicits().withTypeEvalContext(context) + ); + + if (resolveResults != null) { + final List types = new ArrayList<>(); + + for (RatedResolveResult resolveResult : resolveResults) { + types.addAll( + getPossibleReturnTypes(resolveResult.getElement(), context) + ); + } + + return PyUnionType.union(types); + } + } + + return null; } @NotNull @@ -57,4 +88,32 @@ public class PySliceExpressionImpl extends PyElementImpl implements PySliceExpre public PySliceItem getSliceItem() { return PsiTreeUtil.getChildOfType(this, PySliceItem.class); } + + @NotNull + private static List getPossibleReturnTypes(@Nullable PsiElement element, @NotNull TypeEvalContext context) { + final List result = new ArrayList(); + + if (element instanceof PyTypedElement) { + final PyType elementType = context.getType((PyTypedElement)element); + + result.addAll(getPossibleReturnTypes(elementType, context)); + + if (elementType instanceof PyUnionType) { + for (PyType type : ((PyUnionType)elementType).getMembers()) { + result.addAll(getPossibleReturnTypes(type, context)); + } + } + } + + return result; + } + + @NotNull + private static List getPossibleReturnTypes(@Nullable PyType type, @NotNull TypeEvalContext context) { + if (type instanceof PyCallableType) { + return Collections.singletonList(((PyCallableType)type).getReturnType(context)); + } + + return Collections.emptyList(); + } } diff --git a/python/testSrc/com/jetbrains/python/PyTypeTest.java b/python/testSrc/com/jetbrains/python/PyTypeTest.java index 247e1da55543..faf364ff2f74 100644 --- a/python/testSrc/com/jetbrains/python/PyTypeTest.java +++ b/python/testSrc/com/jetbrains/python/PyTypeTest.java @@ -175,11 +175,28 @@ public class PyTypeTest extends PyTestCase { "l = [1, 2, 3]; expr = l[0]"); } - public void testSliceType() { + public void testListSliceType() { doTest("List[int]", "l = [1, 2, 3]; expr = l[0:1]"); } + public void testTupleSliceType() { + doTest("tuple", + "l = (1, 2, 3); expr = l[0:1]"); + } + + // PY-18560 + public void testCustomSliceType() { + doTest( + "int", + "class RectangleFactory(object):\n" + + " def __getitem__(self, item):\n" + + " return 1\n" + + "factory = RectangleFactory()\n" + + "expr = factory[:]" + ); + } + public void testExceptType() { doTest("ImportError", "try:\n" + From 495ae960d3fea5523f1a31cd4c5baa31862f4b42 Mon Sep 17 00:00:00 2001 From: Semyon Proshev Date: Wed, 9 Mar 2016 18:18:15 +0300 Subject: [PATCH 67/83] Remove code duplicates in PySliceExpressionImpl and PyClassTypeImpl by creating new util method in PyUtil. This method allows to calculate return type of type member. --- .../src/com/jetbrains/python/psi/PyUtil.java | 60 +++++++++++++++++++ .../psi/impl/PySliceExpressionImpl.java | 59 ++---------------- .../python/psi/types/PyClassTypeImpl.java | 45 +------------- 3 files changed, 66 insertions(+), 98 deletions(-) diff --git a/python/src/com/jetbrains/python/psi/PyUtil.java b/python/src/com/jetbrains/python/psi/PyUtil.java index 6cf072dc6a4a..8c1e318f0bf3 100644 --- a/python/src/com/jetbrains/python/psi/PyUtil.java +++ b/python/src/com/jetbrains/python/psi/PyUtil.java @@ -1718,6 +1718,66 @@ public class PyUtil { return ScratchFileService.isInScratchRoot(PsiUtilCore.getVirtualFile(element)); } + @Nullable + public static PyType getReturnTypeOfMember(@NotNull PyType type, + @NotNull String memberName, + @Nullable PyExpression location, + @NotNull TypeEvalContext context) { + final PyResolveContext resolveContext = PyResolveContext.noImplicits().withTypeEvalContext(context); + final List resolveResults = type.resolveMember(memberName, location, AccessDirection.READ, + resolveContext); + + if (resolveResults != null) { + final List types = new ArrayList<>(); + + for (RatedResolveResult resolveResult : resolveResults) { + final PyType returnType = getReturnType(resolveResult.getElement(), context); + + if (returnType != null) { + types.add(returnType); + } + } + + return PyUnionType.union(types); + } + + return null; + } + + @Nullable + private static PyType getReturnType(@Nullable PsiElement element, @NotNull TypeEvalContext context) { + if (element instanceof PyTypedElement) { + final PyType type = context.getType((PyTypedElement)element); + + return getReturnType(type, context); + } + + return null; + } + + @Nullable + private static PyType getReturnType(@Nullable PyType type, @NotNull TypeEvalContext context) { + if (type instanceof PyCallableType) { + return ((PyCallableType)type).getReturnType(context); + } + + if (type instanceof PyUnionType) { + final List types = new ArrayList<>(); + + for (PyType pyType : ((PyUnionType)type).getMembers()) { + final PyType returnType = getReturnType(pyType, context); + + if (returnType != null) { + types.add(returnType); + } + } + + return PyUnionType.union(types); + } + + return null; + } + /** * This helper class allows to collect various information about AST nodes composing {@link PyStringLiteralExpression}. */ diff --git a/python/src/com/jetbrains/python/psi/impl/PySliceExpressionImpl.java b/python/src/com/jetbrains/python/psi/impl/PySliceExpressionImpl.java index 8a28b9dc5dda..20a27d6ca6bb 100644 --- a/python/src/com/jetbrains/python/psi/impl/PySliceExpressionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PySliceExpressionImpl.java @@ -16,21 +16,17 @@ package com.jetbrains.python.psi.impl; import com.intellij.lang.ASTNode; -import com.intellij.psi.PsiElement; import com.intellij.psi.util.PsiTreeUtil; import com.jetbrains.python.PyNames; import com.jetbrains.python.PythonDialectsTokenSetProvider; -import com.jetbrains.python.psi.*; -import com.jetbrains.python.psi.resolve.PyResolveContext; -import com.jetbrains.python.psi.resolve.RatedResolveResult; +import com.jetbrains.python.psi.PyExpression; +import com.jetbrains.python.psi.PySliceExpression; +import com.jetbrains.python.psi.PySliceItem; +import com.jetbrains.python.psi.PyUtil; import com.jetbrains.python.psi.types.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - /** * @author yole */ @@ -54,24 +50,7 @@ public class PySliceExpressionImpl extends PyElementImpl implements PySliceExpre } if (type instanceof PyClassType) { - final List resolveResults = type.resolveMember( - PyNames.GETITEM, - null, - AccessDirection.READ, - PyResolveContext.noImplicits().withTypeEvalContext(context) - ); - - if (resolveResults != null) { - final List types = new ArrayList<>(); - - for (RatedResolveResult resolveResult : resolveResults) { - types.addAll( - getPossibleReturnTypes(resolveResult.getElement(), context) - ); - } - - return PyUnionType.union(types); - } + return PyUtil.getReturnTypeOfMember(type, PyNames.GETITEM, null, context); } return null; @@ -88,32 +67,4 @@ public class PySliceExpressionImpl extends PyElementImpl implements PySliceExpre public PySliceItem getSliceItem() { return PsiTreeUtil.getChildOfType(this, PySliceItem.class); } - - @NotNull - private static List getPossibleReturnTypes(@Nullable PsiElement element, @NotNull TypeEvalContext context) { - final List result = new ArrayList(); - - if (element instanceof PyTypedElement) { - final PyType elementType = context.getType((PyTypedElement)element); - - result.addAll(getPossibleReturnTypes(elementType, context)); - - if (elementType instanceof PyUnionType) { - for (PyType type : ((PyUnionType)elementType).getMembers()) { - result.addAll(getPossibleReturnTypes(type, context)); - } - } - } - - return result; - } - - @NotNull - private static List getPossibleReturnTypes(@Nullable PyType type, @NotNull TypeEvalContext context) { - if (type instanceof PyCallableType) { - return Collections.singletonList(((PyCallableType)type).getReturnType(context)); - } - - return Collections.emptyList(); - } } diff --git a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java index dcdd2767e6a6..4bba084e6963 100644 --- a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java +++ b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java @@ -380,54 +380,11 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { @Nullable private PyType getReturnType(@NotNull TypeEvalContext context, @Nullable PyCallSiteExpression callSite) { if (!isDefinition()) { - final PyResolveContext resolveContext = PyResolveContext.noImplicits().withTypeEvalContext(context); - final List resolveResults = resolveMember(PyNames.CALL, callSite, AccessDirection.READ, resolveContext); - - if (resolveResults != null) { - final ArrayList result = new ArrayList(); - - for (RatedResolveResult resolveResult : resolveResults) { - result.addAll( - getPossibleReturnTypes(resolveResult.getElement(), context) - ); - } - - return PyUnionType.union(result); - } + return PyUtil.getReturnTypeOfMember(this, PyNames.CALL, callSite, context); } else { return new PyClassTypeImpl(getPyClass(), false); } - - return null; - } - - @NotNull - private static List getPossibleReturnTypes(@Nullable PsiElement element, @NotNull TypeEvalContext context) { - final ArrayList result = new ArrayList(); - - if (element instanceof PyTypedElement) { - final PyType elementType = context.getType((PyTypedElement)element); - - result.addAll(getPossibleReturnTypes(elementType, context)); - - if (elementType instanceof PyUnionType) { - for (PyType type : ((PyUnionType)elementType).getMembers()) { - result.addAll(getPossibleReturnTypes(type, context)); - } - } - } - - return result; - } - - @NotNull - private static List getPossibleReturnTypes(@Nullable PyType type, @NotNull TypeEvalContext context) { - if (type instanceof PyCallableType) { - return Collections.singletonList(((PyCallableType)type).getReturnType(context)); - } - - return Collections.emptyList(); } @Nullable From 1b18252b5d6d7f85f01bfad7dee424d2c5cb085f Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Wed, 9 Mar 2016 17:30:26 +0300 Subject: [PATCH 68/83] IDEA-152575 diff: change title for clibroard contents make it clear, that changes to "clipboard content" are not propagated back to the clipboard --- .../platform-resources-en/src/messages/DiffBundle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/platform-resources-en/src/messages/DiffBundle.properties b/platform/platform-resources-en/src/messages/DiffBundle.properties index ac08cc3ddaff..e433aa8232d6 100644 --- a/platform/platform-resources-en/src/messages/DiffBundle.properties +++ b/platform/platform-resources-en/src/messages/DiffBundle.properties @@ -9,7 +9,7 @@ unknown.file.type.error=Unknown file type: {0} save.merge.result.command.name=Merge compare.file.vs.file.dialog.title={0} vs {1} diff.content.editor.content.title=Editor -diff.content.clipboard.content.title=Clipboard +diff.content.clipboard.content.title=From \u0441lipboard diff.clipboard.vs.editor.dialog.title=Clipboard vs Editor diff.clipboard.vs.file.dialog.title=Clipboard vs {0} diff.content.selection.from.editor.content.title=Selection from Editor From 646e78521d8ec484c6a395908e6010a8e5307ae0 Mon Sep 17 00:00:00 2001 From: Valentina Kiryushkina Date: Wed, 9 Mar 2016 18:48:20 +0300 Subject: [PATCH 69/83] Fix problem with study task description tool window blinking on task change * Create separate methods for tool window initialization and tool window update * On update only set new text in study tool window browser window --- .../edu/learning/StudyProjectComponent.java | 2 +- .../jetbrains/edu/learning/StudyUtils.java | 54 ++++++++++++++++++- .../edu/learning/ui/StudyToolWindow.java | 30 ++--------- .../learning/ui/StudyToolWindowFactory.java | 8 +++ 4 files changed, 65 insertions(+), 29 deletions(-) diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyProjectComponent.java b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyProjectComponent.java index 45f033e937fd..1dc14d36bb51 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyProjectComponent.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyProjectComponent.java @@ -102,7 +102,7 @@ public class StudyProjectComponent implements ProjectComponent { studyToolWindow.show(null); } if (progressToolWindow != null) { - StudyUtils.updateToolWindows(myProject); + StudyUtils.initToolWindows(myProject); progressToolWindow.show(null); } } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyUtils.java b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyUtils.java index a1ed979c5309..0f68f81654ef 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyUtils.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyUtils.java @@ -18,6 +18,7 @@ import com.intellij.openapi.editor.colors.EditorColors; import com.intellij.openapi.editor.impl.DocumentImpl; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.fileEditor.FileEditor; +import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx; import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.Sdk; @@ -26,26 +27,30 @@ import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.wm.ToolWindow; import com.intellij.openapi.wm.ToolWindowManager; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.ui.JBColor; import com.intellij.ui.awt.RelativePoint; +import com.intellij.ui.content.Content; import com.intellij.util.ui.UIUtil; import com.jetbrains.edu.EduAnswerPlaceholderDeleteHandler; import com.jetbrains.edu.EduAnswerPlaceholderPainter; import com.jetbrains.edu.EduNames; import com.jetbrains.edu.EduUtils; import com.jetbrains.edu.courseFormat.*; -import com.jetbrains.edu.learning.editor.StudyEditor; import com.jetbrains.edu.learning.checker.StudyExecutor; import com.jetbrains.edu.learning.checker.StudyTestRunner; +import com.jetbrains.edu.learning.editor.StudyEditor; import com.jetbrains.edu.learning.ui.StudyProgressToolWindowFactory; +import com.jetbrains.edu.learning.ui.StudyToolWindow; import com.jetbrains.edu.learning.ui.StudyToolWindowFactory; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import javax.swing.*; import java.awt.*; import java.io.*; import java.util.Collection; @@ -56,6 +61,7 @@ public class StudyUtils { } private static final Logger LOG = Logger.getInstance(StudyUtils.class.getName()); + private static final String EMPTY_TASK_TEXT = "Please, open any task to see task description"; public static void closeSilently(@Nullable final Closeable stream) { if (stream != null) { @@ -122,15 +128,42 @@ public class StudyUtils { } public static void updateToolWindows(@NotNull final Project project) { + final ToolWindowManager windowManager = ToolWindowManager.getInstance(project); + StudyToolWindowFactory factory = new StudyToolWindowFactory(); + factory.update(project); + + createProgressToolWindowContent(project, windowManager); + } + + public static void initToolWindows(@NotNull final Project project) { final ToolWindowManager windowManager = ToolWindowManager.getInstance(project); windowManager.getToolWindow(StudyToolWindowFactory.STUDY_TOOL_WINDOW).getContentManager().removeAllContents(false); StudyToolWindowFactory factory = new StudyToolWindowFactory(); factory.createToolWindowContent(project, windowManager.getToolWindow(StudyToolWindowFactory.STUDY_TOOL_WINDOW)); + createProgressToolWindowContent(project, windowManager); + } + + private static void createProgressToolWindowContent(@NotNull Project project, ToolWindowManager windowManager) { windowManager.getToolWindow(StudyProgressToolWindowFactory.ID).getContentManager().removeAllContents(false); StudyProgressToolWindowFactory windowFactory = new StudyProgressToolWindowFactory(); windowFactory.createToolWindowContent(project, windowManager.getToolWindow(StudyProgressToolWindowFactory.ID)); } + + @Nullable + public static StudyToolWindow getStudyToolWindow(@NotNull final Project project) { + ToolWindow toolWindow = ToolWindowManager.getInstance(project).getToolWindow(StudyToolWindowFactory.STUDY_TOOL_WINDOW); + if (toolWindow != null) { + Content[] contents = toolWindow.getContentManager().getContents(); + for (Content content: contents) { + JComponent component = content.getComponent(); + if (component != null && component instanceof StudyToolWindow) { + return (StudyToolWindow)component; + } + } + } + return null; + } public static void deleteFile(@NotNull final VirtualFile file) { try { @@ -427,4 +460,23 @@ public class StudyUtils { } return null; } + + public static String getTaskText(@NotNull final Project project) { + VirtualFile[] files = FileEditorManager.getInstance(project).getSelectedFiles(); + TaskFile taskFile = null; + for (VirtualFile file : files) { + taskFile = getTaskFile(project, file); + if (taskFile != null) { + break; + } + } + if (taskFile == null) { + return EMPTY_TASK_TEXT; + } + final Task task = taskFile.getTask(); + if (task != null) { + return getTaskTextFromTask(task, task.getTaskDir(project)); + } + return null; + } } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyToolWindow.java b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyToolWindow.java index c03a621624e7..e77592c7e355 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyToolWindow.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyToolWindow.java @@ -21,17 +21,13 @@ import com.intellij.openapi.actionSystem.ActionToolbar; import com.intellij.openapi.actionSystem.DataProvider; import com.intellij.openapi.actionSystem.DefaultActionGroup; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.fileEditor.FileEditorManagerListener; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.SimpleToolWindowPanel; -import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.JBCardLayout; import com.intellij.ui.OnePixelSplitter; import com.intellij.util.ui.JBUI; import com.jetbrains.edu.courseFormat.Course; -import com.jetbrains.edu.courseFormat.Task; -import com.jetbrains.edu.courseFormat.TaskFile; import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.StudyToolWindowConfigurator; import com.jetbrains.edu.learning.StudyUtils; @@ -43,7 +39,6 @@ import java.util.Map; public abstract class StudyToolWindow extends SimpleToolWindowPanel implements DataProvider, Disposable { private static final Logger LOG = Logger.getInstance(StudyToolWindow.class); - private static final String EMPTY_TASK_TEXT = "Please, open any task to see task description"; private static final String TASK_INFO_ID = "taskInfo"; private final JBCardLayout myCardLayout; private final JPanel myContentPanel; @@ -57,7 +52,7 @@ public abstract class StudyToolWindow extends SimpleToolWindowPanel implements D } public void init(Project project) { - String taskText = getTaskText(project); + String taskText = StudyUtils.getTaskText(project); if (taskText == null) return; JPanel toolbarPanel = createToolbarPanel(project); @@ -117,27 +112,8 @@ public abstract class StudyToolWindow extends SimpleToolWindowPanel implements D public JPanel getContentPanel() { return myContentPanel; } - - - private static String getTaskText(@NotNull final Project project) { - VirtualFile[] files = FileEditorManager.getInstance(project).getSelectedFiles(); - TaskFile taskFile = null; - for (VirtualFile file : files) { - taskFile = StudyUtils.getTaskFile(project, file); - if (taskFile != null) { - break; - } - } - if (taskFile == null) { - return EMPTY_TASK_TEXT; - } - final Task task = taskFile.getTask(); - if (task != null) { - return StudyUtils.getTaskTextFromTask(task, task.getTaskDir(project)); - } - return null; - } - + + public abstract JComponent createTaskInfoPanel(String taskText, Project project); private static JPanel createToolbarPanel(@NotNull final Project project) { diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyToolWindowFactory.java b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyToolWindowFactory.java index c0ededad3dc6..2394f32158cb 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyToolWindowFactory.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyToolWindowFactory.java @@ -10,6 +10,7 @@ import com.intellij.ui.content.ContentManager; import com.jetbrains.edu.courseFormat.Course; import com.jetbrains.edu.learning.StudyProjectComponent; import com.jetbrains.edu.learning.StudyTaskManager; +import com.jetbrains.edu.learning.StudyUtils; import icons.InteractiveLearningIcons; import org.jetbrains.annotations.NotNull; @@ -39,4 +40,11 @@ public class StudyToolWindowFactory implements ToolWindowFactory, DumbAware { } } + public void update(Project project) { + final StudyToolWindow studyToolWindow = StudyUtils.getStudyToolWindow(project); + if (studyToolWindow != null) { + String taskText = StudyUtils.getTaskText(project); + studyToolWindow.setTaskText(taskText); + } + } } From 8d78d16800b48499257b1a678e215c81bae86a3a Mon Sep 17 00:00:00 2001 From: Semyon Proshev Date: Thu, 25 Feb 2016 12:36:33 +0300 Subject: [PATCH 70/83] PY-18096 Fixed: False positive "Type doesn't have expected attributes" for namedtuple Introduce PyClassLikeType.getMemberNames(boolean, TypeEvalContext). This method returns all members including dynamically ones (e.g. fields of namedtuple) --- .../python/psi/impl/PyJavaClassType.java | 30 ++++++++-- .../python/psi/types/PyClassLikeType.java | 10 +++- .../com/jetbrains/python/PyCustomType.java | 23 +++++++- .../codeInsight/stdlib/PyNamedTupleType.java | 14 ++++- .../inspections/PyTypeCheckerInspection.java | 37 +++++++----- .../python/psi/types/PyClassTypeImpl.java | 45 ++++++++++++-- .../python/psi/types/PyTypeChecker.java | 58 +++---------------- .../NamedTupleBaseClass.py | 16 +++++ .../PyTypeCheckerInspectionTest.java | 5 ++ 9 files changed, 156 insertions(+), 82 deletions(-) create mode 100644 python/testData/inspections/PyTypeCheckerInspection/NamedTupleBaseClass.py diff --git a/python/pluginJava/com/jetbrains/python/psi/impl/PyJavaClassType.java b/python/pluginJava/com/jetbrains/python/psi/impl/PyJavaClassType.java index 651b55ad18c8..670764bdf46a 100644 --- a/python/pluginJava/com/jetbrains/python/psi/impl/PyJavaClassType.java +++ b/python/pluginJava/com/jetbrains/python/psi/impl/PyJavaClassType.java @@ -31,9 +31,7 @@ import com.jetbrains.python.psi.types.TypeEvalContext; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; +import java.util.*; /** * @author yole @@ -49,7 +47,7 @@ public class PyJavaClassType implements PyClassLikeType { @Nullable public List resolveMember(@NotNull final String name, - PyExpression location, + @Nullable PyExpression location, @NotNull AccessDirection direction, @NotNull PyResolveContext resolveContext) { return resolveMember(name, location, direction, resolveContext, true); @@ -156,6 +154,30 @@ public class PyJavaClassType implements PyClassLikeType { // TODO: Implement } + @NotNull + @Override + public Set getMemberNames(boolean inherited, @NotNull TypeEvalContext context) { + final Set result = new LinkedHashSet<>(); + + for (PsiMethod method : myClass.getAllMethods()) { + result.add(method.getName()); + } + + for (PsiField field : myClass.getAllFields()) { + result.add(field.getName()); + } + + if (inherited) { + for (PyClassLikeType type : getAncestorTypes(context)) { + if (type != null) { + result.addAll(type.getMemberNames(false, context)); + } + } + } + + return result; + } + @NotNull @Override public List getAncestorTypes(@NotNull final TypeEvalContext context) { diff --git a/python/psi-api/src/com/jetbrains/python/psi/types/PyClassLikeType.java b/python/psi-api/src/com/jetbrains/python/psi/types/PyClassLikeType.java index 4cf201e14652..f7d40633fd48 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/types/PyClassLikeType.java +++ b/python/psi-api/src/com/jetbrains/python/psi/types/PyClassLikeType.java @@ -26,6 +26,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.List; +import java.util.Set; /** * @author vlan @@ -42,8 +43,10 @@ public interface PyClassLikeType extends PyCallableType, PyWithAncestors { List getSuperClassTypes(@NotNull TypeEvalContext context); @Nullable - List resolveMember(@NotNull final String name, @Nullable PyExpression location, - @NotNull AccessDirection direction, @NotNull PyResolveContext resolveContext, + List resolveMember(@NotNull final String name, + @Nullable PyExpression location, + @NotNull AccessDirection direction, + @NotNull PyResolveContext resolveContext, boolean inherited); // TODO: Pull to PyType at next iteration @@ -58,6 +61,9 @@ public interface PyClassLikeType extends PyCallableType, PyWithAncestors { */ void visitMembers(@NotNull Processor processor, boolean inherited, @NotNull TypeEvalContext context); + @NotNull + Set getMemberNames(boolean inherited, @NotNull TypeEvalContext context); + boolean isValid(); @Nullable diff --git a/python/src/com/jetbrains/python/PyCustomType.java b/python/src/com/jetbrains/python/PyCustomType.java index d01170f9d40f..f8dd377fa589 100644 --- a/python/src/com/jetbrains/python/PyCustomType.java +++ b/python/src/com/jetbrains/python/PyCustomType.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. @@ -112,7 +112,10 @@ public class PyCustomType implements PyClassLikeType { // Delegate calls to classes, we mimic but filter if filter is set. for (final PyClassLikeType typeToMimic : myTypesToMimic) { - final List results = typeToMimic.toInstance().resolveMember(name, location, direction, resolveContext, inherited); + final List results = typeToMimic.toInstance().resolveMember( + name, location, direction, resolveContext, inherited + ); + if (results != null) { globalResult.addAll(Collections2.filter(results, new ResolveFilter())); } @@ -253,7 +256,9 @@ public class PyCustomType implements PyClassLikeType { } @Override - public final void visitMembers(@NotNull final Processor processor, final boolean inherited, @NotNull final TypeEvalContext context) { + public final void visitMembers(@NotNull final Processor processor, + final boolean inherited, + @NotNull final TypeEvalContext context) { for (final PyClassLikeType type : myTypesToMimic) { // Only visit methods that are allowed by filter (if any) type.visitMembers(new Processor() { @@ -271,6 +276,18 @@ public class PyCustomType implements PyClassLikeType { } } + @NotNull + @Override + public Set getMemberNames(boolean inherited, @NotNull TypeEvalContext context) { + final Set result = new LinkedHashSet<>(); + + for (PyClassLikeType type : myTypesToMimic) { + result.addAll(type.getMemberNames(inherited, context)); + } + + return result; + } + /** * Predicate that filters completion using {@link #myFilter} */ diff --git a/python/src/com/jetbrains/python/codeInsight/stdlib/PyNamedTupleType.java b/python/src/com/jetbrains/python/codeInsight/stdlib/PyNamedTupleType.java index 4de9ed6b41da..feaaceea053a 100644 --- a/python/src/com/jetbrains/python/codeInsight/stdlib/PyNamedTupleType.java +++ b/python/src/com/jetbrains/python/codeInsight/stdlib/PyNamedTupleType.java @@ -35,6 +35,7 @@ import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Set; /** * @author yole @@ -69,7 +70,7 @@ public class PyNamedTupleType extends PyClassTypeImpl implements PyCallableType return classMembers; } if (myFields.contains(name)) { - return Collections.singletonList(new RatedResolveResult(1000, new PyElementImpl(myDeclaration.getNode()))); + return Collections.singletonList(new RatedResolveResult(RatedResolveResult.RATE_HIGH, new PyElementImpl(myDeclaration.getNode()))); } return null; } @@ -98,7 +99,7 @@ public class PyNamedTupleType extends PyClassTypeImpl implements PyCallableType @Override public PyType getCallType(@NotNull TypeEvalContext context, @NotNull PyCallSiteExpression callSite) { if (myDefinitionLevel > 0) { - return new PyNamedTupleType(myClass, myDeclaration, myName, myFields, myDefinitionLevel-1); + return new PyNamedTupleType(myClass, myDeclaration, myName, myFields, myDefinitionLevel - 1); } return null; } @@ -113,6 +114,15 @@ public class PyNamedTupleType extends PyClassTypeImpl implements PyCallableType return "PyNamedTupleType: " + myName; } + @NotNull + @Override + public Set getMemberNames(boolean inherited, @NotNull TypeEvalContext context) { + final Set result = super.getMemberNames(inherited, context); + result.addAll(myFields); + + return result; + } + @Nullable public static PyType fromCall(@NotNull PyCallExpression call, @NotNull TypeEvalContext context, int level) { final String name = PyPsiUtils.strValue(call.getArgument(0, PyExpression.class)); diff --git a/python/src/com/jetbrains/python/inspections/PyTypeCheckerInspection.java b/python/src/com/jetbrains/python/inspections/PyTypeCheckerInspection.java index 4438beb72e93..a4858898a3f2 100644 --- a/python/src/com/jetbrains/python/inspections/PyTypeCheckerInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyTypeCheckerInspection.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. @@ -46,8 +46,9 @@ public class PyTypeCheckerInspection extends PyInspection { @NotNull @Override public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly, @NotNull LocalInspectionToolSession session) { - if (LOG.isDebugEnabled()) + if (LOG.isDebugEnabled()) { session.putUserData(TIME_KEY, System.nanoTime()); + } return new Visitor(holder, session); } @@ -88,18 +89,22 @@ public class PyTypeCheckerInspection extends PyInspection { private void checkCallSite(@Nullable PyCallSiteExpression callSite) { final List resultsSet = PyTypeChecker.analyzeCallSite(callSite, myTypeEvalContext); - final List>> problemsSet = new ArrayList>>(); + final List>> problemsSet = + new ArrayList>>(); for (PyTypeChecker.AnalyzeCallResults results : resultsSet) { problemsSet.add(checkMapping(results.getReceiver(), results.getArguments())); } if (!problemsSet.isEmpty()) { - Map> minProblems = Collections.min(problemsSet, new Comparator>>() { - @Override - public int compare(Map> o1, - Map> o2) { - return o1.size() - o2.size(); + Map> minProblems = Collections.min( + problemsSet, + new Comparator>>() { + @Override + public int compare(Map> o1, + Map> o2) { + return o1.size() - o2.size(); + } } - }); + ); for (Map.Entry> entry : minProblems.entrySet()) { registerProblem(entry.getKey(), entry.getValue().getFirst(), entry.getValue().getSecond()); } @@ -109,7 +114,8 @@ public class PyTypeCheckerInspection extends PyInspection { @NotNull private Map> checkMapping(@Nullable PyExpression receiver, @NotNull Map mapping) { - final Map> problems = new HashMap>(); + final Map> problems = + new HashMap>(); final Map substitutions = new LinkedHashMap(); boolean genericsCollected = false; for (Map.Entry entry : mapping.entrySet()) { @@ -130,7 +136,6 @@ public class PyTypeCheckerInspection extends PyInspection { final Pair problem = checkTypes(paramType, argType, myTypeEvalContext, substitutions); if (problem != null) { problems.put(arg, problem); - } } return problems; @@ -151,13 +156,13 @@ public class PyTypeCheckerInspection extends PyInspection { final PyType substitute = PyTypeChecker.substitute(expected, substitutions, context); if (substitute != null) { quotedExpectedName = String.format("'%s' (matched generic type '%s')", - PythonDocumentationProvider.getTypeName(substitute, context), - expectedName); + PythonDocumentationProvider.getTypeName(substitute, context), + expectedName); highlightType = ProblemHighlightType.WEAK_WARNING; } } final String actualName = PythonDocumentationProvider.getTypeName(actual, context); - String msg= String.format("Expected type %s, got '%s' instead", quotedExpectedName, actualName); + String msg = String.format("Expected type %s, got '%s' instead", quotedExpectedName, actualName); if (expected instanceof PyStructuralType) { final Set expectedAttributes = ((PyStructuralType)expected).getAttributeNames(); final Set actualAttributes = getAttributes(actual, context); @@ -190,8 +195,8 @@ public class PyTypeCheckerInspection extends PyInspection { if (type instanceof PyStructuralType) { return ((PyStructuralType)type).getAttributeNames(); } - else if (type instanceof PyClassType) { - return PyTypeChecker.getClassTypeAttributes((PyClassType)type, true, context); + else if (type instanceof PyClassLikeType) { + return ((PyClassLikeType)type).getMemberNames(true, context); } return null; } diff --git a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java index 4bba084e6963..cf9546276d06 100644 --- a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java +++ b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.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. @@ -368,17 +368,17 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { @Nullable @Override public PyType getReturnType(@NotNull TypeEvalContext context) { - return getReturnType(context, null); + return getPossibleCallType(context, null); } @Nullable @Override public PyType getCallType(@NotNull TypeEvalContext context, @NotNull PyCallSiteExpression callSite) { - return getReturnType(context, callSite); + return getPossibleCallType(context, callSite); } @Nullable - private PyType getReturnType(@NotNull TypeEvalContext context, @Nullable PyCallSiteExpression callSite) { + private PyType getPossibleCallType(@NotNull TypeEvalContext context, @Nullable PyCallSiteExpression callSite) { if (!isDefinition()) { return PyUtil.getReturnTypeOfMember(this, PyNames.CALL, callSite, context); } @@ -523,7 +523,6 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { public void visitMembers(@NotNull final Processor processor, final boolean inherited, @NotNull final TypeEvalContext context) { - myClass.visitMethods(new MyProcessorWrapper(processor), false, context); myClass.visitClassAttributes(new MyProcessorWrapper(processor), false, context); @@ -541,6 +540,42 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { } } + @NotNull + @Override + public Set getMemberNames(boolean inherited, @NotNull TypeEvalContext context) { + final Set result = new LinkedHashSet<>(); + + for (PyFunction function : myClass.getMethods()) { + result.add(function.getName()); + } + + for (PyTargetExpression expression : myClass.getClassAttributes()) { + result.add(expression.getName()); + } + + for (PyTargetExpression expression : myClass.getInstanceAttributes()) { + result.add(expression.getName()); + } + + for (PyClassMembersProvider provider : Extensions.getExtensions(PyClassMembersProvider.EP_NAME)) { + for (PyCustomMember member : provider.getMembers(this, null, context)) { + result.add(member.getName()); + } + } + + if (inherited) { + for (PyClassLikeType type : getAncestorTypes(context)) { + if (type != null) { + final PyClassLikeType ancestorType = isDefinition() ? type : type.toInstance(); + + result.addAll(ancestorType.getMemberNames(false, context)); + } + } + } + + return result; + } + private void addOwnClassMembers(PsiElement expressionHook, Set namesAlready, boolean suppressParentheses, diff --git a/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java b/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java index ae860229395c..807ec108cee0 100644 --- a/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java +++ b/python/src/com/jetbrains/python/psi/types/PyTypeChecker.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. @@ -15,11 +15,10 @@ */ package com.jetbrains.python.psi.types; -import com.intellij.openapi.extensions.Extensions; -import com.intellij.psi.*; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiNamedElement; import com.intellij.util.ArrayUtil; import com.jetbrains.python.PyNames; -import com.jetbrains.python.codeInsight.PyCustomMember; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.impl.PyBuiltinCache; import com.jetbrains.python.psi.impl.PyCallExpressionHelper; @@ -46,8 +45,8 @@ public class PyTypeChecker { * For example int matches object, while str doesn't match int. * Work for builtin types, classes, tuples etc. * - * @param expected expected type - * @param actual type to be matched against expected + * @param expected expected type + * @param actual type to be matched against expected * @param context * @param substitutions * @return @@ -179,11 +178,11 @@ public class PyTypeChecker { if (overridesGetAttr(actualClassType.getPyClass(), context)) { return true; } - final Set actualAttributes = getClassTypeAttributes(actualClassType, true, context); + final Set actualAttributes = actualClassType.getMemberNames(true, context); return actualAttributes.containsAll(((PyStructuralType)expected).getAttributeNames()); } if (actual instanceof PyStructuralType && expected instanceof PyClassType) { - final Set expectedAttributes = getClassTypeAttributes((PyClassType)expected, true, context); + final Set expectedAttributes = ((PyClassType)expected).getMemberNames(true, context); return expectedAttributes.containsAll(((PyStructuralType)actual).getAttributeNames()); } if (actual instanceof PyCallableType && expected instanceof PyCallableType) { @@ -212,47 +211,6 @@ public class PyTypeChecker { return matchNumericTypes(expected, actual); } - @NotNull - public static Set getClassTypeAttributes(@NotNull PyClassType type, boolean inherited, @NotNull TypeEvalContext context) { - final Set attributes = getClassAttributes(type.getPyClass(), inherited, type.isDefinition(), context); - for (PyClassMembersProvider provider : Extensions.getExtensions(PyClassMembersProvider.EP_NAME)) { - final Collection members = provider.getMembers(type, null, context); - for (PyCustomMember member : members) { - attributes.add(member.getName()); - } - } - return attributes; - } - - @NotNull - private static Set getClassAttributes(@NotNull PyClass cls, - boolean inherited, - boolean isDefinition, - @NotNull TypeEvalContext context) { - final Set attributes = new HashSet(); - for (PyFunction function : cls.getMethods()) { - attributes.add(function.getName()); - } - for (PyTargetExpression instanceAttribute : cls.getInstanceAttributes()) { - attributes.add(instanceAttribute.getName()); - } - for (PyTargetExpression classAttribute : cls.getClassAttributes()) { - attributes.add(classAttribute.getName()); - } - if (inherited) { - for (PyClass ancestor : cls.getAncestorClasses(null)) { - final PyType ancestorType = context.getType(ancestor); - if (ancestorType instanceof PyClassLikeType) { - final PyClassLikeType classType = isDefinition ? (PyClassLikeType)ancestorType : ((PyClassLikeType)ancestorType).toInstance(); - if (classType instanceof PyClassType) { - attributes.addAll(getClassTypeAttributes((PyClassType)classType, false, context)); - } - } - } - } - return attributes; - } - private static boolean matchNumericTypes(PyType expected, PyType actual) { final String superName = expected.getName(); final String subName = actual.getName(); @@ -527,7 +485,7 @@ public class PyTypeChecker { return isUnionCallable((PyUnionType)type); } if (type instanceof PyCallableType) { - return ((PyCallableType) type).isCallable(); + return ((PyCallableType)type).isCallable(); } if (type instanceof PyStructuralType && ((PyStructuralType)type).isInferredFromUsages()) { return true; diff --git a/python/testData/inspections/PyTypeCheckerInspection/NamedTupleBaseClass.py b/python/testData/inspections/PyTypeCheckerInspection/NamedTupleBaseClass.py new file mode 100644 index 000000000000..84410a41c8ae --- /dev/null +++ b/python/testData/inspections/PyTypeCheckerInspection/NamedTupleBaseClass.py @@ -0,0 +1,16 @@ +from collections import namedtuple + + +class C(namedtuple('C', ['foo', 'bar'])): + pass + + +def f(x): + return x.foo, x.bar + +def g(): + x = C(foo=0, bar=1) + return f(x) + + +print(g()) \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/inspections/PyTypeCheckerInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyTypeCheckerInspectionTest.java index c8d2a222a9ab..f1a4c1b6a338 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyTypeCheckerInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyTypeCheckerInspectionTest.java @@ -151,6 +151,11 @@ public class PyTypeCheckerInspectionTest extends PyTestCase { doTest(); } + // PY-18096 + public void testNamedTupleBaseClass() { + doTest(); + } + // PY-6803 public void testPropertyAndFactoryFunction() { doTest(); From ae1b7f7d17ed77706bd8a428c6ae0f8aa77509eb Mon Sep 17 00:00:00 2001 From: Semyon Proshev Date: Fri, 26 Feb 2016 17:22:18 +0300 Subject: [PATCH 71/83] Implement todos in PyClassTypeImpl and PyJavaClassType which are connected with members and ancestors --- .../python/psi/impl/PyJavaClassType.java | 40 +++++++++++++++++-- .../python/psi/types/PyClassTypeImpl.java | 4 +- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/python/pluginJava/com/jetbrains/python/psi/impl/PyJavaClassType.java b/python/pluginJava/com/jetbrains/python/psi/impl/PyJavaClassType.java index 670764bdf46a..ef2526744022 100644 --- a/python/pluginJava/com/jetbrains/python/psi/impl/PyJavaClassType.java +++ b/python/pluginJava/com/jetbrains/python/psi/impl/PyJavaClassType.java @@ -151,7 +151,23 @@ public class PyJavaClassType implements PyClassLikeType { @Override public void visitMembers(@NotNull final Processor processor, final boolean inherited, @NotNull TypeEvalContext context) { - // TODO: Implement + for (PsiMethod method : myClass.getAllMethods()) { + processor.process(method); + } + + for (PsiField field : myClass.getAllFields()) { + processor.process(field); + } + + if (!inherited) { + return; + } + + for (PyClassLikeType type : getAncestorTypes(context)) { + if (type != null) { + type.visitMembers(processor, false, context); + } + } } @NotNull @@ -181,8 +197,26 @@ public class PyJavaClassType implements PyClassLikeType { @NotNull @Override public List getAncestorTypes(@NotNull final TypeEvalContext context) { - // TODO: Implement - return Collections.emptyList(); + final List result = new ArrayList<>(); + + final Deque deque = new LinkedList<>(); + final Set visited = new HashSet<>(); + + deque.addAll(Arrays.asList(myClass.getSupers())); + + while (!deque.isEmpty()) { + final PsiClass current = deque.pollFirst(); + + if (current == null || !visited.add(current)) { + continue; + } + + result.add(new PyJavaClassType(current, myDefinition)); + + deque.addAll(Arrays.asList(current.getSupers())); + } + + return result; } @Override diff --git a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java index cf9546276d06..0ebe4ffea75f 100644 --- a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java +++ b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java @@ -526,7 +526,9 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { myClass.visitMethods(new MyProcessorWrapper(processor), false, context); myClass.visitClassAttributes(new MyProcessorWrapper(processor), false, context); - // TODO: accept instance attributes as well + for (PyTargetExpression expression : myClass.getInstanceAttributes()) { + processor.process(expression); + } if (!inherited) { return; From 3c1c2ff832aa0da48763566465971ef99b4f7612 Mon Sep 17 00:00:00 2001 From: Semyon Proshev Date: Wed, 9 Mar 2016 19:04:14 +0300 Subject: [PATCH 72/83] Drop PyClassTypeImpl#getPossibleInstanceMembers and its usages as useless and not modified since 2008 --- .../PyUnresolvedReferencesInspection.java | 8 +------- .../jetbrains/python/psi/types/PyClassTypeImpl.java | 13 ------------- 2 files changed, 1 insertion(+), 20 deletions(-) diff --git a/python/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java b/python/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java index 326a27f643c3..e5cecb856ed0 100644 --- a/python/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java +++ b/python/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java @@ -500,13 +500,7 @@ public class PyUnresolvedReferencesInspection extends PyInspection { if (PyNames.COMPARISON_OPERATORS.contains(refName)) { return; } - if (expr.isQualified()) { - final PyClassTypeImpl object_type = (PyClassTypeImpl)PyBuiltinCache.getInstance(node).getObjectType(); - if ((object_type != null) && object_type.getPossibleInstanceMembers().contains(refName)) { - return; - } - } - else { + if (!expr.isQualified()) { if (PyUnreachableCodeInspection.hasAnyInterruptedControlFlowPaths(expr)) { return; } diff --git a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java index 0ebe4ffea75f..3bda9b954c31 100644 --- a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java +++ b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java @@ -684,19 +684,6 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { } } - @NotNull - public Set getPossibleInstanceMembers() { - Set ret = new HashSet(); - /* - if (myClass != null) { - PyClassType otype = PyBuiltinCache.getInstance(myClass.getProject()).getObjectType(); - ret.addAll(otype.getPossibleInstanceMembers()); - } - */ - // TODO: add our own ideas here, e.g. from methods other than constructor - return Collections.unmodifiableSet(ret); - } - @Override public boolean equals(Object o) { if (this == o) return true; From a6237d42567d5da0f1c6eaa6c1238a827ad718ef Mon Sep 17 00:00:00 2001 From: "Egor.Ushakov" Date: Wed, 9 Mar 2016 19:06:16 +0300 Subject: [PATCH 73/83] debugger: support return and try-catch in evaluation --- .../evaluation/expression/CatchEvaluator.java | 54 ++++++++++++ .../expression/CodeFragmentEvaluator.java | 6 +- .../expression/EvaluatorBuilderImpl.java | 85 +++++++++++++------ .../expression/ExpressionEvaluatorImpl.java | 5 +- .../expression/ReturnEvaluator.java | 57 +++++++++++++ .../evaluation/expression/ThrowEvaluator.java | 47 ++++++++++ .../evaluation/expression/TryEvaluator.java | 73 ++++++++++++++++ 7 files changed, 298 insertions(+), 29 deletions(-) create mode 100644 java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/CatchEvaluator.java create mode 100644 java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/ReturnEvaluator.java create mode 100644 java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/ThrowEvaluator.java create mode 100644 java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/TryEvaluator.java diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/CatchEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/CatchEvaluator.java new file mode 100644 index 000000000000..0ad17237fec4 --- /dev/null +++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/CatchEvaluator.java @@ -0,0 +1,54 @@ +/* + * 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.debugger.engine.evaluation.expression; + +import com.intellij.debugger.engine.evaluation.EvaluateException; +import com.intellij.debugger.engine.evaluation.EvaluationContextImpl; +import com.sun.jdi.ObjectReference; + +/** + * @author egor + */ +public class CatchEvaluator implements Evaluator { + private final String myExceptionType; + private final String myParamName; + private final CodeFragmentEvaluator myEvaluator; + + public CatchEvaluator(String exceptionType, String paramName, CodeFragmentEvaluator evaluator) { + myExceptionType = exceptionType; + myParamName = paramName; + myEvaluator = evaluator; + } + + public Object evaluate(ObjectReference exception, EvaluationContextImpl context) throws EvaluateException { + myEvaluator.setValue(myParamName, exception); + return myEvaluator.evaluate(context); + } + + @Override + public Object evaluate(EvaluationContextImpl context) throws EvaluateException { + throw new IllegalStateException("Use evaluate(ObjectReference exception, EvaluationContextImpl context)"); + } + + public String getExceptionType() { + return myExceptionType; + } + + @Override + public Modifier getModifier() { + return null; + } +} diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/CodeFragmentEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/CodeFragmentEvaluator.java index 3575d76fab4f..f3fae7db1796 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/CodeFragmentEvaluator.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/CodeFragmentEvaluator.java @@ -18,6 +18,7 @@ package com.intellij.debugger.engine.evaluation.expression; import com.intellij.debugger.DebuggerBundle; import com.intellij.debugger.engine.evaluation.EvaluateException; import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil; +import com.intellij.debugger.engine.evaluation.EvaluateRuntimeException; import com.intellij.debugger.jdi.VirtualMachineProxyImpl; import com.intellij.openapi.diagnostic.Logger; import com.intellij.util.containers.HashMap; @@ -103,10 +104,11 @@ public class CodeFragmentEvaluator extends BlockStatementEvaluator{ } } - public void setInitialValue(String localName, Object value) throws EvaluateException { + public void setInitialValue(String localName, Object value) { LOG.assertTrue(!(value instanceof Value), "use setValue for jdi values"); if(hasValue(localName)) { - throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.variable.already.declared", localName)); + throw new EvaluateRuntimeException( + EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.variable.already.declared", localName))); } mySyntheticLocals.put(localName, value); } diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/EvaluatorBuilderImpl.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/EvaluatorBuilderImpl.java index 291407a2989d..9081d7060375 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/EvaluatorBuilderImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/EvaluatorBuilderImpl.java @@ -190,9 +190,42 @@ public class EvaluatorBuilderImpl implements EvaluatorBuilder { @Override public void visitTryStatement(PsiTryStatement statement) { - throw new EvaluateRuntimeException(new UnsupportedExpressionException(statement.getText())); + Evaluator bodyEvaluator = accept(statement.getTryBlock()); + if (bodyEvaluator != null) { + PsiCatchSection[] catchSections = statement.getCatchSections(); + CatchEvaluator[] evaluators = new CatchEvaluator[catchSections.length]; + for (int i = 0; i < catchSections.length; i++) { + CodeFragmentEvaluator oldFragmentEvaluator = setNewCodeFragmentEvaluator(); + try { + PsiCatchSection section = catchSections[i]; + PsiParameter parameter = section.getParameter(); + if (parameter != null) { + myCurrentFragmentEvaluator.setInitialValue(parameter.getName(), null); + myCurrentFragmentEvaluator.setStatements(visitStatements(section.getCatchBlock().getStatements())); + evaluators[i] = new CatchEvaluator(parameter.getType().getCanonicalText(), parameter.getName(), myCurrentFragmentEvaluator); + } + } finally { + myCurrentFragmentEvaluator = oldFragmentEvaluator; + } + } + myResult = new TryEvaluator(bodyEvaluator, evaluators, accept(statement.getFinallyBlock())); + } } + @Override + public void visitThrowStatement(PsiThrowStatement statement) { + Evaluator accept = accept(statement.getException()); + if (accept != null) { + myResult = new ThrowEvaluator(accept); + } + } + + @Override + public void visitReturnStatement(PsiReturnStatement statement) { + myResult = new ReturnEvaluator(accept(statement.getReturnValue())); + } + + @Override public void visitStatement(PsiStatement statement) { throwEvaluateException(DebuggerBundle.message("evaluation.error.statement.not.supported", statement.getText())); @@ -204,25 +237,33 @@ public class EvaluatorBuilderImpl implements EvaluatorBuilder { return old; } + private Evaluator[] visitStatements(PsiStatement[] statements) { + Evaluator[] evaluators = new Evaluator[statements.length]; + for (int i = 0; i < statements.length; i++) { + PsiStatement psiStatement = statements[i]; + psiStatement.accept(this); + evaluators[i] = new DisableGC(myResult); + myResult = null; + } + return evaluators; + } + @Override - public void visitBlockStatement(PsiBlockStatement statement) { + public void visitCodeBlock(PsiCodeBlock block) { CodeFragmentEvaluator oldFragmentEvaluator = setNewCodeFragmentEvaluator(); try { - PsiStatement[] statements = statement.getCodeBlock().getStatements(); - Evaluator[] evaluators = new Evaluator[statements.length]; - for (int i = 0; i < statements.length; i++) { - PsiStatement psiStatement = statements[i]; - psiStatement.accept(this); - evaluators[i] = new DisableGC(myResult); - myResult = null; - } - myResult = new BlockStatementEvaluator(evaluators); + myResult = new BlockStatementEvaluator(visitStatements(block.getStatements())); } finally { myCurrentFragmentEvaluator = oldFragmentEvaluator; } } + @Override + public void visitBlockStatement(PsiBlockStatement statement) { + visitCodeBlock(statement.getCodeBlock()); + } + @Override public void visitLabeledStatement(PsiLabeledStatement labeledStatement) { PsiStatement statement = labeledStatement.getStatement(); @@ -273,19 +314,14 @@ public class EvaluatorBuilderImpl implements EvaluatorBuilder { @Override public void visitForeachStatement(PsiForeachStatement statement) { - try { - String iterationParameterName = statement.getIterationParameter().getName(); - myCurrentFragmentEvaluator.setInitialValue(iterationParameterName, null); - SyntheticVariableEvaluator iterationParameterEvaluator = new SyntheticVariableEvaluator(myCurrentFragmentEvaluator, iterationParameterName); + String iterationParameterName = statement.getIterationParameter().getName(); + myCurrentFragmentEvaluator.setInitialValue(iterationParameterName, null); + SyntheticVariableEvaluator iterationParameterEvaluator = new SyntheticVariableEvaluator(myCurrentFragmentEvaluator, iterationParameterName); - Evaluator iteratedValueEvaluator = accept(statement.getIteratedValue()); - Evaluator bodyEvaluator = accept(statement.getBody()); - if (bodyEvaluator != null) { - myResult = new ForeachStatementEvaluator(iterationParameterEvaluator, iteratedValueEvaluator, bodyEvaluator, getLabel(statement)); - } - } - catch (EvaluateException e) { - throw new EvaluateRuntimeException(e); + Evaluator iteratedValueEvaluator = accept(statement.getIteratedValue()); + Evaluator bodyEvaluator = accept(statement.getBody()); + if (bodyEvaluator != null) { + myResult = new ForeachStatementEvaluator(iterationParameterEvaluator, iteratedValueEvaluator, bodyEvaluator, getLabel(statement)); } } @@ -538,9 +574,6 @@ public class EvaluatorBuilderImpl implements EvaluatorBuilder { catch (IncorrectOperationException e) { LOG.error(e); } - catch (EvaluateException e) { - throw new EvaluateRuntimeException(e); - } PsiExpression initializer = localVariable.getInitializer(); if (initializer != null) { diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/ExpressionEvaluatorImpl.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/ExpressionEvaluatorImpl.java index 9c8ab0671541..b0e19e6ac527 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/ExpressionEvaluatorImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/ExpressionEvaluatorImpl.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. @@ -72,6 +72,9 @@ public class ExpressionEvaluatorImpl implements ExpressionEvaluator { myValue = (Value)value; return myValue; } + catch (ReturnEvaluator.ReturnException r) { + return (Value)r.getReturnValue(); + } catch (Throwable/*IncompatibleThreadStateException*/ e) { if (LOG.isDebugEnabled()) { LOG.debug(e); diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/ReturnEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/ReturnEvaluator.java new file mode 100644 index 000000000000..a1b4057d04df --- /dev/null +++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/ReturnEvaluator.java @@ -0,0 +1,57 @@ +/* + * 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.debugger.engine.evaluation.expression; + +import com.intellij.debugger.engine.evaluation.EvaluateException; +import com.intellij.debugger.engine.evaluation.EvaluationContextImpl; +import org.jetbrains.annotations.Nullable; + +/** + * @author egor + */ +public class ReturnEvaluator implements Evaluator { + @Nullable private final Evaluator myReturnValueEvaluator; + + public ReturnEvaluator(@Nullable Evaluator returnValueEvaluator) { + myReturnValueEvaluator = returnValueEvaluator; + } + + @Override + public Object evaluate(EvaluationContextImpl context) throws EvaluateException { + Object returnValue = myReturnValueEvaluator == null ? + context.getDebugProcess().getVirtualMachineProxy().mirrorOfVoid() : + myReturnValueEvaluator.evaluate(context); + throw new ReturnException(returnValue); + } + + @Override + public Modifier getModifier() { + return null; + } + + public static class ReturnException extends EvaluateException { + private final Object myReturnValue; + + public ReturnException(Object returnValue) { + super("Return"); + myReturnValue = returnValue; + } + + public Object getReturnValue() { + return myReturnValue; + } + } +} diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/ThrowEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/ThrowEvaluator.java new file mode 100644 index 000000000000..b14bb4b371d9 --- /dev/null +++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/ThrowEvaluator.java @@ -0,0 +1,47 @@ +/* + * 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.debugger.engine.evaluation.expression; + +import com.intellij.debugger.DebuggerBundle; +import com.intellij.debugger.engine.evaluation.EvaluateException; +import com.intellij.debugger.engine.evaluation.EvaluationContextImpl; +import com.sun.jdi.ObjectReference; +import org.jetbrains.annotations.NotNull; + +/** + * @author egor + */ +public class ThrowEvaluator implements Evaluator { + @NotNull private final Evaluator myExceptionEvaluator; + + public ThrowEvaluator(@NotNull Evaluator exceptionEvaluator) { + myExceptionEvaluator = exceptionEvaluator; + } + + @Override + public Object evaluate(EvaluationContextImpl context) throws EvaluateException { + ObjectReference exception = (ObjectReference)myExceptionEvaluator.evaluate(context); + EvaluateException ex = new EvaluateException( + DebuggerBundle.message("evaluation.error.method.exception", exception.referenceType().name())); + ex.setTargetException(exception); + throw ex; + } + + @Override + public Modifier getModifier() { + return null; + } +} diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/TryEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/TryEvaluator.java new file mode 100644 index 000000000000..14448e58dd10 --- /dev/null +++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/TryEvaluator.java @@ -0,0 +1,73 @@ +/* + * 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.debugger.engine.evaluation.expression; + +import com.intellij.debugger.engine.DebuggerUtils; +import com.intellij.debugger.engine.evaluation.EvaluateException; +import com.intellij.debugger.engine.evaluation.EvaluationContextImpl; +import com.sun.jdi.ObjectReference; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author egor + */ +public class TryEvaluator implements Evaluator { + @NotNull private final Evaluator myBodyEvaluator; + private final CatchEvaluator[] myCatchBlockEvaluators; + @Nullable private final Evaluator myFinallyEvaluator; + + public TryEvaluator(@NotNull Evaluator bodyEvaluator, + CatchEvaluator[] catchBlockEvaluators, + @Nullable Evaluator finallyEvaluator) { + myBodyEvaluator = bodyEvaluator; + myCatchBlockEvaluators = catchBlockEvaluators; + myFinallyEvaluator = finallyEvaluator; + } + + @Override + public Object evaluate(EvaluationContextImpl context) throws EvaluateException { + Object result = context.getSuspendContext().getDebugProcess().getVirtualMachineProxy().mirrorOfVoid(); + try { + result = myBodyEvaluator.evaluate(context); + } catch (EvaluateException e) { + boolean catched = false; + ObjectReference vmException = e.getExceptionFromTargetVM(); + if (vmException != null) { + for (CatchEvaluator evaluator : myCatchBlockEvaluators) { + if (evaluator != null && DebuggerUtils.instanceOf(vmException.type(), evaluator.getExceptionType())) { + result = evaluator.evaluate(vmException, context); + catched = true; + break; + } + } + } + if (!catched) { + throw e; + } + } finally { + if (myFinallyEvaluator != null) { + result = myFinallyEvaluator.evaluate(context); + } + } + return result; + } + + @Override + public Modifier getModifier() { + return null; + } +} From 649a1a4d515c219076f9b620c22803df2076d448 Mon Sep 17 00:00:00 2001 From: "Egor.Ushakov" Date: Wed, 9 Mar 2016 19:15:24 +0300 Subject: [PATCH 74/83] debugger: fixed variables visibility in for statements evaluation --- .../expression/EvaluatorBuilderImpl.java | 44 ++++++++++++------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/EvaluatorBuilderImpl.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/EvaluatorBuilderImpl.java index 9081d7060375..9b8643e2f91f 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/EvaluatorBuilderImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/EvaluatorBuilderImpl.java @@ -300,28 +300,40 @@ public class EvaluatorBuilderImpl implements EvaluatorBuilder { @Override public void visitForStatement(PsiForStatement statement) { - Evaluator initializerEvaluator = accept(statement.getInitialization()); - Evaluator conditionEvaluator = accept(statement.getCondition()); - if (conditionEvaluator != null) { - conditionEvaluator = new UnBoxingEvaluator(conditionEvaluator); - } - Evaluator updateEvaluator = accept(statement.getUpdate()); - Evaluator bodyEvaluator = accept(statement.getBody()); - if (bodyEvaluator != null) { - myResult = new ForStatementEvaluator(initializerEvaluator, conditionEvaluator, updateEvaluator, bodyEvaluator, getLabel(statement)); + CodeFragmentEvaluator oldFragmentEvaluator = setNewCodeFragmentEvaluator(); + try { + Evaluator initializerEvaluator = accept(statement.getInitialization()); + Evaluator conditionEvaluator = accept(statement.getCondition()); + if (conditionEvaluator != null) { + conditionEvaluator = new UnBoxingEvaluator(conditionEvaluator); + } + Evaluator updateEvaluator = accept(statement.getUpdate()); + Evaluator bodyEvaluator = accept(statement.getBody()); + if (bodyEvaluator != null) { + myResult = + new ForStatementEvaluator(initializerEvaluator, conditionEvaluator, updateEvaluator, bodyEvaluator, getLabel(statement)); + } + } finally { + myCurrentFragmentEvaluator = oldFragmentEvaluator; } } @Override public void visitForeachStatement(PsiForeachStatement statement) { - String iterationParameterName = statement.getIterationParameter().getName(); - myCurrentFragmentEvaluator.setInitialValue(iterationParameterName, null); - SyntheticVariableEvaluator iterationParameterEvaluator = new SyntheticVariableEvaluator(myCurrentFragmentEvaluator, iterationParameterName); + CodeFragmentEvaluator oldFragmentEvaluator = setNewCodeFragmentEvaluator(); + try { + String iterationParameterName = statement.getIterationParameter().getName(); + myCurrentFragmentEvaluator.setInitialValue(iterationParameterName, null); + SyntheticVariableEvaluator iterationParameterEvaluator = + new SyntheticVariableEvaluator(myCurrentFragmentEvaluator, iterationParameterName); - Evaluator iteratedValueEvaluator = accept(statement.getIteratedValue()); - Evaluator bodyEvaluator = accept(statement.getBody()); - if (bodyEvaluator != null) { - myResult = new ForeachStatementEvaluator(iterationParameterEvaluator, iteratedValueEvaluator, bodyEvaluator, getLabel(statement)); + Evaluator iteratedValueEvaluator = accept(statement.getIteratedValue()); + Evaluator bodyEvaluator = accept(statement.getBody()); + if (bodyEvaluator != null) { + myResult = new ForeachStatementEvaluator(iterationParameterEvaluator, iteratedValueEvaluator, bodyEvaluator, getLabel(statement)); + } + } finally { + myCurrentFragmentEvaluator = oldFragmentEvaluator; } } From f5341956f04bcc7d4d0695863f35a7b10e8741e8 Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Wed, 9 Mar 2016 19:25:43 +0300 Subject: [PATCH 75/83] [groovy] do not try to infer rValue within index property assignment (EA-58298) --- .../processors/SubstitutorComputer.java | 11 +++++++++-- .../highlighting/GroovyHighlightingTest.groovy | 18 ++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/processors/SubstitutorComputer.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/processors/SubstitutorComputer.java index deeae4e88e43..87ecf001c2f4 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/processors/SubstitutorComputer.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/processors/SubstitutorComputer.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. @@ -33,6 +33,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.branch.GrReturnState import org.jetbrains.plugins.groovy.lang.psi.api.statements.branch.GrThrowStatement; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrAssignmentExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrIndexProperty; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinitionBody; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrGdkMethod; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; @@ -95,7 +96,13 @@ public class SubstitutorComputer { } } else if (parent instanceof GrAssignmentExpression && myPlaceToInferContext.equals(((GrAssignmentExpression)parent).getRValue())) { - return ((GrAssignmentExpression)parent).getLValue().getType(); + GrExpression lValue = ((GrAssignmentExpression)parent).getLValue(); + if (lValue instanceof GrIndexProperty) { + return null; + } + else { + return lValue.getType(); + } } else if (parent instanceof GrVariable) { return ((GrVariable)parent).getDeclaredType(); diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/highlighting/GroovyHighlightingTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/highlighting/GroovyHighlightingTest.groovy index f37b6c998232..c328a68656cb 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/highlighting/GroovyHighlightingTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/highlighting/GroovyHighlightingTest.groovy @@ -1969,6 +1969,24 @@ import groovy.transform.Field @Field def (,) +''' + } + + void 'test no SOE in index property assignment with generic function'() { + testHighlighting ''' +class Main { + + static T foo() {} + + static void main(String[] args) { + def main = new Main() + main[Main] = foo() + } + + def putAt(x, String t) { + println "Works: $x = $t" + } +} ''' } } From 467531cdca1d7a081437731670d84846c6d57ba0 Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Wed, 9 Mar 2016 19:41:42 +0300 Subject: [PATCH 76/83] IDEA-152678 lst: allow to initialize tracker multiple times this is required in case if base revesion was changed - LineStatusTrackerManager.refreshTracker() it was broken in 2cbc4f0 --- .../src/com/intellij/openapi/vcs/ex/LineStatusTracker.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/LineStatusTracker.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/LineStatusTracker.java index 1431c61a2bea..3b069076a4df 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/LineStatusTracker.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/LineStatusTracker.java @@ -124,7 +124,7 @@ public class LineStatusTracker { synchronized (myLock) { try { - if (myInitialized || myReleased) return; + if (myReleased) return; if (myBaseRevisionNumber != null && myBaseRevisionNumber.contains(baseRevisionNumber)) return; myBaseRevisionNumber = baseRevisionNumber; From ad8b09c8e43d8a28d1dd6918b35c668c13d6fe2b Mon Sep 17 00:00:00 2001 From: "Egor.Ushakov" Date: Wed, 9 Mar 2016 20:08:28 +0300 Subject: [PATCH 77/83] removed unused dependencies --- java/debugger/impl/debugger-impl.iml | 2 -- 1 file changed, 2 deletions(-) diff --git a/java/debugger/impl/debugger-impl.iml b/java/debugger/impl/debugger-impl.iml index b35289246ebb..3a9669db998f 100644 --- a/java/debugger/impl/debugger-impl.iml +++ b/java/debugger/impl/debugger-impl.iml @@ -21,8 +21,6 @@ - - From f17ab80c1d86ac6d09de9a56cbbdbb41da6d7555 Mon Sep 17 00:00:00 2001 From: "Egor.Ushakov" Date: Wed, 9 Mar 2016 20:17:53 +0300 Subject: [PATCH 78/83] isEmpty for maps --- .../evaluation/CodeFragmentFactoryContextWrapper.java | 3 ++- .../src/com/intellij/util/containers/ContainerUtil.java | 7 ++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/CodeFragmentFactoryContextWrapper.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/CodeFragmentFactoryContextWrapper.java index f790a0bf68ce..1012a7f2a793 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/CodeFragmentFactoryContextWrapper.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/CodeFragmentFactoryContextWrapper.java @@ -26,6 +26,7 @@ import com.intellij.psi.JavaRecursiveElementVisitor; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiLocalVariable; import com.intellij.util.StringBuilderSpinAllocator; +import com.intellij.util.containers.ContainerUtil; import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XDebuggerManager; import com.intellij.xdebugger.impl.XDebugSessionImpl; @@ -87,7 +88,7 @@ public class CodeFragmentFactoryContextWrapper extends CodeFragmentFactory { XValueMarkers markers = ((XDebugSessionImpl)session).getValueMarkers(); Map markupMap = markers != null ? markers.getAllMarkers() : null; //final Map markupMap = ValueDescriptorImpl.getMarkupMap(process); - if (markupMap != null && markupMap.size() > 0) { + if (ContainerUtil.isEmpty(markupMap)) { final Pair> markupVariables = createMarkupVariablesText(markupMap); int offset = markupVariables.getFirst().length() - 1; final TextWithImportsImpl textWithImports = new TextWithImportsImpl(CodeFragmentKind.CODE_BLOCK, markupVariables.getFirst(), "", myDelegate.getFileType()); diff --git a/platform/util/src/com/intellij/util/containers/ContainerUtil.java b/platform/util/src/com/intellij/util/containers/ContainerUtil.java index 364df20afe67..2f1ac417c375 100644 --- a/platform/util/src/com/intellij/util/containers/ContainerUtil.java +++ b/platform/util/src/com/intellij/util/containers/ContainerUtil.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. @@ -2702,6 +2702,11 @@ public class ContainerUtil extends ContainerUtilRt { return collection == null || collection.isEmpty(); } + @Contract(value = "null -> true", pure = true) + public static boolean isEmpty(Map map) { + return map == null || map.isEmpty(); + } + @NotNull @Contract(pure=true) public static List notNullize(@Nullable List list) { From a096130f8d8406b1b55a69ddcf073a06713affea Mon Sep 17 00:00:00 2001 From: "Vassiliy.Kudryashov" Date: Wed, 9 Mar 2016 21:04:11 +0300 Subject: [PATCH 79/83] IDEA-152440 Changes in License Dialog: 'Terms Of Service' (TOS) and 'Privacy Policy' (PP) Stage #6: more CSS improvements --- .../src/com/intellij/idea/StartupUtil.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/idea/StartupUtil.java b/platform/platform-impl/src/com/intellij/idea/StartupUtil.java index a2e04365f9b5..ece0204ce06c 100644 --- a/platform/platform-impl/src/com/intellij/idea/StartupUtil.java +++ b/platform/platform-impl/src/com/intellij/idea/StartupUtil.java @@ -449,9 +449,15 @@ public class StartupUtil { viewer.setText(htmlText); StyleSheet styleSheet = ((HTMLDocument)viewer.getDocument()).getStyleSheet(); styleSheet.addRule("body {font-family: \"Segoe UI\", Tahoma, sans-serif;}"); - styleSheet.addRule("h2 {margin-top:" + JBUI.scaleFontSize(30) + "pt;}"); + styleSheet.addRule("body {margin-top:0;padding-top:0;}"); + styleSheet.addRule("body {font-size:" + JBUI.scaleFontSize(13) + "pt;}"); + styleSheet.addRule("h2, em {margin-top:" + JBUI.scaleFontSize(20) + "pt;}"); + styleSheet.addRule("h1, h2, h3, p, h4, em {margin-bottom:0;padding-bottom:0;}"); + styleSheet.addRule("p, h1 {margin-top:0;padding-top:"+JBUI.scaleFontSize(6)+"pt;}"); + styleSheet.addRule("li {margin-bottom:" + JBUI.scaleFontSize(6) + "pt;}"); + styleSheet.addRule("h2 {margin-top:0;padding-top:"+JBUI.scaleFontSize(13)+"pt;}"); viewer.setCaretPosition(0); - viewer.setBorder(JBUI.Borders.empty(5)); + viewer.setBorder(JBUI.Borders.empty(0, 5, 5, 5)); centerPanel.add(new JLabel("Please read and accept these terms and conditions:"), BorderLayout.NORTH); centerPanel .add(new JBScrollPane(viewer, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER), From f2345455dc08643efc27f3c03bd20cc80baaed23 Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Wed, 9 Mar 2016 21:14:12 +0300 Subject: [PATCH 80/83] inspection tool window: GlobalInspectionContextImpl must to reset view state --- .../codeInspection/ex/GlobalInspectionContextImpl.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextImpl.java b/platform/lang-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextImpl.java index 74dd8f344560..92c623d6b459 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextImpl.java @@ -779,7 +779,15 @@ public class GlobalInspectionContextImpl extends GlobalInspectionContextBase imp @Override public void close(boolean noSuspisiousCodeFound) { - if (!noSuspisiousCodeFound && (myView == null || myView.isRerun())) return; + if (!noSuspisiousCodeFound) { + if (myView.isRerun()) { + myViewClosed = true; + myView = null; + } + if (myView == null) { + return; + } + } AnalysisUIOptions.getInstance(getProject()).save(myUIOptions); if (myContent != null) { final ContentManager contentManager = getContentManager(); From 67c2d10ef37800ba3ca6eea2905b0bca63306ecd Mon Sep 17 00:00:00 2001 From: "Maxim.Mossienko" Date: Wed, 9 Mar 2016 19:35:07 +0100 Subject: [PATCH 81/83] speed search in results preview based on file names of results (IDEA-152756) --- .../lang-impl/src/com/intellij/find/impl/FindDialog.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/platform/lang-impl/src/com/intellij/find/impl/FindDialog.java b/platform/lang-impl/src/com/intellij/find/impl/FindDialog.java index 52448f725767..004a3b5036b6 100644 --- a/platform/lang-impl/src/com/intellij/find/impl/FindDialog.java +++ b/platform/lang-impl/src/com/intellij/find/impl/FindDialog.java @@ -73,6 +73,7 @@ import com.intellij.usageView.UsageInfo; import com.intellij.usages.*; import com.intellij.usages.impl.UsagePreviewPanel; import com.intellij.util.*; +import com.intellij.util.containers.Convertor; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -644,6 +645,12 @@ public class FindDialog extends DialogWrapper { } }; myResultsPreviewTable = table; + new TableSpeedSearch(table, new Convertor() { + @Override + public String convert(Object o) { + return ((UsageInfo2UsageAdapter)o).getFile().getName(); + } + }); myResultsPreviewTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent e) { From c7a844e7db65b90b2ca47c9da32fc90ee3d2b42e Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 9 Mar 2016 20:01:01 +0100 Subject: [PATCH 82/83] make submitTransactionAndWait more convenient --- .../openapi/application/TransactionGuard.java | 21 ++++++++++++++++--- .../application/TransactionGuardImpl.java | 17 +++++++++++---- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/platform/core-api/src/com/intellij/openapi/application/TransactionGuard.java b/platform/core-api/src/com/intellij/openapi/application/TransactionGuard.java index 7291d411e241..423684a31408 100644 --- a/platform/core-api/src/com/intellij/openapi/application/TransactionGuard.java +++ b/platform/core-api/src/com/intellij/openapi/application/TransactionGuard.java @@ -17,6 +17,8 @@ package com.intellij.openapi.application; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.progress.ProcessCanceledException; +import com.intellij.openapi.util.Computable; +import com.intellij.openapi.util.Ref; import org.jetbrains.annotations.NotNull; /** @@ -96,14 +98,27 @@ public abstract class TransactionGuard { } /** - * Schedules a transaction and waits for it to be completed. Only allowed to be invoked on non-UI thread and outside read action. + * Schedules a transaction and waits for it to be completed. Fails if invoked on UI thread inside an incompatible transaction, + * or inside a read action on non-UI thread. * @see #submitMergeableTransaction(TransactionKind, Runnable) - * @param kind - * @param transaction * @throws ProcessCanceledException if current thread is interrupted */ public abstract void submitTransactionAndWait(@NotNull TransactionKind kind, @NotNull Runnable transaction) throws ProcessCanceledException; + /** + * Same as {@link #submitTransactionAndWait(TransactionKind, Runnable)}, but returns a value computed by the transaction. + */ + public T submitTransactionAndWait(@NotNull TransactionKind kind, @NotNull final Computable transaction) throws ProcessCanceledException { + final Ref result = Ref.create(); + submitTransactionAndWait(kind, new Runnable() { + @Override + public void run() { + result.set(transaction.compute()); + } + }); + return result.get(); + } + /** * A synchronous version of {@link #submitMergeableTransaction(TransactionKind, Runnable)}. * @return a token object for this transaction. Call {@link AccessToken#finish()} (inside finally) when the transaction is complete. diff --git a/platform/core-impl/src/com/intellij/openapi/application/TransactionGuardImpl.java b/platform/core-impl/src/com/intellij/openapi/application/TransactionGuardImpl.java index eb4b02eec959..43ca3fb43d63 100644 --- a/platform/core-impl/src/com/intellij/openapi/application/TransactionGuardImpl.java +++ b/platform/core-impl/src/com/intellij/openapi/application/TransactionGuardImpl.java @@ -48,7 +48,6 @@ public class TransactionGuardImpl extends TransactionGuard { // please assign exceptions that occur here to Peter LOG.error("Nested transactions are not allowed, see FAQ in TransactionGuard class javadoc. Transaction start trace is in attachment. Kind is " + kind, new Attachment("trace.txt", myTransactionStartTrace)); - //throw new IllegalStateException("Nested transactions are not allowed"); } myTransactionStartTrace = DebugUtil.currentStackTrace(); return new AccessToken() { @@ -99,7 +98,7 @@ public class TransactionGuardImpl extends TransactionGuard { Runnable runnable = new Runnable() { @Override public void run() { - if (!isInsideTransaction() || kind != TransactionKind.NO_MERGE && myMergeableKinds.contains(kind)) { + if (canRunTransactionNow(kind)) { runSyncTransaction(kind, transaction); } else { @@ -118,6 +117,10 @@ public class TransactionGuardImpl extends TransactionGuard { } } + protected boolean canRunTransactionNow(@NotNull TransactionKind kind) { + return !isInsideTransaction() || kind != TransactionKind.NO_MERGE && myMergeableKinds.contains(kind); + } + @Override @NotNull public AccessToken acceptNestedTransactions(TransactionKind... kinds) { @@ -144,9 +147,15 @@ public class TransactionGuardImpl extends TransactionGuard { @Override public void submitTransactionAndWait(@NotNull TransactionKind kind, @NotNull final Runnable transaction) throws ProcessCanceledException { Application app = ApplicationManager.getApplication(); - assert !app.isDispatchThread() : "submitTransactionAndWait should not be invoked on dispatch thread"; - assert !app.isReadAccessAllowed() : "submitTransactionAndWait should not be invoked from a read action"; + if (app.isDispatchThread()) { + if (!canRunTransactionNow(kind)) { + throw new AssertionError("Cannot run submitTransactionAndWait from another transaction, kind " + kind + " is not allowed"); + } + runSyncTransaction(kind, transaction); + return; + } + assert !app.isReadAccessAllowed() : "submitTransactionAndWait should not be invoked from a read action"; final Semaphore semaphore = new Semaphore(); semaphore.down(); final Throwable[] exception = {null}; From 9334c5c8589d4005cc68f529d6f5a6436a98b351 Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 9 Mar 2016 20:16:18 +0100 Subject: [PATCH 83/83] determine applicable live templates without committing physical documents in AnAction.update --- .../lookup/impl/actions/ChooseItemAction.java | 18 ++-- .../template/impl/TemplateManagerImpl.java | 83 +++++++++++-------- .../ExpandLiveTemplateCustomAction.java | 2 +- 3 files changed, 61 insertions(+), 42 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/actions/ChooseItemAction.java b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/actions/ChooseItemAction.java index ebd20cf98202..29219f2c185b 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/actions/ChooseItemAction.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/actions/ChooseItemAction.java @@ -23,11 +23,14 @@ import com.intellij.codeInsight.lookup.Lookup; import com.intellij.codeInsight.lookup.LookupManager; import com.intellij.codeInsight.lookup.impl.LookupImpl; import com.intellij.codeInsight.template.impl.*; +import com.intellij.codeInsight.template.impl.editorActions.ExpandLiveTemplateCustomAction; import com.intellij.featureStatistics.FeatureUsageTracker; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.actionSystem.EditorAction; import com.intellij.openapi.editor.actionSystem.EditorActionHandler; +import com.intellij.openapi.editor.actionSystem.EditorWriteActionHandler; +import com.intellij.openapi.project.Project; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; import com.intellij.util.containers.ContainerUtil; @@ -55,7 +58,16 @@ public abstract class ChooseItemAction extends EditorAction { if (lookup == null) { throw new AssertionError("The last lookup disposed at: " + LookupImpl.getLastLookupDisposeTrace() + "\n-----------------------\n"); } - + + if ((finishingChar == Lookup.NORMAL_SELECT_CHAR || finishingChar == Lookup.REPLACE_SELECT_CHAR) && + hasTemplatePrefix(lookup, finishingChar)) { + lookup.hideLookup(true); + + ExpandLiveTemplateCustomAction.createExpandTemplateHandler(finishingChar).execute(editor, null, dataContext); + + return; + } + if (finishingChar == Lookup.NORMAL_SELECT_CHAR) { if (!lookup.isFocused()) { FeatureUsageTracker.getInstance().triggerFeatureUsed(CodeCompletionFeatures.EDITING_COMPLETION_CONTROL_ENTER); @@ -78,10 +90,6 @@ public abstract class ChooseItemAction extends EditorAction { if (lookup == null) return false; if (!lookup.isAvailableToUser()) return false; if (focusedOnly && lookup.getFocusDegree() == LookupImpl.FocusDegree.UNFOCUSED) return false; - if (finishingChar == Lookup.NORMAL_SELECT_CHAR && hasTemplatePrefix(lookup, TemplateSettings.ENTER_CHAR) || - finishingChar == Lookup.REPLACE_SELECT_CHAR && hasTemplatePrefix(lookup, TemplateSettings.TAB_CHAR)) { - return false; - } if (finishingChar == Lookup.REPLACE_SELECT_CHAR) { return !lookup.getItems().isEmpty(); } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateManagerImpl.java b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateManagerImpl.java index d1b0ca3a3363..f268c176823a 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateManagerImpl.java @@ -23,7 +23,6 @@ import com.intellij.lang.Language; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.CommandProcessor; -import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.editor.*; import com.intellij.openapi.editor.event.EditorFactoryAdapter; import com.intellij.openapi.editor.event.EditorFactoryEvent; @@ -33,11 +32,15 @@ import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.Trinity; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; +import com.intellij.psi.util.CachedValueProvider; +import com.intellij.psi.util.CachedValuesManager; import com.intellij.psi.util.PsiUtilBase; import com.intellij.psi.util.PsiUtilCore; import com.intellij.util.PairProcessor; +import com.intellij.util.containers.ConcurrentFactoryMap; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.HashMap; import org.jetbrains.annotations.NotNull; @@ -130,6 +133,7 @@ public class TemplateManagerImpl extends TemplateManager implements Disposable { @Override public boolean startTemplate(@NotNull Editor editor, char shortcutChar) { + PsiDocumentManager.getInstance(myProject).commitDocument(editor.getDocument()); Runnable runnable = prepareTemplate(editor, shortcutChar, null); if (runnable != null) { runnable.run(); @@ -260,36 +264,31 @@ public class TemplateManagerImpl extends TemplateManager implements Disposable { PsiFile file = PsiUtilBase.getPsiFileInEditor(editor, myProject); if (file == null) return null; - TemplateSettings templateSettings = TemplateSettings.getInstance(); - Map template2argument = findMatchingTemplates(file, editor, shortcutChar, templateSettings); + Map template2argument = findMatchingTemplates(file, editor, shortcutChar, TemplateSettings.getInstance()); - for (final CustomLiveTemplate customLiveTemplate : CustomLiveTemplate.EP_NAME.getExtensions()) { - if (shortcutChar == customLiveTemplate.getShortcut()) { - if (editor.getCaretModel().getCaretCount() > 1 && !supportsMultiCaretMode(customLiveTemplate)) { - continue; - } - final Document document = editor.getDocument(); - PsiDocumentManager.getInstance(myProject).commitDocument(document); - if (isApplicable(customLiveTemplate, editor, file)) { - final CustomTemplateCallback callback = new CustomTemplateCallback(editor, file); - final String key = customLiveTemplate.computeTemplateKey(callback); + List customCandidates = ContainerUtil.findAll(CustomLiveTemplate.EP_NAME.getExtensions(), customLiveTemplate -> + shortcutChar == customLiveTemplate.getShortcut() && + (editor.getCaretModel().getCaretCount() <= 1 || supportsMultiCaretMode(customLiveTemplate))); + if (!customCandidates.isEmpty()) { + int caretOffset = editor.getCaretModel().getOffset(); + PsiFile fileCopy = insertDummyIdentifierIfNeeded(file, caretOffset, caretOffset, ""); + Document document = editor.getDocument(); + + for (final CustomLiveTemplate customLiveTemplate : customCandidates) { + if (isApplicable(customLiveTemplate, editor, fileCopy)) { + final String key = customLiveTemplate.computeTemplateKey(new CustomTemplateCallback(editor, fileCopy)); if (key != null) { - int caretOffset = editor.getCaretModel().getOffset(); int offsetBeforeKey = caretOffset - key.length(); CharSequence text = document.getImmutableCharSequence(); if (template2argument == null || !containsTemplateStartingBefore(template2argument, offsetBeforeKey, caretOffset, text)) { - return new Runnable() { - @Override - public void run() { - customLiveTemplate.expand(key, callback); - } - }; + return () -> customLiveTemplate.expand(key, new CustomTemplateCallback(editor, file)); } } } } } + return startNonCustomTemplates(template2argument, editor, processor); } @@ -357,13 +356,6 @@ public class TemplateManagerImpl extends TemplateManager implements Disposable { return null; } - CommandProcessor.getInstance().executeCommand(myProject, new Runnable() { - @Override - public void run() { - PsiDocumentManager.getInstance(myProject).commitDocument(document); - } - }, "", null); - candidatesWithoutArgument = filterApplicableCandidates(file, caretOffset, candidatesWithoutArgument); candidatesWithArgument = filterApplicableCandidates(file, argumentOffset, candidatesWithArgument); Map candidate2Argument = new HashMap(); @@ -474,7 +466,7 @@ public class TemplateManagerImpl extends TemplateManager implements Disposable { return candidates; } - PsiFile copy = insertDummyIdentifier(file, caretOffset, caretOffset); + PsiFile copy = insertDummyIdentifierIfNeeded(file, caretOffset, caretOffset, CompletionUtil.DUMMY_IDENTIFIER_TRIMMED); List result = new ArrayList(); for (TemplateImpl candidate : candidates) { @@ -610,15 +602,34 @@ public class TemplateManagerImpl extends TemplateManager implements Disposable { boolean selection = editor.getSelectionModel().hasSelection(); final int startOffset = selection ? editor.getSelectionModel().getSelectionStart() : editor.getCaretModel().getOffset(); final int endOffset = selection ? editor.getSelectionModel().getSelectionEnd() : startOffset; - return insertDummyIdentifier(file, startOffset, endOffset); + return insertDummyIdentifierIfNeeded(file, startOffset, endOffset, CompletionUtil.DUMMY_IDENTIFIER_TRIMMED); } - public static PsiFile insertDummyIdentifier(PsiFile file, final int startOffset, final int endOffset) { - file = (PsiFile)file.copy(); - final Document document = file.getViewProvider().getDocument(); - assert document != null; - document.replaceString(startOffset, endOffset, CompletionUtil.DUMMY_IDENTIFIER_TRIMMED); - PsiDocumentManager.getInstance(file.getProject()).commitDocument(document); - return file; + private static PsiFile insertDummyIdentifierIfNeeded(PsiFile file, final int startOffset, final int endOffset, String replacement) { + Document originalDocument = file.getViewProvider().getDocument(); + assert originalDocument != null; + + if (replacement.isEmpty() && PsiDocumentManager.getInstance(file.getProject()).isCommitted(originalDocument)) { + return file; + } + + ConcurrentFactoryMap, PsiFile> map = + CachedValuesManager.getCachedValue(file, () -> CachedValueProvider.Result.create(new ConcurrentFactoryMap, PsiFile>() { + @Nullable + @Override + protected PsiFile create(Trinity key) { + PsiFile copy = (PsiFile)file.copy(); + + final Document document = copy.getViewProvider().getDocument(); + assert document != null; + + document.setText(originalDocument.getImmutableCharSequence()); // original file might be uncommitted + document.replaceString(key.first, key.second, key.third); + PsiDocumentManager.getInstance(copy.getProject()).commitDocument(document); + return copy; + } + }, file, originalDocument)); + + return map.get(Trinity.create(startOffset, endOffset, replacement)); } } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/editorActions/ExpandLiveTemplateCustomAction.java b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/editorActions/ExpandLiveTemplateCustomAction.java index 44f138f2229a..c35a85497d57 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/editorActions/ExpandLiveTemplateCustomAction.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/editorActions/ExpandLiveTemplateCustomAction.java @@ -36,7 +36,7 @@ public class ExpandLiveTemplateCustomAction extends EditorAction { setInjectedContext(true); } - static EditorWriteActionHandler createExpandTemplateHandler(final char shortcutChar) { + public static EditorWriteActionHandler createExpandTemplateHandler(final char shortcutChar) { return new EditorWriteActionHandler(true) { @Override public void executeWriteAction(Editor editor, @Nullable Caret caret, DataContext dataContext) {