From 4a5bd5cf1cc50c0f7a93625bbce7d9b9af8a436a Mon Sep 17 00:00:00 2001 From: Andrea Guarino Date: Mon, 9 Sep 2019 14:28:19 +0200 Subject: [PATCH 1/2] Strongly Typed AST: introduce Annotated Assignment and rework Assignment --- .../checks/hotspots/DebugModeCheck.java | 2 +- .../api/tree/PyAnnotatedAssignmentTree.java | 40 +++++++++ .../api/tree/PyAssignmentStatementTree.java | 2 +- .../sonar/python/api/tree/PyTreeVisitor.java | 2 + .../java/org/sonar/python/api/tree/Tree.java | 2 + .../sonar/python/tree/BaseTreeVisitor.java | 10 ++- .../tree/PyAnnotatedAssignmentTreeImpl.java | 88 +++++++++++++++++++ .../tree/PyAssignmentStatementTreeImpl.java | 13 +-- .../sonar/python/tree/PythonTreeMaker.java | 28 ++++-- .../python/tree/BaseTreeVisitorTest.java | 12 +++ .../python/tree/PythonTreeMakerTest.java | 63 ++++++++++++- 11 files changed, 245 insertions(+), 17 deletions(-) create mode 100644 python-squid/src/main/java/org/sonar/python/api/tree/PyAnnotatedAssignmentTree.java create mode 100644 python-squid/src/main/java/org/sonar/python/tree/PyAnnotatedAssignmentTreeImpl.java diff --git a/python-checks/src/main/java/org/sonar/python/checks/hotspots/DebugModeCheck.java b/python-checks/src/main/java/org/sonar/python/checks/hotspots/DebugModeCheck.java index b630249806..a65f6b0941 100644 --- a/python-checks/src/main/java/org/sonar/python/checks/hotspots/DebugModeCheck.java +++ b/python-checks/src/main/java/org/sonar/python/checks/hotspots/DebugModeCheck.java @@ -63,7 +63,7 @@ public void initialize(Context context) { PyAssignmentStatementTree assignmentStatementTree = (PyAssignmentStatementTree) ctx.syntaxNode(); for (PyExpressionListTree lhsExpression : assignmentStatementTree.lhsExpressions()) { boolean isDebugProperties = lhsExpression.expressions().stream().anyMatch(DebugModeCheck::isDebugIdentifier); - if (isDebugProperties && assignmentStatementTree.assignedValues().stream().anyMatch(DebugModeCheck::isTrueLiteral)) { + if (isDebugProperties && isTrueLiteral(assignmentStatementTree.assignedValue())) { ctx.addIssue(assignmentStatementTree, MESSAGE); } } diff --git a/python-squid/src/main/java/org/sonar/python/api/tree/PyAnnotatedAssignmentTree.java b/python-squid/src/main/java/org/sonar/python/api/tree/PyAnnotatedAssignmentTree.java new file mode 100644 index 0000000000..563f1f134e --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/api/tree/PyAnnotatedAssignmentTree.java @@ -0,0 +1,40 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.api.tree; + +import com.sonar.sslr.api.Token; +import javax.annotation.CheckForNull; + +public interface PyAnnotatedAssignmentTree extends PyStatementTree { + // Using PyExpressionTree because variable can be `identifier | attributeref | subscription | slicing` + PyExpressionTree variable(); + + // TODO: move to a dedicated tree for typed expression + Token colonToken(); + + // TODO: move to a dedicated tree for typed expression + PyExpressionTree annotation(); + + @CheckForNull + Token equalToken(); + + @CheckForNull + PyExpressionTree assignedValue(); +} diff --git a/python-squid/src/main/java/org/sonar/python/api/tree/PyAssignmentStatementTree.java b/python-squid/src/main/java/org/sonar/python/api/tree/PyAssignmentStatementTree.java index 50b7e8a861..5c5ab5fdd5 100644 --- a/python-squid/src/main/java/org/sonar/python/api/tree/PyAssignmentStatementTree.java +++ b/python-squid/src/main/java/org/sonar/python/api/tree/PyAssignmentStatementTree.java @@ -23,7 +23,7 @@ import java.util.List; public interface PyAssignmentStatementTree extends PyStatementTree { - List assignedValues(); + PyExpressionTree assignedValue(); List equalTokens(); diff --git a/python-squid/src/main/java/org/sonar/python/api/tree/PyTreeVisitor.java b/python-squid/src/main/java/org/sonar/python/api/tree/PyTreeVisitor.java index 1e41097ab2..2e934b5a39 100644 --- a/python-squid/src/main/java/org/sonar/python/api/tree/PyTreeVisitor.java +++ b/python-squid/src/main/java/org/sonar/python/api/tree/PyTreeVisitor.java @@ -150,4 +150,6 @@ public interface PyTreeVisitor { void visitDictCompExpression(PyDictCompExpressionTreeImpl tree); void visitCompoundAssignment(PyCompoundAssignmentStatementTree pyCompoundAssignmentStatementTree); + + void visitAnnotatedAssignment(PyAnnotatedAssignmentTree pyAnnotatedAssignmentTree); } diff --git a/python-squid/src/main/java/org/sonar/python/api/tree/Tree.java b/python-squid/src/main/java/org/sonar/python/api/tree/Tree.java index 92f6fe70b5..9525de979c 100644 --- a/python-squid/src/main/java/org/sonar/python/api/tree/Tree.java +++ b/python-squid/src/main/java/org/sonar/python/api/tree/Tree.java @@ -49,6 +49,8 @@ enum Kind { ARG_LIST(PyArgListTree.class), + ANNOTATED_ASSIGNMENT(PyAnnotatedAssignmentTree.class), + ASSERT_STMT(PyAssertStatementTree.class), ASSIGNMENT_STMT(PyAssignmentStatementTree.class), diff --git a/python-squid/src/main/java/org/sonar/python/tree/BaseTreeVisitor.java b/python-squid/src/main/java/org/sonar/python/tree/BaseTreeVisitor.java index f459a16ac7..ef4f6fa55e 100644 --- a/python-squid/src/main/java/org/sonar/python/tree/BaseTreeVisitor.java +++ b/python-squid/src/main/java/org/sonar/python/tree/BaseTreeVisitor.java @@ -23,6 +23,7 @@ import javax.annotation.Nullable; import org.sonar.python.api.tree.PyAliasedNameTree; import org.sonar.python.api.tree.PyArgListTree; +import org.sonar.python.api.tree.PyAnnotatedAssignmentTree; import org.sonar.python.api.tree.PyArgumentTree; import org.sonar.python.api.tree.PyAssertStatementTree; import org.sonar.python.api.tree.PyAssignmentStatementTree; @@ -318,7 +319,7 @@ public void visitArgument(PyArgumentTree pyArgumentTree) { @Override public void visitAssignmentStatement(PyAssignmentStatementTree pyAssignmentStatementTree) { scan(pyAssignmentStatementTree.lhsExpressions()); - scan(pyAssignmentStatementTree.assignedValues()); + scan(pyAssignmentStatementTree.assignedValue()); } @Override @@ -472,4 +473,11 @@ public void visitCompoundAssignment(PyCompoundAssignmentStatementTree pyCompound scan(pyCompoundAssignmentStatementTree.lhsExpression()); scan(pyCompoundAssignmentStatementTree.rhsExpression()); } + + @Override + public void visitAnnotatedAssignment(PyAnnotatedAssignmentTree pyAnnotatedAssignmentTree) { + scan(pyAnnotatedAssignmentTree.variable()); + scan(pyAnnotatedAssignmentTree.annotation()); + scan(pyAnnotatedAssignmentTree.assignedValue()); + } } diff --git a/python-squid/src/main/java/org/sonar/python/tree/PyAnnotatedAssignmentTreeImpl.java b/python-squid/src/main/java/org/sonar/python/tree/PyAnnotatedAssignmentTreeImpl.java new file mode 100644 index 0000000000..fa714d989a --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/tree/PyAnnotatedAssignmentTreeImpl.java @@ -0,0 +1,88 @@ +/* + * SonarQube Python Plugin + * Copyright (C) 2011-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.python.tree; + +import com.sonar.sslr.api.Token; +import java.util.Arrays; +import java.util.List; +import javax.annotation.CheckForNull; +import org.sonar.python.api.tree.PyAnnotatedAssignmentTree; +import org.sonar.python.api.tree.PyExpressionTree; +import org.sonar.python.api.tree.PyTreeVisitor; +import org.sonar.python.api.tree.Tree; + +public class PyAnnotatedAssignmentTreeImpl extends PyTree implements PyAnnotatedAssignmentTree { + private final PyExpressionTree variable; + private final Token colonToken; + private final PyExpressionTree annotation; + private final Token equalToken; + private final PyExpressionTree assignedValue; + + public PyAnnotatedAssignmentTreeImpl(PyExpressionTree variable, Token colonToken, PyExpressionTree annotation, Token equalToken, PyExpressionTree assignedValue) { + super(variable.firstToken(), assignedValue != null ? assignedValue.lastToken() : annotation.lastToken()); + this.variable = variable; + this.colonToken = colonToken; + this.annotation = annotation; + this.equalToken = equalToken; + this.assignedValue = assignedValue; + } + + @Override + public PyExpressionTree variable() { + return variable; + } + + @Override + public Token colonToken() { + return colonToken; + } + + @Override + public PyExpressionTree annotation() { + return annotation; + } + + @CheckForNull + @Override + public Token equalToken() { + return equalToken; + } + + @CheckForNull + @Override + public PyExpressionTree assignedValue() { + return assignedValue; + } + + @Override + public void accept(PyTreeVisitor visitor) { + visitor.visitAnnotatedAssignment(this); + } + + @Override + public List children() { + return Arrays.asList(variable, annotation, assignedValue); + } + + @Override + public Kind getKind() { + return Kind.ANNOTATED_ASSIGNMENT; + } +} diff --git a/python-squid/src/main/java/org/sonar/python/tree/PyAssignmentStatementTreeImpl.java b/python-squid/src/main/java/org/sonar/python/tree/PyAssignmentStatementTreeImpl.java index 395885645b..e97bb8ace0 100644 --- a/python-squid/src/main/java/org/sonar/python/tree/PyAssignmentStatementTreeImpl.java +++ b/python-squid/src/main/java/org/sonar/python/tree/PyAssignmentStatementTreeImpl.java @@ -21,6 +21,7 @@ import com.sonar.sslr.api.AstNode; import com.sonar.sslr.api.Token; +import java.util.Collections; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -33,18 +34,18 @@ public class PyAssignmentStatementTreeImpl extends PyTree implements PyAssignmentStatementTree { private final List assignTokens; private final List lhsExpressions; - private final List assignedValues; + private final PyExpressionTree assignedValue; - public PyAssignmentStatementTreeImpl(AstNode astNode, List assignTokens, List lhsExpressions, List assignedValue) { + public PyAssignmentStatementTreeImpl(AstNode astNode, List assignTokens, List lhsExpressions,PyExpressionTree assignedValue) { super(astNode); this.assignTokens = assignTokens; this.lhsExpressions = lhsExpressions; - this.assignedValues = assignedValue; + this.assignedValue = assignedValue; } @Override - public List assignedValues() { - return assignedValues; + public PyExpressionTree assignedValue() { + return assignedValue; } @Override @@ -69,6 +70,6 @@ public Kind getKind() { @Override public List children() { - return Stream.of(lhsExpressions, assignedValues).flatMap(List::stream).collect(Collectors.toList()); + return Stream.of(lhsExpressions, Collections.singletonList(assignedValue)).flatMap(List::stream).collect(Collectors.toList()); } } diff --git a/python-squid/src/main/java/org/sonar/python/tree/PythonTreeMaker.java b/python-squid/src/main/java/org/sonar/python/tree/PythonTreeMaker.java index 8f5b2937f8..7dc9444b7a 100644 --- a/python-squid/src/main/java/org/sonar/python/tree/PythonTreeMaker.java +++ b/python-squid/src/main/java/org/sonar/python/tree/PythonTreeMaker.java @@ -34,6 +34,7 @@ import org.sonar.python.api.PythonPunctuator; import org.sonar.python.api.PythonTokenType; import org.sonar.python.api.tree.PyAliasedNameTree; +import org.sonar.python.api.tree.PyAnnotatedAssignmentTree; import org.sonar.python.api.tree.PyArgListTree; import org.sonar.python.api.tree.PyArgumentTree; import org.sonar.python.api.tree.PyAssertStatementTree; @@ -160,6 +161,9 @@ PyStatementTree statement(AstNode astNode) { if (astNode.is(PythonGrammar.NONLOCAL_STMT)) { return nonlocalStatement(astNode); } + if (astNode.is(PythonGrammar.EXPRESSION_STMT) && astNode.hasDirectChildren(PythonGrammar.ANNASSIGN)) { + return annotatedAssignment(astNode); + } if (astNode.is(PythonGrammar.EXPRESSION_STMT) && astNode.hasDirectChildren(PythonPunctuator.ASSIGN)) { return assignment(astNode); } @@ -184,6 +188,21 @@ PyStatementTree statement(AstNode astNode) { throw new IllegalStateException("Statement " + astNode.getType() + " not correctly translated to strongly typed AST"); } + public PyAnnotatedAssignmentTree annotatedAssignment(AstNode astNode) { + AstNode annAssign = astNode.getFirstChild(PythonGrammar.ANNASSIGN); + AstNode colonTokenNode = annAssign.getFirstChild(PythonPunctuator.COLON); + PyExpressionTree variable = exprListOrTestList(astNode.getFirstChild(PythonGrammar.TESTLIST_STAR_EXPR)); + PyExpressionTree annotation = expression(annAssign.getFirstChild(PythonGrammar.TEST)); + AstNode equalTokenNode = annAssign.getFirstChild(PythonPunctuator.ASSIGN); + Token equalToken = null; + PyExpressionTree assignedValue = null; + if (equalTokenNode != null) { + equalToken = equalTokenNode.getToken(); + assignedValue = expression(equalTokenNode.getNextSibling()); + } + return new PyAnnotatedAssignmentTreeImpl(variable, colonTokenNode.getToken(), annotation, equalToken, assignedValue); + } + private PyStatementListTree getStatementListFromSuite(AstNode suite) { return new PyStatementListTreeImpl(suite, getStatementsFromSuite(suite), suite.getTokens()); } @@ -502,7 +521,6 @@ public PyWhileStatementTreeImpl whileStatement(AstNode astNode) { } public PyExpressionStatementTree expressionStatement(AstNode astNode) { - // TODO: handle ANNASSIGN, b.sequence(AUGASSIGN, b.firstOf(YIELD_EXPR, TESTLIST)), b.zeroOrMore("=", b.firstOf(YIELD_EXPR, TESTLIST_STAR_EXPR) List expressions = astNode.getFirstChild(PythonGrammar.TESTLIST_STAR_EXPR).getChildren(PythonGrammar.TEST, PythonGrammar.STAR_EXPR).stream() .map(this::expression) .collect(Collectors.toList()); @@ -517,11 +535,9 @@ public PyAssignmentStatementTree assignment(AstNode astNode) { assignTokens.add(assignNode.getToken()); lhsExpressions.add(expressionList(assignNode.getPreviousSibling())); } - AstNode assignedValues = assignNodes.get(assignNodes.size() - 1).getNextSibling(); - List expressions = assignedValues.getChildren(PythonGrammar.TEST, PythonGrammar.STAR_EXPR).stream() - .map(this::expression) - .collect(Collectors.toList()); - return new PyAssignmentStatementTreeImpl(astNode, assignTokens, lhsExpressions, expressions); + AstNode assignedValueNode = assignNodes.get(assignNodes.size() - 1).getNextSibling(); + PyExpressionTree assignedValue = assignedValueNode.is(PythonGrammar.YIELD_EXPR) ? yieldExpression(assignedValueNode) : exprListOrTestList(assignedValueNode); + return new PyAssignmentStatementTreeImpl(astNode, assignTokens, lhsExpressions, assignedValue); } public PyCompoundAssignmentStatementTree compoundAssignment(AstNode astNode) { diff --git a/python-squid/src/test/java/org/sonar/python/tree/BaseTreeVisitorTest.java b/python-squid/src/test/java/org/sonar/python/tree/BaseTreeVisitorTest.java index 1b3f5a6a44..90866e7f6e 100644 --- a/python-squid/src/test/java/org/sonar/python/tree/BaseTreeVisitorTest.java +++ b/python-squid/src/test/java/org/sonar/python/tree/BaseTreeVisitorTest.java @@ -23,6 +23,7 @@ import java.util.function.Function; import org.junit.Test; import org.sonar.python.api.PythonGrammar; +import org.sonar.python.api.tree.PyAnnotatedAssignmentTree; import org.sonar.python.api.tree.PyAssertStatementTree; import org.sonar.python.api.tree.PyAssignmentStatementTree; import org.sonar.python.api.tree.PyAwaitExpressionTree; @@ -203,6 +204,17 @@ public void assignement_stmt() { verify(visitor).visitExpressionList(tree.lhsExpressions().get(0)); } + @Test + public void annotated_assignment() { + setRootRule(PythonGrammar.EXPRESSION_STMT); + PyAnnotatedAssignmentTree tree = parse("a : int = b", treeMaker::annotatedAssignment); + BaseTreeVisitor visitor = spy(BaseTreeVisitor.class); + visitor.visitAnnotatedAssignment(tree); + verify(visitor).visitName((PyNameTree) tree.variable()); + verify(visitor).visitName((PyNameTree) tree.annotation()); + verify(visitor).visitName((PyNameTree) tree.assignedValue()); + } + @Test public void lambda() { setRootRule(PythonGrammar.LAMBDEF); diff --git a/python-squid/src/test/java/org/sonar/python/tree/PythonTreeMakerTest.java b/python-squid/src/test/java/org/sonar/python/tree/PythonTreeMakerTest.java index 46a01a904b..3d81eade4f 100644 --- a/python-squid/src/test/java/org/sonar/python/tree/PythonTreeMakerTest.java +++ b/python-squid/src/test/java/org/sonar/python/tree/PythonTreeMakerTest.java @@ -28,6 +28,7 @@ import org.junit.Test; import org.sonar.python.api.PythonGrammar; import org.sonar.python.api.tree.PyAliasedNameTree; +import org.sonar.python.api.tree.PyAnnotatedAssignmentTree; import org.sonar.python.api.tree.PyArgumentTree; import org.sonar.python.api.tree.PyAssertStatementTree; import org.sonar.python.api.tree.PyAssignmentStatementTree; @@ -47,6 +48,7 @@ import org.sonar.python.api.tree.PyElseStatementTree; import org.sonar.python.api.tree.PyExceptClauseTree; import org.sonar.python.api.tree.PyExecStatementTree; +import org.sonar.python.api.tree.PyExpressionListTree; import org.sonar.python.api.tree.PyExpressionStatementTree; import org.sonar.python.api.tree.PyExpressionTree; import org.sonar.python.api.tree.PyFileInputTree; @@ -770,7 +772,7 @@ public void assignement_statement() { setRootRule(PythonGrammar.EXPRESSION_STMT); AstNode astNode = p.parse("x = y"); PyAssignmentStatementTree pyAssignmentStatement = treeMaker.assignment(astNode); - PyNameTree assigned = (PyNameTree) pyAssignmentStatement.assignedValues().get(0); + PyNameTree assigned = (PyNameTree) pyAssignmentStatement.assignedValue(); PyNameTree lhs = (PyNameTree) pyAssignmentStatement.lhsExpressions().get(0).expressions().get(0); assertThat(assigned.name()).isEqualTo("y"); assertThat(lhs.name()).isEqualTo("x"); @@ -778,13 +780,70 @@ public void assignement_statement() { astNode = p.parse("x = y = z"); pyAssignmentStatement = treeMaker.assignment(astNode); + assertThat(pyAssignmentStatement.equalTokens()).hasSize(2); assertThat(pyAssignmentStatement.children()).hasSize(3); - assigned = (PyNameTree) pyAssignmentStatement.assignedValues().get(0); + assigned = (PyNameTree) pyAssignmentStatement.assignedValue(); lhs = (PyNameTree) pyAssignmentStatement.lhsExpressions().get(0).expressions().get(0); PyNameTree lhs2 = (PyNameTree) pyAssignmentStatement.lhsExpressions().get(1).expressions().get(0); assertThat(assigned.name()).isEqualTo("z"); assertThat(lhs.name()).isEqualTo("x"); assertThat(lhs2.name()).isEqualTo("y"); + + astNode = p.parse("a,b = x"); + pyAssignmentStatement = treeMaker.assignment(astNode); + assertThat(pyAssignmentStatement.children()).hasSize(2); + assigned = (PyNameTree) pyAssignmentStatement.assignedValue(); + List expressions = pyAssignmentStatement.lhsExpressions().get(0).expressions(); + assertThat(assigned.name()).isEqualTo("x"); + assertThat(expressions.get(0).getKind()).isEqualTo(Tree.Kind.NAME); + assertThat(expressions.get(1).getKind()).isEqualTo(Tree.Kind.NAME); + + astNode = p.parse("x = a,b"); + pyAssignmentStatement = treeMaker.assignment(astNode); + assertThat(pyAssignmentStatement.children()).hasSize(2); + expressions = pyAssignmentStatement.lhsExpressions().get(0).expressions(); + assertThat(expressions.get(0).getKind()).isEqualTo(Tree.Kind.NAME); + assertThat(pyAssignmentStatement.assignedValue().getKind()).isEqualTo(Tree.Kind.TUPLE); + + astNode = p.parse("x = yield 1"); + pyAssignmentStatement = treeMaker.assignment(astNode); + assertThat(pyAssignmentStatement.children()).hasSize(2); + expressions = pyAssignmentStatement.lhsExpressions().get(0).expressions(); + assertThat(expressions.get(0).getKind()).isEqualTo(Tree.Kind.NAME); + assertThat(pyAssignmentStatement.assignedValue().getKind()).isEqualTo(Tree.Kind.YIELD_EXPR); + + // FIXME: lhs expression list shouldn't allow yield expressions. We need to change the grammar + astNode = p.parse("x = yield 1 = y"); + pyAssignmentStatement = treeMaker.assignment(astNode); + assertThat(pyAssignmentStatement.children()).hasSize(3); + List lhsExpressions = pyAssignmentStatement.lhsExpressions(); + assertThat(lhsExpressions.get(1).expressions().get(0).getKind()).isEqualTo(Tree.Kind.YIELD_EXPR); + assertThat(pyAssignmentStatement.assignedValue().getKind()).isEqualTo(Tree.Kind.NAME); + } + + @Test + public void annotated_assignment() { + setRootRule(PythonGrammar.EXPRESSION_STMT); + AstNode astNode = p.parse("x : string = 1"); + PyAnnotatedAssignmentTree annAssign = treeMaker.annotatedAssignment(astNode); + assertThat(annAssign.getKind()).isEqualTo(Tree.Kind.ANNOTATED_ASSIGNMENT); + assertThat(annAssign.children()).hasSize(3); + assertThat(annAssign.variable().getKind()).isEqualTo(Tree.Kind.NAME); + assertThat(((PyNameTree) annAssign.variable()).name()).isEqualTo("x"); + assertThat(annAssign.assignedValue().getKind()).isEqualTo(Tree.Kind.NUMERIC_LITERAL); + assertThat(annAssign.equalToken().getValue()).isEqualTo("="); + assertThat(annAssign.annotation().getKind()).isEqualTo(Tree.Kind.NAME); + assertThat(((PyNameTree) annAssign.annotation()).name()).isEqualTo("string"); + + setRootRule(PythonGrammar.EXPRESSION_STMT); + astNode = p.parse("x : string"); + annAssign = treeMaker.annotatedAssignment(astNode); + assertThat(annAssign.variable().getKind()).isEqualTo(Tree.Kind.NAME); + assertThat(((PyNameTree) annAssign.variable()).name()).isEqualTo("x"); + assertThat(annAssign.annotation().getKind()).isEqualTo(Tree.Kind.NAME); + assertThat(((PyNameTree) annAssign.annotation()).name()).isEqualTo("string"); + assertThat(annAssign.assignedValue()).isNull(); + assertThat(annAssign.equalToken()).isNull(); } @Test From 3f2fbd1dd5b79032968da241c7cb280f09297ac2 Mon Sep 17 00:00:00 2001 From: Andrea Guarino Date: Mon, 9 Sep 2019 15:35:09 +0200 Subject: [PATCH 2/2] increase coverage --- .../src/test/java/org/sonar/python/tree/PythonTreeMakerTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/python-squid/src/test/java/org/sonar/python/tree/PythonTreeMakerTest.java b/python-squid/src/test/java/org/sonar/python/tree/PythonTreeMakerTest.java index 3d81eade4f..505210c22b 100644 --- a/python-squid/src/test/java/org/sonar/python/tree/PythonTreeMakerTest.java +++ b/python-squid/src/test/java/org/sonar/python/tree/PythonTreeMakerTest.java @@ -834,6 +834,7 @@ public void annotated_assignment() { assertThat(annAssign.equalToken().getValue()).isEqualTo("="); assertThat(annAssign.annotation().getKind()).isEqualTo(Tree.Kind.NAME); assertThat(((PyNameTree) annAssign.annotation()).name()).isEqualTo("string"); + assertThat(annAssign.colonToken().getValue()).isEqualTo(":"); setRootRule(PythonGrammar.EXPRESSION_STMT); astNode = p.parse("x : string");