diff --git a/its/ruling/src/test/resources/expected/python-S5332.json b/its/ruling/src/test/resources/expected/python-S5332.json index 01a6e8ad72..10e1c51d01 100644 --- a/its/ruling/src/test/resources/expected/python-S5332.json +++ b/its/ruling/src/test/resources/expected/python-S5332.json @@ -45,12 +45,19 @@ ], 'project:buildbot-0.8.6p1/buildbot/test/unit/test_status_web_links.py':[ 46, +50, +51, +52, +53, 65, 73, 78, +178, 188, 190, 194, +205, +206, ], 'project:buildbot-0.8.6p1/buildbot/test/unit/test_steps_source_bzr.py':[ 32, @@ -236,6 +243,13 @@ 'project:buildbot-0.8.6p1/docs/bbdocs/ext.py':[ 154, ], +'project:buildbot-0.8.6p1/docs/conf.py':[ +105, +106, +], +'project:buildbot-0.8.6p1/setup.py':[ +209, +], 'project:buildbot-slave-0.8.6p1/buildslave/test/unit/test_commands_bk.py':[ 37, 49, @@ -253,24 +267,30 @@ 'project:buildbot-slave-0.8.6p1/buildslave/test/unit/test_commands_hg.py':[ 40, 52, +66, 86, 96, 98, 104, +118, 134, 146, 149, 155, +169, 190, 200, 202, 208, 216, +230, 246, 257, 260, 266, +280, 290, +304, 326, ], 'project:buildbot-slave-0.8.6p1/buildslave/test/unit/test_commands_svn.py':[ @@ -288,6 +308,9 @@ 36, 36, ], +'project:buildbot-slave-0.8.6p1/setup.py':[ +77, +], 'project:django-2.2.3/django/contrib/contenttypes/views.py':[ 41, ], @@ -312,6 +335,8 @@ 1430, ], 'project:django-2.2.3/django/utils/feedgenerator.py':[ +203, +259, 297, ], 'project:django-2.2.3/django/utils/html.py':[ @@ -320,11 +345,25 @@ 'project:docker-compose-1.24.1/compose/config/config.py':[ 146, ], +'project:docker-compose-1.24.1/tests/unit/cli/main_test.py':[ +160, +], 'project:docker-compose-1.24.1/tests/unit/config/config_test.py':[ 4951, 4963, ], 'project:docker-compose-1.24.1/tests/unit/service_test.py':[ +860, +862, +888, +890, +894, +896, +936, +966, +968, +979, +980, 1505, ], 'project:numpy-1.16.4/numpy/f2py/setup.py':[ @@ -347,6 +386,17 @@ 56, ], 'project:tornado-2.3/tornado/auth.py':[ +106, +108, +110, +117, +127, +129, +131, +134, +135, +136, +145, 180, 181, 182, @@ -384,6 +434,7 @@ 80, 83, 86, +126, ], 'project:tornado-2.3/tornado/test/httpclient_test.py':[ 143, @@ -475,6 +526,10 @@ 'project:twisted-12.1.0/twisted/lore/topfiles/setup.py':[ 23, ], +'project:twisted-12.1.0/twisted/lore/tree.py':[ +964, +965, +], 'project:twisted-12.1.0/twisted/mail/topfiles/setup.py':[ 38, ], @@ -520,6 +575,7 @@ ], 'project:twisted-12.1.0/twisted/web/template.py':[ 37, +66, ], 'project:twisted-12.1.0/twisted/web/test/test_cgi.py':[ 106, @@ -596,11 +652,13 @@ 2970, 2991, 3039, +3044, 3059, 3071, 3084, 3096, 3112, +3117, 3130, ], 'project:twisted-12.1.0/twisted/web/test/test_xml.py':[ @@ -637,6 +695,9 @@ 'project:twisted-12.1.0/twisted/words/protocols/jabber/xmlstream.py':[ 37, ], +'project:twisted-12.1.0/twisted/words/test/test_domish.py':[ +348, +], 'project:twisted-12.1.0/twisted/words/test/test_jabbererror.py':[ 13, 14, @@ -650,4 +711,7 @@ 'project:twisted-12.1.0/twisted/words/topfiles/setup.py':[ 41, ], +'project:twisted-12.1.0/twisted/words/xish/domish.py':[ +29, +], } diff --git a/its/ruling/src/test/resources/expected/python-S5443.json b/its/ruling/src/test/resources/expected/python-S5443.json index 24add8b263..cb05c60ffb 100644 --- a/its/ruling/src/test/resources/expected/python-S5443.json +++ b/its/ruling/src/test/resources/expected/python-S5443.json @@ -6,15 +6,26 @@ 'project:docker-compose-1.24.1/tests/acceptance/cli_test.py':[ 2617, ], +'project:docker-compose-1.24.1/tests/integration/project_test.py':[ +630, +631, +], 'project:docker-compose-1.24.1/tests/integration/service_test.py':[ 284, 303, 448, 706, ], +'project:docker-compose-1.24.1/tests/unit/bundle_test.py':[ +114, +135, +], 'project:docker-compose-1.24.1/tests/unit/config/config_test.py':[ +840, 1352, 1367, +3116, +3116, 3137, 4306, ], diff --git a/python-squid/src/main/java/org/sonar/python/api/tree/PyDictionaryLiteralTree.java b/python-squid/src/main/java/org/sonar/python/api/tree/PyDictionaryLiteralTree.java new file mode 100644 index 0000000000..9638e011bc --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/api/tree/PyDictionaryLiteralTree.java @@ -0,0 +1,35 @@ +/* + * 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 java.util.List; + +public interface PyDictionaryLiteralTree extends PyExpressionTree { + + Token lCurlyBrace(); + + List elements(); + + List commas(); + + Token rCurlyBrace(); + +} diff --git a/python-squid/src/main/java/org/sonar/python/api/tree/PyKeyValuePairTree.java b/python-squid/src/main/java/org/sonar/python/api/tree/PyKeyValuePairTree.java new file mode 100644 index 0000000000..6ecb42a172 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/api/tree/PyKeyValuePairTree.java @@ -0,0 +1,45 @@ +/* + * 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; + +/** + *
+ * {@link #key()} {@link #colon()} {@link #value() OR {@link #starStarToken()} {@link #expression()}
+ * 
+ */ +public interface PyKeyValuePairTree extends Tree { + @CheckForNull + PyExpressionTree key(); + + @CheckForNull + Token colon(); + + @CheckForNull + PyExpressionTree value(); + + @CheckForNull + Token starStarToken(); + + @CheckForNull + PyExpressionTree expression(); +} diff --git a/python-squid/src/main/java/org/sonar/python/api/tree/PySetLiteralTree.java b/python-squid/src/main/java/org/sonar/python/api/tree/PySetLiteralTree.java new file mode 100644 index 0000000000..93b511bc3d --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/api/tree/PySetLiteralTree.java @@ -0,0 +1,33 @@ +/* + * 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 java.util.List; + +public interface PySetLiteralTree extends PyExpressionTree { + Token lCurlyBrace(); + + List elements(); + + List commas(); + + Token rCurlyBrace(); +} 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 782ac072b8..a3f0627dfd 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 @@ -134,4 +134,10 @@ public interface PyTreeVisitor { void visitComprehensionFor(PyComprehensionForTree tree); void visitComprehensionIf(PyComprehensionIfTree tree); + + void visitDictionaryLiteral(PyDictionaryLiteralTree pyDictionaryLiteralTree); + + void visitSetLiteral(PySetLiteralTree pySetLiteralTree); + + void visitKeyValuePair(PyKeyValuePairTree pyKeyValuePairTree); } 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 051179f8ae..f948b9dc55 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 @@ -61,6 +61,8 @@ enum Kind { CONTINUE_STMT(PyContinueStatementTree.class), + DICTIONARY_LITERAL(PyDictionaryLiteralTree.class), + DEL_STMT(PyDelStatementTree.class), DOTTED_NAME(PyDottedNameTree.class), @@ -113,6 +115,8 @@ enum Kind { RETURN_STMT(PyReturnStatementTree.class), + SET_LITERAL(PySetLiteralTree.class), + STATEMENT_LIST(PyStatementListTree.class), STRING_LITERAL(PyStringLiteralTree.class), @@ -172,7 +176,9 @@ enum Kind { UNARY_PLUS(PyUnaryExpressionTree.class), UNARY_MINUS(PyUnaryExpressionTree.class), BITWISE_COMPLEMENT(PyUnaryExpressionTree.class), - NOT(PyUnaryExpressionTree.class); + NOT(PyUnaryExpressionTree.class), + + KEY_VALUE_PAIR(PyKeyValuePairTree.class); final Class associatedInterface; 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 d02b00020d..e8f753c62a 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 @@ -35,6 +35,7 @@ import org.sonar.python.api.tree.PyConditionalExpressionTree; import org.sonar.python.api.tree.PyContinueStatementTree; import org.sonar.python.api.tree.PyDelStatementTree; +import org.sonar.python.api.tree.PyDictionaryLiteralTree; import org.sonar.python.api.tree.PyDottedNameTree; import org.sonar.python.api.tree.PyElseStatementTree; import org.sonar.python.api.tree.PyExceptClauseTree; @@ -49,6 +50,7 @@ import org.sonar.python.api.tree.PyIfStatementTree; import org.sonar.python.api.tree.PyImportFromTree; import org.sonar.python.api.tree.PyImportNameTree; +import org.sonar.python.api.tree.PyKeyValuePairTree; import org.sonar.python.api.tree.PyLambdaExpressionTree; import org.sonar.python.api.tree.PyListLiteralTree; import org.sonar.python.api.tree.PyListOrSetCompExpressionTree; @@ -61,6 +63,7 @@ import org.sonar.python.api.tree.PyQualifiedExpressionTree; import org.sonar.python.api.tree.PyRaiseStatementTree; import org.sonar.python.api.tree.PyReturnStatementTree; +import org.sonar.python.api.tree.PySetLiteralTree; import org.sonar.python.api.tree.PySliceExpressionTree; import org.sonar.python.api.tree.PySliceItemTree; import org.sonar.python.api.tree.PySliceListTree; @@ -426,4 +429,21 @@ public void visitComprehensionIf(PyComprehensionIfTree tree) { scan(tree.condition()); scan(tree.nestedClause()); } + + @Override + public void visitDictionaryLiteral(PyDictionaryLiteralTree pyDictionaryLiteralTree) { + scan(pyDictionaryLiteralTree.elements()); + } + + @Override + public void visitSetLiteral(PySetLiteralTree pySetLiteralTree) { + scan((pySetLiteralTree.elements())); + } + + @Override + public void visitKeyValuePair(PyKeyValuePairTree pyKeyValuePairTree) { + scan(pyKeyValuePairTree.expression()); + scan(pyKeyValuePairTree.key()); + scan(pyKeyValuePairTree.value()); + } } diff --git a/python-squid/src/main/java/org/sonar/python/tree/PyDictOrSetLiteralTreeImpl.java b/python-squid/src/main/java/org/sonar/python/tree/PyDictOrSetLiteralTreeImpl.java new file mode 100644 index 0000000000..f67cfa9446 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/tree/PyDictOrSetLiteralTreeImpl.java @@ -0,0 +1,49 @@ +/* + * 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.AstNode; +import com.sonar.sslr.api.Token; +import java.util.List; + +public abstract class PyDictOrSetLiteralTreeImpl extends PyTree { + private final Token lCurlyBrace; + private final List commas; + private final Token rCurlyBrace; + + public PyDictOrSetLiteralTreeImpl(AstNode node, Token lCurlyBrace, List commas, Token rCurlyBrace) { + super(node); + this.lCurlyBrace = lCurlyBrace; + this.commas = commas; + this.rCurlyBrace = rCurlyBrace; + } + + public Token lCurlyBrace() { + return lCurlyBrace; + } + + public Token rCurlyBrace() { + return rCurlyBrace; + } + + public List commas() { + return commas; + } +} diff --git a/python-squid/src/main/java/org/sonar/python/tree/PyDictionaryLiteralTreeImpl.java b/python-squid/src/main/java/org/sonar/python/tree/PyDictionaryLiteralTreeImpl.java new file mode 100644 index 0000000000..85052cbab7 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/tree/PyDictionaryLiteralTreeImpl.java @@ -0,0 +1,59 @@ +/* + * 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.AstNode; +import com.sonar.sslr.api.Token; +import java.util.Collections; +import java.util.List; +import org.sonar.python.api.tree.PyDictionaryLiteralTree; +import org.sonar.python.api.tree.PyKeyValuePairTree; +import org.sonar.python.api.tree.PyTreeVisitor; +import org.sonar.python.api.tree.Tree; + +public class PyDictionaryLiteralTreeImpl extends PyDictOrSetLiteralTreeImpl implements PyDictionaryLiteralTree { + + private final List elements; + + public PyDictionaryLiteralTreeImpl(AstNode node, Token lCurlyBrace, List commas, List elements, Token rCurlyBrace) { + super(node, lCurlyBrace, commas, rCurlyBrace); + this.elements = elements; + } + + @Override + public List elements() { + return elements; + } + + @Override + public void accept(PyTreeVisitor visitor) { + visitor.visitDictionaryLiteral(this); + } + + @Override + public List children() { + return Collections.unmodifiableList(elements); + } + + @Override + public Kind getKind() { + return Kind.DICTIONARY_LITERAL; + } +} diff --git a/python-squid/src/main/java/org/sonar/python/tree/PyKeyValuePairTreeImpl.java b/python-squid/src/main/java/org/sonar/python/tree/PyKeyValuePairTreeImpl.java new file mode 100644 index 0000000000..3c2b96c5be --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/tree/PyKeyValuePairTreeImpl.java @@ -0,0 +1,101 @@ +/* + * 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.PyExpressionTree; +import org.sonar.python.api.tree.PyKeyValuePairTree; +import org.sonar.python.api.tree.PyTreeVisitor; +import org.sonar.python.api.tree.Tree; + +public class PyKeyValuePairTreeImpl extends PyTree implements PyKeyValuePairTree { + + private final Token starStarToken; + private final PyExpressionTree expression; + private final PyExpressionTree key; + private final Token colon; + private final PyExpressionTree value; + + public PyKeyValuePairTreeImpl(Token starStarToken, PyExpressionTree expression) { + super(starStarToken, expression.lastToken()); + this.starStarToken = starStarToken; + this.expression = expression; + this.key = null; + this.colon = null; + this.value = null; + } + + public PyKeyValuePairTreeImpl(PyExpressionTree key, Token colon, PyExpressionTree value) { + super(key.firstToken(), value.lastToken()); + this.key = key; + this.colon = colon; + this.value = value; + this.starStarToken = null; + this.expression = null; + } + + @CheckForNull + @Override + public PyExpressionTree key() { + return key; + } + + @CheckForNull + @Override + public Token colon() { + return colon; + } + + @CheckForNull + @Override + public PyExpressionTree value() { + return value; + } + + @CheckForNull + @Override + public Token starStarToken() { + return starStarToken; + } + + @CheckForNull + @Override + public PyExpressionTree expression() { + return expression; + } + + @Override + public void accept(PyTreeVisitor visitor) { + visitor.visitKeyValuePair(this); + } + + @Override + public List children() { + return Arrays.asList(expression, key, value); + } + + @Override + public Kind getKind() { + return Kind.KEY_VALUE_PAIR; + } +} diff --git a/python-squid/src/main/java/org/sonar/python/tree/PySetLiteralTreeImpl.java b/python-squid/src/main/java/org/sonar/python/tree/PySetLiteralTreeImpl.java new file mode 100644 index 0000000000..0288bdb925 --- /dev/null +++ b/python-squid/src/main/java/org/sonar/python/tree/PySetLiteralTreeImpl.java @@ -0,0 +1,58 @@ +/* + * 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.AstNode; +import com.sonar.sslr.api.Token; +import java.util.Collections; +import java.util.List; +import org.sonar.python.api.tree.PyExpressionTree; +import org.sonar.python.api.tree.PySetLiteralTree; +import org.sonar.python.api.tree.PyTreeVisitor; +import org.sonar.python.api.tree.Tree; + +public class PySetLiteralTreeImpl extends PyDictOrSetLiteralTreeImpl implements PySetLiteralTree { + private final List elements; + + public PySetLiteralTreeImpl(AstNode node, Token lCurlyBrace, List elements, List commas, Token rCurlyBrace) { + super(node, lCurlyBrace, commas, rCurlyBrace); + this.elements = elements; + } + + @Override + public List elements() { + return elements; + } + + @Override + public void accept(PyTreeVisitor visitor) { + visitor.visitSetLiteral(this); + } + + @Override + public List children() { + return Collections.unmodifiableList(elements); + } + + @Override + public Kind getKind() { + return Kind.SET_LITERAL; + } +} 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 58ecf81050..5d20a63958 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 @@ -41,9 +41,9 @@ import org.sonar.python.api.tree.PyBreakStatementTree; import org.sonar.python.api.tree.PyCallExpressionTree; import org.sonar.python.api.tree.PyClassDefTree; -import org.sonar.python.api.tree.PyConditionalExpressionTree; import org.sonar.python.api.tree.PyComprehensionClauseTree; import org.sonar.python.api.tree.PyComprehensionForTree; +import org.sonar.python.api.tree.PyConditionalExpressionTree; import org.sonar.python.api.tree.PyContinueStatementTree; import org.sonar.python.api.tree.PyDelStatementTree; import org.sonar.python.api.tree.PyDottedNameTree; @@ -62,6 +62,7 @@ import org.sonar.python.api.tree.PyImportFromTree; import org.sonar.python.api.tree.PyImportNameTree; import org.sonar.python.api.tree.PyImportStatementTree; +import org.sonar.python.api.tree.PyKeyValuePairTree; import org.sonar.python.api.tree.PyLambdaExpressionTree; import org.sonar.python.api.tree.PyNameTree; import org.sonar.python.api.tree.PyNonlocalStatementTree; @@ -609,6 +610,9 @@ PyExpressionTree expression(AstNode astNode) { if (astNode.is(PythonGrammar.ATOM) && astNode.getFirstChild().is(PythonPunctuator.LPARENTHESIS)) { return parenthesized(astNode); } + if (astNode.is(PythonGrammar.ATOM) && astNode.getFirstChild().is(PythonPunctuator.LCURLYBRACE)) { + return dictOrSetLiteral(astNode); + } if (astNode.is(PythonGrammar.TEST) && astNode.hasDirectChildren(PythonKeyword.IF)) { return conditionalExpression(astNode); } @@ -665,6 +669,38 @@ PyExpressionTree expression(AstNode astNode) { return new PyExpressionTreeImpl(astNode); } + private PyExpressionTree dictOrSetLiteral(AstNode astNode) { + Token lCurlyBrace = astNode.getFirstChild(PythonPunctuator.LCURLYBRACE).getToken(); + Token rCurlyBrace = astNode.getLastChild(PythonPunctuator.RCURLYBRACE).getToken(); + AstNode dictOrSetMaker = astNode.getFirstChild(PythonGrammar.DICTORSETMAKER); + if (dictOrSetMaker == null) { + return new PyDictionaryLiteralTreeImpl(astNode, lCurlyBrace, Collections.emptyList(), Collections.emptyList(), rCurlyBrace); + } + if (dictOrSetMaker.hasDirectChildren(PythonGrammar.COMP_FOR)) { + // TODO: dictionary comprehension and set comprehension + return new PyExpressionTreeImpl(astNode); + } + List commas = dictOrSetMaker.getChildren(PythonPunctuator.COMMA).stream().map(AstNode::getToken).collect(Collectors.toList()); + if (dictOrSetMaker.hasDirectChildren(PythonPunctuator.COLON) || dictOrSetMaker.hasDirectChildren(PythonPunctuator.MUL_MUL)) { + List keyValuePairTrees = new ArrayList<>(); + List children = dictOrSetMaker.getChildren(); + int index = 0; + while (index < children.size()) { + AstNode currentChild = children.get(index); + if (currentChild.is(PythonPunctuator.MUL_MUL)) { + keyValuePairTrees.add(new PyKeyValuePairTreeImpl(currentChild.getToken(), expression(children.get(index + 1)))); + index += 3; + } else { + keyValuePairTrees.add(new PyKeyValuePairTreeImpl(expression(currentChild), children.get(index + 1).getToken(), expression(children.get(index + 2)))); + index += 4; + } + } + return new PyDictionaryLiteralTreeImpl(astNode, lCurlyBrace, commas, keyValuePairTrees, rCurlyBrace); + } + List expressions = dictOrSetMaker.getChildren(PythonGrammar.TEST, PythonGrammar.STAR_EXPR).stream().map(this::expression).collect(Collectors.toList()); + return new PySetLiteralTreeImpl(astNode, lCurlyBrace, expressions, commas, rCurlyBrace); + } + private PyExpressionTree parenthesized(AstNode atom) { Token lPar = atom.getFirstChild().getToken(); Token rPar = atom.getLastChild().getToken(); 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 26d33ca6ca..b499f91616 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 @@ -41,6 +41,7 @@ import org.sonar.python.api.tree.PyConditionalExpressionTree; import org.sonar.python.api.tree.PyContinueStatementTree; import org.sonar.python.api.tree.PyDelStatementTree; +import org.sonar.python.api.tree.PyDictionaryLiteralTree; import org.sonar.python.api.tree.PyElseStatementTree; import org.sonar.python.api.tree.PyExceptClauseTree; import org.sonar.python.api.tree.PyExecStatementTree; @@ -56,6 +57,7 @@ import org.sonar.python.api.tree.PyImportStatementTree; import org.sonar.python.api.tree.PyInExpressionTree; import org.sonar.python.api.tree.PyIsExpressionTree; +import org.sonar.python.api.tree.PyKeyValuePairTree; import org.sonar.python.api.tree.PyLambdaExpressionTree; import org.sonar.python.api.tree.PyListLiteralTree; import org.sonar.python.api.tree.PyListOrSetCompExpressionTree; @@ -68,6 +70,7 @@ import org.sonar.python.api.tree.PyQualifiedExpressionTree; import org.sonar.python.api.tree.PyRaiseStatementTree; import org.sonar.python.api.tree.PyReturnStatementTree; +import org.sonar.python.api.tree.PySetLiteralTree; import org.sonar.python.api.tree.PySliceExpressionTree; import org.sonar.python.api.tree.PySliceItemTree; import org.sonar.python.api.tree.PyStarredExpressionTree; @@ -1417,6 +1420,55 @@ public void conditional_expression() { assertThat(nestedConditionalExpressionTree.falseExpression().getKind()).isEqualTo(Tree.Kind.CONDITIONAL_EXPR); } + @Test + public void dictionary_literal() { + setRootRule(PythonGrammar.ATOM); + PyDictionaryLiteralTree tree = (PyDictionaryLiteralTree) parse("{'key': 'value'}", treeMaker::expression); + assertThat(tree.getKind()).isEqualTo(Tree.Kind.DICTIONARY_LITERAL); + assertThat(tree.elements()).hasSize(1); + PyKeyValuePairTree keyValuePair = tree.elements().iterator().next(); + assertThat(keyValuePair.getKind()).isEqualTo(Tree.Kind.KEY_VALUE_PAIR); + assertThat(keyValuePair.key().getKind()).isEqualTo(Tree.Kind.STRING_LITERAL); + assertThat(keyValuePair.colon().getValue()).isEqualTo(":"); + assertThat(keyValuePair.value().getKind()).isEqualTo(Tree.Kind.STRING_LITERAL); + assertThat(tree.children()).hasSize(1); + + tree = (PyDictionaryLiteralTree) parse("{'key': 'value', 'key2': 'value2'}", treeMaker::expression); + assertThat(tree.elements()).hasSize(2); + + tree = (PyDictionaryLiteralTree) parse("{** var}", treeMaker::expression); + assertThat(tree.elements()).hasSize(1); + keyValuePair = tree.elements().iterator().next(); + assertThat(keyValuePair.expression().getKind()).isEqualTo(Tree.Kind.NAME); + assertThat(keyValuePair.starStarToken().getValue()).isEqualTo("**"); + + tree = (PyDictionaryLiteralTree) parse("{** var, key: value}", treeMaker::expression); + assertThat(tree.elements()).hasSize(2); + } + + @Test + public void set_literal() { + setRootRule(PythonGrammar.ATOM); + PySetLiteralTree tree = (PySetLiteralTree) parse("{ x }", treeMaker::expression); + assertThat(tree.getKind()).isEqualTo(Tree.Kind.SET_LITERAL); + assertThat(tree.elements()).hasSize(1); + PyExpressionTree element = tree.elements().iterator().next(); + assertThat(element.getKind()).isEqualTo(Tree.Kind.NAME); + assertThat(tree.lCurlyBrace().getValue()).isEqualTo("{"); + assertThat(tree.rCurlyBrace().getValue()).isEqualTo("}"); + assertThat(tree.commas()).hasSize(0); + assertThat(tree.children()).hasSize(1); + + tree = (PySetLiteralTree) parse("{ x, y }", treeMaker::expression); + assertThat(tree.elements()).hasSize(2); + + tree = (PySetLiteralTree) parse("{ *x }", treeMaker::expression); + assertThat(tree.elements()).hasSize(1); + element = tree.elements().iterator().next(); + assertThat(element.getKind()).isEqualTo(Tree.Kind.STARRED_EXPR); + + } + private void assertUnaryExpression(String operator, Tree.Kind kind) { setRootRule(PythonGrammar.EXPR); PyExpressionTree parse = parse(operator+"1", treeMaker::expression);