PY-20744 Warn about illegal targets for variable annotations

This commit is contained in:
Mikhail Golubev
2016-09-16 08:16:26 +03:00
parent 6d86b858ca
commit a48947ab01
4 changed files with 72 additions and 0 deletions
@@ -33,6 +33,7 @@ public class PyAnnotatingVisitor implements Annotator {
private static final Logger LOGGER = Logger.getInstance(PyAnnotatingVisitor.class.getName());
private static final Class[] ANNOTATOR_CLASSES = new Class[] {
AssignTargetAnnotator.class,
TypeAnnotationTargetAnnotator.class,
ParameterListAnnotator.class,
HighlightingAnnotator.class,
ReturnAnnotator.class,
@@ -0,0 +1,55 @@
/*
* Copyright 2000-2016 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.validation;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.PyPsiUtils;
import org.jetbrains.annotations.NotNull;
/**
* @author Mikhail Golubev
*/
public class TypeAnnotationTargetAnnotator extends PyAnnotator {
@Override
public void visitPyAssignmentStatement(PyAssignmentStatement node) {
if (node.getAnnotation() != null && LanguageLevel.forElement(node).isAtLeast(LanguageLevel.PYTHON36)) {
if (node.getRawTargets().length > 1) {
getHolder().createErrorAnnotation(node, "Variable annotation cannot be used in assignment with multiple targets");
}
final PyExpression target = node.getLeftHandSideExpression();
if (target != null) {
checkAnnotationTarget(target);
}
}
}
@Override
public void visitPyTypeDeclarationStatement(PyTypeDeclarationStatement node) {
if (node.getAnnotation() != null && LanguageLevel.forElement(node).isAtLeast(LanguageLevel.PYTHON36)) {
checkAnnotationTarget(node.getTarget());
}
}
private void checkAnnotationTarget(@NotNull PyExpression expression) {
final PyExpression innerExpr = PyPsiUtils.flattenParens(expression);
if (innerExpr instanceof PyTupleExpression || innerExpr instanceof PyListLiteralExpression) {
getHolder().createErrorAnnotation(innerExpr, "Variable annotation cannot be combined with tuple unpacking");
}
else if (innerExpr != null && !(innerExpr instanceof PyTargetExpression || innerExpr instanceof PySubscriptionExpression)) {
getHolder().createErrorAnnotation(innerExpr, "Illegal target for variable annotation");
}
}
}
@@ -0,0 +1,12 @@
(x): int = 42
(((x))): float
x['foo']: str
f(42).attr: dict
<error descr="Illegal target for variable annotation">2 ** 8</error>: int
<error descr="Illegal target for variable annotation">f()</error>: bool
<error descr="Variable annotation cannot be combined with tuple unpacking">x, y, z</error>: Tuple[int, ...]
(<error descr="Variable annotation cannot be combined with tuple unpacking">x, y, z</error>): Tuple[int, int, int]
<error descr="Variable annotation cannot be combined with tuple unpacking">[x, y, z]</error>: Tuple[Any, Any, Any]
<error descr="Variable annotation cannot be combined with tuple unpacking">x, *xs</error>: tuple = range(10)
<error descr="Variable annotation cannot be used in assignment with multiple targets">x:int = y = 42</error>
@@ -297,6 +297,10 @@ public class PythonHighlightingTest extends PyTestCase {
doTest(LanguageLevel.PYTHON35, true, false);
}
public void testIllegalVariableAnnotationTarget() {
doTest(LanguageLevel.PYTHON36, true, false);
}
// ---
private void doTest(final LanguageLevel languageLevel, final boolean checkWarnings, final boolean checkInfos) {
PythonLanguageLevelPusher.setForcedLanguageLevel(myFixture.getProject(), languageLevel);