Skip to content

Kotlin 2 #97

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 54 commits into from
Jun 30, 2025
Merged

Kotlin 2 #97

merged 54 commits into from
Jun 30, 2025

Conversation

nicolas-guichard
Copy link
Contributor

@nicolas-guichard nicolas-guichard commented Apr 11, 2025

This is based on https://github.com/slycodemonkey/scip-kotlin, which replaces the AnalysisHandlerExtension with a FirAdditionalCheckersExtension to collect declarations and expressions and an IrGenerationExtension to analyze them (at the FIR level) after everything has been resolved.

Fixes #82

Test plan

All existing tests pass (or were slightly adapted, eg. class and method signatures).

This is currently used in a test instance of Searchfox at https://kdab.searchfox.org/mozilla-central/source, see eg. https://kdab.searchfox.org/mozilla-central/source/mobile/android/android-components/components/browser/domains/src/main/java/mozilla/components/browser/domains/Domain.kt.
This means the output is accepted by scip-java index-semanticdb then Searchfox's scip-indexer on the whole Firefox for Android code base.

New tests were added based on my testing with Searchfox.

nicolas-guichard and others added 30 commits March 21, 2025 19:47
Run `gradle splotlessApply`, remove trailing whitesepace and file endings.
Also remove the in-repo copy of Semanticdb.java (it is autogenerated by
protoc outside the source tree), and the unused krotoconfig.json, but
keep the SemanticdbBuilders.kt that used to be autogenerated by Kroto+.
This was deprecated in Kotlin 1.9 already.
We don't want the output schema to change, otherwise we're not
outputing SemanticDB anymore and downstream tooling like scip-java
reject our output.

We start with 24 failing tests to fix.
According to existing tests, exceptions in the semanticdb-kotlinc
plugin should not propagate to the host.

Down to 23 failing tests.
We want the symbol for a class to be package/package/Class#, instead
this gave us `package/package/Class`#.

Now we have just Class#, packages will be added back in a future
commit.

The display name should just be the simple class name.

Still 23 failing tests.
This:
- Adds special handling for packages, which are represented by FqName
  rather than PackageDescriptor.

  → This gives package/package/Class# instead of just Class#.

- Removes FileKt# fake classes for toplevel methods and properties
  These don't appear in imports, so I don't think they are wanted.
  This changes the test expectations, but remains SemanticDB-compliant.

  → This gives kotlin/io/println(). instead of kotlin/io/ConsoleKt#println().

Down to 19 failing tests.
SemanticDB and SCIP don't disambiguate methods by adding their full
signature, instead they sort overloaded methods by declaration order,
the first one is `method().`, the next one is `method(+1).`, the next
one `method(+2).` etc.

Down to 14 failing tests.
Identify getters/setters using FirPropertyAccessorSymbol.isGetter and
FirPropertyAccessorSymbol.isSetter instead of doing string comparisons.

Still 14 failing tests.
Down to 13 failing tests.
Constructors should have the special `<init>`(). symbol.

Down to 11 failing tests.
We are generally interested in the identifier for each item, but
sometimes want some other token instead, such as the constructor, get
or set keywords.

Down to 5 failing tests.
We need to support the package directive and the imports so that:
- `package a.b.c` has 3 symbol occurences, for `a/`, `a/b/` and `a/b/c/`
- `import a.b.Klass` has 3 symbol occurences, for `a/`, `a/b/` and
  `a/b/Klass#`

Still 5 failing tests.
Down to 4 failing tests.
We don't want all types to list Any as their supertype.

Down to 3 failings tests.
When we have a reference to a FirPropertySymbol, it is not obvious if
it is used as a read or a write. Emit both just in case.

No more failing tests!
Class overriddenSymbols were not pointing to actual symbols.

Function overriddenSymbols were missing.
We don't want to call all anonymous objects <anonymous>, or we can't
desambiguate between two anonymous objects from the same package.

We emit anonymous objects just like regular classes, but take care to
point at the `object` keyword for the class and primary constructor.
This was caught by Searchfox's scip-indexer, which would error out:
```
[ERROR scip_indexer] InvalidLocalSymbol("local 84getCurrentIndex().")
```
@nicolas-guichard nicolas-guichard mentioned this pull request Apr 11, 2025
@antonsviridov-src
Copy link
Contributor

@nicolas-guichard I've done some testing of this new implementation and I can confirm that it works in the wider scip-java project (I'm not finished yet).

One thing that popped out is that using Kotlin 2.2. with this scip-kotlin PR crashes:

