[FIR] Implement MANY_LAMBDA_EXPRESSION_ARGUMENTS diagnostics, fix tests

This commit is contained in:
Ivan Kochurkin
2021-04-27 19:51:01 +03:00
committed by TeamCityServer
parent b8002cb54f
commit 525cc6df97
12 changed files with 68 additions and 26 deletions
@@ -255,6 +255,8 @@ object DIAGNOSTICS_LIST : DiagnosticList() {
val NAMED_PARAMETER_NOT_FOUND by error<KtValueArgument>(PositioningStrategy.NAME_OF_NAMED_ARGUMENT) { val NAMED_PARAMETER_NOT_FOUND by error<KtValueArgument>(PositioningStrategy.NAME_OF_NAMED_ARGUMENT) {
parameter<String>("name") parameter<String>("name")
} }
val MANY_LAMBDA_EXPRESSION_ARGUMENTS by error<KtValueArgument>()
} }
val AMBIGUITY by object : DiagnosticGroup("Ambiguity") { val AMBIGUITY by object : DiagnosticGroup("Ambiguity") {
@@ -212,6 +212,7 @@ object FirErrors {
val TOO_MANY_ARGUMENTS by error1<PsiElement, FirCallableDeclaration<*>>() val TOO_MANY_ARGUMENTS by error1<PsiElement, FirCallableDeclaration<*>>()
val NO_VALUE_FOR_PARAMETER by error1<KtElement, FirValueParameter>(SourceElementPositioningStrategies.VALUE_ARGUMENTS) val NO_VALUE_FOR_PARAMETER by error1<KtElement, FirValueParameter>(SourceElementPositioningStrategies.VALUE_ARGUMENTS)
val NAMED_PARAMETER_NOT_FOUND by error1<KtValueArgument, String>(SourceElementPositioningStrategies.NAME_OF_NAMED_ARGUMENT) val NAMED_PARAMETER_NOT_FOUND by error1<KtValueArgument, String>(SourceElementPositioningStrategies.NAME_OF_NAMED_ARGUMENT)
val MANY_LAMBDA_EXPRESSION_ARGUMENTS by error0<KtValueArgument>()
// Ambiguity // Ambiguity
val OVERLOAD_RESOLUTION_AMBIGUITY by error1<PsiElement, Collection<AbstractFirBasedSymbol<*>>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED) val OVERLOAD_RESOLUTION_AMBIGUITY by error1<PsiElement, Collection<AbstractFirBasedSymbol<*>>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
@@ -155,6 +155,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.LOCAL_OBJECT_NOT_
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MANY_COMPANION_OBJECTS import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MANY_COMPANION_OBJECTS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MANY_IMPL_MEMBER_NOT_IMPLEMENTED import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MANY_IMPL_MEMBER_NOT_IMPLEMENTED
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MANY_LAMBDA_EXPRESSION_ARGUMENTS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.METHOD_OF_ANY_IMPLEMENTED_IN_INTERFACE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.METHOD_OF_ANY_IMPLEMENTED_IN_INTERFACE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MISPLACED_TYPE_PARAMETER_CONSTRAINTS import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MISPLACED_TYPE_PARAMETER_CONSTRAINTS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MISSING_VAL_ON_ANNOTATION_PARAMETER import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MISSING_VAL_ON_ANNOTATION_PARAMETER
@@ -461,6 +462,7 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension {
map.put(ARGUMENT_PASSED_TWICE, "An argument is already passed for this parameter") map.put(ARGUMENT_PASSED_TWICE, "An argument is already passed for this parameter")
map.put(NO_VALUE_FOR_PARAMETER, "No value passed for parameter ''{0}''", NAME) map.put(NO_VALUE_FOR_PARAMETER, "No value passed for parameter ''{0}''", NAME)
map.put(NAMED_PARAMETER_NOT_FOUND, "Cannot find a parameter with this name: {0}", TO_STRING) map.put(NAMED_PARAMETER_NOT_FOUND, "Cannot find a parameter with this name: {0}", TO_STRING)
map.put(MANY_LAMBDA_EXPRESSION_ARGUMENTS, "Only one lambda expression is allowed outside a parenthesized argument list")
map.put(ARGUMENT_TYPE_MISMATCH, "Argument type mismatch: actual type is {1} but {0} was expected", TO_STRING, TO_STRING) map.put(ARGUMENT_TYPE_MISMATCH, "Argument type mismatch: actual type is {1} but {0} was expected", TO_STRING, TO_STRING)
@@ -142,6 +142,7 @@ private fun mapInapplicableCandidateError(
rootCause.argument.name.asString() rootCause.argument.name.asString()
) )
is UnsafeCall -> mapUnsafeCallError(diagnostic.candidate, rootCause, source, qualifiedAccessSource) is UnsafeCall -> mapUnsafeCallError(diagnostic.candidate, rootCause, source, qualifiedAccessSource)
is ManyLambdaExpressionArguments -> FirErrors.MANY_LAMBDA_EXPRESSION_ARGUMENTS.on(rootCause.argument.source ?: source)
else -> null else -> null
} }
}.ifEmpty { listOf(FirErrors.INAPPLICABLE_CANDIDATE.on(source, diagnostic.candidate.symbol)) } }.ifEmpty { listOf(FirErrors.INAPPLICABLE_CANDIDATE.on(source, diagnostic.candidate.symbol)) }
@@ -61,11 +61,20 @@ fun BodyResolveComponents.mapArguments(
if (arguments.isEmpty() && function.valueParameters.isEmpty()) { if (arguments.isEmpty() && function.valueParameters.isEmpty()) {
return EmptyArgumentMapping return EmptyArgumentMapping
} }
val externalArgument: FirExpression? = arguments.lastOrNull { it is FirLambdaArgumentExpression }
var argumentsInParenthesis: List<FirExpression> = if (externalArgument == null) { val argumentsInParenthesis: MutableList<FirExpression> = mutableListOf()
arguments val excessLambdaArguments: MutableList<FirExpression> = mutableListOf()
} else { var externalArgument: FirExpression? = null
arguments.subList(0, arguments.size - 1) for (argument in arguments) {
if (argument is FirLambdaArgumentExpression) {
if (externalArgument == null) {
externalArgument = argument
} else {
excessLambdaArguments.add(argument)
}
} else {
argumentsInParenthesis.add(argument)
}
} }
// If this is an overloading indexed access operator, it could have default values or a vararg parameter in the middle. // If this is an overloading indexed access operator, it could have default values or a vararg parameter in the middle.
@@ -82,7 +91,8 @@ fun BodyResolveComponents.mapArguments(
isSpread = false isSpread = false
name = function.valueParameters.last().name name = function.valueParameters.last().name
} }
argumentsInParenthesis = argumentsInParenthesis.dropLast(1) + listOf(namedV) argumentsInParenthesis.removeAt(argumentsInParenthesis.size - 1)
argumentsInParenthesis.add(namedV)
} }
} }
@@ -91,6 +101,7 @@ fun BodyResolveComponents.mapArguments(
if (externalArgument != null) { if (externalArgument != null) {
processor.processExternalArgument(externalArgument) processor.processExternalArgument(externalArgument)
} }
processor.processExcessLambdaArguments(excessLambdaArguments)
processor.processDefaultsAndRunChecks() processor.processDefaultsAndRunChecks()
return ArgumentMapping(processor.result, processor.diagnostics ?: emptyList()) return ArgumentMapping(processor.result, processor.diagnostics ?: emptyList())
@@ -212,6 +223,10 @@ private class FirCallArgumentsProcessor(
result[lastParameter] = ResolvedCallArgument.SimpleArgument(externalArgument) result[lastParameter] = ResolvedCallArgument.SimpleArgument(externalArgument)
} }
fun processExcessLambdaArguments(excessLambdaArguments: List<FirExpression>) {
excessLambdaArguments.forEach { arg -> addDiagnostic(ManyLambdaExpressionArguments(arg)) }
}
fun processDefaultsAndRunChecks() { fun processDefaultsAndRunChecks() {
for ((parameter, resolvedArgument) in result) { for ((parameter, resolvedArgument) in result) {
if (!parameter.isVararg) { if (!parameter.isVararg) {
@@ -97,4 +97,8 @@ class ArgumentTypeMismatch(
class NullForNotNullType( class NullForNotNullType(
val argument: FirExpression val argument: FirExpression
) : ResolutionDiagnostic(INAPPLICABLE) ) : ResolutionDiagnostic(INAPPLICABLE)
class ManyLambdaExpressionArguments(
val argument: FirExpression
) : ResolutionDiagnostic(INAPPLICABLE_ARGUMENTS_MAPPING_ERROR)
@@ -25,5 +25,5 @@ fun test() {
foo("", 1) <!TOO_MANY_ARGUMENTS!>{}<!> foo("", 1) <!TOO_MANY_ARGUMENTS!>{}<!>
foo("", 1) <!TOO_MANY_ARGUMENTS!>{}<!> <!TOO_MANY_ARGUMENTS!>{}<!> foo("", 1) <!TOO_MANY_ARGUMENTS!>{}<!> <!MANY_LAMBDA_EXPRESSION_ARGUMENTS!>{}<!>
} }
@@ -6,9 +6,9 @@ fun test(x: () -> Unit, y: () -> Unit) {
} }
fun main() { fun main() {
test { <!NO_VALUE_FOR_PARAMETER!>test {
1 1
} { } <!MANY_LAMBDA_EXPRESSION_ARGUMENTS!>{
2 2
} }<!><!>
} }
@@ -23,9 +23,9 @@ fun testNoArgs() {
noArgs() // comment noArgs() // comment
// comment // comment
<!TOO_MANY_ARGUMENTS!>{}<!> <!TOO_MANY_ARGUMENTS!>{}<!>
noArgs() <!TOO_MANY_ARGUMENTS!>{}<!> <!TOO_MANY_ARGUMENTS!>{}<!> noArgs() <!TOO_MANY_ARGUMENTS!>{}<!> <!MANY_LAMBDA_EXPRESSION_ARGUMENTS!>{}<!>
noArgs() <!TOO_MANY_ARGUMENTS!>{}<!> noArgs() <!TOO_MANY_ARGUMENTS!>{}<!>
<!TOO_MANY_ARGUMENTS!>{}<!> <!MANY_LAMBDA_EXPRESSION_ARGUMENTS!>{}<!>
} }
fun testLambdaArg() { fun testLambdaArg() {
@@ -35,7 +35,7 @@ fun testLambdaArg() {
{} {}
oneLambdaArg() oneLambdaArg()
{} {}
<!TOO_MANY_ARGUMENTS!>{}<!> <!MANY_LAMBDA_EXPRESSION_ARGUMENTS!>{}<!>
oneLambdaArg( oneLambdaArg(
{}, {},
<!TOO_MANY_ARGUMENTS!>{}<!> <!TOO_MANY_ARGUMENTS!>{}<!>
@@ -52,19 +52,19 @@ fun testLambdaArg() {
{} {}
oneLambdaArg() {}/* oneLambdaArg() {}/*
block comment, no new line block comment, no new line
*/ <!TOO_MANY_ARGUMENTS!>{}<!> */ <!MANY_LAMBDA_EXPRESSION_ARGUMENTS!>{}<!>
oneLambdaArg() {}/* oneLambdaArg() {}/*
block comment with new line block comment with new line
*/ */
<!TOO_MANY_ARGUMENTS!>{}<!> <!MANY_LAMBDA_EXPRESSION_ARGUMENTS!>{}<!>
oneLambdaArg() {}// comment oneLambdaArg() {}// comment
// comment // comment
<!TOO_MANY_ARGUMENTS!>{}<!> <!MANY_LAMBDA_EXPRESSION_ARGUMENTS!>{}<!>
oneLambdaArg() {} <!TOO_MANY_ARGUMENTS!>{}<!> oneLambdaArg() {} <!MANY_LAMBDA_EXPRESSION_ARGUMENTS!>{}<!>
oneLambdaArg() {} oneLambdaArg() {}
<!TOO_MANY_ARGUMENTS!>{}<!> <!MANY_LAMBDA_EXPRESSION_ARGUMENTS!>{}<!>
oneLambdaArg() {} // comment oneLambdaArg() {} // comment
<!TOO_MANY_ARGUMENTS!>{}<!> <!MANY_LAMBDA_EXPRESSION_ARGUMENTS!>{}<!>
} }
fun testVararg() { fun testVararg() {
@@ -83,9 +83,9 @@ fun testVararg() {
varargFn(1,2,3) // comment varargFn(1,2,3) // comment
// comment // comment
<!VARARG_OUTSIDE_PARENTHESES!>{}<!> <!VARARG_OUTSIDE_PARENTHESES!>{}<!>
varargFn(1,2,3) <!ARGUMENT_TYPE_MISMATCH!>{}<!> <!ARGUMENT_TYPE_MISMATCH, VARARG_OUTSIDE_PARENTHESES!>{}<!> varargFn(1,2,3) {} <!MANY_LAMBDA_EXPRESSION_ARGUMENTS!>{}<!>
varargFn(1,2,3) <!ARGUMENT_TYPE_MISMATCH!>{}<!> varargFn(1,2,3) {}
<!ARGUMENT_TYPE_MISMATCH, VARARG_OUTSIDE_PARENTHESES!>{}<!> <!MANY_LAMBDA_EXPRESSION_ARGUMENTS!>{}<!>
} }
fun testTwoLambdas() { fun testTwoLambdas() {
@@ -96,14 +96,14 @@ fun testTwoLambdas() {
) )
fun bar(): () -> Unit { fun bar(): () -> Unit {
twoLambdaArgs() twoLambdaArgs(<!NO_VALUE_FOR_PARAMETER!>)<!>
{}
{} {}
<!MANY_LAMBDA_EXPRESSION_ARGUMENTS!>{}<!>
return if (true) { return if (true) {
twoLambdaArgs({}) twoLambdaArgs({})
{} {}
<!TOO_MANY_ARGUMENTS!>{}<!> <!MANY_LAMBDA_EXPRESSION_ARGUMENTS!>{}<!>
} else { } else {
{} {}
} }
@@ -828,6 +828,12 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
token, token,
) )
} }
add(FirErrors.MANY_LAMBDA_EXPRESSION_ARGUMENTS) { firDiagnostic ->
ManyLambdaExpressionArgumentsImpl(
firDiagnostic as FirPsiDiagnostic<*>,
token,
)
}
add(FirErrors.OVERLOAD_RESOLUTION_AMBIGUITY) { firDiagnostic -> add(FirErrors.OVERLOAD_RESOLUTION_AMBIGUITY) { firDiagnostic ->
OverloadResolutionAmbiguityImpl( OverloadResolutionAmbiguityImpl(
firDiagnostic.a.map { abstractFirBasedSymbol -> firDiagnostic.a.map { abstractFirBasedSymbol ->
@@ -595,6 +595,10 @@ sealed class KtFirDiagnostic<PSI: PsiElement> : KtDiagnosticWithPsi<PSI> {
abstract val name: String abstract val name: String
} }
abstract class ManyLambdaExpressionArguments : KtFirDiagnostic<KtValueArgument>() {
override val diagnosticClass get() = ManyLambdaExpressionArguments::class
}
abstract class OverloadResolutionAmbiguity : KtFirDiagnostic<PsiElement>() { abstract class OverloadResolutionAmbiguity : KtFirDiagnostic<PsiElement>() {
override val diagnosticClass get() = OverloadResolutionAmbiguity::class override val diagnosticClass get() = OverloadResolutionAmbiguity::class
abstract val candidates: List<KtSymbol> abstract val candidates: List<KtSymbol>
@@ -953,6 +953,13 @@ internal class NamedParameterNotFoundImpl(
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
} }
internal class ManyLambdaExpressionArgumentsImpl(
firDiagnostic: FirPsiDiagnostic<*>,
override val token: ValidityToken,
) : KtFirDiagnostic.ManyLambdaExpressionArguments(), KtAbstractFirDiagnostic<KtValueArgument> {
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
}
internal class OverloadResolutionAmbiguityImpl( internal class OverloadResolutionAmbiguityImpl(
override val candidates: List<KtSymbol>, override val candidates: List<KtSymbol>,
firDiagnostic: FirPsiDiagnostic<*>, firDiagnostic: FirPsiDiagnostic<*>,