Check experimental API markers for deprecation

#KT-22759 In Progress
This commit is contained in:
Alexander Udalov
2018-01-17 18:51:59 +01:00
parent 5e78adb501
commit ec6b49b8b8
10 changed files with 97 additions and 9 deletions
@@ -99,9 +99,11 @@ class AnalyzerWithCompilerReport(
fun analyzeAndReport(files: Collection<KtFile>, analyze: () -> AnalysisResult) { fun analyzeAndReport(files: Collection<KtFile>, analyze: () -> AnalysisResult) {
analysisResult = analyze() analysisResult = analyze()
ExperimentalUsageChecker.checkCompilerArguments(analysisResult.moduleDescriptor, languageVersionSettings) { message -> ExperimentalUsageChecker.checkCompilerArguments(
messageCollector.report(ERROR, message) analysisResult.moduleDescriptor, languageVersionSettings,
} reportError = { message -> messageCollector.report(ERROR, message) },
reportWarning = { message -> messageCollector.report(WARNING, message) }
)
reportSyntaxErrors(files) reportSyntaxErrors(files)
reportDiagnostics(analysisResult.bindingContext.diagnostics, messageCollector) reportDiagnostics(analysisResult.bindingContext.diagnostics, messageCollector)
reportIncompleteHierarchies() reportIncompleteHierarchies()
@@ -37,6 +37,7 @@ import org.jetbrains.kotlin.resolve.constants.KClassValue
import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.jetbrains.kotlin.utils.SmartSet import org.jetbrains.kotlin.utils.SmartSet
import org.jetbrains.kotlin.utils.addIfNotNull 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 { fun checkAnnotation(fqName: String, allowNonCompilationImpact: Boolean): Boolean {
val descriptor = module.resolveClassByFqName(FqName(fqName), NoLookupLocation.FOR_NON_TRACKED_SCOPE) val descriptor = module.resolveClassByFqName(FqName(fqName), NoLookupLocation.FOR_NON_TRACKED_SCOPE)
val experimentality = descriptor?.loadExperimentalityForMarkerAnnotation() val experimentality = descriptor?.loadExperimentalityForMarkerAnnotation()
@@ -250,9 +261,20 @@ object ExperimentalUsageChecker : CallChecker {
!allowNonCompilationImpact && !experimentality.impact.all(Experimentality.Impact.COMPILATION::equals) -> !allowNonCompilationImpact && !experimentality.impact.all(Experimentality.Impact.COMPILATION::equals) ->
"Experimental API marker $fqName has impact other than COMPILATION, " + "Experimental API marker $fqName has impact other than COMPILATION, " +
"therefore it can't be used with -Xuse-experimental" "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) reportError(message)
return false return false
} }
@@ -201,10 +201,8 @@ class DeprecationResolver(
descriptor.checkSinceKotlinVersionAccessibility(languageVersionSettings) descriptor.checkSinceKotlinVersionAccessibility(languageVersionSettings)
} }
fun getDeprecations( fun getDeprecations(descriptor: DeclarationDescriptor): List<Deprecation> =
descriptor: DeclarationDescriptor deprecations(descriptor.original)
) = deprecations(descriptor.original)
fun isDeprecatedHidden(descriptor: DeclarationDescriptor): Boolean = fun isDeprecatedHidden(descriptor: DeclarationDescriptor): Boolean =
getDeprecations(descriptor).any { it.deprecationLevel == HIDDEN } getDeprecations(descriptor).any { it.deprecationLevel == HIDDEN }
+10
View File
@@ -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
+17
View File
@@ -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
+6
View File
@@ -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
@@ -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
@@ -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
@@ -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
@@ -183,6 +183,18 @@ public class CliTestGenerated extends AbstractCliTest {
doJvmTest(fileName); 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") @TestMetadata("experimentalIsNotAnnotation.args")
public void testExperimentalIsNotAnnotation() throws Exception { public void testExperimentalIsNotAnnotation() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cli/jvm/experimentalIsNotAnnotation.args"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cli/jvm/experimentalIsNotAnnotation.args");