Skip to content

Commit c6bbe47

Browse files
authored
Merge pull request #126 from sourcegraph/nsc/type-signatures
2 parents 17a7d1d + 9c9a706 commit c6bbe47

File tree

12 files changed

+526
-181
lines changed

12 files changed

+526
-181
lines changed

semanticdb-javac/src/main/java/com/sourcegraph/semanticdb_javac/GlobalSymbolsCache.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ public String semanticdbSymbol(Symbol sym, LocalSymbolsCache locals) {
3030
return result;
3131
}
3232

33+
public String semanticdbSymbol(Element element, LocalSymbolsCache locals) {
34+
return semanticdbSymbol((Symbol) element, locals);
35+
}
36+
3337
public boolean isNone(Symbol sym) {
3438
return sym == null;
3539
}
Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
package com.sourcegraph.semanticdb_javac;
2+
3+
import com.sun.tools.javac.code.Symbol;
4+
import com.sun.tools.javac.code.Type;
5+
import com.sourcegraph.semanticdb_javac.Semanticdb.*;
6+
7+
import javax.lang.model.element.Element;
8+
import javax.lang.model.type.*;
9+
import javax.lang.model.type.IntersectionType;
10+
import javax.lang.model.util.SimpleTypeVisitor8;
11+
import java.util.ArrayList;
12+
import java.util.List;
13+
14+
public final class SemanticdbSignatures {
15+
private final GlobalSymbolsCache cache;
16+
private final LocalSymbolsCache locals;
17+
18+
private static final Semanticdb.Type UNRESOLVED_TYPE_REF =
19+
Semanticdb.Type.newBuilder()
20+
.setTypeRef(TypeRef.newBuilder().setSymbol("unresolved_type#"))
21+
.build();
22+
23+
public SemanticdbSignatures(GlobalSymbolsCache cache, LocalSymbolsCache locals) {
24+
this.cache = cache;
25+
this.locals = locals;
26+
}
27+
28+
public Signature generateSignature(Symbol sym) {
29+
if (sym instanceof Symbol.ClassSymbol) {
30+
return generateClassSignature((Symbol.ClassSymbol) sym);
31+
} else if (sym instanceof Symbol.MethodSymbol) {
32+
return generateMethodSignature((Symbol.MethodSymbol) sym);
33+
} else if (sym instanceof Symbol.VarSymbol) {
34+
return generateFieldSignature((Symbol.VarSymbol) sym);
35+
} else if (sym instanceof Symbol.TypeVariableSymbol) {
36+
return generateTypeSignature((Symbol.TypeVariableSymbol) sym);
37+
}
38+
return null;
39+
}
40+
41+
private Signature generateClassSignature(Symbol.ClassSymbol sym) {
42+
ClassSignature.Builder builder = ClassSignature.newBuilder();
43+
44+
builder.setTypeParameters(generateScope(sym.getTypeParameters()));
45+
46+
if (sym.getSuperclass() != Type.noType) {
47+
Semanticdb.Type superType = generateType(sym.getSuperclass());
48+
if (superType == null) {
49+
superType = UNRESOLVED_TYPE_REF;
50+
}
51+
builder.addParents(superType);
52+
}
53+
for (Type iType : sym.getInterfaces()) {
54+
Semanticdb.Type type = generateType(iType);
55+
if (type == null) {
56+
type = UNRESOLVED_TYPE_REF;
57+
}
58+
builder.addParents(type);
59+
}
60+
61+
Scope.Builder declarations = Scope.newBuilder();
62+
for (Symbol enclosed : sym.getEnclosedElements()) {
63+
declarations.addSymlinks(cache.semanticdbSymbol(enclosed, locals));
64+
}
65+
builder.setDeclarations(generateScope(sym.getEnclosedElements()));
66+
67+
return Signature.newBuilder().setClassSignature(builder).build();
68+
}
69+
70+
private Signature generateMethodSignature(Symbol.MethodSymbol sym) {
71+
MethodSignature.Builder builder = MethodSignature.newBuilder();
72+
73+
builder.setTypeParameters(generateScope(sym.getTypeParameters()));
74+
75+
builder.addParameterLists(generateScope(sym.params()));
76+
77+
Semanticdb.Type returnType = generateType(sym.getReturnType());
78+
if (returnType != null) {
79+
builder.setReturnType(returnType);
80+
}
81+
82+
return Signature.newBuilder().setMethodSignature(builder).build();
83+
}
84+
85+
private Signature generateFieldSignature(Symbol.VarSymbol sym) {
86+
Semanticdb.Type generateType = generateType(sym.type);
87+
if (generateType == null) {
88+
generateType = UNRESOLVED_TYPE_REF;
89+
}
90+
return Signature.newBuilder()
91+
.setValueSignature(ValueSignature.newBuilder().setTpe(generateType))
92+
.build();
93+
}
94+
95+
private Signature generateTypeSignature(Symbol.TypeVariableSymbol sym) {
96+
TypeSignature.Builder builder = TypeSignature.newBuilder();
97+
98+
builder.setTypeParameters(generateScope(sym.getTypeParameters()));
99+
100+
Semanticdb.Type upperBound = generateType(sym.type.getUpperBound());
101+
if (upperBound != null) builder.setUpperBound(upperBound);
102+
else builder.setUpperBound(UNRESOLVED_TYPE_REF);
103+
104+
return Signature.newBuilder().setTypeSignature(builder).build();
105+
}
106+
107+
private <T extends Element> Scope generateScope(List<T> elements) {
108+
Scope.Builder scope = Scope.newBuilder();
109+
for (T typeVar : elements) {
110+
String s = cache.semanticdbSymbol(typeVar, locals);
111+
scope.addSymlinks(s);
112+
}
113+
return scope.build();
114+
}
115+
116+
private Semanticdb.Type generateType(TypeMirror mirror) {
117+
return mirror.accept(new SemanticdbTypeVisitor(cache, locals), null);
118+
}
119+
120+
/** A TypeMirror tree visitor that constructs a recursive SemanticDB Type structure. */
121+
private static class SemanticdbTypeVisitor extends SimpleTypeVisitor8<Semanticdb.Type, Void> {
122+
private final GlobalSymbolsCache cache;
123+
private final LocalSymbolsCache locals;
124+
125+
private SemanticdbTypeVisitor(GlobalSymbolsCache cache, LocalSymbolsCache locals) {
126+
this.cache = cache;
127+
this.locals = locals;
128+
}
129+
130+
@Override
131+
public Semanticdb.Type visitDeclared(DeclaredType t, Void unused) {
132+
boolean isExistential =
133+
t.getTypeArguments().stream().anyMatch((type) -> type instanceof WildcardType);
134+
135+
ArrayList<Semanticdb.Type> typeParams = new ArrayList<>();
136+
Scope.Builder declarations = Scope.newBuilder();
137+
for (TypeMirror type : t.getTypeArguments()) {
138+
Semanticdb.Type visited = super.visit(type);
139+
if (visited == null) {
140+
visited = UNRESOLVED_TYPE_REF;
141+
}
142+
typeParams.add(visited);
143+
144+
if (type instanceof WildcardType) {
145+
TypeSignature.Builder typeSig = TypeSignature.newBuilder();
146+
WildcardType wildcardType = (WildcardType) type;
147+
148+
// semanticdb spec asks for List() not None for type_parameters field
149+
typeSig.setTypeParameters(Scope.newBuilder());
150+
151+
if (wildcardType.getExtendsBound() != null) {
152+
typeSig.setUpperBound(super.visit(wildcardType.getExtendsBound()));
153+
} else if (wildcardType.getSuperBound() != null) {
154+
typeSig.setLowerBound(super.visit(wildcardType.getSuperBound()));
155+
}
156+
157+
declarations.addHardlinks(
158+
SymbolInformation.newBuilder()
159+
.setSymbol("local_wildcard")
160+
.setSignature(Signature.newBuilder().setTypeSignature(typeSig)));
161+
} else {
162+
declarations.addSymlinks(cache.semanticdbSymbol(((Type) type).asElement(), locals));
163+
}
164+
}
165+
166+
if (!isExistential) {
167+
return Semanticdb.Type.newBuilder()
168+
.setTypeRef(
169+
TypeRef.newBuilder()
170+
.setSymbol(cache.semanticdbSymbol(t.asElement(), locals))
171+
.addAllTypeArguments(typeParams))
172+
.build();
173+
} else {
174+
return Semanticdb.Type.newBuilder()
175+
.setExistentialType(
176+
ExistentialType.newBuilder()
177+
.setTpe(
178+
Semanticdb.Type.newBuilder()
179+
.setTypeRef(
180+
TypeRef.newBuilder()
181+
.setSymbol(cache.semanticdbSymbol(t.asElement(), locals))
182+
.addAllTypeArguments(typeParams)))
183+
.setDeclarations(declarations))
184+
.build();
185+
}
186+
}
187+
188+
@Override
189+
public Semanticdb.Type visitArray(ArrayType t, Void unused) {
190+
Semanticdb.Type arrayComponentType = super.visit(t.getComponentType());
191+
if (arrayComponentType == null) {
192+
arrayComponentType = UNRESOLVED_TYPE_REF;
193+
}
194+
return Semanticdb.Type.newBuilder()
195+
.setTypeRef(
196+
TypeRef.newBuilder().setSymbol("scala/Array#").addTypeArguments(arrayComponentType))
197+
.build();
198+
}
199+
200+
@Override
201+
public Semanticdb.Type visitPrimitive(PrimitiveType t, Void unused) {
202+
return Semanticdb.Type.newBuilder()
203+
.setTypeRef(TypeRef.newBuilder().setSymbol(primitiveSymbol(t.getKind())))
204+
.build();
205+
}
206+
207+
@Override
208+
public Semanticdb.Type visitTypeVariable(TypeVariable t, Void unused) {
209+
return Semanticdb.Type.newBuilder()
210+
.setTypeRef(
211+
TypeRef.newBuilder().setSymbol(cache.semanticdbSymbol(t.asElement(), locals)).build())
212+
.build();
213+
}
214+
215+
@Override
216+
public Semanticdb.Type visitIntersection(IntersectionType t, Void unused) {
217+
ArrayList<Semanticdb.Type> types = new ArrayList<>();
218+
for (TypeMirror type : t.getBounds()) {
219+
types.add(super.visit(type));
220+
}
221+
222+
return Semanticdb.Type.newBuilder()
223+
.setIntersectionType(Semanticdb.IntersectionType.newBuilder().addAllTypes(types).build())
224+
.build();
225+
}
226+
227+
@Override
228+
public Semanticdb.Type visitWildcard(WildcardType t, Void unused) {
229+
return Semanticdb.Type.newBuilder()
230+
.setTypeRef(
231+
// https://github.com/scalameta/scalameta/issues/1703
232+
// https://sourcegraph.com/github.com/scalameta/scalameta/-/blob/semanticdb/metacp/src/main/scala/scala/meta/internal/javacp/Javacp.scala#L452:19
233+
TypeRef.newBuilder().setSymbol("local_wildcard").build())
234+
.build();
235+
}
236+
237+
public String primitiveSymbol(TypeKind kind) {
238+
switch (kind) {
239+
case BOOLEAN:
240+
return "scala/Boolean#";
241+
case BYTE:
242+
return "scala/Byte#";
243+
case SHORT:
244+
return "scala/Short#";
245+
case INT:
246+
return "scala/Int#";
247+
case LONG:
248+
return "scala/Long#";
249+
case CHAR:
250+
return "scala/Char#";
251+
case FLOAT:
252+
return "scala/Float#";
253+
case DOUBLE:
254+
return "scala/Double#";
255+
case VOID:
256+
return "scala/Unit#";
257+
default:
258+
throw new IllegalArgumentException("got " + kind.name());
259+
}
260+
}
261+
}
262+
}

