PY-16412 Don't suggest the intention for targets that doesn't allow annotations

This commit is contained in:
Mikhail Golubev
2018-02-05 21:41:59 +03:00
committed by Andrey Vlasovskikh
parent 95227c365b
commit fc3d1b7beb
9 changed files with 66 additions and 3 deletions
@@ -63,10 +63,22 @@ public class PyAnnotateVariableTypeIntention extends PyBaseIntentionAction {
return StreamEx.of(PyUtil.multiResolveTopPriority(elementAtCaret, resolveContext))
.select(PyTargetExpression.class)
.filter(target -> !index.isInLibraryClasses(target.getContainingFile().getVirtualFile()))
.filter(target -> canBeAnnotated(target))
.filter(target -> !isAnnotated(target, typeEvalContext))
.toList();
}
private static boolean canBeAnnotated(@NotNull PyTargetExpression target) {
final PsiElement directParent = target.getParent();
if (directParent instanceof PyImportElement ||
directParent instanceof PyComprehensionForComponent ||
directParent instanceof PyGlobalStatement ||
directParent instanceof PyNonlocalStatement) {
return false;
}
return PsiTreeUtil.getParentOfType(target, PyWithItem.class, PyAssignmentStatement.class, PyForPart.class) != null;
}
private static boolean isAnnotated(@NotNull PyTargetExpression target, @NotNull TypeEvalContext context) {
// TODO filter out fields explicitly annotated as Any
return new PyTypingTypeProvider().getReferenceType(target, context, null) != null;
@@ -105,9 +117,9 @@ public class PyAnnotateVariableTypeIntention extends PyBaseIntentionAction {
private static String generateNestedTypeHint(@NotNull PyTargetExpression target) {
final TypeEvalContext context = TypeEvalContext.userInitiated(target.getProject(), target.getContainingFile());
final StringBuilder builder = new StringBuilder();
final PyElement validTargetContainer = PsiTreeUtil.getParentOfType(target, PyForPart.class, PyWithItem.class, PyAssignmentStatement.class);
assert validTargetContainer != null;
final PsiElement topmostTarget = PsiTreeUtil.findPrevParent(validTargetContainer, target);
final PyElement validTargetParent = PsiTreeUtil.getParentOfType(target, PyForPart.class, PyWithItem.class, PyAssignmentStatement.class);
assert validTargetParent != null;
final PsiElement topmostTarget = PsiTreeUtil.findPrevParent(validTargetParent, target);
generateNestedTypeHint(topmostTarget, context, builder);
return builder.toString();
}
@@ -0,0 +1 @@
[v<caret>ar for var in range(10)]
@@ -0,0 +1,3 @@
def func():
global var
v<caret>ar
@@ -0,0 +1 @@
from sys import path as v<caret>ar
@@ -0,0 +1,3 @@
def func(x):
var: int = x
v<caret>ar
@@ -0,0 +1,3 @@
def func(x):
var = x # type: int
v<caret>ar
@@ -0,0 +1,3 @@
def func():
nonlocal var
v<caret>ar
@@ -9,6 +9,35 @@ import org.jetbrains.annotations.NotNull;
* @author Mikhail Golubev
*/
public class PyAnnotateVariableTypeIntentionTest extends PyIntentionTestCase {
public void testNotSuggestedForLocalAssignmentTargetWithAnnotation() {
doNegativeTest();
}
public void testNotSuggestedForLocalAssignmentTargetWithAheadOfTimeAnnotation() {
doNegativeTest();
}
public void testNotSuggestedForLocalAssignmentTargetWithTypeComment() {
doNegativeTest();
}
public void testNotSuggestedForComprehensionTarget() {
doNegativeTest();
}
public void testNotSuggestedForImportTarget() {
doNegativeTest();
}
public void testNotSuggestedForGlobalTarget() {
doNegativeTest();
}
public void testNotSuggestedForNonlocalTarget() {
doNegativeTest();
}
public void testAnnotationLocalSimpleAssignmentTarget() {
doTestAnnotation();
}
@@ -68,4 +97,8 @@ public class PyAnnotateVariableTypeIntentionTest extends PyIntentionTestCase {
private void doTest(@NotNull LanguageLevel languageLevel) {
doTest(PyBundle.message("INTN.annotate.types"), languageLevel);
}
private void doNegativeTest() {
runWithLanguageLevel(LanguageLevel.PYTHON36, () -> doNegativeTest(PyBundle.message("INTN.annotate.types")));
}
}