Skip to content

Commit 5d32f0c

Browse files
Add isPositionalOnly API to Function Symbol parameters
1 parent ac758e0 commit 5d32f0c

3 files changed

Lines changed: 43 additions & 15 deletions

File tree

python-frontend/src/main/java/org/sonar/plugins/python/api/symbols/FunctionSymbol.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ interface Parameter {
4848
String name();
4949
boolean hasDefaultValue();
5050
boolean isKeywordOnly();
51+
boolean isPositionalOnly();
5152
@CheckForNull
5253
LocationInFile location();
5354
}

python-frontend/src/main/java/org/sonar/python/semantic/FunctionSymbolImpl.java

Lines changed: 36 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -118,26 +118,40 @@ private static boolean isInstanceMethod(FunctionDef functionDef) {
118118
}
119119

120120
private void createParameterNames(List<AnyParameter> parameterTrees, @Nullable String fileId) {
121-
boolean keywordOnly = false;
121+
ParameterState parameterState = new ParameterState();
122122
for (AnyParameter anyParameter : parameterTrees) {
123123
if (anyParameter.is(Tree.Kind.PARAMETER)) {
124-
org.sonar.plugins.python.api.tree.Parameter parameter = (org.sonar.plugins.python.api.tree.Parameter) anyParameter;
125-
Name parameterName = parameter.name();
126-
Token starToken = parameter.starToken();
127-
if (parameterName != null) {
128-
this.parameters.add(new ParameterImpl(parameterName.name(), parameter.defaultValue() != null, keywordOnly, locationInFile(anyParameter, fileId)));
129-
if (starToken != null) {
130-
hasVariadicParameter = true;
131-
}
132-
} else if (starToken != null && "*".equals(starToken.value())) {
133-
keywordOnly = true;
134-
}
124+
addParameter((org.sonar.plugins.python.api.tree.Parameter) anyParameter, fileId, parameterState);
135125
} else {
136-
parameters.add(new ParameterImpl(null, false, false, locationInFile(anyParameter, fileId)));
126+
parameters.add(new ParameterImpl(null, false, parameterState, locationInFile(anyParameter, fileId)));
127+
}
128+
}
129+
}
130+
131+
private void addParameter(org.sonar.plugins.python.api.tree.Parameter parameter, @Nullable String fileId, ParameterState parameterState) {
132+
Name parameterName = parameter.name();
133+
Token starToken = parameter.starToken();
134+
if (parameterName != null) {
135+
this.parameters.add(new ParameterImpl(parameterName.name(), parameter.defaultValue() != null, parameterState, locationInFile(parameter, fileId)));
136+
if (starToken != null) {
137+
hasVariadicParameter = true;
138+
}
139+
} else if (starToken != null) {
140+
if ("*".equals(starToken.value())) {
141+
parameterState.keywordOnly = true;
142+
parameterState.positionalOnly = false;
143+
}
144+
if ("/".equals(starToken.value())) {
145+
parameterState.positionalOnly = true;
137146
}
138147
}
139148
}
140149

150+
private static class ParameterState {
151+
boolean keywordOnly = false;
152+
boolean positionalOnly = false;
153+
}
154+
141155
@Override
142156
public List<Parameter> parameters() {
143157
return parameters;
@@ -189,12 +203,14 @@ private static class ParameterImpl implements Parameter {
189203
private final String name;
190204
private final boolean hasDefaultValue;
191205
private final boolean isKeywordOnly;
206+
private final boolean isPositionalOnly;
192207
private final LocationInFile location;
193208

194-
ParameterImpl(@Nullable String name, boolean hasDefaultValue, boolean isKeywordOnly, @Nullable LocationInFile location) {
209+
ParameterImpl(@Nullable String name, boolean hasDefaultValue, ParameterState parameterState, @Nullable LocationInFile location) {
195210
this.name = name;
196211
this.hasDefaultValue = hasDefaultValue;
197-
this.isKeywordOnly = isKeywordOnly;
212+
this.isKeywordOnly = parameterState.keywordOnly;
213+
this.isPositionalOnly = parameterState.positionalOnly;
198214
this.location = location;
199215
}
200216

@@ -214,6 +230,11 @@ public boolean isKeywordOnly() {
214230
return isKeywordOnly;
215231
}
216232

233+
@Override
234+
public boolean isPositionalOnly() {
235+
return isPositionalOnly;
236+
}
237+
217238
@CheckForNull
218239
@Override
219240
public LocationInFile location() {

python-frontend/src/test/java/org/sonar/python/semantic/FunctionSymbolTest.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ public void arity() {
6060
functionSymbol = functionSymbol("def fn(p1, /, p2): pass");
6161
assertThat(functionSymbol.parameters()).extracting(FunctionSymbol.Parameter::name).containsExactly("p1", "p2");
6262
assertThat(functionSymbol.parameters()).extracting(FunctionSymbol.Parameter::isKeywordOnly).containsExactly(false, false);
63+
assertThat(functionSymbol.parameters()).extracting(FunctionSymbol.Parameter::isPositionalOnly).containsExactly(false, true);
64+
65+
functionSymbol = functionSymbol("def fn(p1, /, p2, *, p3): pass");
66+
assertThat(functionSymbol.parameters()).extracting(FunctionSymbol.Parameter::name).containsExactly("p1", "p2", "p3");
67+
assertThat(functionSymbol.parameters()).extracting(FunctionSymbol.Parameter::isKeywordOnly).containsExactly(false, false, true);
68+
assertThat(functionSymbol.parameters()).extracting(FunctionSymbol.Parameter::isPositionalOnly).containsExactly(false, true, false);
6369

6470
functionSymbol = functionSymbol("def fn(p1, p2=42): pass");
6571
assertThat(functionSymbol.parameters()).extracting(FunctionSymbol.Parameter::name).containsExactly("p1", "p2");

0 commit comments

Comments
 (0)