Skip to content

Commit 98671e8

Browse files
committed
Index SemanticDB synthetics
Previously, lsif-java only indexed the "Occurrences" section of a SemanticDB `TextDocument`. Now, we additionall index the "Synthetics" section, which includes inferred expression from Scala implicits, for comprehensions, `.apply` methods and more. The benefit of this fix is that "find references" on an implicit or an `apply` method now shows usages where those symbols got inferred (the user didn't explicitly write a reference to them). It could be that this fix alone is not sufficient to enable "find references" to show results on Sourcegraph. The Sourcegraph backend will also need to accept offset ranges with zero length.
1 parent 6aeae65 commit 98671e8

File tree

19 files changed

+437
-23
lines changed

19 files changed

+437
-23
lines changed

build.sbt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,8 @@ lazy val minimizedScala = project
313313
.in(file("tests/minimized-scala"))
314314
.settings(
315315
(publish / skip) := true,
316-
semanticdbOptions ++= List("-P:semanticdb:text:on")
316+
semanticdbOptions ++=
317+
List("-P:semanticdb:text:on", "-P:semanticdb:synthetics:on")
317318
)
318319
.dependsOn(minimized)
319320

lsif-java/src/main/scala/com/sourcegraph/lsif_java/SemanticdbPrinters.scala

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package com.sourcegraph.lsif_java
33
import scala.jdk.CollectionConverters._
44

55
import com.sourcegraph.lsif_java.commands.CommentSyntax
6+
import com.sourcegraph.lsif_semanticdb.LsifTextDocument
67
import com.sourcegraph.lsif_semanticdb.SignatureFormatter
78
import com.sourcegraph.lsif_semanticdb.Symtab
89
import com.sourcegraph.semanticdb_javac.Semanticdb.SymbolOccurrence
@@ -14,8 +15,8 @@ object SemanticdbPrinters {
1415
doc: TextDocument,
1516
comments: CommentSyntax = CommentSyntax.default
1617
): String = {
17-
val occurrencesByLine = doc
18-
.getOccurrencesList
18+
val occurrencesByLine = LsifTextDocument
19+
.sortedSymbolOccurrences(doc)
1920
.asScala
2021
.groupBy(_.getRange.getStartLine)
2122
val out = new StringBuilder()
@@ -80,13 +81,7 @@ object SemanticdbPrinters {
8081
.append(occ.getSymbol)
8182
.append(
8283
if (isMultiline)
83-
" "
84-
else
85-
""
86-
)
87-
.append(
88-
if (isMultiline)
89-
s"${r.getEndLine - r.getStartLine}:${r.getEndCharacter}"
84+
s" ${r.getEndLine - r.getStartLine}:${r.getEndCharacter}"
9085
else
9186
""
9287
)
@@ -102,7 +97,10 @@ object SemanticdbPrinters {
10297
""
10398
}
10499
)
105-
.append("\n")
100+
while (out.last == ' ') { // Trim trailing whitespace
101+
out.setLength(out.length() - 1)
102+
}
103+
out.append("\n")
106104
}
107105

108106
}

