diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/SourceNavigator.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/SourceNavigator.kt new file mode 100644 index 00000000000..914b3fe35c0 --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/SourceNavigator.kt @@ -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 FirElement.withNavigator(block: SourceNavigator.() -> R): R = with(forElement(this), block) + } +} + +open class LightTreeSourceNavigator : SourceNavigator { + + private fun 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 FirElement.psi(): P? { + val psi = (source as? FirPsiSourceElement<*>)?.psi + return psi as? P + } + + override fun FirTypeRef.isInConstructorCallee(): Boolean = psi()?.parent is KtConstructorCalleeExpression +} diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirDelegationInInterfaceChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirDelegationInInterfaceChecker.kt deleted file mode 100644 index 1c6e5c72da0..00000000000 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirDelegationInInterfaceChecker.kt +++ /dev/null @@ -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) - } - } - } -} diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirFunctionTypeParametersChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirFunctionTypeParametersChecker.kt deleted file mode 100644 index d315b00160f..00000000000 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirFunctionTypeParametersChecker.kt +++ /dev/null @@ -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 - ) - } - } - } -} diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirPrimaryConstructorSuperTypeChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirPrimaryConstructorSuperTypeChecker.kt index 1bf2f1226ec..3ddd059996f 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirPrimaryConstructorSuperTypeChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirPrimaryConstructorSuperTypeChecker.kt @@ -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) + } } } } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirTypeParameterBoundsChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirTypeParameterBoundsChecker.kt index 333e538fcc7..26eebf9e2d6 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirTypeParameterBoundsChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirTypeParameterBoundsChecker.kt @@ -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() 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) { diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirAnonymousFunctionChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirAnonymousFunctionChecker.kt index 3771acb1afe..984b2276011 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirAnonymousFunctionChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirAnonymousFunctionChecker.kt @@ -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 - ) - } } } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/RedundantModalityModifierChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/RedundantModalityModifierSyntaxChecker.kt similarity index 55% rename from compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/RedundantModalityModifierChecker.kt rename to compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/RedundantModalityModifierSyntaxChecker.kt index 57b8c51196b..95739e42427 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/RedundantModalityModifierChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/RedundantModalityModifierSyntaxChecker.kt @@ -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() { - 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) + } } } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/RedundantSingleExpressionStringTemplateChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/RedundantSingleExpressionStringTemplateChecker.kt index 53bf895d66c..6c6f1b6ab73 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/RedundantSingleExpressionStringTemplateChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/RedundantSingleExpressionStringTemplateChecker.kt @@ -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 } } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/RedundantVisibilityModifierChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/RedundantVisibilityModifierSyntaxChecker.kt similarity index 78% rename from compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/RedundantVisibilityModifierChecker.kt rename to compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/RedundantVisibilityModifierSyntaxChecker.kt index aba710182df..af7b7b00859 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/RedundantVisibilityModifierChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/RedundantVisibilityModifierSyntaxChecker.kt @@ -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() { + + 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() 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) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/syntax/FirAnonymousFunctionSyntaxChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/syntax/FirAnonymousFunctionSyntaxChecker.kt new file mode 100644 index 00000000000..5ce467812ab --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/syntax/FirAnonymousFunctionSyntaxChecker.kt @@ -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() { + override fun checkPsi( + element: FirAnonymousFunction, + source: FirPsiSourceElement, + 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 + ) + } + } +} diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/syntax/FirDelegationInInterfaceSyntaxChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/syntax/FirDelegationInInterfaceSyntaxChecker.kt new file mode 100644 index 00000000000..10d3f636da6 --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/syntax/FirDelegationInInterfaceSyntaxChecker.kt @@ -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() { + + override fun isApplicable(element: FirRegularClass, source: FirSourceElement): Boolean = element.isInterface + + override fun checkPsi( + element: FirRegularClass, + source: FirPsiSourceElement, + 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) + } + } + } +} diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/syntax/FirFunctionTypeParametersSyntaxChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/syntax/FirFunctionTypeParametersSyntaxChecker.kt new file mode 100644 index 00000000000..02057ad9a0a --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/syntax/FirFunctionTypeParametersSyntaxChecker.kt @@ -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() { + override fun isApplicable(element: FirSimpleFunction, source: FirSourceElement): Boolean = + source.kind !is FirFakeSourceElementKind + + override fun checkPsi( + element: FirSimpleFunction, + source: FirPsiSourceElement, + 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 + ) + } + } +} diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/syntax/FirSyntaxChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/syntax/FirSyntaxChecker.kt new file mode 100644 index 00000000000..ff5596a15f4 --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/syntax/FirSyntaxChecker.kt @@ -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 { + + 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

, 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

, 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 : + FirDeclarationChecker(), + FirSyntaxChecker { + final override fun check(declaration: D, context: CheckerContext, reporter: DiagnosticReporter) { + checkSyntax(declaration, context, reporter) + } +} + +abstract class FirExpressionSyntaxChecker : + FirExpressionChecker(), + FirSyntaxChecker { + final override fun check(expression: E, context: CheckerContext, reporter: DiagnosticReporter) { + checkSyntax(expression, context, reporter) + } +} diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/syntax/FirTypeParameterSyntaxChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/syntax/FirTypeParameterSyntaxChecker.kt new file mode 100644 index 00000000000..9b4ac21c418 --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/syntax/FirTypeParameterSyntaxChecker.kt @@ -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() { + + override fun isApplicable(element: FirTypeParameter, source: FirSourceElement): Boolean = + element.bounds.size >= 2 + + override fun checkPsi( + element: FirTypeParameter, + source: FirPsiSourceElement, + 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) + } + } +} diff --git a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonDeclarationCheckers.kt b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonDeclarationCheckers.kt index 6637437d03c..50ea96cf571 100644 --- a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonDeclarationCheckers.kt +++ b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonDeclarationCheckers.kt @@ -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 get() = setOf( FirFunctionNameChecker, - FirFunctionTypeParametersChecker, + FirFunctionTypeParametersSyntaxChecker, ) override val propertyCheckers: Set @@ -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 get() = setOf( @@ -116,5 +119,6 @@ object CommonDeclarationCheckers : DeclarationCheckers() { FirTypeParameterBoundsChecker, FirTypeParameterVarianceChecker, FirReifiedTypeParameterChecker, + FirTypeParameterSyntaxChecker, ) } diff --git a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonExpressionCheckers.kt b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonExpressionCheckers.kt index 2edda7ccb3c..f785222e120 100644 --- a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonExpressionCheckers.kt +++ b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonExpressionCheckers.kt @@ -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 @@ -92,6 +93,7 @@ object CommonExpressionCheckers : ExpressionCheckers() { override val anonymousFunctionAsExpressionCheckers: Set get() = setOf( FirAnonymousFunctionChecker, + FirAnonymousFunctionSyntaxChecker, ) override val typeOperatorCallCheckers: Set diff --git a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/ExtendedDeclarationCheckers.kt b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/ExtendedDeclarationCheckers.kt index e3d478768ef..241a833126e 100644 --- a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/ExtendedDeclarationCheckers.kt +++ b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/ExtendedDeclarationCheckers.kt @@ -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 get() = setOf( - RedundantVisibilityModifierChecker, + RedundantVisibilityModifierSyntaxChecker, ) override val memberDeclarationCheckers: Set get() = setOf( - RedundantModalityModifierChecker, + RedundantModalityModifierSyntaxChecker, RedundantExplicitTypeChecker, RedundantSetterParameterTypeChecker, )