1/*
2 * Copyright (c) 2010, 2015, 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 6403465
27 * @summary javac should defer diagnostics until it can be determined they are persistent
28 * @modules jdk.compiler/com.sun.tools.javac.api
29 *          jdk.compiler/com.sun.tools.javac.file
30 *          jdk.compiler/com.sun.tools.javac.util
31 */
32
33import java.io.*;
34import java.util.*;
35import javax.annotation.processing.*;
36import javax.lang.model.*;
37import javax.lang.model.element.TypeElement;
38import javax.tools.*;
39
40import com.sun.source.util.JavacTask;
41import com.sun.tools.javac.api.ClientCodeWrapper;
42import com.sun.tools.javac.api.JavacTool;
43import com.sun.tools.javac.util.JCDiagnostic;
44
45import static com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag.*;
46
47
48public class TestSuppression {
49    public static void main(String... args) throws Exception {
50        new TestSuppression().run(args);
51    }
52
53    enum WarningKind { NO, YES };
54
55    String[] cases = {
56        // missing class C
57        "class X { C c; }",
58        "class X { C foo() { return null; } }",
59        "class X { void foo(C c) { } }",
60        "class X extends C { }",
61        "class X<T extends C> { }",
62        // missing interface I
63        "class X implements I { }",
64        "interface X extends I { }",
65        // missing exception E
66        "class X { void m() throws E { } }",
67        // missing method m
68        "class X extends C { int i = m(); }",
69        // missing field f
70        "class X extends C { int i = f; }"
71    };
72
73    void run(String... args) throws Exception {
74        for (String c: cases) {
75            for (WarningKind wk: WarningKind.values()) {
76                for (int g = 1; g <= 3; g++) {
77                    try {
78                        test(c, wk, g);
79                    } catch (Throwable t) {
80                        error("caught: " + t);
81                    }
82                    if (errors > 0) throw new AssertionError();
83                }
84            }
85        }
86
87        System.err.println(count + " test cases");
88
89        if (errors > 0)
90            throw new Exception(errors + " errors occurred");
91    }
92
93    void test(String src, WarningKind wk, int gen) throws Exception {
94        count++;
95        System.err.println("Test " + count + ": wk:" + wk + " gen:" + gen + " src:" +src);
96
97        File testDir = new File("test" + count);
98        File srcDir = createDir(testDir, "src");
99        File gensrcDir = createDir(testDir, "gensrc");
100        File classesDir = createDir(testDir, "classes");
101
102        File x = writeFile(new File(srcDir, "X.java"), src);
103
104        DiagListener dl = new DiagListener();
105        JavacTool tool = JavacTool.create();
106        try (StandardJavaFileManager fm = tool.getStandardFileManager(dl, null, null)) {
107            fm.setLocation(StandardLocation.CLASS_PATH,
108                    Arrays.asList(classesDir, new File(System.getProperty("test.classes"))));
109            fm.setLocation(StandardLocation.CLASS_OUTPUT, Collections.singleton(classesDir));
110            fm.setLocation(StandardLocation.SOURCE_OUTPUT, Collections.singleton(gensrcDir));
111            List<String> args = new ArrayList<String>();
112    //        args.add("-XprintProcessorInfo");
113            args.add("-XprintRounds");
114            args.add("-Agen=" + gen);
115            if (wk == WarningKind.YES)
116                args.add("-Xlint:serial");
117            Iterable<? extends JavaFileObject> files = fm.getJavaFileObjects(x);
118
119            StringWriter sw = new StringWriter();
120            PrintWriter pw = new PrintWriter(sw);
121            JavacTask task = tool.getTask(pw, fm, dl, args, null, files);
122            task.setProcessors(Arrays.asList(new AnnoProc()));
123            boolean ok = task.call();
124            pw.close();
125
126            System.err.println("ok:" + ok + " diags:" + dl.counts);
127            if (sw.toString().length() > 0) {
128                System.err.println("output:\n" + sw.toString());
129            }
130
131            for (Diagnostic.Kind dk: Diagnostic.Kind.values()) {
132                Integer v = dl.counts.get(dk);
133                int found = (v == null) ? 0 : v;
134                int expect = (dk == Diagnostic.Kind.WARNING && wk == WarningKind.YES) ? gen : 0;
135                if (found != expect) {
136                    error("Unexpected value for " + dk + ": expected: " + expect + " found: " + found);
137                }
138            }
139
140            System.err.println();
141        }
142    }
143
144    File createDir(File parent, String name) {
145        File dir = new File(parent, name);
146        dir.mkdirs();
147        return dir;
148    }
149
150    File writeFile(File f, String content) throws IOException {
151        FileWriter out = new FileWriter(f);
152        try {
153            out.write(content);
154        } finally {
155            out.close();
156        }
157        return f;
158    }
159
160    <T> void add(List<T> list, T... values) {
161        for (T v: values)
162            list.add(v);
163    }
164
165    void error(String msg) {
166        System.err.println("Error: " + msg);
167        errors++;
168    }
169
170    int count;
171    int errors;
172
173    static class DiagListener implements DiagnosticListener<JavaFileObject> {
174        int total;
175        Map<Diagnostic.Kind,Integer> counts = new TreeMap<Diagnostic.Kind,Integer>();
176
177        public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
178            System.err.println((++total) + ": "
179                    + "resolveError:" + isResolveError(unwrap(diagnostic)) + "\n"
180                    + diagnostic);
181            Diagnostic.Kind dk = diagnostic.getKind();
182            Integer c = counts.get(dk);
183            counts.put(dk, (c == null ? 1 : c + 1));
184        }
185
186        private static boolean isResolveError(JCDiagnostic d) {
187            return d.isFlagSet(RESOLVE_ERROR);
188        }
189
190        private JCDiagnostic unwrap(Diagnostic<? extends JavaFileObject> diagnostic) {
191            if (diagnostic instanceof JCDiagnostic)
192                return (JCDiagnostic) diagnostic;
193            if (diagnostic instanceof ClientCodeWrapper.DiagnosticSourceUnwrapper)
194                return ((ClientCodeWrapper.DiagnosticSourceUnwrapper)diagnostic).d;
195            throw new IllegalArgumentException();
196        }
197    }
198
199    @SupportedAnnotationTypes("*")
200    @SupportedOptions("gen")
201    public static class AnnoProc extends AbstractProcessor {
202        Filer f;
203        Messager m;
204        int gen;
205
206        @Override
207        public void init(ProcessingEnvironment processingEnv) {
208            f = processingEnv.getFiler();
209            m = processingEnv.getMessager();
210            Map<String,String> options = processingEnv.getOptions();
211            gen = Integer.parseInt(options.get("gen"));
212        }
213
214        @Override
215        public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
216            round++;
217            if (round < gen)
218                writeSource("Dummy" + round, "class Dummy" + round + " extends java.util.ArrayList{ }");
219            else if (round == gen) {
220                writeSource("C", "class C { int f; int m() { return 0; } }");
221                writeSource("I", "interface I { }");
222                writeSource("E", "class E extends Exception { }");
223            }
224            return true;
225        }
226
227        @Override
228        public SourceVersion getSupportedSourceVersion() {
229            return SourceVersion.latest();
230        }
231
232        private void writeSource(String name, String text) {
233            try {
234                JavaFileObject fo = f.createSourceFile(name);
235                Writer out = fo.openWriter();
236                out.write(text);
237                out.close();
238            } catch (IOException e) {
239                m.printMessage(Diagnostic.Kind.ERROR, e.toString());
240            }
241        }
242
243        int round = 0;
244    }
245}
246