From ec6b49b8b836d8b80c0db953833ab3b345bf58c3 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Wed, 17 Jan 2018 18:51:59 +0100 Subject: [PATCH] Check experimental API markers for deprecation #KT-22759 In Progress --- .../messages/AnalyzerWithCompilerReport.kt | 8 +++--- .../checkers/ExperimentalUsageChecker.kt | 26 +++++++++++++++++-- .../kotlin/resolve/deprecationUtil.kt | 6 ++--- .../cli/jvm/experimentalDeprecated.args | 10 +++++++ .../cli/jvm/experimentalDeprecated.kt | 17 ++++++++++++ .../cli/jvm/experimentalDeprecated.out | 6 +++++ .../jvm/experimentalDeprecatedWarning.args | 8 ++++++ .../cli/jvm/experimentalDeprecatedWarning.kt | 9 +++++++ .../cli/jvm/experimentalDeprecatedWarning.out | 4 +++ .../kotlin/cli/CliTestGenerated.java | 12 +++++++++ 10 files changed, 97 insertions(+), 9 deletions(-) create mode 100644 compiler/testData/cli/jvm/experimentalDeprecated.args create mode 100644 compiler/testData/cli/jvm/experimentalDeprecated.kt create mode 100644 compiler/testData/cli/jvm/experimentalDeprecated.out create mode 100644 compiler/testData/cli/jvm/experimentalDeprecatedWarning.args create mode 100644 compiler/testData/cli/jvm/experimentalDeprecatedWarning.kt create mode 100644 compiler/testData/cli/jvm/experimentalDeprecatedWarning.out diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/common/messages/AnalyzerWithCompilerReport.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/common/messages/AnalyzerWithCompilerReport.kt index c57adfa9617..625a8e50818 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/common/messages/AnalyzerWithCompilerReport.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/common/messages/AnalyzerWithCompilerReport.kt @@ -99,9 +99,11 @@ class AnalyzerWithCompilerReport( fun analyzeAndReport(files: Collection, analyze: () -> AnalysisResult) { analysisResult = analyze() - ExperimentalUsageChecker.checkCompilerArguments(analysisResult.moduleDescriptor, languageVersionSettings) { message -> - messageCollector.report(ERROR, message) - } + ExperimentalUsageChecker.checkCompilerArguments( + analysisResult.moduleDescriptor, languageVersionSettings, + reportError = { message -> messageCollector.report(ERROR, message) }, + reportWarning = { message -> messageCollector.report(WARNING, message) } + ) reportSyntaxErrors(files) reportDiagnostics(analysisResult.bindingContext.diagnostics, messageCollector) reportIncompleteHierarchies() diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExperimentalUsageChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExperimentalUsageChecker.kt index 87fc8c8240c..8578470eb2b 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExperimentalUsageChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExperimentalUsageChecker.kt @@ -37,6 +37,7 @@ import org.jetbrains.kotlin.resolve.constants.KClassValue import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.resolve.descriptorUtil.module +import org.jetbrains.kotlin.storage.LockBasedStorageManager import org.jetbrains.kotlin.utils.SmartSet import org.jetbrains.kotlin.utils.addIfNotNull @@ -237,7 +238,17 @@ object ExperimentalUsageChecker : CallChecker { } } - fun checkCompilerArguments(module: ModuleDescriptor, languageVersionSettings: LanguageVersionSettings, reportError: (String) -> Unit) { + fun checkCompilerArguments( + module: ModuleDescriptor, + languageVersionSettings: LanguageVersionSettings, + reportError: (String) -> Unit, + reportWarning: (String) -> Unit + ) { + // Ideally, we should run full resolution (with all classifier usage checkers) on classifiers used in "-Xexperimental" and + // "-Xuse-experimental" arguments. However, it's not easy to do this. This should be solved in the future with the support of + // module annotations. For now, we only check deprecations because this is needed to correctly retire unneeded compiler arguments. + val deprecationResolver = DeprecationResolver(LockBasedStorageManager(), languageVersionSettings) + fun checkAnnotation(fqName: String, allowNonCompilationImpact: Boolean): Boolean { val descriptor = module.resolveClassByFqName(FqName(fqName), NoLookupLocation.FOR_NON_TRACKED_SCOPE) val experimentality = descriptor?.loadExperimentalityForMarkerAnnotation() @@ -250,9 +261,20 @@ object ExperimentalUsageChecker : CallChecker { !allowNonCompilationImpact && !experimentality.impact.all(Experimentality.Impact.COMPILATION::equals) -> "Experimental API marker $fqName has impact other than COMPILATION, " + "therefore it can't be used with -Xuse-experimental" - else -> return true + else -> { + for (deprecation in deprecationResolver.getDeprecations(descriptor)) { + val report = when (deprecation.deprecationLevel) { + DeprecationLevelValue.WARNING -> reportWarning + DeprecationLevelValue.ERROR, DeprecationLevelValue.HIDDEN -> reportError + } + report("Experimental API marker $fqName is deprecated" + deprecation.message?.let { ". $it" }.orEmpty()) + } + return true + } } + reportError(message) + return false } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecationUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecationUtil.kt index 06f78559160..1c9ba7ba6bd 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecationUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecationUtil.kt @@ -201,10 +201,8 @@ class DeprecationResolver( descriptor.checkSinceKotlinVersionAccessibility(languageVersionSettings) } - fun getDeprecations( - descriptor: DeclarationDescriptor - ) = deprecations(descriptor.original) - + fun getDeprecations(descriptor: DeclarationDescriptor): List = + deprecations(descriptor.original) fun isDeprecatedHidden(descriptor: DeclarationDescriptor): Boolean = getDeprecations(descriptor).any { it.deprecationLevel == HIDDEN } diff --git a/compiler/testData/cli/jvm/experimentalDeprecated.args b/compiler/testData/cli/jvm/experimentalDeprecated.args new file mode 100644 index 00000000000..31514313583 --- /dev/null +++ b/compiler/testData/cli/jvm/experimentalDeprecated.args @@ -0,0 +1,10 @@ +$TESTDATA_DIR$/experimentalDeprecated.kt +-d +$TEMP_DIR$ +-Xskip-runtime-version-check +-language-version +1.3 +-Xexperimental=org.test.BinaryError +-Xexperimental=org.test.BinaryHidden +-Xuse-experimental=org.test.SourceError +-Xuse-experimental=org.test.SourceHidden diff --git a/compiler/testData/cli/jvm/experimentalDeprecated.kt b/compiler/testData/cli/jvm/experimentalDeprecated.kt new file mode 100644 index 00000000000..a150c9e38c9 --- /dev/null +++ b/compiler/testData/cli/jvm/experimentalDeprecated.kt @@ -0,0 +1,17 @@ +package org.test + +@Deprecated("BinaryError", level = DeprecationLevel.ERROR) +@Experimental(Experimental.Level.ERROR, [Experimental.Impact.RUNTIME]) +annotation class BinaryError + +@Deprecated("BinaryHidden", level = DeprecationLevel.HIDDEN) +@Experimental(Experimental.Level.ERROR, [Experimental.Impact.RUNTIME]) +annotation class BinaryHidden + +@Deprecated("SourceError", level = DeprecationLevel.ERROR) +@Experimental(Experimental.Level.ERROR, [Experimental.Impact.COMPILATION]) +annotation class SourceError + +@Deprecated("SourceHidden", level = DeprecationLevel.HIDDEN) +@Experimental(Experimental.Level.ERROR, [Experimental.Impact.COMPILATION]) +annotation class SourceHidden diff --git a/compiler/testData/cli/jvm/experimentalDeprecated.out b/compiler/testData/cli/jvm/experimentalDeprecated.out new file mode 100644 index 00000000000..a129a589ed3 --- /dev/null +++ b/compiler/testData/cli/jvm/experimentalDeprecated.out @@ -0,0 +1,6 @@ +warning: language version 1.3 is experimental, there are no backwards compatibility guarantees for new language and library features +error: experimental API marker org.test.BinaryError is deprecated. BinaryError +error: experimental API marker org.test.BinaryHidden is deprecated. BinaryHidden +error: experimental API marker org.test.SourceError is deprecated. SourceError +error: experimental API marker org.test.SourceHidden is deprecated. SourceHidden +COMPILATION_ERROR diff --git a/compiler/testData/cli/jvm/experimentalDeprecatedWarning.args b/compiler/testData/cli/jvm/experimentalDeprecatedWarning.args new file mode 100644 index 00000000000..165d96e5b33 --- /dev/null +++ b/compiler/testData/cli/jvm/experimentalDeprecatedWarning.args @@ -0,0 +1,8 @@ +$TESTDATA_DIR$/experimentalDeprecatedWarning.kt +-d +$TEMP_DIR$ +-Xskip-runtime-version-check +-language-version +1.3 +-Xuse-experimental=org.test.SourceWarning +-Xexperimental=org.test.BinaryWarning diff --git a/compiler/testData/cli/jvm/experimentalDeprecatedWarning.kt b/compiler/testData/cli/jvm/experimentalDeprecatedWarning.kt new file mode 100644 index 00000000000..74c08cab1c5 --- /dev/null +++ b/compiler/testData/cli/jvm/experimentalDeprecatedWarning.kt @@ -0,0 +1,9 @@ +package org.test + +@Deprecated("BinaryWarning", level = DeprecationLevel.WARNING) +@Experimental(Experimental.Level.ERROR, [Experimental.Impact.RUNTIME]) +annotation class BinaryWarning + +@Deprecated("SourceWarning", level = DeprecationLevel.WARNING) +@Experimental(Experimental.Level.ERROR, [Experimental.Impact.COMPILATION]) +annotation class SourceWarning diff --git a/compiler/testData/cli/jvm/experimentalDeprecatedWarning.out b/compiler/testData/cli/jvm/experimentalDeprecatedWarning.out new file mode 100644 index 00000000000..8fb650a9ae3 --- /dev/null +++ b/compiler/testData/cli/jvm/experimentalDeprecatedWarning.out @@ -0,0 +1,4 @@ +warning: language version 1.3 is experimental, there are no backwards compatibility guarantees for new language and library features +warning: experimental API marker org.test.BinaryWarning is deprecated. BinaryWarning +warning: experimental API marker org.test.SourceWarning is deprecated. SourceWarning +OK diff --git a/compiler/tests/org/jetbrains/kotlin/cli/CliTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/cli/CliTestGenerated.java index 8a40976d775..0e8186f14f5 100644 --- a/compiler/tests/org/jetbrains/kotlin/cli/CliTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/cli/CliTestGenerated.java @@ -183,6 +183,18 @@ public class CliTestGenerated extends AbstractCliTest { doJvmTest(fileName); } + @TestMetadata("experimentalDeprecated.args") + public void testExperimentalDeprecated() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cli/jvm/experimentalDeprecated.args"); + doJvmTest(fileName); + } + + @TestMetadata("experimentalDeprecatedWarning.args") + public void testExperimentalDeprecatedWarning() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cli/jvm/experimentalDeprecatedWarning.args"); + doJvmTest(fileName); + } + @TestMetadata("experimentalIsNotAnnotation.args") public void testExperimentalIsNotAnnotation() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cli/jvm/experimentalIsNotAnnotation.args");