Introduce SinceKotlin annotation, check validity of its value

#KT-14297 Fixed
This commit is contained in:
Alexander Udalov
2016-09-26 19:52:51 +03:00
parent 8d660c2f6e
commit e3df8ed2fe
13 changed files with 215 additions and 2 deletions
@@ -176,6 +176,8 @@ public interface Errors {
DiagnosticFactory0<KtExpression> ANNOTATION_PARAMETER_MUST_BE_ENUM_CONST = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtExpression> ANNOTATION_PARAMETER_DEFAULT_VALUE_MUST_BE_CONSTANT = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<PsiElement> ILLEGAL_SINCE_KOTLIN_VALUE = DiagnosticFactory0.create(ERROR);
// Const
DiagnosticFactory0<PsiElement> CONST_VAL_NOT_TOP_LEVEL_OR_OBJECT = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<PsiElement> CONST_VAL_WITH_GETTER = DiagnosticFactory0.create(ERROR);
@@ -161,6 +161,8 @@ public class DefaultErrorMessages {
MAP.put(INAPPLICABLE_PARAM_TARGET, "'@param:' annotations could be applied only to primary constructor parameters");
MAP.put(REDUNDANT_ANNOTATION_TARGET, "Redundant annotation target ''{0}''", STRING);
MAP.put(ILLEGAL_SINCE_KOTLIN_VALUE, "Invalid @SinceKotlin annotation value (should be 'major.minor' or 'major.minor.patch')");
MAP.put(REDUNDANT_MODIFIER, "Modifier ''{0}'' is redundant because ''{1}'' is present", TO_STRING, TO_STRING);
MAP.put(REDUNDANT_OPEN_IN_INTERFACE, "Modifier 'open' is redundant for abstract interface members");
MAP.put(REDUNDANT_MODIFIER_IN_GETTER, "Visibility modifiers are redundant in getter");
@@ -64,7 +64,9 @@ private val DEFAULT_DECLARATION_CHECKERS = listOf(
InlineParameterChecker,
InfixModifierChecker(),
SuspendModifierChecker,
CoroutineModifierChecker)
CoroutineModifierChecker,
SinceKotlinAnnotationValueChecker
)
private val DEFAULT_CALL_CHECKERS = listOf(
CapturingInClosureChecker(), InlineCheckerWrapper(), ReifiedTypeParameterSubstitutionChecker(), SafeCallChecker(),
@@ -0,0 +1,39 @@
/*
* 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.resolve.checkers
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.getSinceKotlinAnnotation
import org.jetbrains.kotlin.resolve.source.getPsi
object SinceKotlinAnnotationValueChecker : SimpleDeclarationChecker {
private val regex: Regex = "(0|[1-9][0-9]*)".let { number -> Regex("$number\\.$number(\\.$number)?") }
override fun check(
declaration: KtDeclaration, descriptor: DeclarationDescriptor, diagnosticHolder: DiagnosticSink, bindingContext: BindingContext
) {
val annotation = descriptor.getSinceKotlinAnnotation() ?: return
val version = annotation.allValueArguments.values.singleOrNull()?.value as? String ?: return
if (!version.matches(regex)) {
diagnosticHolder.report(Errors.ILLEGAL_SINCE_KOTLIN_VALUE.on(annotation.source.getPsi() ?: declaration))
}
}
}
@@ -0,0 +1,26 @@
/*
* 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.resolve
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.name.FqName
private val SINCE_KOTLIN_FQ_NAME = FqName("kotlin.SinceKotlin")
internal fun DeclarationDescriptor.getSinceKotlinAnnotation(): AnnotationDescriptor? =
annotations.findAnnotation(SINCE_KOTLIN_FQ_NAME)
+6
View File
@@ -647,6 +647,12 @@ public final class ShortArray : kotlin.Cloneable {
public final operator fun set(/*0*/ index: kotlin.Int, /*1*/ value: kotlin.Short): kotlin.Unit
}
@kotlin.annotation.Target(allowedTargets = {AnnotationTarget.CLASS, AnnotationTarget.PROPERTY, AnnotationTarget.CONSTRUCTOR, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER}) @kotlin.annotation.Retention(value = AnnotationRetention.BINARY) @kotlin.annotation.MustBeDocumented() public final annotation class SinceKotlin : kotlin.Annotation {
/*primary*/ public constructor SinceKotlin(/*0*/ version: kotlin.String)
public final val version: kotlin.String
public final fun <get-version>(): kotlin.String
}
public final class String : kotlin.Comparable<kotlin.String>, kotlin.CharSequence {
/*primary*/ public constructor String()
public open override /*1*/ val length: kotlin.Int
+6
View File
@@ -651,6 +651,12 @@ public final class ShortArray : kotlin.Cloneable, java.io.Serializable {
public final operator fun set(/*0*/ index: kotlin.Int, /*1*/ value: kotlin.Short): kotlin.Unit
}
@kotlin.annotation.Target(allowedTargets = {AnnotationTarget.CLASS, AnnotationTarget.PROPERTY, AnnotationTarget.CONSTRUCTOR, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER}) @kotlin.annotation.Retention(value = AnnotationRetention.BINARY) @kotlin.annotation.MustBeDocumented() public final annotation class SinceKotlin : kotlin.Annotation {
/*primary*/ public constructor SinceKotlin(/*0*/ version: kotlin.String)
public final val version: kotlin.String
public final fun <get-version>(): kotlin.String
}
public final class String : kotlin.Comparable<kotlin.String>, kotlin.CharSequence, java.io.Serializable {
/*primary*/ public constructor String()
public open override /*1*/ val length: kotlin.Int
+6
View File
@@ -653,6 +653,12 @@ public final class ShortArray : kotlin.Cloneable, java.io.Serializable {
public final operator fun set(/*0*/ index: kotlin.Int, /*1*/ value: kotlin.Short): kotlin.Unit
}
@kotlin.annotation.Target(allowedTargets = {AnnotationTarget.CLASS, AnnotationTarget.PROPERTY, AnnotationTarget.CONSTRUCTOR, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER}) @kotlin.annotation.Retention(value = AnnotationRetention.BINARY) @kotlin.annotation.MustBeDocumented() public final annotation class SinceKotlin : kotlin.Annotation {
/*primary*/ public constructor SinceKotlin(/*0*/ version: kotlin.String)
public final val version: kotlin.String
public final fun <get-version>(): kotlin.String
}
public final class String : kotlin.Comparable<kotlin.String>, kotlin.CharSequence, java.io.Serializable {
/*primary*/ public constructor String()
public open override /*1*/ val length: kotlin.Int
@@ -651,6 +651,12 @@ public final class ShortArray : kotlin.Cloneable, java.io.Serializable {
public final operator fun set(/*0*/ index: kotlin.Int, /*1*/ value: kotlin.Short): kotlin.Unit
}
@kotlin.annotation.Target(allowedTargets = {AnnotationTarget.CLASS, AnnotationTarget.PROPERTY, AnnotationTarget.CONSTRUCTOR, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER}) @kotlin.annotation.Retention(value = AnnotationRetention.BINARY) @kotlin.annotation.MustBeDocumented() public final annotation class SinceKotlin : kotlin.Annotation {
/*primary*/ public constructor SinceKotlin(/*0*/ version: kotlin.String)
public final val version: kotlin.String
public final fun <get-version>(): kotlin.String
}
public final class String : kotlin.Comparable<kotlin.String>, kotlin.CharSequence, java.io.Serializable {
/*primary*/ public constructor String()
public open override /*1*/ val length: kotlin.Int
@@ -0,0 +1,74 @@
<!ILLEGAL_SINCE_KOTLIN_VALUE!>@SinceKotlin("")<!>
fun f01() {}
<!ILLEGAL_SINCE_KOTLIN_VALUE!>@SinceKotlin("x")<!>
fun f02() {}
<!ILLEGAL_SINCE_KOTLIN_VALUE!>@SinceKotlin("1")<!>
fun f03() {}
<!ILLEGAL_SINCE_KOTLIN_VALUE!>@SinceKotlin("1,0")<!>
fun f04() {}
<!ILLEGAL_SINCE_KOTLIN_VALUE!>@SinceKotlin("1,0,1")<!>
fun f05() {}
<!ILLEGAL_SINCE_KOTLIN_VALUE!>@SinceKotlin("a.b")<!>
fun f06() {}
<!ILLEGAL_SINCE_KOTLIN_VALUE!>@SinceKotlin("1.a")<!>
fun f07() {}
<!ILLEGAL_SINCE_KOTLIN_VALUE!>@SinceKotlin("1.0.a")<!>
fun f08() {}
<!ILLEGAL_SINCE_KOTLIN_VALUE!>@SinceKotlin("1.0-beta")<!>
fun f09() {}
<!ILLEGAL_SINCE_KOTLIN_VALUE!>@SinceKotlin("1.1.0-dev-1111")<!>
fun f10() {}
<!ILLEGAL_SINCE_KOTLIN_VALUE!>@SinceKotlin("1.1.0+rc")<!>
fun f11() {}
<!ILLEGAL_SINCE_KOTLIN_VALUE!>@SinceKotlin("1.5.3.7")<!>
fun f12() {}
<!ILLEGAL_SINCE_KOTLIN_VALUE!>@SinceKotlin("01.1")<!>
fun f13() {}
<!ILLEGAL_SINCE_KOTLIN_VALUE!>@SinceKotlin("1.01")<!>
fun f14() {}
<!ILLEGAL_SINCE_KOTLIN_VALUE!>@SinceKotlin("-1.0")<!>
fun f15() {}
<!ILLEGAL_SINCE_KOTLIN_VALUE!>@SinceKotlin("1.-1.0")<!>
fun f16() {}
<!ILLEGAL_SINCE_KOTLIN_VALUE!>@SinceKotlin("0.00.1")<!>
fun f17() {}
<!ILLEGAL_SINCE_KOTLIN_VALUE!>@SinceKotlin("1..0")<!>
fun f18() {}
<!ILLEGAL_SINCE_KOTLIN_VALUE!>@SinceKotlin(" 1.0")<!>
fun f19() {}
<!ILLEGAL_SINCE_KOTLIN_VALUE!>@SinceKotlin("1.0 ")<!>
fun f20() {}
@SinceKotlin("1.1")
fun ok1() {}
@SinceKotlin("1.1.0")
fun ok2() {}
@SinceKotlin("0.0.0")
fun ok3() {}
@SinceKotlin("123456789012345678901234567890.123456789012345678901234567890.123456789012345678901234567890")
fun ok4() {}
@@ -0,0 +1,26 @@
package
@kotlin.SinceKotlin(version = "") public fun f01(): kotlin.Unit
@kotlin.SinceKotlin(version = "x") public fun f02(): kotlin.Unit
@kotlin.SinceKotlin(version = "1") public fun f03(): kotlin.Unit
@kotlin.SinceKotlin(version = "1,0") public fun f04(): kotlin.Unit
@kotlin.SinceKotlin(version = "1,0,1") public fun f05(): kotlin.Unit
@kotlin.SinceKotlin(version = "a.b") public fun f06(): kotlin.Unit
@kotlin.SinceKotlin(version = "1.a") public fun f07(): kotlin.Unit
@kotlin.SinceKotlin(version = "1.0.a") public fun f08(): kotlin.Unit
@kotlin.SinceKotlin(version = "1.0-beta") public fun f09(): kotlin.Unit
@kotlin.SinceKotlin(version = "1.1.0-dev-1111") public fun f10(): kotlin.Unit
@kotlin.SinceKotlin(version = "1.1.0+rc") public fun f11(): kotlin.Unit
@kotlin.SinceKotlin(version = "1.5.3.7") public fun f12(): kotlin.Unit
@kotlin.SinceKotlin(version = "01.1") public fun f13(): kotlin.Unit
@kotlin.SinceKotlin(version = "1.01") public fun f14(): kotlin.Unit
@kotlin.SinceKotlin(version = "-1.0") public fun f15(): kotlin.Unit
@kotlin.SinceKotlin(version = "1.-1.0") public fun f16(): kotlin.Unit
@kotlin.SinceKotlin(version = "0.00.1") public fun f17(): kotlin.Unit
@kotlin.SinceKotlin(version = "1..0") public fun f18(): kotlin.Unit
@kotlin.SinceKotlin(version = " 1.0") public fun f19(): kotlin.Unit
@kotlin.SinceKotlin(version = "1.0 ") public fun f20(): kotlin.Unit
@kotlin.SinceKotlin(version = "1.1") public fun ok1(): kotlin.Unit
@kotlin.SinceKotlin(version = "1.1.0") public fun ok2(): kotlin.Unit
@kotlin.SinceKotlin(version = "0.0.0") public fun ok3(): kotlin.Unit
@kotlin.SinceKotlin(version = "123456789012345678901234567890.123456789012345678901234567890.123456789012345678901234567890") public fun ok4(): kotlin.Unit
@@ -981,6 +981,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
doTest(fileName);
}
@TestMetadata("illegalSinceKotlinValue.kt")
public void testIllegalSinceKotlinValue() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/annotations/illegalSinceKotlinValue.kt");
doTest(fileName);
}
@TestMetadata("invalidTypesInAnnotationConstructor.kt")
public void testInvalidTypesInAnnotationConstructor() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/annotations/invalidTypesInAnnotationConstructor.kt");
+13 -1
View File
@@ -86,4 +86,16 @@ public annotation class Suppress(vararg val names: String)
@Target(TYPE)
@Retention(SOURCE)
@MustBeDocumented
public annotation class UnsafeVariance
public annotation class UnsafeVariance
/**
* Specifies the first version of Kotlin where a declaration has appeared.
* Using the declaration and specifying an older API version (via the `-api-version` command line option) will result in an error.
*
* @property version the version in the following formats: `<major>.<minor>` or `<major>.<minor>.<patch>`, where major, minor and patch
* are non-negative integer numbers without leading zeros.
*/
@Target(CLASS, PROPERTY, CONSTRUCTOR, FUNCTION, PROPERTY_GETTER, PROPERTY_SETTER)
@Retention(AnnotationRetention.BINARY)
@MustBeDocumented
public annotation class SinceKotlin(val version: String)