Add warning for ambiguous annotated expression syntax

#KT-14238 In Progress
This commit is contained in:
Denis Zharkov
2016-10-12 11:40:12 +03:00
parent 993d226c8f
commit 84153f9636
13 changed files with 159 additions and 12 deletions
@@ -115,8 +115,8 @@ public interface KtNodeTypes {
KtNodeType BREAK = new KtNodeType("BREAK", KtBreakExpression.class);
KtNodeType IF = new KtNodeType("IF", KtIfExpression.class);
KtNodeType CONDITION = new KtNodeType("CONDITION", KtContainerNode.class);
KtNodeType THEN = new KtNodeType("THEN", KtContainerNode.class);
KtNodeType ELSE = new KtNodeType("ELSE", KtContainerNode.class);
KtNodeType THEN = new KtNodeType("THEN", KtContainerNodeForControlStructureBody.class);
KtNodeType ELSE = new KtNodeType("ELSE", KtContainerNodeForControlStructureBody.class);
KtNodeType TRY = new KtNodeType("TRY", KtTryExpression.class);
KtNodeType CATCH = new KtNodeType("CATCH", KtCatchClause.class);
KtNodeType FINALLY = new KtNodeType("FINALLY", KtFinallySection.class);
@@ -124,7 +124,7 @@ public interface KtNodeTypes {
KtNodeType WHILE = new KtNodeType("WHILE", KtWhileExpression.class);
KtNodeType DO_WHILE = new KtNodeType("DO_WHILE", KtDoWhileExpression.class);
KtNodeType LOOP_RANGE = new KtNodeType("LOOP_RANGE", KtContainerNode.class);
KtNodeType BODY = new KtNodeType("BODY", KtContainerNode.class);
KtNodeType BODY = new KtNodeType("BODY", KtContainerNodeForControlStructureBody.class);
KtNodeType BLOCK = new KtNodeType("BLOCK", KtBlockExpression.class);
IElementType LAMBDA_EXPRESSION = new IErrorCounterReparseableElementType("LAMBDA_EXPRESSION", KotlinLanguage.INSTANCE) {
@@ -180,6 +180,7 @@ public interface Errors {
DiagnosticFactory0<KtExpression> ANNOTATION_PARAMETER_MUST_BE_KCLASS_LITERAL = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtExpression> ANNOTATION_PARAMETER_MUST_BE_ENUM_CONST = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtExpression> ANNOTATION_PARAMETER_DEFAULT_VALUE_MUST_BE_CONSTANT = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtAnnotatedExpression> ANNOTATIONS_ON_BLOCK_LEVEL_EXPRESSION_ON_THE_SAME_LINE = DiagnosticFactory0.create(WARNING);
DiagnosticFactory0<PsiElement> ILLEGAL_SINCE_KOTLIN_VALUE = DiagnosticFactory0.create(ERROR);
DiagnosticFactory1<PsiElement, String> NEWER_VERSION_IN_SINCE_KOTLIN = DiagnosticFactory1.create(WARNING);
@@ -725,6 +725,10 @@ public class DefaultErrorMessages {
MAP.put(ANNOTATION_PARAMETER_MUST_BE_KCLASS_LITERAL, "An annotation parameter must be a class literal (T::class)");
MAP.put(ANNOTATION_PARAMETER_DEFAULT_VALUE_MUST_BE_CONSTANT, "Default value of annotation parameter must be a compile-time constant");
MAP.put(ANNOTATIONS_ON_BLOCK_LEVEL_EXPRESSION_ON_THE_SAME_LINE,
"Annotations on block-level expressions are being parsed differently depending on presence of a new line after them. " +
"Use new line if whole block-level expression must be annotated or wrap annotated expression in parentheses");
MAP.put(CONST_VAL_NOT_TOP_LEVEL_OR_OBJECT, "Const 'val' are only allowed on top level or in objects");
MAP.put(CONST_VAL_WITH_DELEGATE, "Const 'val' should not have a delegate");
MAP.put(CONST_VAL_WITH_GETTER, "Const 'val' should not have a getter");
@@ -0,0 +1,21 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.psi
import com.intellij.lang.ASTNode
class KtContainerNodeForControlStructureBody(node: ASTNode) : KtContainerNode(node)
@@ -1539,12 +1539,47 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
}
protected void resolveAnnotationsOnExpression(KtAnnotatedExpression expression, ExpressionTypingContext context) {
if (isAnnotatedExpressionInBlockLevelBinary(expression)) {
context.trace.report(ANNOTATIONS_ON_BLOCK_LEVEL_EXPRESSION_ON_THE_SAME_LINE.on(expression));
}
if (!(expression.getBaseExpression() instanceof KtObjectLiteralExpression)) {
// annotations on object literals are resolved later inside LazyClassDescriptor
components.annotationResolver.resolveAnnotationsWithArguments(context.scope, expression.getAnnotationEntries(), context.trace);
}
}
private static boolean isAnnotatedExpressionInBlockLevelBinary(KtAnnotatedExpression annotatedExpression) {
PsiElement current = annotatedExpression;
PsiElement parent = current.getParent();
// Here we implicitly assume that grammar rules are:
// blockLevelExpression = annotations expression
// expression = binaryExpression
// binaryExpression = prefixExpression <op> prefixExpression
// prefixExpression = annotations expression
// If there is no binary parent, annotations are being parsed the same way independently of newline after them
if (!(parent instanceof KtBinaryExpression)) return false;
while (parent instanceof KtBinaryExpression) {
// if we came not from the left parent, there's no need to report an error
if (((KtBinaryExpression) parent).getLeft() != current) {
return false;
}
current = parent;
parent = parent.getParent();
}
return isParentForBlockLevelExpression(parent);
}
private static boolean isParentForBlockLevelExpression(@Nullable PsiElement parent) {
return parent instanceof KtBlockExpression ||
parent instanceof KtContainerNodeForControlStructureBody ||
parent instanceof KtWhenEntry;
}
@Override
public KotlinTypeInfo visitKtElement(@NotNull KtElement element, ExpressionTypingContext context) {
context.trace.report(UNSUPPORTED.on(element, getClass().getCanonicalName()));
@@ -0,0 +1,59 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
@Target(AnnotationTarget.EXPRESSION)
annotation class Ann1
@Target(AnnotationTarget.EXPRESSION)
annotation class Ann2(val x: String)
fun bar() {}
fun bar(block: () -> Unit) {}
fun foo(y: IntArray) {
@Ann1 bar()
@Ann1 bar() { }
@Ann1 bar { }
@Ann2("") bar()
@Ann2("") bar() { }
@Ann2("") bar { }
@Ann1 @Ann2("") bar()
var x = 1
@Ann1 ++x
@Ann1 x++
@Ann2("") ++x
@Ann2("") x++
@Ann1 @Ann2("") ++x
@Ann1 @Ann2("") x++
@Ann1 y[0]
@Ann1 <!UNUSED_LAMBDA_EXPRESSION!>{ x: Int -> x }<!>
@Ann1 { x: Int -> x }(1)
@Ann1 object { fun foo() = 1 }
@Ann1 object { fun foo() = 1 }.foo()
@Ann1() (x * x)
var z = 1
<!ANNOTATIONS_ON_BLOCK_LEVEL_EXPRESSION_ON_THE_SAME_LINE!>@Ann1 x<!> + z
<!ANNOTATIONS_ON_BLOCK_LEVEL_EXPRESSION_ON_THE_SAME_LINE!>@Ann1 x<!> = x + 2
<!ANNOTATIONS_ON_BLOCK_LEVEL_EXPRESSION_ON_THE_SAME_LINE!>@Ann1 x<!> += z + 2
<!ANNOTATIONS_ON_BLOCK_LEVEL_EXPRESSION_ON_THE_SAME_LINE!>@Ann1 x<!> + 6 * 2 > 0
<!ANNOTATIONS_ON_BLOCK_LEVEL_EXPRESSION_ON_THE_SAME_LINE!>@Ann1 x<!> * 6 + 2 > 0
<!ANNOTATIONS_ON_BLOCK_LEVEL_EXPRESSION_ON_THE_SAME_LINE!>@Ann1 object { operator fun plus(x: Int) = 1 }<!> + 1
<!ANNOTATIONS_ON_BLOCK_LEVEL_EXPRESSION_ON_THE_SAME_LINE!>@Ann1 object { operator fun plus(x: Int) = 1 }<!> + 1 * 4 > 0
<!ANNOTATIONS_ON_BLOCK_LEVEL_EXPRESSION_ON_THE_SAME_LINE!>@Ann1 x<!> foo z + 8
1 + @Ann1 x
1 + @Ann1 x * z + 8
x foo @Ann1 z + 8
}
infix fun Int.foo(other: Int) = 1
@@ -0,0 +1,21 @@
package
public fun bar(): kotlin.Unit
public fun bar(/*0*/ block: () -> kotlin.Unit): kotlin.Unit
public fun foo(/*0*/ y: kotlin.IntArray): kotlin.Unit
public infix fun kotlin.Int.foo(/*0*/ other: kotlin.Int): kotlin.Int
@kotlin.annotation.Target(allowedTargets = {AnnotationTarget.EXPRESSION}) public final annotation class Ann1 : kotlin.Annotation {
public constructor Ann1()
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
}
@kotlin.annotation.Target(allowedTargets = {AnnotationTarget.EXPRESSION}) public final annotation class Ann2 : kotlin.Annotation {
public constructor Ann2(/*0*/ x: kotlin.String)
public final val x: kotlin.String
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
}
@@ -3,7 +3,7 @@ annotation class ExprAnn
fun foo(): Int {
var a: Int
@ExprAnn a = 1
@ExprAnn a += 1
<!ANNOTATIONS_ON_BLOCK_LEVEL_EXPRESSION_ON_THE_SAME_LINE!>@ExprAnn a<!> = 1
<!ANNOTATIONS_ON_BLOCK_LEVEL_EXPRESSION_ON_THE_SAME_LINE!>@ExprAnn a<!> += 1
return a
}
}
@@ -7,5 +7,5 @@ fun transform(i: Int, tr: (Int) -> Int): Int = <!WRONG_ANNOTATION_TARGET!>@base<
@base <!WRONG_ANNOTATION_TARGET!>@special<!> fun foo(i: Int): Int {
val j = <!WRONG_ANNOTATION_TARGET!>@base<!> @special i + 1
if (j == 1) return foo(@special <!WRONG_ANNOTATION_TARGET!>@base<!> 42)
return transform(@special j, @base @special { @special it * 2 })
return transform(@special j, @base @special { <!ANNOTATIONS_ON_BLOCK_LEVEL_EXPRESSION_ON_THE_SAME_LINE!>@special it<!> * 2 })
}
@@ -9,5 +9,5 @@ fun transform(i: Int, tr: (@<!DEBUG_INFO_MISSING_UNRESOLVED!>special<!> Int) ->
fun foo(i: Int): Int {
val j = @special i + 1
if (j == 1) return foo(@special 42)
return transform(@special j, @special { i: @base Int -> <!WRONG_ANNOTATION_TARGET!>@base<!> i * 2 })
return transform(@special j, @special { i: @base Int -> <!ANNOTATIONS_ON_BLOCK_LEVEL_EXPRESSION_ON_THE_SAME_LINE!><!WRONG_ANNOTATION_TARGET!>@base<!> i<!> * 2 })
}
@@ -1,7 +1,7 @@
fun <T : CharSequence> foo(x: Array<Any>, block: (T, Int) -> Int) {
var r: Any?
@Suppress("UNCHECKED_CAST") r = block(<!UNCHECKED_CAST!>x[0] as T<!>, "" <!CAST_NEVER_SUCCEEDS!>as<!> Int)
<!ANNOTATIONS_ON_BLOCK_LEVEL_EXPRESSION_ON_THE_SAME_LINE!>@Suppress("UNCHECKED_CAST") r<!> = block(<!UNCHECKED_CAST!>x[0] as T<!>, "" <!CAST_NEVER_SUCCEEDS!>as<!> Int)
// to prevent unused assignment diagnostic for the above statement
<!DEBUG_INFO_SMARTCAST!>r<!>.hashCode()
@@ -9,11 +9,11 @@ fun <T : CharSequence> foo(x: Array<Any>, block: (T, Int) -> Int) {
var i = 1
if (i != 1) {
@Suppress("UNCHECKED_CAST") i += block(<!UNCHECKED_CAST!>x[0] as T<!>, "" <!CAST_NEVER_SUCCEEDS!>as<!> Int).toInt()
<!ANNOTATIONS_ON_BLOCK_LEVEL_EXPRESSION_ON_THE_SAME_LINE!>@Suppress("UNCHECKED_CAST") i<!> += block(<!UNCHECKED_CAST!>x[0] as T<!>, "" <!CAST_NEVER_SUCCEEDS!>as<!> Int).toInt()
}
if (i != 1) @Suppress("UNCHECKED_CAST")
i += block(x[0] as T, "" <!CAST_NEVER_SUCCEEDS!>as<!> Int).toInt()
if (i != 1) @Suppress("UNCHECKED_CAST") i += block(<!UNCHECKED_CAST!>x[0] as T<!>, "" <!CAST_NEVER_SUCCEEDS!>as<!> Int).toInt()
if (i != 1) <!ANNOTATIONS_ON_BLOCK_LEVEL_EXPRESSION_ON_THE_SAME_LINE!>@Suppress("UNCHECKED_CAST") i<!> += block(<!UNCHECKED_CAST!>x[0] as T<!>, "" <!CAST_NEVER_SUCCEEDS!>as<!> Int).toInt()
}
@@ -939,6 +939,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
doTest(fileName);
}
@TestMetadata("blockLevelOnTheSameLineWarning.kt")
public void testBlockLevelOnTheSameLineWarning() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/annotations/blockLevelOnTheSameLineWarning.kt");
doTest(fileName);
}
@TestMetadata("ConstructorCall.kt")
public void testConstructorCall() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/annotations/ConstructorCall.kt");
+1 -1
View File
@@ -9,7 +9,7 @@ fun <info descr="null">bar</info>(<info descr="null">block</info>: () -> <info d
fun <info descr="null">foo</info>() {
1 + <info descr="null" textAttributesKey="KOTLIN_ANNOTATION">@Ann</info> 2
<info descr="null" textAttributesKey="KOTLIN_ANNOTATION">@Ann</info> 3 + 4
<warning descr="[ANNOTATIONS_ON_BLOCK_LEVEL_EXPRESSION_ON_THE_SAME_LINE] Annotations on block-level expressions are being parsed differently depending on presence of a new line after them. Use new line if whole block-level expression must be annotated or wrap annotated expression in parentheses" textAttributesKey="WARNING_ATTRIBUTES"><info descr="null" textAttributesKey="KOTLIN_ANNOTATION">@Ann</info> 3</warning> + 4
<info descr="null"><info descr="null">bar</info></info> <info descr="null" textAttributesKey="KOTLIN_ANNOTATION">@Ann</info> { 1 }