Support -Werror CLI argument to treat warnings as errors

The option is named "warningsAsErrors" in the Gradle plugin

 #KT-10563 Fixed
This commit is contained in:
Alexander Udalov
2017-08-25 17:11:34 +03:00
committed by Ilya Gorbunov
parent 897261a8a6
commit 5cbcbe4a9c
26 changed files with 133 additions and 9 deletions
@@ -74,7 +74,8 @@ class BuiltInsSerializer(dependOnOldBuiltIns: Boolean) : MetadataSerializer(depe
} }
private fun createMessageCollector() = object : GroupingMessageCollector( private fun createMessageCollector() = object : GroupingMessageCollector(
PrintingMessageCollector(System.err, MessageRenderer.PLAIN_RELATIVE_PATHS, /* verbose = */ false) PrintingMessageCollector(System.err, MessageRenderer.PLAIN_RELATIVE_PATHS, false),
false
) { ) {
override fun report(severity: CompilerMessageSeverity, message: String, location: CompilerMessageLocation?) { override fun report(severity: CompilerMessageSeverity, message: String, location: CompilerMessageLocation?) {
// Only report diagnostics without a particular location because there's plenty of errors in built-in sources // Only report diagnostics without a particular location because there's plenty of errors in built-in sources
@@ -44,4 +44,8 @@ abstract class CommonToolArguments : Freezable(), Serializable {
@GradleOption(DefaultValues.BooleanFalseDefault::class) @GradleOption(DefaultValues.BooleanFalseDefault::class)
@Argument(value = "-nowarn", description = "Generate no warnings") @Argument(value = "-nowarn", description = "Generate no warnings")
var suppressWarnings: Boolean by FreezableVar(false) var suppressWarnings: Boolean by FreezableVar(false)
@GradleOption(DefaultValues.BooleanFalseDefault::class)
@Argument(value = "-Werror", description = "Report an error if there are any warnings")
var warningsAsErrors: Boolean by FreezableVar(false)
} }
@@ -35,11 +35,14 @@ public enum CompilerMessageSeverity {
*/ */
OUTPUT; OUTPUT;
public static final EnumSet<CompilerMessageSeverity> ERRORS = EnumSet.of(ERROR, EXCEPTION);
public static final EnumSet<CompilerMessageSeverity> VERBOSE = EnumSet.of(LOGGING); public static final EnumSet<CompilerMessageSeverity> VERBOSE = EnumSet.of(LOGGING);
public boolean isError() { public boolean isError() {
return ERRORS.contains(this); return this == EXCEPTION || this == ERROR;
}
public boolean isWarning() {
return this == STRONG_WARNING || this == WARNING;
} }
@NotNull @NotNull
@@ -24,16 +24,19 @@ import org.jetbrains.annotations.Nullable;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
public class GroupingMessageCollector implements MessageCollector { public class GroupingMessageCollector implements MessageCollector {
private final MessageCollector delegate; private final MessageCollector delegate;
private final boolean treatWarningsAsErrors;
// Note that the key in this map can be null // Note that the key in this map can be null
private final Multimap<CompilerMessageLocation, Message> groupedMessages = LinkedHashMultimap.create(); private final Multimap<CompilerMessageLocation, Message> groupedMessages = LinkedHashMultimap.create();
public GroupingMessageCollector(@NotNull MessageCollector delegate) { public GroupingMessageCollector(@NotNull MessageCollector delegate, boolean treatWarningsAsErrors) {
this.delegate = delegate; this.delegate = delegate;
this.treatWarningsAsErrors = treatWarningsAsErrors;
} }
@Override @Override
@@ -57,17 +60,29 @@ public class GroupingMessageCollector implements MessageCollector {
@Override @Override
public boolean hasErrors() { public boolean hasErrors() {
return hasExplicitErrors() || (treatWarningsAsErrors && hasWarnings());
}
private boolean hasExplicitErrors() {
return groupedMessages.entries().stream().anyMatch(entry -> entry.getValue().severity.isError()); return groupedMessages.entries().stream().anyMatch(entry -> entry.getValue().severity.isError());
} }
private boolean hasWarnings() {
return groupedMessages.entries().stream().anyMatch(entry -> entry.getValue().severity.isWarning());
}
public void flush() { public void flush() {
boolean hasErrors = hasErrors(); boolean hasExplicitErrors = hasExplicitErrors();
if (treatWarningsAsErrors && !hasExplicitErrors && hasWarnings()) {
report(CompilerMessageSeverity.ERROR, "warnings found and -Werror specified", null);
}
List<CompilerMessageLocation> sortedKeys = List<CompilerMessageLocation> sortedKeys =
CollectionsKt.sortedWith(groupedMessages.keySet(), Comparator.nullsFirst(CompilerMessageLocationComparator.INSTANCE)); CollectionsKt.sortedWith(groupedMessages.keySet(), Comparator.nullsFirst(CompilerMessageLocationComparator.INSTANCE));
for (CompilerMessageLocation location : sortedKeys) { for (CompilerMessageLocation location : sortedKeys) {
for (Message message : groupedMessages.get(location)) { for (Message message : groupedMessages.get(location)) {
if (!hasErrors || message.severity.isError() || message.severity == CompilerMessageSeverity.STRONG_WARNING) { if (!hasExplicitErrors || message.severity.isError() || message.severity == CompilerMessageSeverity.STRONG_WARNING) {
delegate.report(message.severity, message.message, message.location); delegate.report(message.severity, message.message, message.location);
} }
} }
@@ -69,7 +69,7 @@ public abstract class CLICompiler<A extends CommonCompilerArguments> extends CLI
@NotNull @NotNull
@Override @Override
public ExitCode execImpl(@NotNull MessageCollector messageCollector, @NotNull Services services, @NotNull A arguments) { public ExitCode execImpl(@NotNull MessageCollector messageCollector, @NotNull Services services, @NotNull A arguments) {
GroupingMessageCollector groupingCollector = new GroupingMessageCollector(messageCollector); GroupingMessageCollector groupingCollector = new GroupingMessageCollector(messageCollector, arguments.getWarningsAsErrors());
CompilerConfiguration configuration = new CompilerConfiguration(); CompilerConfiguration configuration = new CompilerConfiguration();
configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, groupingCollector); configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, groupingCollector);
@@ -81,7 +81,7 @@ abstract class CLITool<A : CommonToolArguments> {
fun exec(messageCollector: MessageCollector, services: Services, arguments: A): ExitCode { fun exec(messageCollector: MessageCollector, services: Services, arguments: A): ExitCode {
printVersionIfNeeded(messageCollector, arguments) printVersionIfNeeded(messageCollector, arguments)
val fixedMessageCollector = if (arguments.suppressWarnings) { val fixedMessageCollector = if (arguments.suppressWarnings && !arguments.warningsAsErrors) {
FilteringMessageCollector(messageCollector, Predicate.isEqual(CompilerMessageSeverity.WARNING)) FilteringMessageCollector(messageCollector, Predicate.isEqual(CompilerMessageSeverity.WARNING))
} }
else { else {
@@ -28,7 +28,8 @@ class ReplTerminalDiagnosticMessageHolder : MessageCollectorBasedReporter, Diagn
private val outputStream = ByteArrayOutputStream() private val outputStream = ByteArrayOutputStream()
override val messageCollector: GroupingMessageCollector = GroupingMessageCollector( override val messageCollector: GroupingMessageCollector = GroupingMessageCollector(
PrintingMessageCollector(PrintStream(outputStream), MessageRenderer.WITHOUT_PATHS, false) PrintingMessageCollector(PrintStream(outputStream), MessageRenderer.WITHOUT_PATHS, false),
false
) )
override val renderedDiagnostics: String override val renderedDiagnostics: String
+1
View File
@@ -8,4 +8,5 @@ where possible options include:
-nowarn Generate no warnings -nowarn Generate no warnings
-verbose Enable verbose logging output -verbose Enable verbose logging output
-version Display compiler version -version Display compiler version
-Werror Report an error if there are any warnings
OK OK
+1
View File
@@ -25,4 +25,5 @@ where possible options include:
-nowarn Generate no warnings -nowarn Generate no warnings
-verbose Enable verbose logging output -verbose Enable verbose logging output
-version Display compiler version -version Display compiler version
-Werror Report an error if there are any warnings
OK OK
+1
View File
@@ -23,4 +23,5 @@ where possible options include:
-nowarn Generate no warnings -nowarn Generate no warnings
-verbose Enable verbose logging output -verbose Enable verbose logging output
-version Display compiler version -version Display compiler version
-Werror Report an error if there are any warnings
OK OK
+4
View File
@@ -0,0 +1,4 @@
$TESTDATA_DIR$/werror.kt
-d
$TEMP_DIR$
-Werror
+4
View File
@@ -0,0 +1,4 @@
fun foo(s: String, t: String?) {
s!!
t?.toString()
}
+5
View File
@@ -0,0 +1,5 @@
error: warnings found and -Werror specified
compiler/testData/cli/jvm/werror.kt:2:6: warning: unnecessary non-null assertion (!!) on a non-null receiver of type String
s!!
^
COMPILATION_ERROR
@@ -0,0 +1,4 @@
$TESTDATA_DIR$/werrorWithExplicitError.kt
-d
$TEMP_DIR$
-Werror
+5
View File
@@ -0,0 +1,5 @@
fun foo(s: String, t: String?) {
s!!
t?.toString()
t.length
}
+4
View File
@@ -0,0 +1,4 @@
compiler/testData/cli/jvm/werrorWithExplicitError.kt:4:6: error: only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String?
t.length
^
COMPILATION_ERROR
+5
View File
@@ -0,0 +1,5 @@
$TESTDATA_DIR$/werrorWithNoWarn.kt
-d
$TEMP_DIR$
-Werror
-nowarn
+4
View File
@@ -0,0 +1,4 @@
fun foo(s: String, t: String?) {
s!!
t?.toString()
}
+5
View File
@@ -0,0 +1,5 @@
error: warnings found and -Werror specified
compiler/testData/cli/jvm/werrorWithNoWarn.kt:2:6: warning: unnecessary non-null assertion (!!) on a non-null receiver of type String
s!!
^
COMPILATION_ERROR
@@ -0,0 +1,6 @@
$TESTDATA_DIR$/werrorWithStrongWarning.kt
-d
$TEMP_DIR$
-Werror
-cp
non-existing-path.jar
+4
View File
@@ -0,0 +1,4 @@
fun foo(s: String, t: String?) {
s!!
t?.toString()
}
+3
View File
@@ -0,0 +1,3 @@
warning: classpath entry points to a non-existent location: non-existing-path.jar
error: warnings found and -Werror specified
COMPILATION_ERROR
@@ -476,6 +476,30 @@ public class CliTestGenerated extends AbstractCliTest {
doJvmTest(fileName); doJvmTest(fileName);
} }
@TestMetadata("werror.args")
public void testWerror() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cli/jvm/werror.args");
doJvmTest(fileName);
}
@TestMetadata("werrorWithExplicitError.args")
public void testWerrorWithExplicitError() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cli/jvm/werrorWithExplicitError.args");
doJvmTest(fileName);
}
@TestMetadata("werrorWithNoWarn.args")
public void testWerrorWithNoWarn() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cli/jvm/werrorWithNoWarn.args");
doJvmTest(fileName);
}
@TestMetadata("werrorWithStrongWarning.args")
public void testWerrorWithStrongWarning() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cli/jvm/werrorWithStrongWarning.args");
doJvmTest(fileName);
}
@TestMetadata("wrongAbiVersion.args") @TestMetadata("wrongAbiVersion.args")
public void testWrongAbiVersion() throws Exception { public void testWrongAbiVersion() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cli/jvm/wrongAbiVersion.args"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cli/jvm/wrongAbiVersion.args");
@@ -17,4 +17,10 @@ interface KotlinCommonOptions : org.jetbrains.kotlin.gradle.dsl.KotlinCommonToo
* Default value: null * Default value: null
*/ */
var languageVersion: kotlin.String? var languageVersion: kotlin.String?
/**
* Report an error if there are any warnings
* Default value: false
*/
var warningsAsErrors: kotlin.Boolean
} }
@@ -24,6 +24,11 @@ internal abstract class KotlinJsOptionsBase : org.jetbrains.kotlin.gradle.dsl.Ko
get() = languageVersionField ?: null get() = languageVersionField ?: null
set(value) { languageVersionField = value } set(value) { languageVersionField = value }
private var warningsAsErrorsField: kotlin.Boolean? = null
override var warningsAsErrors: kotlin.Boolean
get() = warningsAsErrorsField ?: false
set(value) { warningsAsErrorsField = value }
private var friendModulesDisabledField: kotlin.Boolean? = null private var friendModulesDisabledField: kotlin.Boolean? = null
override var friendModulesDisabled: kotlin.Boolean override var friendModulesDisabled: kotlin.Boolean
get() = friendModulesDisabledField ?: false get() = friendModulesDisabledField ?: false
@@ -84,6 +89,7 @@ internal abstract class KotlinJsOptionsBase : org.jetbrains.kotlin.gradle.dsl.Ko
verboseField?.let { args.verbose = it } verboseField?.let { args.verbose = it }
apiVersionField?.let { args.apiVersion = it } apiVersionField?.let { args.apiVersion = it }
languageVersionField?.let { args.languageVersion = it } languageVersionField?.let { args.languageVersion = it }
warningsAsErrorsField?.let { args.warningsAsErrors = it }
friendModulesDisabledField?.let { args.friendModulesDisabled = it } friendModulesDisabledField?.let { args.friendModulesDisabled = it }
mainField?.let { args.main = it } mainField?.let { args.main = it }
metaInfoField?.let { args.metaInfo = it } metaInfoField?.let { args.metaInfo = it }
@@ -103,6 +109,7 @@ internal fun org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments.fil
verbose = false verbose = false
apiVersion = null apiVersion = null
languageVersion = null languageVersion = null
warningsAsErrors = false
friendModulesDisabled = false friendModulesDisabled = false
main = "call" main = "call"
metaInfo = true metaInfo = true
@@ -24,6 +24,11 @@ internal abstract class KotlinJvmOptionsBase : org.jetbrains.kotlin.gradle.dsl.K
get() = languageVersionField ?: null get() = languageVersionField ?: null
set(value) { languageVersionField = value } set(value) { languageVersionField = value }
private var warningsAsErrorsField: kotlin.Boolean? = null
override var warningsAsErrors: kotlin.Boolean
get() = warningsAsErrorsField ?: false
set(value) { warningsAsErrorsField = value }
private var includeRuntimeField: kotlin.Boolean? = null private var includeRuntimeField: kotlin.Boolean? = null
override var includeRuntime: kotlin.Boolean override var includeRuntime: kotlin.Boolean
get() = includeRuntimeField ?: false get() = includeRuntimeField ?: false
@@ -64,6 +69,7 @@ internal abstract class KotlinJvmOptionsBase : org.jetbrains.kotlin.gradle.dsl.K
verboseField?.let { args.verbose = it } verboseField?.let { args.verbose = it }
apiVersionField?.let { args.apiVersion = it } apiVersionField?.let { args.apiVersion = it }
languageVersionField?.let { args.languageVersion = it } languageVersionField?.let { args.languageVersion = it }
warningsAsErrorsField?.let { args.warningsAsErrors = it }
includeRuntimeField?.let { args.includeRuntime = it } includeRuntimeField?.let { args.includeRuntime = it }
javaParametersField?.let { args.javaParameters = it } javaParametersField?.let { args.javaParameters = it }
jdkHomeField?.let { args.jdkHome = it } jdkHomeField?.let { args.jdkHome = it }
@@ -79,6 +85,7 @@ internal fun org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments.fi
verbose = false verbose = false
apiVersion = null apiVersion = null
languageVersion = null languageVersion = null
warningsAsErrors = false
includeRuntime = false includeRuntime = false
javaParameters = false javaParameters = false
jdkHome = null jdkHome = null