diff --git a/python/src/com/jetbrains/python/validation/PyAnnotatingVisitor.java b/python/src/com/jetbrains/python/validation/PyAnnotatingVisitor.java index d5d1964b0faa..150bebc9a1c6 100644 --- a/python/src/com/jetbrains/python/validation/PyAnnotatingVisitor.java +++ b/python/src/com/jetbrains/python/validation/PyAnnotatingVisitor.java @@ -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, diff --git a/python/src/com/jetbrains/python/validation/TypeAnnotationTargetAnnotator.java b/python/src/com/jetbrains/python/validation/TypeAnnotationTargetAnnotator.java new file mode 100644 index 000000000000..d54de6f85e1c --- /dev/null +++ b/python/src/com/jetbrains/python/validation/TypeAnnotationTargetAnnotator.java @@ -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"); + } + } +} diff --git a/python/testData/highlighting/illegalVariableAnnotationTarget.py b/python/testData/highlighting/illegalVariableAnnotationTarget.py new file mode 100644 index 000000000000..cbaecf67be4e --- /dev/null +++ b/python/testData/highlighting/illegalVariableAnnotationTarget.py @@ -0,0 +1,12 @@ +(x): int = 42 +(((x))): float +x['foo']: str +f(42).attr: dict + +2 ** 8: int +f(): bool +x, y, z: Tuple[int, ...] +(x, y, z): Tuple[int, int, int] +[x, y, z]: Tuple[Any, Any, Any] +x, *xs: tuple = range(10) +x:int = y = 42 \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PythonHighlightingTest.java b/python/testSrc/com/jetbrains/python/PythonHighlightingTest.java index b9c6daeaf54e..5991a6d16135 100644 --- a/python/testSrc/com/jetbrains/python/PythonHighlightingTest.java +++ b/python/testSrc/com/jetbrains/python/PythonHighlightingTest.java @@ -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);