T6769027.java revision 3547:e18190929198
1/*
2 * Copyright (c) 2009, 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24/**
25 * @test
26 * @bug     6769027 8006694
27 * @summary Source line should be displayed immediately after the first diagnostic line
28 * @modules jdk.compiler/com.sun.tools.javac.api
29 *          jdk.compiler/com.sun.tools.javac.util
30 * @run main/othervm T6769027
31 */
32
33// use /othervm to avoid locale issues
34
35import java.net.URI;
36import java.util.ResourceBundle;
37import java.util.regex.Matcher;
38import javax.tools.*;
39import com.sun.tools.javac.util.*;
40
41public class T6769027 {
42
43    enum OutputKind {
44        RAW("rawDiagnostics","rawDiagnostics"),
45        BASIC("","");
46
47        String key;
48        String value;
49
50        void init(Options opts) {
51            opts.put(key, value);
52        }
53
54        OutputKind(String key, String value) {
55            this.key = key;
56            this.value = value;
57        }
58    }
59
60    enum CaretKind {
61        DEFAULT("", ""),
62        SHOW("diags.showCaret","true"),
63        HIDE("diags.showCaret","false");
64
65        String key;
66        String value;
67
68        void init(Options opts) {
69            opts.put(key, value);
70        }
71
72        CaretKind(String key, String value) {
73            this.key = key;
74            this.value = value;
75        }
76
77        boolean isEnabled() {
78            return this == DEFAULT || this == SHOW;
79        }
80    }
81
82    enum SourceLineKind {
83        DEFAULT("", ""),
84        AFTER_SUMMARY("diags.sourcePosition", "top"),
85        BOTTOM("diags.sourcePosition", "bottom");
86
87        String key;
88        String value;
89
90        void init(Options opts) {
91            opts.put(key, value);
92        }
93
94        SourceLineKind(String key, String value) {
95            this.key = key;
96            this.value = value;
97        }
98
99        boolean isAfterSummary() {
100            return this == DEFAULT || this == AFTER_SUMMARY;
101        }
102    }
103
104    enum XDiagsSource {
105        DEFAULT(""),
106        SOURCE("source"),
107        NO_SOURCE("-source");
108
109        String flag;
110
111        void init(Options opts) {
112            if (this != DEFAULT) {
113                String flags = opts.get("diags.formatterOptions");
114                flags = flags == null ? flag : flags + "," + flag;
115                opts.put("diags.formatterOptions", flags);
116            }
117        }
118
119        XDiagsSource(String flag) {
120            this.flag = flag;
121        }
122
123        String getOutput(CaretKind caretKind, IndentKind indent, OutputKind outKind) {
124            String spaces = (outKind == OutputKind.BASIC) ? indent.string : "";
125            return "\n" + spaces + "This is a source line" +
126                   (caretKind.isEnabled() ? "\n" + spaces + "     ^" : "");
127        }
128    }
129
130    enum XDiagsCompact {
131        DEFAULT(""),
132        COMPACT("short"),
133        NO_COMPACT("-short");
134
135        String flag;
136
137        void init(Options opts) {
138            if (this != DEFAULT) {
139                String flags = opts.get("diags.formatterOptions");
140                flags = flags == null ? flag : flags + "," + flag;
141                opts.put("diags.formatterOptions", flags);
142            }
143        }
144
145        XDiagsCompact(String flag) {
146            this.flag = flag;
147        }
148    }
149
150    enum ErrorKind {
151        SINGLE("single",
152            "compiler.err.single: Hello!",
153            "KXThis is a test error message Hello!"),
154        DOUBLE("double",
155            "compiler.err.double: Hello!",
156            "KXThis is a test error message.\n" +
157            "KXYThis is another line of the above error message Hello!");
158
159        String key;
160        String rawOutput;
161        String nonRawOutput;
162
163        String key() {
164            return key;
165        }
166
167        ErrorKind(String key, String rawOutput, String nonRawOutput) {
168            this.key = key;
169            this.rawOutput = rawOutput;
170            this.nonRawOutput = nonRawOutput;
171        }
172
173        String getOutput(OutputKind outKind, IndentKind summaryIndent, IndentKind detailsIndent) {
174            return outKind == OutputKind.RAW ?
175                rawOutput :
176                nonRawOutput.replace("X", summaryIndent.string).replace("Y", detailsIndent.string).replace("K", "");
177        }
178
179        String getOutput(OutputKind outKind, IndentKind summaryIndent, IndentKind detailsIndent, String indent) {
180            return outKind == OutputKind.RAW ?
181                rawOutput :
182                nonRawOutput.replace("X", summaryIndent.string).replace("Y", detailsIndent.string).replace("K", indent);
183        }
184    }
185
186    enum MultilineKind {
187        NONE(0),
188        DOUBLE(1),
189        NESTED(2),
190        DOUBLE_NESTED(3);
191
192        static String[][] rawTemplates = {
193            {"", ",{(E),(E)}", ",{(E,{(E)})}", ",{(E,{(E)}),(E,{(E)})}"}, //ENABLED
194            {"", "", "", "",""}, //DISABLED
195            {"", ",{(E)}", ",{(E,{(E)})}", ",{(E,{(E)})}"}, //LIMIT_LENGTH
196            {"", ",{(E),(E)}", ",{(E)}", ",{(E),(E)}"}, //LIMIT_DEPTH
197            {"", ",{(E)}", ",{(E)}", ",{(E)}"}}; //LIMIT_BOTH
198
199        static String[][] basicTemplates = {
200            {"", "\nE\nE", "\nE\nQ", "\nE\nQ\nE\nQ"}, //ENABLED
201            {"", "", "", "",""}, //DISABLED
202            {"", "\nE", "\nE\nQ", "\nE\nQ"}, //LIMIT_LENGTH
203            {"", "\nE\nE", "\nE", "\nE\nE"}, //LIMIT_DEPTH
204            {"", "\nE", "\nE", "\nE"}}; //LIMIT_BOTH
205
206
207        int index;
208
209        MultilineKind (int index) {
210            this.index = index;
211        }
212
213        boolean isDouble() {
214            return this == DOUBLE || this == DOUBLE_NESTED;
215        }
216
217        boolean isNested() {
218            return this == NESTED || this == DOUBLE_NESTED;
219        }
220
221        String getOutput(OutputKind outKind, ErrorKind errKind, MultilinePolicy policy,
222                IndentKind summaryIndent, IndentKind detailsIndent, IndentKind multiIndent) {
223            String constIndent = (errKind == ErrorKind.DOUBLE) ?
224                summaryIndent.string + detailsIndent.string :
225                summaryIndent.string;
226            constIndent += multiIndent.string;
227
228            String errMsg1 = errKind.getOutput(outKind, summaryIndent, detailsIndent, constIndent);
229            String errMsg2 = errKind.getOutput(outKind, summaryIndent, detailsIndent, constIndent + constIndent);
230
231            errMsg1 = errMsg1.replaceAll("compiler.err", "compiler.misc");
232            errMsg1 = errMsg1.replaceAll("error message", "subdiagnostic");
233            errMsg2 = errMsg2.replaceAll("compiler.err", "compiler.misc");
234            errMsg2 = errMsg2.replaceAll("error message", "subdiagnostic");
235
236            String template = outKind == OutputKind.RAW ?
237                rawTemplates[policy.index][index] :
238                basicTemplates[policy.index][index];
239
240            template = template.replaceAll("E", errMsg1);
241            return template.replaceAll("Q", errMsg2);
242        }
243    }
244
245    enum MultilinePolicy {
246        ENABLED(0, "diags.multilinePolicy", "enabled"),
247        DISABLED(1, "diags.multilinePolicy", "disabled"),
248        LIMIT_LENGTH(2, "diags.multilinePolicy", "limit:1:*"),
249        LIMIT_DEPTH(3, "diags.multilinePolicy", "limit:*:1"),
250        LIMIT_BOTH(4, "diags.multilinePolicy", "limit:1:1");
251
252        String name;
253        String value;
254        int index;
255
256        MultilinePolicy(int index, String name, String value) {
257            this.name = name;
258            this.value = value;
259            this.index = index;
260        }
261
262        void init(Options options) {
263            options.put(name, value);
264        }
265    }
266
267    enum PositionKind {
268        NOPOS(Position.NOPOS, "- ", "error: "),
269        POS(5, "Test.java:1:6: ", "/Test.java:1: error: ");
270
271        int pos;
272        String rawOutput;
273        String nonRawOutput;
274
275        PositionKind(int pos, String rawOutput, String nonRawOutput) {
276            this.pos = pos;
277            this.rawOutput = rawOutput;
278            this.nonRawOutput = nonRawOutput;
279        }
280
281        JCDiagnostic.DiagnosticPosition pos() {
282            return new JCDiagnostic.SimpleDiagnosticPosition(pos);
283        }
284
285        String getOutput(OutputKind outputKind) {
286            return outputKind == OutputKind.RAW ?
287                rawOutput :
288                nonRawOutput;
289        }
290    }
291
292    static class MyFileObject extends SimpleJavaFileObject {
293        private String text;
294        public MyFileObject(String text) {
295            super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
296            this.text = text;
297        }
298        @Override
299        public CharSequence getCharContent(boolean ignoreEncodingErrors) {
300            return text;
301        }
302    }
303
304    enum IndentKind {
305        NONE(""),
306        CUSTOM("   ");
307
308        String string;
309
310        IndentKind(String indent) {
311            string = indent;
312        }
313    }
314
315    class MyLog extends Log {
316        MyLog(Context ctx) {
317            super(ctx);
318        }
319
320        @Override
321        protected boolean shouldReport(JavaFileObject jfo, int pos) {
322            return true;
323        }
324    }
325
326    OutputKind outputKind;
327    ErrorKind errorKind;
328    MultilineKind multiKind;
329    MultilinePolicy multiPolicy;
330    PositionKind posKind;
331    XDiagsSource xdiagsSource;
332    XDiagsCompact xdiagsCompact;
333    CaretKind caretKind;
334    SourceLineKind sourceLineKind;
335    IndentKind summaryIndent;
336    IndentKind detailsIndent;
337    IndentKind sourceIndent;
338    IndentKind subdiagsIndent;
339
340    T6769027(OutputKind outputKind, ErrorKind errorKind, MultilineKind multiKind,
341            MultilinePolicy multiPolicy, PositionKind posKind, XDiagsSource xdiagsSource,
342            XDiagsCompact xdiagsCompact, CaretKind caretKind, SourceLineKind sourceLineKind,
343            IndentKind summaryIndent, IndentKind detailsIndent, IndentKind sourceIndent,
344            IndentKind subdiagsIndent) {
345        this.outputKind = outputKind;
346        this.errorKind = errorKind;
347        this.multiKind = multiKind;
348        this.multiPolicy = multiPolicy;
349        this.posKind = posKind;
350        this.xdiagsSource = xdiagsSource;
351        this.xdiagsCompact = xdiagsCompact;
352        this.caretKind = caretKind;
353        this.sourceLineKind = sourceLineKind;
354        this.summaryIndent = summaryIndent;
355        this.detailsIndent = detailsIndent;
356        this.sourceIndent = sourceIndent;
357        this.subdiagsIndent = subdiagsIndent;
358    }
359
360    public void run() {
361        Context ctx = new Context();
362        Options options = Options.instance(ctx);
363        outputKind.init(options);
364        multiPolicy.init(options);
365        xdiagsSource.init(options);
366        xdiagsCompact.init(options);
367        caretKind.init(options);
368        sourceLineKind.init(options);
369        String indentString = "";
370        indentString = (summaryIndent == IndentKind.CUSTOM) ? "3" : "0";
371        indentString += (detailsIndent == IndentKind.CUSTOM) ? "|3" : "|0";
372        indentString += (sourceIndent == IndentKind.CUSTOM) ? "|3" : "|0";
373        indentString += (subdiagsIndent == IndentKind.CUSTOM) ? "|3" : "|0";
374        options.put("diags.indent", indentString);
375        MyLog log = new MyLog(ctx);
376        JavacMessages messages = JavacMessages.instance(ctx);
377        messages.add(locale -> ResourceBundle.getBundle("tester", locale));
378        JCDiagnostic.Factory diags = JCDiagnostic.Factory.instance(ctx);
379        log.useSource(new MyFileObject("This is a source line"));
380        JCDiagnostic d = diags.error(null, log.currentSource(),
381            posKind.pos(),
382            errorKind.key(), "Hello!");
383        if (multiKind != MultilineKind.NONE) {
384            JCDiagnostic sub = diags.fragment(errorKind.key(), "Hello!");
385            if (multiKind.isNested())
386                sub = new JCDiagnostic.MultilineDiagnostic(sub, List.of(sub));
387            List<JCDiagnostic> subdiags = multiKind.isDouble() ?
388                List.of(sub, sub) :
389                List.of(sub);
390            d = new JCDiagnostic.MultilineDiagnostic(d, subdiags);
391        }
392        String diag = log.getDiagnosticFormatter().format(d, messages.getCurrentLocale());
393        checkOutput(diag);
394    }
395
396    public static void main(String[] args) throws Exception {
397        for (OutputKind outputKind : OutputKind.values()) {
398            for (ErrorKind errKind : ErrorKind.values()) {
399                for (MultilineKind multiKind : MultilineKind.values()) {
400                    for (MultilinePolicy multiPolicy : MultilinePolicy.values()) {
401                        for (PositionKind posKind : PositionKind.values()) {
402                            for (XDiagsSource xdiagsSource : XDiagsSource.values()) {
403                                for (XDiagsCompact xdiagsCompact : XDiagsCompact.values()) {
404                                    for (CaretKind caretKind : CaretKind.values()) {
405                                        for (SourceLineKind sourceLineKind : SourceLineKind.values()) {
406                                            for (IndentKind summaryIndent : IndentKind.values()) {
407                                                for (IndentKind detailsIndent : IndentKind.values()) {
408                                                    for (IndentKind sourceIndent : IndentKind.values()) {
409                                                        for (IndentKind subdiagsIndent : IndentKind.values()) {
410                                                            new T6769027(outputKind,
411                                                                errKind,
412                                                                multiKind,
413                                                                multiPolicy,
414                                                                posKind,
415                                                                xdiagsSource,
416                                                                xdiagsCompact,
417                                                                caretKind,
418                                                                sourceLineKind,
419                                                                summaryIndent,
420                                                                detailsIndent,
421                                                                sourceIndent,
422                                                                subdiagsIndent).run();
423                                                        }
424                                                    }
425                                                }
426                                            }
427                                        }
428                                    }
429                                }
430                            }
431                        }
432                    }
433                }
434            }
435        }
436    }
437
438    void printInfo(String msg, String errorLine) {
439        String sep = "*********************************************************";
440        String desc = "raw=" + outputKind + " pos=" + posKind + " key=" + errorKind.key() +
441                " multiline=" + multiKind +" multiPolicy=" + multiPolicy.value +
442                " diags= " + java.util.Arrays.asList(xdiagsSource.flag, xdiagsCompact.flag) +
443                " caret=" + caretKind + " sourcePosition=" + sourceLineKind +
444                " summaryIndent=" + summaryIndent + " detailsIndent=" + detailsIndent +
445                " sourceIndent=" + sourceIndent + " subdiagsIndent=" + subdiagsIndent;
446        System.err.println(sep);
447        System.err.println(desc);
448        System.err.println(sep);
449        System.err.println(msg);
450        System.err.println("Diagnostic formatting problem - expected diagnostic...\n" + errorLine);
451    }
452
453    void checkOutput(String msg) {
454        boolean shouldPrintSource = posKind == PositionKind.POS &&
455                xdiagsSource != XDiagsSource.NO_SOURCE &&
456                (xdiagsSource == XDiagsSource.SOURCE ||
457                outputKind == OutputKind.BASIC);
458        String errorLine = posKind.getOutput(outputKind) +
459                errorKind.getOutput(outputKind, summaryIndent, detailsIndent);
460        if (xdiagsCompact != XDiagsCompact.COMPACT)
461            errorLine += multiKind.getOutput(outputKind, errorKind, multiPolicy,
462                    summaryIndent, detailsIndent, subdiagsIndent);
463        String[] lines = errorLine.split("\n");
464        if (xdiagsCompact == XDiagsCompact.COMPACT) {
465            errorLine = lines[0];
466            lines = new String[] {errorLine};
467        }
468        if (shouldPrintSource) {
469            if (sourceLineKind.isAfterSummary()) {
470                String sep = "\n";
471                if (lines.length == 1) {
472                    errorLine += "\n";
473                    sep = "";
474                }
475                errorLine = errorLine.replaceFirst("\n",
476                        Matcher.quoteReplacement(xdiagsSource.getOutput(caretKind, sourceIndent, outputKind) + sep));
477            }
478            else
479                errorLine += xdiagsSource.getOutput(caretKind, sourceIndent, outputKind);
480        }
481
482        if (!msg.equals(errorLine)) {
483            printInfo(msg, errorLine);
484            throw new AssertionError("errors were found");
485        }
486    }
487
488}
489