FIR: Fix false positive INAPPLICABLE_INFIX_MODIFIER

This commit is contained in:
Denis Zharkov
2020-04-20 15:29:14 +03:00
parent 27b860682b
commit fa3b3e7a9a
16 changed files with 65 additions and 42 deletions
@@ -10,4 +10,15 @@ infix class A
infix typealias B = A infix typealias B = A
infix val x = 1 infix val x = 1
class C {
infix fun good(x: Int) {}
infix fun Int.goodAsWell(x: Int) {}
<!INAPPLICABLE_INFIX_MODIFIER!>infix fun Int.foo(x: Int, y: Int) {}<!>
<!INAPPLICABLE_INFIX_MODIFIER!>infix fun Int.bar() {}<!>
<!INAPPLICABLE_INFIX_MODIFIER!>infix fun baz(x: Int, y: Int) {}<!>
}
@@ -16,3 +16,24 @@ FILE: infixFunctions.kt
public final typealias B = R|A| public final typealias B = R|A|
public final val x: R|kotlin/Int| = Int(1) public final val x: R|kotlin/Int| = Int(1)
public get(): R|kotlin/Int| public get(): R|kotlin/Int|
public final class C : R|kotlin/Any| {
public constructor(): R|C| {
super<R|kotlin/Any|>()
}
public final infix fun good(x: R|kotlin/Int|): R|kotlin/Unit| {
}
public final infix fun R|kotlin/Int|.goodAsWell(x: R|kotlin/Int|): R|kotlin/Unit| {
}
public final infix fun R|kotlin/Int|.foo(x: R|kotlin/Int|, y: R|kotlin/Int|): R|kotlin/Unit| {
}
public final infix fun R|kotlin/Int|.bar(): R|kotlin/Unit| {
}
public final infix fun baz(x: R|kotlin/Int|, y: R|kotlin/Int|): R|kotlin/Unit| {
}
}
@@ -1,7 +1,7 @@
interface List<out T : Any> { interface List<out T : Any> {
operator fun get(index: Int): T operator fun get(index: Int): T
<!INAPPLICABLE_INFIX_MODIFIER!>infix fun concat(other: List<T>): List<T><!> infix fun concat(other: List<T>): List<T>
} }
typealias StringList = List<out String> typealias StringList = List<out String>
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.fir.FirSourceElement
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
import org.jetbrains.kotlin.fir.declarations.isInfix import org.jetbrains.kotlin.fir.declarations.isInfix
@@ -16,7 +17,7 @@ import org.jetbrains.kotlin.fir.declarations.isInfix
object FirInfixFunctionDeclarationChecker : FirDeclarationChecker<FirMemberDeclaration>() { object FirInfixFunctionDeclarationChecker : FirDeclarationChecker<FirMemberDeclaration>() {
override fun check(declaration: FirMemberDeclaration, context: CheckerContext, reporter: DiagnosticReporter) { override fun check(declaration: FirMemberDeclaration, context: CheckerContext, reporter: DiagnosticReporter) {
if (declaration is FirSimpleFunction && declaration.isInfix) { if (declaration is FirSimpleFunction && declaration.isInfix) {
if (declaration.valueParameters.size != 1 || declaration.receiverTypeRef == null) { if (declaration.valueParameters.size != 1 || !hasExtensionOrDispatchReceiver(declaration, context)) {
reporter.report(declaration.source) reporter.report(declaration.source)
} }
return return
@@ -26,7 +27,15 @@ object FirInfixFunctionDeclarationChecker : FirDeclarationChecker<FirMemberDecla
} }
} }
private fun hasExtensionOrDispatchReceiver(
function: FirSimpleFunction,
context: CheckerContext
): Boolean {
if (function.receiverTypeRef != null) return true
return context.containingDeclarations.lastOrNull() is FirClass<*>
}
private fun DiagnosticReporter.report(source: FirSourceElement?) { private fun DiagnosticReporter.report(source: FirSourceElement?) {
source?.let { report(FirErrors.INAPPLICABLE_INFIX_MODIFIER.on(it, "Inapplicable infix modifier")) } source?.let { report(FirErrors.INAPPLICABLE_INFIX_MODIFIER.on(it, "Inapplicable infix modifier")) }
} }
} }
+1 -1
View File
@@ -29,5 +29,5 @@ infix fun String.on(predicate : (s : URI) -> Boolean) : URI {
} }
class URI(val body : Any) { class URI(val body : Any) {
<!INAPPLICABLE_INFIX_MODIFIER!>infix fun to(dest : String) {}<!> infix fun to(dest : String) {}
} }
+2 -2
View File
@@ -1,7 +1,7 @@
class Pair<out A, out B>(val first: A, val second: B) class Pair<out A, out B>(val first: A, val second: B)
class Example { class Example {
<!INAPPLICABLE_INFIX_MODIFIER!>infix fun to(other: Example) = Pair(this, other)<!> infix fun to(other: Example) = Pair(this, other)
fun toNonInfix(other: Example) = Pair(this, other) fun toNonInfix(other: Example) = Pair(this, other)
} }
@@ -35,4 +35,4 @@ fun test() {
a toExtNonInfixDefaultValues b a toExtNonInfixDefaultValues b
a withLambda { } a withLambda { }
} }
@@ -6,7 +6,7 @@ infix fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that)
// OK // OK
infix fun String.ok1(o: String) {} infix fun String.ok1(o: String) {}
class OkTest { class OkTest {
<!INAPPLICABLE_INFIX_MODIFIER!>infix fun ok2(o: String) {}<!> infix fun ok2(o: String) {}
infix fun String.ok3(o: String) {} infix fun String.ok3(o: String) {}
} }
@@ -24,4 +24,4 @@ class Example {
<!INAPPLICABLE_INFIX_MODIFIER!>infix fun e8(s: String, a: Int = 0) {}<!> <!INAPPLICABLE_INFIX_MODIFIER!>infix fun e8(s: String, a: Int = 0) {}<!>
<!INAPPLICABLE_INFIX_MODIFIER!>infix fun e9(s: String, a: Int) {}<!> <!INAPPLICABLE_INFIX_MODIFIER!>infix fun e9(s: String, a: Int) {}<!>
<!INAPPLICABLE_INFIX_MODIFIER!>infix fun e10() {}<!> <!INAPPLICABLE_INFIX_MODIFIER!>infix fun e10() {}<!>
} }
+2 -2
View File
@@ -59,9 +59,9 @@ interface Example {
operator fun hasNext(): Boolean operator fun hasNext(): Boolean
operator fun hasNext(n: Int): String operator fun hasNext(n: Int): String
<!INAPPLICABLE_INFIX_MODIFIER!>infix fun i1(n: Int)<!> infix fun i1(n: Int)
<!INAPPLICABLE_INFIX_MODIFIER!>infix fun i1(n: Int, n2: Int)<!> <!INAPPLICABLE_INFIX_MODIFIER!>infix fun i1(n: Int, n2: Int)<!>
<!INAPPLICABLE_INFIX_MODIFIER!>infix fun i1(vararg n: Int)<!> infix fun i1(vararg n: Int)
} }
class OkDelegates { class OkDelegates {
@@ -1,19 +0,0 @@
//KT-4529 Lambdas are analyzed improperly in an infix call nested inside a println
class G {
<!INAPPLICABLE_INFIX_MODIFIER!>infix fun foo(bar: (Int) -> Int) = bar<!>
}
fun main() {
use(
G().foo {it + 11} // no error
)
use(
G() foo {it + 11} // ERROR
)
use(
G() foo ({it + 1}) // 2 ERRORs
)
}
fun use(a: Any?) = a
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
//KT-4529 Lambdas are analyzed improperly in an infix call nested inside a println //KT-4529 Lambdas are analyzed improperly in an infix call nested inside a println
class G { class G {
@@ -4,7 +4,7 @@ interface Foo
fun (Foo.() -> Unit).invoke(b : Foo.() -> Unit) {} fun (Foo.() -> Unit).invoke(b : Foo.() -> Unit) {}
object Z { object Z {
<!INAPPLICABLE_INFIX_MODIFIER!>infix fun add(b : Foo.() -> Unit) : Z = Z<!> infix fun add(b : Foo.() -> Unit) : Z = Z
} }
val t2 = Z <!INAPPLICABLE_CANDIDATE!>add<!> { } { } val t2 = Z <!INAPPLICABLE_CANDIDATE!>add<!> { } { }
@@ -1,9 +1,9 @@
// !DIAGNOSTICS: -UNUSED_EXPRESSION -UNUSED_PARAMETER -UNUSED_VARIABLE -NOTHING_TO_INLINE // !DIAGNOSTICS: -UNUSED_EXPRESSION -UNUSED_PARAMETER -UNUSED_VARIABLE -NOTHING_TO_INLINE
class Z { class Z {
<!INAPPLICABLE_INFIX_MODIFIER!>inline infix fun <R> inlineFun(crossinline p: () -> R) { inline infix fun <R> inlineFun(crossinline p: () -> R) {
p() p()
}<!> }
} }
fun <R> fun1(p: () -> R) { fun <R> fun1(p: () -> R) {
@@ -1,10 +1,10 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER, -EXTENSION_SHADOWED_BY_MEMBER // !DIAGNOSTICS: -UNUSED_PARAMETER, -EXTENSION_SHADOWED_BY_MEMBER
class Example { class Example {
<!INAPPLICABLE_INFIX_MODIFIER!>operator infix fun plus(other: Example) = 0<!> operator infix fun plus(other: Example) = 0
fun minus(other: Example) = 0 fun minus(other: Example) = 0
<!INAPPLICABLE_INFIX_MODIFIER!>operator infix fun times(other: Example) = 0<!> operator infix fun times(other: Example) = 0
fun div(other: Example) = 0 fun div(other: Example) = 0
} }
@@ -42,4 +42,4 @@ fun a() {
} }
fun consumeInt(i: Int) {} fun consumeInt(i: Int) {}
fun consumeString(s: String) {} fun consumeString(s: String) {}
@@ -1,8 +1,8 @@
// !WITH_NEW_INFERENCE // !WITH_NEW_INFERENCE
class A() { class A() {
<!INAPPLICABLE_INFIX_MODIFIER!>operator infix fun plus(i : Int) {}<!> operator infix fun plus(i : Int) {}
operator fun unaryMinus() {} operator fun unaryMinus() {}
<!INAPPLICABLE_INFIX_MODIFIER!>operator infix fun contains(a : Any?) : Boolean = true<!> operator infix fun contains(a : Any?) : Boolean = true
} }
operator infix fun A.div(i : Int) {} operator infix fun A.div(i : Int) {}
@@ -1,7 +1,7 @@
interface IFoo { interface IFoo {
fun foo(i: Int): Int fun foo(i: Int): Int
<!INAPPLICABLE_INFIX_MODIFIER!>infix fun bar(i: Int): Int<!> infix fun bar(i: Int): Int
} }
infix fun IFoo.foo(i: Int) = i infix fun IFoo.foo(i: Int) = i
infix fun IFoo.bar(i: Int) = i infix fun IFoo.bar(i: Int) = i
@@ -1,7 +1,7 @@
package o package o
class A { class A {
<!INAPPLICABLE_INFIX_MODIFIER!>infix fun foo(b: B) = b<!> infix fun foo(b: B) = b
} }
class B { class B {
@@ -11,4 +11,4 @@ class B {
fun test(a: A, b: B?) { fun test(a: A, b: B?) {
a foo b!! a foo b!!
b.bar() b.bar()
} }