FIR IDE: Add quickfix for INAPPLICABLE_LATEINIT_MODIFIER.
Also changed FE1.0 checker and all related fix factories to report error on the declaration instead of the lateinit modifier. This is consistent with the direction of all checkers in FIR (no reporting on modifiers).
This commit is contained in:
committed by
Ilya Kirillov
parent
b1ab64e854
commit
4e44804c77
+1
@@ -42,6 +42,7 @@ enum class PositioningStrategy(private val strategy: String) {
|
||||
WHEN_EXPRESSION("WHEN_EXPRESSION"),
|
||||
IF_EXPRESSION("IF_EXPRESSION"),
|
||||
VARIANCE_MODIFIER("VARIANCE_MODIFIER"),
|
||||
LATEINIT_MODIFIER("LATEINIT_MODIFIER"),
|
||||
|
||||
;
|
||||
|
||||
|
||||
+1
-1
@@ -156,7 +156,7 @@ val DIAGNOSTICS_LIST = DiagnosticListBuilder.buildDiagnosticList {
|
||||
val INAPPLICABLE_CANDIDATE by error<FirSourceElement, PsiElement> {
|
||||
parameter<AbstractFirBasedSymbol<*>>("candidate")
|
||||
}
|
||||
val INAPPLICABLE_LATEINIT_MODIFIER by error<FirSourceElement, PsiElement> {
|
||||
val INAPPLICABLE_LATEINIT_MODIFIER by error<FirSourceElement, KtModifierListOwner>(PositioningStrategy.LATEINIT_MODIFIER) {
|
||||
parameter<String>("reason")
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -140,7 +140,7 @@ object FirErrors {
|
||||
// Applicability
|
||||
val NONE_APPLICABLE by error1<FirSourceElement, PsiElement, Collection<AbstractFirBasedSymbol<*>>>()
|
||||
val INAPPLICABLE_CANDIDATE by error1<FirSourceElement, PsiElement, AbstractFirBasedSymbol<*>>()
|
||||
val INAPPLICABLE_LATEINIT_MODIFIER by error1<FirSourceElement, PsiElement, String>()
|
||||
val INAPPLICABLE_LATEINIT_MODIFIER by error1<FirSourceElement, KtModifierListOwner, String>(SourceElementPositioningStrategies.LATEINIT_MODIFIER)
|
||||
|
||||
// Ambiguity
|
||||
val AMBIGUITY by error1<FirSourceElement, PsiElement, Collection<AbstractFirBasedSymbol<*>>>()
|
||||
|
||||
@@ -590,7 +590,7 @@ public interface Errors {
|
||||
DiagnosticFactory0<KtProperty> PRIVATE_PROPERTY_IN_INTERFACE = DiagnosticFactory0.create(ERROR, PRIVATE_MODIFIER);
|
||||
DiagnosticFactory0<KtProperty> BACKING_FIELD_IN_INTERFACE = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE);
|
||||
|
||||
DiagnosticFactory1<PsiElement, String> INAPPLICABLE_LATEINIT_MODIFIER = DiagnosticFactory1.create(ERROR);
|
||||
DiagnosticFactory1<KtModifierListOwner, String> INAPPLICABLE_LATEINIT_MODIFIER = DiagnosticFactory1.create(ERROR, LATEINIT_MODIFIER);
|
||||
DiagnosticFactory0<PsiElement> LATEINIT_INTRINSIC_CALL_ON_NON_LITERAL = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> LATEINIT_INTRINSIC_CALL_ON_NON_LATEINIT = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> LATEINIT_INTRINSIC_CALL_IN_INLINE_FUNCTION = DiagnosticFactory0.create(ERROR);
|
||||
|
||||
+14
-14
@@ -32,7 +32,7 @@ import org.jetbrains.kotlin.types.TypeUtils
|
||||
|
||||
object LateinitModifierApplicabilityChecker {
|
||||
fun checkLateinitModifierApplicability(trace: BindingTrace, ktDeclaration: KtCallableDeclaration, descriptor: VariableDescriptor) {
|
||||
val modifier = ktDeclaration.modifierList?.getModifier(KtTokens.LATEINIT_KEYWORD) ?: return
|
||||
if (!ktDeclaration.hasModifier(KtTokens.LATEINIT_KEYWORD)) return
|
||||
|
||||
val variables = when (descriptor) {
|
||||
is PropertyDescriptor -> "properties"
|
||||
@@ -43,37 +43,37 @@ object LateinitModifierApplicabilityChecker {
|
||||
val type = descriptor.type
|
||||
|
||||
if (!descriptor.isVar) {
|
||||
trace.report(Errors.INAPPLICABLE_LATEINIT_MODIFIER.on(modifier, "is allowed only on mutable $variables"))
|
||||
trace.report(Errors.INAPPLICABLE_LATEINIT_MODIFIER.on(ktDeclaration, "is allowed only on mutable $variables"))
|
||||
}
|
||||
|
||||
if (type.isInlineClassType()) {
|
||||
if (UnsignedTypes.isUnsignedType(type)) {
|
||||
trace.report(Errors.INAPPLICABLE_LATEINIT_MODIFIER.on(modifier, "is not allowed on $variables of unsigned types"))
|
||||
trace.report(Errors.INAPPLICABLE_LATEINIT_MODIFIER.on(ktDeclaration, "is not allowed on $variables of unsigned types"))
|
||||
} else {
|
||||
trace.report(Errors.INAPPLICABLE_LATEINIT_MODIFIER.on(modifier, "is not allowed on $variables of inline class types"))
|
||||
trace.report(Errors.INAPPLICABLE_LATEINIT_MODIFIER.on(ktDeclaration, "is not allowed on $variables of inline class types"))
|
||||
}
|
||||
}
|
||||
|
||||
if (type.isMarkedNullable) {
|
||||
trace.report(Errors.INAPPLICABLE_LATEINIT_MODIFIER.on(modifier, "is not allowed on $variables of nullable types"))
|
||||
trace.report(Errors.INAPPLICABLE_LATEINIT_MODIFIER.on(ktDeclaration, "is not allowed on $variables of nullable types"))
|
||||
} else if (TypeUtils.isNullableType(type)) {
|
||||
trace.report(
|
||||
Errors.INAPPLICABLE_LATEINIT_MODIFIER.on(
|
||||
modifier,
|
||||
ktDeclaration,
|
||||
"is not allowed on $variables of a type with nullable upper bound"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if (KotlinBuiltIns.isPrimitiveType(type)) {
|
||||
trace.report(Errors.INAPPLICABLE_LATEINIT_MODIFIER.on(modifier, "is not allowed on $variables of primitive types"))
|
||||
trace.report(Errors.INAPPLICABLE_LATEINIT_MODIFIER.on(ktDeclaration, "is not allowed on $variables of primitive types"))
|
||||
}
|
||||
|
||||
if (ktDeclaration is KtProperty) {
|
||||
if (ktDeclaration.hasDelegateExpression()) {
|
||||
trace.report(Errors.INAPPLICABLE_LATEINIT_MODIFIER.on(modifier, "is not allowed on delegated properties"))
|
||||
trace.report(Errors.INAPPLICABLE_LATEINIT_MODIFIER.on(ktDeclaration, "is not allowed on delegated properties"))
|
||||
} else if (ktDeclaration.hasInitializer()) {
|
||||
trace.report(Errors.INAPPLICABLE_LATEINIT_MODIFIER.on(modifier, "is not allowed on $variables with initializer"))
|
||||
trace.report(Errors.INAPPLICABLE_LATEINIT_MODIFIER.on(ktDeclaration, "is not allowed on $variables with initializer"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,28 +84,28 @@ object LateinitModifierApplicabilityChecker {
|
||||
val hasBackingField = trace.bindingContext.get(BindingContext.BACKING_FIELD_REQUIRED, descriptor) ?: false
|
||||
|
||||
if (ktDeclaration is KtParameter) {
|
||||
trace.report(Errors.INAPPLICABLE_LATEINIT_MODIFIER.on(modifier, "is not allowed on primary constructor parameters"))
|
||||
trace.report(Errors.INAPPLICABLE_LATEINIT_MODIFIER.on(ktDeclaration, "is not allowed on primary constructor parameters"))
|
||||
}
|
||||
|
||||
if (isAbstract) {
|
||||
trace.report(Errors.INAPPLICABLE_LATEINIT_MODIFIER.on(modifier, "is not allowed on abstract properties"))
|
||||
trace.report(Errors.INAPPLICABLE_LATEINIT_MODIFIER.on(ktDeclaration, "is not allowed on abstract properties"))
|
||||
}
|
||||
|
||||
if (!hasDelegateExpressionOrInitializer) {
|
||||
if (hasAccessorImplementation) {
|
||||
trace.report(
|
||||
Errors.INAPPLICABLE_LATEINIT_MODIFIER.on(
|
||||
modifier,
|
||||
ktDeclaration,
|
||||
"is not allowed on properties with a custom getter or setter"
|
||||
)
|
||||
)
|
||||
} else if (!isAbstract && !hasBackingField) {
|
||||
trace.report(Errors.INAPPLICABLE_LATEINIT_MODIFIER.on(modifier, "is not allowed on properties without backing field"))
|
||||
trace.report(Errors.INAPPLICABLE_LATEINIT_MODIFIER.on(ktDeclaration, "is not allowed on properties without backing field"))
|
||||
}
|
||||
}
|
||||
|
||||
if (descriptor.extensionReceiverParameter != null) {
|
||||
trace.report(Errors.INAPPLICABLE_LATEINIT_MODIFIER.on(modifier, "is not allowed on extension properties"))
|
||||
trace.report(Errors.INAPPLICABLE_LATEINIT_MODIFIER.on(ktDeclaration, "is not allowed on extension properties"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1104,6 +1104,7 @@ fun main(args: Array<String>) {
|
||||
|
||||
testClass<AbstractHighLevelQuickFixTest> {
|
||||
val pattern = "^([\\w\\-_]+)\\.kt$"
|
||||
model("quickfix/lateinit", pattern = pattern, filenameStartsLowerCase = true)
|
||||
model("quickfix/modifiers", pattern = pattern, filenameStartsLowerCase = true, recursive = false)
|
||||
model("quickfix/override/typeMismatchOnOverride", pattern = pattern, filenameStartsLowerCase = true, recursive = false)
|
||||
model("quickfix/variables/changeMutability", pattern = pattern, filenameStartsLowerCase = true, recursive = false)
|
||||
|
||||
@@ -37,6 +37,7 @@ class MainKtQuickFixRegistrar : KtQuickFixRegistrar() {
|
||||
|
||||
private val mutability = KtQuickFixesListBuilder.registerPsiQuickFix {
|
||||
registerPsiQuickFix<PsiElement, KtFirDiagnostic.VarOverriddenByVal>(ChangeVariableMutabilityFix.VAR_OVERRIDDEN_BY_VAL_FACTORY)
|
||||
registerPsiQuickFix<KtModifierListOwner, KtFirDiagnostic.InapplicableLateinitModifier>(ChangeVariableMutabilityFix.LATEINIT_VAL_FACTORY)
|
||||
}
|
||||
|
||||
override val list: KtQuickFixesList = KtQuickFixesList.createCombined(
|
||||
|
||||
Generated
+43
@@ -19,6 +19,49 @@ import java.util.regex.Pattern;
|
||||
@SuppressWarnings("all")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public class HighLevelQuickFixTestGenerated extends AbstractHighLevelQuickFixTest {
|
||||
@TestMetadata("idea/testData/quickfix/lateinit")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class Lateinit extends AbstractHighLevelQuickFixTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInLateinit() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/quickfix/lateinit"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), null, true);
|
||||
}
|
||||
|
||||
@TestMetadata("nullable.kt")
|
||||
public void testNullable() throws Exception {
|
||||
runTest("idea/testData/quickfix/lateinit/nullable.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("val.kt")
|
||||
public void testVal() throws Exception {
|
||||
runTest("idea/testData/quickfix/lateinit/val.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("withGetter.kt")
|
||||
public void testWithGetter() throws Exception {
|
||||
runTest("idea/testData/quickfix/lateinit/withGetter.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("withGetterSetter.kt")
|
||||
public void testWithGetterSetter() throws Exception {
|
||||
runTest("idea/testData/quickfix/lateinit/withGetterSetter.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("withInitializer.kt")
|
||||
public void testWithInitializer() throws Exception {
|
||||
runTest("idea/testData/quickfix/lateinit/withInitializer.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("withSetter.kt")
|
||||
public void testWithSetter() throws Exception {
|
||||
runTest("idea/testData/quickfix/lateinit/withSetter.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/quickfix/modifiers")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
+1
-1
@@ -372,7 +372,7 @@ sealed class KtFirDiagnostic<PSI: PsiElement> : KtDiagnosticWithPsi<PSI> {
|
||||
abstract val candidate: KtSymbol
|
||||
}
|
||||
|
||||
abstract class InapplicableLateinitModifier : KtFirDiagnostic<PsiElement>() {
|
||||
abstract class InapplicableLateinitModifier : KtFirDiagnostic<KtModifierListOwner>() {
|
||||
override val diagnosticClass get() = InapplicableLateinitModifier::class
|
||||
abstract val reason: String
|
||||
}
|
||||
|
||||
+1
-1
@@ -596,7 +596,7 @@ internal class InapplicableLateinitModifierImpl(
|
||||
override val reason: String,
|
||||
firDiagnostic: FirPsiDiagnostic<*>,
|
||||
override val token: ValidityToken,
|
||||
) : KtFirDiagnostic.InapplicableLateinitModifier(), KtAbstractFirDiagnostic<PsiElement> {
|
||||
) : KtFirDiagnostic.InapplicableLateinitModifier(), KtAbstractFirDiagnostic<KtModifierListOwner> {
|
||||
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -64,9 +64,9 @@ class ChangeVariableMutabilityFix(
|
||||
listOf(ChangeVariableMutabilityFix(psiElement, false))
|
||||
}
|
||||
|
||||
val LATEINIT_VAL_FACTORY: QuickFixesPsiBasedFactory<PsiElement> =
|
||||
quickFixesPsiBasedFactory { psiElement: PsiElement ->
|
||||
val property = psiElement.getStrictParentOfType<KtProperty>() ?: return@quickFixesPsiBasedFactory emptyList()
|
||||
val LATEINIT_VAL_FACTORY: QuickFixesPsiBasedFactory<KtModifierListOwner> =
|
||||
quickFixesPsiBasedFactory { psiElement: KtModifierListOwner ->
|
||||
val property = psiElement as? KtProperty ?: return@quickFixesPsiBasedFactory emptyList()
|
||||
if (property.valOrVarKeyword.text != "val") {
|
||||
emptyList()
|
||||
} else {
|
||||
|
||||
+1
-2
@@ -121,8 +121,7 @@ class RemoveModifierFix(
|
||||
|
||||
fun createRemoveLateinitFactory(): QuickFixesPsiBasedFactory<PsiElement> {
|
||||
return quickFixesPsiBasedFactory { psiElement: PsiElement ->
|
||||
val modifierList = psiElement.parent as? KtDeclarationModifierList ?: return@quickFixesPsiBasedFactory emptyList()
|
||||
val property = modifierList.parent as? KtProperty ?: return@quickFixesPsiBasedFactory emptyList()
|
||||
val property = psiElement as? KtProperty ?: return@quickFixesPsiBasedFactory emptyList()
|
||||
if (!property.hasModifier(KtTokens.LATEINIT_KEYWORD)) return@quickFixesPsiBasedFactory emptyList()
|
||||
listOf(RemoveModifierFix(property, KtTokens.LATEINIT_KEYWORD, isRedundant = false))
|
||||
}
|
||||
|
||||
+1
-2
@@ -40,8 +40,7 @@ class ConvertLateinitPropertyToNotNullDelegateFix(
|
||||
|
||||
companion object : KotlinSingleIntentionActionFactory() {
|
||||
override fun createAction(diagnostic: Diagnostic): KotlinQuickFixAction<KtProperty>? {
|
||||
val modifierList = diagnostic.psiElement.parent as? KtDeclarationModifierList ?: return null
|
||||
val property = modifierList.parent as? KtProperty ?: return null
|
||||
val property = diagnostic.psiElement as? KtProperty ?: return null
|
||||
if (!property.hasModifier(KtTokens.LATEINIT_KEYWORD) || !property.isVar || property.hasInitializer()) return null
|
||||
val typeReference = property.typeReference ?: return null
|
||||
val type = property.analyze(BodyResolveMode.PARTIAL)[BindingContext.TYPE, typeReference] ?: return null
|
||||
|
||||
@@ -58,8 +58,7 @@ class RemoveNullableFix(
|
||||
|
||||
object LATEINIT_FACTORY : KotlinSingleIntentionActionFactory() {
|
||||
override fun createAction(diagnostic: Diagnostic): KotlinQuickFixAction<KtNullableType>? {
|
||||
val lateinitElement = Errors.INAPPLICABLE_LATEINIT_MODIFIER.cast(diagnostic).psiElement
|
||||
val property = lateinitElement.getStrictParentOfType<KtProperty>() ?: return null
|
||||
val property = Errors.INAPPLICABLE_LATEINIT_MODIFIER.cast(diagnostic).psiElement as? KtProperty ?: return null
|
||||
val typeReference = property.typeReference ?: return null
|
||||
val typeElement = (typeReference.typeElement ?: return null) as? KtNullableType ?: return null
|
||||
if (typeElement.innerType == null) return null
|
||||
|
||||
@@ -118,8 +118,7 @@ open class RemovePartsFromPropertyFix(
|
||||
|
||||
object LateInitFactory : KotlinSingleIntentionActionFactory() {
|
||||
public override fun createAction(diagnostic: Diagnostic): KotlinQuickFixAction<KtProperty>? {
|
||||
val element = Errors.INAPPLICABLE_LATEINIT_MODIFIER.cast(diagnostic).psiElement
|
||||
val property = PsiTreeUtil.getParentOfType(element, KtProperty::class.java) ?: return null
|
||||
val property = Errors.INAPPLICABLE_LATEINIT_MODIFIER.cast(diagnostic).psiElement as? KtProperty ?: return null
|
||||
val hasInitializer = property.hasInitializer()
|
||||
val hasGetter = property.getter?.bodyExpression != null
|
||||
val hasSetter = property.setter?.bodyExpression != null
|
||||
|
||||
+2
@@ -1,3 +1,5 @@
|
||||
// FIR_COMPARISON
|
||||
|
||||
class C<V>() {
|
||||
<error descr="[INAPPLICABLE_LATEINIT_MODIFIER] 'lateinit' modifier is not allowed on properties of a type with nullable upper bound">lateinit</error> var item: V
|
||||
}
|
||||
+2
-1
@@ -2,4 +2,5 @@
|
||||
|
||||
class A() {
|
||||
<caret>lateinit val foo: String
|
||||
}
|
||||
}
|
||||
/* FIR_COMPARISON */
|
||||
+2
-1
@@ -2,4 +2,5 @@
|
||||
|
||||
class A() {
|
||||
<caret>lateinit var foo: String
|
||||
}
|
||||
}
|
||||
/* FIR_COMPARISON */
|
||||
Reference in New Issue
Block a user