Skip to content

Commit 5a2453e

Browse files
Handle incorrect Scalac options and prevent printing ScalacWorker stacktraces (#1606)
* Improve handling of invalid settings passed to Scala compiler. Throw known exception instead of allowing to None.get (Scala3) or NullPointerException (Scala2) * Prevent printing stack trace of ScalacInvoker on known compilation errors * Run reporter.flush in Scala3 * Restore previous error messages * Addi tests to ensure corect error message and no stack traces are shown * Remove unused code from test_invalid_scalacopts.sh * Print exception message only once. Previously printed in both e.getMessage and e.printStackTrace * Ident with spaces instead of tabs
1 parent ac4181c commit 5a2453e

File tree

8 files changed

+91
-9
lines changed

8 files changed

+91
-9
lines changed

src/java/io/bazel/rulesscala/scalac/ScalacInvoker.java

+10-4
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@ public static ScalacInvokerResults invokeCompiler(CompileOptions ops, String[] c
2424
comp.process(compilerArgs);
2525
} catch (Throwable ex) {
2626
if (ex.toString().contains("scala.reflect.internal.Types$TypeError")) {
27-
throw new RuntimeException("Build failure with type error", ex);
27+
throw new ScalacWorker.CompilationFailed("with type error", ex);
2828
} else if (ex.toString().contains("java.lang.StackOverflowError")) {
29-
throw new RuntimeException("Build failure with StackOverflowError", ex);
29+
throw new ScalacWorker.CompilationFailed("with StackOverflowError", ex);
3030
} else if (isMacroException(ex)) {
31-
throw new RuntimeException("Build failure during macro expansion", ex);
31+
throw new ScalacWorker.CompilationFailed("during macro expansion", ex);
3232
} else {
3333
throw ex;
3434
}
@@ -39,6 +39,12 @@ public static ScalacInvokerResults invokeCompiler(CompileOptions ops, String[] c
3939
results.stopTime = System.currentTimeMillis();
4040

4141
ConsoleReporter reporter = (ConsoleReporter) comp.getReporter();
42+
if (reporter == null) {
43+
// Can happen only when `ReportableMainClass::newCompiler` was not invoked,
44+
// typically due to invalid settings
45+
throw new ScalacWorker.InvalidSettings();
46+
}
47+
4248
if (reporter instanceof ProtoReporter) {
4349
ProtoReporter protoReporter = (ProtoReporter) reporter;
4450
protoReporter.writeTo(Paths.get(ops.diagnosticsFile));
@@ -52,7 +58,7 @@ public static ScalacInvokerResults invokeCompiler(CompileOptions ops, String[] c
5258

5359
if (reporter.hasErrors()) {
5460
reporter.flush();
55-
throw new RuntimeException("Build failed");
61+
throw new ScalacWorker.CompilationFailed("with errors.");
5662
}
5763

5864
return results;

src/java/io/bazel/rulesscala/scalac/ScalacInvoker3.java

+7-3
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,11 @@ public static ScalacInvokerResults invokeCompiler(CompileOptions ops, String[] c
2222
Driver driver = new dotty.tools.dotc.Driver();
2323
Contexts.Context ctx = driver.initCtx().fresh();
2424

25-
Tuple2<scala.collection.immutable.List<AbstractFile>, Contexts.Context> r = driver.setup(compilerArgs, ctx).get();
25+
Tuple2<scala.collection.immutable.List<AbstractFile>, Contexts.Context> r =
26+
driver.setup(compilerArgs, ctx)
27+
.getOrElse(() -> {
28+
throw new ScalacWorker.InvalidSettings();
29+
});
2630

2731
Compiler compiler = driver.newCompiler(r._2);
2832

@@ -39,8 +43,8 @@ public static ScalacInvokerResults invokeCompiler(CompileOptions ops, String[] c
3943

4044

4145
if (reporter.hasErrors()) {
42-
// reporter.flush();
43-
throw new RuntimeException("Build failed");
46+
reporter.flush(ctx);
47+
throw new ScalacWorker.CompilationFailed("with errors.");
4448
}
4549

4650
return results;

src/java/io/bazel/rulesscala/scalac/ScalacWorker.java

+15
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,21 @@
2626

2727
class ScalacWorker implements Worker.Interface {
2828

29+
public static class InvalidSettings extends WorkerException {
30+
public InvalidSettings() {
31+
super("Failed to invoke Scala compiler, ensure passed options are valid");
32+
}
33+
}
34+
35+
public static class CompilationFailed extends WorkerException {
36+
public CompilationFailed(String reason, Throwable cause) {
37+
super("Build failure " + reason, cause);
38+
}
39+
public CompilationFailed(String reason) {
40+
this(reason, null);
41+
}
42+
}
43+
2944
private static final boolean isWindows =
3045
System.getProperty("os.name").toLowerCase().contains("windows");
3146

src/java/io/bazel/rulesscala/worker/Worker.java

+12-2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,16 @@ public final class Worker {
2727

2828
public static interface Interface {
2929
public void work(String[] args) throws Exception;
30+
31+
32+
public abstract class WorkerException extends RuntimeException {
33+
public WorkerException(String message) {
34+
super(message);
35+
}
36+
public WorkerException(String message, Throwable cause) {
37+
super(message, cause);
38+
}
39+
}
3040
}
3141

3242
/**
@@ -87,8 +97,8 @@ public void checkPermission(Permission permission) {
8797
} catch (ExitTrapped e) {
8898
code = e.code;
8999
} catch (Exception e) {
90-
System.err.println(e.getMessage());
91-
e.printStackTrace();
100+
if (e instanceof Interface.WorkerException) System.err.println(e.getMessage());
101+
else e.printStackTrace();
92102
code = 1;
93103
}
94104

test/shell/test_invalid_scalacopts.sh

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# shellcheck source=./test_runner.sh
2+
3+
dir=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
4+
. "${dir}"/test_runner.sh
5+
. "${dir}"/test_helper.sh
6+
runner=$(get_test_runner "${1:-local}")
7+
8+
test_logs_contains() {
9+
scalaVersion=$1
10+
expected=$2
11+
12+
bazel build \
13+
--repo_env=SCALA_VERSION=${scalaVersion} \
14+
//test_expect_failure/scalacopts_invalid:empty \
15+
2>&1 | grep "$expected"
16+
}
17+
18+
test_logs_not_contains() {
19+
scalaVersion=$1
20+
expected=$2
21+
22+
bazel build \
23+
--repo_env=SCALA_VERSION=${scalaVersion} \
24+
//test_expect_failure/scalacopts_invalid:empty \
25+
2>&1 | grep -v "$expected"
26+
}
27+
28+
for scalaVersion in 2.12.19 2.13.14 3.3.3; do
29+
if [[ "$scalaVersion" == 3.* ]]; then
30+
$runner test_logs_contains $scalaVersion "not-existing is not a valid choice for -source"
31+
else
32+
$runner test_logs_contains $scalaVersion "bad option: '-source:not-existing'"
33+
fi
34+
$runner test_logs_contains $scalaVersion 'Failed to invoke Scala compiler, ensure passed options are valid'
35+
$runner test_logs_not_contains $scalaVersion 'at io.bazel.rulesscala.scalac.ScalacWorker.main'
36+
done
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
load("//scala:scala.bzl", "scala_library")
2+
3+
scala_library(
4+
name = "empty",
5+
srcs = ["Empty.scala"],
6+
scalacopts = ["-source:not-existing"],
7+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package test_expect_failure.scalacopts_invalid
2+
3+
class Empty

test_rules_scala.sh

+1
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,4 @@ $runner bazel build //test_statsfile:SimpleNoStatsFile_statsfile --extra_toolcha
5858
. "${test_dir}"/test_persistent_worker.sh
5959
. "${test_dir}"/test_semanticdb.sh
6060
. "${test_dir}"/test_scaladoc.sh
61+
. "${test_dir}"/test_invalid_scalacopts.sh

0 commit comments

Comments
 (0)