PY-16412 Prohibit annotations for structural types

since they cannot be expression in PEP 484 compatible format (yet).
This commit is contained in:
Mikhail Golubev
2018-02-05 21:41:59 +03:00
committed by Andrey Vlasovskikh
parent 7af0d59f92
commit eedca22cbb
8 changed files with 68 additions and 4 deletions
@@ -295,6 +295,8 @@ INTN.convert.static.method.to.function=Convert static method to function
#PyConvertMethodToPropertyIntention
INTN.convert.method.to.property=Convert method to property
INTN.annotate.variable.type.PEP484.incompatible.type=The type ''{0}'' cannot be expressed in PEP 484 format
# Conflict checker
CONFLICT.name.$0.obscured=Name ''{0}'' obscured by local definitions
CONFLICT.name.$0.obscured.cannot.convert=Name ''{0}'' obscured. Cannot convert.
@@ -2,6 +2,7 @@
package com.jetbrains.python.codeInsight.intentions;
import com.intellij.codeInsight.TargetElementUtil;
import com.intellij.codeInsight.hint.HintManager;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectFileIndex;
@@ -17,6 +18,7 @@ import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
import com.jetbrains.python.codeInsight.dataflow.scope.Scope;
import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
import com.jetbrains.python.codeInsight.intentions.PyTypeHintGenerationUtil.AnnotationInfo;
import com.jetbrains.python.codeInsight.intentions.PyTypeHintGenerationUtil.Pep484IncompatibleTypeException;
import com.jetbrains.python.documentation.PythonDocumentationProvider;
import com.jetbrains.python.documentation.doctest.PyDocstringFile;
import com.jetbrains.python.psi.*;
@@ -192,11 +194,16 @@ public class PyAnnotateVariableTypeIntention extends PyBaseIntentionAction {
final List<PyTargetExpression> targets = findSuitableTargetsUnderCaret(project, editor, file);
assert targets.size() == 1;
final PyTargetExpression annotationTarget = targets.get(0);
if (preferSyntacticAnnotation(annotationTarget)) {
insertVariableAnnotation(annotationTarget);
try {
if (preferSyntacticAnnotation(annotationTarget)) {
insertVariableAnnotation(annotationTarget);
}
else {
insertVariableTypeComment(annotationTarget);
}
}
else {
insertVariableTypeComment(annotationTarget);
catch (Pep484IncompatibleTypeException e) {
HintManager.getInstance().showErrorHint(editor, e.getMessage());
}
}
@@ -207,6 +214,7 @@ public class PyAnnotateVariableTypeIntention extends PyBaseIntentionAction {
private static void insertVariableAnnotation(@NotNull PyTargetExpression target) {
final TypeEvalContext context = TypeEvalContext.userInitiated(target.getProject(), target.getContainingFile());
final PyType inferredType = context.getType(target);
PyTypeHintGenerationUtil.checkPep484Compatibility(inferredType);
final String annotationText = PythonDocumentationProvider.getTypeName(inferredType, context);
final AnnotationInfo info = new AnnotationInfo(annotationText, inferredType);
if (isInstanceAttribute(target, context)) {
@@ -277,6 +285,7 @@ public class PyAnnotateVariableTypeIntention extends PyBaseIntentionAction {
}
else if (target instanceof PyTypedElement) {
final PyType singleTargetType = context.getType((PyTypedElement)target);
PyTypeHintGenerationUtil.checkPep484Compatibility(singleTargetType);
final String singleTargetAnnotation = PythonDocumentationProvider.getTypeName(singleTargetType, context);
types.add(singleTargetType);
typeRanges.add(TextRange.from(builder.length(), singleTargetAnnotation.length()));
@@ -17,6 +17,7 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.ThrowableRunnable;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.codeInsight.imports.AddImportHelper;
import com.jetbrains.python.codeInsight.imports.AddImportHelper.ImportPriority;
import com.jetbrains.python.codeInsight.typing.PyTypingTypeProvider;
@@ -320,6 +321,38 @@ public class PyTypeHintGenerationUtil {
}
}
public static void checkPep484Compatibility(@Nullable PyType type) {
if (type == null ||
type instanceof PyNoneType ||
type instanceof PyCallableTypeImpl ||
type instanceof PyGenericType ||
type instanceof PyFunctionType) {
return;
}
else if (type instanceof PyUnionType) {
for (PyType memberType : ((PyUnionType)type).getMembers()) {
checkPep484Compatibility(memberType);
}
}
else if (type instanceof PyCollectionType) {
for (PyType typeParam : ((PyCollectionType)type).getElementTypes()) {
checkPep484Compatibility(typeParam);
}
}
else if (type instanceof PyClassType) {
// In this order since PyCollectionTypeImpl implements PyClassType
}
else {
throw new Pep484IncompatibleTypeException(PyBundle.message("INTN.annotate.variable.type.PEP484.incompatible.type", type.getName()));
}
}
public static final class Pep484IncompatibleTypeException extends RuntimeException {
public Pep484IncompatibleTypeException(String message) {
super(message);
}
}
public static final class AnnotationInfo {
private final String myAnnotationText;
private final List<PyType> myTypes;
@@ -0,0 +1,3 @@
def func(x):
x.foo()
va<caret>r = [x]
@@ -0,0 +1,3 @@
def func(x):
x.foo()
va<caret>r = x
@@ -215,6 +215,14 @@ public class PyAnnotateVariableTypeIntentionTest extends PyIntentionTestCase {
doAnnotationTest();
}
public void testAnnotationNotPossibleForStructuralType() {
doAnnotationTest();
}
public void testAnnotationNotPossibleForNestedStructuralType() {
doAnnotationTest();
}
private void doAnnotationTest() {
doTest(LanguageLevel.PYTHON36);
}