Skip to content

Commit

Permalink
feat: add compiler diagnostics (#33)
Browse files Browse the repository at this point in the history
  • Loading branch information
name-snrl authored Jun 3, 2024
1 parent bdd6e0a commit d9e5617
Show file tree
Hide file tree
Showing 8 changed files with 154 additions and 4 deletions.
2 changes: 2 additions & 0 deletions rules/common/private/get_toolchain.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ load(
def _gen_toolchain(scala, toolchain):
toolchain_info = platform_common.ToolchainInfo(
scala_version = scala[_ScalaConfiguration].version,
enable_diagnostics = toolchain.enable_diagnostics,
enable_semanticdb = toolchain.enable_semanticdb,
semanticdb_bundle_in_jar = toolchain.semanticdb_bundle_in_jar,
is_zinc = True if _ZincConfiguration in scala else False,
Expand Down Expand Up @@ -46,6 +47,7 @@ def get_toolchain(ctx):

if getattr(ctx.attr, "_worker_rule", False):
stub_toolchain = platform_common.ToolchainInfo(
enable_diagnostics = False,
enable_semanticdb = False,
semanticdb_bundle_in_jar = False,
)
Expand Down
6 changes: 5 additions & 1 deletion rules/private/phases/phase_zinc_compile.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ def phase_zinc_compile(ctx, g):
args.add_all(g.classpaths.src_jars, format_each = "--source_jar=%s")
args.add("--tmp", tmp.path)
args.add("--log_level", toolchain.zinc_log_level)
args.add("--enable_diagnostics", toolchain.enable_diagnostics)
if toolchain.enable_diagnostics:
diagnostics_file = ctx.actions.declare_file("{}.diagnosticsproto".format(ctx.label.name))
args.add("--diagnostics_file", diagnostics_file)
args.add_all(g.classpaths.srcs)
args.set_param_file_format("multiline")
args.use_param_file("@%s", use_always = True)
Expand All @@ -106,7 +110,7 @@ def phase_zinc_compile(ctx, g):
] + [zinc.deps_files for zinc in zincs],
)

outputs = [g.classpaths.jar, mains_file, apis, infos, relations, setup, stamps, used, tmp] + semanticdb_files
outputs = [g.classpaths.jar, mains_file, apis, infos, relations, setup, stamps, used, tmp] + semanticdb_files + ([diagnostics_file] if toolchain.enable_diagnostics else [])

# todo: different execution path for nosrc jar?
ctx.actions.run(
Expand Down
14 changes: 14 additions & 0 deletions scala/workers/zinc/compile/BUILD
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
load("@rules_proto//proto:defs.bzl", "proto_library")
load("//rules:scala.bzl", "scala_test", "scala_library")
load("//scala3:defs.bzl", "worker_scala_binary")

proto_library(
name = "diagnostics_proto",
srcs = ["protobuf/diagnostics.proto"],
)

java_proto_library(
name = "diagnostics_java_proto",
visibility = ["//visibility:public"],
deps = [":diagnostics_proto"],
)

worker_scala_binary(
name = "compile",
srcs = glob(["main/*.scala"]),
Expand All @@ -20,6 +32,7 @@ worker_scala_binary(
"@rules_scala3//rules/third_party/jarhelper",
"@rules_scala3//scala/common/worker",
"@rules_scala3//scala/workers/common",
":diagnostics_java_proto",
],
)

Expand All @@ -40,6 +53,7 @@ scala_library(
"@rules_scala3//rules/third_party/jarhelper",
"@rules_scala3//scala/common/worker",
"@rules_scala3//scala/workers/common",
":diagnostics_java_proto",
],
scala = "@//:zinc_3",
)
Expand Down
78 changes: 75 additions & 3 deletions scala/workers/zinc/compile/main/ZincRunner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ import java.text.SimpleDateFormat
import java.util.{Date, List as JList, Optional, Properties}

import scala.jdk.CollectionConverters.*
import scala.jdk.OptionConverters.RichOptional
import scala.util.Try
import scala.util.control.NonFatal

import com.google.devtools.build.buildjar.jarhelper.JarCreator
import sbt.internal.inc.{Analysis, AnalyzingCompiler, CompileFailed, IncrementalCompilerImpl, Locate, PlainVirtualFile, ZincUtil}
import sbt.internal.inc.classpath.ClassLoaderCache
import scopt.{DefaultOParserSetup, OParser, OParserSetup}
import xsbti.{Logger, PathBasedFile, VirtualFile}
import xsbti.{Logger, PathBasedFile, Problem, Severity, VirtualFile}
import xsbti.compile.{
AnalysisContents,
ClasspathOptionsUtil,
Expand All @@ -35,6 +36,41 @@ import xsbti.compile.{

import rules_scala.common.worker.WorkerMain
import rules_scala.workers.common.*
import rules_scala.workers.zinc.diagnostics.Diagnostics;

extension (problem: Problem)
def toDiagnostic: Diagnostics.Diagnostic =
def e =
val path = problem.position.sourcePath.toScala.fold("no data")(identity)
val line = problem.position.line.toScala.fold("no data")(identity)
val pointer = problem.position.pointer.toScala.fold("no data")(identity)
sys.error(s"The compilation Problem($path:$line:$pointer) does not contain enough information. Failed to create compilation diagnostics.")

Diagnostics.Diagnostic.newBuilder
.setSeverity {
problem.severity match
case Severity.Error => Diagnostics.Severity.ERROR
case Severity.Warn => Diagnostics.Severity.WARNING
case Severity.Info => Diagnostics.Severity.INFORMATION
}
.setMessage(problem.message)
.setRange {
Diagnostics.Range.newBuilder
.setStart(
Diagnostics.Position.newBuilder
.setLine(problem.position.startLine().toScala.fold(e)(identity) - 1)
.setCharacter(problem.position.startColumn().toScala.fold(e)(identity))
.build
)
.setEnd(
Diagnostics.Position.newBuilder
.setLine(problem.position.endLine().toScala.fold(e)(identity) - 1)
.setCharacter(problem.position.endColumn().toScala.fold(e)(identity))
.build
)
.build
}
.build

final case class AnalysisArgument(label: String, apis: Path, relations: Path, jars: Vector[Path])
object AnalysisArgument:
Expand Down Expand Up @@ -72,6 +108,8 @@ object ZincRunner extends WorkerMain[ZincRunner.Arguments]:

given logger: AnnexLogger = AnnexLogger(workArgs.logLevel)

val reporter = LoggedReporter(logger)

val sourcesDir = workArgs.tmpDir.resolve("src")
val sources: collection.Seq[File] = workArgs.sources ++ workArgs.sourceJars.zipWithIndex
.flatMap((jar, i) => FileUtil.extractZip(jar, sourcesDir.resolve(i.toString)))
Expand Down Expand Up @@ -167,7 +205,6 @@ object ZincRunner extends WorkerMain[ZincRunner.Arguments]:

val setup =
val incOptions = IncOptions.create()
val reporter = LoggedReporter(logger)
val skip = false
val zincFile: Path = null

Expand Down Expand Up @@ -201,6 +238,27 @@ object ZincRunner extends WorkerMain[ZincRunner.Arguments]:
System.err.println(s"workArgs:$workArgs")
System.err.println(e)
sys.exit(1)
finally
// create compiler diagnostics
if workArgs.enableDiagnostics then
val targetDiagnostics: Diagnostics.TargetDiagnostics =
Diagnostics.TargetDiagnostics.newBuilder.addAllDiagnostics {
reporter.problems
.groupBy(
_.position.sourcePath.toScala
.fold(sys.error("The compilation problem does not contain the path to the source. Failed to create compilation diagnostics."))(
identity
)
)
.map { case (path, problems) =>
Diagnostics.FileDiagnostics.newBuilder
.setPath("workspace-root://" + path)
.addAllDiagnostics(problems.map(_.toDiagnostic).toList.asJava)
.build
}
.asJava
}.build
Files.write(workArgs.diagnosticsFile, targetDiagnostics.toByteArray)

// create analyses
analysisStore.set(AnalysisContents.create(compileResult.analysis, compileResult.setup))
Expand Down Expand Up @@ -272,6 +330,8 @@ object ZincRunner extends WorkerMain[ZincRunner.Arguments]:
javaCompilerOption: Vector[String] = Vector.empty,
label: String = "",
logLevel: LogLevel = LogLevel.Debug,
enableDiagnostics: Boolean = false,
diagnosticsFile: Path = Paths.get("."),
mainManifest: File = new File("."),
outputApis: Path = Paths.get("."),
outputInfos: Path = Paths.get("."),
Expand Down Expand Up @@ -379,7 +439,19 @@ object ZincRunner extends WorkerMain[ZincRunner.Arguments]:
.unbounded()
.optional()
.action((s, c) => c.copy(sources = c.sources :+ s))
.text("Source files")
.text("Source files"),
opt[Boolean]("enable_diagnostics").action((value, c) => c.copy(enableDiagnostics = value)),
opt[File]("diagnostics_file")
.optional()
.action((value, c) => c.copy(diagnosticsFile = value.toPath))
.text("Compilation diagnostics file"),

checkConfig { c =>
if c.enableDiagnostics && c.diagnosticsFile == Paths.get(".") then
failure("If enable_diagnostics is true, diagnostics_file must be specified")
else
success
}
)

def apply(args: collection.Seq[String]): Option[WorkArguments] =
Expand Down
51 changes: 51 additions & 0 deletions scala/workers/zinc/compile/protobuf/diagnostics.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
syntax = "proto3";

option java_package = "rules_scala.workers.zinc.diagnostics";
option java_outer_classname = "Diagnostics";

enum Severity {
UNKNOWN = 0;
ERROR = 1;
WARNING = 2;
INFORMATION = 3;
HINT = 4;
}

message Position {
// 1-indexed
int32 line = 1;
// 1-indexed
int32 character = 2;
}

message Range {
Position start = 1;
Position end = 2;
}

message Location {
string path = 1;
Range range = 2;
}

message DiagnosticRelatedInformation {
Location location = 1;
string message = 2;
}

message Diagnostic {
Range range = 1;
Severity severity = 2;
int64 code = 3;
string message = 5;
repeated DiagnosticRelatedInformation related_information = 6;
}

message FileDiagnostics {
string path = 1;
repeated Diagnostic diagnostics = 2;
}

message TargetDiagnostics {
repeated FileDiagnostics diagnostics = 1;
}
4 changes: 4 additions & 0 deletions scala3/private/toolchain_constants.bzl
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
"A module containing some constants related to the toolchain"

SCALA_TOOLCHAIN_ATTRS = {
"enable_diagnostics": attr.bool(
default = False,
doc = "Enable compiler diagnostic report.",
),
"enable_semanticdb": attr.bool(
default = False,
doc = "Enable SemanticDB.",
Expand Down
2 changes: 2 additions & 0 deletions scala3/repositories.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ def scala3_register_toolchains(
{
"name": "scala3_mezel_toolchain",
"enable_semanticdb": True,
"enable_diagnostics": True,
"global_scalacopts": ["-Xfatal-warnings"],
},
]
Expand Down Expand Up @@ -148,6 +149,7 @@ config_setting(name = "deps_used_error", flag_values = {{ ":deps_used": "error"
scala_toolchain(
name = "toolchain_impl",
scala_version = "{scala_version}",
enable_diagnostics = {enable_diagnostics},
enable_semanticdb = {enable_semanticdb},
semanticdb_bundle_in_jar = {semanticdb_bundle_in_jar},
is_zinc = {is_zinc},
Expand Down
1 change: 1 addition & 0 deletions scala3/toolchain.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ def _scala_toolchain_impl(ctx):

toolchain_info = platform_common.ToolchainInfo(
scala_version = ctx.attr.scala_version,
enable_diagnostics = ctx.attr.enable_diagnostics,
enable_semanticdb = ctx.attr.enable_semanticdb,
semanticdb_bundle_in_jar = ctx.attr.semanticdb_bundle_in_jar,
is_zinc = ctx.attr.is_zinc,
Expand Down

0 comments on commit d9e5617

Please sign in to comment.