[FIR IDE] Correctly check declarations' modalities in SamResolver
Modality might be null before the declaration itself is resolved to `STATUS` ^KT-52667 Fixed
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
package foo
|
||||
|
||||
class Arg
|
||||
|
||||
fun interface Foo {
|
||||
fun foo(a: Arg): Arg
|
||||
}
|
||||
|
||||
fun testMe(f: Foo) {}
|
||||
|
||||
fun resolveMe() {
|
||||
testMe { b -> b }
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
|
||||
RAW_FIR:
|
||||
FILE: lambdaAsSAMInterface.kt
|
||||
public? final? [RAW_FIR] class Arg : R|kotlin/Any| {
|
||||
public? [RAW_FIR] [ContainingClassKey=Arg] constructor(): R|foo/Arg| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
}
|
||||
public? final? fun [RAW_FIR] interface Foo : R|kotlin/Any| {
|
||||
public? final? [RAW_FIR] fun foo([RAW_FIR] a: Arg): Arg
|
||||
|
||||
}
|
||||
public? final? [RAW_FIR] fun testMe([RAW_FIR] f: Foo): R|kotlin/Unit| { LAZY_BLOCK }
|
||||
public? final? [RAW_FIR] fun resolveMe(): R|kotlin/Unit| { LAZY_BLOCK }
|
||||
|
||||
IMPORTS:
|
||||
FILE: lambdaAsSAMInterface.kt
|
||||
public? final? [RAW_FIR] class Arg : R|kotlin/Any| {
|
||||
public? [RAW_FIR] [ContainingClassKey=Arg] constructor(): R|foo/Arg| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
}
|
||||
public? final? fun [RAW_FIR] interface Foo : R|kotlin/Any| {
|
||||
public? final? [RAW_FIR] fun foo([RAW_FIR] a: Arg): Arg
|
||||
|
||||
}
|
||||
public? final? [RAW_FIR] fun testMe([RAW_FIR] f: Foo): R|kotlin/Unit| { LAZY_BLOCK }
|
||||
public? final? [RAW_FIR] fun resolveMe(): R|kotlin/Unit| { LAZY_BLOCK }
|
||||
|
||||
COMPILER_REQUIRED_ANNOTATIONS:
|
||||
FILE: lambdaAsSAMInterface.kt
|
||||
public? final? [RAW_FIR] class Arg : R|kotlin/Any| {
|
||||
public? [RAW_FIR] [ContainingClassKey=Arg] constructor(): R|foo/Arg| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
}
|
||||
public? final? fun [RAW_FIR] interface Foo : R|kotlin/Any| {
|
||||
public? final? [RAW_FIR] fun foo([RAW_FIR] a: Arg): Arg
|
||||
|
||||
}
|
||||
public? final? [RAW_FIR] fun testMe([RAW_FIR] f: Foo): R|kotlin/Unit| { LAZY_BLOCK }
|
||||
public? final? [COMPILER_REQUIRED_ANNOTATIONS] fun resolveMe(): R|kotlin/Unit| { LAZY_BLOCK }
|
||||
|
||||
COMPANION_GENERATION:
|
||||
FILE: lambdaAsSAMInterface.kt
|
||||
public? final? [RAW_FIR] class Arg : R|kotlin/Any| {
|
||||
public? [RAW_FIR] [ContainingClassKey=Arg] constructor(): R|foo/Arg| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
}
|
||||
public? final? fun [RAW_FIR] interface Foo : R|kotlin/Any| {
|
||||
public? final? [RAW_FIR] fun foo([RAW_FIR] a: Arg): Arg
|
||||
|
||||
}
|
||||
public? final? [RAW_FIR] fun testMe([RAW_FIR] f: Foo): R|kotlin/Unit| { LAZY_BLOCK }
|
||||
public? final? [COMPANION_GENERATION] fun resolveMe(): R|kotlin/Unit| { LAZY_BLOCK }
|
||||
|
||||
SUPER_TYPES:
|
||||
FILE: lambdaAsSAMInterface.kt
|
||||
public? final? [RAW_FIR] class Arg : R|kotlin/Any| {
|
||||
public? [RAW_FIR] [ContainingClassKey=Arg] constructor(): R|foo/Arg| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
}
|
||||
public? final? fun [RAW_FIR] interface Foo : R|kotlin/Any| {
|
||||
public? final? [RAW_FIR] fun foo([RAW_FIR] a: Arg): Arg
|
||||
|
||||
}
|
||||
public? final? [RAW_FIR] fun testMe([RAW_FIR] f: Foo): R|kotlin/Unit| { LAZY_BLOCK }
|
||||
public? final? [SUPER_TYPES] fun resolveMe(): R|kotlin/Unit| { LAZY_BLOCK }
|
||||
|
||||
TYPES:
|
||||
FILE: lambdaAsSAMInterface.kt
|
||||
public? final? [RAW_FIR] class Arg : R|kotlin/Any| {
|
||||
public? [RAW_FIR] [ContainingClassKey=Arg] constructor(): R|foo/Arg| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
}
|
||||
public? final? fun [RAW_FIR] interface Foo : R|kotlin/Any| {
|
||||
public? final? [RAW_FIR] fun foo([RAW_FIR] a: Arg): Arg
|
||||
|
||||
}
|
||||
public? final? [RAW_FIR] fun testMe([RAW_FIR] f: Foo): R|kotlin/Unit| { LAZY_BLOCK }
|
||||
public? final? [TYPES] fun resolveMe(): R|kotlin/Unit| { LAZY_BLOCK }
|
||||
|
||||
STATUS:
|
||||
FILE: lambdaAsSAMInterface.kt
|
||||
public? final? [RAW_FIR] class Arg : R|kotlin/Any| {
|
||||
public? [RAW_FIR] [ContainingClassKey=Arg] constructor(): R|foo/Arg| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
}
|
||||
public? final? fun [RAW_FIR] interface Foo : R|kotlin/Any| {
|
||||
public? final? [RAW_FIR] fun foo([RAW_FIR] a: Arg): Arg
|
||||
|
||||
}
|
||||
public? final? [RAW_FIR] fun testMe([RAW_FIR] f: Foo): R|kotlin/Unit| { LAZY_BLOCK }
|
||||
public final [STATUS] fun resolveMe(): R|kotlin/Unit| { LAZY_BLOCK }
|
||||
|
||||
ARGUMENTS_OF_ANNOTATIONS:
|
||||
FILE: lambdaAsSAMInterface.kt
|
||||
public? final? [RAW_FIR] class Arg : R|kotlin/Any| {
|
||||
public? [RAW_FIR] [ContainingClassKey=Arg] constructor(): R|foo/Arg| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
}
|
||||
public? final? fun [RAW_FIR] interface Foo : R|kotlin/Any| {
|
||||
public? final? [RAW_FIR] fun foo([RAW_FIR] a: Arg): Arg
|
||||
|
||||
}
|
||||
public? final? [RAW_FIR] fun testMe([RAW_FIR] f: Foo): R|kotlin/Unit| { LAZY_BLOCK }
|
||||
public final [ARGUMENTS_OF_ANNOTATIONS] fun resolveMe(): R|kotlin/Unit| { LAZY_BLOCK }
|
||||
|
||||
CONTRACTS:
|
||||
FILE: lambdaAsSAMInterface.kt
|
||||
public? final? [RAW_FIR] class Arg : R|kotlin/Any| {
|
||||
public? [RAW_FIR] [ContainingClassKey=Arg] constructor(): R|foo/Arg| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
}
|
||||
public? final? fun [RAW_FIR] interface Foo : R|kotlin/Any| {
|
||||
public? final? [RAW_FIR] fun foo([RAW_FIR] a: Arg): Arg
|
||||
|
||||
}
|
||||
public? final? [RAW_FIR] fun testMe([RAW_FIR] f: Foo): R|kotlin/Unit| { LAZY_BLOCK }
|
||||
public final [CONTRACTS] fun resolveMe(): R|kotlin/Unit| {
|
||||
testMe#(<L> = [STATUS] testMe@fun <implicit>.<anonymous>([RAW_FIR] b: <implicit>): <implicit> <inline=Unknown> {
|
||||
b#
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
IMPLICIT_TYPES_BODY_RESOLVE:
|
||||
FILE: lambdaAsSAMInterface.kt
|
||||
public? final? [RAW_FIR] class Arg : R|kotlin/Any| {
|
||||
public? [RAW_FIR] [ContainingClassKey=Arg] constructor(): R|foo/Arg| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
}
|
||||
public? final? fun [RAW_FIR] interface Foo : R|kotlin/Any| {
|
||||
public? final? [RAW_FIR] fun foo([RAW_FIR] a: Arg): Arg
|
||||
|
||||
}
|
||||
public? final? [RAW_FIR] fun testMe([RAW_FIR] f: Foo): R|kotlin/Unit| { LAZY_BLOCK }
|
||||
public final [IMPLICIT_TYPES_BODY_RESOLVE] fun resolveMe(): R|kotlin/Unit| {
|
||||
testMe#(<L> = [STATUS] testMe@fun <implicit>.<anonymous>([RAW_FIR] b: <implicit>): <implicit> <inline=Unknown> {
|
||||
b#
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
EXPECT_ACTUAL_MATCHING:
|
||||
FILE: lambdaAsSAMInterface.kt
|
||||
public? final? [RAW_FIR] class Arg : R|kotlin/Any| {
|
||||
public? [RAW_FIR] [ContainingClassKey=Arg] constructor(): R|foo/Arg| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
}
|
||||
public? final? fun [RAW_FIR] interface Foo : R|kotlin/Any| {
|
||||
public? final? [RAW_FIR] fun foo([RAW_FIR] a: Arg): Arg
|
||||
|
||||
}
|
||||
public? final? [RAW_FIR] fun testMe([RAW_FIR] f: Foo): R|kotlin/Unit| { LAZY_BLOCK }
|
||||
public final [EXPECT_ACTUAL_MATCHING] fun resolveMe(): R|kotlin/Unit| {
|
||||
testMe#(<L> = [STATUS] testMe@fun <implicit>.<anonymous>([RAW_FIR] b: <implicit>): <implicit> <inline=Unknown> {
|
||||
b#
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
BODY_RESOLVE:
|
||||
FILE: lambdaAsSAMInterface.kt
|
||||
public? final? [COMPILER_REQUIRED_ANNOTATIONS] class Arg : R|kotlin/Any| {
|
||||
public? [COMPILER_REQUIRED_ANNOTATIONS] [ContainingClassKey=Arg] constructor(): R|foo/Arg| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
}
|
||||
public abstract fun [SUPER_TYPES] interface Foo : R|kotlin/Any| {
|
||||
public abstract [STATUS] fun foo([RAW_FIR] a: R|foo/Arg|): R|foo/Arg|
|
||||
|
||||
}
|
||||
public final [CONTRACTS] fun testMe([RAW_FIR] f: R|foo/Foo|): R|kotlin/Unit| {
|
||||
}
|
||||
public final [BODY_RESOLVE] fun resolveMe(): R|kotlin/Unit| {
|
||||
R|foo/testMe|(<L> = [BODY_RESOLVE] [MatchingParameterFunctionTypeKey=foo/Foo] testMe@fun <anonymous>([BODY_RESOLVE] b: R|foo/Arg|): R|foo/Arg| <inline=NoInline> {
|
||||
^ R|<local>/b|
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
FILE RAW TO BODY:
|
||||
FILE: lambdaAsSAMInterface.kt
|
||||
public final [BODY_RESOLVE] class Arg : R|kotlin/Any| {
|
||||
public [BODY_RESOLVE] [ContainingClassKey=Arg] constructor(): R|foo/Arg| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
}
|
||||
public abstract fun [BODY_RESOLVE] interface Foo : R|kotlin/Any| {
|
||||
public abstract [BODY_RESOLVE] fun foo([BODY_RESOLVE] a: R|foo/Arg|): R|foo/Arg|
|
||||
|
||||
}
|
||||
public final [BODY_RESOLVE] fun testMe([BODY_RESOLVE] f: R|foo/Foo|): R|kotlin/Unit| {
|
||||
}
|
||||
public final [BODY_RESOLVE] fun resolveMe(): R|kotlin/Unit| {
|
||||
R|foo/testMe|(<L> = [BODY_RESOLVE] [MatchingParameterFunctionTypeKey=foo/Foo] testMe@fun <anonymous>([BODY_RESOLVE] b: R|foo/Arg|): R|foo/Arg| <inline=NoInline> {
|
||||
^ R|<local>/b|
|
||||
}
|
||||
)
|
||||
}
|
||||
+6
@@ -54,6 +54,12 @@ public class FirLazyDeclarationResolveTestGenerated extends AbstractFirLazyDecla
|
||||
runTest("analysis/low-level-api-fir/testdata/lazyResolve/functionWithParameter.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("lambdaAsSAMInterface.kt")
|
||||
public void testLambdaAsSAMInterface() throws Exception {
|
||||
runTest("analysis/low-level-api-fir/testdata/lazyResolve/lambdaAsSAMInterface.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("parameterOfNonLocalSetter.kt")
|
||||
public void testParameterOfNonLocalSetter() throws Exception {
|
||||
|
||||
@@ -276,10 +276,10 @@ private fun FirRegularClass.computeSamCandidateNames(session: FirSession): Set<N
|
||||
for (clazz in classes) {
|
||||
for (declaration in clazz.declarations) {
|
||||
when (declaration) {
|
||||
is FirProperty -> if (declaration.modality == Modality.ABSTRACT) {
|
||||
is FirProperty -> if (declaration.resolvedIsAbstract) {
|
||||
samCandidateNames.add(declaration.name)
|
||||
}
|
||||
is FirSimpleFunction -> if (declaration.modality == Modality.ABSTRACT) {
|
||||
is FirSimpleFunction -> if (declaration.resolvedIsAbstract) {
|
||||
samCandidateNames.add(declaration.name)
|
||||
}
|
||||
else -> {}
|
||||
@@ -304,7 +304,7 @@ private fun FirRegularClass.findSingleAbstractMethodByNames(
|
||||
if (metIncorrectMember) break
|
||||
|
||||
classUseSiteMemberScope.processPropertiesByName(candidateName) {
|
||||
if ((it as? FirPropertySymbol)?.fir?.modality == Modality.ABSTRACT) {
|
||||
if (it is FirPropertySymbol && it.fir.resolvedIsAbstract) {
|
||||
metIncorrectMember = true
|
||||
}
|
||||
}
|
||||
@@ -313,7 +313,7 @@ private fun FirRegularClass.findSingleAbstractMethodByNames(
|
||||
|
||||
classUseSiteMemberScope.processFunctionsByName(candidateName) { functionSymbol ->
|
||||
val firFunction = functionSymbol.fir
|
||||
if (firFunction.modality != Modality.ABSTRACT ||
|
||||
if (!firFunction.resolvedIsAbstract ||
|
||||
firFunction.isPublicInObject(checkOnlyName = false)
|
||||
) return@processFunctionsByName
|
||||
|
||||
@@ -333,8 +333,8 @@ private fun FirRegularClass.findSingleAbstractMethodByNames(
|
||||
private fun FirRegularClass.hasMoreThenOneAbstractFunctionOrHasAbstractProperty(): Boolean {
|
||||
var wasAbstractFunction = false
|
||||
for (declaration in declarations) {
|
||||
if (declaration is FirProperty && declaration.modality == Modality.ABSTRACT) return true
|
||||
if (declaration is FirSimpleFunction && declaration.modality == Modality.ABSTRACT &&
|
||||
if (declaration is FirProperty && declaration.resolvedIsAbstract) return true
|
||||
if (declaration is FirSimpleFunction && declaration.resolvedIsAbstract &&
|
||||
!declaration.isPublicInObject(checkOnlyName = true)
|
||||
) {
|
||||
if (wasAbstractFunction) return true
|
||||
@@ -345,6 +345,13 @@ private fun FirRegularClass.hasMoreThenOneAbstractFunctionOrHasAbstractProperty(
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if declaration is indeed abstract, ensuring that its status has been completely resolved
|
||||
* beforehand.
|
||||
*/
|
||||
private val FirCallableDeclaration.resolvedIsAbstract: Boolean
|
||||
get() = symbol.isAbstract
|
||||
|
||||
// From the definition of function interfaces in the Java specification (pt. 9.8):
|
||||
// "methods that are members of I that do not have the same signature as any public instance method of the class Object"
|
||||
// It means that if an interface declares `int hashCode()` then the method won't be taken into account when
|
||||
|
||||
-1
@@ -1,5 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// FIR_IDE_IGNORE
|
||||
// TODO: KT-50732
|
||||
// ISSUE: KT-51007
|
||||
|
||||
|
||||
Reference in New Issue
Block a user