test/tools/javac/diags/Example.java

Sat, 18 Sep 2010 09:56:23 -0700

author
mcimadamore
date
Sat, 18 Sep 2010 09:56:23 -0700
changeset 689
77cc34d5e548
parent 636
a31c511db424
child 842
3da26790ccb7
permissions
-rw-r--r--

5088624: cannot find symbol message should be more intelligent
Summary: Resolve.java should keep track of all candidates found during a method resolution sweep to generate more meaningful diagnostics
Reviewed-by: jjg

     1 /*
     2  * Copyright (c) 2010, 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  */
    24 import com.sun.tools.javac.file.JavacFileManager;
    25 import java.io.*;
    26 import java.util.*;
    27 import java.util.regex.*;
    28 import javax.tools.Diagnostic;
    29 import javax.tools.DiagnosticCollector;
    30 import javax.tools.JavaCompiler;
    31 import javax.tools.JavaCompiler.CompilationTask;
    32 import javax.tools.JavaFileObject;
    33 import javax.tools.StandardJavaFileManager;
    34 import javax.tools.ToolProvider;
    36 // The following two classes are both used, but cannot be imported directly
    37 // import com.sun.tools.javac.Main
    38 // import com.sun.tools.javac.main.Main
    40 import com.sun.tools.javac.util.Context;
    41 import com.sun.tools.javac.util.JavacMessages;
    42 import com.sun.tools.javac.util.JCDiagnostic;
    43 import java.net.URL;
    44 import java.net.URLClassLoader;
    45 import javax.annotation.processing.Processor;
    47 /**
    48  * Class to handle example code designed to illustrate javac diagnostic messages.
    49  */
    50 class Example implements Comparable<Example> {
    51     /* Create an Example from the files found at path.
    52      * The head of the file, up to the first Java code, is scanned
    53      * for information about the test, such as what resource keys it
    54      * generates when run, what options are required to run it, and so on.
    55      */
    56     Example(File file) {
    57         this.file = file;
    58         declaredKeys = new TreeSet<String>();
    59         srcFiles = new ArrayList<File>();
    60         procFiles = new ArrayList<File>();
    61         supportFiles = new ArrayList<File>();
    62         srcPathFiles = new ArrayList<File>();
    64         findFiles(file, srcFiles);
    65         for (File f: srcFiles) {
    66             parse(f);
    67         }
    69         if (infoFile == null)
    70             throw new Error("Example " + file + " has no info file");
    71     }
    73     private void findFiles(File f, List<File> files) {
    74         if (f.isDirectory()) {
    75             for (File c: f.listFiles()) {
    76                 if (files == srcFiles && c.getName().equals("processors"))
    77                     findFiles(c, procFiles);
    78                 else if (files == srcFiles && c.getName().equals("sourcepath")) {
    79                     srcPathDir = c;
    80                     findFiles(c, srcPathFiles);
    81                 } else if (files == srcFiles && c.getName().equals("support"))
    82                     findFiles(c, supportFiles);
    83                 else
    84                     findFiles(c, files);
    85             }
    86         } else if (f.isFile() && f.getName().endsWith(".java")) {
    87             files.add(f);
    88         }
    89     }
    91     private void parse(File f) {
    92         Pattern keyPat = Pattern.compile(" *// *key: *([^ ]+) *");
    93         Pattern optPat = Pattern.compile(" *// *options: *(.*)");
    94         Pattern runPat = Pattern.compile(" *// *run: *(.*)");
    95         Pattern javaPat = Pattern.compile(" *@?[A-Za-z].*");
    96         try {
    97             String[] lines = read(f).split("[\r\n]+");
    98             for (String line: lines) {
    99                 Matcher keyMatch = keyPat.matcher(line);
   100                 if (keyMatch.matches()) {
   101                     foundInfo(f);
   102                     declaredKeys.add(keyMatch.group(1));
   103                     continue;
   104                 }
   105                 Matcher optMatch = optPat.matcher(line);
   106                 if (optMatch.matches()) {
   107                     foundInfo(f);
   108                     options = Arrays.asList(optMatch.group(1).trim().split(" +"));
   109                     continue;
   110                 }
   111                 Matcher runMatch = runPat.matcher(line);
   112                 if (runMatch.matches()) {
   113                     foundInfo(f);
   114                     runOpts = Arrays.asList(runMatch.group(1).trim().split(" +"));
   115                 }
   116                 if (javaPat.matcher(line).matches())
   117                     break;
   118             }
   119         } catch (IOException e) {
   120             throw new Error(e);
   121         }
   122     }
   124     private void foundInfo(File file) {
   125         if (infoFile != null && !infoFile.equals(file))
   126             throw new Error("multiple info files found: " + infoFile + ", " + file);
   127         infoFile = file;
   128     }
   130     String getName() {
   131         return file.getName();
   132     }
   134     /**
   135      * Get the set of resource keys that this test declares it will generate
   136      * when it is run.
   137      */
   138     Set<String> getDeclaredKeys() {
   139         return declaredKeys;
   140     }
   142     /**
   143      * Get the set of resource keys that this test generates when it is run.
   144      * The test will be run if it has not already been run.
   145      */
   146     Set<String> getActualKeys() {
   147         if (actualKeys == null)
   148             actualKeys = run(false);
   149         return actualKeys;
   150     }
   152     /**
   153      * Run the test.  Information in the test header is used to determine
   154      * how to run the test.
   155      */
   156     void run(PrintWriter out, boolean raw, boolean verbose) {
   157         if (out == null)
   158             throw new NullPointerException();
   159         try {
   160             run(out, null, raw, verbose);
   161         } catch (IOException e) {
   162             e.printStackTrace(out);
   163         }
   164     }
   166     Set<String> run(boolean verbose) {
   167         Set<String> keys = new TreeSet<String>();
   168         try {
   169             run(null, keys, true, verbose);
   170         } catch (IOException e) {
   171             e.printStackTrace();
   172         }
   173         return keys;
   174     }
   176     /**
   177      * Run the test.  Information in the test header is used to determine
   178      * how to run the test.
   179      */
   180     private void run(PrintWriter out, Set<String> keys, boolean raw, boolean verbose)
   181             throws IOException {
   182         ClassLoader loader = getClass().getClassLoader();
   183         if (supportFiles.size() > 0) {
   184             File supportDir = new File(tempDir, "support");
   185             supportDir.mkdirs();
   186             clean(supportDir);
   187             List<String> sOpts = Arrays.asList("-d", supportDir.getPath());
   188             new Jsr199Compiler(verbose).run(null, null, false, sOpts, procFiles);
   189             URLClassLoader ucl =
   190                     new URLClassLoader(new URL[] { supportDir.toURI().toURL() }, loader);
   191             loader = ucl;
   192         }
   194         File classesDir = new File(tempDir, "classes");
   195         classesDir.mkdirs();
   196         clean(classesDir);
   198         List<String> opts = new ArrayList<String>();
   199         opts.add("-d");
   200         opts.add(classesDir.getPath());
   201         if (options != null)
   202             opts.addAll(options);
   204         if (procFiles.size() > 0) {
   205             List<String> pOpts = Arrays.asList("-d", classesDir.getPath());
   206             new Jsr199Compiler(verbose).run(null, null, false, pOpts, procFiles);
   207             opts.add("-classpath"); // avoid using -processorpath for now
   208             opts.add(classesDir.getPath());
   209             createAnnotationServicesFile(classesDir, procFiles);
   210         }
   212         if (srcPathDir != null) {
   213             opts.add("-sourcepath");
   214             opts.add(srcPathDir.getPath());
   215         }
   217         try {
   218             Compiler c = Compiler.getCompiler(runOpts, verbose);
   219             c.run(out, keys, raw, opts, srcFiles);
   220         } catch (IllegalArgumentException e) {
   221             if (out != null) {
   222                 out.println("Invalid value for run tag: " + runOpts);
   223             }
   224         }
   225     }
   227     void createAnnotationServicesFile(File dir, List<File> procFiles) throws IOException {
   228         File servicesDir = new File(new File(dir, "META-INF"), "services");
   229         servicesDir.mkdirs();
   230         File annoServices = new File(servicesDir, Processor.class.getName());
   231         Writer out = new FileWriter(annoServices);
   232         try {
   233             for (File f: procFiles) {
   234                 out.write(f.getName().toString().replace(".java", ""));
   235             }
   236         } finally {
   237             out.close();
   238         }
   239     }
   241     @Override
   242     public int compareTo(Example e) {
   243         return file.compareTo(e.file);
   244     }
   246     @Override
   247     public String toString() {
   248         return file.getPath();
   249     }
   251     /**
   252      * Read the contents of a file.
   253      */
   254     private String read(File f) throws IOException {
   255         byte[] bytes = new byte[(int) f.length()];
   256         DataInputStream in = new DataInputStream(new FileInputStream(f));
   257         try {
   258             in.readFully(bytes);
   259         } finally {
   260             in.close();
   261         }
   262         return new String(bytes);
   263     }
   265     /**
   266      * Clean the contents of a directory.
   267      */
   268     boolean clean(File dir) {
   269         boolean ok = true;
   270         for (File f: dir.listFiles()) {
   271             if (f.isDirectory())
   272                 ok &= clean(f);
   273             ok &= f.delete();
   274         }
   275         return ok;
   276     }
   278     File file;
   279     List<File> srcFiles;
   280     List<File> procFiles;
   281     File srcPathDir;
   282     List<File> srcPathFiles;
   283     List<File> supportFiles;
   284     File infoFile;
   285     private List<String> runOpts;
   286     private List<String> options;
   287     private Set<String> actualKeys;
   288     private Set<String> declaredKeys;
   290     static File tempDir = new File(System.getProperty("java.io.tmpdir"));
   291     static void setTempDir(File tempDir) {
   292         Example.tempDir = tempDir;
   293     }
   295     abstract static class Compiler {
   296         static Compiler getCompiler(List<String> opts, boolean verbose) {
   297             String first;
   298             String[] rest;
   299             if (opts == null || opts.size() == 0) {
   300                 first = null;
   301                 rest = new String[0];
   302             } else {
   303                 first = opts.get(0);
   304                 rest = opts.subList(1, opts.size()).toArray(new String[opts.size() - 1]);
   305             }
   306             if (first == null || first.equals("jsr199"))
   307                 return new Jsr199Compiler(verbose, rest);
   308             else if (first.equals("simple"))
   309                 return new SimpleCompiler(verbose);
   310             else if (first.equals("backdoor"))
   311                 return new BackdoorCompiler(verbose);
   312             else
   313                 throw new IllegalArgumentException(first);
   314         }
   316         protected Compiler(boolean verbose) {
   317             this.verbose = verbose;
   318         }
   320         abstract boolean run(PrintWriter out, Set<String> keys, boolean raw,
   321                 List<String> opts,  List<File> files);
   323         void setSupportClassLoader(ClassLoader cl) {
   324             loader = cl;
   325         }
   327         protected ClassLoader loader;
   328         protected boolean verbose;
   329     }
   331     /**
   332      * Compile using the JSR 199 API.  The diagnostics generated are
   333      * scanned for resource keys.   Not all diagnostic keys are generated
   334      * via the JSR 199 API -- for example, rich diagnostics are not directly
   335      * accessible, and some diagnostics generated by the file manager may
   336      * not be generated (for example, the JSR 199 file manager does not see
   337      * -Xlint:path).
   338      */
   339     static class Jsr199Compiler extends Compiler {
   340         List<String> fmOpts;
   342         Jsr199Compiler(boolean verbose, String... args) {
   343             super(verbose);
   344             for (int i = 0; i < args.length; i++) {
   345                 String arg = args[i];
   346                 if (arg.equals("-filemanager") && (i + 1 < args.length)) {
   347                     fmOpts = Arrays.asList(args[++i].split(","));
   348                 } else
   349                     throw new IllegalArgumentException(arg);
   350             }
   351         }
   353         @Override
   354         boolean run(PrintWriter out, Set<String> keys, boolean raw, List<String> opts, List<File> files) {
   355             if (out != null && keys != null)
   356                 throw new IllegalArgumentException();
   358             if (verbose)
   359                 System.err.println("run_jsr199: " + opts + " " + files);
   361             DiagnosticCollector<JavaFileObject> dc = null;
   362             if (keys != null)
   363                 dc = new DiagnosticCollector<JavaFileObject>();
   365             if (raw) {
   366                 List<String> newOpts = new ArrayList<String>();
   367                 newOpts.add("-XDrawDiagnostics");
   368                 newOpts.addAll(opts);
   369                 opts = newOpts;
   370             }
   372             JavaCompiler c = ToolProvider.getSystemJavaCompiler();
   374             StandardJavaFileManager fm = c.getStandardFileManager(dc, null, null);
   375             if (fmOpts != null)
   376                 fm = new FileManager(fm, fmOpts);
   378             Iterable<? extends JavaFileObject> fos = fm.getJavaFileObjectsFromFiles(files);
   380             CompilationTask t = c.getTask(out, fm, dc, opts, null, fos);
   381             Boolean ok = t.call();
   383             if (keys != null) {
   384                 for (Diagnostic<? extends JavaFileObject> d: dc.getDiagnostics()) {
   385                     scanForKeys((JCDiagnostic) d, keys);
   386                 }
   387             }
   389             return ok;
   390         }
   392         /**
   393          * Scan a diagnostic for resource keys.  This will not detect additional
   394          * sub diagnostics that might be generated by a rich diagnostic formatter.
   395          */
   396         private static void scanForKeys(JCDiagnostic d, Set<String> keys) {
   397             keys.add(d.getCode());
   398             for (Object o: d.getArgs()) {
   399                 if (o instanceof JCDiagnostic) {
   400                     scanForKeys((JCDiagnostic) o, keys);
   401                 }
   402             }
   403             for (JCDiagnostic sd: d.getSubdiagnostics())
   404                 scanForKeys(sd, keys);
   405         }
   406     }
   408     /**
   409      * Run the test using the standard simple entry point.
   410      */
   411     static class SimpleCompiler extends Compiler {
   412         SimpleCompiler(boolean verbose) {
   413             super(verbose);
   414         }
   416         @Override
   417         boolean run(PrintWriter out, Set<String> keys, boolean raw, List<String> opts, List<File> files) {
   418             if (out != null && keys != null)
   419                 throw new IllegalArgumentException();
   421             if (verbose)
   422                 System.err.println("run_simple: " + opts + " " + files);
   424             List<String> args = new ArrayList<String>();
   426             if (keys != null || raw)
   427                 args.add("-XDrawDiagnostics");
   429             args.addAll(opts);
   430             for (File f: files)
   431                 args.add(f.getPath());
   433             StringWriter sw = null;
   434             PrintWriter pw;
   435             if (keys != null) {
   436                 sw = new StringWriter();
   437                 pw = new PrintWriter(sw);
   438             } else
   439                 pw = out;
   441             int rc = com.sun.tools.javac.Main.compile(args.toArray(new String[args.size()]), pw);
   443             if (keys != null) {
   444                 pw.close();
   445                 scanForKeys(sw.toString(), keys);
   446             }
   448             return (rc == 0);
   449         }
   451         private static void scanForKeys(String text, Set<String> keys) {
   452             StringTokenizer st = new StringTokenizer(text, " ,\r\n():");
   453             while (st.hasMoreElements()) {
   454                 String t = st.nextToken();
   455                 if (t.startsWith("compiler."))
   456                     keys.add(t);
   457             }
   458         }
   459     }
   461     static class BackdoorCompiler extends Compiler {
   462         BackdoorCompiler(boolean verbose) {
   463             super(verbose);
   464         }
   466         @Override
   467         boolean run(PrintWriter out, Set<String> keys, boolean raw, List<String> opts, List<File> files) {
   468             if (out != null && keys != null)
   469                 throw new IllegalArgumentException();
   471             if (verbose)
   472                 System.err.println("run_simple: " + opts + " " + files);
   474             List<String> args = new ArrayList<String>(opts);
   476             if (out != null && raw)
   477                 args.add("-XDrawDiagnostics");
   479             args.addAll(opts);
   480             for (File f: files)
   481                 args.add(f.getPath());
   483             StringWriter sw = null;
   484             PrintWriter pw;
   485             if (keys != null) {
   486                 sw = new StringWriter();
   487                 pw = new PrintWriter(sw);
   488             } else
   489                 pw = out;
   491             Context c = new Context();
   492             JavacFileManager.preRegister(c); // can't create it until Log has been set up
   493             MessageTracker.preRegister(c, keys);
   494             com.sun.tools.javac.main.Main m = new com.sun.tools.javac.main.Main("javac", pw);
   495             int rc = m.compile(args.toArray(new String[args.size()]), c);
   497             if (keys != null) {
   498                 pw.close();
   499             }
   501             return (rc == 0);
   502         }
   504         static class MessageTracker extends JavacMessages {
   506             MessageTracker(Context context) {
   507                 super(context);
   508             }
   510             static void preRegister(final Context c, final Set<String> keys) {
   511                 if (keys != null) {
   512                     c.put(JavacMessages.messagesKey, new Context.Factory<JavacMessages>() {
   513                         public JavacMessages make() {
   514                             return new MessageTracker(c) {
   515                                 @Override
   516                                 public String getLocalizedString(Locale l, String key, Object... args) {
   517                                     keys.add(key);
   518                                     return super.getLocalizedString(l, key, args);
   519                                 }
   520                             };
   521                         }
   522                     });
   523                 }
   524             }
   525         }
   527     }
   528 }

mercurial