Introduce "-api-version" CLI option

The `@SinceKotlin("X.Y.Z")` annotation now hides a particular declaration from
resolution when the API version specified by the `-api-version` option is
_less_ than X.Y.Z. The comparison is performed as for versions in Maven:
MavenComparableVersion is in fact a copy of
org.apache.maven.artifact.versioning.ComparableVersion.

Also support "!API_VERSION" directive in diagnostic tests

 #KT-14298 Fixed
This commit is contained in:
Alexander Udalov
2016-09-26 19:56:22 +03:00
parent e3df8ed2fe
commit 167ab1f860
59 changed files with 1758 additions and 57 deletions
@@ -30,6 +30,11 @@ public abstract class CommonCompilerArguments {
@ValueDescription("<version>")
public String languageVersion;
@GradleOption(DefaultValues.LanguageVersions.class)
@Argument(value = "api-version", description = "Allow to use declarations only from the specified version of bundled libraries")
@ValueDescription("<version>")
public String apiVersion;
@GradleOption(DefaultValues.BooleanFalseDefault.class)
@Argument(value = "nowarn", description = "Generate no warnings")
public boolean suppressWarnings;
@@ -248,27 +248,58 @@ public abstract class CLICompiler<A extends CommonCompilerArguments> {
configuration.put(CLIConfigurationKeys.COMPILER_JAR_LOCATOR, locator);
}
if (arguments.languageVersion != null) {
LanguageVersion languageVersion = LanguageVersion.fromVersionString(arguments.languageVersion);
if (languageVersion != null) {
configuration.put(CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS, new LanguageVersionSettingsImpl(languageVersion));
LanguageVersion languageVersion = parseVersion(configuration, arguments.languageVersion, "language");
LanguageVersion apiVersion = parseVersion(configuration, arguments.apiVersion, "API");
if (languageVersion != null || apiVersion != null) {
if (languageVersion == null) {
// If only "-api-version" is specified, language version is assumed to be the latest
languageVersion = LanguageVersion.LATEST;
}
else {
List<String> versionStrings = ArraysKt.map(LanguageVersion.values(), new Function1<LanguageVersion, String>() {
@Override
public String invoke(LanguageVersion version) {
return version.getVersionString();
}
});
String message = "Unknown language version: " + arguments.languageVersion + "\n" +
"Supported language versions: " + StringsKt.join(versionStrings, ", ");
if (apiVersion == null) {
// If only "-language-version" is specified, API version is assumed to be equal to the language version
// (API version cannot be greater than the language version)
apiVersion = languageVersion;
}
if (apiVersion.compareTo(languageVersion) > 0) {
configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY).report(
CompilerMessageSeverity.ERROR, message, CompilerMessageLocation.NO_LOCATION
CompilerMessageSeverity.ERROR,
"-api-version (" + apiVersion.getVersionString() + ") cannot be greater than " +
"-language-version (" + languageVersion.getVersionString() + ")",
CompilerMessageLocation.NO_LOCATION
);
}
configuration.put(CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS,
new LanguageVersionSettingsImpl(languageVersion, ApiVersion.createByLanguageVersion(apiVersion)));
}
}
private static LanguageVersion parseVersion(
@NotNull CompilerConfiguration configuration, @Nullable String value, @NotNull String versionOf
) {
if (value == null) return null;
LanguageVersion version = LanguageVersion.fromVersionString(value);
if (version != null) {
return version;
}
List<String> versionStrings = ArraysKt.map(LanguageVersion.values(), new Function1<LanguageVersion, String>() {
@Override
public String invoke(LanguageVersion version) {
return version.getVersionString();
}
});
String message = "Unknown " + versionOf + " version: " + value + "\n" +
"Supported " + versionOf + " versions: " + StringsKt.join(versionStrings, ", ");
configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY).report(
CompilerMessageSeverity.ERROR, message, CompilerMessageLocation.NO_LOCATION
);
return null;
}
protected abstract void setupPlatformSpecificArgumentsAndServices(
@NotNull CompilerConfiguration configuration, @NotNull A arguments, @NotNull Services services
);
@@ -73,6 +73,11 @@ public interface Errors {
DiagnosticFactory1<KtReferenceExpression, KtReferenceExpression> UNRESOLVED_REFERENCE =
DiagnosticFactory1.create(ERROR, FOR_UNRESOLVED_REFERENCE);
DiagnosticFactory2<PsiElement, DeclarationDescriptor, String> DEPRECATION = DiagnosticFactory2.create(WARNING);
DiagnosticFactory2<PsiElement, DeclarationDescriptor, String> DEPRECATION_ERROR = DiagnosticFactory2.create(ERROR);
DiagnosticFactory2<PsiElement, String, String> API_NOT_AVAILABLE = DiagnosticFactory2.create(ERROR);
//Elements with "INVISIBLE_REFERENCE" error are marked as unresolved, unlike elements with "INVISIBLE_MEMBER" error
//"INVISIBLE_REFERENCE" is used for invisible classes references and references in import
DiagnosticFactory3<KtSimpleNameExpression, DeclarationDescriptor, Visibility, DeclarationDescriptor> INVISIBLE_REFERENCE =
@@ -278,9 +283,6 @@ public interface Errors {
DiagnosticFactory0<KtObjectDeclaration> MANY_COMPANION_OBJECTS = DiagnosticFactory0.create(ERROR, COMPANION_OBJECT);
DiagnosticFactory2<PsiElement, DeclarationDescriptor, String> DEPRECATION = DiagnosticFactory2.create(WARNING);
DiagnosticFactory2<PsiElement, DeclarationDescriptor, String> DEPRECATION_ERROR = DiagnosticFactory2.create(ERROR);
// Objects
DiagnosticFactory1<KtObjectDeclaration, ClassDescriptor> LOCAL_OBJECT_NOT_ALLOWED = DiagnosticFactory1.create(ERROR, DECLARATION_NAME);
@@ -300,6 +300,8 @@ public class DefaultErrorMessages {
MAP.put(DEPRECATION, "''{0}'' is deprecated. {1}", DEPRECATION_RENDERER, STRING);
MAP.put(DEPRECATION_ERROR, "Using ''{0}'' is an error. {1}", DEPRECATION_RENDERER, STRING);
MAP.put(API_NOT_AVAILABLE, "This declaration is only available since Kotlin {0} and cannot be used with the specified API version {1}", STRING, STRING);
MAP.put(LOCAL_OBJECT_NOT_ALLOWED, "Named object ''{0}'' is a singleton and cannot be local. Try to use anonymous object instead", NAME);
MAP.put(LOCAL_INTERFACE_NOT_ALLOWED, "''{0}'' is an interface so it cannot be local. Try to use anonymous object or abstract class instead", NAME);
MAP.put(ENUM_CLASS_CONSTRUCTOR_CALL, "Enum types cannot be instantiated");
@@ -71,11 +71,11 @@ private val DEFAULT_DECLARATION_CHECKERS = listOf(
private val DEFAULT_CALL_CHECKERS = listOf(
CapturingInClosureChecker(), InlineCheckerWrapper(), ReifiedTypeParameterSubstitutionChecker(), SafeCallChecker(),
DeprecatedCallChecker, CallReturnsArrayOfNothingChecker(), InfixCallChecker(), OperatorCallChecker(),
ConstructorHeaderCallChecker, ProtectedConstructorCallChecker,
ConstructorHeaderCallChecker, ProtectedConstructorCallChecker, ApiVersionCallChecker,
CoroutineSuspendCallChecker, BuilderFunctionsCallChecker
)
private val DEFAULT_TYPE_CHECKERS = emptyList<AdditionalTypeChecker>()
private val DEFAULT_CLASSIFIER_USAGE_CHECKERS = listOf(DeprecatedClassifierUsageChecker())
private val DEFAULT_CLASSIFIER_USAGE_CHECKERS = listOf(DeprecatedClassifierUsageChecker(), ApiVersionClassifierUsageChecker)
abstract class PlatformConfigurator(
@@ -0,0 +1,47 @@
/*
* 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.calls.checkers
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.diagnostics.Errors.API_NOT_AVAILABLE
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject
import org.jetbrains.kotlin.resolve.checkSinceKotlinVersionAccessibility
// TODO: consider combining with DeprecatedCallChecker somehow
object ApiVersionCallChecker : CallChecker {
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
check(resolvedCall.resultingDescriptor, context, reportOn)
}
private fun check(targetDescriptor: CallableDescriptor, context: CallCheckerContext, element: PsiElement) {
// Objects will be checked by ApiVersionClassifierUsageChecker
if (targetDescriptor is FakeCallableDescriptorForObject) return
val accessible = targetDescriptor.checkSinceKotlinVersionAccessibility(context.languageVersionSettings) { version ->
context.trace.report(
API_NOT_AVAILABLE.on(element, version.versionString, context.languageVersionSettings.apiVersion.versionString)
)
}
if (accessible && targetDescriptor is PropertyDescriptor && DeprecatedCallChecker.shouldCheckPropertyGetter(element)) {
targetDescriptor.getter?.let { check(it, context, element) }
}
}
}
@@ -57,7 +57,7 @@ object DeprecatedCallChecker : CallChecker {
private val PROPERTY_SET_OPERATIONS = TokenSet.create(*KtTokens.ALL_ASSIGNMENTS.types, KtTokens.PLUSPLUS, KtTokens.MINUSMINUS)
private fun shouldCheckPropertyGetter(expression: PsiElement): Boolean {
internal fun shouldCheckPropertyGetter(expression: PsiElement): Boolean {
// property getters do not come as callable yet, so we analyse surroundings to check for deprecation annotation on getter
val binaryExpression = PsiTreeUtil.getParentOfType<KtBinaryExpression>(expression, KtBinaryExpression::class.java)
if (binaryExpression != null) {
@@ -0,0 +1,37 @@
/*
* 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 com.intellij.psi.PsiElement
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.checkSinceKotlinVersionAccessibility
object ApiVersionClassifierUsageChecker : ClassifierUsageChecker {
override fun check(
targetDescriptor: ClassifierDescriptor,
trace: BindingTrace,
element: PsiElement,
languageVersionSettings: LanguageVersionSettings
) {
targetDescriptor.checkSinceKotlinVersionAccessibility(languageVersionSettings) { version ->
trace.report(Errors.API_NOT_AVAILABLE.on(element, version.versionString, languageVersionSettings.apiVersion.versionString))
}
}
}
@@ -196,5 +196,8 @@ fun DeclarationDescriptor.isHiddenInResolution(languageVersionSettings: Language
if (isHiddenToOvercomeSignatureClash) return true
if (isHiddenForResolutionEverywhereBesideSupercalls && !isSuperCall) return true
}
if (!checkSinceKotlinVersionAccessibility(languageVersionSettings)) return true
return isDeprecatedHidden()
}
@@ -16,11 +16,59 @@
package org.jetbrains.kotlin.resolve
import org.jetbrains.kotlin.config.ApiVersion
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.PropertyAccessorDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.name.FqName
private val SINCE_KOTLIN_FQ_NAME = FqName("kotlin.SinceKotlin")
// TODO: use-site targeted annotations
internal fun DeclarationDescriptor.getSinceKotlinAnnotation(): AnnotationDescriptor? =
annotations.findAnnotation(SINCE_KOTLIN_FQ_NAME)
/**
* @return true if the descriptor is accessible according to [languageVersionSettings], or false otherwise. The [actionIfInaccessible]
* callback is called with the version specified in the [SinceKotlin] annotation if the descriptor is inaccessible.
*/
internal fun DeclarationDescriptor.checkSinceKotlinVersionAccessibility(
languageVersionSettings: LanguageVersionSettings,
actionIfInaccessible: ((ApiVersion) -> Unit)? = null
): Boolean {
val version =
if (this is CallableMemberDescriptor && !kind.isReal) getSinceKotlinVersionByOverridden(this)
else getOwnSinceKotlinVersion()
// Allow access in the following cases:
// 1) There's no @SinceKotlin annotation for this descriptor
// 2) There's a @SinceKotlin annotation but its value is some unrecognizable nonsense
// 3) The value as a version is not greater than our API version
if (version == null || version <= languageVersionSettings.apiVersion) return true
actionIfInaccessible?.invoke(version)
return false
}
/**
* @return null if there are no overridden members or if there's at least one declaration in the hierarchy not annotated with [SinceKotlin],
* or the minimal value of the version from all declarations annotated with [SinceKotlin] otherwise.
*/
private fun getSinceKotlinVersionByOverridden(descriptor: CallableMemberDescriptor): ApiVersion? {
return DescriptorUtils.getAllOverriddenDeclarations(descriptor).map { it.getOwnSinceKotlinVersion() ?: return null }.min()
}
private fun DeclarationDescriptor.getOwnSinceKotlinVersion(): ApiVersion? {
fun DeclarationDescriptor.loadAnnotationValue(): ApiVersion? =
(getSinceKotlinAnnotation()?.allValueArguments?.values?.singleOrNull()?.value as? String)?.let(ApiVersion.Companion::parse)
val ownVersion = loadAnnotationValue()
val ctorClass = (this as? ConstructorDescriptor)?.containingDeclaration?.loadAnnotationValue()
val property = (this as? PropertyAccessorDescriptor)?.correspondingProperty?.loadAnnotationValue()
return listOfNotNull(ownVersion, ctorClass, property).max()
}
+1
View File
@@ -13,6 +13,7 @@ where possible options include:
-output-prefix <path> Path to file which will be added to the beginning of output file
-output-postfix <path> Path to file which will be added to the end of output file
-language-version <version> Provide source compatibility with specified language version
-api-version <version> Allow to use declarations only from the specified version of bundled libraries
-nowarn Generate no warnings
-verbose Enable verbose logging output
-version Display compiler version
+5
View File
@@ -0,0 +1,5 @@
$TESTDATA_DIR$/apiVersion.kt
-d
$TEMP_DIR$
-api-version
1.0
+3
View File
@@ -0,0 +1,3 @@
fun test() {
""::class.isInstance(42)
}
+4
View File
@@ -0,0 +1,4 @@
compiler/testData/cli/jvm/apiVersion.kt:2:15: error: unresolved reference: isInstance
""::class.isInstance(42)
^
COMPILATION_ERROR
@@ -0,0 +1,7 @@
$TESTDATA_DIR$/apiVersion.kt
-d
$TEMP_DIR$
-api-version
1.1
-language-version
1.0
@@ -0,0 +1,2 @@
error: -api-version (1.1) cannot be greater than -language-version (1.0)
COMPILATION_ERROR
+5
View File
@@ -0,0 +1,5 @@
$TESTDATA_DIR$/simple.kt
-d
$TEMP_DIR$
-api-version
239.42
+3
View File
@@ -0,0 +1,3 @@
error: unknown API version: 239.42
Supported API versions: 1.0, 1.1
COMPILATION_ERROR
@@ -0,0 +1,7 @@
$TESTDATA_DIR$/apiVersion.kt
-d
$TEMP_DIR$
-api-version
1.0
-language-version
1.1
@@ -0,0 +1,4 @@
compiler/testData/cli/jvm/apiVersion.kt:2:15: error: unresolved reference: isInstance
""::class.isInstance(42)
^
COMPILATION_ERROR
+1
View File
@@ -15,6 +15,7 @@ where possible options include:
-module-name Module name
-jvm-target <version> Target version of the generated JVM bytecode (1.6 or 1.8), default is 1.6
-language-version <version> Provide source compatibility with specified language version
-api-version <version> Allow to use declarations only from the specified version of bundled libraries
-nowarn Generate no warnings
-verbose Enable verbose logging output
-version Display compiler version
+4
View File
@@ -3,3 +3,7 @@ package test
sealed class Base
class Derived : Base()
fun test() {
""::class.isInstance(42)
}
+6
View File
@@ -1,4 +1,10 @@
compiler/testData/cli/jvm/languageVersion.kt:5:17: error: this type is sealed, so it can be inherited by only its own nested classes or objects
class Derived : Base()
^
compiler/testData/cli/jvm/languageVersion.kt:8:5: error: the feature is only available since Kotlin 1.1: bound callable references
""::class.isInstance(42)
^
compiler/testData/cli/jvm/languageVersion.kt:8:15: error: unresolved reference: isInstance
""::class.isInstance(42)
^
COMPILATION_ERROR
+1
View File
@@ -16,6 +16,7 @@ where possible options include:
-module-name Module name
-jvm-target <version> Target version of the generated JVM bytecode (1.6 or 1.8), default is 1.6
-language-version <version> Provide source compatibility with specified language version
-api-version <version> Allow to use declarations only from the specified version of bundled libraries
-nowarn Generate no warnings
-verbose Enable verbose logging output
-version Display compiler version
+8
View File
@@ -83,3 +83,11 @@ This directive lets you enable or disable certain language features. Language fe
// !LANGUAGE: -TopLevelSealedInheritance
// !LANGUAGE: +TypeAliases -LocalDelegatedProperties
### 5. API_VERSION
This directive emulates the behavior of the `-api-version` command line option, disallowing to use declarations annotated with `@SinceKotlin(X)` where X is greater than the specified API version.
#### Usage:
// !API_VERSION: 1.0
@@ -0,0 +1,34 @@
// !API_VERSION: 1.0
// MODULE: m1
// FILE: a.kt
package p1
@SinceKotlin("1.1")
fun foo(s: Int): String = s.toString()
// MODULE: m2
// FILE: b.kt
package p2
fun foo(s: Int): Int = s
// MODULE: m3(m1, m2)
// FILE: severalStarImports.kt
import p1.*
import p2.*
fun test1(): Int {
val r = foo(42)
return r
}
// FILE: explicitlyImportP1.kt
import p1.foo // TODO: consider reporting API_NOT_AVAILABLE here
import p2.*
fun test2(): Int {
val r = foo(42)
return r
}
@@ -0,0 +1,29 @@
// -- Module: <m1> --
package
package p1 {
@kotlin.SinceKotlin(version = "1.1") public fun foo(/*0*/ s: kotlin.Int): kotlin.String
}
// -- Module: <m2> --
package
package p2 {
public fun foo(/*0*/ s: kotlin.Int): kotlin.Int
}
// -- Module: <m3> --
package
public fun test1(): kotlin.Int
public fun test2(): kotlin.Int
package p1 {
@kotlin.SinceKotlin(version = "1.1") public fun foo(/*0*/ s: kotlin.Int): kotlin.String
}
package p2 {
public fun foo(/*0*/ s: kotlin.Int): kotlin.Int
}
@@ -0,0 +1,38 @@
// !API_VERSION: 1.0
// MODULE: m1
// FILE: a.kt
package p1
@SinceKotlin("1.1")
class A {
fun m1() {}
}
// MODULE: m2
// FILE: b.kt
package p2
class A {
fun m2() {}
}
// MODULE: m3(m1, m2)
// FILE: severalStarImports.kt
import p1.*
import p2.*
fun test(a: A) {
a.<!UNRESOLVED_REFERENCE!>m1<!>()
a.m2()
}
// FILE: explicitlyImportP1.kt
import p1.<!API_NOT_AVAILABLE!>A<!>
import p2.*
fun test(a: <!API_NOT_AVAILABLE!>A<!>) {
a.m1()
a.<!UNRESOLVED_REFERENCE!>m2<!>()
}
@@ -0,0 +1,49 @@
// -- Module: <m1> --
package
package p1 {
@kotlin.SinceKotlin(version = "1.1") public final class A {
public constructor A()
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 final fun m1(): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
}
// -- Module: <m2> --
package
package p2 {
public final class A {
public constructor A()
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 final fun m2(): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
}
// -- Module: <m3> --
package
public fun test(/*0*/ a: p1.A): kotlin.Unit
public fun test(/*0*/ a: p2.A): kotlin.Unit
package p1 {
@kotlin.SinceKotlin(version = "1.1") public final class A {
// -- Module: <m1> --
}
}
package p2 {
public final class A {
// -- Module: <m2> --
}
}
@@ -0,0 +1,47 @@
// !API_VERSION: 1.0
// MODULE: m1
// FILE: a.kt
package p1
@SinceKotlin("1.1")
class A(val v1: Unit)
// MODULE: m2
// FILE: b.kt
package p2
@SinceKotlin("1.1")
class A(val v2: Unit)
// MODULE: m3
// FILE: c.kt
package p3
@SinceKotlin("1.1")
class A(val v3: Unit)
// MODULE: m4(m1, m2, m3)
// FILE: oneExplicitImportOtherStars.kt
import p1.*
import p2.<!API_NOT_AVAILABLE!>A<!>
import p3.*
fun test(a: <!API_NOT_AVAILABLE!>A<!>) {
a.<!UNRESOLVED_REFERENCE!>v1<!>
a.v2
a.<!UNRESOLVED_REFERENCE!>v3<!>
}
// FILE: severalStarImports.kt
import p1.*
import p2.*
import p3.*
fun test(a: <!UNRESOLVED_REFERENCE!>A<!>) {
<!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>a<!>.<!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>v1<!>
<!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>a<!>.<!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>v2<!>
<!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>a<!>.<!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>v3<!>
}
@@ -0,0 +1,71 @@
// -- Module: <m1> --
package
package p1 {
@kotlin.SinceKotlin(version = "1.1") public final class A {
public constructor A(/*0*/ v1: kotlin.Unit)
public final val v1: kotlin.Unit
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
}
}
// -- Module: <m2> --
package
package p2 {
@kotlin.SinceKotlin(version = "1.1") public final class A {
public constructor A(/*0*/ v2: kotlin.Unit)
public final val v2: kotlin.Unit
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
}
}
// -- Module: <m3> --
package
package p3 {
@kotlin.SinceKotlin(version = "1.1") public final class A {
public constructor A(/*0*/ v3: kotlin.Unit)
public final val v3: kotlin.Unit
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
}
}
// -- Module: <m4> --
package
public fun test(/*0*/ a: [ERROR : A]): kotlin.Unit
public fun test(/*0*/ a: p2.A): kotlin.Unit
package p1 {
@kotlin.SinceKotlin(version = "1.1") public final class A {
// -- Module: <m1> --
}
}
package p2 {
@kotlin.SinceKotlin(version = "1.1") public final class A {
// -- Module: <m2> --
}
}
package p3 {
@kotlin.SinceKotlin(version = "1.1") public final class A {
// -- Module: <m3> --
}
}
@@ -0,0 +1,11 @@
// !API_VERSION: 1.0
@SinceKotlin("1.1")
annotation class Anno1(val s: String)
annotation class Anno2 @SinceKotlin("1.1") constructor()
@<!UNRESOLVED_REFERENCE, API_NOT_AVAILABLE, DEBUG_INFO_UNRESOLVED_WITH_TARGET!>Anno1<!>("")
@<!UNRESOLVED_REFERENCE, DEBUG_INFO_UNRESOLVED_WITH_TARGET!>Anno2<!>
fun t1() {}
@@ -0,0 +1,18 @@
package
@Anno1() @Anno2() public fun t1(): kotlin.Unit
@kotlin.SinceKotlin(version = "1.1") public final annotation class Anno1 : kotlin.Annotation {
public constructor Anno1(/*0*/ s: kotlin.String)
public final val s: 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
}
public final annotation class Anno2 : kotlin.Annotation {
@kotlin.SinceKotlin(version = "1.1") public constructor Anno2()
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,19 @@
// !API_VERSION: 1.0
@SinceKotlin("1.1")
open class Foo
class Bar @SinceKotlin("1.1") constructor()
@SinceKotlin("1.0")
class Baz @SinceKotlin("1.1") constructor()
fun t1(): <!API_NOT_AVAILABLE!>Foo<!> = <!UNRESOLVED_REFERENCE!>Foo<!>()
// TODO: do not report API_NOT_AVAILABLE twice
fun t2() = object : <!UNRESOLVED_REFERENCE, API_NOT_AVAILABLE, API_NOT_AVAILABLE, DEBUG_INFO_UNRESOLVED_WITH_TARGET!>Foo<!>() {}
fun t3(): Bar? = <!UNRESOLVED_REFERENCE!>Bar<!>()
fun t4(): Baz = <!UNRESOLVED_REFERENCE!>Baz<!>()
@@ -0,0 +1,27 @@
package
public fun t1(): Foo
public fun t2(): Foo
public fun t3(): Bar?
public fun t4(): Baz
public final class Bar {
@kotlin.SinceKotlin(version = "1.1") public constructor Bar()
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.SinceKotlin(version = "1.0") public final class Baz {
@kotlin.SinceKotlin(version = "1.1") public constructor Baz()
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.SinceKotlin(version = "1.1") public open class Foo {
public constructor Foo()
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,34 @@
// !API_VERSION: 1.0
// FILE: J.java
public interface J {
void foo();
}
// FILE: test.kt
interface I10 {
@SinceKotlin("1.0")
fun foo()
}
interface I11 {
@SinceKotlin("1.1")
fun foo()
}
fun f1(x: I10) = x.foo()
fun f2(x: I11) = x.<!UNRESOLVED_REFERENCE!>foo<!>()
fun f3(x: J) = x.foo()
interface BothI1 : I10, I11
fun f4(x: BothI1) = x.foo()
interface BothI2 : I11, I10
fun f5(x: BothI2) = x.foo()
interface JAndI10 : J, I10
fun f6(x: JAndI10) = x.foo()
interface JAndI11 : J, I11
fun f7(x: JAndI11) = x.foo()
@@ -0,0 +1,59 @@
package
public /*synthesized*/ fun J(/*0*/ function: () -> kotlin.Unit): J
public fun f1(/*0*/ x: I10): kotlin.Unit
public fun f2(/*0*/ x: I11): [ERROR : Error function type]
public fun f3(/*0*/ x: J): kotlin.Unit
public fun f4(/*0*/ x: BothI1): kotlin.Unit
public fun f5(/*0*/ x: BothI2): kotlin.Unit
public fun f6(/*0*/ x: JAndI10): kotlin.Unit
public fun f7(/*0*/ x: JAndI11): kotlin.Unit
public interface BothI1 : I10, I11 {
public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
@kotlin.SinceKotlin(version = "1.0") public abstract override /*2*/ /*fake_override*/ fun foo(): kotlin.Unit
public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String
}
public interface BothI2 : I11, I10 {
public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
@kotlin.SinceKotlin(version = "1.1") public abstract override /*2*/ /*fake_override*/ fun foo(): kotlin.Unit
public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String
}
public interface I10 {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
@kotlin.SinceKotlin(version = "1.0") public abstract fun foo(): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public interface I11 {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
@kotlin.SinceKotlin(version = "1.1") public abstract fun foo(): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public interface J {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public abstract fun foo(): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public interface JAndI10 : J, I10 {
public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public abstract override /*2*/ /*fake_override*/ fun foo(): kotlin.Unit
public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String
}
public interface JAndI11 : J, I11 {
public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public abstract override /*2*/ /*fake_override*/ fun foo(): kotlin.Unit
public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -0,0 +1,48 @@
// !API_VERSION: 1.0
val v1: String
@SinceKotlin("1.1")
get() = ""
@SinceKotlin("1.1")
val v2 = ""
var v3: String
@SinceKotlin("1.1")
get() = ""
set(value) {}
var v4: String
get() = ""
@SinceKotlin("1.1")
set(value) {}
var v5: String
@SinceKotlin("1.1")
get() = ""
@SinceKotlin("1.1")
set(value) {}
@SinceKotlin("1.1")
var v6: String
get() = ""
set(value) {}
@SinceKotlin("1.0")
val v7: String
@SinceKotlin("1.1")
get() = ""
fun test() {
<!API_NOT_AVAILABLE!>v1<!>
<!UNRESOLVED_REFERENCE!>v2<!>
<!API_NOT_AVAILABLE!>v3<!>
v3 = ""
v4
<!API_NOT_AVAILABLE!>v4<!> = ""
<!API_NOT_AVAILABLE!>v5<!>
<!API_NOT_AVAILABLE!>v5<!> = ""
<!UNRESOLVED_REFERENCE!>v6<!>
<!UNRESOLVED_REFERENCE!>v6<!> = ""
<!API_NOT_AVAILABLE!>v7<!>
}
@@ -0,0 +1,10 @@
package
public val v1: kotlin.String
@kotlin.SinceKotlin(version = "1.1") public val v2: kotlin.String = ""
public var v3: kotlin.String
public var v4: kotlin.String
public var v5: kotlin.String
@kotlin.SinceKotlin(version = "1.1") public var v6: kotlin.String
@kotlin.SinceKotlin(version = "1.0") public val v7: kotlin.String
public fun test(): kotlin.Unit
@@ -0,0 +1,19 @@
// !API_VERSION: 1.0
@SinceKotlin("1.1")
fun f() {}
@SinceKotlin("1.1")
var p = Unit
@SinceKotlin("1.1.2")
fun z() {}
fun t1() = <!UNRESOLVED_REFERENCE!>f<!>()
fun t2() = <!UNRESOLVED_REFERENCE!>p<!>
fun t3() { <!UNRESOLVED_REFERENCE!>p<!> = Unit }
fun t4() { <!UNRESOLVED_REFERENCE!>z<!>() }
@@ -0,0 +1,9 @@
package
@kotlin.SinceKotlin(version = "1.1") public var p: kotlin.Unit
@kotlin.SinceKotlin(version = "1.1") public fun f(): kotlin.Unit
public fun t1(): [ERROR : Error function type]
public fun t2(): [ERROR : Error function type]
public fun t3(): kotlin.Unit
public fun t4(): kotlin.Unit
@kotlin.SinceKotlin(version = "1.1.2") public fun z(): kotlin.Unit
@@ -0,0 +1,18 @@
// !API_VERSION: 1.1
@SinceKotlin("0.9")
fun ok1() {}
@SinceKotlin("1.0")
fun ok2() {}
@SinceKotlin("1.1")
fun ok3() {}
@SinceKotlin("0.9.9")
fun ok4() {}
fun t1() = ok1()
fun t2() = ok2()
fun t3() = ok3()
fun t4() = ok4()
@@ -0,0 +1,10 @@
package
@kotlin.SinceKotlin(version = "0.9") public fun ok1(): kotlin.Unit
@kotlin.SinceKotlin(version = "1.0") public fun ok2(): kotlin.Unit
@kotlin.SinceKotlin(version = "1.1") public fun ok3(): kotlin.Unit
@kotlin.SinceKotlin(version = "0.9.9") public fun ok4(): kotlin.Unit
public fun t1(): kotlin.Unit
public fun t2(): kotlin.Unit
public fun t3(): kotlin.Unit
public fun t4(): kotlin.Unit
@@ -143,7 +143,7 @@ public abstract class AbstractDiagnosticsTest extends BaseDiagnosticsTest {
moduleBindings.put(testModule, moduleTrace.getBindingContext());
LanguageVersionSettings languageVersionSettings = loadCustomLanguageVersionSettings(testFilesInModule);
LanguageVersionSettings languageVersionSettings = loadLanguageVersionSettings(testFilesInModule);
ModuleContext moduleContext = ContextKt.withModule(ContextKt.withProject(context, getProject()), module);
boolean separateModules = groupedByModule.size() == 1;
@@ -210,17 +210,19 @@ public abstract class AbstractDiagnosticsTest extends BaseDiagnosticsTest {
}
@Nullable
private LanguageVersionSettings loadCustomLanguageVersionSettings(List<? extends TestFile> module) {
private LanguageVersionSettings loadLanguageVersionSettings(List<? extends TestFile> module) {
LanguageVersionSettings result = null;
for (TestFile file : module) {
if (file.customLanguageVersionSettings != null) {
if (result != null) {
LanguageVersionSettings current = file.customLanguageVersionSettings;
if (current != null) {
if (result != null && !result.equals(current)) {
Assert.fail(
"More than one file in the module has " + BaseDiagnosticsTest.LANGUAGE_DIRECTIVE + " directive specified. " +
"More than one file in the module has " + BaseDiagnosticsTest.LANGUAGE_DIRECTIVE + " or " +
BaseDiagnosticsTest.API_VERSION_DIRECTIVE + " directive specified. " +
"This is not supported. Please move all directives into one file"
);
}
result = file.customLanguageVersionSettings;
result = current;
}
}
@@ -34,6 +34,7 @@ import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.asJava.DuplicateJvmSignatureUtilKt;
import org.jetbrains.kotlin.config.ApiVersion;
import org.jetbrains.kotlin.config.LanguageFeature;
import org.jetbrains.kotlin.config.LanguageVersionSettings;
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl;
@@ -71,6 +72,8 @@ public abstract class BaseDiagnosticsTest
public static final String LANGUAGE_DIRECTIVE = "LANGUAGE";
private static final Pattern LANGUAGE_PATTERN = Pattern.compile("([\\+\\-])(\\w+)\\s*");
public static final String API_VERSION_DIRECTIVE = "API_VERSION";
public static final String CHECK_TYPE_DIRECTIVE = "CHECK_TYPE";
public static final String CHECK_TYPE_PACKAGE = "tests._checkType";
private static final String CHECK_TYPE_DECLARATIONS = "\npackage " + CHECK_TYPE_PACKAGE +
@@ -155,14 +158,26 @@ public abstract class BaseDiagnosticsTest
}
@Nullable
private static LanguageVersionSettings parseLanguageDirective(Map<String, String> directiveMap) {
private static LanguageVersionSettings parseLanguageVersionSettings(Map<String, String> directiveMap) {
String apiVersionString = directiveMap.get(API_VERSION_DIRECTIVE);
String directives = directiveMap.get(LANGUAGE_DIRECTIVE);
if (directives == null) return null;
if (apiVersionString == null && directives == null) return null;
ApiVersion apiVersion = apiVersionString != null ? ApiVersion.Companion.parse(apiVersionString) : ApiVersion.LATEST;
assert apiVersion != null : "Unknown API version: " + apiVersionString;
Map<LanguageFeature, Boolean> languageFeatures =
directives == null ? Collections.<LanguageFeature, Boolean>emptyMap() : collectLanguageFeatureMap(directives);
return new DiagnosticTestLanguageVersionSettings(languageFeatures, apiVersion);
}
@NotNull
private static Map<LanguageFeature, Boolean> collectLanguageFeatureMap(@NotNull String directives) {
Matcher matcher = LANGUAGE_PATTERN.matcher(directives);
if (!matcher.find()) {
Assert.fail(
"Wrong syntax in the '// !LANGUAGE: ...' directive:\n" +
"Wrong syntax in the '// !" + LANGUAGE_DIRECTIVE + ": ...' directive:\n" +
"found: '" + directives + "'\n" +
"Must be '([+-]LanguageFeatureName)+'\n" +
"where '+' means 'enable' and '-' means 'disable'\n" +
@@ -170,7 +185,7 @@ public abstract class BaseDiagnosticsTest
);
}
final Map<LanguageFeature, Boolean> values = new HashMap<LanguageFeature, Boolean>();
Map<LanguageFeature, Boolean> values = new HashMap<LanguageFeature, Boolean>();
do {
boolean enable = matcher.group(1).equals("+");
String name = matcher.group(2);
@@ -187,16 +202,7 @@ public abstract class BaseDiagnosticsTest
}
while (matcher.find());
return new LanguageVersionSettings() {
@Override
public boolean supportsFeature(@NotNull LanguageFeature feature) {
Boolean enabled = values.get(feature);
if (enabled != null) {
return enabled;
}
return LanguageVersionSettingsImpl.DEFAULT.supportsFeature(feature);
}
};
return values;
}
public static Condition<Diagnostic> parseDiagnosticFilterDirective(Map<String, String> directiveMap) {
@@ -207,7 +213,7 @@ public abstract class BaseDiagnosticsTest
Condition<Diagnostic> condition = Conditions.alwaysTrue();
Matcher matcher = DIAGNOSTICS_PATTERN.matcher(directives);
if (!matcher.find()) {
Assert.fail("Wrong syntax in the '// !DIAGNOSTICS: ...' directive:\n" +
Assert.fail("Wrong syntax in the '// !" + DIAGNOSTICS_DIRECTIVE + ": ...' directive:\n" +
"found: '" + directives + "'\n" +
"Must be '([+-!]DIAGNOSTIC_FACTORY_NAME|ERROR|WARNING|INFO)+'\n" +
"where '+' means 'include'\n" +
@@ -289,6 +295,37 @@ public abstract class BaseDiagnosticsTest
}
}
public static class DiagnosticTestLanguageVersionSettings implements LanguageVersionSettings {
private final Map<LanguageFeature, Boolean> languageFeatures;
private final ApiVersion apiVersion;
public DiagnosticTestLanguageVersionSettings(
@NotNull Map<LanguageFeature, Boolean> languageFeatures, @NotNull ApiVersion apiVersion
) {
this.languageFeatures = languageFeatures;
this.apiVersion = apiVersion;
}
@Override
public boolean supportsFeature(@NotNull LanguageFeature feature) {
Boolean enabled = languageFeatures.get(feature);
return enabled != null ? enabled : LanguageVersionSettingsImpl.DEFAULT.supportsFeature(feature);
}
@NotNull
@Override
public ApiVersion getApiVersion() {
return apiVersion;
}
@Override
public boolean equals(Object obj) {
return obj instanceof DiagnosticTestLanguageVersionSettings &&
((DiagnosticTestLanguageVersionSettings) obj).languageFeatures.equals(languageFeatures) &&
((DiagnosticTestLanguageVersionSettings) obj).apiVersion.equals(apiVersion);
}
}
protected class TestFile {
private final List<CheckerTestUtil.DiagnosedRange> diagnosedRanges = Lists.newArrayList();
public final String expectedText;
@@ -311,7 +348,7 @@ public abstract class BaseDiagnosticsTest
) {
this.module = module;
this.whatDiagnosticsToConsider = parseDiagnosticFilterDirective(directives);
this.customLanguageVersionSettings = parseLanguageDirective(directives);
this.customLanguageVersionSettings = parseLanguageVersionSettings(directives);
this.checkLazyLog = directives.containsKey(CHECK_LAZY_LOG_DIRECTIVE) || CHECK_LAZY_LOG_DEFAULT;
this.declareCheckType = directives.containsKey(CHECK_TYPE_DIRECTIVE);
this.declareFlexibleType = directives.containsKey(EXPLICIT_FLEXIBLE_TYPES_DIRECTIVE);
@@ -12383,6 +12383,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
doTest(fileName);
}
@TestMetadata("sinceKotlin.kt")
public void testSinceKotlin() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/multimodule/duplicateMethod/sinceKotlin.kt");
doTest(fileName);
}
@TestMetadata("substitutedGenericInParams.kt")
public void testSubstitutedGenericInParams() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/multimodule/duplicateMethod/substitutedGenericInParams.kt");
@@ -12442,6 +12448,18 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/multimodule/hiddenClass/deprecatedHiddenMultipleClasses.kt");
doTest(fileName);
}
@TestMetadata("sinceKotlinImportPriority.kt")
public void testSinceKotlinImportPriority() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/multimodule/hiddenClass/sinceKotlinImportPriority.kt");
doTest(fileName);
}
@TestMetadata("sinceKotlinMultipleClasses.kt")
public void testSinceKotlinMultipleClasses() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/multimodule/hiddenClass/sinceKotlinMultipleClasses.kt");
doTest(fileName);
}
}
}
@@ -19488,6 +19506,51 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
doTest(fileName);
}
@TestMetadata("compiler/testData/diagnostics/tests/sourceCompatibility/apiVersion")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class ApiVersion extends AbstractDiagnosticsTest {
public void testAllFilesPresentInApiVersion() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/sourceCompatibility/apiVersion"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("annotations.kt")
public void testAnnotations() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/sourceCompatibility/apiVersion/annotations.kt");
doTest(fileName);
}
@TestMetadata("classesAndConstructors.kt")
public void testClassesAndConstructors() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/sourceCompatibility/apiVersion/classesAndConstructors.kt");
doTest(fileName);
}
@TestMetadata("overriddenMembers.kt")
public void testOverriddenMembers() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/sourceCompatibility/apiVersion/overriddenMembers.kt");
doTest(fileName);
}
@TestMetadata("propertyAccessors.kt")
public void testPropertyAccessors() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/sourceCompatibility/apiVersion/propertyAccessors.kt");
doTest(fileName);
}
@TestMetadata("simpleMembers.kt")
public void testSimpleMembers() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/sourceCompatibility/apiVersion/simpleMembers.kt");
doTest(fileName);
}
@TestMetadata("sinceOldVersionIsOK.kt")
public void testSinceOldVersionIsOK() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/sourceCompatibility/apiVersion/sinceOldVersionIsOK.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/diagnostics/tests/sourceCompatibility/noBoundCallableReferences")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -37,6 +37,30 @@ public class CliTestGenerated extends AbstractCliTest {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/cli/jvm"), Pattern.compile("^(.+)\\.args$"), false);
}
@TestMetadata("apiVersion.args")
public void testApiVersion() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cli/jvm/apiVersion.args");
doJvmTest(fileName);
}
@TestMetadata("apiVersionGreaterThanLanguage.args")
public void testApiVersionGreaterThanLanguage() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cli/jvm/apiVersionGreaterThanLanguage.args");
doJvmTest(fileName);
}
@TestMetadata("apiVersionInvalid.args")
public void testApiVersionInvalid() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cli/jvm/apiVersionInvalid.args");
doJvmTest(fileName);
}
@TestMetadata("apiVersionLessThanLanguage.args")
public void testApiVersionLessThanLanguage() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cli/jvm/apiVersionLessThanLanguage.args");
doJvmTest(fileName);
}
@TestMetadata("classAndFileClassClash.args")
public void testClassAndFileClassClash() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cli/jvm/classAndFileClassClash.args");
@@ -40,8 +40,9 @@ class CodeConformanceTest : TestCase() {
"libraries/tools/kotlin-gradle-plugin-core/gradle_api_jar/build/tmp",
"libraries/tools/kotlin-maven-plugin/target",
"compiler/testData/psi/kdoc",
"compiler/tests/org/jetbrains/kotlin/code/CodeConformanceTest.kt"
).map { File(it) }
"compiler/tests/org/jetbrains/kotlin/code/CodeConformanceTest.kt",
"compiler/util/src/org/jetbrains/kotlin/config/MavenComparableVersion.java"
).map(::File)
}
fun testParserCode() {
@@ -0,0 +1,48 @@
/*
* 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.config
class ApiVersion private constructor(
private val version: MavenComparableVersion,
val versionString: String
) : Comparable<ApiVersion> {
override fun compareTo(other: ApiVersion): Int =
version.compareTo(other.version)
override fun equals(other: Any?) =
(other as? ApiVersion)?.version == version
override fun hashCode() =
version.hashCode()
override fun toString() = versionString
companion object {
@JvmField
val LATEST: ApiVersion = createByLanguageVersion(LanguageVersion.Companion.LATEST)
@JvmStatic
fun createByLanguageVersion(version: LanguageVersion): ApiVersion = parse(version.versionString)!!
fun parse(versionString: String): ApiVersion? = try {
ApiVersion(MavenComparableVersion(versionString), versionString)
}
catch (e: Exception) {
null
}
}
}
@@ -45,6 +45,8 @@ enum class LanguageVersion(val versionString: String) {
KOTLIN_1_0("1.0"),
KOTLIN_1_1("1.1");
override fun toString() = versionString
companion object {
@JvmStatic
fun fromVersionString(str: String) = values().find { it.versionString == str }
@@ -56,15 +58,22 @@ enum class LanguageVersion(val versionString: String) {
interface LanguageVersionSettings {
fun supportsFeature(feature: LanguageFeature): Boolean
val apiVersion: ApiVersion
}
class LanguageVersionSettingsImpl(private val languageVersion: LanguageVersion) : LanguageVersionSettings {
class LanguageVersionSettingsImpl(
private val languageVersion: LanguageVersion,
override val apiVersion: ApiVersion
) : LanguageVersionSettings {
override fun supportsFeature(feature: LanguageFeature): Boolean {
return languageVersion.ordinal >= feature.sinceVersion.ordinal
return languageVersion >= feature.sinceVersion
}
override fun toString() = "Language = $languageVersion, API = $apiVersion"
companion object {
@JvmField
val DEFAULT = LanguageVersionSettingsImpl(LanguageVersion.LATEST)
val DEFAULT = LanguageVersionSettingsImpl(LanguageVersion.LATEST, ApiVersion.LATEST)
}
}
@@ -0,0 +1,504 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.config;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import java.util.Stack;
/**
* Generic implementation of version comparison.
*
* <p>Features:
* <ul>
* <li>mixing of '<code>-</code>' (dash) and '<code>.</code>' (dot) separators,</li>
* <li>transition between characters and digits also constitutes a separator:
* <code>1.0alpha1 =&gt; [1, 0, alpha, 1]</code></li>
* <li>unlimited number of version components,</li>
* <li>version components in the text can be digits or strings,</li>
* <li>strings are checked for well-known qualifiers and the qualifier ordering is used for version ordering.
* Well-known qualifiers (case insensitive) are:<ul>
* <li><code>alpha</code> or <code>a</code></li>
* <li><code>beta</code> or <code>b</code></li>
* <li><code>milestone</code> or <code>m</code></li>
* <li><code>rc</code> or <code>cr</code></li>
* <li><code>snapshot</code></li>
* <li><code>(the empty string)</code> or <code>ga</code> or <code>final</code></li>
* <li><code>sp</code></li>
* </ul>
* Unknown qualifiers are considered after known qualifiers, with lexical order (always case insensitive),
* </li>
* <li>a dash usually precedes a qualifier, and is always less important than something preceded with a dot.</li>
* </ul></p>
*
* @see <a href="https://cwiki.apache.org/confluence/display/MAVENOLD/Versioning">"Versioning" on Maven Wiki</a>
* @author <a href="mailto:kenney@apache.org">Kenney Westerhof</a>
* @author <a href="mailto:hboutemy@apache.org">Hervé Boutemy</a>
*/
@SuppressWarnings("ALL")
public class MavenComparableVersion
implements Comparable<MavenComparableVersion>
{
private String value;
private String canonical;
private ListItem items;
private interface Item
{
int INTEGER_ITEM = 0;
int STRING_ITEM = 1;
int LIST_ITEM = 2;
int compareTo( Item item );
int getType();
boolean isNull();
}
/**
* Represents a numeric item in the version item list.
*/
private static class IntegerItem
implements Item
{
private static final BigInteger BIG_INTEGER_ZERO = new BigInteger( "0" );
private final BigInteger value;
public static final IntegerItem ZERO = new IntegerItem();
private IntegerItem()
{
this.value = BIG_INTEGER_ZERO;
}
public IntegerItem( String str )
{
this.value = new BigInteger( str );
}
public int getType()
{
return INTEGER_ITEM;
}
public boolean isNull()
{
return BIG_INTEGER_ZERO.equals( value );
}
public int compareTo( Item item )
{
if ( item == null )
{
return BIG_INTEGER_ZERO.equals( value ) ? 0 : 1; // 1.0 == 1, 1.1 > 1
}
switch ( item.getType() )
{
case INTEGER_ITEM:
return value.compareTo( ( (IntegerItem) item ).value );
case STRING_ITEM:
return 1; // 1.1 > 1-sp
case LIST_ITEM:
return 1; // 1.1 > 1-1
default:
throw new RuntimeException( "invalid item: " + item.getClass() );
}
}
public String toString()
{
return value.toString();
}
}
/**
* Represents a string in the version item list, usually a qualifier.
*/
private static class StringItem
implements Item
{
private static final String[] QUALIFIERS = { "alpha", "beta", "milestone", "rc", "snapshot", "", "sp" };
@SuppressWarnings( "checkstyle:constantname" )
private static final List<String> _QUALIFIERS = Arrays.asList( QUALIFIERS );
private static final Properties ALIASES = new Properties();
static
{
ALIASES.put( "ga", "" );
ALIASES.put( "final", "" );
ALIASES.put( "cr", "rc" );
}
/**
* A comparable value for the empty-string qualifier. This one is used to determine if a given qualifier makes
* the version older than one without a qualifier, or more recent.
*/
private static final String RELEASE_VERSION_INDEX = String.valueOf( _QUALIFIERS.indexOf( "" ) );
private String value;
public StringItem( String value, boolean followedByDigit )
{
if ( followedByDigit && value.length() == 1 )
{
// a1 = alpha-1, b1 = beta-1, m1 = milestone-1
switch ( value.charAt( 0 ) )
{
case 'a':
value = "alpha";
break;
case 'b':
value = "beta";
break;
case 'm':
value = "milestone";
break;
default:
}
}
this.value = ALIASES.getProperty( value , value );
}
public int getType()
{
return STRING_ITEM;
}
public boolean isNull()
{
return ( comparableQualifier( value ).compareTo( RELEASE_VERSION_INDEX ) == 0 );
}
/**
* Returns a comparable value for a qualifier.
*
* This method takes into account the ordering of known qualifiers then unknown qualifiers with lexical
* ordering.
*
* just returning an Integer with the index here is faster, but requires a lot of if/then/else to check for -1
* or QUALIFIERS.size and then resort to lexical ordering. Most comparisons are decided by the first character,
* so this is still fast. If more characters are needed then it requires a lexical sort anyway.
*
* @param qualifier
* @return an equivalent value that can be used with lexical comparison
*/
public static String comparableQualifier( String qualifier )
{
int i = _QUALIFIERS.indexOf( qualifier );
return i == -1 ? ( _QUALIFIERS.size() + "-" + qualifier ) : String.valueOf( i );
}
public int compareTo( Item item )
{
if ( item == null )
{
// 1-rc < 1, 1-ga > 1
return comparableQualifier( value ).compareTo( RELEASE_VERSION_INDEX );
}
switch ( item.getType() )
{
case INTEGER_ITEM:
return -1; // 1.any < 1.1 ?
case STRING_ITEM:
return comparableQualifier( value ).compareTo( comparableQualifier( ( (StringItem) item ).value ) );
case LIST_ITEM:
return -1; // 1.any < 1-1
default:
throw new RuntimeException( "invalid item: " + item.getClass() );
}
}
public String toString()
{
return value;
}
}
/**
* Represents a version list item. This class is used both for the global item list and for sub-lists (which start
* with '-(number)' in the version specification).
*/
private static class ListItem
extends ArrayList<Item>
implements Item
{
public int getType()
{
return LIST_ITEM;
}
public boolean isNull()
{
return ( size() == 0 );
}
void normalize()
{
for ( int i = size() - 1; i >= 0; i-- )
{
Item lastItem = get( i );
if ( lastItem.isNull() )
{
// remove null trailing items: 0, "", empty list
remove( i );
}
else if ( !( lastItem instanceof ListItem ) )
{
break;
}
}
}
public int compareTo( Item item )
{
if ( item == null )
{
if ( size() == 0 )
{
return 0; // 1-0 = 1- (normalize) = 1
}
Item first = get( 0 );
return first.compareTo( null );
}
switch ( item.getType() )
{
case INTEGER_ITEM:
return -1; // 1-1 < 1.0.x
case STRING_ITEM:
return 1; // 1-1 > 1-sp
case LIST_ITEM:
Iterator<Item> left = iterator();
Iterator<Item> right = ( (ListItem) item ).iterator();
while ( left.hasNext() || right.hasNext() )
{
Item l = left.hasNext() ? left.next() : null;
Item r = right.hasNext() ? right.next() : null;
// if this is shorter, then invert the compare and mul with -1
int result = l == null ? ( r == null ? 0 : -1 * r.compareTo( l ) ) : l.compareTo( r );
if ( result != 0 )
{
return result;
}
}
return 0;
default:
throw new RuntimeException( "invalid item: " + item.getClass() );
}
}
public String toString()
{
StringBuilder buffer = new StringBuilder();
for ( Iterator<Item> iter = iterator(); iter.hasNext(); )
{
Item item = iter.next();
if ( buffer.length() > 0 )
{
buffer.append( ( item instanceof ListItem ) ? '-' : '.' );
}
buffer.append( item );
}
return buffer.toString();
}
}
public MavenComparableVersion( String version )
{
parseVersion( version );
}
public final void parseVersion( String version )
{
this.value = version;
items = new ListItem();
version = version.toLowerCase( Locale.ENGLISH );
ListItem list = items;
Stack<Item> stack = new Stack<Item>();
stack.push( list );
boolean isDigit = false;
int startIndex = 0;
for ( int i = 0; i < version.length(); i++ )
{
char c = version.charAt( i );
if ( c == '.' )
{
if ( i == startIndex )
{
list.add( IntegerItem.ZERO );
}
else
{
list.add( parseItem( isDigit, version.substring( startIndex, i ) ) );
}
startIndex = i + 1;
}
else if ( c == '-' )
{
if ( i == startIndex )
{
list.add( IntegerItem.ZERO );
}
else
{
list.add( parseItem( isDigit, version.substring( startIndex, i ) ) );
}
startIndex = i + 1;
list.add( list = new ListItem() );
stack.push( list );
}
else if ( Character.isDigit( c ) )
{
if ( !isDigit && i > startIndex )
{
list.add( new StringItem( version.substring( startIndex, i ), true ) );
startIndex = i;
list.add( list = new ListItem() );
stack.push( list );
}
isDigit = true;
}
else
{
if ( isDigit && i > startIndex )
{
list.add( parseItem( true, version.substring( startIndex, i ) ) );
startIndex = i;
list.add( list = new ListItem() );
stack.push( list );
}
isDigit = false;
}
}
if ( version.length() > startIndex )
{
list.add( parseItem( isDigit, version.substring( startIndex ) ) );
}
while ( !stack.isEmpty() )
{
list = (ListItem) stack.pop();
list.normalize();
}
canonical = items.toString();
}
private static Item parseItem( boolean isDigit, String buf )
{
return isDigit ? new IntegerItem( buf ) : new StringItem( buf, false );
}
public int compareTo( MavenComparableVersion o )
{
return items.compareTo( o.items );
}
public String toString()
{
return value;
}
public String getCanonical()
{
return canonical;
}
public boolean equals( Object o )
{
return ( o instanceof MavenComparableVersion) && canonical.equals(((MavenComparableVersion) o ).canonical );
}
public int hashCode()
{
return canonical.hashCode();
}
/**
* Main to test version parsing and comparison.
*
* @param args the version strings to parse and compare
*/
public static void main( String... args )
{
System.out.println( "Display parameters as parsed by Maven (in canonical form) and comparison result:" );
if ( args.length == 0 )
{
return;
}
MavenComparableVersion prev = null;
int i = 1;
for ( String version : args )
{
MavenComparableVersion c = new MavenComparableVersion(version );
if ( prev != null )
{
int compare = prev.compareTo( c );
System.out.println( " " + prev.toString() + ' '
+ ( ( compare == 0 ) ? "==" : ( ( compare < 0 ) ? "<" : ">" ) ) + ' ' + version );
}
System.out.println( String.valueOf( i++ ) + ". " + version + " == " + c.getCanonical() );
prev = c;
}
}
}