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. - -![A three stage pipeline that starts with a list of Java sources, creates a list of SemanticDB files that then become a single SCIP index.](assets/semanticdb-javac-pipeline.svg) +- 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() )