test/tools/javac/resolve/ResolveHarness.java

Thu, 21 Feb 2013 15:26:46 +0000

author
mcimadamore
date
Thu, 21 Feb 2013 15:26:46 +0000
changeset 1599
9f0ec00514b6
parent 1466
b52a38d4536c
child 1779
97a9b4b3e63a
permissions
-rw-r--r--

8007461: Regression: bad overload resolution when inner class and outer class have method with same name
Summary: Fix regression in varargs method resolution introduced by bad refactoring
Reviewed-by: jjg

     1 /*
     2  * Copyright (c) 2011, 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 /*
    25  * @test
    26  * @bug 7098660
    27  * @summary Write better overload resolution/inference tests
    28  * @library /tools/javac/lib
    29  * @build JavacTestingAbstractProcessor ResolveHarness
    30  * @run main ResolveHarness
    31  */
    33 import com.sun.source.util.JavacTask;
    34 import com.sun.tools.javac.api.ClientCodeWrapper.DiagnosticSourceUnwrapper;
    35 import com.sun.tools.javac.code.Type.MethodType;
    36 import com.sun.tools.javac.util.JCDiagnostic;
    38 import java.io.File;
    39 import java.util.Set;
    40 import java.util.Arrays;
    41 import java.util.ArrayList;
    42 import java.util.Collections;
    43 import java.util.HashMap;
    44 import java.util.HashSet;
    45 import java.util.List;
    46 import java.util.Map;
    48 import javax.annotation.processing.AbstractProcessor;
    49 import javax.annotation.processing.RoundEnvironment;
    50 import javax.annotation.processing.SupportedAnnotationTypes;
    51 import javax.lang.model.element.Element;
    52 import javax.lang.model.element.TypeElement;
    53 import javax.tools.Diagnostic;
    54 import javax.tools.Diagnostic.Kind;
    55 import javax.tools.DiagnosticListener;
    56 import javax.tools.JavaCompiler;
    57 import javax.tools.JavaFileObject;
    58 import javax.tools.StandardJavaFileManager;
    59 import javax.tools.ToolProvider;
    61 import static javax.tools.StandardLocation.*;
    63 public class ResolveHarness implements javax.tools.DiagnosticListener<JavaFileObject> {
    65     static int nerrors = 0;
    67     static final JavaCompiler comp = ToolProvider.getSystemJavaCompiler();
    68     static final StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null);
    70     public static void main(String[] args) throws Exception {
    71         fm.setLocation(SOURCE_PATH,
    72                 Arrays.asList(new File(System.getProperty("test.src"), "tests")));
    73         for (JavaFileObject jfo : fm.list(SOURCE_PATH, "", Collections.singleton(JavaFileObject.Kind.SOURCE), true)) {
    74             new ResolveHarness(jfo).check();
    75         }
    76         if (nerrors > 0) {
    77             throw new AssertionError("Errors were found");
    78         }
    79     }
    82     JavaFileObject jfo;
    83     DiagnosticProcessor[] diagProcessors;
    84     Map<ElementKey, Candidate> candidatesMap = new HashMap<ElementKey, Candidate>();
    85     Set<String> declaredKeys = new HashSet<>();
    86     List<Diagnostic<? extends JavaFileObject>> diags = new ArrayList<>();
    87     List<ElementKey> seenCandidates = new ArrayList<>();
    89     protected ResolveHarness(JavaFileObject jfo) {
    90         this.jfo = jfo;
    91         this.diagProcessors = new DiagnosticProcessor[] {
    92             new VerboseResolutionNoteProcessor(),
    93             new VerboseDeferredInferenceNoteProcessor(),
    94             new ErrorProcessor()
    95         };
    96     }
    98     protected void check() throws Exception {
    99         String[] options = {
   100             "-XDshouldStopPolicy=ATTR",
   101             "-XDverboseResolution=success,failure,applicable,inapplicable,deferred-inference"
   102         };
   104         AbstractProcessor[] processors = { new ResolveCandidateFinder(), null };
   106         @SuppressWarnings("unchecked")
   107         DiagnosticListener<? super JavaFileObject>[] diagListeners =
   108                 new DiagnosticListener[] { new DiagnosticHandler(false), new DiagnosticHandler(true) };
   110         for (int i = 0 ; i < options.length ; i ++) {
   111             JavacTask ct = (JavacTask)comp.getTask(null, fm, diagListeners[i],
   112                     Arrays.asList(options[i]), null, Arrays.asList(jfo));
   113             if (processors[i] != null) {
   114                 ct.setProcessors(Collections.singleton(processors[i]));
   115             }
   116             ct.analyze();
   117         }
   119         //check diags
   120         for (Diagnostic<? extends JavaFileObject> diag : diags) {
   121             for (DiagnosticProcessor proc : diagProcessors) {
   122                 if (proc.matches(diag)) {
   123                     proc.process(diag);
   124                     break;
   125                 }
   126             }
   127         }
   128         //check all candidates have been used up
   129         for (Map.Entry<ElementKey, Candidate> entry : candidatesMap.entrySet()) {
   130             if (!seenCandidates.contains(entry.getKey())) {
   131                 error("Redundant @Candidate annotation on method " + entry.getKey().elem);
   132             }
   133         }
   134     }
   136     public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
   137         diags.add(diagnostic);
   138     }
   140     Candidate getCandidateAtPos(Element methodSym, long line, long col) {
   141         Candidate c = candidatesMap.get(new ElementKey(methodSym));
   142         if (c != null) {
   143             Pos pos = c.pos();
   144             if (!pos.userDefined() ||
   145                     (pos.line() == line && pos.col() == col)) {
   146                 seenCandidates.add(new ElementKey(methodSym));
   147                 return c;
   148             }
   149         } else {
   150             error("Missing @Candidate annotation on method " + methodSym);
   151         }
   152         return null;
   153     }
   155     void checkSig(Candidate c, Element methodSym, MethodType mtype) {
   156         if (c.sig().length() > 0 && !c.sig().equals(mtype.toString())) {
   157             error("Inferred type mismatch for method: " + methodSym);
   158         }
   159     }
   161     protected void error(String msg) {
   162         nerrors++;
   163         System.err.printf("Error occurred while checking file: %s\nreason: %s\n", jfo.getName(), msg);
   164     }
   166     /**
   167      * Base class for diagnostic processor. It provides methods for matching and
   168      * processing a given diagnostic object (overridden by subclasses).
   169      */
   170     abstract class DiagnosticProcessor {
   172         List<String> codes;
   173         Diagnostic.Kind kind;
   175         public DiagnosticProcessor(Kind kind, String... codes) {
   176             this.codes = Arrays.asList(codes);
   177             this.kind = kind;
   178         }
   180         abstract void process(Diagnostic<? extends JavaFileObject> diagnostic);
   182         boolean matches(Diagnostic<? extends JavaFileObject> diagnostic) {
   183             return (codes.isEmpty() || codes.contains(diagnostic.getCode())) &&
   184                     diagnostic.getKind() == kind;
   185         }
   187         JCDiagnostic asJCDiagnostic(Diagnostic<? extends JavaFileObject> diagnostic) {
   188             if (diagnostic instanceof JCDiagnostic) {
   189                 return (JCDiagnostic)diagnostic;
   190             } else if (diagnostic instanceof DiagnosticSourceUnwrapper) {
   191                 return ((DiagnosticSourceUnwrapper)diagnostic).d;
   192             } else {
   193                 throw new AssertionError("Cannot convert diagnostic to JCDiagnostic: " + diagnostic.getClass().getName());
   194             }
   195         }
   197         List<JCDiagnostic> subDiagnostics(Diagnostic<? extends JavaFileObject> diagnostic) {
   198             JCDiagnostic diag = asJCDiagnostic(diagnostic);
   199             if (diag instanceof JCDiagnostic.MultilineDiagnostic) {
   200                 return ((JCDiagnostic.MultilineDiagnostic)diag).getSubdiagnostics();
   201             } else {
   202                 throw new AssertionError("Cannot extract subdiagnostics: " + diag.getClass().getName());
   203             }
   204         }
   205     }
   207     /**
   208      * Processor for verbose resolution notes generated by javac. The processor
   209      * checks that the diagnostic is associated with a method declared by
   210      * a class annotated with the special @TraceResolve marker annotation. If
   211      * that's the case, all subdiagnostics (one for each resolution candidate)
   212      * are checked against the corresponding @Candidate annotations, using
   213      * a VerboseCandidateSubdiagProcessor.
   214      */
   215     class VerboseResolutionNoteProcessor extends DiagnosticProcessor {
   217         VerboseResolutionNoteProcessor() {
   218             super(Kind.NOTE,
   219                     "compiler.note.verbose.resolve.multi",
   220                     "compiler.note.verbose.resolve.multi.1");
   221         }
   223         @Override
   224         void process(Diagnostic<? extends JavaFileObject> diagnostic) {
   225             Element siteSym = getSiteSym(diagnostic);
   226             if (siteSym.getAnnotation(TraceResolve.class) == null) {
   227                 return;
   228             }
   229             int candidateIdx = 0;
   230             for (JCDiagnostic d : subDiagnostics(diagnostic)) {
   231                 boolean isMostSpecific = candidateIdx++ == mostSpecific(diagnostic);
   232                 VerboseCandidateSubdiagProcessor subProc =
   233                         new VerboseCandidateSubdiagProcessor(isMostSpecific, phase(diagnostic), success(diagnostic));
   234                 if (subProc.matches(d)) {
   235                     subProc.process(d);
   236                 } else {
   237                     throw new AssertionError("Bad subdiagnostic: " + d.getCode());
   238                 }
   239             }
   240         }
   242         Element getSiteSym(Diagnostic<? extends JavaFileObject> diagnostic) {
   243             return (Element)asJCDiagnostic(diagnostic).getArgs()[1];
   244         }
   246         int mostSpecific(Diagnostic<? extends JavaFileObject> diagnostic) {
   247             return success(diagnostic) ?
   248                     (Integer)asJCDiagnostic(diagnostic).getArgs()[2] : -1;
   249         }
   251         boolean success(Diagnostic<? extends JavaFileObject> diagnostic) {
   252             return diagnostic.getCode().equals("compiler.note.verbose.resolve.multi");
   253         }
   255         Phase phase(Diagnostic<? extends JavaFileObject> diagnostic) {
   256             return Phase.fromString(asJCDiagnostic(diagnostic).getArgs()[3].toString());
   257         }
   258     }
   260     /**
   261      * Processor for verbose resolution subdiagnostic notes generated by javac.
   262      * The processor checks that the details of the overload candidate
   263      * match against the info contained in the corresponding @Candidate
   264      * annotation (if any).
   265      */
   266     class VerboseCandidateSubdiagProcessor extends DiagnosticProcessor {
   268         boolean mostSpecific;
   269         Phase phase;
   270         boolean success;
   272         public VerboseCandidateSubdiagProcessor(boolean mostSpecific, Phase phase, boolean success) {
   273             super(Kind.OTHER,
   274                     "compiler.misc.applicable.method.found",
   275                     "compiler.misc.applicable.method.found.1",
   276                     "compiler.misc.not.applicable.method.found");
   277             this.mostSpecific = mostSpecific;
   278             this.phase = phase;
   279             this.success = success;
   280         }
   282         @Override
   283         void process(Diagnostic<? extends JavaFileObject> diagnostic) {
   284             Element methodSym = methodSym(diagnostic);
   285             Candidate c = getCandidateAtPos(methodSym,
   286                     asJCDiagnostic(diagnostic).getLineNumber(),
   287                     asJCDiagnostic(diagnostic).getColumnNumber());
   288             if (c == null) {
   289                 return; //nothing to check
   290             }
   292             if (c.applicable().length == 0 && c.mostSpecific()) {
   293                 error("Inapplicable method cannot be most specific " + methodSym);
   294             }
   296             if (isApplicable(diagnostic) != Arrays.asList(c.applicable()).contains(phase)) {
   297                 error("Invalid candidate's applicability " + methodSym);
   298             }
   300             if (success) {
   301                 for (Phase p : c.applicable()) {
   302                     if (phase.ordinal() < p.ordinal()) {
   303                         error("Invalid phase " + p + " on method " + methodSym);
   304                     }
   305                 }
   306             }
   308             if (Arrays.asList(c.applicable()).contains(phase)) { //applicable
   309                 if (c.mostSpecific() != mostSpecific) {
   310                     error("Invalid most specific value for method " + methodSym);
   311                 }
   312                 MethodType mtype = getSig(diagnostic);
   313                 if (mtype != null) {
   314                     checkSig(c, methodSym, mtype);
   315                 }
   316             }
   317         }
   319         boolean isApplicable(Diagnostic<? extends JavaFileObject> diagnostic) {
   320             return !diagnostic.getCode().equals("compiler.misc.not.applicable.method.found");
   321         }
   323         Element methodSym(Diagnostic<? extends JavaFileObject> diagnostic) {
   324             return (Element)asJCDiagnostic(diagnostic).getArgs()[1];
   325         }
   327         MethodType getSig(Diagnostic<? extends JavaFileObject> diagnostic) {
   328             JCDiagnostic details = (JCDiagnostic)asJCDiagnostic(diagnostic).getArgs()[2];
   329             if (details == null) {
   330                 return null;
   331             } else if (details instanceof JCDiagnostic) {
   332                 return details.getCode().equals("compiler.misc.full.inst.sig") ?
   333                         (MethodType)details.getArgs()[0] : null;
   334             } else {
   335                 throw new AssertionError("Bad diagnostic arg: " + details);
   336             }
   337         }
   338     }
   340     /**
   341      * Processor for verbose deferred inference notes generated by javac. The
   342      * processor checks that the inferred signature for a given generic method
   343      * call corresponds to the one (if any) declared in the @Candidate annotation.
   344      */
   345     class VerboseDeferredInferenceNoteProcessor extends DiagnosticProcessor {
   347         public VerboseDeferredInferenceNoteProcessor() {
   348             super(Kind.NOTE, "compiler.note.deferred.method.inst");
   349         }
   351         @Override
   352         void process(Diagnostic<? extends JavaFileObject> diagnostic) {
   353             Element methodSym = methodSym(diagnostic);
   354             Candidate c = getCandidateAtPos(methodSym,
   355                     asJCDiagnostic(diagnostic).getLineNumber(),
   356                     asJCDiagnostic(diagnostic).getColumnNumber());
   357             MethodType sig = sig(diagnostic);
   358             if (c != null && sig != null) {
   359                 checkSig(c, methodSym, sig);
   360             }
   361         }
   363         Element methodSym(Diagnostic<? extends JavaFileObject> diagnostic) {
   364             return (Element)asJCDiagnostic(diagnostic).getArgs()[0];
   365         }
   367         MethodType sig(Diagnostic<? extends JavaFileObject> diagnostic) {
   368             return (MethodType)asJCDiagnostic(diagnostic).getArgs()[1];
   369         }
   370     }
   372     /**
   373      * Processor for all error diagnostics; if the error key is not declared in
   374      * the test file header, the processor reports an error.
   375      */
   376     class ErrorProcessor extends DiagnosticProcessor {
   378         public ErrorProcessor() {
   379             super(Diagnostic.Kind.ERROR);
   380         }
   382         @Override
   383         void process(Diagnostic<? extends JavaFileObject> diagnostic) {
   384             if (!declaredKeys.contains(diagnostic.getCode())) {
   385                 error("Unexpected compilation error key '" + diagnostic.getCode() + "'");
   386             }
   387         }
   388     }
   390     @SupportedAnnotationTypes({"Candidate","TraceResolve"})
   391     class ResolveCandidateFinder extends JavacTestingAbstractProcessor {
   393         @Override
   394         public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
   395             if (roundEnv.processingOver())
   396                 return true;
   398             TypeElement traceResolveAnno = elements.getTypeElement("TraceResolve");
   399             TypeElement candidateAnno = elements.getTypeElement("Candidate");
   401             if (!annotations.contains(traceResolveAnno)) {
   402                 error("no @TraceResolve annotation found in test class");
   403             }
   405             if (!annotations.contains(candidateAnno)) {
   406                 error("no @candidate annotation found in test class");
   407             }
   409             for (Element elem: roundEnv.getElementsAnnotatedWith(traceResolveAnno)) {
   410                 TraceResolve traceResolve = elem.getAnnotation(TraceResolve.class);
   411                 declaredKeys.addAll(Arrays.asList(traceResolve.keys()));
   412             }
   414             for (Element elem: roundEnv.getElementsAnnotatedWith(candidateAnno)) {
   415                 candidatesMap.put(new ElementKey(elem), elem.getAnnotation(Candidate.class));
   416             }
   417             return true;
   418         }
   419     }
   421     class ElementKey {
   423         String key;
   424         Element elem;
   426         public ElementKey(Element elem) {
   427             this.elem = elem;
   428             this.key = computeKey(elem);
   429         }
   431         @Override
   432         public boolean equals(Object obj) {
   433             if (obj instanceof ElementKey) {
   434                 ElementKey other = (ElementKey)obj;
   435                 return other.key.equals(key);
   436             }
   437             return false;
   438         }
   440         @Override
   441         public int hashCode() {
   442             return key.hashCode();
   443         }
   445         String computeKey(Element e) {
   446             StringBuilder buf = new StringBuilder();
   447             while (e != null) {
   448                 buf.append(e.toString());
   449                 e = e.getEnclosingElement();
   450             }
   451             buf.append(jfo.getName());
   452             return buf.toString();
   453         }
   455         @Override
   456         public String toString() {
   457             return "Key{"+key+"}";
   458         }
   459     }
   461     class DiagnosticHandler implements DiagnosticListener<JavaFileObject> {
   463         boolean shouldRecordDiags;
   465         DiagnosticHandler(boolean shouldRecordDiags) {
   466             this.shouldRecordDiags = shouldRecordDiags;
   467         }
   469         public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
   470             if (shouldRecordDiags)
   471                 diags.add(diagnostic);
   472         }
   474     }
   475 }

mercurial