Skip to content

Commit 0abb3b1

Browse files
joke1196sonartech
authored andcommitted
SONARPY-3561 Serialize parameter type annotation to and from protobuf (#699)
GitOrigin-RevId: e4514903845bd4957cf08b99ff03349d3d08a790
1 parent 0d065f1 commit 0abb3b1

File tree

12 files changed

+372
-74
lines changed

12 files changed

+372
-74
lines changed

python-frontend/src/main/java/org/sonar/python/index/DescriptorUtils.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,18 @@
2323
import java.util.Set;
2424
import java.util.stream.Collectors;
2525
import javax.annotation.Nullable;
26+
import javax.annotation.CheckForNull;
2627
import org.sonar.plugins.python.api.symbols.Symbol;
2728
import org.sonar.plugins.python.api.types.InferredType;
29+
import org.sonar.python.index.TypeAnnotationDescriptor.TypeKind;
2830
import org.sonar.python.semantic.AmbiguousSymbolImpl;
2931
import org.sonar.python.semantic.ClassSymbolImpl;
3032
import org.sonar.python.semantic.FunctionSymbolImpl;
3133
import org.sonar.python.semantic.ProjectLevelSymbolTable;
3234
import org.sonar.python.semantic.SymbolImpl;
3335
import org.sonar.python.types.DeclaredType;
3436
import org.sonar.python.types.InferredTypes;
37+
import org.sonar.python.types.protobuf.SymbolsProtos;
3538

3639
import static org.sonar.python.semantic.SymbolUtils.typeshedSymbolWithFQN;
3740
import static org.sonar.python.types.InferredTypes.anyType;
@@ -195,4 +198,16 @@ private static void setParameterType(FunctionSymbolImpl.ParameterImpl parameter,
195198
}
196199
parameter.setDeclaredType(declaredType);
197200
}
201+
202+
public static SymbolsProtos.TypeKind typeAnnotationKindToSymbolKind(TypeAnnotationDescriptor.TypeKind kind){
203+
return SymbolsProtos.TypeKind.valueOf(kind.name());
204+
}
205+
206+
@CheckForNull
207+
public static TypeKind symbolTypeKindToTypeAnnotationKind(SymbolsProtos.TypeKind kind){
208+
if(kind == SymbolsProtos.TypeKind.UNRECOGNIZED){
209+
return null;
210+
}
211+
return TypeKind.valueOf(kind.name());
212+
}
198213
}

python-frontend/src/main/java/org/sonar/python/index/DescriptorsToProtobuf.java

Lines changed: 64 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,20 @@
1616
*/
1717
package org.sonar.python.index;
1818

19+
import com.google.common.annotations.VisibleForTesting;
1920
import java.util.ArrayList;
2021
import java.util.HashSet;
2122
import java.util.List;
2223
import java.util.Set;
24+
import javax.annotation.CheckForNull;
2325
import org.sonar.plugins.python.api.LocationInFile;
2426
import org.sonar.python.types.protobuf.DescriptorsProtos;
27+
import org.sonar.python.types.protobuf.SymbolsProtos;
2528