Caused by: java.lang.NoSuchMethodError: 'void org.jetbrains.kotlin.fir.renderer.FirRenderer.<init>(java.lang.StringBuilder, org.jetbrains.kotlin.fir.renderer.FirAnnotationRenderer, org.jetbrains.kotlin.fir.renderer.FirBodyRenderer, org.jetbrains.kotlin.fir.renderer.FirCallArgumentsRenderer, org.jetbrains.kotlin.fir.renderer.FirClassMemberRenderer, org.jetbrains.kotlin.fir.contracts.description.ConeContractRenderer, org.jetbrains.kotlin.fir.renderer.FirDeclarationRenderer, org.jetbrains.kotlin.fir.renderer.ConeIdRenderer, org.jetbrains.kotlin.fir.renderer.FirModifierRenderer, org.jetbrains.kotlin.fir.renderer.FirPackageDirectiveRenderer, org.jetbrains.kotlin.fir.renderer.FirPropertyAccessorRenderer, org.jetbrains.kotlin.fir.renderer.FirResolvePhaseRenderer, org.jetbrains.kotlin.fir.renderer.ConeTypeRenderer, org.jetbrains.kotlin.fir.renderer.FirSymbolRenderer, org.jetbrains.kotlin.fir.renderer.FirCallableSignatureRenderer, org.jetbrains.kotlin.fir.renderer.FirErrorExpressionRenderer, org.jetbrains.kotlin.fir.renderer.FirResolvedNamedReferenceRenderer, org.jetbrains.kotlin.fir.renderer.FirResolvedQualifierRenderer, org.jetbrains.kotlin.fir.renderer.FirGetClassCallRenderer, org.jetbrains.kotlin.fir.renderer.FirSupertypeRenderer, boolean, boolean, boolean, int, kotlin.jvm.internal.DefaultConstructorMarker)'
        at com.sourcegraph.semanticdb_kotlinc.SemanticdbTextDocumentBuilder.semanticdbDocumentation(SemanticdbTextDocumentBuilder.kt:148)
        at com.sourcegraph.semanticdb_kotlinc.SemanticdbTextDocumentBuilder.symbolInformation-g6pMfKI(SemanticdbTextDocumentBuilder.kt:96)
        at com.sourcegraph.semanticdb_kotlinc.SemanticdbTextDocumentBuilder.emitSemanticdbData-iN-jdH8(SemanticdbTextDocumentBuilder.kt:61)
        at com.sourcegraph.semanticdb_kotlinc.SemanticdbVisitor.emitAll$lambda$0(SemanticdbVisitor.kt:42)
        at kotlin.sequences.SequencesKt___SequencesKt.onEach$lambda$14$SequencesKt___SequencesKt(_Sequences.kt:2287)
        at kotlin.sequences.TransformingSequence$iterator$1.next(Sequences.kt:243)
        at kotlin.sequences.TransformingSequence$iterator$1.next(Sequences.kt:243)
        at kotlin.sequences.SequencesKt___SequencesKt.toList(_Sequences.kt:819)
        at com.sourcegraph.semanticdb_kotlinc.SemanticdbVisitor.emitAll(SemanticdbVisitor.kt:45)
        at com.sourcegraph.semanticdb_kotlinc.SemanticdbVisitor.visitClassOrObject(SemanticdbVisitor.kt:63)
        at com.sourcegraph.semanticdb_kotlinc.AnalyzerCheckers$SemanticClassLikeChecker.check(AnalyzerCheckers.kt:185)
        at com.sourcegraph.semanticdb_kotlinc.AnalyzerCheckers$SemanticClassLikeChecker.check(AnalyzerCheckers.kt:167)
        at org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirDeclarationChecker.check(FirDeclarationChecker.kt:22)
        at org.jetbrains.kotlin.fir.analysis.checkers.declaration.DeclarationCheckersDiagnosticComponent.visitRegularClass(DeclarationCheckersDiagnosticComponent.kt:275)
        at org.jetbrains.kotlin.fir.analysis.checkers.declaration.DeclarationCheckersDiagnosticComponent.visitRegularClass(DeclarationCheckersDiagnosticComponent.kt:25)
        at org.jetbrains.kotlin.fir.declarations.FirRegularClass.accept(FirRegularClass.kt:49)
        at org.jetbrains.kotlin.fir.analysis.collectors.CheckerRunningDiagnosticCollectorVisitor.checkElement(CheckerRunningDiagnosticCollectorVisitor.kt:24)
        at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.access$checkElement(AbstractDiagnosticCollectorVisitor.kt:30)
        at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitRegularClass(AbstractDiagnosticCollectorVisitor.kt:606)
        at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitRegularClass(AbstractDiagnosticCollectorVisitor.kt:30)
        at org.jetbrains.kotlin.fir.declarations.FirRegularClass.accept(FirRegularClass.kt:49)
        at org.jetbrains.kotlin.fir.declarations.impl.FirFileImpl.acceptChildren(FirFileImpl.kt:58)
        at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitNestedElements(AbstractDiagnosticCollectorVisitor.kt:38)
        at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitFile(AbstractDiagnosticCollectorVisitor.kt:1288)
        at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitFile(AbstractDiagnosticCollectorVisitor.kt:30)
        at org.jetbrains.kotlin.fir.declarations.FirFile.accept(FirFile.kt:43)
        at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollector.collectDiagnostics(AbstractDiagnosticCollector.kt:36)
        at org.jetbrains.kotlin.fir.pipeline.AnalyseKt.runCheckers(analyse.kt:37)
        ... 26 more

