Improve diagnostics for "not yet supported in inline"

- Tell user what exactly is not supported (e.g., local inline function)
- Reduce diagnostics range to a keyword or an identifier
  where appropriate

 #KT-16223 Fixed Target versions 1.1.50
This commit is contained in:
Dmitry Petrov
2017-09-05 16:38:13 +03:00
parent 160ba0c7c0
commit 2b27e64fc8
18 changed files with 105 additions and 30 deletions
@@ -45,7 +45,7 @@ class LocalFunInlineChecker : SimpleDeclarationChecker {
declaration is KtNamedFunction && declaration is KtNamedFunction &&
descriptor is FunctionDescriptor && descriptor is FunctionDescriptor &&
descriptor.visibility == Visibilities.LOCAL) { descriptor.visibility == Visibilities.LOCAL) {
diagnosticHolder.report(Errors.NOT_YET_SUPPORTED_IN_INLINE.on(declaration, declaration, descriptor)) diagnosticHolder.report(Errors.NOT_YET_SUPPORTED_IN_INLINE.on(declaration, "Local inline functions"))
} }
} }
} }
@@ -937,7 +937,7 @@ public interface Errors {
DiagnosticFactory2<KtElement, DeclarationDescriptor, DeclarationDescriptor> NON_PUBLIC_CALL_FROM_PUBLIC_INLINE = DiagnosticFactory2.create(ERROR, CALL_ELEMENT); DiagnosticFactory2<KtElement, DeclarationDescriptor, DeclarationDescriptor> NON_PUBLIC_CALL_FROM_PUBLIC_INLINE = DiagnosticFactory2.create(ERROR, CALL_ELEMENT);
DiagnosticFactory2<KtElement, DeclarationDescriptor, DeclarationDescriptor> PRIVATE_CLASS_MEMBER_FROM_INLINE = DiagnosticFactory2.create(ERROR, CALL_ELEMENT); DiagnosticFactory2<KtElement, DeclarationDescriptor, DeclarationDescriptor> PRIVATE_CLASS_MEMBER_FROM_INLINE = DiagnosticFactory2.create(ERROR, CALL_ELEMENT);
DiagnosticFactory1<KtElement, KtElement> NON_LOCAL_RETURN_NOT_ALLOWED = DiagnosticFactory1.create(ERROR, CALL_ELEMENT); DiagnosticFactory1<KtElement, KtElement> NON_LOCAL_RETURN_NOT_ALLOWED = DiagnosticFactory1.create(ERROR, CALL_ELEMENT);
DiagnosticFactory2<KtElement, KtNamedDeclaration, DeclarationDescriptor> NOT_YET_SUPPORTED_IN_INLINE = DiagnosticFactory2.create(ERROR); DiagnosticFactory1<KtDeclaration, String> NOT_YET_SUPPORTED_IN_INLINE = DiagnosticFactory1.create(ERROR, NOT_SUPPORTED_IN_INLINE_MOST_RELEVANT);
DiagnosticFactory1<PsiElement, DeclarationDescriptor> NOTHING_TO_INLINE = DiagnosticFactory1.create(WARNING); DiagnosticFactory1<PsiElement, DeclarationDescriptor> NOTHING_TO_INLINE = DiagnosticFactory1.create(WARNING);
DiagnosticFactory2<KtElement, KtExpression, DeclarationDescriptor> USAGE_IS_NOT_INLINABLE = DiagnosticFactory2.create(ERROR); DiagnosticFactory2<KtElement, KtExpression, DeclarationDescriptor> USAGE_IS_NOT_INLINABLE = DiagnosticFactory2.create(ERROR);
DiagnosticFactory2<KtElement, KtElement, DeclarationDescriptor> NULLABLE_INLINE_PARAMETER = DiagnosticFactory2.create(ERROR); DiagnosticFactory2<KtElement, KtElement, DeclarationDescriptor> NULLABLE_INLINE_PARAMETER = DiagnosticFactory2.create(ERROR);
@@ -184,6 +184,25 @@ object PositioningStrategies {
} }
} }
@JvmField val NOT_SUPPORTED_IN_INLINE_MOST_RELEVANT: PositioningStrategy<KtDeclaration> = object : PositioningStrategy<KtDeclaration>() {
override fun mark(element: KtDeclaration): List<TextRange> =
markElement(
when (element) {
is KtClassOrObject ->
element.getDeclarationKeyword() ?:
element.nameIdentifier ?:
element
is KtNamedFunction ->
element.modifierList?.getModifier(KtTokens.INLINE_KEYWORD) ?:
element.funKeyword ?:
element
else -> element
}
)
}
@JvmField val TYPE_PARAMETERS_OR_DECLARATION_SIGNATURE: PositioningStrategy<KtDeclaration> = object : PositioningStrategy<KtDeclaration>() { @JvmField val TYPE_PARAMETERS_OR_DECLARATION_SIGNATURE: PositioningStrategy<KtDeclaration> = object : PositioningStrategy<KtDeclaration>() {
override fun mark(element: KtDeclaration): List<TextRange> { override fun mark(element: KtDeclaration): List<TextRange> {
if (element is KtTypeParameterListOwner) { if (element is KtTypeParameterListOwner) {
@@ -851,7 +851,7 @@ public class DefaultErrorMessages {
//Inline //Inline
MAP.put(NON_PUBLIC_CALL_FROM_PUBLIC_INLINE, "Public-API inline function cannot access non-public-API ''{0}''", SHORT_NAMES_IN_TYPES, SHORT_NAMES_IN_TYPES); MAP.put(NON_PUBLIC_CALL_FROM_PUBLIC_INLINE, "Public-API inline function cannot access non-public-API ''{0}''", SHORT_NAMES_IN_TYPES, SHORT_NAMES_IN_TYPES);
MAP.put(PRIVATE_CLASS_MEMBER_FROM_INLINE, "Non-private inline function cannot access members of private classes: ''{0}''", SHORT_NAMES_IN_TYPES, SHORT_NAMES_IN_TYPES); MAP.put(PRIVATE_CLASS_MEMBER_FROM_INLINE, "Non-private inline function cannot access members of private classes: ''{0}''", SHORT_NAMES_IN_TYPES, SHORT_NAMES_IN_TYPES);
MAP.put(NOT_YET_SUPPORTED_IN_INLINE, "''{0}'' construction is not yet supported in inline functions", ELEMENT_TEXT, SHORT_NAMES_IN_TYPES); MAP.put(NOT_YET_SUPPORTED_IN_INLINE, "{0} are not yet supported in inline functions", STRING);
MAP.put(DECLARATION_CANT_BE_INLINED, "'inline' modifier is not allowed on virtual members. Only private or final members can be inlined"); MAP.put(DECLARATION_CANT_BE_INLINED, "'inline' modifier is not allowed on virtual members. Only private or final members can be inlined");
MAP.put(OVERRIDE_BY_INLINE, "Override by an inline function"); MAP.put(OVERRIDE_BY_INLINE, "Override by an inline function");
MAP.put(REIFIED_TYPE_PARAMETER_IN_OVERRIDE, "Override by a function with reified type parameter"); MAP.put(REIFIED_TYPE_PARAMETER_IN_OVERRIDE, "Override by a function with reified type parameter");
@@ -23,6 +23,7 @@ import com.intellij.psi.PsiElement
import com.intellij.psi.PsiWhiteSpace import com.intellij.psi.PsiWhiteSpace
import com.intellij.psi.impl.CheckUtil import com.intellij.psi.impl.CheckUtil
import com.intellij.psi.stubs.IStubElementType import com.intellij.psi.stubs.IStubElementType
import com.intellij.psi.tree.TokenSet
import com.intellij.psi.util.PsiTreeUtil import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.stubs.KotlinClassOrObjectStub import org.jetbrains.kotlin.psi.stubs.KotlinClassOrObjectStub
@@ -110,6 +111,11 @@ abstract class KtClassOrObject :
fun isAnnotation(): Boolean = hasModifier(KtTokens.ANNOTATION_KEYWORD) fun isAnnotation(): Boolean = hasModifier(KtTokens.ANNOTATION_KEYWORD)
fun getDeclarationKeyword(): PsiElement? =
findChildByType(TokenSet.create(
KtTokens.CLASS_KEYWORD, KtTokens.INTERFACE_KEYWORD, KtTokens.OBJECT_KEYWORD
))
override fun delete() { override fun delete() {
CheckUtil.checkWritable(this) CheckUtil.checkWritable(this)
@@ -67,7 +67,7 @@ class InlineAnalyzerExtension(
} }
override fun visitClass(klass: KtClass) { override fun visitClass(klass: KtClass) {
trace.report(Errors.NOT_YET_SUPPORTED_IN_INLINE.on(klass, klass, descriptor)) trace.report(Errors.NOT_YET_SUPPORTED_IN_INLINE.on(klass, "Local classes"))
} }
override fun visitNamedFunction(function: KtNamedFunction) { override fun visitNamedFunction(function: KtNamedFunction) {
@@ -75,7 +75,7 @@ class InlineAnalyzerExtension(
super.visitNamedFunction(function) super.visitNamedFunction(function)
} }
else { else {
trace.report(Errors.NOT_YET_SUPPORTED_IN_INLINE.on(function, function, descriptor)) trace.report(Errors.NOT_YET_SUPPORTED_IN_INLINE.on(function, "Local functions"))
} }
} }
} }
@@ -95,7 +95,7 @@ class InlineAnalyzerExtension(
val inheritDefaultValues = !parameter.declaresDefaultValue() val inheritDefaultValues = !parameter.declaresDefaultValue()
if (checkInlinableParameter(parameter, ktParameter, functionDescriptor, null) || inheritDefaultValues) { if (checkInlinableParameter(parameter, ktParameter, functionDescriptor, null) || inheritDefaultValues) {
if (inheritDefaultValues || !languageVersionSettings.supportsFeature(LanguageFeature.InlineDefaultFunctionalParameters)) { if (inheritDefaultValues || !languageVersionSettings.supportsFeature(LanguageFeature.InlineDefaultFunctionalParameters)) {
trace.report(Errors.NOT_YET_SUPPORTED_IN_INLINE.on(ktParameter, ktParameter, functionDescriptor)) trace.report(Errors.NOT_YET_SUPPORTED_IN_INLINE.on(ktParameter, "Functional parameters with inherited default values"))
} }
else { else {
checkDefaultValue(trace, parameter, ktParameter) checkDefaultValue(trace, parameter, ktParameter)
@@ -17,9 +17,9 @@ fun find(): Any? {
} }
fun find4(): Any? { fun find4(): Any? {
<!NOT_YET_SUPPORTED_IN_INLINE!>inline fun visit(element: Any) { <!NOT_YET_SUPPORTED_IN_INLINE!>inline<!> fun visit(element: Any) {
<!RETURN_NOT_ALLOWED!>return@find4<!> element <!RETURN_NOT_ALLOWED!>return@find4<!> element
}<!> }
return null return null
} }
+2 -2
View File
@@ -1,4 +1,4 @@
fun main(args: Array<String>) { fun main(args: Array<String>) {
<!NOT_YET_SUPPORTED_IN_INLINE!>inline fun a(){ <!NOT_YET_SUPPORTED_IN_INLINE!>inline<!> fun a(){
}<!> }
} }
@@ -0,0 +1,18 @@
<!NOTHING_TO_INLINE!>inline<!> fun inlineFun() {
<!NOT_YET_SUPPORTED_IN_INLINE(Local functions)!>fun<!> localFun() {}
<!NOT_YET_SUPPORTED_IN_INLINE(Local classes)!>class<!> LocalClass {}
}
fun outerFun() {
<!NOT_YET_SUPPORTED_IN_INLINE(Local inline functions)!>inline<!> fun localInlineFun() {}
}
abstract class Base {
abstract fun withDefault(f: () -> Unit = { -> })
}
class Derived : Base() {
<!OVERRIDE_BY_INLINE!>override final inline fun withDefault(
<!NOT_YET_SUPPORTED_IN_INLINE(Functional parameters with inherited default values)!>f: () -> Unit<!>
)<!> {}
}
@@ -0,0 +1,20 @@
package
public inline fun inlineFun(): kotlin.Unit
public fun outerFun(): kotlin.Unit
public abstract class Base {
public constructor Base()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
public abstract fun withDefault(/*0*/ f: () -> kotlin.Unit = ...): kotlin.Unit
}
public final class Derived : Base {
public constructor Derived()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
public final inline override /*1*/ fun withDefault(/*0*/ f: () -> kotlin.Unit = ...): kotlin.Unit
}
@@ -1,15 +1,15 @@
// !DIAGNOSTICS: -UNUSED_EXPRESSION -UNUSED_PARAMETER -UNUSED_VARIABLE -NOTHING_TO_INLINE // !DIAGNOSTICS: -UNUSED_EXPRESSION -UNUSED_PARAMETER -UNUSED_VARIABLE -NOTHING_TO_INLINE
inline fun <R> inlineFunOnlyLocal(crossinline p: () -> R) { inline fun <R> inlineFunOnlyLocal(crossinline p: () -> R) {
<!NOT_YET_SUPPORTED_IN_INLINE!>fun a() { <!NOT_YET_SUPPORTED_IN_INLINE!>fun<!> a() {
val z = p() val z = p()
}<!> }
a() a()
} }
inline fun <R> inlineFun(p: () -> R) { inline fun <R> inlineFun(p: () -> R) {
<!NOT_YET_SUPPORTED_IN_INLINE!>fun a() { <!NOT_YET_SUPPORTED_IN_INLINE!>fun<!> a() {
<!NON_LOCAL_RETURN_NOT_ALLOWED!>p<!>() <!NON_LOCAL_RETURN_NOT_ALLOWED!>p<!>()
}<!> }
a() a()
} }
@@ -1,23 +1,23 @@
// !DIAGNOSTICS: -UNUSED_EXPRESSION -UNUSED_PARAMETER -UNUSED_VARIABLE -NOTHING_TO_INLINE // !DIAGNOSTICS: -UNUSED_EXPRESSION -UNUSED_PARAMETER -UNUSED_VARIABLE -NOTHING_TO_INLINE
inline fun <R> inlineFunOnlyLocal(crossinline p: () -> R) { inline fun <R> inlineFunOnlyLocal(crossinline p: () -> R) {
<!NOT_YET_SUPPORTED_IN_INLINE!>class A { <!NOT_YET_SUPPORTED_IN_INLINE!>class<!> A {
val z = p() val z = p()
fun a() { fun a() {
p() p()
} }
}<!> }
} }
inline fun <R> inlineFun(p: () -> R) { inline fun <R> inlineFun(p: () -> R) {
<!NOT_YET_SUPPORTED_IN_INLINE!>class A { <!NOT_YET_SUPPORTED_IN_INLINE!>class<!> A {
val z = <!NON_LOCAL_RETURN_NOT_ALLOWED!>p<!>() val z = <!NON_LOCAL_RETURN_NOT_ALLOWED!>p<!>()
fun a() { fun a() {
<!NON_LOCAL_RETURN_NOT_ALLOWED!>p<!>() <!NON_LOCAL_RETURN_NOT_ALLOWED!>p<!>()
} }
}<!> }
} }
@@ -6,8 +6,8 @@ public fun test() {
} }
} }
<!NOT_YET_SUPPORTED_IN_INLINE!>inline fun localFun2() { <!NOT_YET_SUPPORTED_IN_INLINE!>inline<!> fun localFun2() {
Z().localFun() Z().localFun()
}<!> }
} }
@@ -4,8 +4,8 @@ public fun test() {
} }
<!NOT_YET_SUPPORTED_IN_INLINE!>inline fun localFun2() { <!NOT_YET_SUPPORTED_IN_INLINE!>inline<!> fun localFun2() {
localFun() localFun()
}<!> }
} }
@@ -3,18 +3,18 @@
inline val z: Int inline val z: Int
get() { get() {
<!NOT_YET_SUPPORTED_IN_INLINE!>class A { <!NOT_YET_SUPPORTED_IN_INLINE!>class<!> A {
fun a() { fun a() {
class AInner {} class AInner {}
} }
}<!> }
<!LOCAL_OBJECT_NOT_ALLOWED!>object B<!>{ <!LOCAL_OBJECT_NOT_ALLOWED!>object B<!>{
<!LOCAL_OBJECT_NOT_ALLOWED!>object BInner<!> {} <!LOCAL_OBJECT_NOT_ALLOWED!>object BInner<!> {}
} }
<!NOT_YET_SUPPORTED_IN_INLINE!>fun local() { <!NOT_YET_SUPPORTED_IN_INLINE!>fun<!> local() {
fun localInner() {} fun localInner() {}
}<!> }
return 1 return 1
} }
@@ -3,19 +3,19 @@
inline fun unsupported() { inline fun unsupported() {
<!NOT_YET_SUPPORTED_IN_INLINE!>class A { <!NOT_YET_SUPPORTED_IN_INLINE!>class<!> A {
fun a() { fun a() {
class AInner {} class AInner {}
} }
}<!> }
<!LOCAL_OBJECT_NOT_ALLOWED!>object B<!>{ <!LOCAL_OBJECT_NOT_ALLOWED!>object B<!>{
<!LOCAL_OBJECT_NOT_ALLOWED!>object BInner<!> {} <!LOCAL_OBJECT_NOT_ALLOWED!>object BInner<!> {}
} }
<!NOT_YET_SUPPORTED_IN_INLINE!>fun local() { <!NOT_YET_SUPPORTED_IN_INLINE!>fun<!> local() {
fun localInner() {} fun localInner() {}
}<!> }
} }
inline fun unsupportedDefault(<!NOT_YET_SUPPORTED_IN_INLINE!>s : ()->Unit = {}<!>) { inline fun unsupportedDefault(<!NOT_YET_SUPPORTED_IN_INLINE!>s : ()->Unit = {}<!>) {
@@ -11353,6 +11353,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
doTest(fileName); doTest(fileName);
} }
@TestMetadata("messagesForUnsupportedInInline.kt")
public void testMessagesForUnsupportedInInline() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/inline/messagesForUnsupportedInInline.kt");
doTest(fileName);
}
@TestMetadata("nonVirtualMembersWithInline.kt") @TestMetadata("nonVirtualMembersWithInline.kt")
public void testNonVirtualMembersWithInline() throws Exception { public void testNonVirtualMembersWithInline() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/inline/nonVirtualMembersWithInline.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/inline/nonVirtualMembersWithInline.kt");
@@ -11353,6 +11353,12 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing
doTest(fileName); doTest(fileName);
} }
@TestMetadata("messagesForUnsupportedInInline.kt")
public void testMessagesForUnsupportedInInline() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/inline/messagesForUnsupportedInInline.kt");
doTest(fileName);
}
@TestMetadata("nonVirtualMembersWithInline.kt") @TestMetadata("nonVirtualMembersWithInline.kt")
public void testNonVirtualMembersWithInline() throws Exception { public void testNonVirtualMembersWithInline() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/inline/nonVirtualMembersWithInline.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/inline/nonVirtualMembersWithInline.kt");