semanticdb-javac/src/main/java/com/sourcegraph/semanticdb_javac/SemanticdbVisitor.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ private void emitSymbolInformation(Symbol sym) {
8282
Semanticdb.SymbolInformation.newBuilder().setSymbol(semanticdbSymbol(sym));
8383
Semanticdb.Documentation documentation = semanticdbDocumentation(sym);
8484
if (documentation != null) builder.setDocumentation(documentation);
85+
Semanticdb.Signature signature = semanticdbSignature(sym);
86+
if (signature != null) builder.setSignature(signature);
8587

8688
Semanticdb.SymbolInformation info = builder.build();
8789

@@ -179,6 +181,10 @@ public Void visitNewClass(NewClassTree node, Void unused) {
179181
// Utilities to generate SemanticDB data structures.
180182
// =================================================
181183

184+
private Semanticdb.Signature semanticdbSignature(Symbol sym) {
185+
return new SemanticdbSignatures(globals, locals).generateSignature(sym);
186+
}
187+
182188
private String semanticdbSymbol(Symbol sym) {
183189
return globals.semanticdbSymbol(sym, locals);
184190
}

semanticdb-javac/src/main/protobuf/semanticdb.proto

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,37 @@ message Range {
4141
int32 end_character = 4;
4242
}
4343

44+
message Signature {
45+
oneof sealed_value {
46+
ClassSignature class_signature = 1;
47+
MethodSignature method_signature = 2;
48+
TypeSignature type_signature = 3;
49+
ValueSignature value_signature = 4;
50+
}
51+
}
52+
53+
message ClassSignature {
54+
Scope type_parameters = 1;
55+
repeated Type parents = 2;
56+
Scope declarations = 4;
57+
}
58+
59+
message MethodSignature {
60+
Scope type_parameters = 1;
61+
repeated Scope parameter_lists = 2;
62+
Type return_type = 3;
63+
}
64+
65+
message TypeSignature {
66+
Scope type_parameters = 1;
67+
Type lower_bound = 2;
68+
Type upper_bound = 3;
69+
}
70+
71+
message ValueSignature {
72+
Type tpe = 1;
73+
}
74+
4475
message SymbolInformation {
4576
enum Kind {
4677
reserved 1, 2, 4, 5, 15, 16;
@@ -86,6 +117,7 @@ message SymbolInformation {
86117
Kind kind = 3;
87118
int32 properties = 4;
88119
string display_name = 5;
120+
Signature signature = 17;
89121
repeated string overridden_symbols = 19;
90122
Documentation documentation = 20;
91123
}
@@ -113,3 +145,30 @@ message SymbolOccurrence {
113145
Role role = 3;
114146
}
115147

148+
message Scope {
149+
repeated string symlinks = 1;
150+
repeated SymbolInformation hardlinks = 2;
151+
}
152+
153+
message Type {
154+
reserved 1, 3, 4, 5, 6, 11, 12, 15, 16;
155+
oneof sealed_value {
156+
TypeRef type_ref = 2;
157+
IntersectionType intersection_type = 17;
158+
ExistentialType existential_type = 9;
159+
}
160+
}
161+
162+
message TypeRef {
163+
string symbol = 2;
164+
repeated Type type_arguments = 3;
165+
}
166+
message IntersectionType {
167+
repeated Type types = 1;
168+
}
169+
170+
message ExistentialType {
171+
reserved 2;
172+
Type tpe = 1;
173+
Scope declarations = 3;
174+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
package minimized;
22

3+
import java.util.Map;
4+
35
public class ParameterizedTypes<A, B> {
46
public String app(A a, B b) {
57
return a.toString() + b;
68
}
9+
10+
public Map<? extends String, ?> doStuff() { return null; }
711
}

0 commit comments

Comments
 (0)