A type alias may still be inherited from. For example:
```
sealed class MyClass
typealias T = MyClass
class Inheritor : T() // `Inheritor` is a direct inheritor of `MyClass`.
```
The index is a simplified version of the IDE's
`KotlinTypeAliasByExpansionShortNameIndex`, but it should be sufficient
for virtually all cases.
^KT-66013
- The new `LLSealedInheritorsProvider` is based on the previous
sealed inheritors provider implementation in the IDE. It uses the
new direct inheritors provider and the module dependents provider to
implement the same functionality that was previously confined to the
IDE. With this design we avoid duplication of complex logic such as
the KMP handling in `searchInheritors`.
- The implementation is designed to work in the production Standalone
mode and the aforementioned services have already been implemented for
Standalone in prior commits. Now we can get rid of the problematic
`SealedClassInheritorsProviderForTests` and tests should more closely
match production behavior.
- In IDE mode tests, `LLSealedInheritorsProvider` is used with
Standalone Analysis API provider implementations. This is in line with
the rest of the test infrastructure, where Standalone AA providers are
generally used, as IDE providers aren't available.
- `KotlinSealedInheritorsProvider` is made obsolete by the common sealed
inheritors provider.
^KT-66013 fixed
^KT-64505 fixed
- We are relying on static indexing to find candidates for sealed
inheritors, hence the extension to the index.
- The direct usage of `KotlinStaticDeclarationProviderFactory` in
`KotlinStandaloneDirectInheritorsProvider` is not pretty, but a proper
design requires making the static index available as a service and
moving "static" services to the Standalone API (from AA providers).
^KT-66013
- Direct inheritors are needed to calculate sealed inheritors. The new
`KotlinDirectInheritorsProvider` can be used to implement a common
sealed inheritors provider in LL FIR.
^KT-66013
- Now that binary libraries are decompiled to stubs instead of PSI
files, we cannot collect sealed inheritors from `KtFile`s anymore.
Since all `KtFile`s and binary library stubs are both indexed by the
declaration provider, we can collect inheritors from its index
instead.
- Invalidating all sessions at the end of `prepareSealedClassInheritors`
fixes some improper resolve phases in lazy resolution test data. While
the previous implementation requested an uncached resolve session, it
didn't account for sessions of dependencies still being cached.
^KT-65960
- Instead of indexing binary library declarations from decompiled PSI,
the static declaration provider now builds and indexes stubs. As noted
in KT-65960, this brings IDE mode tests much more in line with
decompiled stubs indexing in the IDE. It should allow us to catch
issues with the stub-based deserialized symbol provider outside of IDE
tests.
- In the Standalone mode, we can skip stub-indexing completely, as we
provide FIR symbols via class-based deserialization. This also extends
to shared binary roots.
^KT-65960 fixed
- The compiler's `SealedClassInheritorsProvider` should not be exposed
outside LL FIR, as it is an internal compiler component and also
exposes `FirRegularClass`. `KotlinSealedInheritorsProvider` is an
Analysis API provider for sealed inheritors that accepts a `KtClass`
instead.
^KT-64718 fixed
Before the change, all code fragment sessions were invalidated on any
PSI change. It was a simple solution, though not very effective.
Fragments were invalidated not only on a physical module change, but
also on change in another, unrelated code fragment.
As code fragment sessions were reworked to support also ordinary '.kt'
files, the old logic started to have even less sense, because ordinary
dangling files do not have a context element (they only have a context
module).
Currently, code fragment sessions are invalidated on any change in a
physical (non-dangling) module, and are also invalidated on any change
in their contextual dangling module, if any. With some work, this can be
improved further, so code fragments will be invalidated only on changes
in modules they depend on.
Relevant tests can be found in the IntelliJ project
(DanglingFileModuleInvalidationTest).
Multifile-class can contain not only files from the same
module, but also files from the common part.
This commit also fixes KotlinByModulesResolutionScopeProvider as
it should provide all transitive dependencies
^KT-64714 Fixed
- Previously, only callable package name sets were implemented, because
the compiler cannot economically compute classifier package sets for
libraries. This has not changed. However, the K2 IntelliJ plugin and
standalone Analysis API can very easily compute classifier package
sets. Hence, this commit adds support to `FirSymbolNamesProvider` for
such sets.
- Similar to callable package sets, classifier package sets (1) improve
the memory usage of symbol names providers and (2) improve the
performance of `mayHaveTopLevelClassifier`, which is a significant
bottleneck in the IDE.
- In many cases, the package sets for callables and classifiers are the
same. For example, the IDE Kotlin declaration provider computes the
set of packages that contain any classifier and/or callable, for the
following reasons: (1) indexing package names without filtering for
declarations is much faster, (2) computing separate sets is not free
both in time and memory, and (3) the performance impact of having a
more narrow set for callables is expected to be negligible. For this
reason, `FirSymbolNamesProvider.getPackageNames` exists to provide a
shared package set.
- The `hasSpecific*PackageNamesComputation` properties are required to
avoid caching the same package set in cached symbol names providers
twice. Because these properties are constant, they can be checked very
quickly, and no time has to be wasted trying a specific package set
computation to find out whether it's supported.
### IDE Performance Results
Package set construction performance improved in the IDE in multiple
benchmarks. This improves the performance of symbol providers overall,
which has a direct impact on completion, code analysis, and Find Usages.
In a local manual run of the `intellij_commit/setUp` Find Usages
performance test, the total time spent in `getClassLikeSymbolByClassId`
improved from ~18.7s to ~11.2s. Due to parallel resolve, this does not
translate to a wall clock improvement of 7 seconds, but rather of a few
seconds.
Some performance tests improved markedly in warmup, with for example
`toolbox_enterprise/genUuid` Find Usages having an improvement in
`StubBasedFirDeserializedSymbolProvider.getClassLikeSymbolByClassId`
from 2.4s to 0.2s. This has a direct impact on the first-run performance
of the tested Find Usages command.
So far, classifier package sets in the IDE are only implemented for
libraries, and library sessions are cached after the first warmup.
Because the biggest impact of classifier package sets is avoiding
computation of "class names in package" sets, the impact of this
optimization is not accurately reflected in the timings reported by our
performance tests. `toolbox_enterprise/genUuid` above is a good example,
as the warmup timings are great, but after warmup,
`StubBasedFirDeserializedSymbolProvider.getClassLikeSymbolByClassId`
improved only from 150ms to 70ms.
`toolbox_enterprise/genUuid` is another example, as we can confidently
say that the first-run Find Usages performance has improved (which makes
a difference for the user), but it is unclear by how much, as warmup is
not measured in performance tests.
The same optimization for source sessions will be easier to measure, as
source sessions are invalidated after each performance test run. This
commit lays the groundwork for that as well, because source session
support only requires the requisite package set computation in the
IDE declaration provider to be implemented.
^KT-62553 fixed
- This is in line with the API of `FirSymbolNamesProvider`. It only
makes sense to compute package sets for source and library modules.
Also, source module package set computation in the IDE is currently
broken, so it's good to be able to return `null` in the meantime.
- This also allows the removal of the workaround for source modules in
`LLFirProviderHelper`, as the IDE declaration provider can now return
`null` itself in this case.
^KTIJ-27411
This further improves the `KotlinCompositeProvider` abstraction:
- Pulling the abstraction's interfaces outside the `impl` package allows
us to write consolidated documentation on composable Kotlin providers.
- The addition of `KotlinComposableProvider` allows more specific bounds
for the type parameters of `KotlinCompositeProvider` and
`KotlinCompositeProviderFactory`. It also clarifies to Analysis API
implementors which providers can be composed at all, as providers like
`KotlinDeclarationProvider` extend this interface.
- `KotlinComposableProviderMerger` provides a unified interface for
provider mergers.
^KT-61791
- In parallel to Kotlin declaration provider merging, we need a proper
merging strategy for package providers as well, because resolve
extensions may define additional package providers.
- Additionally, other non-scope-based package providers may be added in
the future, and the merger preserves these out of the box.
^KT-61791
- Composite declaration providers and declaration provider mergers are
extremely similar to the composite package providers and (newly to be
implemented) package provider mergers. This commit extracts the common
parts into a `KotlinCompositeProviderFactory`.
^KT-61791
- Module state modification events now have a modification kind, which
allows adding additional kinds of modification in the future (e.g.
separating module property and content root updates, if the workspace
model ever supports it).
- Splitting off modification kinds was also a good opportunity to
better document when module state modification occurs.
- Changed the documentation of modification events to be (1) less
reliant on the IDE implementation and (2) more detailed in the
intended effect of each event.
- Removed the notion of "stable" modules again and replaced it with
"libraries". Even though an SDK is technically not a library, the
term "library modules" should be more friendly to API consumers.
- Specifically for non-global module state modification, we can
guarantee that the event is published before the module is modified.
This allows subscribers to use the provided `KtModule` without needing
to fear that the module has already been disposed (in case of
removal).
- Out-of-block modification of stable modules is meaningless, because
stable modules should not be affected by out-of-block modification.
Hence, only global *source* out-of-block modification makes sense and
global out-of-block modification events can be removed.
- Any time an anchor module's session is invalidated, we need to make
sure that all its dependents are also invalidated. Because those
dependents may include libraries, invalidation after global source
modification needs to invalidate such libraries, even if other stable
module sessions should remain untouched.
- `LLFirSessionCache.sourceCache` can contain library and library
sources sessions. However, global source modification events should
not remove such sessions from the cache, because they belong to
"stable" modules.
- The commit introduces the term "stable module" as a combined term for
binary and library source modules. Library sources are not binaries,
but nevertheless they cannot cause nor be affected by out-of-block
modification.
- The term "source modules" as used by global "source" modification is
slightly imprecise, as it does *not* include library sources, but for
the sake of conciseness, I don't plan to change that.
- Kotlin modification events are published before or after the
modification, depending on the underlying cause. For example, PSI tree
change events in the IDE can occur both before and after the change,
so module and global out-of-block modification events must not make
timing guarantees. Similarly, module state modification events
published by the IDE can both happen before and after a module change,
with most events happening before the change.
- The commit refactors some modification trackers previously provided by
`KotlinModificationTrackerFactory` to a subscription-directed
mechanism implemented via `MessageBus` and `KotlinTopics`. The
following modification trackers are affected:
- Module out-of-block modification: The FE10 Analysis API doesn't use
these modification trackers, so the effect is limited to LL FIR.
- Module state: Likewise, FE10 doesn't use this modification tracker,
so again the effect is limited to LL FIR.
- Project and library modifications trackers remain, because many small
objects in e.g. light classes depend on these trackers (making
listener management unfeasible), and they are not relevant for
`LLFirSession` invalidation.
- This new API paves the way for a session invalidation service to
subscribe to out-of-block and module state changes as events. This
removes the need to iterate through modification trackers.
- Also note that the out-of-block modification provided by the new
subscription mechanism is intended to work for _any_ `KtModule`, as
long as it makes sense to have out-of-block modifications. For
example, the subscription should work for script modules. The OOB
modification tracker previously only supported `KtSourceModule`s,
which required separate single-file modification trackers for script
and not-under-content-root modules.
- `MessageBus` is a general utility provided by IntelliJ, but usually it
is retrieved from `project`. To keep these two concerns decoupled,
`project.messageBus` should not be used directly. Instead, the commit
adds a `KotlinMessageBusProvider`, which for now just provides the
project message bus with its standard implementation, but allows
swapping out the message bus implementation later.
- Global changes are also supported by the new subscription API. Such
global changes may for example occur during cache invalidation in
tests, on global PSI tree changes, or when an SDK is removed.
- Test-only invalidation has been moved from
`KotlinModificationTrackerFactory` to a new
`KotlinGlobalModificationService`. This creates one central service
for invalidation between tests, which is easier from an implementation
and a usage perspective than calling multiple scattered services and
providers.
Use existing stub-based JVM library symbol provider for .knm and
.kotlin_metadata files. The only real difference is the scope filtering
by file types
KT-58769
- A proper merging strategy for declaration providers is required for
cases where the main declaration provider created by
`createDeclarationProvider` can't provide all declarations that the
original declaration providers can provide. Then, only a sublist of
the declaration providers should be merged, while keeping the
unmergeable declaration providers intact.
^KT-58580 fixed
this gives the following benefits:
1. no protobuf in memory, all data is already present in stubs
2. given that symbol provider for libraries is already stub based,
we can get rid of complicated code to find source psi by deserialized fir
3. it's also possible to reduce number of index access,
when fir is requested for given ktElement