Implement diagnostics for ignored compiler arguments on script compilation
This commit is contained in:
+86
-6
@@ -13,6 +13,7 @@ import com.intellij.psi.impl.PsiFileFactoryImpl
|
||||
import com.intellij.testFramework.LightVirtualFile
|
||||
import org.jetbrains.kotlin.analyzer.AnalysisResult
|
||||
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
||||
import org.jetbrains.kotlin.cli.common.arguments.Argument
|
||||
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
|
||||
import org.jetbrains.kotlin.cli.common.arguments.parseCommandLineArguments
|
||||
import org.jetbrains.kotlin.cli.common.environment.setIdeaIoUseFallback
|
||||
@@ -41,7 +42,9 @@ import org.jetbrains.kotlin.script.KotlinScriptDefinition
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
import java.util.*
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KMutableProperty1
|
||||
import kotlin.reflect.KType
|
||||
import kotlin.reflect.full.findAnnotation
|
||||
import kotlin.reflect.full.starProjectedType
|
||||
import kotlin.script.dependencies.ScriptContents
|
||||
import kotlin.script.experimental.api.*
|
||||
@@ -66,6 +69,7 @@ class KJvmCompilerImpl(val hostConfiguration: ScriptingHostConfiguration) : KJvm
|
||||
scriptCompilationConfiguration: ScriptCompilationConfiguration
|
||||
): ResultWithDiagnostics<CompiledScript<*>> {
|
||||
val messageCollector = ScriptDiagnosticsMessageCollector()
|
||||
val reportingState = ReportingState()
|
||||
|
||||
fun failure(vararg diagnostics: ScriptDiagnostic): ResultWithDiagnostics.Failure =
|
||||
ResultWithDiagnostics.Failure(*messageCollector.diagnostics.toTypedArray(), *diagnostics)
|
||||
@@ -80,7 +84,8 @@ class KJvmCompilerImpl(val hostConfiguration: ScriptingHostConfiguration) : KJvm
|
||||
|
||||
// TODO: refactor/cleanup when the internal resolving API will allow easier info passing between resolver and compiler
|
||||
|
||||
val kotlinCompilerConfiguration = createInitialCompilerConfiguration(scriptCompilationConfiguration, messageCollector)
|
||||
val kotlinCompilerConfiguration =
|
||||
createInitialCompilerConfiguration(scriptCompilationConfiguration, messageCollector, reportingState)
|
||||
|
||||
val initialScriptCompilationConfiguration =
|
||||
scriptCompilationConfiguration.withUpdatesFromCompilerConfiguration(kotlinCompilerConfiguration)
|
||||
@@ -113,7 +118,7 @@ class KJvmCompilerImpl(val hostConfiguration: ScriptingHostConfiguration) : KJvm
|
||||
|
||||
// collectScriptsCompilationDependencies calls resolver for every file, so at this point all updated configurations are collected
|
||||
environment.configuration.updateWithRefinedConfigurations(
|
||||
initialScriptCompilationConfiguration, sourcesWithRefinementsState.refinedConfigurations
|
||||
initialScriptCompilationConfiguration, sourcesWithRefinementsState.refinedConfigurations, messageCollector, reportingState
|
||||
)
|
||||
|
||||
val analysisResult = analyze(sourceFiles, environment)
|
||||
@@ -142,6 +147,10 @@ class KJvmCompilerImpl(val hostConfiguration: ScriptingHostConfiguration) : KJvm
|
||||
val refinedConfigurations = hashMapOf<SourceCode, ScriptCompilationConfiguration>()
|
||||
}
|
||||
|
||||
private class ReportingState {
|
||||
var currentArguments = K2JVMCompilerArguments()
|
||||
}
|
||||
|
||||
private fun makeScriptDefinition(
|
||||
scriptCompilationConfiguration: ScriptCompilationConfiguration,
|
||||
mainScript: SourceCode,
|
||||
@@ -174,7 +183,8 @@ class KJvmCompilerImpl(val hostConfiguration: ScriptingHostConfiguration) : KJvm
|
||||
|
||||
private fun createInitialCompilerConfiguration(
|
||||
scriptCompilationConfiguration: ScriptCompilationConfiguration,
|
||||
messageCollector: MessageCollector
|
||||
messageCollector: MessageCollector,
|
||||
reportingState: ReportingState
|
||||
): CompilerConfiguration {
|
||||
|
||||
val baseArguments = K2JVMCompilerArguments()
|
||||
@@ -183,6 +193,9 @@ class KJvmCompilerImpl(val hostConfiguration: ScriptingHostConfiguration) : KJvm
|
||||
baseArguments
|
||||
)
|
||||
|
||||
reportArgumentsIgnoredGenerally(baseArguments, messageCollector, reportingState)
|
||||
reportingState.currentArguments = baseArguments
|
||||
|
||||
return org.jetbrains.kotlin.config.CompilerConfiguration().apply {
|
||||
|
||||
// default value differs from the argument'ss default (see #KT-29405 and #KT-29319)
|
||||
@@ -260,7 +273,9 @@ class KJvmCompilerImpl(val hostConfiguration: ScriptingHostConfiguration) : KJvm
|
||||
|
||||
private fun CompilerConfiguration.updateWithRefinedConfigurations(
|
||||
initialScriptCompilationConfiguration: ScriptCompilationConfiguration,
|
||||
refinedScriptCompilationConfigurations: HashMap<SourceCode, ScriptCompilationConfiguration>
|
||||
refinedScriptCompilationConfigurations: HashMap<SourceCode, ScriptCompilationConfiguration>,
|
||||
messageCollector: ScriptDiagnosticsMessageCollector,
|
||||
reportingState: ReportingState
|
||||
) {
|
||||
val updatedCompilerOptions = refinedScriptCompilationConfigurations.flatMap {
|
||||
it.value[ScriptCompilationConfiguration.compilerOptions] ?: emptyList()
|
||||
@@ -272,13 +287,14 @@ class KJvmCompilerImpl(val hostConfiguration: ScriptingHostConfiguration) : KJvm
|
||||
val updatedArguments = K2JVMCompilerArguments()
|
||||
parseCommandLineArguments(updatedCompilerOptions, updatedArguments)
|
||||
|
||||
reportArgumentsIgnoredGenerally(updatedArguments, messageCollector, reportingState)
|
||||
reportArgumentsIgnoredFromRefinement(updatedArguments, messageCollector, reportingState)
|
||||
|
||||
setupCommonArguments(updatedArguments)
|
||||
|
||||
setupJvmSpecificArguments(updatedArguments)
|
||||
|
||||
configureAdvancedJvmOptions(updatedArguments)
|
||||
|
||||
// TODO: report warnings for all ignored options
|
||||
}
|
||||
}
|
||||
|
||||
@@ -357,6 +373,70 @@ class KJvmCompilerImpl(val hostConfiguration: ScriptingHostConfiguration) : KJvm
|
||||
KJvmCompiledModule(generationState)
|
||||
)
|
||||
}
|
||||
|
||||
private fun reportArgumentsIgnoredGenerally(
|
||||
arguments: K2JVMCompilerArguments,
|
||||
messageCollector: MessageCollector,
|
||||
reportingState: ReportingState
|
||||
) {
|
||||
|
||||
reportIgnoredArguments(
|
||||
arguments,
|
||||
"The following compiler arguments are ignored on script compilation: ",
|
||||
messageCollector,
|
||||
reportingState,
|
||||
K2JVMCompilerArguments::version,
|
||||
K2JVMCompilerArguments::destination,
|
||||
K2JVMCompilerArguments::buildFile,
|
||||
K2JVMCompilerArguments::commonSources,
|
||||
K2JVMCompilerArguments::allWarningsAsErrors,
|
||||
K2JVMCompilerArguments::script,
|
||||
K2JVMCompilerArguments::scriptTemplates,
|
||||
K2JVMCompilerArguments::scriptResolverEnvironment,
|
||||
K2JVMCompilerArguments::disableStandardScript,
|
||||
K2JVMCompilerArguments::disableDefaultScriptingPlugin,
|
||||
K2JVMCompilerArguments::pluginClasspaths,
|
||||
K2JVMCompilerArguments::pluginOptions,
|
||||
K2JVMCompilerArguments::useJavac,
|
||||
K2JVMCompilerArguments::compileJava,
|
||||
K2JVMCompilerArguments::reportPerf,
|
||||
K2JVMCompilerArguments::dumpPerf
|
||||
)
|
||||
}
|
||||
|
||||
private fun reportArgumentsIgnoredFromRefinement(
|
||||
arguments: K2JVMCompilerArguments, messageCollector: MessageCollector, reportingState: ReportingState
|
||||
) {
|
||||
reportIgnoredArguments(
|
||||
arguments,
|
||||
"The following compiler arguments are ignored when configured from refinement callbacks: ",
|
||||
messageCollector,
|
||||
reportingState,
|
||||
K2JVMCompilerArguments::noJdk,
|
||||
K2JVMCompilerArguments::jdkHome,
|
||||
K2JVMCompilerArguments::javaModulePath,
|
||||
K2JVMCompilerArguments::classpath,
|
||||
K2JVMCompilerArguments::noStdlib,
|
||||
K2JVMCompilerArguments::noReflect
|
||||
)
|
||||
}
|
||||
|
||||
private fun reportIgnoredArguments(
|
||||
arguments: K2JVMCompilerArguments, message: String,
|
||||
messageCollector: MessageCollector, reportingState: ReportingState,
|
||||
vararg toIgnore: KMutableProperty1<K2JVMCompilerArguments, *>
|
||||
) {
|
||||
val ignoredArgKeys = toIgnore.mapNotNull { argProperty ->
|
||||
if (argProperty.get(arguments) != argProperty.get(reportingState.currentArguments)) {
|
||||
argProperty.findAnnotation<Argument>()?.value
|
||||
?: throw IllegalStateException("unknown compiler argument property: $argProperty: no Argument annotation found")
|
||||
} else null
|
||||
}
|
||||
|
||||
if (ignoredArgKeys.isNotEmpty()) {
|
||||
messageCollector.report(CompilerMessageSeverity.STRONG_WARNING, "$message${ignoredArgKeys.joinToString(", ")}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+20
@@ -173,6 +173,26 @@ class ScriptingHostTest : TestCase() {
|
||||
assertTrue(res2 is ResultWithDiagnostics.Success)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testIgnoredOptionsWarning() {
|
||||
val script = "println(\"Hi\")"
|
||||
val compilationConfiguration = createJvmCompilationConfigurationFromTemplate<SimpleScriptTemplate> {
|
||||
compilerOptions("-version", "-d", "destDir", "-Xreport-perf", "-no-reflect")
|
||||
refineConfiguration {
|
||||
beforeCompiling { ctx ->
|
||||
ScriptCompilationConfiguration(ctx.compilationConfiguration) {
|
||||
compilerOptions.append("-no-jdk", "-version", "-no-stdlib", "-Xdump-perf", "-no-inline")
|
||||
}.asSuccess()
|
||||
}
|
||||
}
|
||||
}
|
||||
val res = BasicJvmScriptingHost().eval(script.toScriptSource(), compilationConfiguration, null)
|
||||
assertTrue(res is ResultWithDiagnostics.Success)
|
||||
assertNotNull(res.reports.find { it.message == "The following compiler arguments are ignored on script compilation: -version, -d, -Xreport-perf" })
|
||||
assertNotNull(res.reports.find { it.message == "The following compiler arguments are ignored on script compilation: -Xdump-perf" })
|
||||
assertNotNull(res.reports.find { it.message == "The following compiler arguments are ignored when configured from refinement callbacks: -no-jdk, -no-stdlib" })
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMemoryCache() {
|
||||
val script = "val x = 1\nprintln(\"x = \$x\")"
|
||||
|
||||
@@ -71,25 +71,26 @@ private fun splitJarUrl(url: String): Pair<String, String>? {
|
||||
return Pair(jarPath, resourcePath)
|
||||
}
|
||||
|
||||
fun getResourcePathForClass(aClass: Class<*>): File {
|
||||
fun tryGetResourcePathForClass(aClass: Class<*>): File? {
|
||||
val path = "/" + aClass.name.replace('.', '/') + ".class"
|
||||
val resourceRoot = getResourceRoot(aClass, path) ?: throw IllegalStateException("Resource not found: $path")
|
||||
return File(resourceRoot).absoluteFile
|
||||
}
|
||||
|
||||
fun tryGetResourcePathForClassByName(name: String, classLoader: ClassLoader): File? = try {
|
||||
classLoader.loadClass(name)?.let {
|
||||
val path = "/" + name.replace('.', '/') + ".class"
|
||||
getResourceRoot(it, path)
|
||||
}?.let {
|
||||
return getResourceRoot(aClass, path)?.let {
|
||||
File(it).absoluteFile
|
||||
}
|
||||
} catch (_: ClassNotFoundException) {
|
||||
null
|
||||
} catch (_: NoClassDefFoundError) {
|
||||
null
|
||||
}
|
||||
|
||||
fun getResourcePathForClass(aClass: Class<*>): File {
|
||||
return tryGetResourcePathForClass(aClass) ?: throw IllegalStateException("Resource for class: ${aClass.name} not found")
|
||||
}
|
||||
|
||||
fun tryGetResourcePathForClassByName(name: String, classLoader: ClassLoader): File? =
|
||||
try {
|
||||
classLoader.loadClass(name)?.let(::tryGetResourcePathForClass)
|
||||
} catch (_: ClassNotFoundException) {
|
||||
null
|
||||
} catch (_: NoClassDefFoundError) {
|
||||
null
|
||||
}
|
||||
|
||||
internal fun URL.toFile() =
|
||||
try {
|
||||
File(toURI().schemeSpecificPart)
|
||||
|
||||
@@ -187,14 +187,16 @@ object KotlinJars {
|
||||
}
|
||||
|
||||
fun getLib(propertyName: String, jarName: String, markerClass: KClass<*>): File? =
|
||||
System.getProperty(propertyName)?.let(::File)?.takeIf(File::exists)
|
||||
?: explicitCompilerClasspath?.firstOrNull { it.matchMaybeVersionedFile(jarName) }?.takeIf(File::exists)
|
||||
getExplicitLib(propertyName, jarName)
|
||||
?: getResourcePathForClass(markerClass.java).takeIf(File::exists)
|
||||
|
||||
fun getLib(propertyName: String, jarName: String, markerClassName: String): File? =
|
||||
getExplicitLib(propertyName, jarName)
|
||||
?: tryGetResourcePathForClassByName(markerClassName, Thread.currentThread().contextClassLoader)?.takeIf(File::exists)
|
||||
|
||||
private fun getExplicitLib(propertyName: String, jarName: String) =
|
||||
System.getProperty(propertyName)?.let(::File)?.takeIf(File::exists)
|
||||
?: explicitCompilerClasspath?.firstOrNull { it.matchMaybeVersionedFile(jarName) }?.takeIf(File::exists)
|
||||
?: tryGetResourcePathForClassByName(markerClassName, Thread.currentThread().contextClassLoader)?.takeIf(File::exists)
|
||||
|
||||
val stdlibOrNull: File? by lazy {
|
||||
System.getProperty(KOTLIN_STDLIB_JAR_PROPERTY)?.let(::File)?.takeIf(File::exists)
|
||||
@@ -214,7 +216,7 @@ object KotlinJars {
|
||||
getLib(
|
||||
KOTLIN_REFLECT_JAR_PROPERTY,
|
||||
KOTLIN_JAVA_REFLECT_JAR,
|
||||
"kotlin.reflect.full.IllegalCallableAccessException"
|
||||
"kotlin.reflect.full.KClasses" // using a class that is a part of the kotlin-reflect.jar
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user