diff --git a/python/src/META-INF/python-core.xml b/python/src/META-INF/python-core.xml index d669cb89a7be..5e4a1951c0c7 100644 --- a/python/src/META-INF/python-core.xml +++ b/python/src/META-INF/python-core.xml @@ -281,74 +281,74 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -542,6 +542,7 @@ + diff --git a/python/src/com/jetbrains/python/inspections/PyDocstringInspection.java b/python/src/com/jetbrains/python/inspections/PyDocstringInspection.java index ca73f0d89cce..790ff7b0a21f 100644 --- a/python/src/com/jetbrains/python/inspections/PyDocstringInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyDocstringInspection.java @@ -21,19 +21,16 @@ import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.intellij.codeInspection.LocalInspectionToolSession; import com.intellij.codeInspection.ProblemsHolder; -import com.intellij.codeInspection.SuppressQuickFix; import com.intellij.lang.ASTNode; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElementVisitor; -import com.intellij.psi.util.PsiTreeUtil; import com.jetbrains.python.PyBundle; import com.jetbrains.python.PyNames; import com.jetbrains.python.documentation.DocStringUtil; import com.jetbrains.python.inspections.quickfix.DocstringQuickFix; -import com.jetbrains.python.inspections.quickfix.PySuppressInspectionFix; import com.jetbrains.python.psi.*; import com.jetbrains.python.testing.PythonUnitTestUtil; import com.jetbrains.python.toolbox.Substring; @@ -229,19 +226,4 @@ public class PyDocstringInspection extends PyInspection { return hasMissing ? missing : Collections.emptyList(); } } - - @NotNull - @Override - public SuppressQuickFix[] getBatchSuppressActions(@Nullable PsiElement element) { - List result = new ArrayList(); - if (element != null) { - if (PsiTreeUtil.getParentOfType(element, PyFunction.class) != null) { - result.add(new PySuppressInspectionFix(getShortName().replace("Inspection", ""), "Suppress for function", PyFunction.class)); - } - if (PsiTreeUtil.getParentOfType(element, PyClass.class) != null) { - result.add(new PySuppressInspectionFix(getShortName().replace("Inspection", ""), "Suppress for class", PyClass.class)); - } - } - return result.toArray(new SuppressQuickFix[result.size()]); - } } diff --git a/python/src/com/jetbrains/python/inspections/PyInspection.java b/python/src/com/jetbrains/python/inspections/PyInspection.java index 993dcdcb22f3..bbc064d5c89a 100644 --- a/python/src/com/jetbrains/python/inspections/PyInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyInspection.java @@ -16,31 +16,26 @@ package com.jetbrains.python.inspections; import com.intellij.codeInspection.LocalInspectionTool; -import com.intellij.codeInspection.SuppressQuickFix; -import com.intellij.codeInspection.SuppressionUtil; -import com.intellij.psi.PsiComment; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiWhiteSpace; import com.intellij.psi.util.PsiTreeUtil; import com.jetbrains.python.PyBundle; -import com.jetbrains.python.codeInsight.controlflow.ScopeOwner; -import com.jetbrains.python.inspections.quickfix.PySuppressInspectionFix; -import com.jetbrains.python.psi.*; +import com.jetbrains.python.psi.PyExpressionCodeFragment; import com.jetbrains.python.psi.impl.PyFileImpl; +import org.intellij.lang.annotations.Pattern; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * @author yole - */ public abstract class PyInspection extends LocalInspectionTool { + @Pattern(VALID_ID_PATTERN) + @NotNull + @Override + public String getID() { + //noinspection PatternValidation + return getShortName(super.getID()); + } + @Nls @NotNull @Override @@ -59,80 +54,20 @@ public abstract class PyInspection extends LocalInspectionTool { return true; } - @NotNull - @Override - public SuppressQuickFix[] getBatchSuppressActions(@Nullable PsiElement element) { - List result = new ArrayList(); - result.add(new PySuppressInspectionFix(getSuppressId(), "Suppress for statement", PyStatement.class) { - @Override - public PsiElement getContainer(PsiElement context) { - if (PsiTreeUtil.getParentOfType(context, PyStatementList.class, false, ScopeOwner.class) != null || - PsiTreeUtil.getParentOfType(context, PyFunction.class, PyClass.class) == null) { - return super.getContainer(context); - } - return null; - } - }); - result.add(new PySuppressInspectionFix(getSuppressId(), "Suppress for function", PyFunction.class)); - result.add(new PySuppressInspectionFix(getSuppressId(), "Suppress for class", PyClass.class)); - return result.toArray(new SuppressQuickFix[result.size()]); - } - @Override public boolean isSuppressedFor(@NotNull PsiElement element) { final PsiFile file = element.getContainingFile(); - boolean isAccepted = true; - if (file instanceof PyFileImpl) { - isAccepted = ((PyFileImpl)file).isAcceptedFor(this.getClass()); + if (file instanceof PyFileImpl && !((PyFileImpl)file).isAcceptedFor(this.getClass())) { + return true; } - return !isAccepted || isSuppressedForParent(element, PyStatement.class) || - isSuppressedForParent(element, PyFunction.class) || - isSuppressedForParent(element, PyClass.class) || - isSuppressForCodeFragment(element); + return isSuppressForCodeFragment(element) || super.isSuppressedFor(element); } - private boolean isSuppressForCodeFragment(PsiElement element) { + private boolean isSuppressForCodeFragment(@Nullable PsiElement element) { return isSuppressForCodeFragment() && PsiTreeUtil.getParentOfType(element, PyExpressionCodeFragment.class) != null; } protected boolean isSuppressForCodeFragment() { return false; } - - private boolean isSuppressedForParent(PsiElement element, final Class parentClass) { - PyElement parent = PsiTreeUtil.getParentOfType(element, parentClass, false); - if (parent == null) { - return false; - } - return isSuppressedForElement(parent); - } - - private boolean isSuppressedForElement(PyElement stmt) { - PsiElement prevSibling = stmt.getPrevSibling(); - if (prevSibling == null) { - final PsiElement parent = stmt.getParent(); - if (parent != null) { - prevSibling = parent.getPrevSibling(); - } - } - while (prevSibling instanceof PsiComment || prevSibling instanceof PsiWhiteSpace) { - if (prevSibling instanceof PsiComment && isSuppressedInComment(prevSibling.getText().substring(1).trim())) { - return true; - } - prevSibling = prevSibling.getPrevSibling(); - } - return false; - } - - private static final Pattern SUPPRESS_PATTERN = Pattern.compile(SuppressionUtil.COMMON_SUPPRESS_REGEXP); - - private boolean isSuppressedInComment(String commentText) { - Matcher m = SUPPRESS_PATTERN.matcher(commentText); - return m.matches() && SuppressionUtil.isInspectionToolIdMentioned(m.group(1), getSuppressId()); - } - - @NotNull - protected String getSuppressId() { - return getShortName().replace("Inspection", ""); - } } diff --git a/python/src/com/jetbrains/python/inspections/PyInspectionsSuppressor.java b/python/src/com/jetbrains/python/inspections/PyInspectionsSuppressor.java new file mode 100644 index 000000000000..545c1b37e46d --- /dev/null +++ b/python/src/com/jetbrains/python/inspections/PyInspectionsSuppressor.java @@ -0,0 +1,88 @@ +package com.jetbrains.python.inspections; + +import com.intellij.codeInspection.InspectionSuppressor; +import com.intellij.codeInspection.SuppressQuickFix; +import com.intellij.codeInspection.SuppressionUtil; +import com.intellij.psi.PsiComment; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiWhiteSpace; +import com.intellij.psi.util.PsiTreeUtil; +import com.jetbrains.python.codeInsight.controlflow.ScopeOwner; +import com.jetbrains.python.inspections.quickfix.PySuppressInspectionFix; +import com.jetbrains.python.psi.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class PyInspectionsSuppressor implements InspectionSuppressor { + private static final Pattern SUPPRESS_PATTERN = Pattern.compile(SuppressionUtil.COMMON_SUPPRESS_REGEXP); + private static final String PY_DOCSTRING_INSPECTION_ID = new PyDocstringInspection().getID(); + + @NotNull + @Override + public SuppressQuickFix[] getSuppressActions(@Nullable PsiElement element, @NotNull String toolId) { + if (PY_DOCSTRING_INSPECTION_ID.equals(toolId)) { + return new SuppressQuickFix[]{ + new PySuppressInspectionFix(toolId, "Suppress for function", PyFunction.class), + new PySuppressInspectionFix(toolId, "Suppress for class", PyClass.class) + }; + } + else { + return new SuppressQuickFix[]{ + new PySuppressInspectionFix(toolId, "Suppress for statement", PyStatement.class) { + @Override + public PsiElement getContainer(PsiElement context) { + if (PsiTreeUtil.getParentOfType(context, PyStatementList.class, false, ScopeOwner.class) != null || + PsiTreeUtil.getParentOfType(context, PyFunction.class, PyClass.class) == null) { + return super.getContainer(context); + } + return null; + } + }, + new PySuppressInspectionFix(toolId, "Suppress for function", PyFunction.class), + new PySuppressInspectionFix(toolId, "Suppress for class", PyClass.class) + }; + } + } + + @Override + public boolean isSuppressedFor(@NotNull PsiElement element, @NotNull String toolId) { + return isSuppressedForParent(element, PyStatement.class, toolId) || + isSuppressedForParent(element, PyFunction.class, toolId) || + isSuppressedForParent(element, PyClass.class, toolId); + } + + private static boolean isSuppressedForParent(@NotNull PsiElement element, + @NotNull final Class parentClass, + @NotNull String suppressId) { + PyElement parent = PsiTreeUtil.getParentOfType(element, parentClass, false); + if (parent == null) { + return false; + } + return isSuppressedForElement(parent, suppressId); + } + + private static boolean isSuppressedForElement(@NotNull PyElement stmt, @NotNull String suppressId) { + PsiElement prevSibling = stmt.getPrevSibling(); + if (prevSibling == null) { + final PsiElement parent = stmt.getParent(); + if (parent != null) { + prevSibling = parent.getPrevSibling(); + } + } + while (prevSibling instanceof PsiComment || prevSibling instanceof PsiWhiteSpace) { + if (prevSibling instanceof PsiComment && isSuppressedInComment(prevSibling.getText().substring(1).trim(), suppressId)) { + return true; + } + prevSibling = prevSibling.getPrevSibling(); + } + return false; + } + + private static boolean isSuppressedInComment(@NotNull String commentText, @NotNull String suppressId) { + Matcher m = SUPPRESS_PATTERN.matcher(commentText); + return m.matches() && SuppressionUtil.isInspectionToolIdMentioned(m.group(1), suppressId); + } +} diff --git a/python/testData/inspections/suppress/suppressInsideInjection.py b/python/testData/inspections/suppress/suppressInsideInjection.py new file mode 100644 index 000000000000..a81f71a27282 --- /dev/null +++ b/python/testData/inspections/suppress/suppressInsideInjection.py @@ -0,0 +1,3 @@ +# language=Python +print "# noinspection PyUnresolvedReferences\ +print xxx" \ No newline at end of file diff --git a/python/testData/inspections/suppress/suppressOutsideInjection.py b/python/testData/inspections/suppress/suppressOutsideInjection.py new file mode 100644 index 000000000000..911cbc5dbd58 --- /dev/null +++ b/python/testData/inspections/suppress/suppressOutsideInjection.py @@ -0,0 +1,3 @@ +# noinspection PyUnresolvedReferences +#language=Python +print "print xxx" \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PySuppressInspectionsTest.java b/python/testSrc/com/jetbrains/python/PySuppressInspectionsTest.java index 1eab5f2ed627..0a049b9ce2e3 100644 --- a/python/testSrc/com/jetbrains/python/PySuppressInspectionsTest.java +++ b/python/testSrc/com/jetbrains/python/PySuppressInspectionsTest.java @@ -18,14 +18,11 @@ package com.jetbrains.python; import com.intellij.codeInsight.intention.IntentionAction; import com.jetbrains.python.fixtures.PyTestCase; import com.jetbrains.python.inspections.PyInspection; -import com.jetbrains.python.inspections.unresolvedReference.PyUnresolvedReferencesInspection; import com.jetbrains.python.inspections.PyUnusedLocalInspection; +import com.jetbrains.python.inspections.unresolvedReference.PyUnresolvedReferencesInspection; import java.util.List; -/** - * @author yole - */ public class PySuppressInspectionsTest extends PyTestCase { public void testSuppressedForStatement() { doTestHighlighting(PyUnresolvedReferencesInspection.class); @@ -46,7 +43,15 @@ public class PySuppressInspectionsTest extends PyTestCase { public void testSuppressForImport() { // PY-2240 doTestHighlighting(PyUnresolvedReferencesInspection.class); } + + public void testSuppressInsideInjection() { + doTestHighlighting(PyUnresolvedReferencesInspection.class); + } + public void testSuppressOutsideInjection() { + doTestHighlighting(PyUnresolvedReferencesInspection.class); + } + private void doTestHighlighting(final Class inspectionClass) { myFixture.configureByFile("inspections/suppress/" + getTestName(true) + ".py"); myFixture.enableInspections(inspectionClass);