improving javac diagnostics - opinion needed
Maurizio Cimadamore
Maurizio.Cimadamore at Sun.COM
Mon Mar 23 03:26:14 PDT 2009
Hi
as you might know I've spent some time in order to improve the quality
of javac's error messages [2, 4]. Some chunks of this work have already
been pushed to the tl workspace; those changes involved mostly the
refactoring the error messages layout - e.g. the error line has been
moved from the bottom of the message to the second line, so that now
messages have the following structure (see [1, 5] for more detailed info):
<SUMMARY> (one line description of the error message)
<ERROR LINE>
<DETAILS> (more info about the error, e.g. expected/found types)
The remaining part of this work on error messages is to improve the
integration between the javac diagnostic subsystem and the Java
type-system. This involves a number of improvements involving how
type-variables, intersection types and captured types are rendered in a
given error message. Consider the following example (taken form [2]):
class Foo<T extends String> {
<T extends Integer> void foo(T t) {
test(t);
}
void test(T t) {}
}
This used to generate the following diagnostic:
TestX.java:3: method test in class Foo<T> cannot be applied to given types
test(t);
^
required: T
found: T
1 error
Once my work on diagnostics is complete, the above message will be
displayed as follows:
Test.java:3: method test in class Foo<T#0> cannot be applied to given types
test(t);
^
required: T#0
found: T#1
where T#0,T#1 are type variables:
T#0 extends String
(declared in class Foo)
T#1 extends Integer
(declared in method <T>foo(T))
1 error
As you can see, the new error is more informative, as it contains a lot
of details about the error message elements - e.g. for each type
variables, the corresponding declared bound(s) is reported and clashing
names are disambiguated with integer indexes (if needed) - in order to
avoid confusion.
A similar technique is exploited for captured type-variables: consider
the following example (taken from [3]):
interface List<E> {}
class Test {
<T> void merge(List<T> l1, List<T> l2) {}
void test(List<? extends Test> list) {
merge(list, list);
}
}
The standard javac diagnostic is:
../Test.java:6: method merge in class Test cannot be applied to given types
merge(list, list);
^
required: List<T>,List<T>
found: List<capture#1 of ? extends Test>,List<capture#2 of ? extends Test>
1 error
Which will be transformed to:
Test.java:6: method merge in class Test cannot be applied to given types
merge(list, list);
^
required: List<T>,List<T>
found: List<? #1>,List<? #2>
where T is a type-variable:
T extends Object
(declared in method <T>merge(List<T>,List<T>))
where ? #1,? #2 are fresh type-variables:
? #1 extends Test
(capture of ? extends Test)
? #2 extends Test
(capture of ? extends Test)
1 error
As you can see, captured types are represented with a '?' sign, followed
by an integer index - at the end of the diagnostic a list of 'where'
clauses collects all the info about the captured types that have been
displayed such as upper/lower bounds and where the captured type comes
from. For intersection types a similar technique has been exploited (for
brevity I won't include an example here - an intersection type is
represented by the '&' sign followed by an integer index - and the info
about the types forming the intersection are given in a where clause
similar to the ones shown above).
The new error messages are very powerful - and they provide a lot of
info that can help the programmers to know more about an error messages
- they proved to be a very useful tool in detecting ambiguities/name
clashes. Their biggest disadvantage is perhaps in their verbosity -
which leads to the question: should javac emit by default the new
augmented diagnostics ? Or, perhaps, should javac just print old
diagnostics (by default) *plus* a note at the end of the output, to
advise the user that more info is available and that it can be retrieved
by passing some verbosity option to the compiler?
When we designed this new diagnostic subsystem we felt that the more
info available the better - we also didn't worry too much about the
diagnostic verbosity as (i) an IDE could easily present the contents of
the error message in a more sophisticated way (e.g. tree representation)
so that the user can optionally 'click' on parts of the diagnostics in
order to retrieve more details and (ii) the diagnostic subsystem (see
[1]) is now mature enough to display only selected parts of a given
diagnostic - which means that it would be quite easy to include options
to optionally disable the 'where' clauses in case the user doesn't need
them. Moreover I expect standard error messages (e.g. not related to
generics) not to be affected by where clauses at all.
So - how do you think the formal of the jdk 7's javac should look like?
Do you prefer more informartive but longer diagnostics enabled by
default, or do you think it would be better to generate additional info
on-demand ?
Thanks
Maurizio
[1] http://blogs.sun.com/mcimadamore/entry/playing_with_formatters
[2] http://blogs.sun.com/mcimadamore/entry/improving_javac_diagnostics
[3] http://blogs.sun.com/ahe/entry/diagnosing_captured_wildcards
[4] http://monaco.sfbay.sun.com/detail.jsf?cr=6492019
[5] http://hg.openjdk.java.net/jdk7/tl/langtools/rev/6ada6122dd4f
More information about the compiler-dev
mailing list