diff --git a/analysis/low-level-api-fir/testdata/lazyResolve/lambdaAsSAMInterface.kt b/analysis/low-level-api-fir/testdata/lazyResolve/lambdaAsSAMInterface.kt new file mode 100644 index 00000000000..ed945d624be --- /dev/null +++ b/analysis/low-level-api-fir/testdata/lazyResolve/lambdaAsSAMInterface.kt @@ -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 } +} \ No newline at end of file diff --git a/analysis/low-level-api-fir/testdata/lazyResolve/lambdaAsSAMInterface.txt b/analysis/low-level-api-fir/testdata/lazyResolve/lambdaAsSAMInterface.txt new file mode 100644 index 00000000000..358e0dc83d9 --- /dev/null +++ b/analysis/low-level-api-fir/testdata/lazyResolve/lambdaAsSAMInterface.txt @@ -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() + } + + } + 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() + } + + } + 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() + } + + } + 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() + } + + } + 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() + } + + } + 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() + } + + } + 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() + } + + } + 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() + } + + } + 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() + } + + } + 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#( = [STATUS] testMe@fun .([RAW_FIR] b: ): { + 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() + } + + } + 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#( = [STATUS] testMe@fun .([RAW_FIR] b: ): { + 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() + } + + } + 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#( = [STATUS] testMe@fun .([RAW_FIR] b: ): { + 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() + } + + } + 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|( = [BODY_RESOLVE] [MatchingParameterFunctionTypeKey=foo/Foo] testMe@fun ([BODY_RESOLVE] b: R|foo/Arg|): R|foo/Arg| { + ^ R|/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() + } + + } + 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|( = [BODY_RESOLVE] [MatchingParameterFunctionTypeKey=foo/Foo] testMe@fun ([BODY_RESOLVE] b: R|foo/Arg|): R|foo/Arg| { + ^ R|/b| + } + ) + } diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/FirLazyDeclarationResolveTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/FirLazyDeclarationResolveTestGenerated.java index 000343a60db..d3373c66113 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/FirLazyDeclarationResolveTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/FirLazyDeclarationResolveTestGenerated.java @@ -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 { diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/SamResolution.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/SamResolution.kt index 2ffead03632..356afd6a875 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/SamResolution.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/SamResolution.kt @@ -276,10 +276,10 @@ private fun FirRegularClass.computeSamCandidateNames(session: FirSession): Set 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 diff --git a/compiler/testData/diagnostics/tests/multimodule/samWithSuspendFunctionFromAnotherModule.kt b/compiler/testData/diagnostics/tests/multimodule/samWithSuspendFunctionFromAnotherModule.kt index 82883735e73..97eaa9cd661 100644 --- a/compiler/testData/diagnostics/tests/multimodule/samWithSuspendFunctionFromAnotherModule.kt +++ b/compiler/testData/diagnostics/tests/multimodule/samWithSuspendFunctionFromAnotherModule.kt @@ -1,5 +1,4 @@ // FIR_IDENTICAL -// FIR_IDE_IGNORE // TODO: KT-50732 // ISSUE: KT-51007