For the following example, when we run the reference shortener, it
drops `a.b.c` qualifier, because it matches "FOURTH".
```
package a.b.c
fun <T, E, D> foo(a: T, b: E, c: D) = a.hashCode() + b.hashCode() + c.hashCode() // FIRST
fun <E> E.foo() = hashCode() // SECOND
object Receiver {
fun <T, E, D> foo(a: T, b: E, c: D) = a.hashCode() + b.hashCode() + c.hashCode() // THIRD
fun foo(a: Int, b: Boolean, c: String) = a.hashCode() + b.hashCode() + c.hashCode() // FOURTH
fun test(): Int {
fun foo(a: Int, b: Boolean, c: Int) = a + b.hashCode() + c // FIFTH
return <expr>a.b.c.foo(1, false, "bar")</expr>
}
}
```
As shown in the above example, when SHORTEN_IF_ALEADY_IMPORTED option is
given from a user, the reference shortener has to check whether it can
drop the qualifier without changing the referenced symbol and if it is
possible to do that without adding a new import directive, it deletes
the qualifier.
It needs two steps:
1. Collect all candidate symbols matching the signature e.g., function
arguments / type arguments
2. Determine whether the referenced symbol has the highest reference
priority when we drops the qualifier depending on scopes
This commit uses `AllCandidatesResolver(shorteningContext.analysisSession.useSiteSession).
getAllCandidates( .. fake FIR call/property-access ..)` for step1.
For step2, we use a heuristic based on scopes of candidates. If a
candidate symbol is under the same scope with the target expression, it
has a `FirLocalScope` which has the high priority. So when we have a
candidate under a `FirLocalScope` and the actual referenced symbol is
different from the candidate, we must avoid dropping its qualifier
because the shortening will change its semantics i.e., reference.
The order of scopes depending on their scope types is:
1. FirLocalScope
2. FirClassUseSiteMemberScope / FirNestedClassifierScope
3. FirExplicitSimpleImportingScope
4. FirPackageMemberScope
5. others
Note that for "others" the above rule can be wrong. Please update it if
you find other scopes that have a priority higher than the specified
scopes.
One of non-trivial parts is the priority among multiple
FirClassUseSiteMemberScope and FirNestedClassifierScope. They are
basically scopes for class declarations. We decide their priorities
based on the distance of class declaration from the target expression.
Note that we take a strict approach to reject all false positive. For
example, when we are not sure, we don't shorten it to avoid changing its
semantics.
TODO: One corner case is handling receivers. We have to update
```
private fun shortenIfAlreadyImported(
firQualifiedAccess: FirQualifiedAccess,
calledSymbol: FirCallableSymbol<*>,
expressionInScope: KtExpression,
): Boolean
```
The current implementation cannot handle the following example:
```
package foo
class Foo {
fun test() {
// It references FIRST. Removing `foo` lets it reference SECOND.
<caret>foo.myRun {
42
}
}
}
inline fun <R> myRun(block: () -> R): R = block() // FIRST
inline fun <T, R> T.myRun(block: T.() -> R): R = block() // SECOND
```
Tests related to TODO:
- analysis/analysis-api/testData/components/referenceShortener/referenceShortener/receiver2.kt
- analysis/analysis-api/testData/components/referenceShortener/referenceShortener/receiver3.kt
FirReferenceResolveHelper internally checks whether the referenced class
id matches the qualifed access or not. If they do not match, it reports
an error. When the companion object has the same name as the class,
resolving a qualified expression access to a member of the companion
object causes an error because of the mismatch e.g.,
```
package my.sample
class Test {
fun a() {
my.sample.<caret>Test.say()
}
companion object Test {
fun say() {}
}
}
```
This commit fixes the issue.
TODO: When the companion object has a name difference from class, it
does not report an error but the resolution result is wrong in FIR. See
KT-56167.
---
Commentary from rebaser: the issue mentioned in this code is
fixed in 71a368e06e, so the actual
fix is omitted, and only test data is preserved
They are disabling error for using JS/Legacy backend via System property
by passing it to the Kotlin Daemon. But daemon may already be started
and not contain such property.
Workaround for now is to use 'in-process' compilation for such cases.
Source mapping URL is not saved in JS module cache anymore,
because it was a wrong url pointed to the source map from the cache.
Instead, the URL is appended every time on writing the output JS code.
^KT-56469 Fixed
Avoid assumption there, that for accessors we have a containing property
in the context
This necessary as a part of getting rid context modifications in checkers
element !is FirMemberDeclaration means that
`isAccessorWithSameVisibility == false` because
`FirPropertyAccessor <: FirMemberDeclaration`
Also, remove a function became unused
Now KtFirScopeProvider do not contain caches, so it can be reused between the threads. The only thread-local thing it had, was a ScopeSession. Now it's not stored in the KtFirScopeProvider
Not fully initialized DeclarationDescriptor could leak to bindingTrace.
It is hard to detect the outcome of that kind of leakage due to
possibility to have not fully initialized instances if PCE happens in
the middle of publication and instance full initialization.
#KT-56364
#KT-56388
#KT-54085
#KT-55150
SimpleFunctionDescriptorImpl initialization consists of two phases:
ctor + initialize.
When SimpleFunctionDescriptorImpl is created wrapped descriptor
(e.g. ValueParameterDescriptorImpl) is leaked through bindingTrace
with not fully initialized `containingDeclaration` (that is
SimpleFunctionDescriptorImpl).
If PCE happens after this unsafe publication prior to `initialize` then
it will be case with NPE on fully initialized instance reading.
#KT-56364 Fixed
PropertyDescriptorImpl initialization consists of two phases:
ctor + setType.
When PropertyDescriptorImpl is created wrapped descriptor
(e.g. WithDestructuringDeclaration) is leaked through bindingTrace
with not fully initialized `containingDeclaration` (that is
PropertyDescriptorImpl).
If PCE happens after this unsafe publication prior to `setType` then it
will be case with NPE on fully initialized instance reading.
#KT-56388 Fixed
Because SerializationJvmIrIntrinsicSupport does not instantiate annotations yet,
this info could be lost. As a workaround, it is possible to call Companion.serializer()
functions instead of direct serializer instantiation, as they are plugin-generated
and correctly handle annotations.
Note that for some cases (enums & interfaces) this WA is not enough, so additional work
is needed later.
Fixes https://github.com/Kotlin/kotlinx.serialization/issues/2179