[FIR] Syntax checkers
Introduce base class for checkers that dive into source trees.
This commit is contained in:
committed by
teamcityserver
parent
1be39cb505
commit
787c743333
+70
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.fir.analysis.checkers
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.KtNodeTypes
|
||||
import org.jetbrains.kotlin.fir.FirElement
|
||||
import org.jetbrains.kotlin.fir.FirLightSourceElement
|
||||
import org.jetbrains.kotlin.fir.FirPsiSourceElement
|
||||
import org.jetbrains.kotlin.fir.FirSourceElement
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.getAncestors
|
||||
import org.jetbrains.kotlin.fir.types.FirTypeRef
|
||||
import org.jetbrains.kotlin.psi.KtConstructorCalleeExpression
|
||||
import org.jetbrains.kotlin.psi.KtTypeReference
|
||||
|
||||
/**
|
||||
* Service to answer source-related questions in generic fashion.
|
||||
* Shouldn't expose (receive or return) any specific source tree types
|
||||
*/
|
||||
interface SourceNavigator {
|
||||
|
||||
fun FirTypeRef.isInConstructorCallee(): Boolean
|
||||
|
||||
fun FirTypeRef.isInTypeConstraint(): Boolean
|
||||
|
||||
companion object {
|
||||
|
||||
private val lightTreeInstance = LightTreeSourceNavigator()
|
||||
|
||||
fun forElement(e: FirElement): SourceNavigator = when (e.source) {
|
||||
is FirLightSourceElement -> lightTreeInstance
|
||||
is FirPsiSourceElement<*> -> PsiSourceNavigator
|
||||
null -> lightTreeInstance //shouldn't matter
|
||||
}
|
||||
|
||||
inline fun <R> FirElement.withNavigator(block: SourceNavigator.() -> R): R = with(forElement(this), block)
|
||||
}
|
||||
}
|
||||
|
||||
open class LightTreeSourceNavigator : SourceNavigator {
|
||||
|
||||
private fun <T> FirElement.withSource(f: (FirSourceElement) -> T): T? =
|
||||
source?.let { f(it) }
|
||||
|
||||
override fun FirTypeRef.isInConstructorCallee(): Boolean = withSource { source ->
|
||||
source.treeStructure.getParent(source.lighterASTNode)?.tokenType == KtNodeTypes.CONSTRUCTOR_CALLEE
|
||||
} ?: false
|
||||
|
||||
override fun FirTypeRef.isInTypeConstraint(): Boolean {
|
||||
val source = source ?: return false
|
||||
return source.treeStructure.getAncestors(source.lighterASTNode)
|
||||
.find { it.tokenType == KtNodeTypes.TYPE_CONSTRAINT || it.tokenType == KtNodeTypes.TYPE_PARAMETER }
|
||||
?.tokenType == KtNodeTypes.TYPE_CONSTRAINT
|
||||
}
|
||||
}
|
||||
|
||||
//by default psi tree can reuse light tree manipulations
|
||||
object PsiSourceNavigator : LightTreeSourceNavigator() {
|
||||
|
||||
//Swallows incorrect casts!!!
|
||||
private inline fun <reified P : PsiElement> FirElement.psi(): P? {
|
||||
val psi = (source as? FirPsiSourceElement<*>)?.psi
|
||||
return psi as? P
|
||||
}
|
||||
|
||||
override fun FirTypeRef.isInConstructorCallee(): Boolean = psi<KtTypeReference>()?.parent is KtConstructorCalleeExpression
|
||||
}
|
||||
-29
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.fir.analysis.checkers.declaration
|
||||
|
||||
import org.jetbrains.kotlin.KtNodeTypes
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
|
||||
import org.jetbrains.kotlin.fir.declarations.isInterface
|
||||
|
||||
object FirDelegationInInterfaceChecker : FirRegularClassChecker() {
|
||||
override fun check(declaration: FirRegularClass, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
if (!declaration.isInterface) {
|
||||
return
|
||||
}
|
||||
|
||||
for (superTypeRef in declaration.superTypeRefs) {
|
||||
val source = superTypeRef.source ?: continue
|
||||
if (source.treeStructure.getParent(source.lighterASTNode)?.tokenType == KtNodeTypes.DELEGATED_SUPER_TYPE_ENTRY) {
|
||||
reporter.reportOn(source, FirErrors.DELEGATION_IN_INTERFACE, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-31
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.fir.analysis.checkers.declaration
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.*
|
||||
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
|
||||
import org.jetbrains.kotlin.fir.toFirLightSourceElement
|
||||
|
||||
object FirFunctionTypeParametersChecker : FirSimpleFunctionChecker() {
|
||||
|
||||
override fun check(declaration: FirSimpleFunction, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
declaration.source?.let { source ->
|
||||
if (source.kind is FirFakeSourceElementKind) return
|
||||
|
||||
val typeParamsNode = source.treeStructure.typeParametersList(source.lighterASTNode)
|
||||
val nameNode = source.treeStructure.nameIdentifier(source.lighterASTNode)
|
||||
if (typeParamsNode != null && nameNode != null && typeParamsNode.startOffset > nameNode.startOffset) {
|
||||
reporter.reportOn(
|
||||
source,
|
||||
FirErrors.DEPRECATED_TYPE_PARAMETER_SYNTAX,
|
||||
context
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
-8
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.fir.analysis.checkers.declaration
|
||||
|
||||
import org.jetbrains.kotlin.KtNodeTypes
|
||||
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.SourceNavigator
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.toRegularClass
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||
@@ -25,10 +26,11 @@ import org.jetbrains.kotlin.utils.addToStdlib.lastIsInstanceOrNull
|
||||
object FirPrimaryConstructorSuperTypeChecker : FirRegularClassChecker() {
|
||||
override fun check(declaration: FirRegularClass, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
if (declaration.isInterface) {
|
||||
for (superTypeRef in declaration.superTypeRefs) {
|
||||
val source = superTypeRef.source ?: continue
|
||||
if (source.treeStructure.getParent(source.lighterASTNode)?.tokenType == KtNodeTypes.CONSTRUCTOR_CALLEE) {
|
||||
reporter.reportOn(source, FirErrors.SUPERTYPE_INITIALIZED_IN_INTERFACE, context)
|
||||
with(SourceNavigator.forElement(declaration)) {
|
||||
for (superTypeRef in declaration.superTypeRefs) {
|
||||
if (superTypeRef.isInConstructorCallee()) {
|
||||
reporter.reportOn(superTypeRef.source, FirErrors.SUPERTYPE_INITIALIZED_IN_INTERFACE, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
@@ -97,10 +99,11 @@ object FirPrimaryConstructorSuperTypeChecker : FirRegularClassChecker() {
|
||||
reporter: DiagnosticReporter,
|
||||
context: CheckerContext
|
||||
) {
|
||||
for (superTypeRef in regularClass.superTypeRefs) {
|
||||
val source = superTypeRef.source ?: continue
|
||||
if (source.treeStructure.getParent(source.lighterASTNode)?.tokenType == KtNodeTypes.CONSTRUCTOR_CALLEE) {
|
||||
reporter.reportOn(regularClass.source, FirErrors.SUPERTYPE_INITIALIZED_WITHOUT_PRIMARY_CONSTRUCTOR, context)
|
||||
with(SourceNavigator.forElement(regularClass)) {
|
||||
for (superTypeRef in regularClass.superTypeRefs) {
|
||||
if (superTypeRef.isInConstructorCallee()) {
|
||||
reporter.reportOn(regularClass.source, FirErrors.SUPERTYPE_INITIALIZED_WITHOUT_PRIMARY_CONSTRUCTOR, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-22
@@ -5,14 +5,12 @@
|
||||
|
||||
package org.jetbrains.kotlin.fir.analysis.checkers.declaration
|
||||
|
||||
import org.jetbrains.kotlin.KtNodeTypes
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.fir.FirRealSourceElementKind
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.*
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.getAncestors
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.typeContext
|
||||
@@ -43,7 +41,6 @@ object FirTypeParameterBoundsChecker : FirTypeParameterChecker() {
|
||||
checkBoundUniqueness(declaration, context, reporter)
|
||||
checkConflictingBounds(declaration, context, reporter)
|
||||
checkTypeAliasBound(declaration, containingDeclaration, context, reporter)
|
||||
checkBoundsPlacement(declaration, context, reporter)
|
||||
checkDynamicBounds(declaration, context, reporter)
|
||||
}
|
||||
|
||||
@@ -92,7 +89,9 @@ object FirTypeParameterBoundsChecker : FirTypeParameterChecker() {
|
||||
// report the diagnostic on that bound
|
||||
|
||||
//take TypeConstraint bounds only to report on the same point as old FE
|
||||
val constraintBounds = bounds.filter { it.isInTypeConstraint() }.toSet()
|
||||
val constraintBounds = with(SourceNavigator.forElement(declaration)){
|
||||
bounds.filter { it.isInTypeConstraint() }.toSet()
|
||||
}
|
||||
val reportOn =
|
||||
if (bounds.size == 2) {
|
||||
val boundDecl = otherBounds.firstOrNull() ?: boundWithParam.last()
|
||||
@@ -105,14 +104,6 @@ object FirTypeParameterBoundsChecker : FirTypeParameterChecker() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun FirTypeRef.isInTypeConstraint(): Boolean {
|
||||
val source = source ?: return false
|
||||
return source.treeStructure.getAncestors(source.lighterASTNode)
|
||||
.find { it.tokenType == KtNodeTypes.TYPE_CONSTRAINT || it.tokenType == KtNodeTypes.TYPE_PARAMETER }
|
||||
?.tokenType == KtNodeTypes.TYPE_CONSTRAINT
|
||||
}
|
||||
|
||||
|
||||
private fun checkBoundUniqueness(declaration: FirTypeParameter, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
val seenClasses = mutableSetOf<FirRegularClass>()
|
||||
val allNonErrorBounds = declaration.bounds.filter { it !is FirErrorTypeRef }
|
||||
@@ -152,16 +143,6 @@ object FirTypeParameterBoundsChecker : FirTypeParameterChecker() {
|
||||
}
|
||||
}
|
||||
|
||||
//TODO should be moved to extended checkers (because this is basically a code-style warning)
|
||||
private fun checkBoundsPlacement(declaration: FirTypeParameter, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
if (declaration.bounds.size < 2) return
|
||||
|
||||
val (constraint, params) = declaration.bounds.partition { it.isInTypeConstraint() }
|
||||
if (params.isNotEmpty() && constraint.isNotEmpty()) {
|
||||
reporter.reportOn(declaration.source, FirErrors.MISPLACED_TYPE_PARAMETER_CONSTRAINTS, context)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkDynamicBounds(declaration: FirTypeParameter, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
declaration.bounds.forEach { bound ->
|
||||
if (bound is FirDynamicTypeRef) {
|
||||
|
||||
-17
@@ -20,22 +20,5 @@ object FirAnonymousFunctionChecker : FirAnonymousFunctionAsExpressionChecker() {
|
||||
reporter.reportOn(source, FirErrors.USELESS_VARARG_ON_PARAMETER, context)
|
||||
}
|
||||
}
|
||||
|
||||
checkTypeParameters(expression, reporter, context)
|
||||
}
|
||||
|
||||
private fun checkTypeParameters(
|
||||
expression: FirAnonymousFunction,
|
||||
reporter: DiagnosticReporter,
|
||||
context: CheckerContext
|
||||
) {
|
||||
val source = expression.source ?: return
|
||||
source.treeStructure.typeParametersList(source.lighterASTNode)?.let { _ ->
|
||||
reporter.reportOn(
|
||||
source,
|
||||
FirErrors.TYPE_PARAMETERS_NOT_ALLOWED,
|
||||
context
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+19
-11
@@ -8,9 +8,10 @@ package org.jetbrains.kotlin.fir.analysis.checkers.extended
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
|
||||
import org.jetbrains.kotlin.fir.FirSourceElement
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirMemberDeclarationChecker
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.implicitModality
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.syntax.FirDeclarationSyntaxChecker
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.REDUNDANT_MODALITY_MODIFIER
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.modalityModifier
|
||||
@@ -18,22 +19,29 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.declarations.FirClass
|
||||
import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration
|
||||
import org.jetbrains.kotlin.fir.declarations.modality
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
|
||||
object RedundantModalityModifierChecker : FirMemberDeclarationChecker() {
|
||||
override fun check(declaration: FirMemberDeclaration, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
val source = declaration.source
|
||||
if (source?.kind is FirFakeSourceElementKind) return
|
||||
object RedundantModalityModifierSyntaxChecker : FirDeclarationSyntaxChecker<FirMemberDeclaration, KtDeclaration>() {
|
||||
|
||||
val modality = declaration.modality ?: return
|
||||
override fun isApplicable(element: FirMemberDeclaration, source: FirSourceElement): Boolean =
|
||||
source.kind !is FirFakeSourceElementKind
|
||||
|
||||
override fun checkLightTree(
|
||||
element: FirMemberDeclaration,
|
||||
source: FirSourceElement,
|
||||
context: CheckerContext,
|
||||
reporter: DiagnosticReporter
|
||||
) {
|
||||
val modality = element.modality ?: return
|
||||
if (
|
||||
modality == Modality.FINAL
|
||||
&& (context.containingDeclarations.last() as? FirClass<*>)?.classKind == ClassKind.INTERFACE
|
||||
) return
|
||||
|
||||
if (source != null && source.treeStructure.modalityModifier(source.lighterASTNode) == null) return
|
||||
val implicitModality = declaration.implicitModality(context)
|
||||
if (modality != implicitModality) return
|
||||
|
||||
reporter.reportOn(source, REDUNDANT_MODALITY_MODIFIER, context)
|
||||
if (source.treeStructure.modalityModifier(source.lighterASTNode) == null) return
|
||||
val implicitModality = element.implicitModality(context)
|
||||
if (modality == implicitModality) {
|
||||
reporter.reportOn(source, REDUNDANT_MODALITY_MODIFIER, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -40,7 +40,7 @@ object RedundantSingleExpressionStringTemplateChecker : FirStringConcatenationCa
|
||||
return when (val source = source) {
|
||||
is FirPsiSourceElement<*> -> source.psi.stringParentChildrenCount()
|
||||
is FirLightSourceElement -> source.lighterASTNode.stringParentChildrenCount(source)
|
||||
else -> null
|
||||
null -> null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+20
-15
@@ -11,45 +11,50 @@ import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.descriptors.Visibility
|
||||
import org.jetbrains.kotlin.fir.FirFakeSourceElement
|
||||
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
|
||||
import org.jetbrains.kotlin.fir.FirPsiSourceElement
|
||||
import org.jetbrains.kotlin.fir.FirSourceElement
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.*
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.findClosest
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirBasicDeclarationChecker
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.isLocalMember
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.syntax.FirDeclarationSyntaxChecker
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.*
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
|
||||
object RedundantVisibilityModifierChecker : FirBasicDeclarationChecker() {
|
||||
override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
val source = declaration.source ?: return
|
||||
if (declaration is FirConstructor && source.kind is FirFakeSourceElementKind) return
|
||||
object RedundantVisibilityModifierSyntaxChecker : FirDeclarationSyntaxChecker<FirDeclaration, KtDeclaration>() {
|
||||
|
||||
override fun checkLightTree(
|
||||
element: FirDeclaration,
|
||||
source: FirSourceElement,
|
||||
context: CheckerContext,
|
||||
reporter: DiagnosticReporter
|
||||
) {
|
||||
if (element is FirConstructor && source.kind is FirFakeSourceElementKind) return
|
||||
if (source is FirFakeSourceElement<*>) return
|
||||
if (
|
||||
declaration !is FirMemberDeclaration
|
||||
&& !(declaration is FirPropertyAccessor && declaration.visibility == context.containingPropertyVisibility)
|
||||
element !is FirMemberDeclaration
|
||||
&& !(element is FirPropertyAccessor && element.visibility == context.containingPropertyVisibility)
|
||||
) return
|
||||
|
||||
val visibilityModifier = source.treeStructure.visibilityModifier(source.lighterASTNode)
|
||||
val explicitVisibility = (visibilityModifier?.tokenType as? KtModifierKeywordToken)?.toVisibilityOrNull()
|
||||
val implicitVisibility = declaration.implicitVisibility(context)
|
||||
val implicitVisibility = element.implicitVisibility(context)
|
||||
val containingMemberDeclaration = context.findClosest<FirMemberDeclaration>()
|
||||
|
||||
val redundantVisibility = when {
|
||||
explicitVisibility == implicitVisibility -> implicitVisibility
|
||||
explicitVisibility == Visibilities.Internal &&
|
||||
containingMemberDeclaration.let { decl ->
|
||||
decl != null && decl.isLocalMember
|
||||
} -> Visibilities.Internal
|
||||
explicitVisibility == Visibilities.Internal && containingMemberDeclaration?.isLocalMember == true -> Visibilities.Internal
|
||||
else -> return
|
||||
}
|
||||
|
||||
if (
|
||||
redundantVisibility == Visibilities.Public
|
||||
&& declaration is FirProperty
|
||||
&& element is FirProperty
|
||||
&& source.treeStructure.overrideModifier(source.lighterASTNode) != null
|
||||
&& declaration.isVar
|
||||
&& declaration.setter?.visibility == Visibilities.Public
|
||||
&& element.isVar
|
||||
&& element.setter?.visibility == Visibilities.Public
|
||||
) return
|
||||
|
||||
reporter.reportOn(source, FirErrors.REDUNDANT_VISIBILITY_MODIFIER, context)
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.fir.analysis.checkers.syntax
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirPsiSourceElement
|
||||
import org.jetbrains.kotlin.fir.FirSourceElement
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.typeParametersList
|
||||
import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction
|
||||
import org.jetbrains.kotlin.psi.KtFunction
|
||||
|
||||
object FirAnonymousFunctionSyntaxChecker : FirExpressionSyntaxChecker<FirAnonymousFunction, KtFunction>() {
|
||||
override fun checkPsi(
|
||||
element: FirAnonymousFunction,
|
||||
source: FirPsiSourceElement<KtFunction>,
|
||||
context: CheckerContext,
|
||||
reporter: DiagnosticReporter
|
||||
) {
|
||||
if (source.psi.typeParameterList != null) {
|
||||
reporter.reportOn(
|
||||
source,
|
||||
FirErrors.TYPE_PARAMETERS_NOT_ALLOWED,
|
||||
context
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun checkLightTree(
|
||||
element: FirAnonymousFunction,
|
||||
source: FirSourceElement,
|
||||
context: CheckerContext,
|
||||
reporter: DiagnosticReporter
|
||||
) {
|
||||
source.treeStructure.typeParametersList(source.lighterASTNode)?.let { _ ->
|
||||
reporter.reportOn(
|
||||
source,
|
||||
FirErrors.TYPE_PARAMETERS_NOT_ALLOWED,
|
||||
context
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.fir.analysis.checkers.syntax
|
||||
|
||||
import org.jetbrains.kotlin.KtNodeTypes
|
||||
import org.jetbrains.kotlin.fir.*
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
|
||||
import org.jetbrains.kotlin.fir.declarations.isInterface
|
||||
import org.jetbrains.kotlin.psi.KtClass
|
||||
import org.jetbrains.kotlin.psi.KtDelegatedSuperTypeEntry
|
||||
|
||||
object FirDelegationInInterfaceSyntaxChecker : FirDeclarationSyntaxChecker<FirRegularClass, KtClass>() {
|
||||
|
||||
override fun isApplicable(element: FirRegularClass, source: FirSourceElement): Boolean = element.isInterface
|
||||
|
||||
override fun checkPsi(
|
||||
element: FirRegularClass,
|
||||
source: FirPsiSourceElement<KtClass>,
|
||||
context: CheckerContext,
|
||||
reporter: DiagnosticReporter
|
||||
) {
|
||||
for (superTypeRef in element.superTypeRefs) {
|
||||
val superSource = superTypeRef.source ?: continue
|
||||
if (superSource.psi?.parent is KtDelegatedSuperTypeEntry) {
|
||||
reporter.reportOn(superSource, FirErrors.DELEGATION_IN_INTERFACE, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun checkLightTree(
|
||||
element: FirRegularClass,
|
||||
source: FirSourceElement,
|
||||
context: CheckerContext,
|
||||
reporter: DiagnosticReporter
|
||||
) {
|
||||
for (superTypeRef in element.superTypeRefs) {
|
||||
val superSource = superTypeRef.source ?: continue
|
||||
if (superSource.treeStructure.getParent(superSource.lighterASTNode)?.tokenType == KtNodeTypes.DELEGATED_SUPER_TYPE_ENTRY) {
|
||||
reporter.reportOn(superSource, FirErrors.DELEGATION_IN_INTERFACE, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.fir.analysis.checkers.syntax
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
|
||||
import org.jetbrains.kotlin.fir.FirPsiSourceElement
|
||||
import org.jetbrains.kotlin.fir.FirSourceElement
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.*
|
||||
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
|
||||
import org.jetbrains.kotlin.psi.KtFunction
|
||||
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
||||
|
||||
object FirFunctionTypeParametersSyntaxChecker : FirDeclarationSyntaxChecker<FirSimpleFunction, KtFunction>() {
|
||||
override fun isApplicable(element: FirSimpleFunction, source: FirSourceElement): Boolean =
|
||||
source.kind !is FirFakeSourceElementKind
|
||||
|
||||
override fun checkPsi(
|
||||
element: FirSimpleFunction,
|
||||
source: FirPsiSourceElement<KtFunction>,
|
||||
context: CheckerContext,
|
||||
reporter: DiagnosticReporter
|
||||
) {
|
||||
val typeParamsNode = source.psi.typeParameterList
|
||||
val nameNode = source.psi.nameIdentifier
|
||||
|
||||
if (typeParamsNode != null && nameNode != null && typeParamsNode.startOffset > nameNode.startOffset) {
|
||||
reporter.reportOn(
|
||||
source,
|
||||
FirErrors.DEPRECATED_TYPE_PARAMETER_SYNTAX,
|
||||
context
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun checkLightTree(
|
||||
element: FirSimpleFunction,
|
||||
source: FirSourceElement,
|
||||
context: CheckerContext,
|
||||
reporter: DiagnosticReporter
|
||||
) {
|
||||
val typeParamsNode = source.treeStructure.typeParametersList(source.lighterASTNode)
|
||||
val nameNode = source.treeStructure.nameIdentifier(source.lighterASTNode)
|
||||
if (typeParamsNode != null && nameNode != null && typeParamsNode.startOffset > nameNode.startOffset) {
|
||||
reporter.reportOn(
|
||||
source,
|
||||
FirErrors.DEPRECATED_TYPE_PARAMETER_SYNTAX,
|
||||
context
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.fir.analysis.checkers.syntax
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.fir.FirElement
|
||||
import org.jetbrains.kotlin.fir.FirLightSourceElement
|
||||
import org.jetbrains.kotlin.fir.FirPsiSourceElement
|
||||
import org.jetbrains.kotlin.fir.FirSourceElement
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirDeclarationChecker
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirExpressionChecker
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
||||
import org.jetbrains.kotlin.fir.expressions.FirStatement
|
||||
|
||||
/**
|
||||
* Checker that heavily relies on source tree. So it should have different implementation for each tree variant
|
||||
*/
|
||||
interface FirSyntaxChecker<in D : FirElement, P : PsiElement> {
|
||||
|
||||
fun checkSyntax(element: D, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
val source = element.source ?: return
|
||||
if (!isApplicable(element, source)) return
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
when (source) {
|
||||
is FirPsiSourceElement<*> -> checkPsi(element, source as FirPsiSourceElement<P>, context, reporter)
|
||||
is FirLightSourceElement -> checkLightTree(element, source, context, reporter)
|
||||
}
|
||||
}
|
||||
|
||||
fun isApplicable(element: D, source: FirSourceElement): Boolean = true
|
||||
|
||||
/**
|
||||
* By default psi tree should be equivalent to light tree and can be processed the same way
|
||||
*/
|
||||
fun checkPsi(element: D, source: FirPsiSourceElement<P>, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
checkLightTree(element, source, context, reporter)
|
||||
}
|
||||
|
||||
/*
|
||||
accepts FirSourceElement instead of FirLightSourceElement because now they have the same interface
|
||||
and it allows to reuse LT check for psi tree
|
||||
*/
|
||||
fun checkLightTree(element: D, source: FirSourceElement, context: CheckerContext, reporter: DiagnosticReporter)
|
||||
}
|
||||
|
||||
abstract class FirDeclarationSyntaxChecker<in D : FirDeclaration, P : PsiElement> :
|
||||
FirDeclarationChecker<D>(),
|
||||
FirSyntaxChecker<D, P> {
|
||||
final override fun check(declaration: D, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
checkSyntax(declaration, context, reporter)
|
||||
}
|
||||
}
|
||||
|
||||
abstract class FirExpressionSyntaxChecker<in E : FirStatement, P : PsiElement> :
|
||||
FirExpressionChecker<E>(),
|
||||
FirSyntaxChecker<E, P> {
|
||||
final override fun check(expression: E, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
checkSyntax(expression, context, reporter)
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.fir.analysis.checkers.syntax
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirPsiSourceElement
|
||||
import org.jetbrains.kotlin.fir.FirSourceElement
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.SourceNavigator.Companion.withNavigator
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.declarations.FirTypeParameter
|
||||
import org.jetbrains.kotlin.fir.psi
|
||||
import org.jetbrains.kotlin.psi.KtTypeConstraint
|
||||
import org.jetbrains.kotlin.psi.KtTypeParameter
|
||||
|
||||
object FirTypeParameterSyntaxChecker : FirDeclarationSyntaxChecker<FirTypeParameter, KtTypeParameter>() {
|
||||
|
||||
override fun isApplicable(element: FirTypeParameter, source: FirSourceElement): Boolean =
|
||||
element.bounds.size >= 2
|
||||
|
||||
override fun checkPsi(
|
||||
element: FirTypeParameter,
|
||||
source: FirPsiSourceElement<KtTypeParameter>,
|
||||
context: CheckerContext,
|
||||
reporter: DiagnosticReporter
|
||||
) {
|
||||
val (constraint, params) = element.bounds.partition { it.psi?.parent is KtTypeConstraint }
|
||||
if (params.isNotEmpty() && constraint.isNotEmpty()) {
|
||||
reporter.reportOn(source, FirErrors.MISPLACED_TYPE_PARAMETER_CONSTRAINTS, context)
|
||||
}
|
||||
}
|
||||
|
||||
override fun checkLightTree(
|
||||
element: FirTypeParameter,
|
||||
source: FirSourceElement,
|
||||
context: CheckerContext,
|
||||
reporter: DiagnosticReporter
|
||||
) {
|
||||
val (constraint, params) = element.withNavigator {
|
||||
element.bounds.partition { it.isInTypeConstraint() }
|
||||
}
|
||||
if (params.isNotEmpty() && constraint.isNotEmpty()) {
|
||||
reporter.reportOn(source, FirErrors.MISPLACED_TYPE_PARAMETER_CONSTRAINTS, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
-7
@@ -11,6 +11,9 @@ import org.jetbrains.kotlin.fir.analysis.cfa.FirPropertyInitializationAnalyzer
|
||||
import org.jetbrains.kotlin.fir.analysis.cfa.FirReturnsImpliesAnalyzer
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.cfa.FirControlFlowChecker
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.*
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.syntax.FirDelegationInInterfaceSyntaxChecker
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.syntax.FirFunctionTypeParametersSyntaxChecker
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.syntax.FirTypeParameterSyntaxChecker
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirReservedUnderscoreDeclarationChecker
|
||||
|
||||
object CommonDeclarationCheckers : DeclarationCheckers() {
|
||||
@@ -43,7 +46,7 @@ object CommonDeclarationCheckers : DeclarationCheckers() {
|
||||
override val simpleFunctionCheckers: Set<FirSimpleFunctionChecker>
|
||||
get() = setOf(
|
||||
FirFunctionNameChecker,
|
||||
FirFunctionTypeParametersChecker,
|
||||
FirFunctionTypeParametersSyntaxChecker,
|
||||
)
|
||||
|
||||
override val propertyCheckers: Set<FirPropertyChecker>
|
||||
@@ -72,7 +75,7 @@ object CommonDeclarationCheckers : DeclarationCheckers() {
|
||||
FirCommonConstructorDelegationIssuesChecker,
|
||||
FirConstructorInInterfaceChecker,
|
||||
FirDelegationSuperCallInEnumConstructorChecker,
|
||||
FirDelegationInInterfaceChecker,
|
||||
FirDelegationInInterfaceSyntaxChecker,
|
||||
FirEnumClassSimpleChecker,
|
||||
FirInterfaceWithSuperclassChecker,
|
||||
FirLocalEntityNotAllowedChecker,
|
||||
@@ -82,11 +85,11 @@ object CommonDeclarationCheckers : DeclarationCheckers() {
|
||||
FirPrimaryConstructorSuperTypeChecker,
|
||||
FirTypeParametersInObjectChecker,
|
||||
FirFunInterfaceDeclarationChecker,
|
||||
FirMemberFunctionsChecker,
|
||||
FirMemberPropertiesChecker,
|
||||
FirNestedClassChecker,
|
||||
FirInlineClassDeclarationChecker,
|
||||
)
|
||||
FirMemberFunctionsChecker,
|
||||
FirMemberPropertiesChecker,
|
||||
FirNestedClassChecker,
|
||||
FirInlineClassDeclarationChecker,
|
||||
)
|
||||
|
||||
override val constructorCheckers: Set<FirConstructorChecker>
|
||||
get() = setOf(
|
||||
@@ -116,5 +119,6 @@ object CommonDeclarationCheckers : DeclarationCheckers() {
|
||||
FirTypeParameterBoundsChecker,
|
||||
FirTypeParameterVarianceChecker,
|
||||
FirReifiedTypeParameterChecker,
|
||||
FirTypeParameterSyntaxChecker,
|
||||
)
|
||||
}
|
||||
|
||||
+2
@@ -6,6 +6,7 @@
|
||||
package org.jetbrains.kotlin.fir.checkers
|
||||
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.expression.*
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.syntax.FirAnonymousFunctionSyntaxChecker
|
||||
|
||||
object CommonExpressionCheckers : ExpressionCheckers() {
|
||||
override val annotationCallCheckers: Set<FirAnnotationCallChecker>
|
||||
@@ -92,6 +93,7 @@ object CommonExpressionCheckers : ExpressionCheckers() {
|
||||
override val anonymousFunctionAsExpressionCheckers: Set<FirAnonymousFunctionAsExpressionChecker>
|
||||
get() = setOf(
|
||||
FirAnonymousFunctionChecker,
|
||||
FirAnonymousFunctionSyntaxChecker,
|
||||
)
|
||||
|
||||
override val typeOperatorCallCheckers: Set<FirTypeOperatorCallChecker>
|
||||
|
||||
+2
-3
@@ -9,17 +9,16 @@ import org.jetbrains.kotlin.fir.analysis.cfa.AbstractFirPropertyInitializationCh
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.cfa.FirControlFlowChecker
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.*
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.extended.*
|
||||
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
||||
|
||||
object ExtendedDeclarationCheckers : DeclarationCheckers() {
|
||||
override val basicDeclarationCheckers: Set<FirBasicDeclarationChecker>
|
||||
get() = setOf(
|
||||
RedundantVisibilityModifierChecker,
|
||||
RedundantVisibilityModifierSyntaxChecker,
|
||||
)
|
||||
|
||||
override val memberDeclarationCheckers: Set<FirMemberDeclarationChecker>
|
||||
get() = setOf(
|
||||
RedundantModalityModifierChecker,
|
||||
RedundantModalityModifierSyntaxChecker,
|
||||
RedundantExplicitTypeChecker,
|
||||
RedundantSetterParameterTypeChecker,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user