javac diagnostics
In recent releases, a lot of effort has been put into improving the diagnostics generated by javac. Partly, this has led to the evolution of the internal APIs used to generate diagnostics, but there is also some infrastructure outside of javac that is used to manage the set of diagnostic messages.
Diagnostic APIs
The primary reporting API is Log
but these days
this is just a facade for creating JCDiagnostic
objects which are rendered by an instance of a
DiagnosticFormatter
. (All these classes are in
com.sun.tools.javac.util
.) Log
also
provides support for filtering and deferring diagnostics.
Diagnostics are composed of a message key and zero or more arguments. In general, the arguments are provided with their "natural" type, and are not converted to anything like String beforehand. The formatters can use the types of the arguments to decide how best to present the value. (For example, an IDE-based formatter could link the name of a symbol to its declaration.)
Two standard formatters are provided.
BasicDiagnosticFormatter
provides basic formatting, and is primarily for use while the compiler is starting up.RichDiagnosticFormatter
provides advanced formatting, such as adding “where” clauses, and analysing symbols to avoid printing unnecessary package names
-XDrawDiagnostics
To facilitate debugging and to isolate “golden-file” tests from changes in the text of message strings, the diagnostic formatters provide a "raw diagnostics mode", in which the message key and arguments are printed directly, instead of being converted to a user-friendly text message. Normally, raw diagnostics mode works in conjunction with the basic diagnostic formatter, since it is the original content of the diagnostic that is being tested, but it can be useful to use raw diagnostics mode with the rich diagnostic formatter as a way of testing the functionality of the formatter itself.Resource Files
The message keys are mapped to strings using standard resource bundles. The source for these are properties files incom.sun.tools.javac.resources
. There are two primary
properties files:
- javac.properties contains the messages for the main program and option decoding
- compiler.properties contains the messages for the body of the compiler
In the primary versions of the property files, many of the
property definitions are immediately preceded by a stylized
comment. These comments indicate the nature of any values that will
be substituted into the property value when generating the text for
a diagnostic. The comments are in stylised English (such as
“list of type” or “string or message
segment”). The comments are suggested by the
MessageInfo
test utility, in conjunction with a set of
examples to exercise as many diagnostics as
possible.
The properties files are compiled into equivalent class files as part of the build process.
- CheckResourceKeys
-
The test program langtools/test/tools/javac/diags/CheckResourceKeys compares the message keys defined in the javac resource bundles with the strings defined in the javac class files, and uses heuristics to determine which messages are not defined (but should be) and which messages are defined (but need not be.)
- MessageInfo
-
This utility runs a set of examples, and from the types of the arguments for each message key, it generates the stylized comments for the corresponding message keys. It does not directly modify the resource files (since they are source files under source code management, but it does generate suggestions which can easily be cut and pasted into the resource files.
langtools/test/tools/javac/diags/examples
The langtools/test/tools/javac/diags/examples directory contains a large, organized collection of examples designed to provide basic coverage of as many javac diagnostics as possible. The purpose is to make it easy to see diagnostic messages in context, instead of as raw strings in a resource file. This is useful when proof-reading messages, and it is useful for localization teams to see how messages may be used.
Each example is intended to be as simple as possible to
demonstrate a specific diagnostic. It is a non-goal to demonstrate
the diagnostic in the context of a realistic program; likewise, it
is a non-goal to demonstrate all the ways in which a diagnostic may
appear. Each example is represented by a single file or a directory
of files in the main examples
directory. The name of
the file or directory is derived from the key of the primary
diagnostic the example is designed to illustrate.
Each example uses stylized comments to identify the message keys
of the diagnostics that the example illustrates, and may contain
additional comments for simple metadata such as how to run the
example. These comments are used by two related utilities,
CheckExamples
and RunExamples
.
- CheckExamples
-
This program runs each example to verify that it generates exactly the set of message keys that are declared in the metadata for each example, and it compares the overall set of message keys against the keys defined in the javac resource bundles, to identify any messages that do not have a corresponding example.
Unfortunately, not all messages are easy to generate, such as those for handling IO exceptions or other system resource issues. And some messages come from defensive checks that should not fail in practice. To accomodate such messages, there is an exclusion mechanism, optimistically called the “not yet” list. The file for this is examples.not-yet.txt.
- RunExamples
-
This program runs the examples and generates a report showing the source code for each example, and the corresponding messages generated by javac.
diags-examples
to
run RunExamples
.