2629
public class DescriptorsToProtobuf {
2730

28-
private DescriptorsToProtobuf() {}
31+
private DescriptorsToProtobuf() {
32+
}
2933

3034
public static DescriptorsProtos.ModuleDescriptor toProtobufModuleDescriptor(Set<Descriptor> descriptors) {
3135
List<DescriptorsProtos.ClassDescriptor> classDescriptors = new ArrayList<>();
@@ -52,7 +56,7 @@ public static DescriptorsProtos.ModuleDescriptor toProtobufModuleDescriptor(Set<
5256
.build();
5357
}
5458

55-
public static DescriptorsProtos.AmbiguousDescriptor toProtobuf(AmbiguousDescriptor ambiguousDescriptor) {
59+
private static DescriptorsProtos.AmbiguousDescriptor toProtobuf(AmbiguousDescriptor ambiguousDescriptor) {
5660
List<DescriptorsProtos.FunctionDescriptor> functionDescriptors = new ArrayList<>();
5761
List<DescriptorsProtos.VarDescriptor> variableDescriptors = new ArrayList<>();
5862
List<DescriptorsProtos.ClassDescriptor> classDescriptors = new ArrayList<>();
@@ -78,7 +82,7 @@ public static DescriptorsProtos.AmbiguousDescriptor toProtobuf(AmbiguousDescript
7882
return builder.build();
7983
}
8084

81-
public static DescriptorsProtos.ClassDescriptor toProtobuf(ClassDescriptor classDescriptor) {
85+
private static DescriptorsProtos.ClassDescriptor toProtobuf(ClassDescriptor classDescriptor) {
8286
List<DescriptorsProtos.FunctionDescriptor> functionMembers = new ArrayList<>();
8387
List<DescriptorsProtos.VarDescriptor> variableMembers = new ArrayList<>();
8488
List<DescriptorsProtos.AmbiguousDescriptor> ambiguousMembers = new ArrayList<>();
@@ -118,7 +122,7 @@ public static DescriptorsProtos.ClassDescriptor toProtobuf(ClassDescriptor class
118122
return builder.build();
119123
}
120124

121-
public static DescriptorsProtos.FunctionDescriptor toProtobuf(FunctionDescriptor functionDescriptor) {
125+
private static DescriptorsProtos.FunctionDescriptor toProtobuf(FunctionDescriptor functionDescriptor) {
122126
DescriptorsProtos.FunctionDescriptor.Builder builder = DescriptorsProtos.FunctionDescriptor.newBuilder()
123127
.setName(functionDescriptor.name())
124128
.setFullyQualifiedName(functionDescriptor.fullyQualifiedName())
@@ -138,7 +142,7 @@ public static DescriptorsProtos.FunctionDescriptor toProtobuf(FunctionDescriptor
138142
return builder.build();
139143
}
140144

141-
public static DescriptorsProtos.ParameterDescriptor toProtobuf(FunctionDescriptor.Parameter parameterDescriptor) {
145+
private static DescriptorsProtos.ParameterDescriptor toProtobuf(FunctionDescriptor.Parameter parameterDescriptor) {
142146
DescriptorsProtos.ParameterDescriptor.Builder builder = DescriptorsProtos.ParameterDescriptor.newBuilder()
143147
.setHasDefaultValue(parameterDescriptor.hasDefaultValue())
144148
.setIsKeywordVariadic(parameterDescriptor.isKeywordVariadic())
@@ -152,14 +156,19 @@ public static DescriptorsProtos.ParameterDescriptor toProtobuf(FunctionDescripto
152156
if (annotatedType != null) {
153157
builder.setAnnotatedType(annotatedType);
154158
}
159+
TypeAnnotationDescriptor typeDescriptor = parameterDescriptor.descriptor();
160+
if (typeDescriptor != null) {
161+
builder.setTypeAnnotationDescriptor(toProtobuf(typeDescriptor));
162+
}
155163
LocationInFile location = parameterDescriptor.location();
156164
if (location != null) {
157165
builder.setDefinitionLocation(toProtobuf(location));
158166
}
159167
return builder.build();
160168
}
161169

162-
public static DescriptorsProtos.VarDescriptor toProtobuf(VariableDescriptor variableDescriptor) {
170+
@VisibleForTesting
171+
static DescriptorsProtos.VarDescriptor toProtobuf(VariableDescriptor variableDescriptor) {
163172
DescriptorsProtos.VarDescriptor.Builder builder = DescriptorsProtos.VarDescriptor.newBuilder();
164173
builder.setName(variableDescriptor.name());
165174
String fullyQualifiedName = variableDescriptor.fullyQualifiedName();
@@ -173,7 +182,7 @@ public static DescriptorsProtos.VarDescriptor toProtobuf(VariableDescriptor vari
173182
return builder.build();
174183
}
175184

176-
public static DescriptorsProtos.LocationInFile toProtobuf(LocationInFile locationInFile) {
185+
private static DescriptorsProtos.LocationInFile toProtobuf(LocationInFile locationInFile) {
177186
return DescriptorsProtos.LocationInFile.newBuilder()
178187
.setFileId(locationInFile.fileId())
179188
.setStartLine(locationInFile.startLine())
@@ -183,6 +192,19 @@ public static DescriptorsProtos.LocationInFile toProtobuf(LocationInFile locatio
183192
.build();
184193
}
185194

195+
private static SymbolsProtos.Type toProtobuf(TypeAnnotationDescriptor typeAnnotationDescriptor) {
196+
SymbolsProtos.Type.Builder builder = SymbolsProtos.Type.newBuilder()
197+
.setPrettyPrintedName(typeAnnotationDescriptor.prettyPrintedName())
198+
.setKind(DescriptorUtils.typeAnnotationKindToSymbolKind(typeAnnotationDescriptor.kind()))
199+
.addAllArgs(typeAnnotationDescriptor.args().stream().map(DescriptorsToProtobuf::toProtobuf).toList())
200+
.setIsSelf(typeAnnotationDescriptor.isSelf());
201+
String fullyQualifiedName = typeAnnotationDescriptor.fullyQualifiedName();
202+
if (fullyQualifiedName != null) {
203+
builder.setFullyQualifiedName(fullyQualifiedName);
204+
}
205+
return builder.build();
206+
}
207+
186208
public static Set<Descriptor> fromProtobuf(DescriptorsProtos.ModuleDescriptor moduleDescriptorProto) {
187209
Set<Descriptor> descriptors = new HashSet<>();
188210
moduleDescriptorProto.getClassDescriptorsList().forEach(proto -> descriptors.add(fromProtobuf(proto)));
@@ -192,7 +214,7 @@ public static Set<Descriptor> fromProtobuf(DescriptorsProtos.ModuleDescriptor mo
192214
return descriptors;
193215
}
194216

195-
public static AmbiguousDescriptor fromProtobuf(DescriptorsProtos.AmbiguousDescriptor ambiguousDescriptor) {
217+
private static AmbiguousDescriptor fromProtobuf(DescriptorsProtos.AmbiguousDescriptor ambiguousDescriptor) {
196218
String fullyQualifiedName = ambiguousDescriptor.hasFullyQualifiedName() ? ambiguousDescriptor.getFullyQualifiedName() : null;
197219
Set<Descriptor> descriptors = new HashSet<>();
198220
ambiguousDescriptor.getClassDescriptorsList().forEach(proto -> descriptors.add(fromProtobuf(proto)));
@@ -201,11 +223,10 @@ public static AmbiguousDescriptor fromProtobuf(DescriptorsProtos.AmbiguousDescri
201223
return new AmbiguousDescriptor(
202224
ambiguousDescriptor.getName(),
203225
fullyQualifiedName,
204-
descriptors
205-
);
226+
descriptors);
206227
}
207228

208-
public static ClassDescriptor fromProtobuf(DescriptorsProtos.ClassDescriptor classDescriptorProto) {
229+
private static ClassDescriptor fromProtobuf(DescriptorsProtos.ClassDescriptor classDescriptorProto) {
209230
String metaclassFQN = classDescriptorProto.hasMetaClassFQN() ? classDescriptorProto.getMetaClassFQN() : null;
210231
LocationInFile definitionLocation = classDescriptorProto.hasDefinitionLocation() ? fromProtobuf(classDescriptorProto.getDefinitionLocation()) : null;
211232
String fullyQualifiedName = classDescriptorProto.getFullyQualifiedName();
@@ -228,7 +249,7 @@ public static ClassDescriptor fromProtobuf(DescriptorsProtos.ClassDescriptor cla
228249
.build();
229250
}
230251

231-
public static FunctionDescriptor fromProtobuf(DescriptorsProtos.FunctionDescriptor functionDescriptorProto) {
252+
private static FunctionDescriptor fromProtobuf(DescriptorsProtos.FunctionDescriptor functionDescriptorProto) {
232253
String fullyQualifiedName = functionDescriptorProto.getFullyQualifiedName();
233254
List<FunctionDescriptor.Parameter> parameters = new ArrayList<>();
234255
functionDescriptorProto.getParametersList().forEach(proto -> parameters.add(fromProtobuf(proto)));
@@ -243,43 +264,63 @@ public static FunctionDescriptor fromProtobuf(DescriptorsProtos.FunctionDescript
243264
new ArrayList<>(functionDescriptorProto.getDecoratorsList()),
244265
functionDescriptorProto.getHasDecorators(),
245266
definitionLocation,
246-
annotatedReturnTypeName
247-
);
267+
annotatedReturnTypeName,
268+
// TypeAnnotationDescriptor is not serialized in protobuf
269+
null);
248270
}
249271

250-
public static FunctionDescriptor.Parameter fromProtobuf(DescriptorsProtos.ParameterDescriptor parameterDescriptorProto) {
272+
private static FunctionDescriptor.Parameter fromProtobuf(DescriptorsProtos.ParameterDescriptor parameterDescriptorProto) {
251273
String name = parameterDescriptorProto.hasName() ? parameterDescriptorProto.getName() : null;
252274
String annotatedType = parameterDescriptorProto.hasAnnotatedType() ? parameterDescriptorProto.getAnnotatedType() : null;
253275
LocationInFile location = parameterDescriptorProto.hasDefinitionLocation() ? fromProtobuf(parameterDescriptorProto.getDefinitionLocation()) : null;
276+
TypeAnnotationDescriptor typeAnnotationDescriptor = parameterDescriptorProto.hasTypeAnnotationDescriptor()
277+
? fromProtobuf(parameterDescriptorProto.getTypeAnnotationDescriptor())
278+
: null;
254279
return new FunctionDescriptor.Parameter(
255280
name,
256281
annotatedType,
282+
typeAnnotationDescriptor,
257283
parameterDescriptorProto.getHasDefaultValue(),
258284
parameterDescriptorProto.getIsKeywordOnly(),
259285
parameterDescriptorProto.getIsPositionalOnly(),
260286
parameterDescriptorProto.getIsPositionalVariadic(),
261287
parameterDescriptorProto.getIsKeywordVariadic(),
262-
location
263-
);
288+
location);
264289
}
265290

266-
public static VariableDescriptor fromProtobuf(DescriptorsProtos.VarDescriptor varDescriptorProto) {
291+
@VisibleForTesting
292+
static VariableDescriptor fromProtobuf(DescriptorsProtos.VarDescriptor varDescriptorProto) {
267293
String fullyQualifiedName = varDescriptorProto.hasFullyQualifiedName() ? varDescriptorProto.getFullyQualifiedName() : null;
268294
String annotatedType = varDescriptorProto.hasAnnotatedType() ? varDescriptorProto.getAnnotatedType() : null;
269295
return new VariableDescriptor(
270296
varDescriptorProto.getName(),
271297
fullyQualifiedName,
272-
annotatedType
273-
);
298+
annotatedType);
274299
}
275300

276-
public static LocationInFile fromProtobuf(DescriptorsProtos.LocationInFile locationInFileProto) {
301+
private static LocationInFile fromProtobuf(DescriptorsProtos.LocationInFile locationInFileProto) {
277302
return new LocationInFile(
278303
locationInFileProto.getFileId(),
279304
locationInFileProto.getStartLine(),
280305
locationInFileProto.getStartLineOffset(),
281306
locationInFileProto.getEndLine(),
282-
locationInFileProto.getEndLineOffset()
283-
);
307+
locationInFileProto.getEndLineOffset());
308+
}
309+
310+
@CheckForNull
311+
private static TypeAnnotationDescriptor fromProtobuf(SymbolsProtos.Type typeProto) {
312+
String fullyQualifiedName = typeProto.hasFullyQualifiedName() ? typeProto.getFullyQualifiedName() : null;
313+
var kind = DescriptorUtils.symbolTypeKindToTypeAnnotationKind(typeProto.getKind());
314+
if (kind == null) {
315+
return null;
316+
}
317+
List<TypeAnnotationDescriptor> args = new ArrayList<>();
318+
typeProto.getArgsList().forEach(proto -> args.add(fromProtobuf(proto)));
319+
return new TypeAnnotationDescriptor(
320+
typeProto.getPrettyPrintedName(),
321+
kind,
322+
args,
323+
fullyQualifiedName,
324+
typeProto.getIsSelf());
284325
}
285326
}

python-frontend/src/main/java/org/sonar/python/index/FunctionDescriptor.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,17 +116,19 @@ public static class Parameter {
116116

117117
private final String name;
118118
private final String annotatedType;
119+
private final TypeAnnotationDescriptor descriptor;
119120
private final boolean hasDefaultValue;
120121
private final boolean isKeywordVariadic;
121122
private final boolean isPositionalVariadic;
122123
private final boolean isKeywordOnly;
123124
private final boolean isPositionalOnly;
124125
private final LocationInFile location;
125126

126-
public Parameter(@Nullable String name, @Nullable String annotatedType, boolean hasDefaultValue,
127+
public Parameter(@Nullable String name, @Nullable String annotatedType, @Nullable TypeAnnotationDescriptor descriptor, boolean hasDefaultValue,
127128
boolean isKeywordOnly, boolean isPositionalOnly, boolean isPositionalVariadic, boolean isKeywordVariadic, @Nullable LocationInFile location) {
128129
this.name = name;
129130
this.annotatedType = annotatedType;
131+
this.descriptor = descriptor;
130132
this.hasDefaultValue = hasDefaultValue;
131133
this.isKeywordVariadic = isKeywordVariadic;
132134
this.isPositionalVariadic = isPositionalVariadic;
@@ -172,6 +174,11 @@ public boolean isPositionalVariadic() {
172174
public LocationInFile location() {
173175
return location;
174176
}
177+
178+
@CheckForNull
179+
public TypeAnnotationDescriptor descriptor() {
180+
return descriptor;
181+
}
175182
}
176183

177184
public static class FunctionDescriptorBuilder {

python-frontend/src/main/java/org/sonar/python/semantic/v2/converter/ParameterConverter.java

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,26 +17,23 @@
1717
package org.sonar.python.semantic.v2.converter;
1818

1919
import java.util.Optional;
20-
import org.sonar.plugins.python.api.types.v2.ObjectType;
2120
import org.sonar.plugins.python.api.types.v2.ParameterV2;
2221
import org.sonar.plugins.python.api.types.v2.PythonType;
2322
import org.sonar.plugins.python.api.types.v2.TypeWrapper;
2423
import org.sonar.python.index.FunctionDescriptor;
25-
import org.sonar.python.types.v2.LazyTypeWrapper;
26-
import org.sonar.python.types.v2.SimpleTypeWrapper;
2724

2825
public class ParameterConverter {
2926

30-
public ParameterV2 convert(ConversionContext ctx, FunctionDescriptor.Parameter parameter) {
31-
var typeWrapper = Optional.ofNullable(parameter.annotatedType())
32-
.map(fqn -> (PythonType) ctx.lazyTypesContext().getOrCreateLazyType(fqn))
33-
.map(lt -> (TypeWrapper) new LazyTypeWrapper(lt))
34-
.orElseGet(() -> new SimpleTypeWrapper(PythonType.UNKNOWN));
27+
private final TypeAnnotationToPythonTypeConverter typeAnnotationConverter = new TypeAnnotationToPythonTypeConverter();
3528

36-
var type = ObjectType.Builder.fromTypeWrapper(typeWrapper).build();
29+
public ParameterV2 convert(ConversionContext ctx, FunctionDescriptor.Parameter parameter) {
30+
// Prefer TypeAnnotationDescriptor if available, otherwise use legacy string-based approach
31+
var type = getTypeFromDescriptor(ctx, parameter)
32+
.or(() -> getTypeFromFqn(ctx, parameter))
33+
.orElseGet(() -> PythonType.UNKNOWN);
3734

3835
return new ParameterV2(parameter.name(),
39-
new SimpleTypeWrapper(type),
36+
TypeWrapper.of(type),
4037
parameter.hasDefaultValue(),
4138
parameter.isKeywordOnly(),
4239
parameter.isPositionalOnly(),
@@ -45,4 +42,14 @@ public ParameterV2 convert(ConversionContext ctx, FunctionDescriptor.Parameter p
4542
parameter.location());
4643
}
4744

45+
private Optional<PythonType> getTypeFromDescriptor(ConversionContext ctx, FunctionDescriptor.Parameter parameter) {
46+
return Optional.ofNullable(parameter.descriptor())
47+
.map(typeAnnotationDescriptor -> typeAnnotationConverter.convert(ctx, typeAnnotationDescriptor));
48+
}
49+
50+
private static Optional<PythonType> getTypeFromFqn(ConversionContext ctx, FunctionDescriptor.Parameter parameter) {
51+
return Optional.ofNullable(parameter.annotatedType())
52+
.map(fqn -> (PythonType) ctx.lazyTypesContext().getOrCreateLazyType(fqn));
53+
}
54+
4855
}

0 commit comments

Comments
 (0)