提交 8b4f2b26 编写于 作者: J Jinseong Jeon 提交者: Mikhail Glukhikh

FIR checker: introduce PARAMETER_* positioning strategies

and use them to add support diagnostics:
ANONYMOUS_FUNCTION_PARAMETER_WITH_DEFAULT_VALUE
USELESS_VARARG_ON_PARAMETER
上级 33b7c68a
......@@ -1231,6 +1231,24 @@ public class LazyBodyIsNotTouchedTilContractsPhaseTestGenerated extends Abstract
public void testValOnAnnotationParameter() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/valOnAnnotationParameter.kt");
}
@TestMetadata("compiler/fir/analysis-tests/testData/resolve/diagnostics/functionAsExpression")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class FunctionAsExpression extends AbstractLazyBodyIsNotTouchedTilContractsPhaseTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
public void testAllFilesPresentInFunctionAsExpression() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/diagnostics/functionAsExpression"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
}
@TestMetadata("Parameters.kt")
public void testParameters() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/functionAsExpression/Parameters.kt");
}
}
}
@TestMetadata("compiler/fir/analysis-tests/testData/resolve/expresssions")
......
FILE: Parameters.kt
public final val bar: R|(kotlin/Int) -> kotlin/Unit| = fun <anonymous>(p: R|kotlin/Int| = Int(3)): R|kotlin/Unit| {
}
public get(): R|(kotlin/Int) -> kotlin/Unit|
public final val bas: R|(kotlin/Int) -> kotlin/Unit| = fun <anonymous>(vararg p: R|kotlin/Int|): R|kotlin/Unit| {
}
public get(): R|(kotlin/Int) -> kotlin/Unit|
public final fun gar(): R|(kotlin/Int) -> kotlin/Unit| {
^gar fun <anonymous>(p: R|kotlin/Int| = Int(3)): R|kotlin/Unit| {
}
}
public final fun gas(): R|(kotlin/Int) -> kotlin/Unit| {
^gas fun <anonymous>(vararg p: R|kotlin/Int|): R|kotlin/Unit| {
}
}
public final fun outer(b: R|kotlin/Any?|): R|kotlin/Unit| {
lval bar: R|(kotlin/Int) -> kotlin/Unit| = fun <anonymous>(p: R|kotlin/Int| = Int(3)): R|kotlin/Unit| {
}
lval bas: R|(kotlin/Int) -> kotlin/Unit| = fun <anonymous>(vararg p: R|kotlin/Int|): R|kotlin/Unit| {
}
local final fun gar(): R|(kotlin/Int) -> kotlin/Unit| {
^gar fun <anonymous>(p: R|kotlin/Int| = Int(3)): R|kotlin/Unit| {
}
}
local final fun gas(): R|(kotlin/Int) -> kotlin/Unit| {
^gas fun <anonymous>(vararg p: R|kotlin/Int|): R|kotlin/Unit| {
}
}
R|/outer|(fun <anonymous>(p: R|kotlin/Int| = Int(3)): R|kotlin/Unit| {
}
)
R|/outer|(fun <anonymous>(vararg p: R|kotlin/Int|): R|kotlin/Unit| {
}
)
}
val bar = fun(p: Int = <!ANONYMOUS_FUNCTION_PARAMETER_WITH_DEFAULT_VALUE!>3<!>) {}
val bas = fun(<!USELESS_VARARG_ON_PARAMETER!>vararg p: Int<!>) {}
fun gar() = fun(p: Int = <!ANONYMOUS_FUNCTION_PARAMETER_WITH_DEFAULT_VALUE!>3<!>) {}
fun gas() = fun(<!USELESS_VARARG_ON_PARAMETER!>vararg p: Int<!>) {}
fun outer(b: Any?) {
val bar = fun(p: Int = <!ANONYMOUS_FUNCTION_PARAMETER_WITH_DEFAULT_VALUE!>3<!>) {}
val bas = fun(<!USELESS_VARARG_ON_PARAMETER!>vararg p: Int<!>) {}
fun gar() = fun(p: Int = <!ANONYMOUS_FUNCTION_PARAMETER_WITH_DEFAULT_VALUE!>3<!>) {}
fun gas() = fun(<!USELESS_VARARG_ON_PARAMETER!>vararg p: Int<!>) {}
outer(fun(p: Int = <!ANONYMOUS_FUNCTION_PARAMETER_WITH_DEFAULT_VALUE!>3<!>) {})
outer(fun(<!USELESS_VARARG_ON_PARAMETER!>vararg p: Int<!>) {})
}
......@@ -1424,6 +1424,22 @@ public class FirDiagnosticTestGenerated extends AbstractFirDiagnosticTest {
public void testValOnAnnotationParameter() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/valOnAnnotationParameter.kt");
}
@Nested
@TestMetadata("compiler/fir/analysis-tests/testData/resolve/diagnostics/functionAsExpression")
@TestDataPath("$PROJECT_ROOT")
public class FunctionAsExpression extends AbstractFirDiagnosticTest {
@Test
public void testAllFilesPresentInFunctionAsExpression() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/diagnostics/functionAsExpression"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
}
@Test
@TestMetadata("Parameters.kt")
public void testParameters() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/functionAsExpression/Parameters.kt");
}
}
}
@Nested
......
......@@ -1433,6 +1433,23 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos
public void testValOnAnnotationParameter() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/valOnAnnotationParameter.kt");
}
@Nested
@TestMetadata("compiler/fir/analysis-tests/testData/resolve/diagnostics/functionAsExpression")
@TestDataPath("$PROJECT_ROOT")
@Execution(ExecutionMode.SAME_THREAD)
public class FunctionAsExpression extends AbstractFirDiagnosticsWithLightTreeTest {
@Test
public void testAllFilesPresentInFunctionAsExpression() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/diagnostics/functionAsExpression"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
}
@Test
@TestMetadata("Parameters.kt")
public void testParameters() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/functionAsExpression/Parameters.kt");
}
}
}
@Nested
......
/*
* 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.expression
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.extended.report
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction
import org.jetbrains.kotlin.fir.expressions.FirStatement
object FirAnonymousFunctionChecker : FirExpressionChecker<FirStatement>() {
override fun check(expression: FirStatement, context: CheckerContext, reporter: DiagnosticReporter) {
if (expression !is FirAnonymousFunction) {
return
}
for (valueParameter in expression.valueParameters) {
val source = valueParameter.source ?: continue
if (valueParameter.defaultValue != null) {
reporter.report(source, FirErrors.ANONYMOUS_FUNCTION_PARAMETER_WITH_DEFAULT_VALUE)
}
if (valueParameter.isVararg) {
reporter.report(source, FirErrors.USELESS_VARARG_ON_PARAMETER)
}
}
}
}
......@@ -10,11 +10,16 @@ import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirExpressionChecke
import org.jetbrains.kotlin.fir.analysis.checkersComponent
import org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollector
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction
import org.jetbrains.kotlin.fir.expressions.*
class ExpressionCheckersDiagnosticComponent(collector: AbstractDiagnosticCollector) : AbstractDiagnosticCollectorComponent(collector) {
private val checkers = session.checkersComponent.expressionCheckers
override fun visitAnonymousFunction(anonymousFunction: FirAnonymousFunction, data: CheckerContext) {
checkers.basicExpressionCheckers.check(anonymousFunction, data, reporter)
}
override fun visitTypeOperatorCall(typeOperatorCall: FirTypeOperatorCall, data: CheckerContext) {
checkers.basicExpressionCheckers.check(typeOperatorCall, data, reporter)
}
......
......@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ABSTRACT_SUPER_CA
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.AMBIGUITY
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ANNOTATION_CLASS_MEMBER
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ANNOTATION_PARAMETER_DEFAULT_VALUE_MUST_BE_CONSTANT
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ANONYMOUS_FUNCTION_PARAMETER_WITH_DEFAULT_VALUE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ANY_METHOD_IMPLEMENTED_IN_INTERFACE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ARRAY_EQUALITY_OPERATOR_CAN_BE_REPLACED_WITH_EQUALS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ASSIGNED_VALUE_IS_NEVER_READ
......@@ -133,6 +134,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNRESOLVED_LABEL
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNRESOLVED_REFERENCE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNUSED_VARIABLE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UPPER_BOUND_VIOLATED
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.USELESS_VARARG_ON_PARAMETER
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VARIABLE_EXPECTED
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VARIABLE_INITIALIZER_IS_REDUNDANT
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VARIABLE_NEVER_READ
......@@ -360,6 +362,12 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension {
map.put(FUNCTION_DECLARATION_WITH_NO_NAME, "Function declaration must have a name")
map.put(
ANONYMOUS_FUNCTION_PARAMETER_WITH_DEFAULT_VALUE,
"An anonymous function is not allowed to specify default values for its parameters"
)
map.put(USELESS_VARARG_ON_PARAMETER, "Vararg on this parameter is useless")
// Properties & accessors
map.put(
ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS,
......
......@@ -158,6 +158,10 @@ object FirErrors {
val FUNCTION_DECLARATION_WITH_NO_NAME by error0<FirSourceElement, KtFunction>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE)
// TODO: val ANONYMOUS_FUNCTION_WITH_NAME by error1<FirSourceElement, PsiElement, Name>(SourceElementPositioningStrategies.DECLARATION_NAME)
val ANONYMOUS_FUNCTION_PARAMETER_WITH_DEFAULT_VALUE by error0<FirSourceElement, KtParameter>(SourceElementPositioningStrategies.PARAMETER_DEFAULT_VALUE)
val USELESS_VARARG_ON_PARAMETER by warning0<FirSourceElement, KtParameter>()
// Properties & accessors
val ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS by error2<FirSourceElement, KtModifierListOwner, FirMemberDeclaration, FirMemberDeclaration>(SourceElementPositioningStrategies.MODALITY_MODIFIER)
val PRIVATE_PROPERTY_IN_INTERFACE by error0<FirSourceElement, KtProperty>(SourceElementPositioningStrategies.VISIBILITY_MODIFIER)
......
......@@ -247,6 +247,30 @@ object LightTreePositioningStrategies {
return markElement(tree.operationReference(node) ?: node, startOffset, endOffset, tree, node)
}
}
val PARAMETER_DEFAULT_VALUE: LightTreePositioningStrategy = object : LightTreePositioningStrategy() {
override fun mark(
node: LighterASTNode,
startOffset: Int,
endOffset: Int,
tree: FlyweightCapableTreeStructure<LighterASTNode>
): List<TextRange> {
val defaultValueElement = tree.defaultValue(node) ?: node
return markElement(defaultValueElement, startOffset, endOffset, tree, node)
}
}
val PARAMETER_VARARG_MODIFIER: LightTreePositioningStrategy = object : LightTreePositioningStrategy() {
override fun mark(
node: LighterASTNode,
startOffset: Int,
endOffset: Int,
tree: FlyweightCapableTreeStructure<LighterASTNode>
): List<TextRange> {
val modifier = tree.modifierList(node)?.let { modifierList -> tree.findChildByType(modifierList, KtTokens.VARARG_KEYWORD) }
return markElement(modifier ?: node, startOffset, endOffset, tree, node)
}
}
}
fun FirSourceElement.hasValOrVar(): Boolean =
......@@ -317,6 +341,20 @@ private fun FlyweightCapableTreeStructure<LighterASTNode>.receiverTypeReference(
}
}
private fun FlyweightCapableTreeStructure<LighterASTNode>.defaultValue(node: LighterASTNode): LighterASTNode? {
val childrenRef = Ref<Array<LighterASTNode?>>()
getChildren(node, childrenRef)
// p : T = v
return childrenRef.get()?.reversed()?.firstOrNull {
it != null &&
it.tokenType != KtTokens.WHITE_SPACE &&
it.tokenType != KtTokens.EQ &&
it.tokenType != KtNodeTypes.TYPE_REFERENCE &&
it.tokenType != KtTokens.COLON &&
it.tokenType != KtTokens.IDENTIFIER
}
}
fun FlyweightCapableTreeStructure<LighterASTNode>.findChildByType(node: LighterASTNode, type: IElementType): LighterASTNode? {
val childrenRef = Ref<Array<LighterASTNode?>>()
getChildren(node, childrenRef)
......
......@@ -52,4 +52,14 @@ object SourceElementPositioningStrategies {
LightTreePositioningStrategies.OPERATOR,
PositioningStrategies.OPERATOR
)
val PARAMETER_DEFAULT_VALUE = SourceElementPositioningStrategy(
LightTreePositioningStrategies.PARAMETER_DEFAULT_VALUE,
PositioningStrategies.PARAMETER_DEFAULT_VALUE
)
val PARAMETER_VARARG_MODIFIER = SourceElementPositioningStrategy(
LightTreePositioningStrategies.PARAMETER_VARARG_MODIFIER,
PositioningStrategies.PARAMETER_VARARG_MODIFIER
)
}
\ No newline at end of file
......@@ -8,6 +8,10 @@ package org.jetbrains.kotlin.fir.checkers
import org.jetbrains.kotlin.fir.analysis.checkers.expression.*
object CommonExpressionCheckers : ExpressionCheckers() {
override val basicExpressionCheckers: Set<FirBasicExpressionChecker> = setOf(
FirAnonymousFunctionChecker,
)
override val qualifiedAccessCheckers: Set<FirQualifiedAccessChecker> = setOf(
FirSuperNotAvailableChecker,
FirNotASupertypeChecker,
......
// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_ANONYMOUS_PARAMETER -UNUSED_VARIABLE
val bar = fun(p: Int = 3) {}
val bas = fun(vararg p: Int) {}
fun gar() = fun(p: Int = 3) {}
fun gas() = fun(vararg p: Int) {}
fun outer(b: Any?) {
val bar = fun(p: Int = 3) {}
val bas = fun(vararg p: Int) {}
fun gar() = fun(p: Int = 3) {}
fun gas() = fun(vararg p: Int) {}
outer(fun(p: Int = 3) {})
outer(fun(vararg p: Int) {})
}
\ No newline at end of file
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_ANONYMOUS_PARAMETER -UNUSED_VARIABLE
val bar = fun(p: Int = <!ANONYMOUS_FUNCTION_PARAMETER_WITH_DEFAULT_VALUE!>3<!>) {}
......
......@@ -10,7 +10,7 @@ class A {
fun f3(a0: Int, vararg a1: Foo) {
fun f4(vararg a: Foo) {}
val g = fun (vararg v: Foo) {}
val g = fun (<!USELESS_VARARG_ON_PARAMETER!>vararg v: Foo<!>) {}
}
}
......
......@@ -16,7 +16,7 @@ class A {
fun f3(a0: Int, vararg a1: Foo) {
fun f4(vararg a: Foo) {}
val g = fun (vararg v: Foo) {}
val g = fun (<!USELESS_VARARG_ON_PARAMETER!>vararg v: Foo<!>) {}
}
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册