diff --git a/MODULE.bazel b/MODULE.bazel index 8a9a0d285..728e38ed2 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -10,7 +10,7 @@ maven.install( artifacts = [ "com.google.protobuf:protobuf-java-util:4.34.2", "org.projectlombok:lombok:1.18.46", - "org.scip-code:scip-java-bindings:0.8.0", + "org.scip-code:scip-java-bindings:0.9.0", ], known_contributing_modules = ["protobuf"], repositories = ["https://repo1.maven.org/maven2"], diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b09deff11..156c064b3 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -12,7 +12,7 @@ maven-plugin-api = "3.6.3" maven-project = "2.2.1" protobuf = "4.34.2" protobuf-plugin = "0.10.0" -scip-bindings = "0.8.0" +scip-bindings = "0.9.0" shadow = "9.4.3" spotless = "8.7.0" vanniktech-maven-publish = "0.35.0" diff --git a/scip-aggregator/src/main/java/com/sourcegraph/scip_aggregator/ScipAggregator.java b/scip-aggregator/src/main/java/com/sourcegraph/scip_aggregator/ScipAggregator.java index 04f027144..845d4d5fb 100644 --- a/scip-aggregator/src/main/java/com/sourcegraph/scip_aggregator/ScipAggregator.java +++ b/scip-aggregator/src/main/java/com/sourcegraph/scip_aggregator/ScipAggregator.java @@ -153,11 +153,20 @@ private Document rewriteDocument( for (Occurrence occ : shard.getOccurrencesList()) { Occurrence.Builder rebuilt = Occurrence.newBuilder() - .addAllRange(occ.getRangeList()) .setSymbol(rewriter.rewrite(occ.getSymbol())) .setSymbolRoles(occ.getSymbolRoles()); - if (occ.getEnclosingRangeCount() > 0) { - rebuilt.addAllEnclosingRange(occ.getEnclosingRangeList()); + switch (occ.getTypedRangeCase()) { + case SINGLE_LINE_RANGE -> rebuilt.setSingleLineRange(occ.getSingleLineRange()); + case MULTI_LINE_RANGE -> rebuilt.setMultiLineRange(occ.getMultiLineRange()); + case TYPEDRANGE_NOT_SET -> + throw new IllegalArgumentException("expected SCIP 0.9 typed occurrence range"); + } + switch (occ.getTypedEnclosingRangeCase()) { + case SINGLE_LINE_ENCLOSING_RANGE -> + rebuilt.setSingleLineEnclosingRange(occ.getSingleLineEnclosingRange()); + case MULTI_LINE_ENCLOSING_RANGE -> + rebuilt.setMultiLineEnclosingRange(occ.getMultiLineEnclosingRange()); + case TYPEDENCLOSINGRANGE_NOT_SET -> {} } out.addOccurrences(rebuilt); } diff --git a/scip-javac/src/main/java/com/sourcegraph/scip_javac/ScipVisitor.java b/scip-javac/src/main/java/com/sourcegraph/scip_javac/ScipVisitor.java index 1878da466..a6e70b12e 100644 --- a/scip-javac/src/main/java/com/sourcegraph/scip_javac/ScipVisitor.java +++ b/scip-javac/src/main/java/com/sourcegraph/scip_javac/ScipVisitor.java @@ -2,6 +2,7 @@ import com.sourcegraph.scip.LocalSymbolsCache; import com.sourcegraph.scip.ScipDocumentBuilder; +import com.sourcegraph.scip.ScipRange; import com.sourcegraph.scip.ScipSymbols; import com.sun.source.tree.AnnotatedTypeTree; import com.sun.source.tree.ClassTree; @@ -182,7 +183,7 @@ private void resolveMethodTree(MethodTree node, TreePath treePath) { private void resolveVariableTree(VariableTree node, TreePath treePath) { Element sym = trees.getElement(treePath); if (sym == null) return; - int[] range = + ScipRange range = emitDefinition(sym, node, sym.getSimpleName(), CompilerRange.FROM_POINT_WITH_TEXT_SEARCH); if (sym.getKind() == ElementKind.ENUM_CONSTANT) { TreePath typeTreePath = nodes.get(node.getInitializer()); @@ -246,8 +247,8 @@ private void resolveNewClassTree(NewClassTree node, TreePath treePath) { // ======================================= // Occurrence + SymbolInformation emission // ======================================= - private int[] emitDefinition(Element sym, Tree tree, Name name, CompilerRange kind) { - int[] range = computeRange(tree, kind, sym, name == null ? null : name.toString()); + private ScipRange emitDefinition(Element sym, Tree tree, Name name, CompilerRange kind) { + ScipRange range = computeRange(tree, kind, sym, name == null ? null : name.toString()); if (range == null) return null; emitOccurrence(sym, range, SymbolRole.Definition_VALUE, computeEnclosingRange(tree)); declTrees.put(sym, tree); @@ -256,18 +257,26 @@ private int[] emitDefinition(Element sym, Tree tree, Name name, CompilerRange ki } private void emitReference(Element sym, Tree tree, Name name, CompilerRange kind) { - int[] range = computeRange(tree, kind, sym, name == null ? null : name.toString()); + ScipRange range = computeRange(tree, kind, sym, name == null ? null : name.toString()); if (range == null) return; emitOccurrence(sym, range, 0 /* reference */, null); } - private void emitOccurrence(Element sym, int[] range, int role, int[] enclosingRange) { + private void emitOccurrence(Element sym, ScipRange range, int role, ScipRange enclosingRange) { String symbol = scipSymbol(sym); if (symbol.isEmpty()) return; Occurrence.Builder b = Occurrence.newBuilder().setSymbol(symbol).setSymbolRoles(role); - for (int v : range) b.addRange(v); + if (range.isSingleLine()) { + b.setSingleLineRange(range.toSingleLineRange()); + } else { + b.setMultiLineRange(range.toMultiLineRange()); + } if (enclosingRange != null) { - for (int v : enclosingRange) b.addEnclosingRange(v); + if (enclosingRange.isSingleLine()) { + b.setSingleLineEnclosingRange(enclosingRange.toSingleLineRange()); + } else { + b.setMultiLineEnclosingRange(enclosingRange.toMultiLineRange()); + } } documentBuilder.addOccurrence(b.build()); } @@ -360,7 +369,7 @@ private SymbolInformation.Kind scipKind(Element sym) { // ======================================= // Source position / range computation // ======================================= - private int[] computeRange(Tree tree, CompilerRange kind, Element sym, String name) { + private ScipRange computeRange(Tree tree, CompilerRange kind, Element sym, String name) { if (sym == null) return null; SourcePositions sourcePositions = trees.getSourcePositions(); int start = (int) sourcePositions.getStartPosition(compUnitTree, tree); @@ -394,7 +403,7 @@ private int[] computeRange(Tree tree, CompilerRange kind, Element sym, String na return lineMapRange(start, end); } - private int[] computeEnclosingRange(Tree tree) { + private ScipRange computeEnclosingRange(Tree tree) { if (tree == null) return null; TreePath path = nodes.get(tree); if (path == null) return null; @@ -413,7 +422,7 @@ private int[] computeEnclosingRange(Tree tree) { return lineMapRange(start, end); } - private int[] lineMapRange(int start, int end) { + private ScipRange lineMapRange(int start, int end) { if (start == Diagnostic.NOPOS || end == Diagnostic.NOPOS || end <= start) return null; LineMap lineMap = compUnitTree.getLineMap(); int startLine = (int) lineMap.getLineNumber(start) - 1; @@ -431,8 +440,8 @@ private int[] lineMapRange(int start, int end) { endChar -= count * 7; } - if (startLine == endLine) return new int[] {startLine, startChar, endChar}; - return new int[] {startLine, startChar, endLine, endChar}; + if (startLine == endLine) return ScipRange.singleLine(startLine, startChar, endChar); + return ScipRange.range(startLine, startChar, endLine, endChar); } // ======================================= diff --git a/scip-kotlinc/src/main/kotlin/com/sourcegraph/scip_kotlinc/ScipTextDocumentBuilder.kt b/scip-kotlinc/src/main/kotlin/com/sourcegraph/scip_kotlinc/ScipTextDocumentBuilder.kt index 778fb7b1e..9f2c7a542 100644 --- a/scip-kotlinc/src/main/kotlin/com/sourcegraph/scip_kotlinc/ScipTextDocumentBuilder.kt +++ b/scip-kotlinc/src/main/kotlin/com/sourcegraph/scip_kotlinc/ScipTextDocumentBuilder.kt @@ -1,6 +1,7 @@ package com.sourcegraph.scip_kotlinc import com.sourcegraph.scip.ScipDocumentBuilder +import com.sourcegraph.scip.ScipRange import com.sourcegraph.scip.ScipShardPaths import java.nio.file.Path import java.nio.file.Paths @@ -22,7 +23,6 @@ import org.scip_code.scip.Document import org.scip_code.scip.Occurrence import org.scip_code.scip.SymbolInformation import org.scip_code.scip.SymbolRole -import org.scip_code.scip.occurrence import org.scip_code.scip.relationship import org.scip_code.scip.signature import org.scip_code.scip.symbolInformation @@ -98,29 +98,36 @@ class ScipTextDocumentBuilder( element: KtSourceElement, isDefinition: Boolean, enclosingSource: KtSourceElement?, - ): Occurrence = occurrence { - this.symbol = symbol.toString() - if (isDefinition) symbolRoles = SymbolRole.Definition.number - range += range(element).asIterable() + ): Occurrence { + val builder = Occurrence.newBuilder().setSymbol(symbol.toString()) + if (isDefinition) builder.setSymbolRoles(SymbolRole.Definition.number) + val range = range(element) + if (range.isSingleLine) builder.singleLineRange = range.toSingleLineRange() + else builder.multiLineRange = range.toMultiLineRange() if (enclosingSource != null) { - enclosingRange += enclosingRange(enclosingSource).asIterable() + val enclosingRange = enclosingRange(enclosingSource) + if (enclosingRange.isSingleLine) { + builder.singleLineEnclosingRange = enclosingRange.toSingleLineRange() + } else { + builder.multiLineEnclosingRange = enclosingRange.toMultiLineRange() + } } + return builder.build() } - private fun range(element: KtSourceElement): IntArray { + private fun range(element: KtSourceElement): ScipRange { val line = lineMap.lineNumber(element) - 1 val startCol = lineMap.startCharacter(element) val endCol = lineMap.endCharacter(element) - return intArrayOf(line, startCol, endCol) + return ScipRange.singleLine(line, startCol, endCol) } - private fun enclosingRange(element: KtSourceElement): IntArray { + private fun enclosingRange(element: KtSourceElement): ScipRange { val startLine = lineMap.lineNumber(element) - 1 val startCol = lineMap.startCharacter(element) val endLine = lineMap.lineNumberForOffset(element.endOffset) - 1 val endCol = lineMap.columnForOffset(element.endOffset) - return if (startLine == endLine) intArrayOf(startLine, startCol, endCol) - else intArrayOf(startLine, startCol, endLine, endCol) + return ScipRange.range(startLine, startCol, endLine, endCol) } private fun relativePath(): String = diff --git a/scip-kotlinc/src/test/kotlin/com/sourcegraph/scip_kotlinc/test/ScipBuilders.kt b/scip-kotlinc/src/test/kotlin/com/sourcegraph/scip_kotlinc/test/ScipBuilders.kt index ff579e796..369b71acb 100644 --- a/scip-kotlinc/src/test/kotlin/com/sourcegraph/scip_kotlinc/test/ScipBuilders.kt +++ b/scip-kotlinc/src/test/kotlin/com/sourcegraph/scip_kotlinc/test/ScipBuilders.kt @@ -1,9 +1,9 @@ package com.sourcegraph.scip_kotlinc.test +import com.sourcegraph.scip.ScipRange import org.scip_code.scip.Occurrence import org.scip_code.scip.SymbolInformation import org.scip_code.scip.SymbolRole -import org.scip_code.scip.occurrence import org.scip_code.scip.relationship import org.scip_code.scip.signature import org.scip_code.scip.symbolInformation @@ -38,10 +38,9 @@ class ScipRangeBuilder { var endLine: Int = -1 var endCharacter: Int = 0 - internal fun toIntList(): List { + internal fun toScipRange(): ScipRange { val line = if (endLine < 0) startLine else endLine - return if (line == startLine) listOf(startLine, startCharacter, endCharacter) - else listOf(startLine, startCharacter, line, endCharacter) + return ScipRange.range(startLine, startCharacter, line, endCharacter) } } @@ -49,22 +48,28 @@ class ScipRangeBuilder { class ScipOccurrenceBuilder { var role: Int = REFERENCE var symbol: String = "" - private var range: List? = null - private var enclosingRange: List? = null + private var range: ScipRange? = null + private var enclosingRange: ScipRange? = null fun range(block: ScipRangeBuilder.() -> Unit) { - range = ScipRangeBuilder().apply(block).toIntList() + range = ScipRangeBuilder().apply(block).toScipRange() } fun enclosingRange(block: ScipRangeBuilder.() -> Unit) { - enclosingRange = ScipRangeBuilder().apply(block).toIntList() + enclosingRange = ScipRangeBuilder().apply(block).toScipRange() } - internal fun build(): Occurrence = occurrence { - symbol = this@ScipOccurrenceBuilder.symbol - symbolRoles = role - this@ScipOccurrenceBuilder.range?.let { range += it } - this@ScipOccurrenceBuilder.enclosingRange?.let { enclosingRange += it } + internal fun build(): Occurrence { + val builder = Occurrence.newBuilder().setSymbol(symbol).setSymbolRoles(role) + range?.let { + if (it.isSingleLine) builder.singleLineRange = it.toSingleLineRange() + else builder.multiLineRange = it.toMultiLineRange() + } + enclosingRange?.let { + if (it.isSingleLine) builder.singleLineEnclosingRange = it.toSingleLineRange() + else builder.multiLineEnclosingRange = it.toMultiLineRange() + } + return builder.build() } } diff --git a/scip-shared/src/main/java/com/sourcegraph/scip/ScipDocumentBuilder.java b/scip-shared/src/main/java/com/sourcegraph/scip/ScipDocumentBuilder.java index e9c53b858..15eb6bf1d 100644 --- a/scip-shared/src/main/java/com/sourcegraph/scip/ScipDocumentBuilder.java +++ b/scip-shared/src/main/java/com/sourcegraph/scip/ScipDocumentBuilder.java @@ -1,7 +1,6 @@ package com.sourcegraph.scip; import java.util.ArrayList; -import java.util.Collections; import java.util.Comparator; import java.util.LinkedHashMap; import java.util.List; @@ -21,8 +20,8 @@ */ public final class ScipDocumentBuilder { private static final Comparator OCCURRENCE_ORDER = - Comparator.comparingInt(o -> rangeStartLine(o)) - .thenComparingInt(o -> rangeStartCharacter(o)); + Comparator.comparingInt(o -> ScipRange.from(o).startLine()) + .thenComparingInt(o -> ScipRange.from(o).startCharacter()); private final Map occurrences = new LinkedHashMap<>(); private final Map symbols = new LinkedHashMap<>(); @@ -49,21 +48,13 @@ public Document build(String language, String relativePath, String text) { .build(); } - private static int rangeStartLine(Occurrence occurrence) { - return occurrence.getRangeCount() > 0 ? occurrence.getRange(0) : 0; - } - - private static int rangeStartCharacter(Occurrence occurrence) { - return occurrence.getRangeCount() > 1 ? occurrence.getRange(1) : 0; - } - private static final class OccurrenceKey { - private final List range; + private final ScipRange range; private final String symbol; private final int roles; OccurrenceKey(Occurrence occurrence) { - this.range = Collections.unmodifiableList(new ArrayList<>(occurrence.getRangeList())); + this.range = ScipRange.from(occurrence); this.symbol = occurrence.getSymbol(); this.roles = occurrence.getSymbolRoles(); } diff --git a/scip-shared/src/main/java/com/sourcegraph/scip/ScipRange.java b/scip-shared/src/main/java/com/sourcegraph/scip/ScipRange.java new file mode 100644 index 000000000..4424bf737 --- /dev/null +++ b/scip-shared/src/main/java/com/sourcegraph/scip/ScipRange.java @@ -0,0 +1,65 @@ +package com.sourcegraph.scip; + +import org.scip_code.scip.MultiLineRange; +import org.scip_code.scip.Occurrence; +import org.scip_code.scip.SingleLineRange; + +/** A normalized source range that can be serialized to SCIP's typed range oneofs. */ +public record ScipRange(int startLine, int startCharacter, int endLine, int endCharacter) { + + public static ScipRange singleLine(int line, int startCharacter, int endCharacter) { + return new ScipRange(line, startCharacter, line, endCharacter); + } + + public static ScipRange range(int startLine, int startCharacter, int endLine, int endCharacter) { + return new ScipRange(startLine, startCharacter, endLine, endCharacter); + } + + public static ScipRange from(Occurrence occurrence) { + switch (occurrence.getTypedRangeCase()) { + case SINGLE_LINE_RANGE: + return from(occurrence.getSingleLineRange()); + case MULTI_LINE_RANGE: + return from(occurrence.getMultiLineRange()); + case TYPEDRANGE_NOT_SET: + throw new IllegalArgumentException("expected SCIP 0.9 typed occurrence range"); + } + throw new IllegalArgumentException("expected SCIP 0.9 typed occurrence range"); + } + + private static ScipRange from(SingleLineRange range) { + return new ScipRange( + range.getLine(), range.getStartCharacter(), range.getLine(), range.getEndCharacter()); + } + + private static ScipRange from(MultiLineRange range) { + return new ScipRange( + range.getStartLine(), + range.getStartCharacter(), + range.getEndLine(), + range.getEndCharacter()); + } + + public boolean isSingleLine() { + return startLine == endLine; + } + + public SingleLineRange toSingleLineRange() { + if (!isSingleLine()) throw new IllegalStateException("expected single-line range"); + return SingleLineRange.newBuilder() + .setLine(startLine) + .setStartCharacter(startCharacter) + .setEndCharacter(endCharacter) + .build(); + } + + public MultiLineRange toMultiLineRange() { + if (isSingleLine()) throw new IllegalStateException("expected multi-line range"); + return MultiLineRange.newBuilder() + .setStartLine(startLine) + .setStartCharacter(startCharacter) + .setEndLine(endLine) + .setEndCharacter(endCharacter) + .build(); + } +}