[FIR] KT-54978 Prohibit explicit type arguments in property accesses

- Add a checker which ensures that property accesses have no explicit
  type arguments. If an error on the property access's callee reference
  already exists, the new error is not reported in favor of the existing
  error, as the property access may have been intended to be a function
  call.
- `complicatedLTGT.fir.kt`: The underlying parser issue is not yet
  solved, which is why `x` is parsed as a property access with explicit
  type arguments.
- `reservedExpressionSyntax` tests: This new check makes a lot of the
  access expressions in these tests illegal, so valid lines have been
  added and invalid lines appropriately marked with
  `EXPLICIT_TYPE_ARGUMENTS_IN_PROPERTY_ACCESS` errors.

^KT-54978 fixed
This commit is contained in:
Marco Pennekamp
2022-11-23 16:04:07 +01:00
committed by teamcity
parent 4d20b43619
commit d4e6a4ad54
26 changed files with 281 additions and 16 deletions
@@ -0,0 +1,88 @@
FILE: propertyAccessWithExplicitTypeArguments.kt
public final fun f1(x: R|kotlin/Int|): R|kotlin/Unit| {
lval y: R|kotlin/Int| = Int(5)
lval s: R|kotlin/String| = String(hello)
R|<local>/x|<R|kotlin/Int|>
R|<local>/x|<R|kotlin/String|, R|kotlin/String|>
R|<local>/y|<R|kotlin/Int|>
R|<local>/y|<R|kotlin/String|, R|kotlin/Int|>
R|<local>/s|<R|kotlin/String|>
R|<local>/s|<R|kotlin/Int|, R|kotlin/String|>
}
public final val property: R|kotlin/Int| = Int(10)
public get(): R|kotlin/Int|
public final fun f2(): R|kotlin/Unit| {
R|/property|<R|kotlin/String|>
}
public final val property2: R|kotlin/Int|
public get(): R|kotlin/Int| {
^ Int(10)
}
public final fun f3(): R|kotlin/Unit| {
R|/property2|<R|kotlin/String|>
}
public abstract interface Context<A> : R|kotlin/Any| {
}
public final class ContextImpl<A> : R|Context<A>| {
public constructor<A>(): R|ContextImpl<A>| {
super<R|kotlin/Any|>()
}
}
public final class Receiver<A> : R|kotlin/Any| {
public constructor<A>(): R|Receiver<A>| {
super<R|kotlin/Any|>()
}
}
public final val <A> R|Receiver<A>|.hello1: R|kotlin/String|
public get(): R|kotlin/String| {
^ String(hello 1)
}
context(R|Context<A>|)
public final val <A> hello2: R|kotlin/String|
public get(): R|kotlin/String| {
^ String(hello 2)
}
context(R|Context<B>|)
public final val <A, B> R|Receiver<A>|.hello3: R|kotlin/String|
public get(): R|kotlin/String| {
^ String(hello 3)
}
public final operator fun <A, B> R|kotlin/String|.invoke(): R|kotlin/String| {
^invoke String(world)
}
public final fun f4(): R|kotlin/Unit| {
lval receiver: R|Receiver<kotlin/Int>| = R|/Receiver.Receiver|<R|kotlin/Int|>()
R|<local>/receiver|.R|/hello1|<R|kotlin/Int|>
R|<local>/receiver|.R|/hello1|<R|kotlin/Int|>
R|<local>/receiver|.R|/hello1|<R|kotlin/Int|>.R|/invoke|<R|kotlin/Int|, R|kotlin/String|>()
R|<local>/receiver|.R|/hello1|<R|kotlin/Int|>
R|<local>/receiver|.R|/hello1|<R|kotlin/Int|>
R|<local>/receiver|.R|/hello1|<R|kotlin/Int|>
R|kotlin/with|<R|ContextImpl<kotlin/String>|, R|kotlin/String|>(R|/ContextImpl.ContextImpl|<R|kotlin/String|>(), <L> = with@fun R|ContextImpl<kotlin/String>|.<anonymous>(): R|kotlin/String| <inline=Inline, kind=EXACTLY_ONCE> {
R|/hello2|<R|kotlin/String|>
R|/hello2|<R|kotlin/String|>
R|/hello2|<R|kotlin/String|>.R|/invoke|<R|kotlin/String|, R|kotlin/Int|>()
R|/hello2|<R|kotlin/String|>
R|/hello2|<R|kotlin/String|>
R|/hello2|<R|kotlin/String|>
R|<local>/receiver|.R|/hello3|<R|kotlin/Int|, R|kotlin/String|>
R|<local>/receiver|.R|/hello3|<R|kotlin/Int|, R|kotlin/String|>
R|<local>/receiver|.R|/hello3|<R|kotlin/Int|, R|kotlin/String|>.R|/invoke|<R|kotlin/Int|, R|kotlin/String|>()
R|<local>/receiver|.R|/hello3|<R|kotlin/Int|, R|kotlin/String|>
R|<local>/receiver|.R|/hello3|<R|kotlin/Int|, R|kotlin/String|>
^ R|<local>/receiver|.R|/hello3|<R|kotlin/Int|, R|kotlin/String|>
}
)
}
public final val <reified A> R|Receiver<A>|.helloReified: R|kotlin/String|
public inline get(): R|kotlin/String| {
^ String(hello)
}
public final fun f5(): R|kotlin/Unit| {
lval receiver: R|Receiver<kotlin/Int>| = R|/Receiver.Receiver|<R|kotlin/Int|>()
R|<local>/receiver|.R|/helloReified|<R|kotlin/Int|>
R|<local>/receiver|.R|/helloReified|<R|kotlin/Int|>
R|<local>/receiver|.R|/helloReified|<R|kotlin/Int|>
}
@@ -0,0 +1,85 @@
// ISSUE: KT-54978
// Case 1: Parameters and local variables
fun f1(x: Int) {
val y = 5
val s = "hello"
<!EXPLICIT_TYPE_ARGUMENTS_IN_PROPERTY_ACCESS!>x<!><Int>
<!EXPLICIT_TYPE_ARGUMENTS_IN_PROPERTY_ACCESS!>x<!><String, String>
<!EXPLICIT_TYPE_ARGUMENTS_IN_PROPERTY_ACCESS!>y<!><Int>
<!EXPLICIT_TYPE_ARGUMENTS_IN_PROPERTY_ACCESS!>y<!><String, Int>
<!EXPLICIT_TYPE_ARGUMENTS_IN_PROPERTY_ACCESS!>s<!><String>
<!EXPLICIT_TYPE_ARGUMENTS_IN_PROPERTY_ACCESS!>s<!><Int, String>
}
// Case 2: Simple property
val property: Int = 10
fun f2() {
<!EXPLICIT_TYPE_ARGUMENTS_IN_PROPERTY_ACCESS!>property<!><String>
}
// Case 3: Simple property with getter
val property2: Int
get() = 10
fun f3() {
<!EXPLICIT_TYPE_ARGUMENTS_IN_PROPERTY_ACCESS!>property2<!><String>
}
// Case 4: Property with extension and/or context receiver
interface Context<A>
class ContextImpl<A> : Context<A>
class Receiver<A>
val <A> Receiver<A>.hello1: String
get() = "hello 1"
context(Context<A>)
val <A> hello2: String
get() = "hello 2"
context(Context<B>)
val <A, B> Receiver<A>.hello3: String
get() = "hello 3"
operator fun <A, B> String.invoke(): String = "world"
fun f4() {
val receiver = Receiver<Int>()
receiver.hello1
receiver.<!EXPLICIT_TYPE_ARGUMENTS_IN_PROPERTY_ACCESS!>hello1<!><Int>
receiver.hello1<Int, String>() // legal `String.invoke` call
receiver.<!EXPLICIT_TYPE_ARGUMENTS_IN_PROPERTY_ACCESS!>hello1<!><String>
receiver.<!EXPLICIT_TYPE_ARGUMENTS_IN_PROPERTY_ACCESS!>hello1<!><Int, String>
receiver.<!EXPLICIT_TYPE_ARGUMENTS_IN_PROPERTY_ACCESS!>hello1<!><Int, String, String>
with (ContextImpl<String>()) {
hello2
<!EXPLICIT_TYPE_ARGUMENTS_IN_PROPERTY_ACCESS!>hello2<!><String>
hello2<String, Int>() // legal `String.invoke` call
<!EXPLICIT_TYPE_ARGUMENTS_IN_PROPERTY_ACCESS!>hello2<!><Int>
<!EXPLICIT_TYPE_ARGUMENTS_IN_PROPERTY_ACCESS!>hello2<!><String, Int>
<!EXPLICIT_TYPE_ARGUMENTS_IN_PROPERTY_ACCESS!>hello2<!><String, Int, Int>
receiver.hello3
receiver.<!EXPLICIT_TYPE_ARGUMENTS_IN_PROPERTY_ACCESS!>hello3<!><Int, String>
receiver.hello3<Int, String>() // legal `String.invoke` call
receiver.<!EXPLICIT_TYPE_ARGUMENTS_IN_PROPERTY_ACCESS!>hello3<!><String, Int>
receiver.<!EXPLICIT_TYPE_ARGUMENTS_IN_PROPERTY_ACCESS!>hello3<!><Int>
receiver.<!EXPLICIT_TYPE_ARGUMENTS_IN_PROPERTY_ACCESS!>hello3<!><Int, String, String>
}
}
// Case 5: Property with receiver and reified type parameter
inline val <reified A> Receiver<A>.helloReified: String
get() = "hello"
fun f5() {
val receiver = Receiver<Int>()
receiver.helloReified
receiver.<!EXPLICIT_TYPE_ARGUMENTS_IN_PROPERTY_ACCESS!>helloReified<!><Int>
receiver.<!EXPLICIT_TYPE_ARGUMENTS_IN_PROPERTY_ACCESS!>helloReified<!><String>
}