[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:
Roman Golyshev
2022-06-07 16:34:44 +04:00
committed by teamcity
parent 3abcd84802
commit 8fca9f41eb
5 changed files with 254 additions and 7 deletions
@@ -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|
}
)
}
@@ -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,5 +1,4 @@
// FIR_IDENTICAL
// FIR_IDE_IGNORE
// TODO: KT-50732
// ISSUE: KT-51007