mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
PY-17211 Split PyDocstringInspection in two
The first inspection PyIncorrectDocstringInspection checks that docstring contains valid parameters and it's enabled by default. Another - PyMissingOrEmptyDocstringInspection - ensures that docstring exists for every suitable declaration. It often annoys users, so it's turned off initially. Additionally I moved messages from these inspections in PyBundle.
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
<html>
|
||||
<body>
|
||||
<span style="font-family: verdana,serif;">
|
||||
This inspection detects mismatched parameters in docstring, lack of docstring and an empty docstring.
|
||||
</span>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,7 @@
|
||||
<html>
|
||||
<body>
|
||||
<span style="font-family: verdana,serif;">
|
||||
This inspection detects mismatched parameters in docstring.
|
||||
</span>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,7 @@
|
||||
<html>
|
||||
<body>
|
||||
<span style="font-family: verdana,serif;">
|
||||
This inspection detects lack of docstring and an empty docstring.
|
||||
</span>
|
||||
</body>
|
||||
</html>
|
||||
@@ -321,7 +321,8 @@
|
||||
<localInspection language="Python" shortName="PyExceptionInheritInspection" suppressId="PyExceptionInherit" bundle="com.jetbrains.python.PyBundle" key="INSP.NAME.exception.not.inherit" groupKey="INSP.GROUP.python" enabledByDefault="true" level="WARNING" implementationClass="com.jetbrains.python.inspections.PyExceptionInheritInspection"/>
|
||||
<localInspection language="Python" shortName="PyDefaultArgumentInspection" suppressId="PyDefaultArgument" bundle="com.jetbrains.python.PyBundle" key="INSP.NAME.default.argument" groupKey="INSP.GROUP.python" enabledByDefault="true" level="WARNING" implementationClass="com.jetbrains.python.inspections.PyDefaultArgumentInspection"/>
|
||||
<localInspection language="Python" shortName="PyRaisingNewStyleClassInspection" suppressId="PyRaisingNewStyleClass" bundle="com.jetbrains.python.PyBundle" key="INSP.NAME.raising.new.style.class" groupKey="INSP.GROUP.python" enabledByDefault="true" level="WARNING" implementationClass="com.jetbrains.python.inspections.PyRaisingNewStyleClassInspection"/>
|
||||
<localInspection language="Python" shortName="PyDocstringInspection" suppressId="PyDocstring" bundle="com.jetbrains.python.PyBundle" key="INSP.NAME.docstring" groupKey="INSP.GROUP.python" enabledByDefault="false" level="WEAK WARNING" implementationClass="com.jetbrains.python.inspections.PyDocstringInspection"/>
|
||||
<localInspection language="Python" shortName="PyIncorrectDocstringInspection" suppressId="PyIncorrectDocstring" bundle="com.jetbrains.python.PyBundle" key="INSP.NAME.incorrect.docstring" groupKey="INSP.GROUP.python" enabledByDefault="true" level="WEAK WARNING" implementationClass="com.jetbrains.python.inspections.PyIncorrectDocstringInspection"/>
|
||||
<localInspection language="Python" shortName="PyMissingOrEmptyDocstringInspection" suppressId="PyMissingOrEmptyDocstring" bundle="com.jetbrains.python.PyBundle" key="INSP.NAME.missing.or.empty.docstring" groupKey="INSP.GROUP.python" enabledByDefault="false" level="WEAK WARNING" implementationClass="com.jetbrains.python.inspections.PyMissingOrEmptyDocstringInspection"/>
|
||||
<localInspection language="Python" shortName="PyUnboundLocalVariableInspection" suppressId="PyUnboundLocalVariable" bundle="com.jetbrains.python.PyBundle" key="INSP.NAME.unbound" groupKey="INSP.GROUP.python" enabledByDefault="true" level="WARNING" implementationClass="com.jetbrains.python.inspections.PyUnboundLocalVariableInspection"/>
|
||||
<localInspection language="Python" shortName="PyStatementEffectInspection" suppressId="PyStatementEffect" bundle="com.jetbrains.python.PyBundle" key="INSP.NAME.statement.effect" groupKey="INSP.GROUP.python" enabledByDefault="true" level="WARNING" implementationClass="com.jetbrains.python.inspections.PyStatementEffectInspection"/>
|
||||
<localInspection language="Python" shortName="PySimplifyBooleanCheckInspection" suppressId="PySimplifyBooleanCheck" bundle="com.jetbrains.python.PyBundle" key="INSP.NAME.check.can.be.simplified" groupKey="INSP.GROUP.python" enabledByDefault="true" level="WEAK WARNING" implementationClass="com.jetbrains.python.inspections.PySimplifyBooleanCheckInspection"/>
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<PyNamedParameter> 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<Substring> 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<Substring> getUnexpectedParams(@NotNull StructuredDocString docString, @NotNull PyParameter[] realParams) {
|
||||
final Map<String, Substring> 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<PyNamedParameter> getMissingParams(@NotNull StructuredDocString docString, @NotNull PyParameter[] realParams) {
|
||||
final List<PyNamedParameter> missing = new ArrayList<PyNamedParameter>();
|
||||
final List<String> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<PyNamedParameter> 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<Substring> 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<PyNamedParameter> getMissingParams(@NotNull StructuredDocString docString, @NotNull PyParameter[] realParams) {
|
||||
final List<PyNamedParameter> missing = new ArrayList<PyNamedParameter>();
|
||||
final List<String> 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<Substring> getUnexpectedParams(@NotNull StructuredDocString docString, @NotNull PyParameter[] realParams) {
|
||||
final Map<String, Substring> 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());
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -25,4 +25,14 @@
|
||||
<line>13</line>
|
||||
<description>Empty docstring</description>
|
||||
</problem>
|
||||
<problem>
|
||||
<file>test.py</file>
|
||||
<line>17</line>
|
||||
<description>Missing docstring</description>
|
||||
</problem>
|
||||
<problem>
|
||||
<file>test.py</file>
|
||||
<line>19</line>
|
||||
<description>Missing docstring</description>
|
||||
</problem>
|
||||
</problems>
|
||||
@@ -11,4 +11,18 @@ class B:
|
||||
|
||||
def bar():
|
||||
""""""
|
||||
pass
|
||||
pass
|
||||
|
||||
|
||||
class C:
|
||||
@property
|
||||
def x(self):
|
||||
return 42
|
||||
|
||||
@x.setter
|
||||
def x(self, value):
|
||||
pass
|
||||
|
||||
@x.deleter
|
||||
def x(self):
|
||||
pass
|
||||
@@ -41,17 +41,4 @@ def foo(a, <weak_warning descr="Missing parameter c in docstring">c</weak_warnin
|
||||
@return:
|
||||
"""
|
||||
pass
|
||||
|
||||
class <weak_warning descr="Missing docstring">C</weak_warning>:
|
||||
@property
|
||||
def <weak_warning descr="Missing docstring">x</weak_warning>(self):
|
||||
return 42
|
||||
|
||||
@x.setter
|
||||
def x(self, value):
|
||||
pass
|
||||
|
||||
@x.deleter
|
||||
def x(self):
|
||||
pass
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user