diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 808c2bef5..b94c20909 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -31,8 +31,8 @@ If you'd rather install tools manually, you'll need at least:
These are the main components of the project.
-- `semanticdb-javac/src/main/java`: the Java compiler plugin that creates
- SemanticDB files.
+- `semanticdb-javac/src/main/java`: the Java compiler plugin that emits
+ SCIP shard files.
- `tests/minimized`: minimized Java source files that reproduce interesting test
cases.
- `tests/unit`: fast running unit tests that are helpful for local edit-and-test
diff --git a/build.sbt b/build.sbt
index 847ec203c..63ae14fc9 100644
--- a/build.sbt
+++ b/build.sbt
@@ -531,7 +531,7 @@ lazy val semanticdbKotlincMinimized = project
Def.sequential(
Compile / compile,
(cli / Compile / runMain).toTask(
- s" $mainCls index-semanticdb --no-emit-inverse-relationships --use-scip-shards --cwd $srcRoot --output $scipOut $tgtRoot"
+ s" $mainCls index-semanticdb --no-emit-inverse-relationships --cwd $srcRoot --output $scipOut $tgtRoot"
),
(cli / Compile / runMain).toTask(
s" $mainCls snapshot --cwd $srcRoot --output $snapDir ${file(
diff --git a/docs/design.md b/docs/design.md
index af79ca5f6..853fc6f25 100644
--- a/docs/design.md
+++ b/docs/design.md
@@ -5,10 +5,9 @@ title: Design
This project is implemented as a
[Java compiler plugin](https://docs.oracle.com/en/java/javase/11/docs/api/jdk.compiler/com/sun/source/util/Plugin.html)
-that generates one
-[SemanticDB](https://scalameta.org/docs/semanticdb/specification.html) file for
-every `*.java` source file. After compilation completes, the SemanticDB files
-are processed to produce SCIP.
+that emits one [SCIP](https://github.com/sourcegraph/scip) shard file for every
+`*.java` source file. After compilation completes, the per-file SCIP shards are
+aggregated into a single SCIP index.
### Why Java compiler plugin?
@@ -24,24 +23,3 @@ There are several benefits to implementing scip-java as a compiler plugin:
tool, we minimize the risk of diverging from the CI build environment such as
installed system dependencies, custom compiler options and custom annotation
processors.
-
-### Why SemanticDB?
-
-SemanticDB is Protobuf schema for information about symbols and types in Java
-programs and other languages. There are several benefits to using SemanticDB as
-an intermediary representation for SCIP:
-
-- **Simplicity**: It's easy to translate a single Java source file into a single
- SemanticDB file inside a compiler plugin. It's more complicated to produce
- SCIP because compiler plugins does not have access to a project-wide context,
- which is necessary to produce accurate definitions and hovers in multi-module
- projects with external library dependencies.
-- **Performance**: SemanticDB is fast to write and read. Each compilation unit
- can be processed independently to keep memory usage low. The final conversion
- from SemanticDB to SCIP can be safely parallelized.
-- **Cross-repository**: Compiler plugins have access to both source code and the
- classpath (compiled bytecode of upstream dependencies). SemanticDB has been
- designed so that it's also possible to generate spec-compliant symbols from
- the classpath alone (no source code) and from the syntax tree of an individual
- source file (no classpath). This flexibility will be helpful for scip-java in
- the future to unblock cross-repository navigation.
diff --git a/docs/getting-started.md b/docs/getting-started.md
index 93d896a09..9e1a3515a 100644
--- a/docs/getting-started.md
+++ b/docs/getting-started.md
@@ -351,10 +351,10 @@ Next, run the following command to generate the SCIP index (`index.scip`).
```
bazel run @scip_java//scip-semanticdb:bazel -- --sourceroot $PWD
-# (optional) Validate that SemanticDB files were generated.
+# (optional) Validate that SCIP shard files were generated.
# The command below works for the `examples/bazel-example` directory in the sourcegraph/scip-java repository.
-❯ jar tf bazel-bin/src/main/java/example/libexample.jar | grep semanticdb$
-META-INF/semanticdb/src/main/java/example/Example.java.semanticdb
+❯ jar tf bazel-bin/src/main/java/example/libexample.jar | grep scip$
+META-INF/scip/src/main/java/example/Example.java.scip
```
Finally, run the following commands to upload the SCIP index to Sourcegraph.
diff --git a/docs/manual-configuration.md b/docs/manual-configuration.md
index 49e3597d5..98c473fb4 100644
--- a/docs/manual-configuration.md
+++ b/docs/manual-configuration.md
@@ -12,10 +12,9 @@ fails.
Indexing a codebase consists of two independent phases:
-- Compile the codebase with the SemanticDB compiler plugin.
-- Generate SCIP index from SemanticDB files.
-
-
+- Compile the codebase with the SemanticDB compiler plugin, which writes one
+ SCIP shard per Java source file.
+- Aggregate the SCIP shards into a single SCIP index.
The first phase can be complicated to configure and it can take a while to run.
The second phase is quite simple to configure and it usually runs very fast.
@@ -63,7 +62,7 @@ compiler plugin. To do this you need to explicitly configure two directories:
It's important that all of the source files that should be index live under
this directory.
- `-targetroot:PATH`: the absolute path to the directory where to generate
- SemanticDB file. This directory can be anywhere on your file system.
+ SCIP shard files. This directory can be anywhere on your file system.
Alternatively, pass in `-targetroot:javac-classes-directory` for the plugin to
automatically use the `javac` output directory.
@@ -112,13 +111,13 @@ examples:
- Maven: `mvn clean verify -DskipTests`
- Bazel: `bazel build //...`
-If everything went well, you should have a lot of `*.semanticdb` files in the
+If everything went well, you should have a lot of `*.scip` shard files in the
targetroot directory.
```
❯ find $TARGETROOT -type f
-build/semanticdb-targetroot/META-INF/semanticdb/j11/src/test/java/example/ExampleTest.java.semanticdb
-build/semanticdb-targetroot/META-INF/semanticdb/j11/src/main/java/example/Example.java.semanticdb
+build/semanticdb-targetroot/META-INF/scip/j11/src/test/java/example/ExampleTest.java.scip
+build/semanticdb-targetroot/META-INF/scip/j11/src/main/java/example/Example.java.scip
...
```
@@ -198,13 +197,13 @@ Which allows you to invoke it by simply running `mvn sourcegraph:sourcegraphDepe
Cross-repository navigation is a feature that allows "goto definition" and "find
references" to show results from multiple repositories.
-## Step 5: Generate SCIP index from SemanticDB files
+## Step 5: Aggregate SCIP shards into a single SCIP index
First, install the `scip-java` command-line tool according to the instructions
in the [getting started guide](getting-started.md).
-Next, run the `scip-java index-semanticdb` command to convert SemanticDB files
-into SCIP.
+Next, run the `scip-java index-semanticdb` command to aggregate the per-file
+SCIP shards into a single SCIP index.
```sh
❯ scip-java index-semanticdb $TARGETROOT
diff --git a/scip-java/src/main/scala/com/sourcegraph/scip_java/buildtools/ScipBuildTool.scala b/scip-java/src/main/scala/com/sourcegraph/scip_java/buildtools/ScipBuildTool.scala
index 7656a8dd0..3bcb1bae6 100644
--- a/scip-java/src/main/scala/com/sourcegraph/scip_java/buildtools/ScipBuildTool.scala
+++ b/scip-java/src/main/scala/com/sourcegraph/scip_java/buildtools/ScipBuildTool.scala
@@ -206,7 +206,7 @@ class ScipBuildTool(index: IndexCommand) extends BuildTool("SCIP", index) {
.app
.reporter
.info(
- "Some SemanticDB files got generated even if there were compile errors. " +
+ "Some SCIP shard files got generated even if there were compile errors. " +
"In most cases, this means that scip-java managed to index everything " +
"except the locations that had compile errors and you can ignore the compile errors."
)
diff --git a/scip-java/src/main/scala/com/sourcegraph/scip_java/commands/IndexCommand.scala b/scip-java/src/main/scala/com/sourcegraph/scip_java/commands/IndexCommand.scala
index 566fc0c0e..04e962280 100644
--- a/scip-java/src/main/scala/com/sourcegraph/scip_java/commands/IndexCommand.scala
+++ b/scip-java/src/main/scala/com/sourcegraph/scip_java/commands/IndexCommand.scala
@@ -29,7 +29,7 @@ case class IndexCommand(
@Description("The path where to generate the SCIP index.")
output: Path = Paths.get("index.scip"),
@Description(
- "The directory where to generate SemanticDB files. " +
+ "The directory where to generate SCIP shard files. " +
"Defaults to a build-specific path. " +
"For example, the default value for Gradle is 'build/semanticdb-targetroot' and for Maven it's 'target/semanticdb-targetroot'"
)
diff --git a/scip-java/src/main/scala/com/sourcegraph/scip_java/commands/IndexSemanticdbCommand.scala b/scip-java/src/main/scala/com/sourcegraph/scip_java/commands/IndexSemanticdbCommand.scala
index 7a14f8d96..8f3b48563 100644
--- a/scip-java/src/main/scala/com/sourcegraph/scip_java/commands/IndexSemanticdbCommand.scala
+++ b/scip-java/src/main/scala/com/sourcegraph/scip_java/commands/IndexSemanticdbCommand.scala
@@ -10,7 +10,6 @@ import com.sourcegraph.io.AbsolutePath
import com.sourcegraph.scip_java.BuildInfo
import com.sourcegraph.scip_java.buildtools.ClasspathEntry
import com.sourcegraph.scip_semanticdb.ConsoleScipSemanticdbReporter
-import com.sourcegraph.scip_semanticdb.ScipSemanticdb
import com.sourcegraph.scip_semanticdb.ScipSemanticdbOptions
import com.sourcegraph.scip_semanticdb.ScipShardAggregator
import moped.annotations._
@@ -21,7 +20,7 @@ import org.scip_code.scip.ToolInfo
import ujson.Arr
import ujson.Obj
-@Description("Converts SemanticDB files into a single SCIP index file.")
+@Description("Aggregates SCIP shard files into a single SCIP index file.")
@Usage("scip-java index-semanticdb [OPTIONS ...] [POSITIONAL ARGUMENTS ...]")
@ExampleUsage(
"scip-java index-semanticdb --out=myindex.scip my/targetroot1 my/targetroot2"
@@ -30,10 +29,10 @@ import ujson.Obj
final case class IndexSemanticdbCommand(
@Description("The name of the output file.")
output: Path = Paths.get("index.scip"),
- @Description("Whether to process the SemanticDB files in parallel")
+ @Description("Whether to process the SCIP shard files in parallel")
parallel: Boolean = true,
@Description(
- "Whether to infer the location of SemanticDB files based as produced by Bazel"
+ "Whether to infer the location of SCIP shard files based as produced by Bazel"
)
bazel: Boolean = true,
@Description(
@@ -44,7 +43,7 @@ final case class IndexSemanticdbCommand(
@Description("URL to a PackageHub instance")
@Hidden
packagehub: Option[String] = None,
- @Description("Directories that contain SemanticDB files.")
+ @Description("Directories that contain SCIP shard files.")
@PositionalArguments()
targetroot: List[Path] = Nil,
@Description(
@@ -60,11 +59,6 @@ final case class IndexSemanticdbCommand(
"Maven->Maven or Gradle->Gradle projects because those build tools compile sources to classfiles inside directories."
)
allowExportingGlobalSymbolsFromDirectoryEntries: Boolean = true,
- @Description(
- "If true, aggregate *.scip shards under META-INF/scip/ instead of *.semanticdb files. " +
- "Pass --use-scip-shards=false to fall back to the SemanticDB-based aggregator."
- )
- useScipShards: Boolean = true,
@Inline()
app: Application = Application.default
) extends Command {
@@ -102,10 +96,7 @@ final case class IndexSemanticdbCommand(
allowEmptyIndex,
allowExportingGlobalSymbolsFromDirectoryEntries
)
- if (useScipShards)
- ScipShardAggregator.run(options)
- else
- ScipSemanticdb.run(options)
+ ScipShardAggregator.run(options)
postPackages(packages)
if (!app.reporter.hasErrors()) {
app.info(options.output.toString)
diff --git a/scip-semanticdb/BUILD b/scip-semanticdb/BUILD
index 9cee3def4..e764f968f 100644
--- a/scip-semanticdb/BUILD
+++ b/scip-semanticdb/BUILD
@@ -19,7 +19,7 @@ java_library(
srcs = glob(["src/main/java/**/*.java"]),
deps = [
":all_java_proto",
- "//semanticdb-shared",
+ "//semanticdb-java",
"@maven//:com_google_code_findbugs_jsr305",
"@maven//:com_google_protobuf_protobuf_java",
"@maven//:com_google_protobuf_protobuf_java_util",
diff --git a/scip-semanticdb/src/main/java/com/sourcegraph/scip_semanticdb/BazelBuildTool.java b/scip-semanticdb/src/main/java/com/sourcegraph/scip_semanticdb/BazelBuildTool.java
index 0d6df55fe..248a694b1 100644
--- a/scip-semanticdb/src/main/java/com/sourcegraph/scip_semanticdb/BazelBuildTool.java
+++ b/scip-semanticdb/src/main/java/com/sourcegraph/scip_semanticdb/BazelBuildTool.java
@@ -65,7 +65,7 @@ public boolean hasErrors() {
/* allowEmptyIndex */ true,
/* indexDirectoryEntries */ false // because Bazel only compiles to jar files.
);
- ScipSemanticdb.run(scipOptions);
+ ScipShardAggregator.run(scipOptions);
if (!scipOptions.reporter.hasErrors()) {
System.out.println("done: " + scipOptions.output);
diff --git a/scip-semanticdb/src/main/java/com/sourcegraph/scip_semanticdb/RangeComparator.java b/scip-semanticdb/src/main/java/com/sourcegraph/scip_semanticdb/RangeComparator.java
deleted file mode 100644
index 951028c22..000000000
--- a/scip-semanticdb/src/main/java/com/sourcegraph/scip_semanticdb/RangeComparator.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package com.sourcegraph.scip_semanticdb;
-
-import com.sourcegraph.semanticdb.Semanticdb.Range;
-
-/**
- * Comparator that sorts SemanticDB ranges by appearance in the document.
- *
- *
We can't guarantee ordering of SymbolOccurrence in SemanticDB payloads so it's good to sort
- * them before processing.
- */
-public class RangeComparator implements java.util.Comparator {
-
- @Override
- public int compare(Range r1, Range r2) {
- int byStartLine = Integer.compare(r1.getStartLine(), r2.getStartLine());
- if (byStartLine != 0) {
- return byStartLine;
- }
- int byStartCharacter = Integer.compare(r1.getStartCharacter(), r2.getStartCharacter());
- if (byStartCharacter != 0) {
- return byStartCharacter;
- }
- int byEndLine = Integer.compare(r1.getEndLine(), r2.getEndLine());
- if (byEndLine != 0) {
- return byEndLine;
- }
- return Integer.compare(r1.getEndCharacter(), r2.getEndCharacter());
- }
-}
diff --git a/scip-semanticdb/src/main/java/com/sourcegraph/scip_semanticdb/ScipSemanticdb.java b/scip-semanticdb/src/main/java/com/sourcegraph/scip_semanticdb/ScipSemanticdb.java
deleted file mode 100644
index 540dd83d6..000000000
--- a/scip-semanticdb/src/main/java/com/sourcegraph/scip_semanticdb/ScipSemanticdb.java
+++ /dev/null
@@ -1,431 +0,0 @@
-package com.sourcegraph.scip_semanticdb;
-
-import com.google.protobuf.CodedInputStream;
-import com.sourcegraph.semanticdb.Semanticdb;
-import com.sourcegraph.semanticdb.Semanticdb.SymbolOccurrence;
-import com.sourcegraph.semanticdb.Semanticdb.SymbolOccurrence.Role;
-import com.sourcegraph.semanticdb.SemanticdbSymbols;
-import org.scip_code.scip.Document;
-import org.scip_code.scip.Index;
-import org.scip_code.scip.Metadata;
-import org.scip_code.scip.Occurrence;
-import org.scip_code.scip.ProtocolVersion;
-import org.scip_code.scip.Relationship;
-import org.scip_code.scip.Signature;
-import org.scip_code.scip.SymbolInformation;
-import org.scip_code.scip.SymbolRole;
-import org.scip_code.scip.TextEncoding;
-import org.scip_code.scip.ToolInfo;
-
-import java.io.IOException;
-import java.net.URI;
-import java.nio.file.*;
-import java.util.*;
-
-import java.util.jar.JarEntry;
-import java.util.jar.JarFile;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-import java.util.stream.StreamSupport;
-
-/** The core logic that converts SemanticDB into SCIP. */
-public class ScipSemanticdb {
- private final ScipWriter writer;
- private final ScipSemanticdbOptions options;
-
- public ScipSemanticdb(ScipWriter writer, ScipSemanticdbOptions options) {
- this.writer = writer;
- this.options = options;
- }
-
- public static void run(ScipSemanticdbOptions options) throws IOException {
- ScipWriter writer = new ScipWriter(options);
- new ScipSemanticdb(writer, options).run();
- }
-
- private void run() throws IOException {
- PackageTable packages = new PackageTable(options);
- List files = SemanticdbWalker.findSemanticdbFiles(options);
- Collections.sort(files);
- if (options.reporter.hasErrors()) return;
- if (files.isEmpty() && !options.allowEmptyIndex) {
- options.reporter.error(
- "No SemanticDB files found. "
- + "This typically means that `scip-java` is unable to automatically "
- + "index this codebase. If you are using Gradle or Maven, please report an issue to "
- + "https://github.com/sourcegraph/scip-java and include steps to reproduce. "
- + "If you are using a different build tool, make sure that you have followed all "
- + "of the steps from https://sourcegraph.github.io/scip-java/docs/manual-configuration.html");
- return;
- }
- options.reporter.startProcessing(files.size());
- runTyped(files, packages);
- writer.build();
- options.reporter.endProcessing();
- }
-
- private void runTyped(List files, PackageTable packages) {
- writer.emitTyped(typedMetadata());
- InverseReferenceRelationships references = inverseReferenceRelationships(files);
- filesStream(files).forEach(document -> processTypedDocument(document, packages, references));
- }
-
- private String typedSymbol(String symbol, Package pkg) {
- if (symbol.isEmpty()) {
- return "";
- }
- if (symbol.startsWith("local")) {
- return "local " + symbol.substring("local".length());
- }
- return "semanticdb maven " + pkg.repoName() + " " + pkg.version() + " " + symbol;
- }
-
- private static SymbolInformation.Kind scipKind(Semanticdb.SymbolInformation info) {
- Semanticdb.SymbolInformation.Kind kind = info.getKind();
- int properties = info.getProperties();
- boolean isStatic = (properties & Semanticdb.SymbolInformation.Property.STATIC_VALUE) > 0;
- boolean isAbstract = (properties & Semanticdb.SymbolInformation.Property.ABSTRACT_VALUE) > 0;
- boolean isEnum = (properties & Semanticdb.SymbolInformation.Property.ENUM_VALUE) > 0;
-
- switch (kind) {
- case CLASS:
- if (isEnum) {
- return SymbolInformation.Kind.Enum;
- } else {
- return SymbolInformation.Kind.Class;
- }
- case CONSTRUCTOR:
- return SymbolInformation.Kind.Constructor;
- case FIELD:
- if (isStatic) {
- return SymbolInformation.Kind.StaticField;
- } else {
- return SymbolInformation.Kind.Field;
- }
- case INTERFACE:
- return SymbolInformation.Kind.Interface;
- case LOCAL:
- if (isStatic) {
- return SymbolInformation.Kind.StaticVariable;
- } else {
- return SymbolInformation.Kind.Variable;
- }
- case MACRO:
- return SymbolInformation.Kind.Macro;
- case METHOD:
- if (isStatic) {
- return SymbolInformation.Kind.StaticMethod;
- } else if (isAbstract) {
- return SymbolInformation.Kind.AbstractMethod;
- } else {
- return SymbolInformation.Kind.Method;
- }
- case OBJECT:
- return SymbolInformation.Kind.Object;
- case PACKAGE:
- return SymbolInformation.Kind.Package;
- case PACKAGE_OBJECT:
- return SymbolInformation.Kind.PackageObject;
- case PARAMETER:
- return SymbolInformation.Kind.Parameter;
- case SELF_PARAMETER:
- return SymbolInformation.Kind.SelfParameter;
- case TRAIT:
- return SymbolInformation.Kind.Trait;
- case TYPE:
- if (isEnum) {
- return SymbolInformation.Kind.Enum;
- } else {
- return SymbolInformation.Kind.Type;
- }
- case TYPE_PARAMETER:
- return SymbolInformation.Kind.TypeParameter;
- case UNKNOWN_KIND:
- return SymbolInformation.Kind.UnspecifiedKind;
- }
-
- return SymbolInformation.Kind.UnspecifiedKind;
- }
-
- public static boolean isDefinitionRole(Role role) {
- return role == Role.DEFINITION || role == Role.SYNTHETIC_DEFINITION;
- }
-
- private void processTypedDocument(
- Path path, PackageTable packages, InverseReferenceRelationships references) {
- for (ScipTextDocument doc : parseTextDocument(path).collect(Collectors.toList())) {
- if (doc.semanticdb.getOccurrencesCount() == 0) {
- continue;
- }
-
- Path absolutePath = Paths.get(URI.create(doc.semanticdb.getUri()));
- String relativePath =
- StreamSupport.stream(options.sourceroot.relativize(absolutePath).spliterator(), false)
- .map(p -> p.getFileName().toString())
- .collect(Collectors.joining("/"));
- Document.Builder tdoc = Document.newBuilder().setRelativePath(relativePath);
- for (SymbolOccurrence occ : doc.sortedSymbolOccurrences()) {
- if (occ.getSymbol().isEmpty()) {
- continue;
- }
- int role = 0;
- if (isDefinitionRole(occ.getRole())) {
- role |= SymbolRole.Definition_VALUE;
- }
- boolean isSingleLineRange = occ.getRange().getStartLine() == occ.getRange().getEndLine();
- Iterable range =
- isSingleLineRange
- ? Arrays.asList(
- occ.getRange().getStartLine(),
- occ.getRange().getStartCharacter(),
- occ.getRange().getEndCharacter())
- : Arrays.asList(
- occ.getRange().getStartLine(),
- occ.getRange().getStartCharacter(),
- occ.getRange().getEndLine(),
- occ.getRange().getEndCharacter());
- Package pkg = packages.packageForSymbol(occ.getSymbol()).orElse(Package.EMPTY);
- Occurrence.Builder occBuilder =
- Occurrence.newBuilder()
- .addAllRange(range)
- .setSymbol(typedSymbol(occ.getSymbol(), pkg))
- .setSymbolRoles(role);
- // Add enclosing_range if it exists
- if (occ.hasEnclosingRange()) {
- Semanticdb.Range enclosingRange = occ.getEnclosingRange();
- boolean isEnclosingSingleLine =
- enclosingRange.getStartLine() == enclosingRange.getEndLine();
- Iterable enclosingRangeInts =
- isEnclosingSingleLine
- ? Arrays.asList(
- enclosingRange.getStartLine(),
- enclosingRange.getStartCharacter(),
- enclosingRange.getEndCharacter())
- : Arrays.asList(
- enclosingRange.getStartLine(),
- enclosingRange.getStartCharacter(),
- enclosingRange.getEndLine(),
- enclosingRange.getEndCharacter());
- occBuilder.addAllEnclosingRange(enclosingRangeInts);
- }
- tdoc.addOccurrences(occBuilder);
- }
- Symtab symtab = new Symtab(doc.semanticdb);
- for (Semanticdb.SymbolInformation info : doc.semanticdb.getSymbolsList()) {
- if (info.getSymbol().isEmpty()) {
- continue;
- }
- Package pkg = packages.packageForSymbol(info.getSymbol()).orElse(Package.EMPTY);
- SymbolInformation.Builder scipInfo =
- SymbolInformation.newBuilder().setSymbol(typedSymbol(info.getSymbol(), pkg));
-
- scipInfo.setDisplayName(info.getDisplayName());
- if (!info.getEnclosingSymbol().isEmpty()) {
- scipInfo.setEnclosingSymbol(typedSymbol(info.getEnclosingSymbol(), pkg));
- }
-
- scipInfo.setKind(scipKind(info));
-
- // TODO: this can be removed once https://github.com/sourcegraph/sourcegraph/issues/50927 is
- // fixed.
- ArrayList inverseReferences = references.map.get(info.getSymbol());
- if (inverseReferences != null) {
- for (String inverseReference : inverseReferences) {
- Package inverseReferencePkg =
- packages.packageForSymbol(inverseReference).orElse(Package.EMPTY);
- scipInfo.addRelationships(
- Relationship.newBuilder()
- .setSymbol(typedSymbol(inverseReference, inverseReferencePkg))
- .setIsImplementation(true)
- .setIsReference(true));
- }
- }
-
- for (int i = 0; i < info.getDefinitionRelationshipsCount(); i++) {
- String definitionSymbol = info.getDefinitionRelationships(i);
- if (definitionSymbol.isEmpty()) {
- continue;
- }
- Package definitionSymbolPkg =
- packages.packageForSymbol(definitionSymbol).orElse(Package.EMPTY);
- Semanticdb.SymbolInformation definitionInfo = symtab.symbols.get(definitionSymbol);
-
- scipInfo.addRelationships(
- Relationship.newBuilder()
- .setSymbol(typedSymbol(definitionSymbol, definitionSymbolPkg))
- .setIsDefinition(true)
- .setIsReference(
- definitionInfo != null
- && definitionInfo.getDisplayName().equals(info.getDisplayName())
- && supportsReferenceRelationship(info)));
- }
-
- for (int i = 0; i < info.getOverriddenSymbolsCount(); i++) {
- String overriddenSymbol = info.getOverriddenSymbols(i);
- if (overriddenSymbol.isEmpty()) {
- continue;
- }
- if (isIgnoredOverriddenSymbol(overriddenSymbol)) {
- continue;
- }
- Package overriddenSymbolPkg =
- packages.packageForSymbol(overriddenSymbol).orElse(Package.EMPTY);
- scipInfo.addRelationships(
- Relationship.newBuilder()
- .setSymbol(typedSymbol(overriddenSymbol, overriddenSymbolPkg))
- .setIsImplementation(true)
- .setIsReference(supportsReferenceRelationship(info)));
- }
- if (info.hasSignature()) {
- String language =
- doc.semanticdb.getLanguage().toString().toLowerCase(Locale.ROOT).intern();
- String signature = new SignatureFormatter(info, symtab).formatSymbol();
- Signature.Builder signatureDocumentation =
- Signature.newBuilder().setLanguage(language).setText(signature);
- scipInfo.setSignatureDocumentation(signatureDocumentation);
- }
- String documentation = info.getDocumentation().getMessage();
- if (!documentation.isEmpty()) {
- scipInfo.addDocumentation(documentation);
- }
- tdoc.addSymbols(scipInfo);
- }
- writer.emitTyped(Index.newBuilder().addDocuments(tdoc).build());
- }
- }
-
- private Index typedMetadata() {
- return Index.newBuilder()
- .setMetadata(
- Metadata.newBuilder()
- .setVersion(ProtocolVersion.UnspecifiedProtocolVersion)
- .setProjectRoot(options.sourceroot.toUri().toString())
- .setTextDocumentEncoding(TextEncoding.UTF8)
- .setToolInfo(
- ToolInfo.newBuilder()
- .setName(options.toolInfo.getName())
- .setVersion(options.toolInfo.getVersion())
- .addAllArguments(options.toolInfo.getArgumentsList())))
- .build();
- }
-
- private Stream filesStream(List files) {
- return options.parallel ? files.parallelStream() : files.stream();
- }
-
- private static class InverseReferenceRelationships {
- public final Map> map;
-
- private InverseReferenceRelationships(Map> map) {
- this.map = map;
- }
- }
-
- private InverseReferenceRelationships inverseReferenceRelationships(List files) {
- if (!options.emitInverseRelationships) {
- return new InverseReferenceRelationships(Collections.emptyMap());
- }
- return new InverseReferenceRelationships(
- filesStream(files)
- .flatMap(this::parseTextDocument)
- .flatMap(this::referenceRelationships)
- .collect(
- Collectors.groupingBy(
- SymbolRelationship::getTo,
- Collectors.mapping(
- SymbolRelationship::getFrom, Collectors.toCollection(ArrayList::new)))));
- }
-
- private Stream referenceRelationships(ScipTextDocument document) {
- ArrayList relationships = new ArrayList<>();
- for (int i = 0; i < document.semanticdb.getSymbolsCount(); i++) {
- Semanticdb.SymbolInformation info = document.semanticdb.getSymbols(i);
- if (!supportsReferenceRelationship(info)) {
- continue;
- }
- if (info.getSymbol().isEmpty() || SemanticdbSymbols.isLocal(info.getSymbol())) {
- continue;
- }
- for (int j = 0; j < info.getOverriddenSymbolsCount(); j++) {
- String overriddenSymbol = info.getOverriddenSymbols(j);
- if (SemanticdbSymbols.isLocal(overriddenSymbol)) {
- continue;
- }
- relationships.add(new SymbolRelationship(info.getSymbol(), overriddenSymbol));
- }
- }
- return relationships.stream();
- }
-
- private static boolean supportsReferenceRelationship(Semanticdb.SymbolInformation info) {
- switch (info.getKind()) {
- case INTERFACE:
- case TYPE:
- case CLASS:
- case OBJECT:
- case PACKAGE_OBJECT:
- return false;
- default:
- return true;
- }
- }
-
- private Stream parseTextDocument(Path semanticdbPath) {
- try {
- return textDocumentsParseFrom(semanticdbPath).getDocumentsList().stream()
- .filter(sdb -> !sdb.getOccurrencesList().isEmpty())
- .map(sdb -> new ScipTextDocument(semanticdbPath, sdb, options.sourceroot));
- } catch (IOException e) {
- options.reporter.error("invalid protobuf: " + semanticdbPath);
- options.reporter.error(e);
- return Stream.empty();
- }
- }
-
- private static PathMatcher jarPattern = FileSystems.getDefault().getPathMatcher("glob:**.jar");
-
- private Semanticdb.TextDocuments textDocumentsParseFrom(Path semanticdbPath) throws IOException {
- if (jarPattern.matches(semanticdbPath)) {
- return textDocumentsParseJarFile(semanticdbPath);
- }
- return textDocumentsParseFromBytes(Files.readAllBytes(semanticdbPath));
- }
-
- private Semanticdb.TextDocuments textDocumentsParseJarFile(Path jarFile) throws IOException {
- Semanticdb.TextDocuments.Builder result = Semanticdb.TextDocuments.newBuilder();
- try (JarFile file = new JarFile(jarFile.toFile())) {
- Enumeration entries = file.entries();
- while (entries.hasMoreElements()) {
- JarEntry element = entries.nextElement();
- if (element.getName().endsWith(".semanticdb")) {
- byte[] bytes = InputStreamBytes.readAll(file.getInputStream(element));
- result.addAllDocuments(textDocumentsParseFromBytes(bytes).getDocumentsList());
- }
- }
- }
- return result.build();
- }
-
- private Semanticdb.TextDocuments textDocumentsParseFromBytes(byte[] bytes) throws IOException {
- try {
- CodedInputStream in = CodedInputStream.newInstance(bytes);
- in.setRecursionLimit(1000);
- return Semanticdb.TextDocuments.parseFrom(in);
- } catch (NoSuchMethodError ignored) {
- // NOTE(olafur): For some reason, NoSuchMethodError gets thrown when running
- // `snapshots/run`
- // in the sbt build. I'm unable to reproduce the error in `snapshots/test` or
- // when running the
- // published version
- // of `scip-java index`.
- return Semanticdb.TextDocuments.parseFrom(bytes);
- }
- }
-
- private boolean isIgnoredOverriddenSymbol(String symbol) {
- // Skip java/lang/Object# and similar symbols from Scala since it's the parent
- // of all classes
- // making it noisy for "find implementations" results.
- return symbol.equals("java/lang/Object#");
- }
-}
diff --git a/scip-semanticdb/src/main/java/com/sourcegraph/scip_semanticdb/ScipShardAggregator.java b/scip-semanticdb/src/main/java/com/sourcegraph/scip_semanticdb/ScipShardAggregator.java
index e407ddde6..3892e01ab 100644
--- a/scip-semanticdb/src/main/java/com/sourcegraph/scip_semanticdb/ScipShardAggregator.java
+++ b/scip-semanticdb/src/main/java/com/sourcegraph/scip_semanticdb/ScipShardAggregator.java
@@ -21,6 +21,7 @@
import java.util.Collections;
import java.util.Enumeration;
import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -220,10 +221,9 @@ public int hashCode() {
private static SymbolInformation mergeSymbol(SymbolInformation a, SymbolInformation b) {
SymbolInformation.Builder builder = b.toBuilder();
- LinkedHashMap rels = new LinkedHashMap<>();
- for (Relationship r : a.getRelationshipsList()) rels.put(r, r);
- for (Relationship r : b.getRelationshipsList()) rels.put(r, r);
- builder.clearRelationships().addAllRelationships(rels.values());
+ LinkedHashSet rels = new LinkedHashSet<>(a.getRelationshipsList());
+ rels.addAll(b.getRelationshipsList());
+ builder.clearRelationships().addAllRelationships(rels);
return builder.build();
}
diff --git a/scip-semanticdb/src/main/java/com/sourcegraph/scip_semanticdb/ScipTextDocument.java b/scip-semanticdb/src/main/java/com/sourcegraph/scip_semanticdb/ScipTextDocument.java
deleted file mode 100644
index 13c5dddae..000000000
--- a/scip-semanticdb/src/main/java/com/sourcegraph/scip_semanticdb/ScipTextDocument.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package com.sourcegraph.scip_semanticdb;
-
-import com.sourcegraph.semanticdb.Semanticdb;
-
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.List;
-
-/** Wrapper around a SemanticDB TextDocument with SCIP-related utilities. */
-public class ScipTextDocument {
- public final Path semanticdbPath;
- public final Semanticdb.TextDocument semanticdb;
-
- public ScipTextDocument(
- Path semanticdbPath, Semanticdb.TextDocument semanticdb, Path sourceroot) {
- this.semanticdbPath = semanticdbPath;
- String uri = sourceroot.resolve(semanticdb.getUri()).toUri().toString();
- this.semanticdb = Semanticdb.TextDocument.newBuilder(semanticdb).setUri(uri).build();
- }
-
- @Override
- public String toString() {
- return "ScipDocument{" + "path=" + semanticdbPath + ", semanticdb=" + semanticdb + '}';
- }
-
- public List sortedSymbolOccurrences() {
- return ScipTextDocument.sortedSymbolOccurrences(semanticdb);
- }
-
- public static List sortedSymbolOccurrences(
- Semanticdb.TextDocument semanticdb) {
- ArrayList result =
- new ArrayList<>(semanticdb.getOccurrencesList().size());
- result.addAll(semanticdb.getOccurrencesList());
- for (Semanticdb.Synthetic synthetic : semanticdb.getSyntheticsList()) {
- addAllSyntheticOccurrences(synthetic, result);
- }
- result.sort((o1, o2) -> new RangeComparator().compare(o1.getRange(), o2.getRange()));
- return result;
- }
-
- private static void addAllSyntheticOccurrences(
- Semanticdb.Synthetic synthetic, ArrayList buffer) {
- Semanticdb.Range offsetRange =
- Semanticdb.Range.newBuilder(synthetic.getRange())
- .setStartLine(synthetic.getRange().getEndLine())
- .setStartCharacter(synthetic.getRange().getEndCharacter())
- .build();
- new SemanticdbTreeVisitor() {
- @Override
- void visitIdTree(Semanticdb.IdTree tree) {
- Semanticdb.SymbolOccurrence syntheticOccurrence =
- Semanticdb.SymbolOccurrence.newBuilder()
- .setRange(offsetRange)
- .setSymbol(tree.getSymbol())
- .setRole(Semanticdb.SymbolOccurrence.Role.REFERENCE)
- .build();
- buffer.add(syntheticOccurrence);
- }
- }.visitTree(synthetic.getTree());
- }
-}
diff --git a/scip-semanticdb/src/main/java/com/sourcegraph/scip_semanticdb/SemanticdbTreeVisitor.java b/scip-semanticdb/src/main/java/com/sourcegraph/scip_semanticdb/SemanticdbTreeVisitor.java
deleted file mode 100644
index ca9b296cd..000000000
--- a/scip-semanticdb/src/main/java/com/sourcegraph/scip_semanticdb/SemanticdbTreeVisitor.java
+++ /dev/null
@@ -1,80 +0,0 @@
-package com.sourcegraph.scip_semanticdb;
-
-import com.sourcegraph.semanticdb.Semanticdb.*;
-
-public abstract class SemanticdbTreeVisitor {
- public void visitTree(Tree tree) {
- if (tree.hasApplyTree()) {
- this.visitApplyTree(tree.getApplyTree());
- } else if (tree.hasFunctionTree()) {
- this.visitFunctionTree(tree.getFunctionTree());
- } else if (tree.hasIdTree()) {
- this.visitIdTree(tree.getIdTree());
- } else if (tree.hasLiteralTree()) {
- this.visitLiteralTree(tree.getLiteralTree());
- } else if (tree.hasMacroExpansionTree()) {
- this.visitMacroExpansionTree(tree.getMacroExpansionTree());
- } else if (tree.hasOriginalTree()) {
- this.visitOriginalTree(tree.getOriginalTree());
- } else if (tree.hasSelectTree()) {
- this.visitSelectTree(tree.getSelectTree());
- } else if (tree.hasTypeApplyTree()) {
- this.visitTypeApplyTree(tree.getTypeApplyTree());
- } else if (tree.hasAnnotationTree()) {
- this.visitAnnotationTree(tree.getAnnotationTree());
- } else if (tree.hasAssignTree()) {
- this.visitAssignTree(tree.getAssignTree());
- } else if (tree.hasBinopTree()) {
- this.visitBinaryOperatorTree(tree.getBinopTree());
- }
- }
-
- void visitApplyTree(ApplyTree tree) {
- visitTree(tree.getFunction());
- for (Tree argument : tree.getArgumentsList()) {
- visitTree(argument);
- }
- }
-
- void visitFunctionTree(FunctionTree tree) {
- for (IdTree parameter : tree.getParametersList()) {
- visitIdTree(parameter);
- }
- visitTree(tree.getBody());
- }
-
- void visitIdTree(IdTree tree) {}
-
- void visitLiteralTree(LiteralTree tree) {}
-
- void visitMacroExpansionTree(MacroExpansionTree tree) {
- visitTree(tree.getBeforeExpansion());
- }
-
- void visitOriginalTree(OriginalTree tree) {}
-
- void visitSelectTree(SelectTree tree) {
- visitTree(tree.getQualifier());
- visitIdTree(tree.getId());
- }
-
- void visitTypeApplyTree(TypeApplyTree tree) {
- visitTree(tree.getFunction());
- }
-
- void visitAnnotationTree(AnnotationTree tree) {
- for (Tree parameter : tree.getParametersList()) {
- visitTree(parameter);
- }
- }
-
- void visitAssignTree(AssignTree tree) {
- visitTree(tree.getLhs());
- visitTree(tree.getRhs());
- }
-
- void visitBinaryOperatorTree(BinaryOperatorTree tree) {
- visitTree(tree.getLhs());
- visitTree(tree.getRhs());
- }
-}
diff --git a/scip-semanticdb/src/main/java/com/sourcegraph/scip_semanticdb/SemanticdbWalker.java b/scip-semanticdb/src/main/java/com/sourcegraph/scip_semanticdb/SemanticdbWalker.java
deleted file mode 100644
index c0cd0a717..000000000
--- a/scip-semanticdb/src/main/java/com/sourcegraph/scip_semanticdb/SemanticdbWalker.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package com.sourcegraph.scip_semanticdb;
-
-import java.io.IOException;
-import java.nio.file.FileSystems;
-import java.nio.file.FileVisitResult;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.PathMatcher;
-import java.nio.file.SimpleFileVisitor;
-import java.nio.file.attribute.BasicFileAttributes;
-import java.util.ArrayList;
-import java.util.List;
-
-/** A file visitor that recursively collects all SemanticDB files in a given directory. */
-public class SemanticdbWalker extends SimpleFileVisitor {
- private final ArrayList result;
- private final ScipSemanticdbOptions options;
- private final PathMatcher semanticdbPattern =
- FileSystems.getDefault().getPathMatcher("glob:**.semanticdb");
-
- public SemanticdbWalker(ScipSemanticdbOptions options) {
- this.options = options;
- result = new ArrayList<>();
- }
-
- @Override
- public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
- if (semanticdbPattern.matches(file)) {
- result.add(file);
- }
- return super.visitFile(file, attrs);
- }
-
- @Override
- public FileVisitResult visitFileFailed(Path file, IOException exc) {
- options.reporter.error(exc);
- return FileVisitResult.CONTINUE;
- }
-
- public static List findSemanticdbFiles(ScipSemanticdbOptions options) throws IOException {
- SemanticdbWalker walker = new SemanticdbWalker(options);
- PathMatcher jarPattern = FileSystems.getDefault().getPathMatcher("glob:**.jar");
- for (Path root : options.targetroots) {
- if (jarPattern.matches(root)) {
- walker.result.add(root);
- } else {
- Files.walkFileTree(root, walker);
- }
- }
- return walker.result;
- }
-}
diff --git a/scip-semanticdb/src/main/java/com/sourcegraph/scip_semanticdb/SignatureFormatter.java b/scip-semanticdb/src/main/java/com/sourcegraph/scip_semanticdb/SignatureFormatter.java
deleted file mode 100644
index 216c2adb3..000000000
--- a/scip-semanticdb/src/main/java/com/sourcegraph/scip_semanticdb/SignatureFormatter.java
+++ /dev/null
@@ -1,822 +0,0 @@
-package com.sourcegraph.scip_semanticdb;
-
-import com.sourcegraph.semanticdb.Semanticdb.SymbolInformation.Property;
-import com.sourcegraph.semanticdb.Semanticdb.*;
-
-import com.sourcegraph.semanticdb.SemanticdbSymbols;
-
-import java.util.*;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.stream.Collectors;
-
-import static com.sourcegraph.semanticdb.SemanticdbBuilders.typeRef;
-
-public class SignatureFormatter {
- private static final Type OBJECT_TYPE_REF = typeRef("java/lang/Object#");
- private static final Type PRODUCT_TYPE_REF = typeRef("scala/Product#");
- private static final Type SCALA_SERIALIZABLE_TYPE_REF = typeRef("scala/package.Serializable#");
- private static final Type JAVA_SERIALIZABLE_TYPE_REF = typeRef("java/util/Serializable#");
- private static final Type SCALA_ANY_TYPE_REF = typeRef("scala/Any#");
- private static final Type SCALA_ANYREF_TYPE_REF = typeRef("scala/AnyRef#");
-
- private static final Type WILDCARD_TYPE_REF = typeRef("local_wildcard");
-
- private static final Type NOTHING_SYMBOL = typeRef("scala/Nothing#");
- private static final String FUNCTION_SYMBOL_PREFIX = "scala/Function";
- // Special case scala/Function object to not conflict with Function1 for example
- private static final String FUNCTION_OBJECT = "scala/Function.";
- private static final String TUPLE_SYMBOL_PREFIX = "scala/Tuple";
- private static final String ARRAY_SYMBOL = "scala/Array#";
- private static final String ENUM_SYMBOL = "java/lang/Enum#";
- private static final String ANNOTATION_SYMBOL = "java/lang/annotation/Annotation#";
- private static final Set REDUNDANT_CLASS_PARENTS = new HashSet<>();
- private static final Set CASE_CLASS_PARENTS = new HashSet<>();
-
- {
- REDUNDANT_CLASS_PARENTS.add(OBJECT_TYPE_REF);
- REDUNDANT_CLASS_PARENTS.add(SCALA_ANY_TYPE_REF);
- REDUNDANT_CLASS_PARENTS.add(SCALA_ANYREF_TYPE_REF);
-
- CASE_CLASS_PARENTS.add(PRODUCT_TYPE_REF);
- CASE_CLASS_PARENTS.add(SCALA_SERIALIZABLE_TYPE_REF);
- CASE_CLASS_PARENTS.add(JAVA_SERIALIZABLE_TYPE_REF);
- }
-
- private final StringBuilder s = new StringBuilder();
- private final SymbolInformation symbolInformation;
- private final Symtab symtab;
- private final boolean isScala;
-
- public SignatureFormatter(SymbolInformation symbolInformation, Symtab symtab) {
- this.symbolInformation = symbolInformation;
- this.symtab = symtab;
- this.isScala = symbolInformation.getLanguage() == Language.SCALA;
- }
-
- public String formatSymbol() {
- try {
- return formatSymbolUnsafe();
- } catch (Exception e) {
- throw new SignatureFormatterException(symbolInformation, e);
- }
- }
-
- private String formatSymbolUnsafe() {
- Signature signature = symbolInformation.getSignature();
- if (signature.hasClassSignature()) {
- formatClassSignature(signature.getClassSignature());
- } else if (signature.hasMethodSignature()) {
- formatMethodSignature(signature.getMethodSignature());
- } else if (signature.hasValueSignature()) {
- formatValueSignature(signature.getValueSignature());
- } else if (signature.hasTypeSignature()) {
- formatTypeParameterSignature(signature.getTypeSignature());
- }
- return s.toString();
- }
-
- private void formatClassSignature(ClassSignature classSignature) {
- boolean isAnnotation =
- classSignature.getParentsList().stream()
- .anyMatch(t -> t.getTypeRef().getSymbol().equals(ANNOTATION_SYMBOL));
-
- boolean isEnum = has(Property.ENUM);
- boolean isInterface = symbolInformation.getKind() == SymbolInformation.Kind.INTERFACE;
-
- printKeywordln(formatAnnotations());
-
- printKeyword(formatAccess());
- if (!isEnum && !isAnnotation && !isInterface) printKeyword(formatModifiers());
-
- switch (symbolInformation.getKind()) {
- case CLASS:
- if (isEnum) {
- printKeyword("enum");
- } else {
- printKeyword("class");
- }
- break;
- case INTERFACE:
- if (isAnnotation) {
- printKeyword("@interface");
- break;
- }
- printKeyword("interface");
- break;
- case OBJECT:
- printKeyword("object");
- break;
- case TRAIT:
- printKeyword("trait");
- break;
- case PACKAGE_OBJECT:
- printKeyword("package object");
- break;
- default:
- break;
- }
- s.append(symbolInformation.getDisplayName());
- if (symbolInformation.getKind() == SymbolInformation.Kind.CLASS && has(Property.CASE)) {
- primaryConstructor(classSignature)
- .ifPresent(
- constructorSignature ->
- formatScalaParameterList(constructorSignature.getParameterListsList()));
- }
-
- List typeParameters = getSymlinks(classSignature.getTypeParameters());
- if (!typeParameters.isEmpty()) {
- s.append(
- typeParameters.stream()
- .map(this::formatTypeParameter)
- .collect(Collectors.joining(", ", isScala ? "[" : "<", isScala ? "]" : ">")));
- }
-
- boolean hasNonRedundantParent =
- classSignature.getParentsList().size() > 0
- && !REDUNDANT_CLASS_PARENTS.contains(classSignature.getParentsList().get(0));
-
- boolean isCaseClass =
- isScala
- && symbolInformation.getKind() == SymbolInformation.Kind.CLASS
- && has(Property.CASE);
-
- List nonSyntheticParents =
- classSignature.getParentsList().stream()
- .filter(parent -> !REDUNDANT_CLASS_PARENTS.contains(parent))
- .filter(parent -> !parent.getTypeRef().getSymbol().equals(ENUM_SYMBOL))
- .filter(parent -> isCaseClass && !CASE_CLASS_PARENTS.contains(parent))
- .filter(parent -> !parent.getTypeRef().getSymbol().equals(ANNOTATION_SYMBOL))
- .collect(Collectors.toList());
-
- if (nonSyntheticParents.isEmpty()) return;
-
- if (isScala) {
- printKeyword(" extends");
- s.append(
- nonSyntheticParents.stream().map(this::formatType).collect(Collectors.joining(" with ")));
- return;
- }
-
- // Determine which parents from ClassSignature.parents are classes or interfaces so we know to
- // use
- // 'extends' or 'implements'.
- // The logic is as follows:
- // 1. If the symbol has type CLASS, there will always be at least 1 parent. For enums, this is
- // java/lang/Enum#, otherwise it is java/lang/Object# if no superclass is specified.
- // Therefore, if the parents list contains java/lang/Object# type or the symbol is an enum,
- // then no superclass was defined and all parents are interfaces and we must print
- // 'implements'
- // followed by all superinterfaces.
- // Else if it is not an enum and the list of non-synthetic parents is non empty, a superclass
- // was specified and we must print it with the 'extends' keyword prefixed, followed by
- // 'implements' and all superinterfaces, if any.
- // 2. If the symbol has type INTERFACE, then any defined parents must also be interfaces, so if
- // the list of non-synthetic parents is not empty, print 'implements' and all
- // superinterfaces.
- switch (symbolInformation.getKind()) {
- case CLASS:
- // if no superclass or is an enum, every non synthetic parent is an interface
- if (isEnum || !hasNonRedundantParent) {
- printKeyword(" implements");
-
- String superInterfaces =
- nonSyntheticParents.stream().map(this::formatType).collect(Collectors.joining(", "));
- s.append(superInterfaces);
- } else {
- // else if has a superclass and is not an enum
- printKeyword(" extends");
- s.append(formatType(nonSyntheticParents.get(0)));
-
- String superInterfaces =
- nonSyntheticParents.stream()
- .skip(1)
- .map(this::formatType)
- .collect(Collectors.joining(", "));
- if (!superInterfaces.isEmpty()) {
- printKeyword(" implements");
- s.append(superInterfaces);
- }
- }
- break;
- case INTERFACE:
- // can only extend other interfaces
- printKeyword(" extends");
-
- String superInterfaces =
- nonSyntheticParents.stream().map(this::formatType).collect(Collectors.joining(", "));
- s.append(superInterfaces);
- }
- }
-
- private void formatMethodSignature(MethodSignature methodSignature) {
- if (isScala) {
- formatScalaMethodSignature(methodSignature);
- return;
- }
-
- printKeywordln(formatAnnotations());
- printKeyword(formatAccess());
- printKeyword(formatModifiers());
-
- List typeParameters = getSymlinks(methodSignature.getTypeParameters());
- if (!typeParameters.isEmpty()) {
- printKeyword(
- typeParameters.stream()
- .map(this::formatTypeParameter)
- .collect(Collectors.joining(", ", "<", ">")));
- }
-
- if (symbolInformation.getKind() == SymbolInformation.Kind.CONSTRUCTOR) {
- String owner = SymbolDescriptor.parseFromSymbol(symbolInformation.getSymbol()).owner;
- // Fix for https://github.com/sourcegraph/scip-java/issues/150
- if (!owner.equals(SemanticdbSymbols.NONE)) {
- s.append(SymbolDescriptor.parseFromSymbol(owner).descriptor.name);
- }
- } else {
- printKeyword(formatType(methodSignature.getReturnType()));
- s.append(symbolInformation.getDisplayName());
- }
-
- s.append(
- methodSignature.getParameterListsList().stream()
- .flatMap((params) -> getSymlinks(params).stream())
- .map(this::formatTermParameter)
- .collect(Collectors.joining(", ", "(", ")")));
-
- if (!methodSignature.getThrowsList().isEmpty()) {
- printKeyword(" throws");
- s.append(
- methodSignature.getThrowsList().stream()
- .map(this::formatType)
- .collect(Collectors.joining(", ")));
- }
- }
-
- private String formatTermParameter(SymbolInformation info) {
- if (info == null) return "";
- if (isScala) {
- return info.getDisplayName()
- + ": "
- + formatType(info.getSignature().getValueSignature().getTpe());
- }
- return formatType(info.getSignature().getValueSignature().getTpe())
- + " "
- + info.getDisplayName();
- }
-
- private void formatScalaMethodSignature(MethodSignature methodSignature) {
- printKeywordln(formatAnnotations());
- printKeyword(formatAccess());
- printKeyword(formatModifiers());
- if (has(Property.VAL)) {
- printKeyword("val");
- } else if (has(Property.VAR)) {
- printKeyword("var");
- } else {
- printKeyword("def");
- }
- s.append(
- symbolInformation.getKind() == SymbolInformation.Kind.CONSTRUCTOR
- ? "this"
- : symbolInformation.getDisplayName());
- formatScalaParameterList(methodSignature.getParameterListsList());
- if (symbolInformation.getKind() != SymbolInformation.Kind.CONSTRUCTOR) {
- printKeyword(":");
- s.append(this.formatType(methodSignature.getReturnType()));
- }
- }
-
- private Optional primaryConstructor(ClassSignature classSignature) {
- Symtab scopeSymtab = symtab.withHardlinks(classSignature.getDeclarations());
- int n = classSignature.getDeclarations().getSymlinksCount();
- for (int i = 0; i < n; i++) {
- String symlink = classSignature.getDeclarations().getSymlinks(i);
- SymbolInformation info = scopeSymtab.symbols.get(symlink);
- if (info != null
- && info.getKind() == SymbolInformation.Kind.CONSTRUCTOR
- && has(Property.PRIMARY, info)
- && info.hasSignature()
- && info.getSignature().hasMethodSignature()) {
- return Optional.of(info.getSignature().getMethodSignature());
- }
- }
- return Optional.empty();
- }
-
- private void formatScalaParameterList(List parameterList) {
- for (Scope scope : parameterList) {
- List infos =
- scope.getHardlinksCount() > 0 ? scope.getHardlinksList() : getSymlinks(scope);
- s.append(
- infos.stream()
- .map(this::formatTermParameter)
- .collect(Collectors.joining(", ", "(", ")")));
- }
- }
-
- private void formatValueSignature(ValueSignature valueSignature) {
- printKeywordln(formatAnnotations());
- if (isEnumConstant()) {
- String ownerSym = SymbolDescriptor.parseFromSymbol(symbolInformation.getSymbol()).owner;
- SymbolInformation ownerInfo = symtab.symbols.get(ownerSym);
- List enumConstants =
- getSymlinks(ownerInfo.getSignature().getClassSignature().getDeclarations()).stream()
- .filter(Objects::nonNull)
- .filter(this::isEnumConstant)
- .collect(Collectors.toList());
- int ordinal = enumConstants.indexOf(symbolInformation);
- s.append(ownerInfo.getDisplayName()).append('.');
- s.append(this.symbolInformation.getDisplayName());
- s.append(" /* ordinal ").append(ordinal).append(" */");
- } else {
- printKeyword(formatAccess());
- printKeyword(formatModifiers());
- if (isScala) {
- s.append(symbolInformation.getDisplayName());
- printKeyword(":");
- printKeyword(formatType(valueSignature.getTpe()));
- } else {
- printKeyword(formatType(valueSignature.getTpe()));
- s.append(symbolInformation.getDisplayName());
- }
- }
- }
-
- private void formatTypeParameterSignature(TypeSignature typeSignature) {
- if (isScala && symbolInformation.getKind() == SymbolInformation.Kind.TYPE) {
- printKeyword("type");
- }
- s.append(symbolInformation.getDisplayName());
- if (typeSignature.hasLowerBound()
- && (!isScala || !typeSignature.getLowerBound().equals(NOTHING_SYMBOL))) {
- printKeyword(isScala ? " >:" : " super");
- s.append(formatType(typeSignature.getLowerBound()));
- }
- if (typeSignature.hasUpperBound()
- && !typeSignature.getUpperBound().equals(isScala ? SCALA_ANY_TYPE_REF : OBJECT_TYPE_REF)) {
- printKeyword(isScala ? " <:" : " extends");
- s.append(formatType(typeSignature.getUpperBound()));
- }
- }
-
- /**
- * Transforms symlinks from a Scope into a List of SymbolInformation's looked up in the Symtab.
- */
- private List getSymlinks(Scope scope) {
- ArrayList symlinks = new ArrayList<>();
- for (int i = 0; i < scope.getSymlinksCount(); i++) {
- SymbolInformation info = symtab.symbols.get(scope.getSymlinks(i));
- if (info != null) {
- symlinks.add(info);
- }
- }
- return symlinks;
- }
-
- /**
- * Formats one of a method's/class's type parameter symbols through recursion from the
- * SymbolInformation extracted from the Symtab. This works by the signature being a TypeSignature.
- */
- private String formatTypeParameter(SymbolInformation typeInfo) {
- return new SignatureFormatter(typeInfo, symtab).formatSymbol();
- }
-
- private String formatTypeArguments(List typeArguments) {
- if (typeArguments.isEmpty()) return "";
-
- return typeArguments.stream()
- .map(this::formatType)
- .collect(Collectors.joining(", ", isScala ? "[" : "<", isScala ? "]" : ">"));
- }
-
- private String formatAnnotations() {
- return formatAnnotations(symbolInformation);
- }
-
- private String formatAnnotations(SymbolInformation symInfo) {
- return symInfo.getAnnotationsList().stream()
- .map(this::formatAnnotation)
- .collect(Collectors.joining("\n"));
- }
-
- private String formatAnnotation(AnnotationTree annotation) {
- StringBuilder b = new StringBuilder();
- b.append('@');
- b.append(formatType(annotation.getTpe()));
-
- if (annotation.getParametersCount() == 1) {
- b.append('(');
-
- Tree parameter = annotation.getParameters(0);
- // if only 1 parameter, and its LHS is named 'value', we can omit the 'value = '
- // https://docs.oracle.com/javase/specs/jls/se8/html/jls-9.html#jls-9.7.3
- AssignTree firstParam = parameter.getAssignTree();
- if (parameter.hasAssignTree()
- && SymbolDescriptor.parseFromSymbol(firstParam.getLhs().getIdTree().getSymbol())
- .descriptor
- .name
- .equals("value")) {
- b.append(formatTree(firstParam.getRhs()));
- } else {
- b.append(formatTree(parameter));
- }
-
- b.append(')');
- } else if (annotation.getParametersCount() > 1) {
- b.append('(');
- String parameter =
- annotation.getParametersList().stream()
- .map(this::formatTree)
- .collect(Collectors.joining(", "));
- b.append(parameter);
- b.append(')');
- }
-
- return b.toString();
- }
-
- private String formatTree(Tree tree) {
- if (tree.hasIdTree()) {
- return SymbolDescriptor.parseFromSymbol(tree.getIdTree().getSymbol()).descriptor.name;
- } else if (tree.hasLiteralTree()) {
- return formatConstant(tree.getLiteralTree().getConstant());
- } else if (tree.hasSelectTree()) {
- return formatTree(tree.getSelectTree().getQualifier())
- + "."
- + SymbolDescriptor.parseFromSymbol(tree.getSelectTree().getId().getSymbol())
- .descriptor
- .name;
- } else if (tree.hasAnnotationTree()) {
- return formatAnnotation(tree.getAnnotationTree());
- } else if (tree.hasApplyTree()) {
- if (tree.getApplyTree().getFunction().hasIdTree()
- && tree.getApplyTree().getFunction().getIdTree().getSymbol().equals(ARRAY_SYMBOL)) {
- return tree.getApplyTree().getArgumentsList().stream()
- .map(this::formatTree)
- .collect(Collectors.joining(", ", "{", "}"));
- } else {
- throw new IllegalArgumentException(
- "unexpected apply tree function " + tree.getApplyTree().getFunction());
- }
- } else if (tree.hasBinopTree()) {
- return formatTree(tree.getBinopTree().getLhs())
- + " "
- + formatBinaryOperator(tree.getBinopTree().getOp())
- + " "
- + formatTree(tree.getBinopTree().getRhs());
- } else if (tree.hasAssignTree()) {
- return formatTree(tree.getAssignTree().getLhs())
- + " = "
- + formatTree(tree.getAssignTree().getRhs());
- } else if (tree.hasUnaryopTree()) {
- return formatUnaryOperation(tree.getUnaryopTree());
- } else if (tree.hasCastTree()) {
- return "("
- + formatType(tree.getCastTree().getTpe())
- + ")"
- + " "
- + formatTree(tree.getCastTree().getValue());
- }
-
- throw new IllegalArgumentException("tree was of unexpected type " + tree);
- }
-
- private String formatUnaryOperation(UnaryOperatorTree tree) {
- String formattedValue = formatTree(tree.getTree());
- switch (tree.getOp()) {
- case UNARY_MINUS:
- return "-" + formattedValue;
- case UNARY_PLUS:
- return "-" + formattedValue;
- case UNARY_POSTFIX_INCREMENT:
- return formattedValue + "++";
- case UNARY_POSTFIX_DECREMENT:
- return formattedValue + "--";
- case UNARY_PREFIX_DECREMENT:
- return "--" + formattedValue;
- case UNARY_PREFIX_INCREMENT:
- return "++" + formattedValue;
-
- case UNARY_BITWISE_COMPLEMENT:
- return "~" + formattedValue;
- case UNARY_LOGICAL_COMPLEMENT:
- return "!" + formattedValue;
- }
-
- throw new IllegalArgumentException(
- "unary operation of unexpected type" + tree.getOp().toString());
- }
-
- private String formatConstant(Constant constant) {
- if (constant.hasUnitConstant()) {
- return isScala ? "()" : "scala.Unit()";
- } else if (constant.hasBooleanConstant()) {
- return Boolean.toString(constant.getBooleanConstant().getValue());
- } else if (constant.hasByteConstant()) {
- return Integer.toString(constant.getByteConstant().getValue());
- } else if (constant.hasShortConstant()) {
- return Integer.toString(constant.getShortConstant().getValue());
- } else if (constant.hasCharConstant()) {
- return String.format("'%s'", (char) constant.getCharConstant().getValue());
- } else if (constant.hasIntConstant()) {
- return Integer.toString(constant.getIntConstant().getValue());
- } else if (constant.hasLongConstant()) {
- return Long.toString(constant.getLongConstant().getValue());
- } else if (constant.hasFloatConstant()) {
- return Float.toString(constant.getFloatConstant().getValue()) + 'f';
- } else if (constant.hasDoubleConstant()) {
- return Double.toString(constant.getDoubleConstant().getValue());
- } else if (constant.hasStringConstant()) {
- return '"' + constant.getStringConstant().getValue() + '"';
- } else if (constant.hasNullConstant()) {
- return "null";
- }
- throw new IllegalArgumentException("constant was not of known type " + constant);
- }
-
- private String formatBinaryOperator(BinaryOperator op) {
- switch (op) {
- case PLUS:
- return "+";
- case MINUS:
- return "-";
- case MULTIPLY:
- return "*";
- case DIVIDE:
- return "/";
- case REMAINDER:
- return "%";
- case GREATER_THAN:
- return ">";
- case LESS_THAN:
- return "<";
- case AND:
- return "&";
- case XOR:
- return "^";
- case OR:
- return "|";
- case CONDITIONAL_AND:
- return "&&";
- case CONDITIONAL_OR:
- return "||";
- case SHIFT_LEFT:
- return "<<";
- case SHIFT_RIGHT:
- return ">>";
- case SHIFT_RIGHT_UNSIGNED:
- return ">>>";
- case EQUAL_TO:
- return "==";
- case NOT_EQUAL_TO:
- return "!=";
- case GREATER_THAN_EQUAL:
- return ">=";
- case LESS_THAN_EQUAL:
- return "<=";
- case UNRECOGNIZED:
- throw new IllegalArgumentException("unexpected binary operator " + op);
- }
- return null;
- }
-
- private String formatType(Type type) {
- StringBuilder b = new StringBuilder();
- if (type.hasTypeRef()) {
- TypeRef typeRef = type.getTypeRef();
- if (typeRef.getSymbol().equals(ARRAY_SYMBOL)) {
- if (isScala) {
- b.append("Array[");
- b.append(formatType(typeRef.getTypeArguments(0)));
- b.append("]");
- } else {
- b.append(formatType(typeRef.getTypeArguments(0)));
- b.append("[]");
- }
- } else if (isScala
- && typeRef.getSymbol().startsWith(FUNCTION_SYMBOL_PREFIX)
- && typeRef.getTypeArgumentsCount() > 0
- && !typeRef.getSymbol().startsWith(FUNCTION_OBJECT)) {
- int n = typeRef.getTypeArgumentsCount() - 1;
- if (n == 0) {
- // Special-case for Function1[A, B]: don't wrap `A` in parenthesis like this `(A) => B`
- s.append(formatType(typeRef.getTypeArguments(0)));
- } else {
- b.append(
- typeRef.getTypeArgumentsList().stream()
- .limit(Math.max(0, n))
- .map(this::formatType)
- .collect(Collectors.joining(", ", "(", ")")));
- }
- b.append(" => ");
- b.append(formatType(typeRef.getTypeArguments(n)));
- } else if (isScala && typeRef.getSymbol().startsWith(TUPLE_SYMBOL_PREFIX)) {
- b.append(
- typeRef.getTypeArgumentsList().stream()
- .map(this::formatType)
- .collect(Collectors.joining(", ", "(", ")")));
- } else {
- b.append(symbolDisplayName(typeRef.getSymbol()));
- b.append(formatTypeArguments(typeRef.getTypeArgumentsList()));
- }
- } else if (type.hasSingleType()) {
- SingleType tpe = type.getSingleType();
- if (tpe.hasPrefix()) {
- b.append(formatType(tpe.getPrefix()));
- }
- SymbolInformation info = symtab.symbols.get(tpe.getSymbol());
- if (info != null) {
- b.append(info.getDisplayName()).append(".type");
- }
- } else if (type.hasThisType()) {
- b.append("this.type");
- } else if (type.hasSuperType()) {
- SuperType tpe = type.getSuperType();
- if (tpe.hasPrefix()) {
- b.append(formatType(tpe.getPrefix())).append(".");
- }
- b.append("super");
- } else if (type.hasConstantType()) {
- b.append(this.formatConstant(type.getConstantType().getConstant()));
- } else if (type.hasIntersectionType()) {
- b.append(
- type.getIntersectionType().getTypesList().stream()
- .map(this::formatType)
- .collect(Collectors.joining(isScala ? " with " : " & ")));
- } else if (type.hasUnionType()) {
- b.append(
- type.getIntersectionType().getTypesList().stream()
- .map(this::formatType)
- .collect(Collectors.joining(" | ")));
- } else if (type.hasStructuralType()) {
- StructuralType tpe = type.getStructuralType();
- int n = tpe.getDeclarations().getHardlinksCount();
- if (n == 0) {
- b.append(" {}");
- } else {
- b.append(formatType(tpe.getTpe())).append(" {");
- Symtab hardlinkSymtab = symtab.withHardlinks(tpe.getDeclarations());
- for (int i = 0; i < n; i++) {
- SymbolInformation info = tpe.getDeclarations().getHardlinks(i);
- if (i > 0) {
- b.append(";");
- }
- b.append(" ").append(new SignatureFormatter(info, hardlinkSymtab).formatSymbol());
- }
- b.append(" }");
- }
- } else if (type.hasExistentialType()) {
- AtomicInteger hardlinkStep = new AtomicInteger();
- TypeRef typeRef = type.getExistentialType().getTpe().getTypeRef();
- b.append(symbolDisplayName(type.getExistentialType().getTpe().getTypeRef().getSymbol()));
- b.append(
- typeRef.getTypeArgumentsList().stream()
- .map(
- (typeArg) -> {
- // if hardlink (aka wildcard) we need to reach into declarations at index
- // hardlinkStep
- if (typeArg.equals(WILDCARD_TYPE_REF)) {
- SymbolInformation hardlink =
- type.getExistentialType()
- .getDeclarations()
- .getHardlinks(hardlinkStep.getAndIncrement());
- return symbolDisplayName(hardlink.getSymbol())
- + new SignatureFormatter(hardlink, symtab).formatSymbol();
- }
- // else for symlink we can use the usual path
- return formatType(typeArg);
- })
- .collect(Collectors.joining(", ", isScala ? "[" : "<", isScala ? "[" : ">")));
- } else if (type.hasByNameType()) {
- b.append("=> ").append(formatType(type.getByNameType().getTpe()));
- } else if (type.hasRepeatedType()) {
- b.append(formatType(type.getRepeatedType().getTpe())).append("*");
- }
-
- return b.toString().trim();
- }
-
- private String formatAccess() {
- Access access = symbolInformation.getAccess();
- if (access.hasPrivateAccess()) {
- return "private";
- } else if (!isScala && access.hasPublicAccess()) {
- return "public";
- } else if (access.hasProtectedAccess()) {
- return "protected";
- } else if (isScala && access.hasPrivateThisAccess()) {
- return "private[this]";
- } else if (isScala && access.hasPrivateWithinAccess()) {
- String name =
- SymbolDescriptor.parseFromSymbol(access.getPrivateWithinAccess().getSymbol())
- .descriptor
- .name;
- return String.format("protected[%s]", name);
- }
- return "";
- }
-
- // https://checkstyle.sourceforge.io/config_modifier.html#ModifierOrder
- private String formatModifiers() {
- ArrayList modifiers = new ArrayList<>();
- if (has(Property.ABSTRACT)) {
- if (isScala && symbolInformation.getKind() != SymbolInformation.Kind.CLASS) {
- } else {
- modifiers.add("abstract");
- }
- }
- if (has(Property.DEFAULT)) modifiers.add("default");
- if (has(Property.STATIC)) modifiers.add("static");
- if (has(Property.FINAL)) {
- if (symbolInformation.getKind() != SymbolInformation.Kind.OBJECT
- && symbolInformation.getKind() != SymbolInformation.Kind.PACKAGE_OBJECT) {
- modifiers.add("final");
- }
- }
- if (has(Property.IMPLICIT)) modifiers.add("implicit");
- if (has(Property.SEALED)) modifiers.add("sealed");
- if (has(Property.CASE)) modifiers.add("case");
- return String.join(" ", modifiers);
- }
-
- private void printKeyword(String keyword) {
- if (keyword.isEmpty()) return;
- s.append(keyword).append(' ');
- }
-
- private void printKeywordln(String keyword) {
- if (keyword.isEmpty()) return;
- s.append(keyword).append('\n');
- }
-
- private boolean isEnumConstant(SymbolInformation symInfo) {
- if (!(has(Property.ENUM, symInfo)
- && has(Property.FINAL, symInfo)
- && has(Property.STATIC, symInfo)
- && symInfo.getAccess().hasPublicAccess())) {
- return false;
- }
- SymbolInformation owner =
- symtab.symbols.get(SymbolDescriptor.parseFromSymbol(symInfo.getSymbol()).owner);
- if (owner == null) return false;
- return owner.getKind() == SymbolInformation.Kind.CLASS && has(Property.ENUM, owner);
- }
-
- private boolean isEnumConstant() {
- return isEnumConstant(symbolInformation);
- }
-
- private boolean has(Property property, SymbolInformation symInfo) {
- return (symInfo.getProperties() & property.getNumber()) > 0;
- }
-
- private boolean has(Property property) {
- return has(property, symbolInformation);
- }
-
- /**
- * Transforms a SemanticDB symbol string into its Java identifier display string. As SemanticDB
- * uses Scala "primitive" types for Java primitives (but not for Java boxing primitive wrappers),
- * we check for those first before attempting to decode a SemanticDB symbol.
- */
- public String symbolDisplayName(String symbol) {
- if (isScala) {
- return symbolScalaDisplayName(symbol);
- }
- return symbolJavaDisplayName(symbol);
- }
-
- private String symbolScalaDisplayName(String symbol) {
- if ("local_wildcard".equals(symbol)) {
- return "*";
- }
- return SymbolDescriptor.parseFromSymbol(symbol).descriptor.name;
- }
-
- private String symbolJavaDisplayName(String symbol) {
- switch (symbol) {
- case "local_wildcard":
- return "?";
- case "scala/Boolean#":
- return "boolean";
- case "scala/Byte#":
- return "byte";
- case "scala/Short#":
- return "short";
- case "scala/Int#":
- return "int";
- case "scala/Long#":
- return "long";
- case "scala/Char#":
- return "char";
- case "scala/Float#":
- return "float";
- case "scala/Double#":
- return "double";
- case "scala/Unit#":
- return "void";
- default:
- return SymbolDescriptor.parseFromSymbol(symbol).descriptor.name;
- }
- }
-}
diff --git a/scip-semanticdb/src/main/java/com/sourcegraph/scip_semanticdb/SignatureFormatterException.java b/scip-semanticdb/src/main/java/com/sourcegraph/scip_semanticdb/SignatureFormatterException.java
deleted file mode 100644
index 32799afb2..000000000
--- a/scip-semanticdb/src/main/java/com/sourcegraph/scip_semanticdb/SignatureFormatterException.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package com.sourcegraph.scip_semanticdb;
-
-import com.sourcegraph.semanticdb.Semanticdb;
-
-public class SignatureFormatterException extends RuntimeException {
- public SignatureFormatterException(
- Semanticdb.SymbolInformation symbolInformation, Throwable cause) {
- super(
- String.format(
- "failed to format symbol '%s'\n%s", symbolInformation.getSymbol(), symbolInformation),
- cause);
- }
-
- @Override
- public synchronized Throwable fillInStackTrace() {
- // This exception doesn't have a relevant stack trace. The cause exception
- // already points to the culprit filename and line number.
- return this;
- }
-}
diff --git a/scip-semanticdb/src/main/java/com/sourcegraph/scip_semanticdb/SymbolOccurrences.java b/scip-semanticdb/src/main/java/com/sourcegraph/scip_semanticdb/SymbolOccurrences.java
deleted file mode 100644
index 5ffe51001..000000000
--- a/scip-semanticdb/src/main/java/com/sourcegraph/scip_semanticdb/SymbolOccurrences.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package com.sourcegraph.scip_semanticdb;
-
-import com.sourcegraph.semanticdb.Semanticdb;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class SymbolOccurrences {
- public List occurrences = new ArrayList<>();
-
- public void addSyntheticDefinition(String sym) {
- occurrences.add(
- Semanticdb.SymbolOccurrence.newBuilder()
- .setSymbol(sym)
- .setRole(Semanticdb.SymbolOccurrence.Role.SYNTHETIC_DEFINITION)
- .build());
- }
-
- public void addDefinition(String sym) {
- occurrences.add(
- Semanticdb.SymbolOccurrence.newBuilder()
- .setSymbol(sym)
- .setRole(Semanticdb.SymbolOccurrence.Role.DEFINITION)
- .build());
- }
-}
diff --git a/scip-semanticdb/src/main/java/com/sourcegraph/scip_semanticdb/Symtab.java b/scip-semanticdb/src/main/java/com/sourcegraph/scip_semanticdb/Symtab.java
deleted file mode 100644
index 5cd54d7cf..000000000
--- a/scip-semanticdb/src/main/java/com/sourcegraph/scip_semanticdb/Symtab.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package com.sourcegraph.scip_semanticdb;
-
-import com.sourcegraph.semanticdb.Semanticdb;
-
-import java.util.HashMap;
-
-public class Symtab {
- public final HashMap symbols = new HashMap<>();
-
- public Symtab(Semanticdb.TextDocument document) {
- for (Semanticdb.SymbolInformation symbolInformation : document.getSymbolsList()) {
- symbols.put(symbolInformation.getSymbol(), symbolInformation);
- }
- }
-
- public Symtab withHardlinks(Semanticdb.Scope scope) {
- Symtab hardlinkSymtab = new Symtab(Semanticdb.TextDocument.getDefaultInstance());
- hardlinkSymtab.symbols.putAll(this.symbols);
- for (int i = 0; i < scope.getHardlinksCount(); i++) {
- Semanticdb.SymbolInformation info = scope.getHardlinks(i);
- hardlinkSymtab.symbols.put(info.getSymbol(), info);
- }
- return hardlinkSymtab;
- }
-}
diff --git a/semanticdb-javac/BUILD b/semanticdb-javac/BUILD
index a67ed8a82..8b4c3fcfe 100644
--- a/semanticdb-javac/BUILD
+++ b/semanticdb-javac/BUILD
@@ -31,7 +31,7 @@ java_library(
srcs = glob(["src/main/java/**/*.java"]),
resources = ["src/main/resources/META-INF/services/com.sun.source.util.Plugin"],
deps = [
- "//semanticdb-shared",
+ "//semanticdb-java",
"@maven//:org_scip_code_scip_java_bindings",
],
)
diff --git a/semanticdb-javac/src/main/java/com/sourcegraph/semanticdb_javac/CompilerRange.java b/semanticdb-javac/src/main/java/com/sourcegraph/semanticdb_javac/CompilerRange.java
index d5ce770bf..d64440dbd 100644
--- a/semanticdb-javac/src/main/java/com/sourcegraph/semanticdb_javac/CompilerRange.java
+++ b/semanticdb-javac/src/main/java/com/sourcegraph/semanticdb_javac/CompilerRange.java
@@ -30,12 +30,6 @@ public enum CompilerRange {
*/
FROM_POINT_TO_SYMBOL_NAME,
- /**
- * Map the compiler (point + 1) position to SemanticDB start and use (point + symbol name length +
- * 1) for the SemanticDB end position.
- */
- FROM_POINT_TO_SYMBOL_NAME_PLUS_ONE,
-
/**
* Use text search to find the start of the symbol name and use (found start + symbol name length)
* for the SemanticDB end position.
@@ -46,18 +40,11 @@ public enum CompilerRange {
* Use text search to find the start of the symbol name, using the point position as the starting
* search offset and using (found start + symbol name length) for the SemanticDB end position.
*/
- FROM_POINT_WITH_TEXT_SEARCH,
-
- /**
- * Use text search to find the start of the symbol name, searching from the end instead of the
- * start.
- */
- FROM_END_WITH_TEXT_SEARCH;
+ FROM_POINT_WITH_TEXT_SEARCH;
public boolean isFromPoint() {
switch (this) {
case FROM_POINT_TO_SYMBOL_NAME:
- case FROM_POINT_TO_SYMBOL_NAME_PLUS_ONE:
case FROM_POINT_WITH_TEXT_SEARCH:
return true;
default:
@@ -66,36 +53,16 @@ public boolean isFromPoint() {
}
public boolean isFromEndPoint() {
- switch (this) {
- case FROM_END_TO_SYMBOL_NAME:
- case FROM_END_WITH_TEXT_SEARCH:
- return true;
- default:
- return false;
- }
+ return this == FROM_END_TO_SYMBOL_NAME;
}
public boolean isFromTextSearch() {
switch (this) {
case FROM_TEXT_SEARCH:
- case FROM_END_WITH_TEXT_SEARCH:
case FROM_POINT_WITH_TEXT_SEARCH:
return true;
default:
return false;
}
}
-
- public boolean isPlusOne() {
- switch (this) {
- case FROM_POINT_TO_SYMBOL_NAME_PLUS_ONE:
- return true;
- default:
- return false;
- }
- }
-
- public boolean isFromEnd() {
- return this == CompilerRange.FROM_END_WITH_TEXT_SEARCH;
- }
}
diff --git a/semanticdb-javac/src/main/java/com/sourcegraph/semanticdb_javac/MD5.java b/semanticdb-javac/src/main/java/com/sourcegraph/semanticdb_javac/MD5.java
deleted file mode 100644
index 3d5be0659..000000000
--- a/semanticdb-javac/src/main/java/com/sourcegraph/semanticdb_javac/MD5.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package com.sourcegraph.semanticdb_javac;
-
-import java.nio.CharBuffer;
-import java.nio.charset.StandardCharsets;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-
-/** Utility to compute MD5 checksums of strings. */
-public final class MD5 {
- private static final char[] HEX_ARRAY;
-
- static {
- HEX_ARRAY = "0123456789ABCDEF".toCharArray();
- }
-
- private static String bytesToHex(byte[] bytes) {
- char[] hexChars = new char[bytes.length * 2];
- int j = 0;
- while (j < bytes.length) {
- int v = bytes[j] & 0xFF;
- hexChars[j * 2] = HEX_ARRAY[v >>> 4];
- hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
- j += 1;
- }
- return new String(hexChars);
- }
-
- public static String digest(CharSequence chars) throws NoSuchAlgorithmException {
- MessageDigest md5 = MessageDigest.getInstance("MD5");
- return bytesToHex(md5.digest(chars.toString().getBytes()));
- }
-}
diff --git a/semanticdb-javac/src/main/java/com/sourcegraph/semanticdb_javac/RangeFinder.java b/semanticdb-javac/src/main/java/com/sourcegraph/semanticdb_javac/RangeFinder.java
index 27a325ce6..948af07b6 100644
--- a/semanticdb-javac/src/main/java/com/sourcegraph/semanticdb_javac/RangeFinder.java
+++ b/semanticdb-javac/src/main/java/com/sourcegraph/semanticdb_javac/RangeFinder.java
@@ -2,7 +2,6 @@
import javax.tools.Diagnostic;
-import javax.lang.model.element.Element;
import java.util.Optional;
public class RangeFinder {
@@ -16,81 +15,30 @@ public static class StartEndRange {
}
}
+ /**
+ * Searches {@code source} for {@code name} starting at {@code start}. If the name cannot be found
+ * and {@code fallback} is not {@link Diagnostic#NOPOS}, returns a range anchored at {@code
+ * fallback}; otherwise returns {@link Optional#empty()}.
+ */
public static Optional findRange(
- Element element,
- String name,
- int originalStartPos,
- int originalEndPos,
- String source,
- boolean fromEnd) {
- int startPos = findNameIn(name, originalStartPos, originalEndPos, element, source, fromEnd);
- int endPos = startPos + name.length();
-
- if (endPos == -1 || startPos == -1) {
- return Optional.empty();
- }
-
- return Optional.of(new StartEndRange(startPos, endPos));
- }
-
- private static int findNameFromEnd(
- String name,
- int originalStartPos,
- int originalEndPos,
- int end,
- Element element,
- String source) {
- if (end < 0) return -1;
- int offset = source.lastIndexOf(name, end);
- if (offset == -1 && originalStartPos != Diagnostic.NOPOS && originalEndPos != Diagnostic.NOPOS)
- return originalStartPos;
- if (offset == -1) {
- return -1;
- }
- int endOfWord = offset + name.length();
- // found name in wrong word? e.g. finding `"A"` in `A("A")`
- if (offset > 0 && Character.isJavaIdentifierPart(source.charAt(offset - 1)))
- return findNameFromEnd(name, originalStartPos, originalEndPos, offset - 1, element, source);
- if (endOfWord < source.length() && Character.isJavaIdentifierPart(source.charAt(endOfWord)))
- return findNameFromEnd(name, originalStartPos, originalEndPos, offset - 1, element, source);
-
- return offset;
+ String name, int start, int fallback, String source) {
+ if (source.length() == 0) return Optional.empty();
+ int startPos = findNameFromStart(name, start, fallback, source);
+ if (startPos == -1) return Optional.empty();
+ return Optional.of(new StartEndRange(startPos, startPos + name.length()));
}
- private static int findNameFromStart(
- String name,
- int start,
- int originalStartPos,
- int originalEndPos,
- Element element,
- String source) {
+ private static int findNameFromStart(String name, int start, int fallback, String source) {
if (start >= source.length()) return -1;
int offset = source.indexOf(name, start);
- if (offset == -1 && originalStartPos != Diagnostic.NOPOS && originalEndPos != Diagnostic.NOPOS)
- return originalStartPos;
- if (offset == -1) {
- return -1;
- }
+ if (offset == -1 && fallback != Diagnostic.NOPOS) return fallback;
+ if (offset == -1) return -1;
int end = offset + name.length();
// found name in wrong word? e.g. finding `"A"` in `A("A")`
if (offset > 0 && Character.isJavaIdentifierPart(source.charAt(offset - 1)))
- return findNameFromStart(name, end + 1, originalStartPos, originalEndPos, element, source);
+ return findNameFromStart(name, end + 1, fallback, source);
if (end < source.length() && Character.isJavaIdentifierPart(source.charAt(end)))
- return findNameFromStart(name, end + 1, originalStartPos, originalEndPos, element, source);
-
+ return findNameFromStart(name, end + 1, fallback, source);
return offset;
}
-
- private static int findNameIn(
- String name, int start, int end, Element element, String source, boolean fromEnd) {
- if (source.length() == 0) return -1;
-
- int offset;
- if (fromEnd) offset = findNameFromEnd(name, start, start, end, element, source);
- else offset = findNameFromStart(name, start, end, end, element, source);
- if (offset > -1) {
- return offset;
- }
- return -1;
- }
}
diff --git a/semanticdb-javac/src/main/java/com/sourcegraph/semanticdb_javac/ScipOccurrences.java b/semanticdb-javac/src/main/java/com/sourcegraph/semanticdb_javac/ScipOccurrences.java
index 893282719..68dd0fbc3 100644
--- a/semanticdb-javac/src/main/java/com/sourcegraph/semanticdb_javac/ScipOccurrences.java
+++ b/semanticdb-javac/src/main/java/com/sourcegraph/semanticdb_javac/ScipOccurrences.java
@@ -8,15 +8,14 @@
import java.util.Objects;
/**
- * Accumulator that deduplicates SCIP {@link Occurrence} entries by their {@code (symbol, range,
- * roles)} key. Variants that differ only in whether {@code enclosing_range} is set are collapsed,
- * preferring the one that carries the enclosing range.
+ * Accumulator that deduplicates SCIP {@link Occurrence} entries by {@code (symbol, range, roles)}.
+ * Variants differing only in whether {@code enclosing_range} is set are collapsed, preferring the
+ * one that carries it.
*/
final class ScipOccurrences {
private final LinkedHashMap out = new LinkedHashMap<>();
- /** Adds {@code occ}, collapsing it into any existing entry with the same {@link Key}. */
void add(Occurrence occ) {
Key key = Key.of(occ);
Occurrence existing = out.get(key);
@@ -29,12 +28,10 @@ void add(Occurrence occ) {
}
}
- /** Adds every occurrence in {@code occs}. */
void addAll(Iterable occs) {
for (Occurrence occ : occs) add(occ);
}
- /** Returns the deduplicated occurrences in insertion order. */
Collection values() {
return out.values();
}
diff --git a/semanticdb-javac/src/main/java/com/sourcegraph/semanticdb_javac/ScipShards.java b/semanticdb-javac/src/main/java/com/sourcegraph/semanticdb_javac/ScipShards.java
index 092996967..93d3cccb9 100644
--- a/semanticdb-javac/src/main/java/com/sourcegraph/semanticdb_javac/ScipShards.java
+++ b/semanticdb-javac/src/main/java/com/sourcegraph/semanticdb_javac/ScipShards.java
@@ -7,7 +7,9 @@
import java.util.ArrayList;
import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
import java.util.List;
+import java.util.Map;
/**
* Pure merge helpers for SCIP shards produced by the compiler plugin.
@@ -21,10 +23,7 @@ public final class ScipShards {
private ScipShards() {}
- /**
- * Merges two SCIP shards by combining their document lists. Documents that share a {@code
- * relative_path} have their occurrences, symbols and external symbols deduplicated.
- */
+ /** Merges two shards: documents sharing a {@code relative_path} are dedup-merged. */
static Index merge(Index a, Index b) {
Index.Builder builder = Index.newBuilder();
if (b.hasMetadata()) {
@@ -47,7 +46,7 @@ static Index merge(Index a, Index b) {
}
builder.addAllDocuments(byPath.values());
- // External symbols: deduplicate by symbol string. Last writer wins to keep latest data.
+ // External symbols: last writer wins to keep the most recent data.
LinkedHashMap externals = new LinkedHashMap<>();
for (SymbolInformation s : a.getExternalSymbolsList()) externals.put(s.getSymbol(), s);
for (SymbolInformation s : b.getExternalSymbolsList()) externals.put(s.getSymbol(), s);
@@ -57,19 +56,16 @@ static Index merge(Index a, Index b) {
}
private static Document mergeDocuments(Document a, Document b) {
+ // b.toBuilder() carries forward the most recent language/relative_path/text/encoding.
Document.Builder builder = b.toBuilder().clearOccurrences().clearSymbols();
- // Use the most recent metadata for language/relative_path/text/encoding which already
- // come from b via toBuilder().
- // Deduplicate occurrences by (range, symbol, roles). Variants that differ only in
- // enclosing_range get collapsed, preferring the one that carries the enclosing range.
ScipOccurrences occurrences = new ScipOccurrences();
occurrences.addAll(a.getOccurrencesList());
occurrences.addAll(b.getOccurrencesList());
builder.addAllOccurrences(occurrences.values());
- // Deduplicate symbols by symbol string; merge relationships and documentation.
- LinkedHashMap bySymbol = new LinkedHashMap<>();
+ // Dedup symbols by symbol string; merge relationships and documentation.
+ Map bySymbol = new LinkedHashMap<>();
for (SymbolInformation info : a.getSymbolsList()) bySymbol.put(info.getSymbol(), info);
for (SymbolInformation info : b.getSymbolsList()) {
SymbolInformation existing = bySymbol.get(info.getSymbol());
@@ -82,13 +78,12 @@ private static Document mergeDocuments(Document a, Document b) {
private static SymbolInformation mergeSymbol(SymbolInformation a, SymbolInformation b) {
SymbolInformation.Builder builder = b.toBuilder();
- // Merge relationships, deduplicating by structural equality with deterministic ordering.
- LinkedHashMap rels = new LinkedHashMap<>();
- for (Relationship r : a.getRelationshipsList()) rels.put(r, r);
- for (Relationship r : b.getRelationshipsList()) rels.put(r, r);
- builder.clearRelationships().addAllRelationships(rels.values());
+ // Dedup relationships by structural equality; preserve insertion order.
+ LinkedHashSet rels = new LinkedHashSet<>(a.getRelationshipsList());
+ rels.addAll(b.getRelationshipsList());
+ builder.clearRelationships().addAllRelationships(rels);
- // Merge documentation, preserving order and avoiding duplicates.
+ // Dedup documentation; preserve insertion order.
List docs = new ArrayList<>(a.getDocumentationList());
for (String d : b.getDocumentationList()) {
if (!docs.contains(d)) docs.add(d);
diff --git a/semanticdb-javac/src/main/java/com/sourcegraph/semanticdb_javac/ScipVisitor.java b/semanticdb-javac/src/main/java/com/sourcegraph/semanticdb_javac/ScipVisitor.java
index b0a7f6b60..805fbd0c2 100644
--- a/semanticdb-javac/src/main/java/com/sourcegraph/semanticdb_javac/ScipVisitor.java
+++ b/semanticdb-javac/src/main/java/com/sourcegraph/semanticdb_javac/ScipVisitor.java
@@ -55,11 +55,9 @@
import javax.tools.Diagnostic;
/**
- * Walks the AST of a typechecked compilation unit and generates a {@link Document} directly.
- *
- * Symbols are produced through {@link GlobalSymbolsCache} and then translated to the placeholder
- * SCIP form via {@link ScipSymbols#fromSemanticdbSymbol(String)}. Signature documentation is
- * produced by {@link ScipSignatureFormatter} directly from javac's element model.
+ * Walks a typechecked compilation unit and builds a {@link Document}. Symbols come from {@link
+ * GlobalSymbolsCache} via {@link ScipSymbols#fromSemanticdbSymbol(String)} and signatures from
+ * {@link ScipSignatureFormatter}.
*/
public final class ScipVisitor extends TreePathScanner {
@@ -97,9 +95,9 @@ public ScipVisitor(
this.nodes = new LinkedHashMap<>();
}
- /** Builds a single-document {@link Index} shard for the given compilation unit. */
- public Index buildShard(CompilationUnitTree tree) {
- this.scan(tree, null);
+ /** Builds a single-document {@link Index} shard for this compilation unit. */
+ public Index buildShard() {
+ this.scan(compUnitTree, null);
resolveNodes();
Document.Builder document =
@@ -116,10 +114,6 @@ public Index buildShard(CompilationUnitTree tree) {
/** SCIP {@code Document.language} value for Java sources. */
private static final String LANGUAGE_JAVA = "java";
- // ==========================
- // Symbol/occurrence emission
- // ==========================
-
private Optional emitSymbolOccurrence(
Element sym, Tree tree, Name name, int roles, CompilerRange kind) {
if (sym == null || name == null) return Optional.empty();
@@ -245,10 +239,6 @@ private static boolean supportsReferenceRelationship(Element sym) {
}
}
- // =================================================
- // Kind translation for SCIP emission.
- // =================================================
-
private static SymbolInformation.Kind scipKind(Element sym) {
Set modifiers = sym.getModifiers();
boolean isStatic = modifiers.contains(Modifier.STATIC);
@@ -290,10 +280,6 @@ private static SymbolInformation.Kind scipKind(Element sym) {
}
}
- // ===========================================
- // Node resolution and traversal (unchanged from SemanticdbVisitor)
- // ===========================================
-
void resolveNodes() {
HashSet ignoreNodes = new HashSet<>();
for (Tree node : nodes.keySet()) {
@@ -478,10 +464,6 @@ private void resolveNewClassTree(NewClassTree node, TreePath treePath) {
}
}
- // =================================================
- // Symbol / range helpers used by the SCIP emission path.
- // =================================================
-
private String semanticdbSymbol(Element sym) {
return globals.semanticdbSymbol(sym, locals);
}
@@ -492,12 +474,11 @@ private Optional scipRangeOf(Tree tree, CompilerRange kind, Element s
SourcePositions sourcePositions = trees.getSourcePositions();
int start = (int) sourcePositions.getStartPosition(compUnitTree, tree);
int end = (int) sourcePositions.getEndPosition(compUnitTree, tree);
- if (kind.isPlusOne()) start++;
if (name != null) {
if (kind.isFromTextSearch() && name.length() > 0) {
Optional startEndRange =
- RangeFinder.findRange(sym, name, start, end, this.source, kind.isFromEnd());
+ RangeFinder.findRange(name, start, end, this.source);
if (startEndRange.isPresent()) {
start = startEndRange.get().start;
end = startEndRange.get().end;
diff --git a/semanticdb-javac/src/main/java/com/sourcegraph/semanticdb_javac/SemanticdbJavacOptions.java b/semanticdb-javac/src/main/java/com/sourcegraph/semanticdb_javac/SemanticdbJavacOptions.java
index 75387d4ff..6374d2787 100644
--- a/semanticdb-javac/src/main/java/com/sourcegraph/semanticdb_javac/SemanticdbJavacOptions.java
+++ b/semanticdb-javac/src/main/java/com/sourcegraph/semanticdb_javac/SemanticdbJavacOptions.java
@@ -13,12 +13,11 @@
import com.sun.source.util.JavacTask;
import static javax.tools.StandardLocation.CLASS_OUTPUT;
-import static javax.tools.StandardLocation.SOURCE_OUTPUT;
/** Settings that can be configured alongside the -Xplugin compiler option. */
public class SemanticdbJavacOptions {
- /** The directory to place META-INF and its .semanticdb files */
+ /** The directory to place META-INF and its SCIP shard files */
public Path targetroot;
public Path sourceroot;
@@ -28,7 +27,6 @@ public class SemanticdbJavacOptions {
public boolean alreadyReportedErrors = false;
public UriScheme uriScheme = UriScheme.DEFAULT;
public NoRelativePathMode noRelativePath = NoRelativePathMode.INDEX_ANYWAY;
- public Path generatedTargetRoot;
public SemanticdbJavacOptions() {
errors = new ArrayList<>();
@@ -52,7 +50,7 @@ public static SemanticdbJavacOptions parse(String[] args, JavacTask task) {
String argValue = arg.substring("-targetroot:".length());
if (argValue.equals(JAVAC_CLASSES_DIR_ARG)) {
useJavacClassesDir = true;
- result.targetroot = getJavacClassesDir(result, task).classes;
+ result.targetroot = getJavacClassesDir(result, task);
} else {
result.targetroot = Paths.get(argValue);
}
@@ -82,9 +80,7 @@ public static SemanticdbJavacOptions parse(String[] args, JavacTask task) {
} else if (arg.equals("-build-tool:bazel")) {
result.uriScheme = UriScheme.BAZEL;
useJavacClassesDir = true;
- TargetPaths paths = getJavacClassesDir(result, task);
- result.targetroot = paths.classes;
- result.generatedTargetRoot = paths.sources;
+ result.targetroot = getJavacClassesDir(result, task);
} else if (arg.equals("-text:on")) {
result.includeText = true;
} else if (arg.equals("-text:off")) {
@@ -124,28 +120,22 @@ private static boolean isSourcerootDefined(SemanticdbJavacOptions options) {
// warning - use of internal API
// requires --add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED
- private static TargetPaths getJavacClassesDir(SemanticdbJavacOptions result, JavacTask task) {
+ private static Path getJavacClassesDir(SemanticdbJavacOptions result, JavacTask task) {
// both Context and BasicJavacTask are internal JDK classes so not exported
// under >= JDK 17
// com.sun.tools.javac.util.Context ctx =
// ((com.sun.tools.javac.api.BasicJavacTask)
// task).getContext();
// I'm not aware of a better way to get the class output directory from javac
- Path classOutputDir = null;
- Path sourceOutputDir = null;
try {
Method getContext = task.getClass().getMethod("getContext");
Object context = getContext.invoke(task);
Method get = context.getClass().getMethod("get", Class.class);
JavaFileManager fm = (JavaFileManager) get.invoke(context, JavaFileManager.class);
- FileObject sourceOutputDirStub =
- fm.getJavaFileForOutput(
- SOURCE_OUTPUT, SemanticdbPlugin.stubClassName, JavaFileObject.Kind.SOURCE, null);
- FileObject clasSOutputDirStub =
+ FileObject classOutputDirStub =
fm.getJavaFileForOutput(
CLASS_OUTPUT, SemanticdbPlugin.stubClassName, JavaFileObject.Kind.CLASS, null);
- classOutputDir = Paths.get(clasSOutputDirStub.toUri()).toAbsolutePath().getParent();
- sourceOutputDir = Paths.get(sourceOutputDirStub.toUri()).toAbsolutePath().getParent();
+ return Paths.get(classOutputDirStub.toUri()).toAbsolutePath().getParent();
} catch (Exception e) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
e.printStackTrace(new PrintStream(out));
@@ -154,7 +144,7 @@ private static TargetPaths getJavacClassesDir(SemanticdbJavacOptions result, Jav
"exception while processing SemanticDB option '-targetroot:%s'\n%s",
JAVAC_CLASSES_DIR_ARG, out.toString());
result.errors.add(errorMsg);
+ return null;
}
- return new TargetPaths(classOutputDir, sourceOutputDir);
}
}
diff --git a/semanticdb-javac/src/main/java/com/sourcegraph/semanticdb_javac/SemanticdbTaskListener.java b/semanticdb-javac/src/main/java/com/sourcegraph/semanticdb_javac/SemanticdbTaskListener.java
index 50788bb79..f67bd5530 100644
--- a/semanticdb-javac/src/main/java/com/sourcegraph/semanticdb_javac/SemanticdbTaskListener.java
+++ b/semanticdb-javac/src/main/java/com/sourcegraph/semanticdb_javac/SemanticdbTaskListener.java
@@ -114,7 +114,7 @@ private void onFinishedAnalyze(TaskEvent e) {
try {
Index shard =
new ScipVisitor(globals, e.getCompilationUnit(), options, types, trees, elements)
- .buildShard(e.getCompilationUnit());
+ .buildShard();
Files.createDirectories(shardPath.getParent());
if (Files.exists(shardPath)) {
try (InputStream is = Files.newInputStream(shardPath)) {
diff --git a/semanticdb-javac/src/main/java/com/sourcegraph/semanticdb_javac/TargetPaths.java b/semanticdb-javac/src/main/java/com/sourcegraph/semanticdb_javac/TargetPaths.java
deleted file mode 100644
index c523e3534..000000000
--- a/semanticdb-javac/src/main/java/com/sourcegraph/semanticdb_javac/TargetPaths.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.sourcegraph.semanticdb_javac;
-
-import java.nio.file.Path;
-
-public class TargetPaths {
- public Path classes;
- public Path sources;
-
- public TargetPaths(Path classesDir, Path sourcesDir) {
- classes = classesDir;
- sources = sourcesDir;
- }
-}
diff --git a/semanticdb-kotlinc/src/main/java/com/sourcegraph/semanticdb_kotlinc/SemanticdbBuilders.kt b/semanticdb-kotlinc/src/main/java/com/sourcegraph/semanticdb_kotlinc/SemanticdbBuilders.kt
deleted file mode 100644
index 652ebe267..000000000
--- a/semanticdb-kotlinc/src/main/java/com/sourcegraph/semanticdb_kotlinc/SemanticdbBuilders.kt
+++ /dev/null
@@ -1,407 +0,0 @@
-// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
-@file:JvmName("-SemanticdbBuilders")
-
-package com.sourcegraph.semanticdb_kotlinc
-
-import kotlin.DslMarker
-import kotlin.Unit
-import kotlin.annotation.AnnotationRetention
-import kotlin.annotation.AnnotationTarget
-import kotlin.annotation.Retention
-import kotlin.annotation.Target
-import kotlin.jvm.JvmName
-
-inline fun Semanticdb.TextDocuments.copy(block: Semanticdb.TextDocuments.Builder.() -> Unit):
- Semanticdb.TextDocuments = this.toBuilder().apply(block).build()
-
-operator fun Semanticdb.TextDocuments.plus(other: Semanticdb.TextDocuments):
- Semanticdb.TextDocuments = this.toBuilder().mergeFrom(other).build()
-
-fun Semanticdb.TextDocuments?.orDefault(): Semanticdb.TextDocuments = this ?:
- Semanticdb.TextDocuments.getDefaultInstance()
-
-inline fun Semanticdb.TextDocuments.Builder.addDocuments(block:
- Semanticdb.TextDocument.Builder.() -> Unit): Semanticdb.TextDocuments.Builder =
- this.addDocuments(Semanticdb.TextDocument.newBuilder().apply(block).build())
-
-inline fun Semanticdb.TextDocument.copy(block: Semanticdb.TextDocument.Builder.() -> Unit):
- Semanticdb.TextDocument = this.toBuilder().apply(block).build()
-
-operator fun Semanticdb.TextDocument.plus(other: Semanticdb.TextDocument): Semanticdb.TextDocument =
- this.toBuilder().mergeFrom(other).build()
-
-fun Semanticdb.TextDocument?.orDefault(): Semanticdb.TextDocument = this ?:
- Semanticdb.TextDocument.getDefaultInstance()
-
-inline fun Semanticdb.TextDocument.Builder.addSymbols(block:
- Semanticdb.SymbolInformation.Builder.() -> Unit): Semanticdb.TextDocument.Builder =
- this.addSymbols(Semanticdb.SymbolInformation.newBuilder().apply(block).build())
-
-inline fun Semanticdb.TextDocument.Builder.addOccurrences(block:
- Semanticdb.SymbolOccurrence.Builder.() -> Unit): Semanticdb.TextDocument.Builder =
- this.addOccurrences(Semanticdb.SymbolOccurrence.newBuilder().apply(block).build())
-
-inline fun Semanticdb.Range.copy(block: Semanticdb.Range.Builder.() -> Unit): Semanticdb.Range =
- this.toBuilder().apply(block).build()
-
-operator fun Semanticdb.Range.plus(other: Semanticdb.Range): Semanticdb.Range =
- this.toBuilder().mergeFrom(other).build()
-
-fun Semanticdb.Range?.orDefault(): Semanticdb.Range = this ?: Semanticdb.Range.getDefaultInstance()
-
-inline fun Semanticdb.Signature.copy(block: Semanticdb.Signature.Builder.() -> Unit):
- Semanticdb.Signature = this.toBuilder().apply(block).build()
-
-operator fun Semanticdb.Signature.plus(other: Semanticdb.Signature): Semanticdb.Signature =
- this.toBuilder().mergeFrom(other).build()
-
-fun Semanticdb.Signature?.orDefault(): Semanticdb.Signature = this ?:
- Semanticdb.Signature.getDefaultInstance()
-
-inline fun Semanticdb.Signature.Builder.classSignature(block:
- Semanticdb.ClassSignature.Builder.() -> Unit): Semanticdb.Signature.Builder =
- this.setClassSignature(Semanticdb.ClassSignature.newBuilder().apply(block).build())
-
-inline fun Semanticdb.Signature.Builder.methodSignature(block:
- Semanticdb.MethodSignature.Builder.() -> Unit): Semanticdb.Signature.Builder =
- this.setMethodSignature(Semanticdb.MethodSignature.newBuilder().apply(block).build())
-
-inline fun Semanticdb.Signature.Builder.typeSignature(block: Semanticdb.TypeSignature.Builder.() ->
- Unit): Semanticdb.Signature.Builder =
- this.setTypeSignature(Semanticdb.TypeSignature.newBuilder().apply(block).build())
-
-inline fun Semanticdb.Signature.Builder.valueSignature(block:
- Semanticdb.ValueSignature.Builder.() -> Unit): Semanticdb.Signature.Builder =
- this.setValueSignature(Semanticdb.ValueSignature.newBuilder().apply(block).build())
-
-inline fun Semanticdb.ClassSignature.copy(block: Semanticdb.ClassSignature.Builder.() -> Unit):
- Semanticdb.ClassSignature = this.toBuilder().apply(block).build()
-
-operator fun Semanticdb.ClassSignature.plus(other: Semanticdb.ClassSignature):
- Semanticdb.ClassSignature = this.toBuilder().mergeFrom(other).build()
-
-fun Semanticdb.ClassSignature?.orDefault(): Semanticdb.ClassSignature = this ?:
- Semanticdb.ClassSignature.getDefaultInstance()
-
-inline fun Semanticdb.ClassSignature.Builder.typeParameters(block: Semanticdb.Scope.Builder.() ->
- Unit): Semanticdb.ClassSignature.Builder =
- this.setTypeParameters(Semanticdb.Scope.newBuilder().apply(block).build())
-
-inline fun Semanticdb.ClassSignature.Builder.addParents(block: Semanticdb.Type.Builder.() -> Unit):
- Semanticdb.ClassSignature.Builder =
- this.addParents(Semanticdb.Type.newBuilder().apply(block).build())
-
-inline fun Semanticdb.ClassSignature.Builder.declarations(block: Semanticdb.Scope.Builder.() ->
- Unit): Semanticdb.ClassSignature.Builder =
- this.setDeclarations(Semanticdb.Scope.newBuilder().apply(block).build())
-
-inline fun Semanticdb.MethodSignature.copy(block: Semanticdb.MethodSignature.Builder.() -> Unit):
- Semanticdb.MethodSignature = this.toBuilder().apply(block).build()
-
-operator fun Semanticdb.MethodSignature.plus(other: Semanticdb.MethodSignature):
- Semanticdb.MethodSignature = this.toBuilder().mergeFrom(other).build()
-
-fun Semanticdb.MethodSignature?.orDefault(): Semanticdb.MethodSignature = this ?:
- Semanticdb.MethodSignature.getDefaultInstance()
-
-inline fun Semanticdb.MethodSignature.Builder.typeParameters(block: Semanticdb.Scope.Builder.() ->
- Unit): Semanticdb.MethodSignature.Builder =
- this.setTypeParameters(Semanticdb.Scope.newBuilder().apply(block).build())
-
-inline fun Semanticdb.MethodSignature.Builder.addParameterLists(block:
- Semanticdb.Scope.Builder.() -> Unit): Semanticdb.MethodSignature.Builder =
- this.addParameterLists(Semanticdb.Scope.newBuilder().apply(block).build())
-
-inline fun Semanticdb.MethodSignature.Builder.returnType(block: Semanticdb.Type.Builder.() -> Unit):
- Semanticdb.MethodSignature.Builder =
- this.setReturnType(Semanticdb.Type.newBuilder().apply(block).build())
-
-inline fun Semanticdb.TypeSignature.copy(block: Semanticdb.TypeSignature.Builder.() -> Unit):
- Semanticdb.TypeSignature = this.toBuilder().apply(block).build()
-
-operator fun Semanticdb.TypeSignature.plus(other: Semanticdb.TypeSignature):
- Semanticdb.TypeSignature = this.toBuilder().mergeFrom(other).build()
-
-fun Semanticdb.TypeSignature?.orDefault(): Semanticdb.TypeSignature = this ?:
- Semanticdb.TypeSignature.getDefaultInstance()
-
-inline fun Semanticdb.TypeSignature.Builder.typeParameters(block: Semanticdb.Scope.Builder.() ->
- Unit): Semanticdb.TypeSignature.Builder =
- this.setTypeParameters(Semanticdb.Scope.newBuilder().apply(block).build())
-
-inline fun Semanticdb.TypeSignature.Builder.lowerBound(block: Semanticdb.Type.Builder.() -> Unit):
- Semanticdb.TypeSignature.Builder =
- this.setLowerBound(Semanticdb.Type.newBuilder().apply(block).build())
-
-inline fun Semanticdb.TypeSignature.Builder.upperBound(block: Semanticdb.Type.Builder.() -> Unit):
- Semanticdb.TypeSignature.Builder =
- this.setUpperBound(Semanticdb.Type.newBuilder().apply(block).build())
-
-inline fun Semanticdb.ValueSignature.copy(block: Semanticdb.ValueSignature.Builder.() -> Unit):
- Semanticdb.ValueSignature = this.toBuilder().apply(block).build()
-
-operator fun Semanticdb.ValueSignature.plus(other: Semanticdb.ValueSignature):
- Semanticdb.ValueSignature = this.toBuilder().mergeFrom(other).build()
-
-fun Semanticdb.ValueSignature?.orDefault(): Semanticdb.ValueSignature = this ?:
- Semanticdb.ValueSignature.getDefaultInstance()
-
-inline fun Semanticdb.ValueSignature.Builder.tpe(block: Semanticdb.Type.Builder.() -> Unit):
- Semanticdb.ValueSignature.Builder =
- this.setTpe(Semanticdb.Type.newBuilder().apply(block).build())
-
-inline fun Semanticdb.SymbolInformation.copy(block: Semanticdb.SymbolInformation.Builder.() ->
- Unit): Semanticdb.SymbolInformation = this.toBuilder().apply(block).build()
-
-operator fun Semanticdb.SymbolInformation.plus(other: Semanticdb.SymbolInformation):
- Semanticdb.SymbolInformation = this.toBuilder().mergeFrom(other).build()
-
-fun Semanticdb.SymbolInformation?.orDefault(): Semanticdb.SymbolInformation = this ?:
- Semanticdb.SymbolInformation.getDefaultInstance()
-
-inline fun Semanticdb.SymbolInformation.Builder.signature(block: Semanticdb.Signature.Builder.() ->
- Unit): Semanticdb.SymbolInformation.Builder =
- this.setSignature(Semanticdb.Signature.newBuilder().apply(block).build())
-
-inline fun Semanticdb.SymbolInformation.Builder.access(block: Semanticdb.Access.Builder.() -> Unit):
- Semanticdb.SymbolInformation.Builder =
- this.setAccess(Semanticdb.Access.newBuilder().apply(block).build())
-
-inline fun Semanticdb.SymbolInformation.Builder.documentation(block:
- Semanticdb.Documentation.Builder.() -> Unit): Semanticdb.SymbolInformation.Builder =
- this.setDocumentation(Semanticdb.Documentation.newBuilder().apply(block).build())
-
-inline fun Semanticdb.Access.copy(block: Semanticdb.Access.Builder.() -> Unit): Semanticdb.Access =
- this.toBuilder().apply(block).build()
-
-operator fun Semanticdb.Access.plus(other: Semanticdb.Access): Semanticdb.Access =
- this.toBuilder().mergeFrom(other).build()
-
-fun Semanticdb.Access?.orDefault(): Semanticdb.Access = this ?:
- Semanticdb.Access.getDefaultInstance()
-
-inline fun Semanticdb.Access.Builder.privateAccess(block: Semanticdb.PrivateAccess.Builder.() ->
- Unit): Semanticdb.Access.Builder =
- this.setPrivateAccess(Semanticdb.PrivateAccess.newBuilder().apply(block).build())
-
-inline fun Semanticdb.Access.Builder.privateWithinAccess(block:
- Semanticdb.PrivateWithinAccess.Builder.() -> Unit): Semanticdb.Access.Builder =
- this.setPrivateWithinAccess(Semanticdb.PrivateWithinAccess.newBuilder().apply(block).build())
-
-inline fun Semanticdb.Access.Builder.protectedAccess(block: Semanticdb.ProtectedAccess.Builder.() ->
- Unit): Semanticdb.Access.Builder =
- this.setProtectedAccess(Semanticdb.ProtectedAccess.newBuilder().apply(block).build())
-
-inline fun Semanticdb.Access.Builder.publicAccess(block: Semanticdb.PublicAccess.Builder.() ->
- Unit): Semanticdb.Access.Builder =
- this.setPublicAccess(Semanticdb.PublicAccess.newBuilder().apply(block).build())
-
-inline fun Semanticdb.PrivateAccess.copy(block: Semanticdb.PrivateAccess.Builder.() -> Unit):
- Semanticdb.PrivateAccess = this.toBuilder().apply(block).build()
-
-operator fun Semanticdb.PrivateAccess.plus(other: Semanticdb.PrivateAccess):
- Semanticdb.PrivateAccess = this.toBuilder().mergeFrom(other).build()
-
-fun Semanticdb.PrivateAccess?.orDefault(): Semanticdb.PrivateAccess = this ?:
- Semanticdb.PrivateAccess.getDefaultInstance()
-
-inline fun Semanticdb.PrivateWithinAccess.copy(block: Semanticdb.PrivateWithinAccess.Builder.() ->
- Unit): Semanticdb.PrivateWithinAccess = this.toBuilder().apply(block).build()
-
-operator fun Semanticdb.PrivateWithinAccess.plus(other: Semanticdb.PrivateWithinAccess):
- Semanticdb.PrivateWithinAccess = this.toBuilder().mergeFrom(other).build()
-
-fun Semanticdb.PrivateWithinAccess?.orDefault(): Semanticdb.PrivateWithinAccess = this ?:
- Semanticdb.PrivateWithinAccess.getDefaultInstance()
-
-inline fun Semanticdb.ProtectedAccess.copy(block: Semanticdb.ProtectedAccess.Builder.() -> Unit):
- Semanticdb.ProtectedAccess = this.toBuilder().apply(block).build()
-
-operator fun Semanticdb.ProtectedAccess.plus(other: Semanticdb.ProtectedAccess):
- Semanticdb.ProtectedAccess = this.toBuilder().mergeFrom(other).build()
-
-fun Semanticdb.ProtectedAccess?.orDefault(): Semanticdb.ProtectedAccess = this ?:
- Semanticdb.ProtectedAccess.getDefaultInstance()
-
-inline fun Semanticdb.PublicAccess.copy(block: Semanticdb.PublicAccess.Builder.() -> Unit):
- Semanticdb.PublicAccess = this.toBuilder().apply(block).build()
-
-operator fun Semanticdb.PublicAccess.plus(other: Semanticdb.PublicAccess): Semanticdb.PublicAccess =
- this.toBuilder().mergeFrom(other).build()
-
-fun Semanticdb.PublicAccess?.orDefault(): Semanticdb.PublicAccess = this ?:
- Semanticdb.PublicAccess.getDefaultInstance()
-
-inline fun Semanticdb.Documentation.copy(block: Semanticdb.Documentation.Builder.() -> Unit):
- Semanticdb.Documentation = this.toBuilder().apply(block).build()
-
-operator fun Semanticdb.Documentation.plus(other: Semanticdb.Documentation):
- Semanticdb.Documentation = this.toBuilder().mergeFrom(other).build()
-
-fun Semanticdb.Documentation?.orDefault(): Semanticdb.Documentation = this ?:
- Semanticdb.Documentation.getDefaultInstance()
-
-inline fun Semanticdb.SymbolOccurrence.copy(block: Semanticdb.SymbolOccurrence.Builder.() -> Unit):
- Semanticdb.SymbolOccurrence = this.toBuilder().apply(block).build()
-
-operator fun Semanticdb.SymbolOccurrence.plus(other: Semanticdb.SymbolOccurrence):
- Semanticdb.SymbolOccurrence = this.toBuilder().mergeFrom(other).build()
-
-fun Semanticdb.SymbolOccurrence?.orDefault(): Semanticdb.SymbolOccurrence = this ?:
- Semanticdb.SymbolOccurrence.getDefaultInstance()
-
-inline fun Semanticdb.SymbolOccurrence.Builder.range(block: Semanticdb.Range.Builder.() -> Unit):
- Semanticdb.SymbolOccurrence.Builder =
- this.setRange(Semanticdb.Range.newBuilder().apply(block).build())
-
-inline fun Semanticdb.SymbolOccurrence.Builder.enclosingRange(block: Semanticdb.Range.Builder.() -> Unit):
- Semanticdb.SymbolOccurrence.Builder =
- this.setEnclosingRange(Semanticdb.Range.newBuilder().apply(block).build())
-
-inline fun Semanticdb.Scope.copy(block: Semanticdb.Scope.Builder.() -> Unit): Semanticdb.Scope =
- this.toBuilder().apply(block).build()
-
-operator fun Semanticdb.Scope.plus(other: Semanticdb.Scope): Semanticdb.Scope =
- this.toBuilder().mergeFrom(other).build()
-
-fun Semanticdb.Scope?.orDefault(): Semanticdb.Scope = this ?: Semanticdb.Scope.getDefaultInstance()
-
-inline fun Semanticdb.Scope.Builder.addHardlinks(block: Semanticdb.SymbolInformation.Builder.() ->
- Unit): Semanticdb.Scope.Builder =
- this.addHardlinks(Semanticdb.SymbolInformation.newBuilder().apply(block).build())
-
-inline fun Semanticdb.Type.copy(block: Semanticdb.Type.Builder.() -> Unit): Semanticdb.Type =
- this.toBuilder().apply(block).build()
-
-operator fun Semanticdb.Type.plus(other: Semanticdb.Type): Semanticdb.Type =
- this.toBuilder().mergeFrom(other).build()
-
-fun Semanticdb.Type?.orDefault(): Semanticdb.Type = this ?: Semanticdb.Type.getDefaultInstance()
-
-inline fun Semanticdb.Type.Builder.typeRef(block: Semanticdb.TypeRef.Builder.() -> Unit):
- Semanticdb.Type.Builder =
- this.setTypeRef(Semanticdb.TypeRef.newBuilder().apply(block).build())
-
-inline fun Semanticdb.Type.Builder.existentialType(block: Semanticdb.ExistentialType.Builder.() ->
- Unit): Semanticdb.Type.Builder =
- this.setExistentialType(Semanticdb.ExistentialType.newBuilder().apply(block).build())
-
-inline fun Semanticdb.Type.Builder.intersectionType(block: Semanticdb.IntersectionType.Builder.() ->
- Unit): Semanticdb.Type.Builder =
- this.setIntersectionType(Semanticdb.IntersectionType.newBuilder().apply(block).build())
-
-inline fun Semanticdb.TypeRef.copy(block: Semanticdb.TypeRef.Builder.() -> Unit): Semanticdb.TypeRef
- = this.toBuilder().apply(block).build()
-
-operator fun Semanticdb.TypeRef.plus(other: Semanticdb.TypeRef): Semanticdb.TypeRef =
- this.toBuilder().mergeFrom(other).build()
-
-fun Semanticdb.TypeRef?.orDefault(): Semanticdb.TypeRef = this ?:
- Semanticdb.TypeRef.getDefaultInstance()
-
-inline fun Semanticdb.TypeRef.Builder.addTypeArguments(block: Semanticdb.Type.Builder.() -> Unit):
- Semanticdb.TypeRef.Builder =
- this.addTypeArguments(Semanticdb.Type.newBuilder().apply(block).build())
-
-inline fun Semanticdb.IntersectionType.copy(block: Semanticdb.IntersectionType.Builder.() -> Unit):
- Semanticdb.IntersectionType = this.toBuilder().apply(block).build()
-
-operator fun Semanticdb.IntersectionType.plus(other: Semanticdb.IntersectionType):
- Semanticdb.IntersectionType = this.toBuilder().mergeFrom(other).build()
-
-fun Semanticdb.IntersectionType?.orDefault(): Semanticdb.IntersectionType = this ?:
- Semanticdb.IntersectionType.getDefaultInstance()
-
-inline fun Semanticdb.IntersectionType.Builder.addTypes(block: Semanticdb.Type.Builder.() -> Unit):
- Semanticdb.IntersectionType.Builder =
- this.addTypes(Semanticdb.Type.newBuilder().apply(block).build())
-
-inline fun Semanticdb.ExistentialType.copy(block: Semanticdb.ExistentialType.Builder.() -> Unit):
- Semanticdb.ExistentialType = this.toBuilder().apply(block).build()
-
-operator fun Semanticdb.ExistentialType.plus(other: Semanticdb.ExistentialType):
- Semanticdb.ExistentialType = this.toBuilder().mergeFrom(other).build()
-
-fun Semanticdb.ExistentialType?.orDefault(): Semanticdb.ExistentialType = this ?:
- Semanticdb.ExistentialType.getDefaultInstance()
-
-inline fun Semanticdb.ExistentialType.Builder.tpe(block: Semanticdb.Type.Builder.() -> Unit):
- Semanticdb.ExistentialType.Builder =
- this.setTpe(Semanticdb.Type.newBuilder().apply(block).build())
-
-inline fun Semanticdb.ExistentialType.Builder.declarations(block: Semanticdb.Scope.Builder.() ->
- Unit): Semanticdb.ExistentialType.Builder =
- this.setDeclarations(Semanticdb.Scope.newBuilder().apply(block).build())
-
-inline fun TextDocuments(block: Semanticdb.TextDocuments.Builder.() -> Unit):
- Semanticdb.TextDocuments = Semanticdb.TextDocuments.newBuilder().apply(block).build()
-
-inline fun TextDocument(block: Semanticdb.TextDocument.Builder.() -> Unit): Semanticdb.TextDocument
- = Semanticdb.TextDocument.newBuilder().apply(block).build()
-
-inline fun Range(block: Semanticdb.Range.Builder.() -> Unit): Semanticdb.Range =
- Semanticdb.Range.newBuilder().apply(block).build()
-
-inline fun Signature(block: Semanticdb.Signature.Builder.() -> Unit): Semanticdb.Signature =
- Semanticdb.Signature.newBuilder().apply(block).build()
-
-inline fun ClassSignature(block: Semanticdb.ClassSignature.Builder.() -> Unit):
- Semanticdb.ClassSignature = Semanticdb.ClassSignature.newBuilder().apply(block).build()
-
-inline fun MethodSignature(block: Semanticdb.MethodSignature.Builder.() -> Unit):
- Semanticdb.MethodSignature = Semanticdb.MethodSignature.newBuilder().apply(block).build()
-
-inline fun TypeSignature(block: Semanticdb.TypeSignature.Builder.() -> Unit):
- Semanticdb.TypeSignature = Semanticdb.TypeSignature.newBuilder().apply(block).build()
-
-inline fun ValueSignature(block: Semanticdb.ValueSignature.Builder.() -> Unit):
- Semanticdb.ValueSignature = Semanticdb.ValueSignature.newBuilder().apply(block).build()
-
-inline fun SymbolInformation(block: Semanticdb.SymbolInformation.Builder.() -> Unit):
- Semanticdb.SymbolInformation =
- Semanticdb.SymbolInformation.newBuilder().apply(block).build()
-
-inline fun Access(block: Semanticdb.Access.Builder.() -> Unit): Semanticdb.Access =
- Semanticdb.Access.newBuilder().apply(block).build()
-
-inline fun PrivateAccess(block: Semanticdb.PrivateAccess.Builder.() -> Unit):
- Semanticdb.PrivateAccess = Semanticdb.PrivateAccess.newBuilder().apply(block).build()
-
-inline fun PrivateWithinAccess(block: Semanticdb.PrivateWithinAccess.Builder.() -> Unit):
- Semanticdb.PrivateWithinAccess =
- Semanticdb.PrivateWithinAccess.newBuilder().apply(block).build()
-
-inline fun ProtectedAccess(block: Semanticdb.ProtectedAccess.Builder.() -> Unit):
- Semanticdb.ProtectedAccess = Semanticdb.ProtectedAccess.newBuilder().apply(block).build()
-
-inline fun PublicAccess(block: Semanticdb.PublicAccess.Builder.() -> Unit): Semanticdb.PublicAccess
- = Semanticdb.PublicAccess.newBuilder().apply(block).build()
-
-inline fun Documentation(block: Semanticdb.Documentation.Builder.() -> Unit):
- Semanticdb.Documentation = Semanticdb.Documentation.newBuilder().apply(block).build()
-
-inline fun SymbolOccurrence(block: Semanticdb.SymbolOccurrence.Builder.() -> Unit):
- Semanticdb.SymbolOccurrence = Semanticdb.SymbolOccurrence.newBuilder().apply(block).build()
-
-inline fun Scope(block: Semanticdb.Scope.Builder.() -> Unit): Semanticdb.Scope =
- Semanticdb.Scope.newBuilder().apply(block).build()
-
-inline fun Type(block: Semanticdb.Type.Builder.() -> Unit): Semanticdb.Type =
- Semanticdb.Type.newBuilder().apply(block).build()
-
-inline fun TypeRef(block: Semanticdb.TypeRef.Builder.() -> Unit): Semanticdb.TypeRef =
- Semanticdb.TypeRef.newBuilder().apply(block).build()
-
-inline fun IntersectionType(block: Semanticdb.IntersectionType.Builder.() -> Unit):
- Semanticdb.IntersectionType = Semanticdb.IntersectionType.newBuilder().apply(block).build()
-
-inline fun ExistentialType(block: Semanticdb.ExistentialType.Builder.() -> Unit):
- Semanticdb.ExistentialType = Semanticdb.ExistentialType.newBuilder().apply(block).build()
-
-@DslMarker
-@Target(AnnotationTarget.CLASS)
-@Retention(AnnotationRetention.BINARY)
-annotation class SemanticdbDslMarker
-
-@SemanticdbDslMarker
-interface SemanticdbDslBuilder
diff --git a/semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/AnalyzerCommandLineProcessor.kt b/semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/AnalyzerCommandLineProcessor.kt
index 4c67ac789..291dd6ec7 100644
--- a/semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/AnalyzerCommandLineProcessor.kt
+++ b/semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/AnalyzerCommandLineProcessor.kt
@@ -28,7 +28,7 @@ class AnalyzerCommandLineProcessor : CommandLineProcessor {
CliOption(
VAL_TARGET,
"",
- "the absolute path to the directory where to generate SemanticDB files.",
+ "the absolute path to the directory where to generate SCIP shard files.",
required = true))
override fun processOption(
diff --git a/semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/AnalyzerRegistrar.kt b/semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/AnalyzerRegistrar.kt
index 410dee517..2d65d45de 100644
--- a/semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/AnalyzerRegistrar.kt
+++ b/semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/AnalyzerRegistrar.kt
@@ -9,8 +9,7 @@ import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrarAdapter
@OptIn(ExperimentalCompilerApi::class)
@ExperimentalContracts
-class AnalyzerRegistrar(private val callback: (Semanticdb.TextDocument) -> Unit = {}) :
- CompilerPluginRegistrar() {
+class AnalyzerRegistrar : CompilerPluginRegistrar() {
override fun ExtensionStorage.registerExtensions(configuration: CompilerConfiguration) {
FirExtensionRegistrarAdapter.registerExtension(
AnalyzerFirExtensionRegistrar(sourceroot = configuration[KEY_SOURCES]!!)
@@ -18,8 +17,7 @@ class AnalyzerRegistrar(private val callback: (Semanticdb.TextDocument) -> Unit
IrGenerationExtension.registerExtension(
PostAnalysisExtension(
sourceRoot = configuration[KEY_SOURCES]!!,
- targetRoot = configuration[KEY_TARGET]!!,
- callback = callback))
+ targetRoot = configuration[KEY_TARGET]!!))
}
override val supportsK2: Boolean
diff --git a/semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/PostAnalysisExtension.kt b/semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/PostAnalysisExtension.kt
index 07ccfa1ce..18355dced 100644
--- a/semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/PostAnalysisExtension.kt
+++ b/semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/PostAnalysisExtension.kt
@@ -16,24 +16,20 @@ import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
+/** Writes the per-source SCIP shards collected by [SemanticdbVisitor] after FIR analysis. */
class PostAnalysisExtension(
private val sourceRoot: Path,
private val targetRoot: Path,
- private val callback: (Semanticdb.TextDocument) -> Unit
) : IrGenerationExtension {
@OptIn(ExperimentalContracts::class)
override fun generate(moduleFragment: IrModuleFragment, pluginContext: IrPluginContext) {
try {
for ((ktSourceFile, visitor) in AnalyzerCheckers.visitors) {
try {
- val document = visitor.build()
- semanticdbOutPathForFile(ktSourceFile)?.apply {
- Files.write(this, TextDocuments { addDocuments(document) }.toByteArray())
+ scipShardOutPathForFile(ktSourceFile)?.let { outPath ->
+ Files.createDirectories(outPath.parent)
+ Files.write(outPath, visitor.buildScipIndex().toByteArray())
}
- scipShardOutPathForFile(ktSourceFile)?.apply {
- ScipShardWriter.write(this, visitor.buildScipIndex())
- }
- callback(document)
} catch (e: Exception) {
handleException(e)
}
@@ -43,32 +39,19 @@ class PostAnalysisExtension(
}
}
- private fun semanticdbOutPathForFile(file: KtSourceFile): Path? =
- outPathForFile(file, subdir = "semanticdb", suffix = ".semanticdb")
-
- private fun scipShardOutPathForFile(file: KtSourceFile): Path? =
- outPathForFile(file, subdir = "scip", suffix = ".scip")
-
- private fun outPathForFile(file: KtSourceFile, subdir: String, suffix: String): Path? {
+ private fun scipShardOutPathForFile(file: KtSourceFile): Path? {
val normalizedPath = Paths.get(file.path).normalize()
if (normalizedPath.startsWith(sourceRoot)) {
val relative = sourceRoot.relativize(normalizedPath)
- val filename = relative.fileName.toString() + suffix
- val outPath =
- targetRoot
- .resolve("META-INF")
- .resolve(subdir)
- .resolve(relative)
- .resolveSibling(filename)
-
- Files.createDirectories(outPath.parent)
- return outPath
- }
- // Warn once per file; attach to the SemanticDB path to avoid double warnings.
- if (subdir == "semanticdb") {
- System.err.println(
- "given file is not under the sourceroot.\n\tSourceroot: $sourceRoot\n\tFile path: ${file.path}\n\tNormalized file path: $normalizedPath")
+ val filename = relative.fileName.toString() + ".scip"
+ return targetRoot
+ .resolve("META-INF")
+ .resolve("scip")
+ .resolve(relative)
+ .resolveSibling(filename)
}
+ System.err.println(
+ "given file is not under the sourceroot.\n\tSourceroot: $sourceRoot\n\tFile path: ${file.path}\n\tNormalized file path: $normalizedPath")
return null
}
@@ -95,7 +78,7 @@ class PostAnalysisExtension(
writer.println("Exception in semanticdb-kotlin compiler plugin:")
e.printStackTrace(writer)
writer.println(
- "Please report a bug to https://github.com/sourcegraph/scip-kotlin with the stack trace above.")
+ "Please report a bug to https://github.com/sourcegraph/scip-java with the stack trace above.")
writer.close()
}
}
diff --git a/semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/ScipShardWriter.kt b/semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/ScipShardWriter.kt
deleted file mode 100644
index b2611c571..000000000
--- a/semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/ScipShardWriter.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-package com.sourcegraph.semanticdb_kotlinc
-
-import org.scip_code.scip.Document
-import org.scip_code.scip.Index
-import java.nio.file.Files
-import java.nio.file.Path
-
-/**
- * Writes per-source SCIP shards from the kotlinc plug-in. Kotlin compiles each source once per
- * round and stale shards are removed via [delete], so no merge step is needed.
- */
-internal object ScipShardWriter {
-
- fun write(output: Path, shard: Index) {
- Files.createDirectories(output.parent)
- Files.write(output, shard.toByteArray())
- }
-
- fun delete(output: Path) {
- Files.deleteIfExists(output)
- }
-}
diff --git a/semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/ScipSymbols.kt b/semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/ScipSymbols.kt
index c8685acae..49b7de0f0 100644
--- a/semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/ScipSymbols.kt
+++ b/semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/ScipSymbols.kt
@@ -10,13 +10,12 @@ object ScipSymbols {
const val PLACEHOLDER_PREFIX: String = ". . . . "
- fun fromSemanticdbSymbol(symbol: String?): String {
- if (symbol.isNullOrEmpty()) return ""
- if (symbol.startsWith("local")) {
- return "local " + symbol.substring("local".length)
+ fun fromSemanticdbSymbol(symbol: Symbol): String {
+ if (symbol == Symbol.NONE) return ""
+ val raw = symbol.toString()
+ if (symbol.isLocal()) {
+ return "local " + raw.substring("local".length)
}
- return PLACEHOLDER_PREFIX + symbol
+ return PLACEHOLDER_PREFIX + raw
}
-
- fun fromSemanticdbSymbol(symbol: Symbol): String = fromSemanticdbSymbol(symbol.toString())
}
diff --git a/semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/ScipTextDocumentBuilder.kt b/semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/ScipTextDocumentBuilder.kt
index 18fd3e8fa..fc5fa6ac3 100644
--- a/semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/ScipTextDocumentBuilder.kt
+++ b/semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/ScipTextDocumentBuilder.kt
@@ -7,10 +7,9 @@ import org.scip_code.scip.Relationship
import org.scip_code.scip.Signature
import org.scip_code.scip.SymbolInformation
import org.scip_code.scip.SymbolRole
-import com.sourcegraph.semanticdb_kotlinc.Semanticdb.SymbolOccurrence.Role
import kotlin.contracts.ExperimentalContracts
import org.jetbrains.kotlin.KtSourceElement
-import org.jetbrains.kotlin.KtSourceFile
+import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.directOverriddenSymbolsSafe
import org.jetbrains.kotlin.fir.analysis.checkers.toClassLikeSymbol
@@ -30,7 +29,6 @@ import org.jetbrains.kotlin.text
*/
@ExperimentalContracts
class ScipTextDocumentBuilder(
- private val file: KtSourceFile,
private val lineMap: LineMap,
private val cache: SymbolsCache,
private val relativePath: String,
@@ -39,29 +37,29 @@ class ScipTextDocumentBuilder(
// Keyed by symbol so re-encounters (multi-round compilation, synthetic accessors) don't dup.
private val symbols = LinkedHashMap()
- fun build(): Document =
- Document
- .newBuilder()
- .setRelativePath(relativePath)
- .setLanguage(LANGUAGE_KOTLIN)
- .addAllOccurrences(occurrences.values())
- .addAllSymbols(symbols.values)
+ fun buildIndex(): Index =
+ Index.newBuilder()
+ .addDocuments(
+ Document.newBuilder()
+ .setRelativePath(relativePath)
+ .setLanguage(LANGUAGE_KOTLIN)
+ .addAllOccurrences(occurrences.values())
+ .addAllSymbols(symbols.values)
+ .build())
.build()
- fun buildIndex(): Index = Index.newBuilder().addDocuments(build()).build()
-
fun emitScipData(
firBasedSymbol: FirBasedSymbol<*>?,
symbol: Symbol,
element: KtSourceElement,
- role: Role,
+ roles: Int,
context: CheckerContext,
enclosingSource: KtSourceElement? = null,
) {
if (symbol == Symbol.NONE) return
- emitOccurrence(symbol, element, role, enclosingSource)
- if (role == Role.DEFINITION) {
+ emitOccurrence(symbol, element, roles, enclosingSource)
+ if (roles == SymbolRole.Definition_VALUE) {
emitSymbolInformation(firBasedSymbol, symbol, element, context)
}
}
@@ -69,7 +67,7 @@ class ScipTextDocumentBuilder(
private fun emitOccurrence(
symbol: Symbol,
element: KtSourceElement,
- role: Role,
+ roles: Int,
enclosingSource: KtSourceElement?,
) {
val builder =
@@ -77,7 +75,7 @@ class ScipTextDocumentBuilder(
.newBuilder()
.addAllRange(scipRange(element))
.setSymbol(ScipSymbols.fromSemanticdbSymbol(symbol))
- .setSymbolRoles(scipRole(role))
+ .setSymbolRoles(roles)
if (enclosingSource != null) {
builder.addAllEnclosingRange(scipEnclosingRange(enclosingSource))
}
@@ -244,27 +242,16 @@ class ScipTextDocumentBuilder(
else -> firBasedSymbol.toString()
}
- private fun scipRole(role: Role): Int =
- when (role) {
- Role.DEFINITION -> SymbolRole.Definition_VALUE
- else -> 0
- }
-
private fun scipKind(firBasedSymbol: FirBasedSymbol<*>?): SymbolInformation.Kind =
when (firBasedSymbol) {
null -> SymbolInformation.Kind.UnspecifiedKind
is FirRegularClassSymbol ->
when (firBasedSymbol.classKind) {
- org.jetbrains.kotlin.descriptors.ClassKind.INTERFACE ->
- SymbolInformation.Kind.Interface
- org.jetbrains.kotlin.descriptors.ClassKind.ENUM_CLASS ->
- SymbolInformation.Kind.Enum
- org.jetbrains.kotlin.descriptors.ClassKind.ENUM_ENTRY ->
- SymbolInformation.Kind.EnumMember
- org.jetbrains.kotlin.descriptors.ClassKind.OBJECT ->
- SymbolInformation.Kind.Object
- org.jetbrains.kotlin.descriptors.ClassKind.ANNOTATION_CLASS ->
- SymbolInformation.Kind.Interface
+ ClassKind.INTERFACE -> SymbolInformation.Kind.Interface
+ ClassKind.ENUM_CLASS -> SymbolInformation.Kind.Enum
+ ClassKind.ENUM_ENTRY -> SymbolInformation.Kind.EnumMember
+ ClassKind.OBJECT -> SymbolInformation.Kind.Object
+ ClassKind.ANNOTATION_CLASS -> SymbolInformation.Kind.Interface
else -> SymbolInformation.Kind.Class
}
is FirAnonymousObjectSymbol -> SymbolInformation.Kind.Object
diff --git a/semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/SemanticdbSymbols.kt b/semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/SemanticdbSymbols.kt
index 3e60befcf..33137285f 100644
--- a/semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/SemanticdbSymbols.kt
+++ b/semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/SemanticdbSymbols.kt
@@ -23,8 +23,6 @@ value class Symbol(private val symbol: String) {
override fun toString(): String = symbol
}
-fun String.symbol(): Symbol = Symbol(this)
-
data class SemanticdbSymbolDescriptor(
val kind: Kind,
val name: String,
diff --git a/semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/SemanticdbTextDocumentBuilder.kt b/semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/SemanticdbTextDocumentBuilder.kt
deleted file mode 100644
index d37d5bee1..000000000
--- a/semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/SemanticdbTextDocumentBuilder.kt
+++ /dev/null
@@ -1,227 +0,0 @@
-package com.sourcegraph.semanticdb_kotlinc
-
-import com.sourcegraph.semanticdb_kotlinc.Semanticdb.SymbolOccurrence.Role
-import java.nio.file.Path
-import java.nio.file.Paths
-import java.security.MessageDigest
-import kotlin.contracts.ExperimentalContracts
-import org.jetbrains.kotlin.KtSourceElement
-import org.jetbrains.kotlin.KtSourceFile
-import org.jetbrains.kotlin.com.intellij.lang.java.JavaLanguage
-import org.jetbrains.kotlin.fir.FirElement
-import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
-import org.jetbrains.kotlin.fir.analysis.checkers.directOverriddenSymbolsSafe
-import org.jetbrains.kotlin.fir.analysis.checkers.toClassLikeSymbol
-import org.jetbrains.kotlin.fir.analysis.getChild
-import org.jetbrains.kotlin.fir.renderer.*
-import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
-import org.jetbrains.kotlin.fir.symbols.SymbolInternals
-import org.jetbrains.kotlin.fir.symbols.impl.*
-import org.jetbrains.kotlin.fir.types.impl.FirImplicitAnyTypeRef
-import org.jetbrains.kotlin.idea.KotlinLanguage
-import org.jetbrains.kotlin.lexer.KtTokens
-import org.jetbrains.kotlin.psi
-import org.jetbrains.kotlin.text
-
-@ExperimentalContracts
-class SemanticdbTextDocumentBuilder(
- private val sourceroot: Path,
- private val file: KtSourceFile,
- private val lineMap: LineMap,
- private val cache: SymbolsCache,
-) {
- private val occurrences = mutableListOf()
- private val symbols = mutableListOf()
- private val fileText = file.getContentsAsStream().reader().readText()
- private val semanticMd5 = semanticdbMD5()
-
- fun build() = TextDocument {
- this.text = fileText
- this.uri = semanticdbURI()
- this.md5 = semanticMd5
- this.schema = Semanticdb.Schema.SEMANTICDB4
- this.language = Semanticdb.Language.KOTLIN
- occurrences.sortWith(compareBy({ it.range.startLine }, { it.range.startCharacter }))
- this.addAllOccurrences(occurrences)
- this.addAllSymbols(symbols)
- }
-
- fun emitSemanticdbData(
- firBasedSymbol: FirBasedSymbol<*>?,
- symbol: Symbol,
- element: KtSourceElement,
- role: Role,
- context: CheckerContext,
- enclosingSource: KtSourceElement? = null,
- ) {
- symbolOccurrence(symbol, element, role, enclosingSource).let {
- if (!occurrences.contains(it)) {
- occurrences.add(it)
- }
- }
- val symbolInformation = symbolInformation(firBasedSymbol, symbol, element, context)
- if (role == Role.DEFINITION && !symbols.contains(symbolInformation))
- symbols.add(symbolInformation)
- }
-
- @OptIn(SymbolInternals::class)
- private fun symbolInformation(
- firBasedSymbol: FirBasedSymbol<*>?,
- symbol: Symbol,
- element: KtSourceElement,
- context: CheckerContext,
- ): Semanticdb.SymbolInformation {
- val supers =
- when (firBasedSymbol) {
- is FirClassSymbol ->
- firBasedSymbol
- .resolvedSuperTypeRefs
- .filter { it !is FirImplicitAnyTypeRef }
- .map { it.toClassLikeSymbol(firBasedSymbol.moduleData.session) }
- .filterNotNull()
- .flatMap { cache[it] }
- is FirFunctionSymbol<*> ->
- firBasedSymbol.directOverriddenSymbolsSafe(context).flatMap { cache[it] }
- else -> emptyList().asIterable()
- }
- return SymbolInformation {
- this.symbol = symbol.toString()
- this.displayName =
- if (firBasedSymbol != null) {
- displayName(firBasedSymbol)
- } else {
- element.text.toString()
- }
- this.documentation =
- if (firBasedSymbol != null) {
- semanticdbDocumentation(firBasedSymbol.fir)
- } else {
- Documentation {
- format = Semanticdb.Documentation.Format.MARKDOWN
- message = ""
- }
- }
- this.addAllOverriddenSymbols(supers.map { it.toString() })
- this.language =
- when (element.psi?.language ?: KotlinLanguage.INSTANCE) {
- is KotlinLanguage -> Semanticdb.Language.KOTLIN
- is JavaLanguage -> Semanticdb.Language.JAVA
- else -> throw IllegalArgumentException("unexpected language")
- }
- }
- }
-
- private fun symbolOccurrence(
- symbol: Symbol,
- element: KtSourceElement,
- role: Role,
- enclosingSource: KtSourceElement? = null,
- ): Semanticdb.SymbolOccurrence {
- return SymbolOccurrence {
- this.symbol = symbol.toString()
- this.role = role
- this.range = semanticdbRange(element)
- if (enclosingSource != null) {
- this.enclosingRange = semanticdbEnclosingRange(enclosingSource)
- }
- }
- }
-
- private fun semanticdbRange(element: KtSourceElement): Semanticdb.Range {
- return Range {
- startCharacter = lineMap.startCharacter(element)
- startLine = lineMap.lineNumber(element) - 1
- endCharacter = lineMap.endCharacter(element)
- endLine = lineMap.lineNumber(element) - 1
- }
- }
-
- private fun semanticdbEnclosingRange(element: KtSourceElement): Semanticdb.Range {
- return Range {
- startLine = lineMap.lineNumber(element) - 1
- startCharacter = lineMap.startCharacter(element)
- endLine = lineMap.lineNumberForOffset(element.endOffset) - 1
- endCharacter = lineMap.columnForOffset(element.endOffset)
- }
- }
-
- private fun semanticdbURI(): String {
- // TODO: unix-style only
- val relative = sourceroot.relativize(Paths.get(file.path))
- return relative.toString()
- }
-
- private fun semanticdbMD5(): String =
- MessageDigest.getInstance("MD5")
- .digest(file.getContentsAsStream().readBytes())
- .joinToString("") { "%02X".format(it) }
-
- private fun semanticdbDocumentation(element: FirElement): Semanticdb.Documentation = Documentation {
- format = Semanticdb.Documentation.Format.MARKDOWN
- // Like FirRenderer().forReadability, but using FirAllModifierRenderer instead of FirPartialModifierRenderer
- val renderer = FirRenderer(
- typeRenderer = ConeTypeRenderer(),
- idRenderer = ConeIdShortRenderer(),
- classMemberRenderer = FirNoClassMemberRenderer(),
- bodyRenderer = null,
- propertyAccessorRenderer = null,
- callArgumentsRenderer = FirCallNoArgumentsRenderer(),
- modifierRenderer = FirAllModifierRenderer(),
- callableSignatureRenderer = FirCallableSignatureRendererForReadability(),
- declarationRenderer = FirDeclarationRenderer("local "),
- )
- val renderOutput = renderer.renderElementAsString(element)
- val kdoc = element.source?.getChild(KtTokens.DOC_COMMENT)?.text?.toString() ?: ""
- message = "```kotlin\n$renderOutput\n```${stripKDocAsterisks(kdoc)}"
- }
-
- // Returns the kdoc string with all leading and trailing "/*" tokens removed. Naive
- // implementation that can
- // be replaced with a utility method from the compiler in the future, if one exists.
- private fun stripKDocAsterisks(kdoc: String): String {
- if (kdoc.isEmpty()) return kdoc
- val out = StringBuilder().append("\n\n").append("----").append("\n")
- kdoc.lineSequence().forEach { line ->
- if (line.isEmpty()) return@forEach
- var start = 0
- while (start < line.length && line[start].isWhitespace()) {
- start++
- }
- if (start < line.length && line[start] == '/') {
- start++
- }
- while (start < line.length && line[start] == '*') {
- start++
- }
- var end = line.length - 1
- if (end > start && line[end] == '/') {
- end--
- }
- while (end > start && line[end] == '*') {
- end--
- }
- while (end > start && line[end].isWhitespace()) {
- end--
- }
- start = minOf(start, line.length - 1)
- if (end > start) {
- end++
- }
- out.append("\n").append(line, start, end)
- }
- return out.toString()
- }
-
- companion object {
- @OptIn(SymbolInternals::class)
- private fun displayName(firBasedSymbol: FirBasedSymbol<*>): String =
- when (firBasedSymbol) {
- is FirClassSymbol -> firBasedSymbol.classId.shortClassName.asString()
- is FirPropertyAccessorSymbol -> firBasedSymbol.fir.propertySymbol.name.asString()
- is FirFunctionSymbol -> firBasedSymbol.callableId.callableName.asString()
- is FirPropertySymbol -> firBasedSymbol.callableId.callableName.asString()
- is FirVariableSymbol -> firBasedSymbol.name.asString()
- else -> firBasedSymbol.toString()
- }
- }
-}
diff --git a/semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/SemanticdbVisitor.kt b/semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/SemanticdbVisitor.kt
index bdbbb4ba8..0f3f4e9c1 100644
--- a/semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/SemanticdbVisitor.kt
+++ b/semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/SemanticdbVisitor.kt
@@ -1,7 +1,7 @@
package com.sourcegraph.semanticdb_kotlinc
import org.scip_code.scip.Index
-import com.sourcegraph.semanticdb_kotlinc.Semanticdb.SymbolOccurrence.Role
+import org.scip_code.scip.SymbolRole
import java.nio.file.Path
import java.nio.file.Paths
import kotlin.contracts.ExperimentalContracts
@@ -15,100 +15,92 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.name.FqName
+/**
+ * Walks the FIR analysis tree of a Kotlin source and builds a self-contained `Index` shard via
+ * [ScipTextDocumentBuilder].
+ */
@ExperimentalContracts
class SemanticdbVisitor(
sourceroot: Path,
file: KtSourceFile,
lineMap: LineMap,
globals: GlobalSymbolsCache,
- locals: LocalSymbolsCache = LocalSymbolsCache()
) {
- private val cache = SymbolsCache(globals, locals)
+ private val cache = SymbolsCache(globals, LocalSymbolsCache())
private val relativePath: String = computeRelativePath(sourceroot, file)
- private val documentBuilder = SemanticdbTextDocumentBuilder(sourceroot, file, lineMap, cache)
- private val scipBuilder = ScipTextDocumentBuilder(file, lineMap, cache, relativePath)
+ private val scipBuilder = ScipTextDocumentBuilder(lineMap, cache, relativePath)
private data class SymbolDescriptorPair(
val firBasedSymbol: FirBasedSymbol<*>?,
val symbol: Symbol
)
- fun build(): Semanticdb.TextDocument {
- return documentBuilder.build()
- }
-
fun buildScipIndex(): Index = scipBuilder.buildIndex()
- fun scipRelativePath(): String = relativePath
-
private fun Sequence?.emitAll(
element: KtSourceElement,
- role: Role,
+ roles: Int,
context: CheckerContext,
enclosingSource: KtSourceElement? = null,
- ): List? =
- this?.onEach { (firBasedSymbol, symbol) ->
- documentBuilder.emitSemanticdbData(firBasedSymbol, symbol, element, role, context, enclosingSource)
- scipBuilder.emitScipData(firBasedSymbol, symbol, element, role, context, enclosingSource)
- }
- ?.map { it.symbol }
- ?.toList()
+ ) {
+ this?.forEach { (firBasedSymbol, symbol) ->
+ scipBuilder.emitScipData(firBasedSymbol, symbol, element, roles, context, enclosingSource)
+ }
+ }
private fun Sequence.with(firBasedSymbol: FirBasedSymbol<*>?) =
this.map { SymbolDescriptorPair(firBasedSymbol, it) }
fun visitPackage(pkg: FqName, element: KtSourceElement, context: CheckerContext) {
- cache[pkg].with(null).emitAll(element, Role.REFERENCE, context)
+ cache[pkg].with(null).emitAll(element, 0, context)
}
fun visitClassReference(firClassSymbol: FirClassLikeSymbol<*>, element: KtSourceElement, context: CheckerContext) {
- cache[firClassSymbol].with(firClassSymbol).emitAll(element, Role.REFERENCE, context)
+ cache[firClassSymbol].with(firClassSymbol).emitAll(element, 0, context)
}
fun visitCallableReference(firClassSymbol: FirCallableSymbol<*>, element: KtSourceElement, context: CheckerContext) {
- cache[firClassSymbol].with(firClassSymbol).emitAll(element, Role.REFERENCE, context)
+ cache[firClassSymbol].with(firClassSymbol).emitAll(element, 0, context)
}
fun visitClassOrObject(firClass: FirClassLikeDeclaration, element: KtSourceElement, context: CheckerContext, enclosingSource: KtSourceElement? = null) {
- cache[firClass.symbol].with(firClass.symbol).emitAll(element, Role.DEFINITION, context, enclosingSource)
+ cache[firClass.symbol].with(firClass.symbol).emitAll(element, SymbolRole.Definition_VALUE, context, enclosingSource)
}
fun visitPrimaryConstructor(firConstructor: FirConstructor, source: KtSourceElement, context: CheckerContext, enclosingSource: KtSourceElement? = null) {
- // if the constructor is not denoted by the 'constructor' keyword, we want to link it to the
- // class ident
- cache[firConstructor.symbol].with(firConstructor.symbol).emitAll(source, Role.DEFINITION, context, enclosingSource)
+ cache[firConstructor.symbol].with(firConstructor.symbol).emitAll(source, SymbolRole.Definition_VALUE, context, enclosingSource)
}
fun visitSecondaryConstructor(firConstructor: FirConstructor, source: KtSourceElement, context: CheckerContext, enclosingSource: KtSourceElement? = null) {
- cache[firConstructor.symbol].with(firConstructor.symbol).emitAll(source, Role.DEFINITION, context, enclosingSource)
+ cache[firConstructor.symbol].with(firConstructor.symbol).emitAll(source, SymbolRole.Definition_VALUE, context, enclosingSource)
}
fun visitNamedFunction(firFunction: FirFunction, source: KtSourceElement, context: CheckerContext, enclosingSource: KtSourceElement? = null) {
- cache[firFunction.symbol].with(firFunction.symbol).emitAll(source, Role.DEFINITION, context, enclosingSource)
+ cache[firFunction.symbol].with(firFunction.symbol).emitAll(source, SymbolRole.Definition_VALUE, context, enclosingSource)
}
fun visitProperty(firProperty: FirProperty, source: KtSourceElement, context: CheckerContext, enclosingSource: KtSourceElement? = null) {
- cache[firProperty.symbol].with(firProperty.symbol).emitAll(source, Role.DEFINITION, context, enclosingSource)
+ cache[firProperty.symbol].with(firProperty.symbol).emitAll(source, SymbolRole.Definition_VALUE, context, enclosingSource)
}
fun visitParameter(firParameter: FirValueParameter, source: KtSourceElement, context: CheckerContext, enclosingSource: KtSourceElement? = null) {
- cache[firParameter.symbol].with(firParameter.symbol).emitAll(source, Role.DEFINITION, context, enclosingSource)
+ cache[firParameter.symbol].with(firParameter.symbol).emitAll(source, SymbolRole.Definition_VALUE, context, enclosingSource)
}
fun visitTypeParameter(firTypeParameter: FirTypeParameter, source: KtSourceElement, context: CheckerContext, enclosingSource: KtSourceElement? = null) {
cache[firTypeParameter.symbol]
.with(firTypeParameter.symbol)
- .emitAll(source, Role.DEFINITION, context, enclosingSource)
+ .emitAll(source, SymbolRole.Definition_VALUE, context, enclosingSource)
}
fun visitTypeAlias(firTypeAlias: FirTypeAlias, source: KtSourceElement, context: CheckerContext, enclosingSource: KtSourceElement? = null) {
- cache[firTypeAlias.symbol].with(firTypeAlias.symbol).emitAll(source, Role.DEFINITION, context, enclosingSource)
+ cache[firTypeAlias.symbol].with(firTypeAlias.symbol).emitAll(source, SymbolRole.Definition_VALUE, context, enclosingSource)
}
fun visitPropertyAccessor(firPropertyAccessor: FirPropertyAccessor, source: KtSourceElement, context: CheckerContext, enclosingSource: KtSourceElement? = null) {
cache[firPropertyAccessor.symbol]
.with(firPropertyAccessor.symbol)
- .emitAll(source, Role.DEFINITION, context, enclosingSource)
+ .emitAll(source, SymbolRole.Definition_VALUE, context, enclosingSource)
}
fun visitSimpleNameExpression(
@@ -117,7 +109,7 @@ class SemanticdbVisitor(
) {
cache[firResolvedNamedReference.resolvedSymbol]
.with(firResolvedNamedReference.resolvedSymbol)
- .emitAll(source, Role.REFERENCE, context)
+ .emitAll(source, 0, context)
}
}
@@ -133,4 +125,3 @@ private fun computeRelativePath(sourceroot: Path, file: KtSourceFile): String {
normalized.toString().replace('\\', '/')
}
}
-
diff --git a/semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/SymbolsCache.kt b/semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/SymbolsCache.kt
index 0c02279fb..11c95bf6e 100644
--- a/semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/SymbolsCache.kt
+++ b/semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/SymbolsCache.kt
@@ -20,12 +20,9 @@ import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeAsciiOnly
@ExperimentalContracts
-class GlobalSymbolsCache(testing: Boolean = false) : Iterable {
- private val globals =
- if (testing) LinkedHashMap, Symbol>()
- else HashMap, Symbol>()
- private val packages =
- if (testing) LinkedHashMap() else HashMap()
+class GlobalSymbolsCache {
+ private val globals = HashMap, Symbol>()
+ private val packages = HashMap()
operator fun get(symbol: FirBasedSymbol<*>, locals: LocalSymbolsCache): Sequence =
sequence {
@@ -206,20 +203,12 @@ class GlobalSymbolsCache(testing: Boolean = false) : Iterable {
if (count == 0 || !found) return "()"
return "(+${count})"
}
-
- override fun iterator(): Iterator = globals.values.iterator()
}
-class LocalSymbolsCache : Iterable {
+class LocalSymbolsCache {
private val symbols = HashMap, Symbol>()
private var localsCounter = 0
- val iterator: Iterable, Symbol>>
- get() = symbols.asIterable()
-
- val size: Int
- get() = symbols.size
-
operator fun get(symbol: FirBasedSymbol<*>): Symbol? = symbols[symbol]
operator fun plus(symbol: FirBasedSymbol<*>): Symbol {
@@ -227,8 +216,6 @@ class LocalSymbolsCache : Iterable {
symbols[symbol] = result
return result
}
-
- override fun iterator(): Iterator = symbols.values.iterator()
}
@ExperimentalContracts
diff --git a/semanticdb-kotlinc/src/main/proto/com.sourcegraph.semanticdb_kotlin/semanticdb.proto b/semanticdb-kotlinc/src/main/proto/com.sourcegraph.semanticdb_kotlin/semanticdb.proto
deleted file mode 100644
index f09cf9231..000000000
--- a/semanticdb-kotlinc/src/main/proto/com.sourcegraph.semanticdb_kotlin/semanticdb.proto
+++ /dev/null
@@ -1,192 +0,0 @@
-// Original source: https://github.com/scalameta/scalameta/blob/cf796cf2436b40494baf2bdc266623dc65264ad5/semanticdb/semanticdb/semanticdb.proto
-// Local modifications:
-// - Removes unused fields to minimize the amount of generated code.
-// - Adds SymbolInformation.documentation that is pending upstream approval.
-// - Adds SymbolOccurrence.enclosing_range to support SCIP's enclosing_range field.
-
-syntax = "proto3";
-
-package com.sourcegraph.semanticdb_kotlinc;
-
-option java_package = "com.sourcegraph.semanticdb_kotlinc";
-option java_multiple_files = false;
-
-enum Schema {
- LEGACY = 0;
- SEMANTICDB3 = 3;
- SEMANTICDB4 = 4;
-}
-
-message TextDocuments {
- repeated TextDocument documents = 1;
-}
-
-message TextDocument {
- reserved 4, 8, 9;
- Schema schema = 1;
- string uri = 2;
- string text = 3;
- string md5 = 11;
- Language language = 10;
- repeated SymbolInformation symbols = 5;
- repeated SymbolOccurrence occurrences = 6;
-}
-
-enum Language {
- UNKNOWN_LANGUAGE = 0;
- SCALA = 1;
- JAVA = 2;
- KOTLIN = 3;
-}
-
-message Range {
- int32 start_line = 1;
- int32 start_character = 2;
- int32 end_line = 3;
- int32 end_character = 4;
-}
-
-message Signature {
- oneof sealed_value {
- ClassSignature class_signature = 1;
- MethodSignature method_signature = 2;
- TypeSignature type_signature = 3;
- ValueSignature value_signature = 4;
- }
-}
-
-message ClassSignature {
- Scope type_parameters = 1;
- repeated Type parents = 2;
- Scope declarations = 4;
-}
-
-message MethodSignature {
- Scope type_parameters = 1;
- repeated Scope parameter_lists = 2;
- Type return_type = 3;
-}
-
-message TypeSignature {
- Scope type_parameters = 1;
- Type lower_bound = 2;
- Type upper_bound = 3;
-}
-
-message ValueSignature {
- Type tpe = 1;
-}
-
-message SymbolInformation {
- enum Kind {
- reserved 1, 2, 4, 5, 15, 16;
- UNKNOWN_KIND = 0;
- LOCAL = 19;
- FIELD = 20;
- METHOD = 3;
- CONSTRUCTOR = 21;
- TYPE = 7;
- PARAMETER = 8;
- TYPE_PARAMETER = 9;
- PACKAGE = 11;
- CLASS = 13;
- INTERFACE = 18;
- }
- enum Property {
- UNKNOWN_PROPERTY = 0;
- reserved 0x1;
- reserved 0x2;
- ABSTRACT = 0x4;
- FINAL = 0x8;
- SEALED = 0x10;
- STATIC = 0x1000;
- ENUM = 0x4000;
- }
- reserved 2, 6, 7, 8, 9, 10, 11, 12, 14, 15;
- string symbol = 1;
- Language language = 16;
- Kind kind = 3;
- int32 properties = 4;
- string display_name = 5;
- Signature signature = 17;
- Access access = 18;
- repeated string overridden_symbols = 19;
- // NOTE: this field does not exist in the upstream SemanticDB spec.
- // It is added to support documentation strings (e.g., KDoc).
- Documentation documentation = 20;
-}
-
-message Access {
- oneof sealed_value {
- PrivateAccess private_access = 1;
- PrivateWithinAccess private_within_access = 3;
- ProtectedAccess protected_access = 4;
- PublicAccess public_access = 7;
- }
-}
-
-message PrivateAccess {}
-
-message PrivateWithinAccess {
- string symbol = 1;
-}
-
-message ProtectedAccess {}
-
-message PublicAccess {}
-
-message Documentation {
- enum Format {
- HTML = 0;
- MARKDOWN = 1;
- JAVADOC = 2;
- SCALADOC = 3;
- KDOC = 4;
- }
- string message = 1;
- Format format = 2;
-}
-
-message SymbolOccurrence {
- enum Role {
- UNKNOWN_ROLE = 0;
- REFERENCE = 1;
- DEFINITION = 2;
- }
- Range range = 1;
- string symbol = 2;
- Role role = 3;
- // NOTE: this field does not exist in the upstream SemanticDB spec.
- // It is added to support SCIP's enclosing_range field.
- // This is the range of the nearest non-trivial enclosing AST node.
- optional Range enclosing_range = 4;
-}
-
-message Scope {
- repeated string symlinks = 1;
- repeated SymbolInformation hardlinks = 2;
-}
-
-message Type {
- reserved 1, 3, 4, 5, 6, 11, 12, 15, 16;
- oneof sealed_value {
- TypeRef type_ref = 2;
- ExistentialType existential_type = 9;
- IntersectionType intersection_type = 17;
- }
-}
-
-message TypeRef {
- string symbol = 2;
- repeated Type type_arguments = 3;
-}
-
-message IntersectionType {
- repeated Type types = 1;
-}
-
-message ExistentialType {
- reserved 2;
- Type tpe = 1;
- Scope declarations = 3;
-}
diff --git a/semanticdb-kotlinc/src/test/kotlin/com/sourcegraph/semanticdb_kotlinc/test/AnalyzerTest.kt b/semanticdb-kotlinc/src/test/kotlin/com/sourcegraph/semanticdb_kotlinc/test/AnalyzerTest.kt
deleted file mode 100644
index 09724ed34..000000000
--- a/semanticdb-kotlinc/src/test/kotlin/com/sourcegraph/semanticdb_kotlinc/test/AnalyzerTest.kt
+++ /dev/null
@@ -1,1528 +0,0 @@
-package com.sourcegraph.semanticdb_kotlinc.test
-
-import com.sourcegraph.semanticdb_kotlinc.*
-import com.sourcegraph.semanticdb_kotlinc.Semanticdb.Language.KOTLIN
-import com.sourcegraph.semanticdb_kotlinc.Semanticdb.SymbolOccurrence.Role
-import com.sourcegraph.semanticdb_kotlinc.Semanticdb.TextDocument
-import com.tschuchort.compiletesting.KotlinCompilation
-import com.tschuchort.compiletesting.PluginOption
-import com.tschuchort.compiletesting.SourceFile
-import io.kotest.assertions.assertSoftly
-import io.kotest.assertions.fail
-import io.kotest.assertions.withClue
-import io.kotest.matchers.collections.shouldContain
-import io.kotest.matchers.shouldBe
-import io.kotest.matchers.shouldNotBe
-import java.io.File
-import java.nio.file.Path
-import kotlin.contracts.ExperimentalContracts
-import kotlin.test.Test
-import kotlin.test.assertEquals
-import org.intellij.lang.annotations.Language
-import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi
-import org.junit.jupiter.api.io.TempDir
-import java.nio.file.Paths
-
-@OptIn(ExperimentalCompilerApi::class)
-@ExperimentalContracts
-class AnalyzerTest {
- fun compileSemanticdb(path: Path, @Language("kotlin") code: String): TextDocument {
- val buildPath = File(path.resolve("build").toString()).apply { mkdir() }
- val source = SourceFile.testKt(code)
- lateinit var document: TextDocument
-
- val result =
- KotlinCompilation()
- .apply {
- sources = listOf(source)
- compilerPluginRegistrars = listOf(AnalyzerRegistrar { document = it })
- verbose = false
- pluginOptions =
- listOf(
- PluginOption("semanticdb-kotlinc", "sourceroot", path.toString()),
- PluginOption("semanticdb-kotlinc", "targetroot", buildPath.toString())
- )
- commandLineProcessors = listOf(AnalyzerCommandLineProcessor())
- workingDir = path.toFile()
- }
- .compile()
-
- result.exitCode shouldBe KotlinCompilation.ExitCode.OK
- document shouldNotBe null
- return document
- }
-
- @Test
- fun `basic test`(@TempDir path: Path) {
- val document =
- compileSemanticdb(
- path,
- """
- package sample
- class Banana {
- fun foo() { }
- }"""
- )
-
- val occurrences =
- arrayOf(
- SymbolOccurrence {
- role = Role.REFERENCE
- symbol = "sample/"
- range {
- startLine = 0
- startCharacter = 8
- endLine = 0
- endCharacter = 14
- }
- },
- SymbolOccurrence {
- role = Role.DEFINITION
- symbol = "sample/Banana#"
- range {
- startLine = 1
- startCharacter = 6
- endLine = 1
- endCharacter = 12
- }
- enclosingRange {
- startLine = 1
- endLine = 3
- endCharacter = 1
- }
- },
- SymbolOccurrence {
- role = Role.DEFINITION
- symbol = "sample/Banana#foo()."
- range {
- startLine = 2
- startCharacter = 8
- endLine = 2
- endCharacter = 11
- }
- enclosingRange {
- startLine = 2
- startCharacter = 4
- endLine = 2
- endCharacter = 17
- }
- })
- assertSoftly(document.occurrencesList) {
- withClue(this) { occurrences.forEach(::shouldContain) }
- }
-
- val symbols =
- arrayOf(
- SymbolInformation {
- symbol = "sample/Banana#"
- language = KOTLIN
- displayName = "Banana"
- documentation =
- Documentation {
- format = Semanticdb.Documentation.Format.MARKDOWN
- message = "```kotlin\npublic final class Banana : Any\n```"
- }
- },
- SymbolInformation {
- symbol = "sample/Banana#foo()."
- language = KOTLIN
- displayName = "foo"
- documentation =
- Documentation {
- format = Semanticdb.Documentation.Format.MARKDOWN
- message = "```kotlin\npublic final fun foo(): Unit\n```"
- }
- })
- assertSoftly(document.symbolsList) { withClue(this) { symbols.forEach(::shouldContain) } }
- }
-
- @Test
- fun imports(@TempDir path: Path) {
- val document =
- compileSemanticdb(
- path,
- """
- package sample
-
- import kotlin.Boolean
- import kotlin.Int as KInt
- """
- )
-
- val occurrences =
- arrayOf(
- SymbolOccurrence {
- role = Role.REFERENCE
- symbol = "sample/"
- range {
- startLine = 0
- startCharacter = 8
- endLine = 0
- endCharacter = 14
- }
- },
- SymbolOccurrence {
- role = Role.REFERENCE
- symbol = "kotlin/"
- range {
- startLine = 2
- startCharacter = 7
- endLine = 2
- endCharacter = 13
- }
- },
- SymbolOccurrence {
- role = Role.REFERENCE
- symbol = "kotlin/Boolean#"
- range {
- startLine = 2
- startCharacter = 14
- endLine = 2
- endCharacter = 21
- }
- },
- SymbolOccurrence {
- role = Role.REFERENCE
- symbol = "kotlin/"
- range {
- startLine = 3
- startCharacter = 7
- endLine = 3
- endCharacter = 13
- }
- },
- SymbolOccurrence {
- role = Role.REFERENCE
- symbol = "kotlin/Int#"
- range {
- startLine = 3
- startCharacter = 14
- endLine = 3
- endCharacter = 17
- }
- },
- )
- assertSoftly(document.occurrencesList) {
- withClue(this) { occurrences.forEach(::shouldContain) }
- }
- }
-
- @Test
- fun `local classes`(@TempDir path: Path) {
- val document =
- compileSemanticdb(
- path,
- """
- package sample
-
- fun foo() {
- class LocalClass {
- fun localClassMethod() {}
- }
- }
- """
- )
-
- val occurrences =
- arrayOf(
- SymbolOccurrence {
- role = Role.DEFINITION
- symbol = "sample/foo()."
- range {
- startLine = 2
- startCharacter = 4
- endLine = 2
- endCharacter = 7
- }
- enclosingRange {
- startLine = 2
- endLine = 6
- endCharacter = 1
- }
- },
- // LocalClass
- SymbolOccurrence {
- role = Role.DEFINITION
- symbol = "local0"
- range {
- startLine = 3
- startCharacter = 8
- endLine = 3
- endCharacter = 18
- }
- enclosingRange {
- startLine = 3
- startCharacter = 2
- endLine = 5
- endCharacter = 3
- }
- },
- // LocalClass constructor
- SymbolOccurrence {
- role = Role.DEFINITION
- symbol = "local1"
- range {
- startLine = 3
- startCharacter = 8
- endLine = 3
- endCharacter = 18
- }
- enclosingRange {
- startLine = 3
- startCharacter = 2
- endLine = 5
- endCharacter = 3
- }
- },
- // localClassMethod
- SymbolOccurrence {
- role = Role.DEFINITION
- symbol = "local2"
- range {
- startLine = 4
- startCharacter = 8
- endLine = 4
- endCharacter = 24
- }
- enclosingRange {
- startLine = 4
- startCharacter = 4
- endLine = 4
- endCharacter = 29
- }
- },
- )
- assertSoftly(document.occurrencesList) {
- withClue(this) { occurrences.forEach(::shouldContain) }
- }
-
- val symbols =
- arrayOf(
- SymbolInformation {
- symbol = "sample/foo()."
- displayName = "foo"
- language = KOTLIN
- documentation {
- message = "```kotlin\npublic final fun foo(): Unit\n```"
- format = Semanticdb.Documentation.Format.MARKDOWN
- }
- },
- SymbolInformation {
- symbol = "local0"
- displayName = "LocalClass"
- language = KOTLIN
- documentation {
- message = "```kotlin\nlocal final class LocalClass : Any\n```"
- format = Semanticdb.Documentation.Format.MARKDOWN
- }
- },
- SymbolInformation {
- symbol = "local1"
- displayName = "LocalClass"
- language = KOTLIN
- documentation {
- message = "```kotlin\npublic constructor(): LocalClass\n```"
- format = Semanticdb.Documentation.Format.MARKDOWN
- }
- },
- SymbolInformation {
- symbol = "local2"
- displayName = "localClassMethod"
- language = KOTLIN
- documentation {
- message = "```kotlin\npublic final fun localClassMethod(): Unit\n```"
- format = Semanticdb.Documentation.Format.MARKDOWN
- }
- },
- )
- assertSoftly(document.symbolsList) { withClue(this) { symbols.forEach(::shouldContain) } }
- }
-
- @Test
- fun overrides(@TempDir path: Path) {
- val document =
- compileSemanticdb(
- path,
- """
- package sample
-
- interface Interface {
- fun foo()
- }
-
- class Class : Interface {
- override fun foo() {}
- }
- """
- )
-
- val occurrences =
- arrayOf(
- SymbolOccurrence {
- role = Role.REFERENCE
- symbol = "sample/"
- range {
- startLine = 0
- startCharacter = 8
- endLine = 0
- endCharacter = 14
- }
- },
- SymbolOccurrence {
- role = Role.DEFINITION
- symbol = "sample/Interface#"
- range {
- startLine = 2
- startCharacter = 10
- endLine = 2
- endCharacter = 19
- }
- enclosingRange {
- startLine = 2
- endLine = 4
- endCharacter = 1
- }
- },
- SymbolOccurrence {
- role = Role.DEFINITION
- symbol = "sample/Interface#foo()."
- range {
- startLine = 3
- startCharacter = 8
- endLine = 3
- endCharacter = 11
- }
- enclosingRange {
- startLine = 3
- startCharacter = 4
- endLine = 3
- endCharacter = 13
- }
- },
- SymbolOccurrence {
- role = Role.DEFINITION
- symbol = "sample/Class#"
- range {
- startLine = 6
- startCharacter = 6
- endLine = 6
- endCharacter = 11
- }
- enclosingRange {
- startLine = 6
- endLine = 8
- endCharacter = 1
- }
- },
- SymbolOccurrence {
- role = Role.REFERENCE
- symbol = "sample/Interface#"
- range {
- startLine = 6
- startCharacter = 14
- endLine = 6
- endCharacter = 23
- }
- },
- SymbolOccurrence {
- role = Role.DEFINITION
- symbol = "sample/Class#foo()."
- range {
- startLine = 7
- startCharacter = 17
- endLine = 7
- endCharacter = 20
- }
- enclosingRange {
- startLine = 7
- startCharacter = 4
- endLine = 7
- endCharacter = 25
- }
- },
- )
- assertSoftly(document.occurrencesList) {
- withClue(this) { occurrences.forEach(::shouldContain) }
- }
-
- val symbols =
- arrayOf(
- SymbolInformation {
- symbol = "sample/Interface#"
- displayName = "Interface"
- language = KOTLIN
- documentation {
- message = "```kotlin\npublic abstract interface Interface : Any\n```"
- format = Semanticdb.Documentation.Format.MARKDOWN
- }
- },
- SymbolInformation {
- symbol = "sample/Interface#foo()."
- displayName = "foo"
- language = KOTLIN
- documentation {
- message = "```kotlin\npublic abstract fun foo(): Unit\n\n```"
- format = Semanticdb.Documentation.Format.MARKDOWN
- }
- },
- SymbolInformation {
- symbol = "sample/Class#"
- displayName = "Class"
- language = KOTLIN
- documentation {
- message = "```kotlin\npublic final class Class : Interface\n```"
- format = Semanticdb.Documentation.Format.MARKDOWN
- }
- addOverriddenSymbols("sample/Interface#")
- },
- SymbolInformation {
- symbol = "sample/Class#foo()."
- displayName = "foo"
- language = KOTLIN
- documentation {
- message = "```kotlin\npublic open override fun foo(): Unit\n```"
- format = Semanticdb.Documentation.Format.MARKDOWN
- }
- addOverriddenSymbols("sample/Interface#foo().")
- },
- )
- assertSoftly(document.symbolsList) { withClue(this) { symbols.forEach(::shouldContain) } }
- }
-
- @Test
- fun `anonymous object`(@TempDir path: Path) {
- val document =
- compileSemanticdb(
- path,
- """
- package sample
-
- interface Interface {
- fun foo()
- }
-
- fun main() {
- val a = object : Interface {
- override fun foo() {}
- }
- val b = object : Interface {
- override fun foo() {}
- }
- }
- """
- )
-
- val occurrences =
- arrayOf(
- SymbolOccurrence {
- role = Role.REFERENCE
- symbol = "sample/"
- range {
- startLine = 0
- startCharacter = 8
- endLine = 0
- endCharacter = 14
- }
- },
- SymbolOccurrence {
- role = Role.DEFINITION
- symbol = "sample/Interface#"
- range {
- startLine = 2
- startCharacter = 10
- endLine = 2
- endCharacter = 19
- }
- enclosingRange {
- startLine = 2
- endLine = 4
- endCharacter = 1
- }
- },
- SymbolOccurrence {
- role = Role.DEFINITION
- symbol = "sample/Interface#foo()."
- range {
- startLine = 3
- startCharacter = 8
- endLine = 3
- endCharacter = 11
- }
- enclosingRange {
- startLine = 3
- startCharacter = 4
- endLine = 3
- endCharacter = 13
- }
- },
- SymbolOccurrence {
- role = Role.DEFINITION
- symbol = "sample/``#"
- range {
- startLine = 7
- startCharacter = 12
- endLine = 7
- endCharacter = 18
- }
- enclosingRange {
- startLine = 7
- startCharacter = 12
- endLine = 9
- endCharacter = 5
- }
- },
- SymbolOccurrence {
- role = Role.DEFINITION
- symbol = "sample/``#``()."
- range {
- startLine = 7
- startCharacter = 12
- endLine = 7
- endCharacter = 18
- }
- enclosingRange {
- startLine = 7
- startCharacter = 12
- endLine = 9
- endCharacter = 5
- }
- },
- SymbolOccurrence {
- role = Role.REFERENCE
- symbol = "sample/Interface#"
- range {
- startLine = 7
- startCharacter = 21
- endLine = 7
- endCharacter = 30
- }
- },
- SymbolOccurrence {
- role = Role.DEFINITION
- symbol = "sample/``#foo()."
- range {
- startLine = 8
- startCharacter = 21
- endLine = 8
- endCharacter = 24
- }
- enclosingRange {
- startLine = 8
- startCharacter = 8
- endLine = 8
- endCharacter = 29
- }
- },
- SymbolOccurrence {
- role = Role.DEFINITION
- symbol = "sample/``#"
- range {
- startLine = 10
- startCharacter = 12
- endLine = 10
- endCharacter = 18
- }
- enclosingRange {
- startLine = 10
- startCharacter = 12
- endLine = 12
- endCharacter = 5
- }
- },
- SymbolOccurrence {
- role = Role.DEFINITION
- symbol = "sample/``#``()."
- range {
- startLine = 10
- startCharacter = 12
- endLine = 10
- endCharacter = 18
- }
- enclosingRange {
- startLine = 10
- startCharacter = 12
- endLine = 12
- endCharacter = 5
- }
- },
- SymbolOccurrence {
- role = Role.REFERENCE
- symbol = "sample/Interface#"
- range {
- startLine = 10
- startCharacter = 21
- endLine = 10
- endCharacter = 30
- }
- },
- SymbolOccurrence {
- role = Role.DEFINITION
- symbol = "sample/``#foo()."
- range {
- startLine = 11
- startCharacter = 21
- endLine = 11
- endCharacter = 24
- }
- enclosingRange {
- startLine = 11
- startCharacter = 8
- endLine = 11
- endCharacter = 29
- }
- },
- )
- assertSoftly(document.occurrencesList) {
- withClue(this) { occurrences.forEach(::shouldContain) }
- }
-
- val symbols =
- arrayOf(
- SymbolInformation {
- symbol = "sample/Interface#"
- displayName = "Interface"
- language = KOTLIN
- documentation {
- message = "```kotlin\npublic abstract interface Interface : Any\n```"
- format = Semanticdb.Documentation.Format.MARKDOWN
- }
- },
- SymbolInformation {
- symbol = "sample/``#"
- displayName = ""
- language = KOTLIN
- documentation {
- message = "```kotlin\nobject : Interface\n```"
- format = Semanticdb.Documentation.Format.MARKDOWN
- }
- addOverriddenSymbols("sample/Interface#")
- },
- SymbolInformation {
- symbol = "sample/``#foo()."
- displayName = "foo"
- language = KOTLIN
- documentation {
- message = "```kotlin\npublic open override fun foo(): Unit\n```"
- format = Semanticdb.Documentation.Format.MARKDOWN
- }
- addOverriddenSymbols("sample/Interface#foo().")
- },
- SymbolInformation {
- symbol = "sample/``#"
- displayName = ""
- language = KOTLIN
- documentation {
- message = "```kotlin\nobject : Interface\n```"
- format = Semanticdb.Documentation.Format.MARKDOWN
- }
- addOverriddenSymbols("sample/Interface#")
- },
- SymbolInformation {
- symbol = "sample/``#foo()."
- displayName = "foo"
- language = KOTLIN
- documentation {
- message = "```kotlin\npublic open override fun foo(): Unit\n```"
- format = Semanticdb.Documentation.Format.MARKDOWN
- }
- addOverriddenSymbols("sample/Interface#foo().")
- },
- )
- assertSoftly(document.symbolsList) { withClue(this) { symbols.forEach(::shouldContain) } }
- }
-
- @Test
- fun `function return type`(@TempDir path: Path) {
- val document =
- compileSemanticdb(
- path,
- """
- package sample
-
- fun foo(arg: Int): Boolean = true
- """
- )
-
- val occurrences =
- arrayOf(
- SymbolOccurrence {
- role = Role.DEFINITION
- symbol = "sample/foo()."
- range {
- startLine = 2
- startCharacter = 4
- endLine = 2
- endCharacter = 7
- }
- enclosingRange {
- startLine = 2
- endLine = 2
- endCharacter = 33
- }
- },
- SymbolOccurrence {
- role = Role.DEFINITION
- symbol = "sample/foo().(arg)"
- range {
- startLine = 2
- startCharacter = 8
- endLine = 2
- endCharacter = 11
- }
- enclosingRange {
- startLine = 2
- startCharacter = 8
- endLine = 2
- endCharacter = 16
- }
- },
- SymbolOccurrence {
- role = Role.REFERENCE
- symbol = "kotlin/Int#"
- range {
- startLine = 2
- startCharacter = 13
- endLine = 2
- endCharacter = 16
- }
- },
- SymbolOccurrence {
- role = Role.REFERENCE
- symbol = "kotlin/Boolean#"
- range {
- startLine = 2
- startCharacter = 19
- endLine = 2
- endCharacter = 26
- }
- },
- )
- assertSoftly(document.occurrencesList) {
- withClue(this) { occurrences.forEach(::shouldContain) }
- }
- }
-
- @Test
- fun `type operators`(@TempDir path: Path) {
- val document =
- compileSemanticdb(
- path,
- """
- package sample
-
- fun foo(x: Any) {
- when (x) {
- is Int -> true
- else -> x as Float
- }
- }
- """)
-
- val occurrences =
- arrayOf(
- SymbolOccurrence {
- role = Role.REFERENCE
- symbol = "kotlin/Int#"
- range {
- startLine = 4
- startCharacter = 11
- endLine = 4
- endCharacter = 14
- }
- },
- SymbolOccurrence {
- role = Role.REFERENCE
- symbol = "kotlin/Float#"
- range {
- startLine = 5
- startCharacter = 21
- endLine = 5
- endCharacter = 26
- }
- },
- )
- assertSoftly(document.occurrencesList) {
- withClue(this) { occurrences.forEach(::shouldContain) }
- }
- }
-
- @Test
- fun `exception test`(@TempDir path: Path) {
- val buildPath = File(path.resolve("build").toString()).apply { mkdir() }
- val result =
- KotlinCompilation()
- .apply {
- sources = listOf(SourceFile.testKt(""))
- compilerPluginRegistrars =
- listOf(AnalyzerRegistrar { throw Exception("sample text") })
- verbose = false
- pluginOptions =
- listOf(
- PluginOption("semanticdb-kotlinc", "sourceroot", path.toString()),
- PluginOption("semanticdb-kotlinc", "targetroot", buildPath.toString())
- )
- commandLineProcessors = listOf(AnalyzerCommandLineProcessor())
- workingDir = path.toFile()
- }
- .compile()
-
- result.exitCode shouldBe KotlinCompilation.ExitCode.OK
- }
-
- @Test
- // shamelessly stolen code snippet from https://learnxinyminutes.com/docs/kotlin/
- fun `learn x in y test`(@TempDir path: Path) {
- val buildPath = File(path.resolve("build").toString()).apply { mkdir() }
-
- val source =
- SourceFile.testKt(
- """
- @file:Suppress("UNUSED_VARIABLE", "UNUSED_PARAMETER", "NAME_SHADOWING", "ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE", "UNUSED_VALUE")
- package sample
-
- fun main(args: Array) {
- val fooVal = 10 // we cannot later reassign fooVal to something else
- var fooVar = 10
- fooVar = 20 // fooVar can be reassigned
-
- /*
- In most cases, Kotlin can determine what the type of a variable is,
- so we don't have to explicitly specify it every time.
- We can explicitly declare the type of a variable like so:
- */
- val foo: Int = 7
-
- /*
- Strings can be represented in a similar way as in Java.
- Escaping is done with a backslash.
- */
- val fooString = "My String Is Here!"
- val barString = "Printing on a new line?\nNo Problem!"
- val bazString = "Do you want to add a tab?\tNo Problem!"
- println(fooString)
- println(barString)
- println(bazString)
-
- /*
- Strings can contain template expressions.
- A template expression starts with a dollar sign (${'$'}).
- */
- val fooTemplateString = "$'fooString' has ${"fooString.length"} characters"
- println(fooTemplateString) // => My String Is Here! has 18 characters
-
- /*
- For a variable to hold null it must be explicitly specified as nullable.
- A variable can be specified as nullable by appending a ? to its type.
- We can access a nullable variable by using the ?. operator.
- We can use the ?: operator to specify an alternative value to use
- if a variable is null.
- */
- var fooNullable: String? = "abc"
- println(fooNullable?.length) // => 3
- println(fooNullable?.length ?: -1) // => 3
- fooNullable = null
- println(fooNullable?.length) // => null
- println(fooNullable?.length ?: -1) // => -1
-
- /*
- Functions can be declared using the "fun" keyword.
- Function arguments are specified in brackets after the function name.
- Function arguments can optionally have a default value.
- The function return type, if required, is specified after the arguments.
- */
- fun hello(name: String = "world"): String {
- return "Hello, $'name'!"
- }
- println(hello("foo")) // => Hello, foo!
- println(hello(name = "bar")) // => Hello, bar!
- println(hello()) // => Hello, world!
-
- /*
- A function parameter may be marked with the "vararg" keyword
- to allow a variable number of arguments to be passed to the function.
- */
- fun varargExample(vararg names: Int) {
- println("Argument has ${"names.size"} elements")
- }
- varargExample() // => Argument has 0 elements
- varargExample(1) // => Argument has 1 elements
- varargExample(1, 2, 3) // => Argument has 3 elements
-
- /*
- When a function consists of a single expression then the curly brackets can
- be omitted. The body is specified after the = symbol.
- */
- fun odd(x: Int): Boolean = x % 2 == 1
- println(odd(6)) // => false
- println(odd(7)) // => true
-
- // If the return type can be inferred then we don't need to specify it.
- fun even(x: Int) = x % 2 == 0
- println(even(6)) // => true
- println(even(7)) // => false
-
- // Functions can take functions as arguments and return functions.
- fun not(f: (Int) -> Boolean): (Int) -> Boolean {
- return {n -> !f.invoke(n)}
- }
- // Named functions can be specified as arguments using the :: operator.
- val notOdd = not(::odd)
- val notEven = not(::even)
- // Lambda expressions can be specified as arguments.
- val notZero = not {n -> n == 0}
- /*
- If a lambda has only one parameter
- then its declaration can be omitted (along with the ->).
- The name of the single parameter will be "it".
- */
- val notPositive = not {it > 0}
- for (i in 0..4) {
- println("${"notOdd(i)"} ${"notEven(i)"} ${"notZero(i)"} ${"notPositive(i)"}")
- }
-
- // The "class" keyword is used to declare classes.
- class ExampleClass(val x: Int) {
- fun memberFunction(y: Int): Int {
- return x + y
- }
-
- infix fun infixMemberFunction(y: Int): Int {
- return x * y
- }
- }
- /*
- To create a new instance we call the constructor.
- Note that Kotlin does not have a "new" keyword.
- */
- val fooExampleClass = ExampleClass(7)
- // Member functions can be called using dot notation.
- println(fooExampleClass.memberFunction(4)) // => 11
- /*
- If a function has been marked with the "infix" keyword then it can be
- called using infix notation.
- */
- println(fooExampleClass infixMemberFunction 4) // => 28
-
- /*
- Data classes are a concise way to create classes that just hold data.
- The "hashCode"/"equals" and "toString" methods are automatically generated.
- */
- data class DataClassExample (val x: Int, val y: Int, val z: Int)
- val fooData = DataClassExample(1, 2, 4)
- println(fooData) // => DataClassExample(x=1, y=2, z=4)
-
- // Data classes have a "copy" function.
- val fooCopy = fooData.copy(y = 100)
- println(fooCopy) // => DataClassExample(x=1, y=100, z=4)
-
- // Objects can be destructured into multiple variables.
- val (a, b, c) = fooCopy
- println("$'a' $'b' $'c'") // => 1 100 4
-
- // destructuring in "for" loop
- for ((a, b, c) in listOf(fooData)) {
- println("$'a' $'b' $'c'") // => 1 2 4
- }
-
- val mapData = mapOf("a" to 1, "b" to 2)
- // Map.Entry is destructurable as well
- for ((key, value) in mapData) {
- println("$'key' -> $'value'")
- }
-
- // The "with" function is similar to the JavaScript "with" statement.
- data class MutableDataClassExample (var x: Int, var y: Int, var z: Int)
- val fooMutableData = MutableDataClassExample(7, 4, 9)
- with (fooMutableData) {
- x -= 2
- y += 2
- z--
- }
- println(fooMutableData) // => MutableDataClassExample(x=5, y=6, z=8)
-
- /*
- We can create a list using the "listOf" function.
- The list will be immutable - elements cannot be added or removed.
- */
- val fooList = listOf("a", "b", "c")
- println(fooList.size) // => 3
- println(fooList.first()) // => a
- println(fooList.last()) // => c
- // Elements of a list can be accessed by their index.
- println(fooList[1]) // => b
-
- // A mutable list can be created using the "mutableListOf" function.
- val fooMutableList = mutableListOf("a", "b", "c")
- fooMutableList.add("d")
- println(fooMutableList.last()) // => d
- println(fooMutableList.size) // => 4
-
- // We can create a set using the "setOf" function.
- val fooSet = setOf("a", "b", "c")
- println(fooSet.contains("a")) // => true
- println(fooSet.contains("z")) // => false
-
- // We can create a map using the "mapOf" function.
- val fooMap = mapOf("a" to 8, "b" to 7, "c" to 9)
- // Map values can be accessed by their key.
- println(fooMap["a"]) // => 8
-
- /*
- Sequences represent lazily-evaluated collections.
- We can create a sequence using the "generateSequence" function.
- */
- val fooSequence = generateSequence(1, { it + 1 })
- val x = fooSequence.take(10).toList()
- println(x) // => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
-
- // An example of using a sequence to generate Fibonacci numbers:
- fun fibonacciSequence(): Sequence {
- var a = 0L
- var b = 1L
-
- fun next(): Long {
- val result = a + b
- a = b
- b = result
- return a
- }
-
- return generateSequence(::next)
- }
- val y = fibonacciSequence().take(10).toList()
- println(y) // => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
-
- // Kotlin provides higher-order functions for working with collections.
- val z = (1..9).map {it * 3}
- .filter {it < 20}
- .groupBy {it % 2 == 0}
- .mapKeys {if (it.key) "even" else "odd"}
- println(z) // => {odd=[3, 9, 15], even=[6, 12, 18]}
-
- // A "for" loop can be used with anything that provides an iterator.
- for (c in "hello") {
- println(c)
- }
-
- // "while" loops work in the same way as other languages.
- var ctr = 0
- while (ctr < 5) {
- println(ctr)
- ctr++
- }
- do {
- println(ctr)
- ctr++
- } while (ctr < 10)
-
- /*
- "if" can be used as an expression that returns a value.
- For this reason the ternary ?: operator is not needed in Kotlin.
- */
- val num = 5
- val message = if (num % 2 == 0) "even" else "odd"
- println("$'num' is $'message'") // => 5 is odd
-
- // "when" can be used as an alternative to "if-else if" chains.
- val i = 10
- when {
- i < 7 -> println("first block")
- fooString.startsWith("hello") -> println("second block")
- else -> println("else block")
- }
-
- // "when" can be used with an argument.
- when (i) {
- 0, 21 -> println("0 or 21")
- in 1..20 -> println("in the range 1 to 20")
- else -> println("none of the above")
- }
-
- // "when" can be used as a function that returns a value.
- var result = when (i) {
- 0, 21 -> "0 or 21"
- in 1..20 -> "in the range 1 to 20"
- else -> "none of the above"
- }
- println(result)
-
- /*
- We can check if an object is of a particular type by using the "is" operator.
- If an object passes a type check then it can be used as that type without
- explicitly casting it.
- */
- fun smartCastExample(x: Any) : Boolean {
- if (x is Boolean) {
- // x is automatically cast to Boolean
- return x
- } else if (x is Int) {
- // x is automatically cast to Int
- return x > 0
- } else if (x is String) {
- // x is automatically cast to String
- return x.isNotEmpty()
- } else {
- return false
- }
- }
- println(smartCastExample("Hello, world!")) // => true
- println(smartCastExample("")) // => false
- println(smartCastExample(5)) // => true
- println(smartCastExample(0)) // => false
- println(smartCastExample(true)) // => true
-
- // Smartcast also works with when block
- fun smartCastWhenExample(x: Any) = when (x) {
- is Boolean -> x
- is Int -> x > 0
- is String -> x.isNotEmpty()
- else -> false
- }
-
- /*
- Extensions are a way to add new functionality to a class.
- This is similar to C# extension methods.
- */
- fun String.remove(c: Char): String {
- return this.filter {it != c}
- }
- println("Hello, world!".remove('l')) // => Heo, word!
- }
-
- // Enum classes are similar to Java enum types.
- enum class EnumExample {
- A, B, C // Enum constants are separated with commas.
- }
- fun printEnum() = println(EnumExample.A) // => A
-
- // Since each enum is an instance of the enum class, they can be initialized as:
- enum class EnumExample1(val value: Int) {
- A(value = 1),
- B(value = 2),
- C(value = 3)
- }
- fun printProperty() = println(EnumExample1.A.value) // => 1
-
- // Every enum has properties to obtain its name and ordinal(position) in the enum class declaration:
- fun printName() = println(EnumExample1.A.name) // => A
- fun printPosition() = println(EnumExample1.A.ordinal) // => 0
-
- /*
- The "object" keyword can be used to create singleton objects.
- We cannot instantiate it but we can refer to its unique instance by its name.
- This is similar to Scala singleton objects.
- */
- object ObjectExample {
- fun hello(): String {
- return "hello"
- }
-
- override fun toString(): String {
- return "Hello, it's me, ${"ObjectExample::class.simpleName"}"
- }
- }
-
-
- fun useSingletonObject() {
- println(ObjectExample.hello()) // => hello
- // In Kotlin, "Any" is the root of the class hierarchy, just like "Object" is in Java
- val someRef: Any = ObjectExample
- println(someRef) // => Hello, it's me, ObjectExample
- }
-
-
- /* The not-null assertion operator (!!) converts any value to a non-null type and
- throws an exception if the value is null.
- */
- var b: String? = "abc"
- val l = b!!.length
-
- data class Counter(var value: Int) {
- // overload Counter += Int
- operator fun plusAssign(increment: Int) {
- this.value += increment
- }
-
- // overload Counter++ and ++Counter
- operator fun inc() = Counter(value + 1)
-
- // overload Counter + Counter
- operator fun plus(other: Counter) = Counter(this.value + other.value)
-
- // overload Counter * Counter
- operator fun times(other: Counter) = Counter(this.value * other.value)
-
- // overload Counter * Int
- operator fun times(value: Int) = Counter(this.value * value)
-
- // overload Counter in Counter
- operator fun contains(other: Counter) = other.value == this.value
-
- // overload Counter[Int] = Int
- operator fun set(index: Int, value: Int) {
- this.value = index + value
- }
-
- // overload Counter instance invocation
- operator fun invoke() = println("The value of the counter is $'value'")
-
- }
- /* You can also overload operators through extension methods */
- // overload -Counter
- operator fun Counter.unaryMinus() = Counter(-this.value)
-
- fun operatorOverloadingDemo() {
- var counter1 = Counter(0)
- var counter2 = Counter(5)
- counter1 += 7
- println(counter1) // => Counter(value=7)
- println(counter1 + counter2) // => Counter(value=12)
- println(counter1 * counter2) // => Counter(value=35)
- println(counter2 * 2) // => Counter(value=10)
- println(counter1 in Counter(5)) // => false
- println(counter1 in Counter(7)) // => true
- counter1[26] = 10
- println(counter1) // => Counter(value=36)
- counter1() // => The value of the counter is 36
- println(-counter2) // => Counter(value=-5)
- }
- """
- )
-
- val result =
- KotlinCompilation()
- .apply {
- sources = listOf(source)
- compilerPluginRegistrars = listOf(AnalyzerRegistrar())
- verbose = false
- pluginOptions =
- listOf(
- PluginOption("semanticdb-kotlinc", "sourceroot", path.toString()),
- PluginOption("semanticdb-kotlinc", "targetroot", buildPath.toString())
- )
- commandLineProcessors = listOf(AnalyzerCommandLineProcessor())
- workingDir = path.toFile()
- }
- .compile()
-
- result.exitCode shouldBe KotlinCompilation.ExitCode.OK
- }
-
-
- @Test
- fun `compound package name semicolon test`(@TempDir path: Path) {
- val document =
- compileSemanticdb(
- path, """
- package hello.sample;
- class Apple
- """.trimIndent()
- )
-
- val occurrences =
- arrayOf(
- SymbolOccurrence {
- role = Role.REFERENCE
- symbol = "hello/"
- range {
- startLine = 0
- startCharacter = 8
- endLine = 0
- endCharacter = 13
- }
- },
- SymbolOccurrence {
- role = Role.REFERENCE
- symbol = "hello/sample/"
- range {
- startLine = 0
- startCharacter = 14
- endLine = 0
- endCharacter = 20
- }
- },
- SymbolOccurrence {
- role = Role.DEFINITION
- symbol = "hello/sample/Apple#"
- range {
- startLine = 1
- startCharacter = 6
- endLine = 1
- endCharacter = 11
- }
- enclosingRange {
- startLine = 1
- endLine = 1
- endCharacter = 11
- }
- },
- SymbolOccurrence {
- role = Role.DEFINITION
- symbol = "hello/sample/Apple#``()."
- range {
- startLine = 1
- startCharacter = 6
- endLine = 1
- endCharacter = 11
- }
- enclosingRange {
- startLine = 1
- endLine = 1
- endCharacter = 11
- }
- },
- )
-
- assertSoftly(document.occurrencesList) {
- withClue(document.occurrencesList) { occurrences.forEach(::shouldContain) }
- }
-
- val symbols =
- arrayOf(
- SymbolInformation {
- symbol = "hello/sample/Apple#"
- language = KOTLIN
- displayName = "Apple"
- documentation =
- Documentation {
- format = Semanticdb.Documentation.Format.MARKDOWN
- message = "```kotlin\npublic final class Apple : Any\n```"
- }
- })
-
- assertSoftly(document.symbolsList) { withClue(this) { symbols.forEach(::shouldContain) } }
- }
-
-
- @Test
- fun `simple package name semicolon test`(@TempDir path: Path) {
- val document =
- compileSemanticdb(
- path,
- """
- package sample;
- class Banana {
- fun foo() { }
- }"""
- )
-
- val occurrences =
- arrayOf(
- SymbolOccurrence {
- role = Role.REFERENCE
- symbol = "sample/"
- range {
- startLine = 0
- startCharacter = 8
- endLine = 0
- endCharacter = 14
- }
- },
- SymbolOccurrence {
- role = Role.DEFINITION
- symbol = "sample/Banana#"
- range {
- startLine = 1
- startCharacter = 6
- endLine = 1
- endCharacter = 12
- }
- enclosingRange {
- startLine = 1
- endLine = 3
- endCharacter = 1
- }
- },
- SymbolOccurrence {
- role = Role.DEFINITION
- symbol = "sample/Banana#foo()."
- range {
- startLine = 2
- startCharacter = 8
- endLine = 2
- endCharacter = 11
- }
- enclosingRange {
- startLine = 2
- startCharacter = 4
- endLine = 2
- endCharacter = 17
- }
- },
- SymbolOccurrence {
- role = Role.DEFINITION
- symbol = "sample/Banana#"
- range {
- startLine = 1
- startCharacter = 6
- endLine = 1
- endCharacter = 12
- }
- enclosingRange {
- startLine = 1
- endLine = 3
- endCharacter = 1
- }
- },
- )
- assertSoftly(document.occurrencesList) {
- withClue(this) { occurrences.forEach(::shouldContain) }
- }
-
- val symbols =
- arrayOf(
- SymbolInformation {
- symbol = "sample/Banana#"
- language = KOTLIN
- displayName = "Banana"
- documentation =
- Documentation {
- format = Semanticdb.Documentation.Format.MARKDOWN
- message = "```kotlin\npublic final class Banana : Any\n```"
- }
- },
- SymbolInformation {
- symbol = "sample/Banana#foo()."
- language = KOTLIN
- displayName = "foo"
- documentation =
- Documentation {
- format = Semanticdb.Documentation.Format.MARKDOWN
- message = "```kotlin\npublic final fun foo(): Unit\n```"
- }
- })
- assertSoftly(document.symbolsList) { withClue(this) { symbols.forEach(::shouldContain) } }
- }
-
- @Test
- fun documentation(@TempDir path: Path) {
- val document =
- compileSemanticdb(
- path,
- """
- package sample
- import java.io.Serializable
- abstract class DocstringSuperclass
-
- /** Example class docstring */
- class Docstrings: DocstringSuperclass(), Serializable
-
- /**
- * Example method docstring
- *
- **/
- inline fun docstrings(msg: String): Int { return msg.length }
- """.trimIndent()
- )
- document.assertDocumentation("sample/Docstrings#", "Example class docstring")
- document.assertDocumentation("sample/docstrings().", "Example method docstring")
- }
-
- private fun TextDocument.assertDocumentation(symbol: String, expectedDocumentation: String) {
- val markdown =
- this.symbolsList.find { it.symbol == symbol }?.documentation?.message
- ?: fail("no documentation for symbol $symbol")
- val obtainedDocumentation = markdown.split("----").last().trim()
- assertEquals(expectedDocumentation, obtainedDocumentation)
- }
-}
diff --git a/semanticdb-kotlinc/src/test/kotlin/com/sourcegraph/semanticdb_kotlinc/test/SemanticdbSymbolsTest.kt b/semanticdb-kotlinc/src/test/kotlin/com/sourcegraph/semanticdb_kotlinc/test/SemanticdbSymbolsTest.kt
deleted file mode 100644
index d3cef3027..000000000
--- a/semanticdb-kotlinc/src/test/kotlin/com/sourcegraph/semanticdb_kotlinc/test/SemanticdbSymbolsTest.kt
+++ /dev/null
@@ -1,726 +0,0 @@
-package com.sourcegraph.semanticdb_kotlinc.test
-
-import com.sourcegraph.semanticdb_kotlinc.*
-import com.sourcegraph.semanticdb_kotlinc.Semanticdb.Documentation.Format
-import com.sourcegraph.semanticdb_kotlinc.Semanticdb.Language
-import com.sourcegraph.semanticdb_kotlinc.Semanticdb.SymbolOccurrence.Role
-import com.sourcegraph.semanticdb_kotlinc.test.ExpectedSymbols.SemanticdbData
-import com.sourcegraph.semanticdb_kotlinc.test.ExpectedSymbols.SymbolCacheData
-import com.tschuchort.compiletesting.SourceFile
-import kotlin.contracts.ExperimentalContracts
-import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi
-import org.junit.jupiter.api.TestFactory
-
-@ExperimentalCompilerApi
-@ExperimentalContracts
-class SemanticdbSymbolsTest {
- @TestFactory
- fun `method disambiguator`() =
- listOf(
- ExpectedSymbols(
- "Basic two methods",
- SourceFile.testKt(
- """
- |class Test {
- | fun sample() {}
- | fun sample(x: Int) {}
- |}
- |""".trimMargin()),
- symbolsCacheData =
- SymbolCacheData(
- listOf("Test#sample().".symbol(), "Test#sample(+1).".symbol()),
- )),
- ExpectedSymbols(
- "Inline class constructor",
- SourceFile.testKt(
- """
- |class Test(val x: Int)
- |""".trimMargin()),
- symbolsCacheData = SymbolCacheData(listOf("Test#``().(x)".symbol()))),
- ExpectedSymbols(
- "Inline + secondary class constructors",
- SourceFile.testKt(
- """
- |class Test(val x: Int) {
- | constructor(y: Long): this(y.toInt())
- | constructor(z: String): this(z.toInt())
- |}
- |""".trimMargin()),
- symbolsCacheData =
- SymbolCacheData(
- listOf(
- "Test#``().(x)".symbol(),
- "Test#``(+1).(y)".symbol(),
- "Test#``(+2).(z)".symbol()))),
- ExpectedSymbols(
- "Disambiguator number is not affected by different named methods",
- SourceFile.testKt(
- """
- |class Test {
- | fun sample() {}
- | fun test() {}
- | fun test(x: Int) {}
- |}
- |""".trimMargin()),
- symbolsCacheData =
- SymbolCacheData(
- listOf("Test#test().".symbol(), "Test#test(+1).".symbol()))),
- ExpectedSymbols(
- "Top level overloaded functions",
- SourceFile.testKt(
- """
- |fun test() {}
- |fun test(x: Int) {}
- |""".trimMargin()),
- symbolsCacheData =
- SymbolCacheData(listOf("test().".symbol(), "test(+1).(x)".symbol()))),
- ExpectedSymbols(
- "Annotations incl annotation type alias",
- SourceFile.testKt(
- """
- |import kotlin.contracts.ExperimentalContracts
- |import kotlin.test.Test
- |
- |@ExperimentalContracts
- |class Banaan {
- | @Test
- | fun test() {}
- |}
- |""".trimMargin()),
- symbolsCacheData =
- SymbolCacheData(
- listOf(
- "kotlin/contracts/ExperimentalContracts#".symbol(),
- "kotlin/test/Test#".symbol()))),
- // https://kotlinlang.slack.com/archives/C7L3JB43G/p1624995376114900
- /*ExpectedSymbols(
- "Method call with type parameters",
- SourceFile.testKt("""
- import org.junit.jupiter.api.io.TempDir
- val burger = LinkedHashMap()
- """),
- symbolsCacheData = SymbolCacheData(
- listOf("kotlin/collection/TypeAliasesKt#LinkedHashMap#``().".symbol())
- )
- )*/
- )
- .mapCheckExpectedSymbols()
-
-
- @TestFactory
- fun `check package symbols`() =
- listOf(
- ExpectedSymbols(
- "single component package name",
- SourceFile.testKt(
- """
- |package main
- |
- |class Test
- |""".trimMargin()),
- symbolsCacheData = SymbolCacheData(listOf("main/Test#".symbol()), 0)),
- ExpectedSymbols(
- "multi component package name",
- SourceFile.testKt(
- """
- |package test.sample.main
- |
- |class Test
- |""".trimMargin()),
- symbolsCacheData =
- SymbolCacheData(listOf("test/sample/main/Test#".symbol()), 0)),
- ExpectedSymbols(
- "no package name",
- SourceFile.testKt(
- """
- |class Test
- |""".trimMargin()),
- symbolsCacheData = SymbolCacheData(listOf("Test#".symbol()), 0)))
- .mapCheckExpectedSymbols()
-
- @TestFactory
- fun `check locals counts`() =
- listOf(
- ExpectedSymbols(
- "simple variables",
- SourceFile.testKt(
- """
- |fun test() {
- | val x = "hello"
- | println(x)
- |}
- |""".trimMargin()),
- symbolsCacheData = SymbolCacheData(localsCount = 1)))
- .mapCheckExpectedSymbols()
-
- @TestFactory
- fun `builtin symbols`() =
- listOf(
- ExpectedSymbols(
- "types",
- SourceFile.testKt(
- """
- |var x: Int = 1
- |lateinit var y: Unit
- |lateinit var z: Any
- |lateinit var w: Nothing
- |""".trimMargin()),
- symbolsCacheData =
- SymbolCacheData(
- listOf(
- "kotlin/Int#".symbol(),
- "kotlin/Unit#".symbol(),
- "kotlin/Any#".symbol(),
- "kotlin/Nothing#".symbol()))),
- ExpectedSymbols(
- "functions",
- SourceFile.testKt(
- """
- |val x = mapOf()
- |fun main() {
- | println()
- |}
- |""".trimMargin()),
- symbolsCacheData =
- SymbolCacheData(
- listOf(
- "kotlin/collections/mapOf(+2).".symbol(),
- "kotlin/io/println(+10).".symbol()))))
- .mapCheckExpectedSymbols()
-
- @TestFactory
- fun `reference expressions`() =
- listOf(
- ExpectedSymbols(
- "dot qualified expression",
- SourceFile.testKt(
- """
- |import java.lang.System
- |
- |fun main() {
- | System.err
- |}
- |""".trimMargin()),
- symbolsCacheData = SymbolCacheData(listOf("java/lang/System#err.".symbol()))))
- .mapCheckExpectedSymbols()
-
- @TestFactory
- fun `properties with getters-setters`() =
- listOf(
- ExpectedSymbols(
- "top level properties - implicit",
- SourceFile.testKt(
- """
- |var x: Int = 5
- |""".trimMargin()),
- semanticdb =
- SemanticdbData(
- expectedOccurrences =
- listOf(
- SymbolOccurrence {
- role = Role.DEFINITION
- symbol = "x."
- range {
- startLine = 0
- startCharacter = 4
- endLine = 0
- endCharacter = 5
- }
- enclosingRange {
- endCharacter = 14
- }
- },
- SymbolOccurrence {
- role = Role.DEFINITION
- symbol = "getX()."
- range {
- startLine = 0
- startCharacter = 4
- endLine = 0
- endCharacter = 5
- }
- enclosingRange {
- endCharacter = 14
- }
- },
- SymbolOccurrence {
- role = Role.DEFINITION
- symbol = "setX()."
- range {
- startLine = 0
- startCharacter = 4
- endLine = 0
- endCharacter = 5
- }
- enclosingRange {
- endCharacter = 14
- }
- })),
- ),
- ExpectedSymbols(
- "top level properties - explicit getter",
- SourceFile.testKt(
- """
- |var x: Int = 5
- | get() = field + 10
- |""".trimMargin()),
- semanticdb =
- SemanticdbData(
- expectedOccurrences =
- listOf(
- SymbolOccurrence {
- role = Role.DEFINITION
- symbol = "x."
- range {
- startLine = 0
- startCharacter = 4
- endLine = 0
- endCharacter = 5
- }
- enclosingRange {
- endLine = 1
- endCharacter = 22
- }
- },
- SymbolOccurrence {
- role = Role.DEFINITION
- symbol = "setX()."
- range {
- startLine = 0
- startCharacter = 4
- endLine = 0
- endCharacter = 5
- }
- enclosingRange {
- endLine = 1
- endCharacter = 22
- }
- },
- SymbolOccurrence {
- role = Role.DEFINITION
- symbol = "getX()."
- range {
- startLine = 1
- startCharacter = 4
- endLine = 1
- endCharacter = 7
- }
- enclosingRange {
- startLine = 1
- startCharacter = 4
- endLine = 1
- endCharacter = 22
- }
- })),
- ),
- ExpectedSymbols(
- "top level properties - explicit setter",
- SourceFile.testKt(
- """
- |var x: Int = 5
- | set(value) { field = value + 5 }
- |""".trimMargin()),
- semanticdb =
- SemanticdbData(
- expectedOccurrences =
- listOf(
- SymbolOccurrence {
- role = Role.DEFINITION
- symbol = "x."
- range {
- startLine = 0
- startCharacter = 4
- endLine = 0
- endCharacter = 5
- }
- enclosingRange {
- endLine = 1
- endCharacter = 36
- }
- },
- SymbolOccurrence {
- role = Role.DEFINITION
- symbol = "getX()."
- range {
- startLine = 0
- startCharacter = 4
- endLine = 0
- endCharacter = 5
- }
- enclosingRange {
- endLine = 1
- endCharacter = 36
- }
- },
- SymbolOccurrence {
- role = Role.DEFINITION
- symbol = "setX()."
- range {
- startLine = 1
- startCharacter = 4
- endLine = 1
- endCharacter = 7
- }
- enclosingRange {
- startLine = 1
- startCharacter = 4
- endLine = 1
- endCharacter = 36
- }
- })),
- ),
- ExpectedSymbols(
- "top level properties - explicit getter & setter",
- SourceFile.testKt(
- """
- |var x: Int = 5
- | get() = field + 10
- | set(value) { field = value + 10 }
- |""".trimMargin()),
- semanticdb =
- SemanticdbData(
- expectedOccurrences =
- listOf(
- SymbolOccurrence {
- role = Role.DEFINITION
- symbol = "x."
- range {
- startLine = 0
- startCharacter = 4
- endLine = 0
- endCharacter = 5
- }
- enclosingRange {
- endLine = 2
- endCharacter = 37
- }
- },
- SymbolOccurrence {
- role = Role.DEFINITION
- symbol = "getX()."
- range {
- startLine = 1
- startCharacter = 4
- endLine = 1
- endCharacter = 7
- }
- enclosingRange {
- startLine = 1
- startCharacter = 4
- endLine = 1
- endCharacter = 22
- }
- },
- SymbolOccurrence {
- role = Role.DEFINITION
- symbol = "setX()."
- range {
- startLine = 2
- startCharacter = 4
- endLine = 2
- endCharacter = 7
- }
- enclosingRange {
- startLine = 2
- startCharacter = 4
- endLine = 2
- endCharacter = 37
- }
- })),
- ),
- ExpectedSymbols(
- "class constructor properties",
- SourceFile.testKt(
- """
- |class Test(var sample: Int, text: String): Throwable(sample.toString()) {
- | fun test() {
- | println(sample)
- | }
- |}
- |""".trimMargin()),
- semanticdb =
- SemanticdbData(
- expectedOccurrences =
- listOf(
- SymbolOccurrence {
- role = Role.DEFINITION
- symbol = "Test#``().(sample)"
- range {
- startLine = 0
- startCharacter = 15
- endLine = 0
- endCharacter = 21
- }
- enclosingRange {
- startCharacter = 11
- endCharacter = 26
- }
- },
- SymbolOccurrence {
- role = Role.DEFINITION
- symbol = "Test#sample."
- range {
- startLine = 0
- startCharacter = 15
- endLine = 0
- endCharacter = 21
- }
- enclosingRange {
- startCharacter = 11
- endCharacter = 26
- }
- },
- SymbolOccurrence {
- role = Role.DEFINITION
- symbol = "Test#getSample()."
- range {
- startLine = 0
- startCharacter = 15
- endLine = 0
- endCharacter = 21
- }
- enclosingRange {
- startCharacter = 11
- endCharacter = 26
- }
- },
- SymbolOccurrence {
- role = Role.DEFINITION
- symbol = "Test#setSample()."
- range {
- startLine = 0
- startCharacter = 15
- endLine = 0
- endCharacter = 21
- }
- enclosingRange {
- startCharacter = 11
- endCharacter = 26
- }
- },
- SymbolOccurrence {
- role = Role.REFERENCE
- symbol = "Test#``().(sample)"
- range {
- startLine = 0
- startCharacter = 53
- endLine = 0
- endCharacter = 59
- }
- },
- SymbolOccurrence {
- role = Role.REFERENCE
- symbol = "Test#sample."
- range {
- startLine = 2
- startCharacter = 16
- endLine = 2
- endCharacter = 22
- }
- },
- SymbolOccurrence {
- role = Role.REFERENCE
- symbol = "Test#getSample()."
- range {
- startLine = 2
- startCharacter = 16
- endLine = 2
- endCharacter = 22
- }
- },
- ))))
- .mapCheckExpectedSymbols()
-
- @TestFactory
- fun `class constructors`() =
- listOf(
- ExpectedSymbols(
- "implicit primary constructor",
- SourceFile.testKt(
- """
- |class Banana
- |""".trimMargin()),
- semanticdb =
- SemanticdbData(
- expectedOccurrences =
- listOf(
- SymbolOccurrence {
- role = Role.DEFINITION
- symbol = "Banana#"
- range {
- startLine = 0
- startCharacter = 6
- endLine = 0
- endCharacter = 12
- }
- enclosingRange {
- endCharacter = 12
- }
- },
- SymbolOccurrence {
- role = Role.DEFINITION
- symbol = "Banana#``()."
- range {
- startLine = 0
- startCharacter = 6
- endLine = 0
- endCharacter = 12
- }
- enclosingRange {
- endCharacter = 12
- }
- },
- ))),
- ExpectedSymbols(
- "explicit primary constructor without keyword",
- SourceFile.testKt(
- """
- |class Banana(size: Int)
- |""".trimMargin()),
- semanticdb =
- SemanticdbData(
- expectedOccurrences =
- listOf(
- SymbolOccurrence {
- role = Role.DEFINITION
- symbol = "Banana#"
- range {
- startLine = 0
- startCharacter = 6
- endLine = 0
- endCharacter = 12
- }
- enclosingRange {
- endCharacter = 23
- }
- },
- SymbolOccurrence {
- role = Role.DEFINITION
- symbol = "Banana#``()."
- range {
- startLine = 0
- startCharacter = 6
- endLine = 0
- endCharacter = 12
- }
- enclosingRange {
- startCharacter = 12
- endCharacter = 23
- }
- },
- ))),
- ExpectedSymbols(
- "explicit primary constructor with keyword",
- SourceFile.testKt(
- """
- |class Banana constructor(size: Int)
- |""".trimMargin()),
- semanticdb =
- SemanticdbData(
- expectedOccurrences =
- listOf(
- SymbolOccurrence {
- role = Role.DEFINITION
- symbol = "Banana#"
- range {
- startLine = 0
- startCharacter = 6
- endLine = 0
- endCharacter = 12
- }
- enclosingRange {
- endCharacter = 35
- }
- },
- SymbolOccurrence {
- role = Role.DEFINITION
- symbol = "Banana#``()."
- range {
- startLine = 0
- startCharacter = 13
- endLine = 0
- endCharacter = 24
- }
- enclosingRange {
- startCharacter = 13
- endCharacter = 35
- }
- },
- ))))
- .mapCheckExpectedSymbols()
-
- @TestFactory
- fun `Single Abstract Method interface`() =
- listOf(
- ExpectedSymbols(
- "basic java.lang.Runnable",
- SourceFile.testKt(
- """
- |val x = Runnable { }.run()
- |""".trimMargin()),
- semanticdb =
- SemanticdbData(
- expectedOccurrences =
- listOf(
- SymbolOccurrence {
- role = Role.REFERENCE
- symbol = "java/lang/Runnable#"
- range {
- startLine = 0
- startCharacter = 8
- endLine = 0
- endCharacter = 16
- }
- },
- SymbolOccurrence {
- role = Role.REFERENCE
- symbol = "java/lang/Runnable#run()."
- range {
- startLine = 0
- startCharacter = 21
- endLine = 0
- endCharacter = 24
- }
- }))))
- .mapCheckExpectedSymbols()
-
- @TestFactory
- fun kdoc() =
- listOf(
- ExpectedSymbols(
- "empty kdoc line",
- SourceFile.testKt(
- """
- |/**
- |
- |hello world
- |* test content
- |*/
- |val x = ""
- |""".trimMargin()),
- semanticdb =
- SemanticdbData(
- expectedSymbols =
- listOf(
- SymbolInformation {
- symbol = "x."
- displayName = "x"
- language = Language.KOTLIN
- documentation {
- message =
- "```kotlin\npublic final val x: String\n```\n\n----\n\n\nhello world\n test content\n"
- format = Format.MARKDOWN
- }
- },
- SymbolInformation {
- symbol = "getX()."
- displayName = "x"
- language = Language.KOTLIN
- documentation {
- message =
- "```kotlin\npublic get(): String\n```\n\n----\n\n\nhello world\n test content\n"
- format = Format.MARKDOWN
- }
- }))))
- .mapCheckExpectedSymbols()
-}
diff --git a/semanticdb-kotlinc/src/test/kotlin/com/sourcegraph/semanticdb_kotlinc/test/Utils.kt b/semanticdb-kotlinc/src/test/kotlin/com/sourcegraph/semanticdb_kotlinc/test/Utils.kt
deleted file mode 100644
index 77ffc63d2..000000000
--- a/semanticdb-kotlinc/src/test/kotlin/com/sourcegraph/semanticdb_kotlinc/test/Utils.kt
+++ /dev/null
@@ -1,203 +0,0 @@
-package com.sourcegraph.semanticdb_kotlinc.test
-
-import com.sourcegraph.semanticdb_kotlinc.*
-import com.sourcegraph.semanticdb_kotlinc.AnalyzerCheckers.Companion.visitors
-import com.tschuchort.compiletesting.KotlinCompilation
-import com.tschuchort.compiletesting.SourceFile
-import io.kotest.assertions.assertSoftly
-import io.kotest.assertions.throwables.shouldNotThrowAny
-import io.kotest.matchers.collections.shouldContainInOrder
-import io.kotest.matchers.shouldBe
-import java.nio.file.Path
-import java.nio.file.Paths
-import kotlin.contracts.ExperimentalContracts
-import org.intellij.lang.annotations.Language
-import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
-import org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar
-import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi
-import org.jetbrains.kotlin.config.CompilerConfiguration
-import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
-import org.jetbrains.kotlin.fir.FirSession
-import org.jetbrains.kotlin.fir.analysis.checkers.MppCheckerKind
-import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
-import org.jetbrains.kotlin.fir.analysis.checkers.declaration.DeclarationCheckers
-import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirFileChecker
-import org.jetbrains.kotlin.fir.declarations.FirFile
-import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar
-import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrarAdapter
-import org.junit.jupiter.api.Assumptions.assumeFalse
-import org.junit.jupiter.api.DynamicTest
-import org.junit.jupiter.api.DynamicTest.dynamicTest
-
-data class ExpectedSymbols(
- val testName: String,
- val source: SourceFile,
- val symbolsCacheData: SymbolCacheData? = null,
- val semanticdb: SemanticdbData? = null
-) {
- data class SemanticdbData(
- val expectedOccurrences: List? = null,
- val expectedSymbols: List? = null
- )
-
- data class SymbolCacheData(
- val expectedGlobals: List? = null,
- val localsCount: Int? = null
- )
-}
-
-fun SourceFile.Companion.testKt(@Language("kotlin") contents: String): SourceFile =
- kotlin("Test.kt", contents)
-
-@ExperimentalCompilerApi
-@ExperimentalContracts
-fun List.mapCheckExpectedSymbols(): List =
- this.flatMap { (testName, source, symbolsData, semanticdbData) ->
- val globals = GlobalSymbolsCache(testing = true)
- val locals = LocalSymbolsCache()
- lateinit var document: Semanticdb.TextDocument
- val compilation = configureTestCompiler(source, globals, locals) { document = it }
- listOf(
- dynamicTest("$testName - compilation") {
- val result = shouldNotThrowAny { compilation.compile() }
- result.exitCode shouldBe KotlinCompilation.ExitCode.OK
- },
- dynamicTest("$testName - symbols") {
- symbolsData?.apply {
- println(
- "checking symbols: ${expectedGlobals?.size ?: 0} globals and presence of $localsCount locals")
- checkContainsExpectedSymbols(globals, locals, expectedGlobals, localsCount)
- }
- ?: assumeFalse(true)
- },
- dynamicTest("$testName - semanticdb") {
- semanticdbData?.apply {
- println(
- "checking semanticdb: ${expectedOccurrences?.size ?: 0} occurrences and ${expectedSymbols?.size ?: 0} symbols")
- checkContainsExpectedSemanticdb(document, expectedOccurrences, expectedSymbols)
- }
- ?: assumeFalse(true)
- })
- }
-
-@ExperimentalContracts
-fun checkContainsExpectedSymbols(
- globals: GlobalSymbolsCache,
- locals: LocalSymbolsCache,
- expectedGlobals: List?,
- localsCount: Int? = null
-) {
- assertSoftly(globals) { expectedGlobals?.let { this.shouldContainInOrder(it) } }
- localsCount?.also { locals.size shouldBe it }
-}
-
-@ExperimentalContracts
-fun checkContainsExpectedSemanticdb(
- document: Semanticdb.TextDocument,
- expectedOccurrences: List?,
- expectedSymbols: List?
-) {
- assertSoftly(document.occurrencesList) {
- expectedOccurrences?.let { this.shouldContainInOrder(it) }
- }
- assertSoftly(document.symbolsList) { expectedSymbols?.let { this.shouldContainInOrder(it) } }
-}
-
-@OptIn(ExperimentalCompilerApi::class)
-@ExperimentalContracts
-private fun configureTestCompiler(
- source: SourceFile,
- globals: GlobalSymbolsCache,
- locals: LocalSymbolsCache,
- hook: (Semanticdb.TextDocument) -> Unit = {}
-): KotlinCompilation {
- val compilation =
- KotlinCompilation().apply {
- sources = listOf(source)
- inheritClassPath = true
- verbose = false
- }
-
- val analyzer = semanticdbVisitorAnalyzer(globals, locals, compilation.workingDir.toPath(), hook)
- compilation.apply { compilerPluginRegistrars = listOf(analyzer) }
- return compilation
-}
-
-@OptIn(ExperimentalContracts::class)
-private class TestAnalyzerDeclarationCheckers(
- globals: GlobalSymbolsCache,
- locals: LocalSymbolsCache,
- sourceRoot: Path
-) : AnalyzerCheckers.AnalyzerDeclarationCheckers(sourceRoot) {
- override val fileCheckers: Set =
- setOf(
- object : FirFileChecker(MppCheckerKind.Common) {
- context(context: CheckerContext, reporter: DiagnosticReporter)
- override fun check(declaration: FirFile) {
- val ktFile = declaration.sourceFile ?: return
- val lineMap = LineMap(declaration)
- val visitor = SemanticdbVisitor(sourceRoot, ktFile, lineMap, globals, locals)
- visitors[ktFile] = visitor
- }
- },
- AnalyzerCheckers.SemanticImportsChecker())
-}
-
-private class TestAnalyzerCheckers(session: FirSession) : AnalyzerCheckers(session) {
- @OptIn(ExperimentalContracts::class)
- override val declarationCheckers: DeclarationCheckers
- get() =
- TestAnalyzerDeclarationCheckers(
- session.testAnalyzerParamsProvider.globals,
- session.testAnalyzerParamsProvider.locals,
- session.testAnalyzerParamsProvider.sourceroot
- )
-}
-
-@OptIn(ExperimentalContracts::class)
-class TestAnalyzerParamsProvider(
- session: FirSession,
- var globals: GlobalSymbolsCache,
- var locals: LocalSymbolsCache,
- sourceroot: Path,
-) : AnalyzerParamsProvider(session, sourceroot) {
- companion object {
- fun getFactory(
- globals: GlobalSymbolsCache,
- locals: LocalSymbolsCache,
- sourceroot: Path,
- ): Factory {
- return Factory { TestAnalyzerParamsProvider(it, globals, locals, sourceroot) }
- }
- }
-}
-
-val FirSession.testAnalyzerParamsProvider: TestAnalyzerParamsProvider by FirSession
- .sessionComponentAccessor()
-
-@OptIn(ExperimentalCompilerApi::class)
-@ExperimentalContracts
-fun semanticdbVisitorAnalyzer(
- globals: GlobalSymbolsCache,
- locals: LocalSymbolsCache,
- sourceroot: Path,
- hook: (Semanticdb.TextDocument) -> Unit = {}
-): CompilerPluginRegistrar {
- return object : CompilerPluginRegistrar() {
- override fun ExtensionStorage.registerExtensions(configuration: CompilerConfiguration) {
- FirExtensionRegistrarAdapter.registerExtension(
- object : FirExtensionRegistrar() {
- override fun ExtensionRegistrarContext.configurePlugin() {
- +TestAnalyzerParamsProvider.getFactory(globals, locals, sourceroot)
- +::TestAnalyzerCheckers
- }
- })
- IrGenerationExtension.registerExtension(
- PostAnalysisExtension(
- sourceRoot = sourceroot, targetRoot = Paths.get(""), callback = hook))
- }
-
- override val supportsK2: Boolean
- get() = true
- }
-}
diff --git a/tests/snapshots/src/main/scala/tests/MinimizedSnapshotScipGenerator.scala b/tests/snapshots/src/main/scala/tests/MinimizedSnapshotScipGenerator.scala
index 746e99435..b17dafe99 100644
--- a/tests/snapshots/src/main/scala/tests/MinimizedSnapshotScipGenerator.scala
+++ b/tests/snapshots/src/main/scala/tests/MinimizedSnapshotScipGenerator.scala
@@ -44,7 +44,6 @@ class MinimizedSnapshotScipGenerator {
sourceroot.toString(),
"--output",
scipOutput.toString,
- "--use-scip-shards",
"--targetroot",
targetroot.toString()
)