e: Compiler terminated with internal error

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':compileKotlin'.
> A failure occurred while executing org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers$GradleKotlinCompilerWorkAction
   > Internal compiler error. See log for more details

In the past my understanding was that our usages of kotlin compiler APIs were prone to breaking with every minor version – this makes versioning strategy quite annoying.

Questions for you:

  1. Is this still the case in Kotlin 2? Or can we avoid it?
  2. What is the consensus in Kotlin community about minor upgrades – do people execute them fairly quickly or do people stick to the chosen minor for a long time?
    With the breakages I'm observing I would expect plugins to suffer greatly between minors...

@nicolas-guichard
Copy link
Contributor Author

Is this [breaking on every Kotlin version] still the case in Kotlin 2? Or can we avoid it?

Yes it's unfortunately still the case. The fix for that would be to rewrite the indexer using the Kotlin Analysis API, which “offers both source and binary backward compatibility for its stable parts”. But:

  • it's only available to IntelliJ plugins and experimentally for standalone CLI utilities, ie not to compiler plugins
  • it would mean an almost complete rewrite

Since making this PR I have upgraded to Kotlin 2.2.0 in https://github.com/mozsearch/semanticdb-kotlinc/tree/kotlin-2.2.0, along with an extra bugfix for type operators (as as? is !is).

What is the consensus in Kotlin community about minor upgrades – do people execute them fairly quickly or do people stick to the chosen minor for a long time?

I can't really tell about the broader community, since I'm not really a Kotlin dev either ;-)
In the specific case of Firefox for Android, there's definitely interest in staying up-to-date.

With the breakages I'm observing I would expect plugins to suffer greatly between minors...

We use semanticdb-kotlinc and semanticdb-javac as part of the Firefox build process (not through scip-java), so bumping the indexer plugin along with the Kotlin version isn't too much of an issue for us. I can see how this makes running scip-java index on a random Kotlin project more complicated though…

@antonsviridov-src
Copy link
Contributor

Thanks for you responses, that's very useful!

it's only available to IntelliJ plugins and experimentally for standalone CLI utilities, ie not to compiler plugins

Yeah, that's a deal breaker then. Shame.

Since making this PR I have upgraded to Kotlin 2.2.0 in https://github.com/mozsearch/semanticdb-kotlinc/tree/kotlin-2.2.0, along with an extra bugfix for type operators (as as? is !is).

Nice, hopefully we can bring those changes in soon (to the next minor).

We use semanticdb-kotlinc and semanticdb-javac as part of the Firefox build process (not through scip-java),

Nice! How has your usage been so far? Gradle aggressively resists automatic injection of plugins or dependencies, so adding semanticdb plugins manually to the build is likely out future recommendation as I'm tired of fighting with Gradle.

@nicolas-guichard
Copy link
Contributor Author

Nice! How has your usage been so far? Gradle aggressively resists automatic injection of plugins or dependencies, so adding semanticdb plugins manually to the build is likely out future recommendation as I'm tired of fighting with Gradle.

The initial implementation wasn't too hard: it was mainly a case of conditionally adding the compiler plugins to the build when the build is meant for indexing, and making sure that everything gets built even though we were not running the tests. The biggest issue we've had is that we lost Kotlin indexing when migrating to Kotlin 2 while I figured things out, and we didn't know what plans you had in #82.

For a quick overview of our setup:

  1. the Firefox CI builds the android-gradle-dependencies mozconfig to download, lock and cache our Gradle dependencies, including semanticdb-kotlinc and semanticdb-javac
  2. the Firefox builder builds the android-aarch64/debug-searchfox mozconfig with the --enable-mozsearch-plugin flag, which is eventually picked up by our build.gradle to enable the indexer plugins
  3. the semanticdb files are archived and uploaded as CI artifacts
  4. a mozsearch indexer instance eventually downloads the semanticdb artifacts and turns them into SCIP using scip-java
  5. that SCIP is turned into mozsearch's custom JSON-based analysis format by scip-indexer
  6. that's eventually turned into static HTML and used for dynamic queries like class and call diagrams

@antonsviridov-src antonsviridov-src merged commit 09d1d97 into sourcegraph:main Jun 30, 2025
6 checks passed
@antonsviridov-src
Copy link
Contributor

Thank you for this gargantuan amount of work @nicolas-guichard!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Kotlin 2.x support
2 participants