KT-35811: Type parameter angle brackets followed by equal sign are parsed incorrectly if whitespace is missing

This commit is contained in:
Iven Krall
2022-03-26 18:40:15 +01:00
committed by teamcity
parent 8de4eb798c
commit ba5c85d6f2
21 changed files with 705 additions and 382 deletions
@@ -10655,6 +10655,28 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
}
}
@Nested
@TestMetadata("compiler/testData/diagnostics/tests/expressionAfterTypeReference")
@TestDataPath("$PROJECT_ROOT")
public class ExpressionAfterTypeReference {
@Test
public void testAllFilesPresentInExpressionAfterTypeReference() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/expressionAfterTypeReference"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
}
@Test
@TestMetadata("allowExpressionAfterTypeReference.kt")
public void testAllowExpressionAfterTypeReference() throws Exception {
runTest("compiler/testData/diagnostics/tests/expressionAfterTypeReference/allowExpressionAfterTypeReference.kt");
}
@Test
@TestMetadata("forbidExpressionAfterTypeReference.kt")
public void testForbidExpressionAfterTypeReference() throws Exception {
runTest("compiler/testData/diagnostics/tests/expressionAfterTypeReference/forbidExpressionAfterTypeReference.kt");
}
}
@Nested
@TestMetadata("compiler/testData/diagnostics/tests/extensions")
@TestDataPath("$PROJECT_ROOT")
@@ -10655,6 +10655,28 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
}
}
@Nested
@TestMetadata("compiler/testData/diagnostics/tests/expressionAfterTypeReference")
@TestDataPath("$PROJECT_ROOT")
public class ExpressionAfterTypeReference {
@Test
public void testAllFilesPresentInExpressionAfterTypeReference() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/expressionAfterTypeReference"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
}
@Test
@TestMetadata("allowExpressionAfterTypeReference.kt")
public void testAllowExpressionAfterTypeReference() throws Exception {
runTest("compiler/testData/diagnostics/tests/expressionAfterTypeReference/allowExpressionAfterTypeReference.kt");
}
@Test
@TestMetadata("forbidExpressionAfterTypeReference.kt")
public void testForbidExpressionAfterTypeReference() throws Exception {
runTest("compiler/testData/diagnostics/tests/expressionAfterTypeReference/forbidExpressionAfterTypeReference.kt");
}
}
@Nested
@TestMetadata("compiler/testData/diagnostics/tests/extensions")
@TestDataPath("$PROJECT_ROOT")
@@ -10655,6 +10655,28 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
}
}
@Nested
@TestMetadata("compiler/testData/diagnostics/tests/expressionAfterTypeReference")
@TestDataPath("$PROJECT_ROOT")
public class ExpressionAfterTypeReference {
@Test
public void testAllFilesPresentInExpressionAfterTypeReference() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/expressionAfterTypeReference"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
}
@Test
@TestMetadata("allowExpressionAfterTypeReference.kt")
public void testAllowExpressionAfterTypeReference() throws Exception {
runTest("compiler/testData/diagnostics/tests/expressionAfterTypeReference/allowExpressionAfterTypeReference.kt");
}
@Test
@TestMetadata("forbidExpressionAfterTypeReference.kt")
public void testForbidExpressionAfterTypeReference() throws Exception {
runTest("compiler/testData/diagnostics/tests/expressionAfterTypeReference/forbidExpressionAfterTypeReference.kt");
}
}
@Nested
@TestMetadata("compiler/testData/diagnostics/tests/extensions")
@TestDataPath("$PROJECT_ROOT")
@@ -235,6 +235,8 @@ public interface Errors {
DiagnosticFactoryForDeprecation1<PsiElement, CallableDescriptor> PROGRESSIONS_CHANGING_RESOLVE = DiagnosticFactoryForDeprecation1.create(LanguageFeature.ProgressionsChangingResolve);
DiagnosticFactory0<PsiElement> EXPRESSION_AFTER_TYPE_REFERENCE_WITHOUT_SPACING_NOT_ALLOWED = DiagnosticFactory0.create(ERROR);
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Errors in declarations
@@ -625,6 +625,7 @@ public class DefaultErrorMessages {
"This call will resolve to another declaration: {0}. " +
"See https://youtrack.jetbrains.com/issue/KT-49276 for more details. " +
"Please specify a progression type of argument explicitly through explicit cast to resolve to a proper declaration", COMPACT);
MAP.put(EXPRESSION_AFTER_TYPE_REFERENCE_WITHOUT_SPACING_NOT_ALLOWED, "Expression body directly after type without a whitespace in-between the '>=' is only allowed since version 1.8");
MAP.put(TOO_MANY_ARGUMENTS, "Too many arguments for {0}", FQ_NAMES_IN_TYPES);
@@ -51,6 +51,7 @@ private val DEFAULT_DECLARATION_CHECKERS = listOf(
SubtypingBetweenContextReceiversChecker,
ValueParameterUsageInDefaultArgumentChecker,
CyclicAnnotationsChecker,
ExpressionAfterTypeParameterWithoutSpacingChecker,
)
private val DEFAULT_CALL_CHECKERS = listOf(
@@ -0,0 +1,62 @@
/*
* Copyright 2010-2022 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.resolve.checkers
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.PropertyGetterDescriptor
import org.jetbrains.kotlin.descriptors.PropertySetterDescriptor
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
object ExpressionAfterTypeParameterWithoutSpacingChecker : DeclarationChecker {
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
if (context.languageVersionSettings.supportsFeature(LanguageFeature.AllowExpressionAfterTypeReferenceWithoutSpacing)) return
when (declaration) {
is KtProperty -> {
if (descriptor is PropertyGetterDescriptor || descriptor is PropertySetterDescriptor) return
check(declaration.typeReference, declaration.initializer, context)
}
is KtFunction -> {
for (valueParameter in declaration.valueParameters) {
check(valueParameter.typeReference, valueParameter.defaultValue, context)
}
check(declaration.typeReference, declaration.bodyExpression, context)
}
}
}
private fun check(typeReference: KtTypeReference?, expression: KtExpression?, context: DeclarationCheckerContext) {
if (typeReference == null) return
if (expression == null) return
val leftSide = typeReference.getRightMostToken()
if (leftSide.node.elementType != KtTokens.GT) return
val rightSide = typeReference.nextSibling?.getLeftMostToken() ?: return
if (rightSide.node.elementType != KtTokens.EQ) return
context.trace.report(Errors.EXPRESSION_AFTER_TYPE_REFERENCE_WITHOUT_SPACING_NOT_ALLOWED.on(rightSide))
}
private fun PsiElement.getLeftMostToken(): PsiElement {
var current = firstChild ?: return this
while (true) {
current = current.firstChild ?: return current
}
}
private fun PsiElement.getRightMostToken(): PsiElement {
var current = lastChild ?: return this
while (true) {
current = current.lastChild ?: return current
}
}
}
@@ -283,7 +283,6 @@ LONELY_BACKTICK=`
"++" { return KtTokens.PLUSPLUS ; }
"--" { return KtTokens.MINUSMINUS; }
"<=" { return KtTokens.LTEQ ; }
">=" { return KtTokens.GTEQ ; }
"==" { return KtTokens.EQEQ ; }
"!=" { return KtTokens.EXCLEQ ; }
"&&" { return KtTokens.ANDAND ; }
File diff suppressed because it is too large Load Diff
@@ -2428,11 +2428,13 @@ public class KotlinParsing extends AbstractKotlinParsing {
projection.done(TYPE_PROJECTION);
if (!at(COMMA)) break;
advance(); // COMMA
if (at(GT)) {
if (at(GT) || at(GTEQ)) {
break;
}
}
myBuilder.disableJoiningComplexTokens();
boolean atGT = at(GT);
if (!atGT) {
error("Expecting a '>'");
@@ -2440,6 +2442,9 @@ public class KotlinParsing extends AbstractKotlinParsing {
else {
advance(); // GT
}
myBuilder.restoreJoiningComplexTokensState();
myBuilder.restoreNewlinesState();
return atGT;
}
@@ -30,7 +30,7 @@ import org.jetbrains.kotlin.lexer.KtTokens;
import static org.jetbrains.kotlin.lexer.KtTokens.*;
public class SemanticWhitespaceAwarePsiBuilderImpl extends PsiBuilderAdapter implements SemanticWhitespaceAwarePsiBuilder {
private final TokenSet complexTokens = TokenSet.create(SAFE_ACCESS, ELVIS, EXCLEXCL);
private final TokenSet complexTokens = TokenSet.create(SAFE_ACCESS, ELVIS, EXCLEXCL, GTEQ);
private final Stack<Boolean> joinComplexTokens = new Stack<>();
private final Stack<Boolean> newlinesEnabled = new Stack<>();
@@ -155,6 +155,10 @@ public class SemanticWhitespaceAwarePsiBuilderImpl extends PsiBuilderAdapter imp
IElementType nextRawToken = rawLookup(rawLookupSteps);
if (nextRawToken == EXCL) return EXCLEXCL;
}
else if (rawTokenType == GT){
IElementType nextRawToken = rawLookup(rawLookupSteps);
if (nextRawToken == EQ) return GTEQ;
}
return rawTokenType;
}
@@ -181,9 +185,11 @@ public class SemanticWhitespaceAwarePsiBuilderImpl extends PsiBuilderAdapter imp
if (!joinComplexTokens()) return super.getTokenText();
IElementType tokenType = getTokenType();
if (complexTokens.contains(tokenType)) {
if (tokenType == ELVIS) return "?:";
if (tokenType == SAFE_ACCESS) return "?.";
}
if (tokenType == ELVIS) return "?:";
if (tokenType == SAFE_ACCESS) return "?.";
if (tokenType == EXCLEXCL) return "!!";
if (tokenType == GTEQ) return ">=";
}
return super.getTokenText();
}
@@ -0,0 +1,14 @@
// FIR_IDENTICAL
// !LANGUAGE: +AllowExpressionAfterTypeReferenceWithoutSpacing
// ISSUE: KT-35811
class A<T>
val reportedProperty: A<String>=A()
fun reportedFunction(a: A<String>=A()): A<String>=a
val unreportedProperty0: A<String> =A()
fun unreportedFunction0(a: A<String> =A()): A<String> =A()
val unreportedProperty1: String=""
fun unreportedFunction1(a: Int=0): Int=a
@@ -0,0 +1,15 @@
package
public val reportedProperty: A<kotlin.String>
public val unreportedProperty0: A<kotlin.String>
public val unreportedProperty1: kotlin.String = ""
public fun reportedFunction(/*0*/ a: A<kotlin.String> = ...): A<kotlin.String>
public fun unreportedFunction0(/*0*/ a: A<kotlin.String> = ...): A<kotlin.String>
public fun unreportedFunction1(/*0*/ a: kotlin.Int = ...): kotlin.Int
public final class A</*0*/ T> {
public constructor A</*0*/ T>()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -0,0 +1,13 @@
// !LANGUAGE: -AllowExpressionAfterTypeReferenceWithoutSpacing
// ISSUE: KT-35811
class A<T>
val reportedProperty: A<String>=A()
fun reportedFunction(a: A<String>=A()): A<String>=a
val unreportedProperty0: A<String> =A()
fun unreportedFunction0(a: A<String> =A()): A<String> =A()
val unreportedProperty1: String=""
fun unreportedFunction1(a: Int=0): Int=a
@@ -0,0 +1,13 @@
// !LANGUAGE: -AllowExpressionAfterTypeReferenceWithoutSpacing
// ISSUE: KT-35811
class A<T>
val reportedProperty: A<String><!EXPRESSION_AFTER_TYPE_REFERENCE_WITHOUT_SPACING_NOT_ALLOWED!>=<!>A()
fun reportedFunction(a: A<String><!EXPRESSION_AFTER_TYPE_REFERENCE_WITHOUT_SPACING_NOT_ALLOWED!>=<!>A()): A<String><!EXPRESSION_AFTER_TYPE_REFERENCE_WITHOUT_SPACING_NOT_ALLOWED!>=<!>a
val unreportedProperty0: A<String> =A()
fun unreportedFunction0(a: A<String> =A()): A<String> =A()
val unreportedProperty1: String=""
fun unreportedFunction1(a: Int=0): Int=a
@@ -0,0 +1,15 @@
package
public val reportedProperty: A<kotlin.String>
public val unreportedProperty0: A<kotlin.String>
public val unreportedProperty1: kotlin.String = ""
public fun reportedFunction(/*0*/ a: A<kotlin.String> = ...): A<kotlin.String>
public fun unreportedFunction0(/*0*/ a: A<kotlin.String> = ...): A<kotlin.String>
public fun unreportedFunction1(/*0*/ a: kotlin.Int = ...): kotlin.Int
public final class A</*0*/ T> {
public constructor A</*0*/ T>()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -0,0 +1,3 @@
val a: A<B>= 1
fun a(): A<B>= 1
fun a(a: A<B>= 1) {}
@@ -0,0 +1,85 @@
KtFile: TypeParameterBeforeEqualSign.kt
PACKAGE_DIRECTIVE
<empty list>
IMPORT_LIST
<empty list>
PROPERTY
PsiElement(val)('val')
PsiWhiteSpace(' ')
PsiElement(IDENTIFIER)('a')
PsiElement(COLON)(':')
PsiWhiteSpace(' ')
TYPE_REFERENCE
USER_TYPE
REFERENCE_EXPRESSION
PsiElement(IDENTIFIER)('A')
TYPE_ARGUMENT_LIST
PsiElement(LT)('<')
TYPE_PROJECTION
TYPE_REFERENCE
USER_TYPE
REFERENCE_EXPRESSION
PsiElement(IDENTIFIER)('B')
PsiElement(GT)('>')
PsiElement(EQ)('=')
PsiWhiteSpace(' ')
INTEGER_CONSTANT
PsiElement(INTEGER_LITERAL)('1')
PsiWhiteSpace('\n')
FUN
PsiElement(fun)('fun')
PsiWhiteSpace(' ')
PsiElement(IDENTIFIER)('a')
VALUE_PARAMETER_LIST
PsiElement(LPAR)('(')
PsiElement(RPAR)(')')
PsiElement(COLON)(':')
PsiWhiteSpace(' ')
TYPE_REFERENCE
USER_TYPE
REFERENCE_EXPRESSION
PsiElement(IDENTIFIER)('A')
TYPE_ARGUMENT_LIST
PsiElement(LT)('<')
TYPE_PROJECTION
TYPE_REFERENCE
USER_TYPE
REFERENCE_EXPRESSION
PsiElement(IDENTIFIER)('B')
PsiElement(GT)('>')
PsiElement(EQ)('=')
PsiWhiteSpace(' ')
INTEGER_CONSTANT
PsiElement(INTEGER_LITERAL)('1')
PsiWhiteSpace('\n')
FUN
PsiElement(fun)('fun')
PsiWhiteSpace(' ')
PsiElement(IDENTIFIER)('a')
VALUE_PARAMETER_LIST
PsiElement(LPAR)('(')
VALUE_PARAMETER
PsiElement(IDENTIFIER)('a')
PsiElement(COLON)(':')
PsiWhiteSpace(' ')
TYPE_REFERENCE
USER_TYPE
REFERENCE_EXPRESSION
PsiElement(IDENTIFIER)('A')
TYPE_ARGUMENT_LIST
PsiElement(LT)('<')
TYPE_PROJECTION
TYPE_REFERENCE
USER_TYPE
REFERENCE_EXPRESSION
PsiElement(IDENTIFIER)('B')
PsiElement(GT)('>')
PsiElement(EQ)('=')
PsiWhiteSpace(' ')
INTEGER_CONSTANT
PsiElement(INTEGER_LITERAL)('1')
PsiElement(RPAR)(')')
PsiWhiteSpace(' ')
BLOCK
PsiElement(LBRACE)('{')
PsiElement(RBRACE)('}')
@@ -10661,6 +10661,28 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
}
}
@Nested
@TestMetadata("compiler/testData/diagnostics/tests/expressionAfterTypeReference")
@TestDataPath("$PROJECT_ROOT")
public class ExpressionAfterTypeReference {
@Test
public void testAllFilesPresentInExpressionAfterTypeReference() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/expressionAfterTypeReference"), Pattern.compile("^(.*)\\.kts?$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
}
@Test
@TestMetadata("allowExpressionAfterTypeReference.kt")
public void testAllowExpressionAfterTypeReference() throws Exception {
runTest("compiler/testData/diagnostics/tests/expressionAfterTypeReference/allowExpressionAfterTypeReference.kt");
}
@Test
@TestMetadata("forbidExpressionAfterTypeReference.kt")
public void testForbidExpressionAfterTypeReference() throws Exception {
runTest("compiler/testData/diagnostics/tests/expressionAfterTypeReference/forbidExpressionAfterTypeReference.kt");
}
}
@Nested
@TestMetadata("compiler/testData/diagnostics/tests/extensions")
@TestDataPath("$PROJECT_ROOT")
@@ -2396,6 +2396,11 @@ public class ParsingTestGenerated extends AbstractParsingTest {
runTest("compiler/testData/psi/recovery/SameLineStatementRecovery.kt");
}
@TestMetadata("TypeParameterBeforeEqualSign.kt")
public void testTypeParameterBeforeEqualSign() throws Exception {
runTest("compiler/testData/psi/recovery/TypeParameterBeforeEqualSign.kt");
}
@TestMetadata("UnfinishedExtension.kt")
public void testUnfinishedExtension() throws Exception {
runTest("compiler/testData/psi/recovery/UnfinishedExtension.kt");
@@ -262,6 +262,7 @@ enum class LanguageFeature(
SynchronizedSuspendError(KOTLIN_1_8),
EnableDfaWarningsInK2(KOTLIN_1_8, kind = OTHER), // KT-50965
ReportNonVarargSpreadOnGenericCalls(KOTLIN_1_8, kind = BUG_FIX), // KT-48162
AllowExpressionAfterTypeReferenceWithoutSpacing(KOTLIN_1_8, kind = BUG_FIX), // KT-35811
// 1.9