Skip to content

Commit

Permalink
K1: add diagnostic BUILDER_INFERENCE_STUB_RECEIVER
Browse files Browse the repository at this point in the history
It's reported on receivers in extension function calls with stub type,
as such calls can shadow members of finalized stub types causing
change of resolve when corresponding type argument specified explicitly

It works by checking extension receiver during call resolution parts run
That way we can easily detect if we found an extension applicable to
stub receiver and report call diagnostic for it

KT-53739
  • Loading branch information
semoro authored and teamcity committed Aug 30, 2022
1 parent 105358d commit 154e53c
Show file tree
Hide file tree
Showing 18 changed files with 428 additions and 44 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -862,6 +862,7 @@ enum BadNamedArgumentsTarget {
DiagnosticFactory3<PsiElement, KotlinType, String, String> STUB_TYPE_IN_ARGUMENT_CAUSES_AMBIGUITY = DiagnosticFactory3.create(ERROR);
DiagnosticFactory4<PsiElement, KotlinType, String, String, BuilderLambdaLabelingInfo> STUB_TYPE_IN_RECEIVER_CAUSES_AMBIGUITY = DiagnosticFactory4.create(ERROR);
DiagnosticFactory2<PsiElement, Name, Name> BUILDER_INFERENCE_MULTI_LAMBDA_RESTRICTION = DiagnosticFactory2.create(ERROR);
DiagnosticFactory2<PsiElement, Name, Name> BUILDER_INFERENCE_STUB_RECEIVER = DiagnosticFactory2.create(ERROR);

DiagnosticFactory1<PsiElement, Collection<? extends ResolvedCall<?>>> NONE_APPLICABLE = DiagnosticFactory1.create(ERROR);
DiagnosticFactory1<PsiElement, Collection<? extends ResolvedCall<?>>> CANNOT_COMPLETE_RESOLVE = DiagnosticFactory1.create(ERROR);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -945,6 +945,7 @@ public static DiagnosticRenderer getRendererForDiagnostic(@NotNull UnboundDiagno
MAP.put(STUB_TYPE_IN_ARGUMENT_CAUSES_AMBIGUITY, "The type of an argument hasn''t been inferred yet. To disambiguate this call, explicitly cast it to `{0}` if you want the builder''s type parameter(s) `{1}` to be inferred to `{2}`.", RENDER_TYPE, STRING, STRING);
MAP.put(STUB_TYPE_IN_RECEIVER_CAUSES_AMBIGUITY, "The type of a receiver hasn''t been inferred yet. To disambiguate this call, explicitly cast it to `{0}` if you want the builder''s type parameter(s) `{1}` to be inferred to `{2}`.", RENDER_TYPE, STRING, STRING, null);
MAP.put(BUILDER_INFERENCE_MULTI_LAMBDA_RESTRICTION, "Unstable inference behaviour with multiple lambdas. Please either specify type argument for generic parameter `{0}` of `{1}` explicitly or add @BuilderInference to corresponding parameter declaration", TO_STRING, TO_STRING);
MAP.put(BUILDER_INFERENCE_STUB_RECEIVER, "The type of a receiver hasn''t been inferred yet. Please specify type argument for generic parameter `{0}` of `{1}` explicitly", TO_STRING, TO_STRING);
MAP.put(NONE_APPLICABLE, "None of the following functions can be called with the arguments supplied: {0}", AMBIGUOUS_CALLS);
MAP.put(CANNOT_COMPLETE_RESOLVE, "Cannot choose among the following candidates without completing type inference: {0}", AMBIGUOUS_CALLS);
MAP.put(UNRESOLVED_REFERENCE_WRONG_RECEIVER, "Unresolved reference. None of the following candidates is applicable because of receiver type mismatch: {0}", AMBIGUOUS_CALLS);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.StubTypeForBuilderInference
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.checker.intersectWrappedTypes
import org.jetbrains.kotlin.types.error.ErrorUtils
Expand Down Expand Up @@ -199,6 +200,21 @@ class DiagnosticReporterByTrackingStrategy(
trace.report(SUPER_CANT_BE_EXTENSION_RECEIVER.on(psiExpression, psiExpression.text))
}
}

StubBuilderInferenceReceiver::class.java -> {
diagnostic as StubBuilderInferenceReceiver

val stubType = callReceiver.receiver.receiverValue.type as? StubTypeForBuilderInference
val originalTypeParameter = stubType?.originalTypeVariable?.originalTypeParameter

trace.report(
BUILDER_INFERENCE_STUB_RECEIVER.on(
callReceiver.psiExpression ?: call.callElement,
originalTypeParameter?.name ?: SpecialNames.NO_NAME_PROVIDED,
originalTypeParameter?.containingDeclaration?.name ?: SpecialNames.NO_NAME_PROVIDED
)
)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -661,9 +661,25 @@ internal object CheckReceivers : ResolutionPart() {
candidateDescriptor.dispatchReceiverParameter,
shouldCheckImplicitInvoke = true,
)

1 -> {
if (resolvedCall.extensionReceiverArgument == null) {
resolvedCall.extensionReceiverArgument = chooseExtensionReceiverCandidate() ?: return
val checkBuilderInferenceRestriction =
!callComponents.languageVersionSettings
.supportsFeature(LanguageFeature.NoBuilderInferenceWithoutAnnotationRestriction)
if (checkBuilderInferenceRestriction) {
var extensionReceiverArgument = resolvedCall.extensionReceiverArgument
if (extensionReceiverArgument == null) {
extensionReceiverArgument = chooseExtensionReceiverCandidate() ?: return
resolvedCall.extensionReceiverArgument = extensionReceiverArgument
}
if (extensionReceiverArgument.receiver.receiverValue.type is StubTypeForBuilderInference) {
addDiagnostic(
StubBuilderInferenceReceiver(
extensionReceiverArgument,
candidateDescriptor.extensionReceiverParameter!!
)
)
}
}
checkReceiver(
resolvedCall.extensionReceiverArgument,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,13 @@ class MultiLambdaBuilderInferenceRestriction(
override fun report(reporter: DiagnosticReporter) = reporter.onCallArgument(argument, this)
}

class StubBuilderInferenceReceiver(
val receiver: SimpleKotlinCallArgument,
val extensionReceiverParameter: ReceiverParameterDescriptor,
) : KotlinCallDiagnostic(RESOLVED) {
override fun report(reporter: DiagnosticReporter) = reporter.onCallReceiver(receiver, this)
}

class MixingNamedAndPositionArguments(override val argument: KotlinCallArgument) : InapplicableArgumentDiagnostic()

class NamedArgumentNotAllowed(val argument: KotlinCallArgument, val descriptor: CallableDescriptor) : KotlinCallDiagnostic(INAPPLICABLE) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ fun box(): String {
val ret = build {
emit(1)
emit(null)
get()?.test()
get()?.test2()
get().test2()
// Error, resolved to extension on stub receiver
// get()?.test()
// get()?.test2()
// get().test2()
get()?.hashCode()
get()?.equals(1)
val x = get()
Expand All @@ -38,9 +39,10 @@ fun box(): String {
x.equals("")
x.hashCode()
x.toString()
x.test()
x?.test2()
x.test2()
// Error, resolved to extension on stub receiver
// x.test()
// x?.test2()
// x.test2()
}

if (x == null) {
Expand All @@ -50,8 +52,9 @@ fun box(): String {
// x.hashCode()
// x.toString()
// x.test()
x?.test2()
x.test2()
// Error, resolved to extension on stub receiver
// x?.test2()
// x.test2()
}

if (x === null) {
Expand All @@ -61,8 +64,9 @@ fun box(): String {
// x.hashCode()
// x.toString()
// x.test()
x?.test2()
x.test2()
// Error, resolved to extension on stub receiver
// x?.test2()
// x.test2()
}

""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ fun main() {

buildList {
add(Bar())
this.get(0).test() // resolved to Any?.test
<!BUILDER_INFERENCE_STUB_RECEIVER!>this.get(0)<!>.test() // resolved to Any?.test
}
buildList<Bar> {
add(Bar())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// WITH_STDLIB
// SKIP_TXT
fun test() {
foo(
flow { emit(0) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
fun test() {
foo(
flow { emit(0) }
) <!BUILDER_INFERENCE_MULTI_LAMBDA_RESTRICTION!>{ it.collect <!TOO_MANY_ARGUMENTS!>{}<!> }<!>
) <!BUILDER_INFERENCE_MULTI_LAMBDA_RESTRICTION!>{ <!BUILDER_INFERENCE_STUB_RECEIVER!>it<!>.collect <!TOO_MANY_ARGUMENTS!>{}<!> }<!>

// 0. Initial
// W <: Any / declared upper bound
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ fun test() {
val ret1 = build {
emit(1)
emit(null)
get()?.test()
get()?.test2()
get().test2()
<!BUILDER_INFERENCE_STUB_RECEIVER!>get()<!>?.test()
<!BUILDER_INFERENCE_STUB_RECEIVER!>get()<!>?.test2()
<!BUILDER_INFERENCE_STUB_RECEIVER!>get()<!>.test2()
get()?.hashCode()
get()?.equals(1)
// there is `String?.equals` extension
Expand All @@ -31,9 +31,9 @@ fun test() {
val ret2 = build {
emit(1)
emit(null)
get()?.test()
get()?.test2()
get().test2()
<!BUILDER_INFERENCE_STUB_RECEIVER!>get()<!>?.test()
<!BUILDER_INFERENCE_STUB_RECEIVER!>get()<!>?.test2()
<!BUILDER_INFERENCE_STUB_RECEIVER!>get()<!>.test2()
get()?.hashCode()
get()?.equals(1)
val x = get()
Expand All @@ -44,9 +44,9 @@ fun test() {
val ret3 = build {
emit(1)
emit(null)
get()?.test()
get()?.test2()
get().test2()
<!BUILDER_INFERENCE_STUB_RECEIVER!>get()<!>?.test()
<!BUILDER_INFERENCE_STUB_RECEIVER!>get()<!>?.test2()
<!BUILDER_INFERENCE_STUB_RECEIVER!>get()<!>.test2()
get()?.hashCode()
get()?.equals(1)
val x = get()
Expand All @@ -62,9 +62,9 @@ fun test() {
x.equals("")
x.hashCode()
x.toString()
x.test()
x<!UNNECESSARY_SAFE_CALL!>?.<!>test2()
x.test2()
<!BUILDER_INFERENCE_STUB_RECEIVER!>x<!>.test()
<!BUILDER_INFERENCE_STUB_RECEIVER!>x<!><!UNNECESSARY_SAFE_CALL!>?.<!>test2()
<!BUILDER_INFERENCE_STUB_RECEIVER!>x<!>.test2()
}

""
Expand Down Expand Up @@ -104,7 +104,7 @@ fun test() {
emit(null)
val x = get()
if (x == null) {
<!TYPE_MISMATCH("Any; Nothing?")!>x<!>.test()
<!BUILDER_INFERENCE_STUB_RECEIVER, TYPE_MISMATCH("Any; Nothing?")!>x<!>.test()
}

""
Expand Down Expand Up @@ -144,7 +144,7 @@ fun test() {
emit(null)
val x = get()
if (x === null) {
<!TYPE_MISMATCH("Any; Nothing?")!>x<!>.test()
<!BUILDER_INFERENCE_STUB_RECEIVER, TYPE_MISMATCH("Any; Nothing?")!>x<!>.test()
}

""
Expand All @@ -153,16 +153,16 @@ fun test() {
emit(1)
emit(null)
val x = get()
<!TYPE_MISMATCH("Any; Nothing?")!>x<!>.test()
<!BUILDER_INFERENCE_STUB_RECEIVER, TYPE_MISMATCH("Any; Nothing?")!>x<!>.test()

""
}
val ret41 = build {
emit(1)
emit(null)
get()?.test()
get()?.test2()
get().test2()
<!BUILDER_INFERENCE_STUB_RECEIVER!>get()<!>?.test()
<!BUILDER_INFERENCE_STUB_RECEIVER!>get()<!>?.test2()
<!BUILDER_INFERENCE_STUB_RECEIVER!>get()<!>.test2()
get()?.hashCode()
get()?.equals(1)
val x = get()
Expand All @@ -181,11 +181,11 @@ fun test() {
}

if (x == null) {
<!DEBUG_INFO_CONSTANT!>x<!>?.test2()
<!BUILDER_INFERENCE_STUB_RECEIVER, DEBUG_INFO_CONSTANT!>x<!>?.test2()
}

if (x == null) {
x.test2()
<!BUILDER_INFERENCE_STUB_RECEIVER!>x<!>.test2()
}

if (x === null) {
Expand All @@ -197,11 +197,11 @@ fun test() {
}

if (x === null) {
<!DEBUG_INFO_CONSTANT!>x<!>?.test2()
<!BUILDER_INFERENCE_STUB_RECEIVER, DEBUG_INFO_CONSTANT!>x<!>?.test2()
}

if (x === null) {
x.test2()
<!BUILDER_INFERENCE_STUB_RECEIVER!>x<!>.test2()
}

""
Expand Down Expand Up @@ -239,7 +239,7 @@ fun test() {
emit(null)
val x = get()
if (x == null) {
<!TYPE_MISMATCH("Any; Nothing?")!>x<!>.test()
<!BUILDER_INFERENCE_STUB_RECEIVER, TYPE_MISMATCH("Any; Nothing?")!>x<!>.test()
}
""
}
Expand Down Expand Up @@ -276,23 +276,23 @@ fun test() {
emit(null)
val x = get()
if (x === null) {
<!TYPE_MISMATCH("Any; Nothing?")!>x<!>.test()
<!BUILDER_INFERENCE_STUB_RECEIVER, TYPE_MISMATCH("Any; Nothing?")!>x<!>.test()
}
""
}
val ret508 = build {
emit(1)
emit(null)
val x = get()
<!TYPE_MISMATCH("Any; Nothing?")!>x<!>.test()
<!BUILDER_INFERENCE_STUB_RECEIVER, TYPE_MISMATCH("Any; Nothing?")!>x<!>.test()
""
}
val ret51 = build {
emit(1)
emit(null)
get()?.test()
get()?.test2()
get().test2()
<!BUILDER_INFERENCE_STUB_RECEIVER!>get()<!>?.test()
<!BUILDER_INFERENCE_STUB_RECEIVER!>get()<!>?.test2()
<!BUILDER_INFERENCE_STUB_RECEIVER!>get()<!>.test2()
get()?.hashCode()
get()?.equals(1)
val x = get()
Expand All @@ -305,8 +305,8 @@ fun test() {
if (x == null) {
<!DEBUG_INFO_CONSTANT!>x<!>?.hashCode()
<!DEBUG_INFO_CONSTANT!>x<!>?.equals(1)
<!DEBUG_INFO_CONSTANT!>x<!>?.test2()
x.test2()
<!BUILDER_INFERENCE_STUB_RECEIVER, DEBUG_INFO_CONSTANT!>x<!>?.test2()
<!BUILDER_INFERENCE_STUB_RECEIVER!>x<!>.test2()
}

""
Expand Down
Loading

0 comments on commit 154e53c

Please sign in to comment.