lsif-semanticdb/src/main/java/com/sourcegraph/lsif_semanticdb/LsifSemanticdb.java

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import com.sourcegraph.semanticdb_javac.Semanticdb.SymbolOccurrence;
88
import com.sourcegraph.semanticdb_javac.Semanticdb.SymbolOccurrence.Role;
99
import com.sourcegraph.semanticdb_javac.SemanticdbSymbols;
10+
1011
import java.io.IOException;
1112
import java.nio.file.Files;
1213
import java.nio.file.Path;
@@ -189,9 +190,7 @@ private Integer processDocumentUnsafe(
189190

190191
private Stream<LsifTextDocument> parseTextDocument(Path semanticdbPath) {
191192
try {
192-
CodedInputStream in = CodedInputStream.newInstance(Files.readAllBytes(semanticdbPath));
193-
in.setRecursionLimit(1000);
194-
return Semanticdb.TextDocuments.parseFrom(in).getDocumentsList().stream()
193+
return textDocumentsParseFrom(semanticdbPath).getDocumentsList().stream()
195194
.filter(sdb -> !sdb.getOccurrencesList().isEmpty())
196195
.map(sdb -> new LsifTextDocument(semanticdbPath, sdb, options.sourceroot));
197196
} catch (IOException e) {
@@ -200,4 +199,19 @@ private Stream<LsifTextDocument> parseTextDocument(Path semanticdbPath) {
200199
return Stream.empty();
201200
}
202201
}
202+
203+
private Semanticdb.TextDocuments textDocumentsParseFrom(Path semanticdbPath) throws IOException {
204+
byte[] bytes = Files.readAllBytes(semanticdbPath);
205+
try {
206+
CodedInputStream in = CodedInputStream.newInstance(bytes);
207+
in.setRecursionLimit(1000);
208+
return Semanticdb.TextDocuments.parseFrom(in);
209+
} catch (NoSuchMethodError ignored) {
210+
// NOTE(olafur): For some reason, NoSuchMethodError gets thrown when running `snapshots/run`
211+
// in the sbt build. I'm unable to reproduce the error in `snapshots/test` or when running the
212+
// published version
213+
// of `lsif-java index`.
214+
return Semanticdb.TextDocuments.parseFrom(bytes);
215+
}
216+
}
203217
}

lsif-semanticdb/src/main/java/com/sourcegraph/lsif_semanticdb/LsifTextDocument.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,42 @@ public String toString() {
3737
}
3838

3939
public List<Semanticdb.SymbolOccurrence> sortedSymbolOccurrences() {
40+
return LsifTextDocument.sortedSymbolOccurrences(semanticdb);
41+
}
42+
43+
public static List<Semanticdb.SymbolOccurrence> sortedSymbolOccurrences(
44+
Semanticdb.TextDocument semanticdb) {
4045
ArrayList<Semanticdb.SymbolOccurrence> result =
4146
new ArrayList<>(semanticdb.getOccurrencesList().size());
4247
result.addAll(semanticdb.getOccurrencesList());
48+
for (Semanticdb.Synthetic synthetic : semanticdb.getSyntheticsList()) {
49+
addAllSyntheticOccurrences(synthetic, result);
50+
}
4351
result.sort((o1, o2) -> new RangeComparator().compare(o1.getRange(), o2.getRange()));
4452
return result;
4553
}
4654

55+
private static void addAllSyntheticOccurrences(
56+
Semanticdb.Synthetic synthetic, ArrayList<Semanticdb.SymbolOccurrence> buffer) {
57+
Semanticdb.Range offsetRange =
58+
Semanticdb.Range.newBuilder(synthetic.getRange())
59+
.setStartLine(synthetic.getRange().getEndLine())
60+
.setStartCharacter(synthetic.getRange().getEndCharacter())
61+
.build();
62+
new SemanticdbTreeVisitor() {
63+
@Override
64+
void visitIdTree(Semanticdb.IdTree tree) {
65+
Semanticdb.SymbolOccurrence syntheticOccurrence =
66+
Semanticdb.SymbolOccurrence.newBuilder()
67+
.setRange(offsetRange)
68+
.setSymbol(tree.getSymbol())
69+
.setRole(Semanticdb.SymbolOccurrence.Role.REFERENCE)
70+
.build();
71+
buffer.add(syntheticOccurrence);
72+
}
73+
}.visitTree(synthetic.getTree());
74+
}
75+
4776
private void setSemanticdb(Semanticdb.TextDocument semanticdb) {
4877
this.semanticdb = semanticdb;
4978
for (Semanticdb.SymbolInformation info : semanticdb.getSymbolsList()) {
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package com.sourcegraph.lsif_semanticdb;
2+
3+
import com.sourcegraph.semanticdb_javac.Semanticdb.*;
4+
5+
public abstract class SemanticdbTreeVisitor {
6+
public void visitTree(Tree tree) {
7+
if (tree.hasApplyTree()) {
8+
this.visitApplyTree(tree.getApplyTree());
9+
} else if (tree.hasFunctionTree()) {
10+
this.visitFunctionTree(tree.getFunctionTree());
11+
} else if (tree.hasIdTree()) {
12+
this.visitIdTree(tree.getIdTree());
13+
} else if (tree.hasLiteralTree()) {
14+
this.visitLiteralTree(tree.getLiteralTree());
15+
} else if (tree.hasMacroExpansionTree()) {
16+
this.visitMacroExpansionTree(tree.getMacroExpansionTree());
17+
} else if (tree.hasOriginalTree()) {
18+
this.visitOriginalTree(tree.getOriginalTree());
19+
} else if (tree.hasSelectTree()) {
20+
this.visitSelectTree(tree.getSelectTree());
21+
} else if (tree.hasTypeApplyTree()) {
22+
this.visitTypeApplyTree(tree.getTypeApplyTree());
23+
} else if (tree.hasAnnotationTree()) {
24+
this.visitAnnotationTree(tree.getAnnotationTree());
25+
} else if (tree.hasAssignTree()) {
26+
this.visitAssignTree(tree.getAssignTree());
27+
} else if (tree.hasBinopTree()) {
28+
this.visitBinaryOperatorTree(tree.getBinopTree());
29+
}
30+
}
31+
32+
void visitApplyTree(ApplyTree tree) {
33+
visitTree(tree.getFunction());
34+
for (Tree argument : tree.getArgumentsList()) {
35+
visitTree(argument);
36+
}
37+
}
38+
39+
void visitFunctionTree(FunctionTree tree) {
40+
for (IdTree parameter : tree.getParametersList()) {
41+
visitIdTree(parameter);
42+
}
43+
visitTree(tree.getBody());
44+
}
45+
46+
void visitIdTree(IdTree tree) {}
47+
48+
void visitLiteralTree(LiteralTree tree) {}
49+
50+
void visitMacroExpansionTree(MacroExpansionTree tree) {
51+
visitTree(tree.getBeforeExpansion());
52+
}
53+
54+
void visitOriginalTree(OriginalTree tree) {}
55+
56+
void visitSelectTree(SelectTree tree) {
57+
visitTree(tree.getQualifier());
58+
visitIdTree(tree.getId());
59+
}
60+
61+
void visitTypeApplyTree(TypeApplyTree tree) {
62+
visitTree(tree.getFunction());
63+
}
64+
65+
void visitAnnotationTree(AnnotationTree tree) {
66+
for (Tree parameter : tree.getParametersList()) {
67+
visitTree(parameter);
68+
}
69+
}
70+
71+
void visitAssignTree(AssignTree tree) {
72+
visitTree(tree.getLhs());
73+
visitTree(tree.getRhs());
74+
}
75+
76+
void visitBinaryOperatorTree(BinaryOperatorTree tree) {
77+
visitTree(tree.getLhs());
78+
visitTree(tree.getRhs());
79+
}
80+
}

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

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ message TextDocument {
2626
Language language = 10;
2727
repeated SymbolInformation symbols = 5;
2828
repeated SymbolOccurrence occurrences = 6;
29+
repeated Synthetic synthetics = 12;
2930
}
3031

3132
enum Language {
@@ -267,13 +268,21 @@ message RepeatedType {
267268
Type tpe = 1;
268269
}
269270

271+
message Synthetic {
272+
Range range = 1;
273+
Tree tree = 2;
274+
}
275+
270276
message Tree {
271277
oneof sealed_value {
272278
ApplyTree apply_tree = 1;
279+
FunctionTree function_tree = 2;
273280
IdTree id_tree = 3;
274281
LiteralTree literal_tree = 4;
282+
MacroExpansionTree macro_expansion_tree = 5;
275283
OriginalTree original_tree = 6;
276284
SelectTree select_tree = 7;
285+
TypeApplyTree type_apply_tree = 8;
277286
// -- OUT OF SPEC -- //
278287
AnnotationTree annotation_tree = 9;
279288
AssignTree assign_tree = 10;
@@ -287,23 +296,40 @@ message ApplyTree {
287296
repeated Tree arguments = 2;
288297
}
289298

299+
300+
message FunctionTree {
301+
repeated IdTree parameters = 1;
302+
Tree body = 2;
303+
}
304+
290305
message IdTree {
291306
string symbol = 1;
292307
}
293308

294-
message OriginalTree {
295-
Range range = 1;
296-
}
297309

298310
message LiteralTree {
299311
Constant constant = 1;
300312
}
301313

314+
message MacroExpansionTree {
315+
Tree before_expansion = 1;
316+
Type tpe = 2;
317+
}
318+
319+
message OriginalTree {
320+
Range range = 1;
321+
}
322+
302323
message SelectTree {
303324
Tree qualifier = 1;
304325
IdTree id = 2;
305326
}
306327

328+
message TypeApplyTree {
329+
Tree function = 1;
330+
repeated Type type_arguments = 2;
331+
}
332+
307333
// -- OUT OF SPEC -- //
308334
message AnnotationTree {
309335
Type tpe = 1;
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package minimized
2+
3+
import scala.concurrent.ExecutionContext.Implicits.global
4+
import scala.concurrent.Future
5+
6+
class MinimizedScalaSynthetic {
7+
def everything(): Unit = Future(1)
8+
def applyTree(): Unit = Future.apply[Int](1)
9+
def applyTree2(): Unit = List.apply[Int](1).sorted
10+
def selectTree(): Unit = Future[Int](1)
11+
def typeApplyTree(): Unit = Future.apply(1)
12+
def forComprehensions(): Unit =
13+
for {
14+
x <- Future(1)
15+
y <- Future.successful(1)
16+
if y < 2
17+
z <- Future.apply[Int](1)
18+
} yield x + y + z
19+
}

0 commit comments

Comments
 (0)