Skip to content

Commit 95cd31b

Browse files
SONARPY-915 Match / case statement: support mapping patterns
1 parent 68454aa commit 95cd31b

File tree

13 files changed

+484
-16
lines changed

13 files changed

+484
-16
lines changed

python-frontend/src/main/java/org/sonar/plugins/python/api/tree/BaseTreeVisitor.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,11 @@ public void visitDelStatement(DelStatement pyDelStatementTree) {
8383
scan(pyDelStatementTree.expressions());
8484
}
8585

86+
@Override
87+
public void visitDoubleStarPattern(DoubleStarPattern doubleStarPattern) {
88+
scan(doubleStarPattern.capturePattern());
89+
}
90+
8691
@Override
8792
public void visitPassStatement(PassStatement pyPassStatementTree) {
8893
// nothing to visit for pass statement
@@ -417,6 +422,12 @@ public void visitKeyValuePair(KeyValuePair keyValuePair) {
417422
scan(keyValuePair.value());
418423
}
419424

425+
@Override
426+
public void visitKeyValuePattern(KeyValuePattern keyValuePattern) {
427+
scan(keyValuePattern.key());
428+
scan(keyValuePattern.value());
429+
}
430+
420431
@Override
421432
public void visitDictCompExpression(DictCompExpressionImpl tree) {
422433
scan(tree.keyExpression());
@@ -501,6 +512,10 @@ public void visitOrPattern(OrPattern orPattern) {
501512
scan(orPattern.patterns());
502513
}
503514

515+
public void visitMappingPattern(MappingPattern mappingPattern) {
516+
scan(mappingPattern.elements());
517+
}
518+
504519
@Override
505520
public void visitGuard(Guard guard) {
506521
scan(guard.condition());
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* SonarQube Python Plugin
3+
* Copyright (C) 2011-2021 SonarSource SA
4+
* mailto:info AT sonarsource DOT com
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the GNU Lesser General Public
8+
* License as published by the Free Software Foundation; either
9+
* version 3 of the License, or (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
* Lesser General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Lesser General Public License
17+
* along with this program; if not, write to the Free Software Foundation,
18+
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19+
*/
20+
package org.sonar.plugins.python.api.tree;
21+
22+
/**
23+
* Double Star Pattern (only used within mapping patterns)
24+
*
25+
* <pre>
26+
* case {"x": "foo", **rest}:
27+
* ...
28+
* </pre>
29+
*
30+
* See https://docs.python.org/3/reference/compound_stmts.html#mapping-patterns
31+
*/
32+
public interface DoubleStarPattern extends Pattern {
33+
34+
Token doubleStarToken();
35+
36+
CapturePattern capturePattern();
37+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* SonarQube Python Plugin
3+
* Copyright (C) 2011-2021 SonarSource SA
4+
* mailto:info AT sonarsource DOT com
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the GNU Lesser General Public
8+
* License as published by the Free Software Foundation; either
9+
* version 3 of the License, or (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
* Lesser General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Lesser General Public License
17+
* along with this program; if not, write to the Free Software Foundation,
18+
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19+
*/
20+
package org.sonar.plugins.python.api.tree;
21+
22+
/**
23+
* Key Value Pattern (only used within mapping patterns)
24+
*
25+
* <pre>
26+
* case {"key": "value"}:
27+
* ...
28+
* </pre>
29+
*
30+
* See https://docs.python.org/3/reference/compound_stmts.html#mapping-patterns
31+
*/
32+
public interface KeyValuePattern extends Pattern {
33+
Pattern key();
34+
35+
Token colon();
36+
37+
Pattern value();
38+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* SonarQube Python Plugin
3+
* Copyright (C) 2011-2021 SonarSource SA
4+
* mailto:info AT sonarsource DOT com
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the GNU Lesser General Public
8+
* License as published by the Free Software Foundation; either
9+
* version 3 of the License, or (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
* Lesser General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Lesser General Public License
17+
* along with this program; if not, write to the Free Software Foundation,
18+
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19+
*/
20+
package org.sonar.plugins.python.api.tree;
21+
22+
import java.util.List;
23+
24+
/**
25+
* Mapping Pattern
26+
*
27+
* <pre>
28+
* case {"x": "foo", "y": "bar"}:
29+
* ...
30+
* </pre>
31+
*
32+
* See https://docs.python.org/3/reference/compound_stmts.html#mapping-patterns
33+
*/
34+
public interface MappingPattern extends Pattern {
35+
36+
Token lCurlyBrace();
37+
38+
List<Pattern> elements();
39+
40+
List<Token> commas();
41+
42+
Token rCurlyBrace();
43+
}

python-frontend/src/main/java/org/sonar/plugins/python/api/tree/Tree.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ enum Kind {
8080

8181
DOTTED_NAME(DottedName.class),
8282

83+
DOUBLE_STAR_PATTERN(DoubleStarPattern.class),
84+
8385
ELLIPSIS(EllipsisExpression.class),
8486

8587
ELSE_CLAUSE(ElseClause.class),
@@ -124,6 +126,8 @@ enum Kind {
124126

125127
MATCH_STMT(MatchStatement.class),
126128

129+
MAPPING_PATTERN(MappingPattern.class),
130+
127131
NAME(Name.class),
128132

129133
NONLOCAL_STMT(NonlocalStatement.class),
@@ -233,6 +237,7 @@ enum Kind {
233237
ASSIGNMENT_EXPRESSION(AssignmentExpression.class),
234238

235239
KEY_VALUE_PAIR(KeyValuePair.class),
240+
KEY_VALUE_PATTERN(KeyValuePattern.class),
236241
TOKEN(Token.class);
237242
final Class<? extends Tree> associatedInterface;
238243

python-frontend/src/main/java/org/sonar/plugins/python/api/tree/TreeVisitor.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ public interface TreeVisitor {
3737

3838
void visitDelStatement(DelStatement delStatement);
3939

40+
void visitDoubleStarPattern(DoubleStarPattern doubleStarPattern);
41+
4042
void visitPassStatement(PassStatement passStatement);
4143

4244
void visitPrintStatement(PrintStatement printStatement);
@@ -153,6 +155,8 @@ public interface TreeVisitor {
153155

154156
void visitKeyValuePair(KeyValuePair keyValuePair);
155157

158+
void visitKeyValuePattern(KeyValuePattern keyValuePattern);
159+
156160
void visitDictCompExpression(DictCompExpressionImpl dictCompExpression);
157161

158162
void visitCompoundAssignment(CompoundAssignmentStatement compoundAssignmentStatement);
@@ -183,6 +187,8 @@ public interface TreeVisitor {
183187

184188
void visitOrPattern(OrPattern orPattern);
185189

190+
void visitMappingPattern(MappingPattern mappingPattern);
191+
186192
void visitGuard(Guard guard);
187193

188194
void visitCapturePattern(CapturePattern capturePattern);

python-frontend/src/main/java/org/sonar/python/api/PythonGrammar.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,10 @@ public enum PythonGrammar implements GrammarRuleKey {
174174
KEYWORD_PATTERN,
175175
NAME_OR_ATTR,
176176
VALUE_PATTERN,
177+
MAPPING_PATTERN,
178+
ITEMS_PATTERN,
179+
KEY_VALUE_PATTERN,
180+
DOUBLE_STAR_PATTERN,
177181

178182
SIGNED_NUMBER,
179183
COMPLEX_NUMBER,
@@ -476,11 +480,20 @@ public static void compoundStatements(LexerfulGrammarBuilder b) {
476480

477481
b.rule(PATTERNS).is(b.firstOf(OPEN_SEQUENCE_PATTERN, PATTERN));
478482
b.rule(PATTERN).is(b.firstOf(AS_PATTERN, OR_PATTERN));
479-
b.rule(CLOSED_PATTERN).is(b.firstOf(CLASS_PATTERN, LITERAL_PATTERN, GROUP_PATTERN, WILDCARD_PATTERN, VALUE_PATTERN, CAPTURE_PATTERN, SEQUENCE_PATTERN));
483+
b.rule(CLOSED_PATTERN).is(b.firstOf(CLASS_PATTERN, LITERAL_PATTERN, GROUP_PATTERN, WILDCARD_PATTERN, VALUE_PATTERN, CAPTURE_PATTERN, SEQUENCE_PATTERN, MAPPING_PATTERN));
480484
b.rule(AS_PATTERN).is(OR_PATTERN, "as", CAPTURE_PATTERN);
481485
b.rule(OR_PATTERN).is(CLOSED_PATTERN, b.zeroOrMore("|", CLOSED_PATTERN));
482486
b.rule(CAPTURE_PATTERN).is(NAME);
483487
b.rule(VALUE_PATTERN).is(ATTR);
488+
b.rule(MAPPING_PATTERN).is(b.firstOf(
489+
b.sequence("{", "}"),
490+
b.sequence("{", DOUBLE_STAR_PATTERN, b.optional(","), "}"),
491+
b.sequence("{", ITEMS_PATTERN, ",", DOUBLE_STAR_PATTERN, b.optional(","), "}"),
492+
b.sequence("{", ITEMS_PATTERN, b.optional(","), "}")
493+
));
494+
b.rule(ITEMS_PATTERN).is(KEY_VALUE_PATTERN, b.zeroOrMore(",", KEY_VALUE_PATTERN));
495+
b.rule(KEY_VALUE_PATTERN).is(b.firstOf(LITERAL_PATTERN, VALUE_PATTERN), ":", PATTERN);
496+
b.rule(DOUBLE_STAR_PATTERN).is("**", CAPTURE_PATTERN);
484497

485498
b.rule(SEQUENCE_PATTERN).is(b.firstOf(
486499
b.sequence("[", b.optional(MAYBE_SEQUENCE_PATTERN) , "]"),
@@ -493,6 +506,7 @@ public static void compoundStatements(LexerfulGrammarBuilder b) {
493506

494507
b.rule(CLASS_PATTERN).is(NAME_OR_ATTR, "(", b.optional(PATTERN_ARGS), ")");
495508
b.rule(NAME_OR_ATTR).is(NAME, b.zeroOrMore(".", NAME));
509+
b.rule(ATTR).is(NAME, b.oneOrMore(".", NAME));
496510
b.rule(PATTERN_ARGS).is(PATTERN_ARG, b.zeroOrMore(b.sequence(",", PATTERN_ARG)), b.optional(","));
497511
b.rule(PATTERN_ARG).is(b.firstOf(KEYWORD_PATTERN, PATTERN));
498512
b.rule(KEYWORD_PATTERN).is(NAME, "=", PATTERN);
@@ -526,7 +540,6 @@ public static void compoundStatements(LexerfulGrammarBuilder b) {
526540
b.rule(DECORATORS).is(b.oneOrMore(DECORATOR));
527541
b.rule(DECORATOR).is("@", EXPR, NEWLINE);
528542
b.rule(DOTTED_NAME).is(NAME, b.zeroOrMore(".", NAME));
529-
b.rule(ATTR).is(NAME, b.oneOrMore(b.nextNot("(", "["), TRAILER));
530543

531544
b.rule(CLASSDEF).is(b.optional(DECORATORS), "class", CLASSNAME, b.optional("(", b.optional(ARGLIST), ")"), ":", SUITE);
532545
b.rule(CLASSNAME).is(NAME);
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* SonarQube Python Plugin
3+
* Copyright (C) 2011-2021 SonarSource SA
4+
* mailto:info AT sonarsource DOT com
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the GNU Lesser General Public
8+
* License as published by the Free Software Foundation; either
9+
* version 3 of the License, or (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
* Lesser General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Lesser General Public License
17+
* along with this program; if not, write to the Free Software Foundation,
18+
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19+
*/
20+
package org.sonar.python.tree;
21+
22+
import java.util.Arrays;
23+
import java.util.List;
24+
import org.sonar.plugins.python.api.tree.CapturePattern;
25+
import org.sonar.plugins.python.api.tree.DoubleStarPattern;
26+
import org.sonar.plugins.python.api.tree.Token;
27+
import org.sonar.plugins.python.api.tree.Tree;
28+
import org.sonar.plugins.python.api.tree.TreeVisitor;
29+
30+
public class DoubleStarPatternImpl extends PyTree implements DoubleStarPattern {
31+
32+
private final Token doubleStarToken;
33+
private final CapturePattern capturePattern;
34+
35+
public DoubleStarPatternImpl(Token doubleStarToken, CapturePattern capturePattern) {
36+
this.doubleStarToken = doubleStarToken;
37+
this.capturePattern = capturePattern;
38+
}
39+
@Override
40+
public Token doubleStarToken() {
41+
return doubleStarToken;
42+
}
43+
44+
@Override
45+
public CapturePattern capturePattern() {
46+
return capturePattern;
47+
}
48+
49+
@Override
50+
public void accept(TreeVisitor visitor) {
51+
visitor.visitDoubleStarPattern(this);
52+
}
53+
54+
@Override
55+
public Kind getKind() {
56+
return Kind.DOUBLE_STAR_PATTERN;
57+
}
58+
59+
@Override
60+
List<Tree> computeChildren() {
61+
return Arrays.asList(doubleStarToken, capturePattern);
62+
}
63+
}

python-frontend/src/main/java/org/sonar/python/tree/KeyValuePairImpl.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,8 @@
1919
*/
2020
package org.sonar.python.tree;
2121

22+
import java.util.Arrays;
2223
import java.util.List;
23-
import java.util.stream.Collectors;
24-
import java.util.stream.Stream;
2524
import javax.annotation.CheckForNull;
2625
import org.sonar.plugins.python.api.tree.Expression;
2726
import org.sonar.plugins.python.api.tree.KeyValuePair;
@@ -65,7 +64,7 @@ public void accept(TreeVisitor visitor) {
6564

6665
@Override
6766
public List<Tree> computeChildren() {
68-
return Stream.of(key, colon, value).collect(Collectors.toList());
67+
return Arrays.asList(key, colon, value);
6968
}
7069

7170
@Override

0 commit comments

Comments
 (0)