Skip to content

Commit f350f9b

Browse files
SONARPY-916 Match / case statement: support class patterns (#990)
1 parent ea882ad commit f350f9b

File tree

12 files changed

+409
-4
lines changed

12 files changed

+409
-4
lines changed

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,4 +530,16 @@ public void visitWildcardPattern(WildcardPattern wildcardPattern) {
530530
public void visitGroupPattern(GroupPattern groupPattern) {
531531
scan(groupPattern.pattern());
532532
}
533+
534+
@Override
535+
public void visitClassPattern(ClassPattern classPattern) {
536+
scan(classPattern.targetClass());
537+
scan(classPattern.arguments());
538+
}
539+
540+
@Override
541+
public void visitKeywordPattern(KeywordPattern keywordPattern) {
542+
scan(keywordPattern.attributeName());
543+
scan(keywordPattern.pattern());
544+
}
533545
}
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+
* Class pattern
26+
* <pre>case Foo(x): ...</pre>
27+
* See https://docs.python.org/3/reference/compound_stmts.html#class-patterns
28+
*/
29+
public interface ClassPattern extends Pattern {
30+
31+
/**
32+
* Return type can only be {@link Name} or {@link QualifiedExpression}
33+
*/
34+
Expression targetClass();
35+
36+
Token leftPar();
37+
38+
List<Pattern> arguments();
39+
40+
List<Token> argumentSeparators();
41+
42+
Token rightPar();
43+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
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+
/**
24+
* Keyword pattern
25+
* Used only inside Class pattern
26+
*
27+
* <pre>
28+
* case Foo({@link #attributeName()} = {@link #pattern()}):
29+
* ...
30+
* </pre>
31+
*
32+
* See https://docs.python.org/3/reference/compound_stmts.html#grammar-token-python-grammar-keyword_pattern
33+
*/
34+
public interface KeywordPattern extends Pattern {
35+
36+
Name attributeName();
37+
38+
Token equalToken();
39+
40+
Pattern pattern();
41+
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ enum Kind {
6464

6565
CLASSDEF(ClassDef.class),
6666

67+
CLASS_PATTERN(ClassPattern.class),
68+
6769
CONDITIONAL_EXPR(ConditionalExpression.class),
6870

6971
CONTINUE_STMT(ContinueStatement.class),
@@ -112,6 +114,8 @@ enum Kind {
112114

113115
IMPORT_STMT(DottedName.class),
114116

117+
KEYWORD_PATTERN(KeywordPattern.class),
118+
115119
LAMBDA(LambdaExpression.class),
116120

117121
LIST_LITERAL(ListLiteral.class),

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,4 +194,8 @@ public interface TreeVisitor {
194194
void visitWildcardPattern(WildcardPattern wildcardPattern);
195195

196196
void visitGroupPattern(GroupPattern groupPattern);
197+
198+
void visitClassPattern(ClassPattern classPattern);
199+
200+
void visitKeywordPattern(KeywordPattern keywordPattern);
197201
}

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,11 @@ public enum PythonGrammar implements GrammarRuleKey {
168168
OPEN_SEQUENCE_PATTERN,
169169
WILDCARD_PATTERN,
170170
GROUP_PATTERN,
171+
CLASS_PATTERN,
172+
PATTERN_ARGS,
173+
PATTERN_ARG,
174+
KEYWORD_PATTERN,
175+
NAME_OR_ATTR,
171176

172177
SIGNED_NUMBER,
173178
COMPLEX_NUMBER,
@@ -469,7 +474,7 @@ public static void compoundStatements(LexerfulGrammarBuilder b) {
469474

470475
b.rule(PATTERNS).is(b.firstOf(OPEN_SEQUENCE_PATTERN, PATTERN));
471476
b.rule(PATTERN).is(b.firstOf(AS_PATTERN, OR_PATTERN));
472-
b.rule(CLOSED_PATTERN).is(b.firstOf(LITERAL_PATTERN, GROUP_PATTERN, WILDCARD_PATTERN, CAPTURE_PATTERN, SEQUENCE_PATTERN));
477+
b.rule(CLOSED_PATTERN).is(b.firstOf(CLASS_PATTERN, LITERAL_PATTERN, GROUP_PATTERN, WILDCARD_PATTERN, CAPTURE_PATTERN, SEQUENCE_PATTERN));
473478
b.rule(AS_PATTERN).is(OR_PATTERN, "as", CAPTURE_PATTERN);
474479
b.rule(OR_PATTERN).is(CLOSED_PATTERN, b.zeroOrMore("|", CLOSED_PATTERN));
475480
b.rule(CAPTURE_PATTERN).is(NAME);
@@ -483,6 +488,12 @@ public static void compoundStatements(LexerfulGrammarBuilder b) {
483488
b.rule(MAYBE_STAR_PATTERN).is(b.firstOf(STAR_PATTERN, PATTERN));
484489
b.rule(STAR_PATTERN).is("*", b.firstOf(WILDCARD_PATTERN, CAPTURE_PATTERN));
485490

491+
b.rule(CLASS_PATTERN).is(NAME_OR_ATTR, "(", b.optional(PATTERN_ARGS), ")");
492+
b.rule(NAME_OR_ATTR).is(NAME, b.zeroOrMore(".", NAME));
493+
b.rule(PATTERN_ARGS).is(PATTERN_ARG, b.zeroOrMore(b.sequence(",", PATTERN_ARG)), b.optional(","));
494+
b.rule(PATTERN_ARG).is(b.firstOf(KEYWORD_PATTERN, PATTERN));
495+
b.rule(KEYWORD_PATTERN).is(NAME, "=", PATTERN);
496+
486497
b.rule(LITERAL_PATTERN).is(b.firstOf(
487498
COMPLEX_NUMBER,
488499
SIGNED_NUMBER,
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
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.ArrayList;
23+
import java.util.Arrays;
24+
import java.util.List;
25+
import org.sonar.plugins.python.api.tree.ClassPattern;
26+
import org.sonar.plugins.python.api.tree.Expression;
27+
import org.sonar.plugins.python.api.tree.Pattern;
28+
import org.sonar.plugins.python.api.tree.Token;
29+
import org.sonar.plugins.python.api.tree.Tree;
30+
import org.sonar.plugins.python.api.tree.TreeVisitor;
31+
32+
public class ClassPatternImpl extends PyTree implements ClassPattern {
33+
34+
private final Expression targetClass;
35+
private final Token leftPar;
36+
private final List<Pattern> arguments;
37+
private final List<Token> argumentSeparators;
38+
private final Token rightPar;
39+
40+
public ClassPatternImpl(Expression targetClass, Token leftPar, List<Pattern> arguments, List<Token> argumentSeparators, Token rightPar) {
41+
this.targetClass = targetClass;
42+
this.leftPar = leftPar;
43+
this.arguments = arguments;
44+
this.argumentSeparators = argumentSeparators;
45+
this.rightPar = rightPar;
46+
}
47+
48+
@Override
49+
public Expression targetClass() {
50+
return targetClass;
51+
}
52+
53+
@Override
54+
public Token leftPar() {
55+
return leftPar;
56+
}
57+
58+
@Override
59+
public List<Pattern> arguments() {
60+
return arguments;
61+
}
62+
63+
@Override
64+
public List<Token> argumentSeparators() {
65+
return argumentSeparators;
66+
}
67+
68+
@Override
69+
public Token rightPar() {
70+
return rightPar;
71+
}
72+
73+
@Override
74+
public void accept(TreeVisitor visitor) {
75+
visitor.visitClassPattern(this);
76+
}
77+
78+
@Override
79+
public Kind getKind() {
80+
return Kind.CLASS_PATTERN;
81+
}
82+
83+
@Override
84+
List<Tree> computeChildren() {
85+
List<Tree> children = new ArrayList<>(Arrays.asList(targetClass, leftPar));
86+
int i = 0;
87+
for (Pattern element : arguments) {
88+
children.add(element);
89+
if (i < argumentSeparators.size()) {
90+
children.add(argumentSeparators.get(i));
91+
}
92+
i++;
93+
}
94+
children.add(rightPar);
95+
return children;
96+
}
97+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
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.KeywordPattern;
25+
import org.sonar.plugins.python.api.tree.Name;
26+
import org.sonar.plugins.python.api.tree.Pattern;
27+
import org.sonar.plugins.python.api.tree.Token;
28+
import org.sonar.plugins.python.api.tree.Tree;
29+
import org.sonar.plugins.python.api.tree.TreeVisitor;
30+
31+
public class KeywordPatternImpl extends PyTree implements KeywordPattern {
32+
private final Name attributeName;
33+
private final Token equalToken;
34+
private final Pattern pattern;
35+
36+
public KeywordPatternImpl(Name attributeName, Token equalToken, Pattern pattern) {
37+
this.attributeName = attributeName;
38+
this.equalToken = equalToken;
39+
this.pattern = pattern;
40+
}
41+
42+
@Override
43+
public Name attributeName() {
44+
return attributeName;
45+
}
46+
47+
@Override
48+
public Token equalToken() {
49+
return equalToken;
50+
}
51+
52+
@Override
53+
public Pattern pattern() {
54+
return pattern;
55+
}
56+
57+
@Override
58+
public void accept(TreeVisitor visitor) {
59+
visitor.visitKeywordPattern(this);
60+
}
61+
62+
@Override
63+
public Kind getKind() {
64+
return Kind.KEYWORD_PATTERN;
65+
}
66+
67+
@Override
68+
List<Tree> computeChildren() {
69+
return Arrays.asList(attributeName, equalToken, pattern);
70+
}
71+
}

0 commit comments

Comments
 (0)