diff --git a/python/ipnb/src/org/jetbrains/plugins/ipnb/IpnbVisitorFilter.java b/python/ipnb/src/org/jetbrains/plugins/ipnb/IpnbVisitorFilter.java
index 46bcfdd5a850..add58bd89a00 100644
--- a/python/ipnb/src/org/jetbrains/plugins/ipnb/IpnbVisitorFilter.java
+++ b/python/ipnb/src/org/jetbrains/plugins/ipnb/IpnbVisitorFilter.java
@@ -16,7 +16,8 @@
package org.jetbrains.plugins.ipnb;
import com.intellij.psi.PsiFile;
-import com.jetbrains.python.inspections.PyDocstringInspection;
+import com.jetbrains.python.inspections.PyIncorrectDocstringInspection;
+import com.jetbrains.python.inspections.PyMissingOrEmptyDocstringInspection;
import com.jetbrains.python.inspections.PyStatementEffectInspection;
import com.jetbrains.python.inspections.PythonVisitorFilter;
import org.jetbrains.annotations.NotNull;
@@ -24,7 +25,9 @@ import org.jetbrains.annotations.NotNull;
public class IpnbVisitorFilter implements PythonVisitorFilter {
@Override
public boolean isSupported(@NotNull final Class visitorClass, @NotNull final PsiFile file) {
- if (visitorClass == PyDocstringInspection.class || visitorClass == PyStatementEffectInspection.class) {
+ if (visitorClass == PyIncorrectDocstringInspection.class ||
+ visitorClass == PyMissingOrEmptyDocstringInspection.class ||
+ visitorClass == PyStatementEffectInspection.class) {
return false;
}
return true;
diff --git a/python/resources/inspectionDescriptions/PyDocstringInspection.html b/python/resources/inspectionDescriptions/PyDocstringInspection.html
deleted file mode 100644
index f9f48e5e7204..000000000000
--- a/python/resources/inspectionDescriptions/PyDocstringInspection.html
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
- This inspection detects mismatched parameters in docstring, lack of docstring and an empty docstring.
-
-
-
\ No newline at end of file
diff --git a/python/resources/inspectionDescriptions/PyIncorrectDocstringInspection.html b/python/resources/inspectionDescriptions/PyIncorrectDocstringInspection.html
new file mode 100644
index 000000000000..8c99992a005c
--- /dev/null
+++ b/python/resources/inspectionDescriptions/PyIncorrectDocstringInspection.html
@@ -0,0 +1,7 @@
+
+
+
+ This inspection detects mismatched parameters in docstring.
+
+
+
\ No newline at end of file
diff --git a/python/resources/inspectionDescriptions/PyMissingOrEmptyDocstringInspection.html b/python/resources/inspectionDescriptions/PyMissingOrEmptyDocstringInspection.html
new file mode 100644
index 000000000000..e17f82b55107
--- /dev/null
+++ b/python/resources/inspectionDescriptions/PyMissingOrEmptyDocstringInspection.html
@@ -0,0 +1,7 @@
+
+
+
+This inspection detects lack of docstring and an empty docstring.
+
+
+
\ No newline at end of file
diff --git a/python/src/META-INF/python-core.xml b/python/src/META-INF/python-core.xml
index 9aef78114434..d343115692a7 100644
--- a/python/src/META-INF/python-core.xml
+++ b/python/src/META-INF/python-core.xml
@@ -321,7 +321,8 @@
-
+
+
diff --git a/python/src/com/jetbrains/python/PyBundle.properties b/python/src/com/jetbrains/python/PyBundle.properties
index 4a4bc7fa2b14..672864b648ac 100644
--- a/python/src/com/jetbrains/python/PyBundle.properties
+++ b/python/src/com/jetbrains/python/PyBundle.properties
@@ -414,8 +414,13 @@ INSP.NAME.default.argument=Default argument is mutable
# PyRaisingNewStyleClassInspection
INSP.NAME.raising.new.style.class=Raising a new style class
-# PyDocstringInspection
-INSP.NAME.docstring=Missing, empty or incorrect docstring
+# PyIncorrectDocstringInspection
+INSP.NAME.incorrect.docstring=Incorrect docstring
+INSP.missing.parameter.in.docstring=Missing parameter {0} in docstring
+INSP.unexpected.parameter.in.docstring=Unexpected parameter {0} in docstring
+
+# PyMissingOrEmptyDocstringInspection
+INSP.NAME.missing.or.empty.docstring=Missing or empty docstring
INSP.no.docstring=Missing docstring
INSP.empty.docstring=Empty docstring
diff --git a/python/src/com/jetbrains/python/console/ConsoleVisitorFilter.java b/python/src/com/jetbrains/python/console/ConsoleVisitorFilter.java
index 83bed83570f5..23cc90f3c11a 100644
--- a/python/src/com/jetbrains/python/console/ConsoleVisitorFilter.java
+++ b/python/src/com/jetbrains/python/console/ConsoleVisitorFilter.java
@@ -33,7 +33,8 @@ public class ConsoleVisitorFilter implements PythonVisitorFilter {
//inspections
if (visitorClass == PyUnusedLocalInspection.class || visitorClass == PyUnboundLocalVariableInspection.class ||
visitorClass == PyStatementEffectInspection.class || visitorClass == PySingleQuotedDocstringInspection.class ||
- visitorClass == PyDocstringInspection.class || visitorClass == PyMandatoryEncodingInspection.class) {
+ visitorClass == PyIncorrectDocstringInspection.class || visitorClass == PyMissingOrEmptyDocstringInspection.class ||
+ visitorClass == PyMandatoryEncodingInspection.class) {
return false;
}
diff --git a/python/src/com/jetbrains/python/documentation/doctest/PyDocstringVisitorFilter.java b/python/src/com/jetbrains/python/documentation/doctest/PyDocstringVisitorFilter.java
index 4f8d7496e693..29226b0d2759 100644
--- a/python/src/com/jetbrains/python/documentation/doctest/PyDocstringVisitorFilter.java
+++ b/python/src/com/jetbrains/python/documentation/doctest/PyDocstringVisitorFilter.java
@@ -38,12 +38,13 @@ public class PyDocstringVisitorFilter implements PythonVisitorFilter {
if (visitorClass == PyArgumentListInspection.class) {
return false;
}
- if (visitorClass == PyDocstringInspection.class || visitorClass == PyStatementEffectInspection.class ||
+ if (visitorClass == PyIncorrectDocstringInspection.class || visitorClass == PyMissingOrEmptyDocstringInspection.class ||
visitorClass == PyUnboundLocalVariableInspection.class || visitorClass == PyUnnecessaryBackslashInspection.class ||
visitorClass == PyByteLiteralInspection.class || visitorClass == PyNonAsciiCharInspection.class ||
visitorClass == PyPackageRequirementsInspection.class || visitorClass == PyMandatoryEncodingInspection.class ||
visitorClass == PyInterpreterInspection.class || visitorClass == PyDocstringTypesInspection.class ||
- visitorClass == PySingleQuotedDocstringInspection.class || visitorClass == PyClassHasNoInitInspection.class) {
+ visitorClass == PySingleQuotedDocstringInspection.class || visitorClass == PyClassHasNoInitInspection.class ||
+ visitorClass == PyStatementEffectInspection.class) {
return false;
}
//annotators
diff --git a/python/src/com/jetbrains/python/inspections/PyBaseDocstringInspection.java b/python/src/com/jetbrains/python/inspections/PyBaseDocstringInspection.java
new file mode 100644
index 000000000000..ae9f371fb401
--- /dev/null
+++ b/python/src/com/jetbrains/python/inspections/PyBaseDocstringInspection.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2000-2015 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.jetbrains.python.inspections;
+
+import com.intellij.codeInspection.LocalInspectionToolSession;
+import com.intellij.codeInspection.ProblemsHolder;
+import com.intellij.openapi.extensions.Extensions;
+import com.jetbrains.python.psi.*;
+import com.jetbrains.python.testing.PythonUnitTestUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author Mikhail Golubev
+ */
+public abstract class PyBaseDocstringInspection extends PyInspection {
+ @NotNull
+ @Override
+ public abstract Visitor buildVisitor(@NotNull ProblemsHolder holder,
+ boolean isOnTheFly,
+ @NotNull LocalInspectionToolSession session);
+
+ protected static abstract class Visitor extends PyInspectionVisitor {
+ public Visitor(@Nullable ProblemsHolder holder, @NotNull LocalInspectionToolSession session) {
+ super(holder, session);
+ }
+
+ @Override
+ public void visitPyFile(@NotNull PyFile node) {
+ checkDocString(node);
+ }
+
+ @Override
+ public void visitPyFunction(@NotNull PyFunction node) {
+ if (PythonUnitTestUtil.isUnitTestCaseFunction(node)) return;
+ final PyClass containingClass = node.getContainingClass();
+ if (containingClass != null && PythonUnitTestUtil.isUnitTestCaseClass(containingClass)) return;
+ final Property property = node.getProperty();
+ if (property != null && (node == property.getSetter().valueOrNull() || node == property.getDeleter().valueOrNull())) {
+ return;
+ }
+ final String name = node.getName();
+ if (name != null && !name.startsWith("_")) checkDocString(node);
+ }
+
+ @Override
+ public void visitPyClass(@NotNull PyClass node) {
+ if (PythonUnitTestUtil.isUnitTestCaseClass(node)) return;
+ final String name = node.getName();
+ if (name == null || name.startsWith("_")) {
+ return;
+ }
+ checkDocString(node);
+ }
+
+ protected void checkDocString(@NotNull PyDocStringOwner node) {
+ for (PyInspectionExtension extension : Extensions.getExtensions(PyInspectionExtension.EP_NAME)) {
+ if (extension.ignoreMissingDocstring(node)) {
+ return;
+ }
+ }
+ }
+ }
+}
diff --git a/python/src/com/jetbrains/python/inspections/PyDocstringInspection.java b/python/src/com/jetbrains/python/inspections/PyDocstringInspection.java
deleted file mode 100644
index e66cc2d0c731..000000000000
--- a/python/src/com/jetbrains/python/inspections/PyDocstringInspection.java
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * Copyright 2000-2014 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.jetbrains.python.inspections;
-
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.intellij.codeInspection.LocalInspectionToolSession;
-import com.intellij.codeInspection.ProblemsHolder;
-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.jetbrains.python.PyBundle;
-import com.jetbrains.python.documentation.docstrings.DocStringUtil;
-import com.jetbrains.python.documentation.docstrings.PlainDocString;
-import com.jetbrains.python.inspections.quickfix.DocstringQuickFix;
-import com.jetbrains.python.psi.*;
-import com.jetbrains.python.testing.PythonUnitTestUtil;
-import com.jetbrains.python.toolbox.Substring;
-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.Map;
-
-/**
- * @author Alexey.Ivanov
- */
-public class PyDocstringInspection extends PyInspection {
- @Nls
- @NotNull
- @Override
- public String getDisplayName() {
- return PyBundle.message("INSP.NAME.docstring");
- }
-
- @Override
- public boolean isEnabledByDefault() {
- return false;
- }
-
- @NotNull
- @Override
- public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder,
- boolean isOnTheFly,
- @NotNull LocalInspectionToolSession session) {
- return new Visitor(holder, session);
- }
-
- public static class Visitor extends PyInspectionVisitor {
- public Visitor(@Nullable ProblemsHolder holder, @NotNull LocalInspectionToolSession session) {
- super(holder, session);
- }
-
- @Override
- public void visitPyFile(@NotNull PyFile node) {
- checkDocString(node);
- }
-
- @Override
- public void visitPyFunction(@NotNull PyFunction node) {
- if (PythonUnitTestUtil.isUnitTestCaseFunction(node)) return;
- final PyClass containingClass = node.getContainingClass();
- if (containingClass != null && PythonUnitTestUtil.isUnitTestCaseClass(containingClass)) return;
- final Property property = node.getProperty();
- if (property != null && (node == property.getSetter().valueOrNull() || node == property.getDeleter().valueOrNull())) {
- return;
- }
- final String name = node.getName();
- if (name != null && !name.startsWith("_")) checkDocString(node);
- }
-
- @Override
- public void visitPyClass(@NotNull PyClass node) {
- if (PythonUnitTestUtil.isUnitTestCaseClass(node)) return;
- final String name = node.getName();
- if (name == null || name.startsWith("_")) {
- return;
- }
- for (PyInspectionExtension extension : Extensions.getExtensions(PyInspectionExtension.EP_NAME)) {
- if (extension.ignoreMissingDocstring(node)) {
- return;
- }
- }
- checkDocString(node);
- }
-
- private void checkDocString(@NotNull PyDocStringOwner node) {
- final PyStringLiteralExpression docStringExpression = node.getDocStringExpression();
- if (docStringExpression == null) {
- PsiElement marker = null;
- if (node instanceof PyClass) {
- final ASTNode n = ((PyClass)node).getNameNode();
- if (n != null) marker = n.getPsi();
- }
- else if (node instanceof PyFunction) {
- final ASTNode n = ((PyFunction)node).getNameNode();
- if (n != null) marker = n.getPsi();
- }
- else if (node instanceof PyFile) {
- final TextRange tr = new TextRange(0, 0);
- final ProblemsHolder holder = getHolder();
- if (holder != null) {
- holder.registerProblem(node, tr, PyBundle.message("INSP.no.docstring"));
- }
- return;
- }
- if (marker == null) marker = node;
- if (node instanceof PyFunction || (node instanceof PyClass && ((PyClass)node).findInitOrNew(false, null) != null)) {
- registerProblem(marker, PyBundle.message("INSP.no.docstring"), new DocstringQuickFix(null, null));
- }
- else {
- registerProblem(marker, PyBundle.message("INSP.no.docstring"));
- }
- }
- else {
- final boolean registered = checkParameters(node, docStringExpression);
- if (!registered && StringUtil.isEmptyOrSpaces(docStringExpression.getStringValue())) {
- registerProblem(docStringExpression, PyBundle.message("INSP.empty.docstring"));
- }
- }
- }
-
- private boolean checkParameters(@NotNull PyDocStringOwner pyDocStringOwner, @NotNull PyStringLiteralExpression node) {
- final String text = node.getText();
- if (text == null) {
- return false;
- }
-
- final StructuredDocString docString = DocStringUtil.parse(text, node);
-
- if (docString instanceof PlainDocString) {
- return false;
- }
-
- if (pyDocStringOwner instanceof PyFunction) {
- final PyParameter[] realParams = ((PyFunction)pyDocStringOwner).getParameterList().getParameters();
-
- final List missingParams = getMissingParams(docString, realParams);
- boolean registered = false;
- if (!missingParams.isEmpty()) {
- for (PyNamedParameter param : missingParams) {
- registerProblem(param, "Missing parameter " + param.getName() + " in docstring", new DocstringQuickFix(param, null));
- }
- registered = true;
- }
- final List unexpectedParams = getUnexpectedParams(docString, realParams);
- if (!unexpectedParams.isEmpty()) {
- for (Substring param : unexpectedParams) {
- final ProblemsHolder holder = getHolder();
-
- if (holder != null) {
- holder.registerProblem(node, param.getTextRange(),
- "Unexpected parameter " + param + " in docstring",
- new DocstringQuickFix(null, param.getValue()));
- }
- }
- registered = true;
- }
- return registered;
- }
- return false;
- }
-
- @NotNull
- private static List getUnexpectedParams(@NotNull StructuredDocString docString, @NotNull PyParameter[] realParams) {
- final Map unexpected = Maps.newHashMap();
-
- for (Substring s : docString.getParameterSubstrings()) {
- unexpected.put(s.toString(), s);
- }
-
- for (PyParameter p : realParams) {
- if (unexpected.containsKey(p.getName())) {
- unexpected.remove(p.getName());
- }
- }
- return Lists.newArrayList(unexpected.values());
- }
-
- @NotNull
- private static List getMissingParams(@NotNull StructuredDocString docString, @NotNull PyParameter[] realParams) {
- final List missing = new ArrayList();
- final List docStringParameters = docString.getParameters();
- for (PyParameter p : realParams) {
- if (p.isSelf() || !(p instanceof PyNamedParameter)) {
- continue;
- }
- if (!docStringParameters.contains(p.getName())) {
- missing.add((PyNamedParameter)p);
- }
- }
- return missing;
- }
- }
-}
diff --git a/python/src/com/jetbrains/python/inspections/PyIncorrectDocstringInspection.java b/python/src/com/jetbrains/python/inspections/PyIncorrectDocstringInspection.java
new file mode 100644
index 000000000000..4d4359a796c5
--- /dev/null
+++ b/python/src/com/jetbrains/python/inspections/PyIncorrectDocstringInspection.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2000-2014 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.jetbrains.python.inspections;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.intellij.codeInspection.LocalInspectionToolSession;
+import com.intellij.codeInspection.ProblemsHolder;
+import com.jetbrains.python.PyBundle;
+import com.jetbrains.python.documentation.docstrings.DocStringUtil;
+import com.jetbrains.python.documentation.docstrings.PlainDocString;
+import com.jetbrains.python.inspections.quickfix.DocstringQuickFix;
+import com.jetbrains.python.psi.*;
+import com.jetbrains.python.toolbox.Substring;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Mikhail Golubev
+ * @author Alexey.Ivanov
+ */
+public class PyIncorrectDocstringInspection extends PyBaseDocstringInspection {
+ @NotNull
+ @Override
+ public Visitor buildVisitor(@NotNull ProblemsHolder holder,
+ boolean isOnTheFly,
+ @NotNull LocalInspectionToolSession session) {
+ return new Visitor(holder, session) {
+
+ @Override
+ protected void checkDocString(@NotNull PyDocStringOwner node) {
+ super.checkDocString(node);
+ final PyStringLiteralExpression docStringExpression1 = node.getDocStringExpression();
+ if (docStringExpression1 != null) {
+ checkParameters(node, docStringExpression1);
+ }
+ }
+
+ private boolean checkParameters(@NotNull PyDocStringOwner pyDocStringOwner, @NotNull PyStringLiteralExpression node) {
+ final String text = node.getText();
+ if (text == null) {
+ return false;
+ }
+
+ final StructuredDocString docString = DocStringUtil.parse(text, node);
+
+ if (docString instanceof PlainDocString) {
+ return false;
+ }
+
+ if (pyDocStringOwner instanceof PyFunction) {
+ final PyParameter[] realParams = ((PyFunction)pyDocStringOwner).getParameterList().getParameters();
+
+ final List missingParams = getMissingParams(docString, realParams);
+ boolean registered = false;
+ if (!missingParams.isEmpty()) {
+ for (PyNamedParameter param : missingParams) {
+ registerProblem(param,
+ PyBundle.message("INSP.missing.parameter.in.docstring", param.getName()),
+ new DocstringQuickFix(param, null));
+ }
+ registered = true;
+ }
+ final List unexpectedParams = getUnexpectedParams(docString, realParams);
+ if (!unexpectedParams.isEmpty()) {
+ for (Substring param : unexpectedParams) {
+ final ProblemsHolder holder = getHolder();
+
+ if (holder != null) {
+ holder.registerProblem(node, param.getTextRange(),
+ PyBundle.message("INSP.unexpected.parameter.in.docstring", param),
+ new DocstringQuickFix(null, param.getValue()));
+ }
+ }
+ registered = true;
+ }
+ return registered;
+ }
+ return false;
+ }
+ };
+ }
+
+ @NotNull
+ private static List getMissingParams(@NotNull StructuredDocString docString, @NotNull PyParameter[] realParams) {
+ final List missing = new ArrayList();
+ final List docStringParameters = docString.getParameters();
+ for (PyParameter p : realParams) {
+ if (p.isSelf() || !(p instanceof PyNamedParameter)) {
+ continue;
+ }
+ if (!docStringParameters.contains(p.getName())) {
+ missing.add((PyNamedParameter)p);
+ }
+ }
+ return missing;
+ }
+
+ @NotNull
+ private static List getUnexpectedParams(@NotNull StructuredDocString docString, @NotNull PyParameter[] realParams) {
+ final Map unexpected = Maps.newHashMap();
+
+ for (Substring s : docString.getParameterSubstrings()) {
+ unexpected.put(s.toString(), s);
+ }
+
+ for (PyParameter p : realParams) {
+ if (unexpected.containsKey(p.getName())) {
+ unexpected.remove(p.getName());
+ }
+ }
+ return Lists.newArrayList(unexpected.values());
+ }
+}
diff --git a/python/src/com/jetbrains/python/inspections/PyInspectionsSuppressor.java b/python/src/com/jetbrains/python/inspections/PyInspectionsSuppressor.java
index 545c1b37e46d..495577ae4cbb 100644
--- a/python/src/com/jetbrains/python/inspections/PyInspectionsSuppressor.java
+++ b/python/src/com/jetbrains/python/inspections/PyInspectionsSuppressor.java
@@ -18,12 +18,13 @@ 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();
+ private static final String PY_INCORRECT_DOCSTRING_INSPECTION_ID = new PyIncorrectDocstringInspection().getID();
+ private static final String PY_MISSING_OR_EMPTY_DOCSTRING_INSPECTION_ID = new PyMissingOrEmptyDocstringInspection().getID();
@NotNull
@Override
public SuppressQuickFix[] getSuppressActions(@Nullable PsiElement element, @NotNull String toolId) {
- if (PY_DOCSTRING_INSPECTION_ID.equals(toolId)) {
+ if (PY_INCORRECT_DOCSTRING_INSPECTION_ID.equals(toolId) || PY_MISSING_OR_EMPTY_DOCSTRING_INSPECTION_ID.equals(toolId)) {
return new SuppressQuickFix[]{
new PySuppressInspectionFix(toolId, "Suppress for function", PyFunction.class),
new PySuppressInspectionFix(toolId, "Suppress for class", PyClass.class)
diff --git a/python/src/com/jetbrains/python/inspections/PyMissingOrEmptyDocstringInspection.java b/python/src/com/jetbrains/python/inspections/PyMissingOrEmptyDocstringInspection.java
new file mode 100644
index 000000000000..18dc367886ec
--- /dev/null
+++ b/python/src/com/jetbrains/python/inspections/PyMissingOrEmptyDocstringInspection.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2000-2015 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.jetbrains.python.inspections;
+
+import com.intellij.codeInspection.LocalInspectionToolSession;
+import com.intellij.codeInspection.ProblemsHolder;
+import com.intellij.lang.ASTNode;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.psi.PsiElement;
+import com.jetbrains.python.PyBundle;
+import com.jetbrains.python.inspections.quickfix.DocstringQuickFix;
+import com.jetbrains.python.psi.*;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author Mikhail Golubev
+ */
+public class PyMissingOrEmptyDocstringInspection extends PyBaseDocstringInspection {
+ @NotNull
+ @Override
+ public Visitor buildVisitor(@NotNull ProblemsHolder holder,
+ boolean isOnTheFly,
+ @NotNull LocalInspectionToolSession session) {
+ return new Visitor(holder, session) {
+ @Override
+ protected void checkDocString(@NotNull PyDocStringOwner node) {
+ super.checkDocString(node);
+ final PyStringLiteralExpression docStringExpression = node.getDocStringExpression();
+ if (docStringExpression == null) {
+ PsiElement marker = null;
+ if (node instanceof PyClass) {
+ final ASTNode n = ((PyClass)node).getNameNode();
+ if (n != null) marker = n.getPsi();
+ }
+ else if (node instanceof PyFunction) {
+ final ASTNode n = ((PyFunction)node).getNameNode();
+ if (n != null) marker = n.getPsi();
+ }
+ else if (node instanceof PyFile) {
+ final TextRange tr = new TextRange(0, 0);
+ final ProblemsHolder holder = getHolder();
+ if (holder != null) {
+ holder.registerProblem(node, tr, PyBundle.message("INSP.no.docstring"));
+ }
+ return;
+ }
+ if (marker == null) marker = node;
+ if (node instanceof PyFunction || (node instanceof PyClass && ((PyClass)node).findInitOrNew(false, null) != null)) {
+ registerProblem(marker, PyBundle.message("INSP.no.docstring"), new DocstringQuickFix(null, null));
+ }
+ else {
+ registerProblem(marker, PyBundle.message("INSP.no.docstring"));
+ }
+ }
+ else if (StringUtil.isEmptyOrSpaces(docStringExpression.getStringValue())) {
+ registerProblem(docStringExpression, PyBundle.message("INSP.empty.docstring"));
+ }
+ }
+ };
+ }
+}
diff --git a/python/testData/inspections/PyDocstringInspection/expected.xml b/python/testData/inspections/PyDocstringInspection/expected.xml
index 7b6e3c7d5079..a6288b4c4703 100644
--- a/python/testData/inspections/PyDocstringInspection/expected.xml
+++ b/python/testData/inspections/PyDocstringInspection/expected.xml
@@ -25,4 +25,14 @@
13
Empty docstring
+
+ test.py
+ 17
+ Missing docstring
+
+
+ test.py
+ 19
+ Missing docstring
+
\ No newline at end of file
diff --git a/python/testData/inspections/PyDocstringInspection/src/test.py b/python/testData/inspections/PyDocstringInspection/src/test.py
index 42a2cdf8cdf8..3c28c7aa380e 100644
--- a/python/testData/inspections/PyDocstringInspection/src/test.py
+++ b/python/testData/inspections/PyDocstringInspection/src/test.py
@@ -11,4 +11,18 @@ class B:
def bar():
""""""
- pass
\ No newline at end of file
+ pass
+
+
+class C:
+ @property
+ def x(self):
+ return 42
+
+ @x.setter
+ def x(self, value):
+ pass
+
+ @x.deleter
+ def x(self):
+ pass
\ No newline at end of file
diff --git a/python/testData/inspections/PyDocstringParametersInspection/test.py b/python/testData/inspections/PyDocstringParametersInspection/test.py
index 9e3e31e83b0a..3bd62cf93e11 100644
--- a/python/testData/inspections/PyDocstringParametersInspection/test.py
+++ b/python/testData/inspections/PyDocstringParametersInspection/test.py
@@ -41,17 +41,4 @@ def foo(a, cC:
- @property
- def x(self):
- return 42
-
- @x.setter
- def x(self, value):
- pass
-
- @x.deleter
- def x(self):
- pass
\ No newline at end of file
diff --git a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java
index 24c4b90f513b..c76a713877fb 100644
--- a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java
+++ b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java
@@ -441,7 +441,7 @@ public class PyQuickFixTest extends PyTestCase {
getIndentOptions().INDENT_SIZE = 2;
runWithDocStringFormat(DocStringFormat.EPYTEXT, new Runnable() {
public void run() {
- doInspectionTest(PyDocstringInspection.class, PyBundle.message("QFIX.docstring.add.$0", "b"), true, true);
+ doInspectionTest(PyIncorrectDocstringInspection.class, PyBundle.message("QFIX.docstring.add.$0", "b"), true, true);
}
});
}
@@ -450,7 +450,7 @@ public class PyQuickFixTest extends PyTestCase {
getIndentOptions().INDENT_SIZE = 2;
runWithDocStringFormat(DocStringFormat.EPYTEXT, new Runnable() {
public void run() {
- doInspectionTest(PyDocstringInspection.class, PyBundle.message("QFIX.docstring.remove.$0", "c"), true, true);
+ doInspectionTest(PyIncorrectDocstringInspection.class, PyBundle.message("QFIX.docstring.remove.$0", "c"), true, true);
}
});
}
@@ -459,7 +459,7 @@ public class PyQuickFixTest extends PyTestCase {
public void testDocstringParams2() {
runWithDocStringFormat(DocStringFormat.EPYTEXT, new Runnable() {
public void run() {
- doInspectionTest(PyDocstringInspection.class, PyBundle.message("QFIX.docstring.add.$0", "ham"), true, true);
+ doInspectionTest(PyIncorrectDocstringInspection.class, PyBundle.message("QFIX.docstring.add.$0", "ham"), true, true);
}
});
}
@@ -469,7 +469,7 @@ public class PyQuickFixTest extends PyTestCase {
runWithDocStringFormat(DocStringFormat.GOOGLE, new Runnable() {
@Override
public void run() {
- doInspectionTest(PyDocstringInspection.class, PyBundle.message("QFIX.docstring.add.$0", "b"), true, true);
+ doInspectionTest(PyIncorrectDocstringInspection.class, PyBundle.message("QFIX.docstring.add.$0", "b"), true, true);
}
});
}
@@ -479,7 +479,7 @@ public class PyQuickFixTest extends PyTestCase {
runWithDocStringFormat(DocStringFormat.GOOGLE, new Runnable() {
@Override
public void run() {
- doInspectionTest(PyDocstringInspection.class, PyBundle.message("QFIX.docstring.remove.$0", "c"), true, true);
+ doInspectionTest(PyIncorrectDocstringInspection.class, PyBundle.message("QFIX.docstring.remove.$0", "c"), true, true);
}
});
}
@@ -489,7 +489,7 @@ public class PyQuickFixTest extends PyTestCase {
runWithDocStringFormat(DocStringFormat.GOOGLE, new Runnable() {
@Override
public void run() {
- doInspectionTest(PyDocstringInspection.class, PyBundle.message("QFIX.docstring.remove.$0", "c"), true, true);
+ doInspectionTest(PyIncorrectDocstringInspection.class, PyBundle.message("QFIX.docstring.remove.$0", "c"), true, true);
}
});
}
@@ -499,7 +499,7 @@ public class PyQuickFixTest extends PyTestCase {
runWithDocStringFormat(DocStringFormat.GOOGLE, new Runnable() {
@Override
public void run() {
- doInspectionTest(PyDocstringInspection.class, PyBundle.message("QFIX.docstring.remove.$0", "args"), true, true);
+ doInspectionTest(PyIncorrectDocstringInspection.class, PyBundle.message("QFIX.docstring.remove.$0", "args"), true, true);
}
});
}
@@ -509,7 +509,7 @@ public class PyQuickFixTest extends PyTestCase {
runWithDocStringFormat(DocStringFormat.GOOGLE, new Runnable() {
@Override
public void run() {
- doInspectionTest(PyDocstringInspection.class, PyBundle.message("QFIX.docstring.remove.$0", "kwargs"), true, true);
+ doInspectionTest(PyIncorrectDocstringInspection.class, PyBundle.message("QFIX.docstring.remove.$0", "kwargs"), true, true);
}
});
}
@@ -519,7 +519,7 @@ public class PyQuickFixTest extends PyTestCase {
runWithDocStringFormat(DocStringFormat.GOOGLE, new Runnable() {
@Override
public void run() {
- doInspectionTest(PyDocstringInspection.class, PyBundle.message("QFIX.docstring.add.$0", "args"), true, true);
+ doInspectionTest(PyIncorrectDocstringInspection.class, PyBundle.message("QFIX.docstring.add.$0", "args"), true, true);
}
});
}
@@ -529,7 +529,7 @@ public class PyQuickFixTest extends PyTestCase {
runWithDocStringFormat(DocStringFormat.GOOGLE, new Runnable() {
@Override
public void run() {
- doInspectionTest(PyDocstringInspection.class, PyBundle.message("QFIX.docstring.add.$0", "kwargs"), true, true);
+ doInspectionTest(PyIncorrectDocstringInspection.class, PyBundle.message("QFIX.docstring.add.$0", "kwargs"), true, true);
}
});
}
@@ -539,7 +539,7 @@ public class PyQuickFixTest extends PyTestCase {
runWithDocStringFormat(DocStringFormat.NUMPY, new Runnable() {
@Override
public void run() {
- doInspectionTest(PyDocstringInspection.class, PyBundle.message("QFIX.docstring.remove.$0", "x"), true, true);
+ doInspectionTest(PyIncorrectDocstringInspection.class, PyBundle.message("QFIX.docstring.remove.$0", "x"), true, true);
}
});
}
@@ -549,7 +549,7 @@ public class PyQuickFixTest extends PyTestCase {
runWithDocStringFormat(DocStringFormat.NUMPY, new Runnable() {
@Override
public void run() {
- doInspectionTest(PyDocstringInspection.class, PyBundle.message("QFIX.docstring.remove.$0", "y"), true, true);
+ doInspectionTest(PyIncorrectDocstringInspection.class, PyBundle.message("QFIX.docstring.remove.$0", "y"), true, true);
}
});
}
@@ -559,7 +559,7 @@ public class PyQuickFixTest extends PyTestCase {
runWithDocStringFormat(DocStringFormat.NUMPY, new Runnable() {
@Override
public void run() {
- doInspectionTest(PyDocstringInspection.class, PyBundle.message("QFIX.docstring.remove.$0", "z"), true, true);
+ doInspectionTest(PyIncorrectDocstringInspection.class, PyBundle.message("QFIX.docstring.remove.$0", "z"), true, true);
}
});
}
@@ -569,7 +569,7 @@ public class PyQuickFixTest extends PyTestCase {
runWithDocStringFormat(DocStringFormat.NUMPY, new Runnable() {
@Override
public void run() {
- doInspectionTest(PyDocstringInspection.class, PyBundle.message("QFIX.docstring.remove.$0", "args"), true, true);
+ doInspectionTest(PyIncorrectDocstringInspection.class, PyBundle.message("QFIX.docstring.remove.$0", "args"), true, true);
}
});
}
diff --git a/python/testSrc/com/jetbrains/python/PythonInspectionsTest.java b/python/testSrc/com/jetbrains/python/PythonInspectionsTest.java
index eebdd4e3262f..b85751460054 100644
--- a/python/testSrc/com/jetbrains/python/PythonInspectionsTest.java
+++ b/python/testSrc/com/jetbrains/python/PythonInspectionsTest.java
@@ -143,7 +143,7 @@ public class PythonInspectionsTest extends PyTestCase {
}
public void testPyDocstringInspection() {
- LocalInspectionTool inspection = new PyDocstringInspection();
+ LocalInspectionTool inspection = new PyMissingOrEmptyDocstringInspection();
doTest(getTestName(false), inspection);
}
@@ -151,7 +151,7 @@ public class PythonInspectionsTest extends PyTestCase {
public void testPyDocstringParametersInspection() {
runWithDocStringFormat(DocStringFormat.EPYTEXT, new Runnable() {
public void run() {
- doHighlightingTest(PyDocstringInspection.class, LanguageLevel.PYTHON33);
+ doHighlightingTest(PyIncorrectDocstringInspection.class, LanguageLevel.PYTHON33);
}
});
}
@@ -160,7 +160,7 @@ public class PythonInspectionsTest extends PyTestCase {
public void testGoogleDocstringParametersInspection() {
runWithDocStringFormat(DocStringFormat.GOOGLE, new Runnable() {
public void run() {
- doHighlightingTest(PyDocstringInspection.class, LanguageLevel.PYTHON33);
+ doHighlightingTest(PyIncorrectDocstringInspection.class, LanguageLevel.PYTHON33);
}
});
}