1.1 --- a/src/share/classes/com/sun/tools/javac/comp/Resolve.java Wed May 15 00:00:39 2013 -0700 1.2 +++ b/src/share/classes/com/sun/tools/javac/comp/Resolve.java Wed May 15 14:00:31 2013 +0100 1.3 @@ -37,7 +37,10 @@ 1.4 import com.sun.tools.javac.comp.Infer.InferenceContext; 1.5 import com.sun.tools.javac.comp.Infer.FreeTypeListener; 1.6 import com.sun.tools.javac.comp.Resolve.MethodResolutionContext.Candidate; 1.7 +import com.sun.tools.javac.comp.Resolve.MethodResolutionDiagHelper.DiagnosticRewriter; 1.8 +import com.sun.tools.javac.comp.Resolve.MethodResolutionDiagHelper.Template; 1.9 import com.sun.tools.javac.jvm.*; 1.10 +import com.sun.tools.javac.main.Option; 1.11 import com.sun.tools.javac.tree.*; 1.12 import com.sun.tools.javac.tree.JCTree.*; 1.13 import com.sun.tools.javac.tree.JCTree.JCMemberReference.ReferenceKind; 1.14 @@ -94,6 +97,7 @@ 1.15 public final boolean allowDefaultMethods; 1.16 public final boolean allowStructuralMostSpecific; 1.17 private final boolean debugResolve; 1.18 + private final boolean compactMethodDiags; 1.19 final EnumSet<VerboseResolutionMode> verboseResolutionMode; 1.20 1.21 Scope polymorphicSignatureScope; 1.22 @@ -124,6 +128,8 @@ 1.23 varargsEnabled = source.allowVarargs(); 1.24 Options options = Options.instance(context); 1.25 debugResolve = options.isSet("debugresolve"); 1.26 + compactMethodDiags = options.isSet(Option.XDIAGS, "compact") || 1.27 + options.isUnset(Option.XDIAGS) && options.isUnset("rawDiagnostics"); 1.28 verboseResolutionMode = VerboseResolutionMode.getVerboseResolutionMode(options); 1.29 Target target = Target.instance(context); 1.30 allowMethodHandles = target.hasMethodHandles(); 1.31 @@ -661,6 +667,10 @@ 1.32 this.basicKey = basicKey; 1.33 this.inferKey = inferKey; 1.34 } 1.35 + 1.36 + String regex() { 1.37 + return String.format("([a-z]*\\.)*(%s|%s)", basicKey, inferKey); 1.38 + } 1.39 } 1.40 1.41 /** 1.42 @@ -691,6 +701,7 @@ 1.43 Warner warn) { 1.44 //should we expand formals? 1.45 boolean useVarargs = deferredAttrContext.phase.isVarargsRequired(); 1.46 + List<JCExpression> trees = TreeInfo.args(env.tree); 1.47 1.48 //inference context used during this method check 1.49 InferenceContext inferenceContext = deferredAttrContext.inferenceContext; 1.50 @@ -699,17 +710,19 @@ 1.51 1.52 if (varargsFormal == null && 1.53 argtypes.size() != formals.size()) { 1.54 - reportMC(MethodCheckDiag.ARITY_MISMATCH, inferenceContext); // not enough args 1.55 + reportMC(env.tree, MethodCheckDiag.ARITY_MISMATCH, inferenceContext); // not enough args 1.56 } 1.57 1.58 while (argtypes.nonEmpty() && formals.head != varargsFormal) { 1.59 - checkArg(false, argtypes.head, formals.head, deferredAttrContext, warn); 1.60 + DiagnosticPosition pos = trees != null ? trees.head : null; 1.61 + checkArg(pos, false, argtypes.head, formals.head, deferredAttrContext, warn); 1.62 argtypes = argtypes.tail; 1.63 formals = formals.tail; 1.64 + trees = trees != null ? trees.tail : trees; 1.65 } 1.66 1.67 if (formals.head != varargsFormal) { 1.68 - reportMC(MethodCheckDiag.ARITY_MISMATCH, inferenceContext); // not enough args 1.69 + reportMC(env.tree, MethodCheckDiag.ARITY_MISMATCH, inferenceContext); // not enough args 1.70 } 1.71 1.72 if (useVarargs) { 1.73 @@ -717,8 +730,10 @@ 1.74 //the last argument of a varargs is _not_ an array type (see JLS 15.12.2.5) 1.75 final Type elt = types.elemtype(varargsFormal); 1.76 while (argtypes.nonEmpty()) { 1.77 - checkArg(true, argtypes.head, elt, deferredAttrContext, warn); 1.78 + DiagnosticPosition pos = trees != null ? trees.head : null; 1.79 + checkArg(pos, true, argtypes.head, elt, deferredAttrContext, warn); 1.80 argtypes = argtypes.tail; 1.81 + trees = trees != null ? trees.tail : trees; 1.82 } 1.83 } 1.84 } 1.85 @@ -726,9 +741,9 @@ 1.86 /** 1.87 * Does the actual argument conforms to the corresponding formal? 1.88 */ 1.89 - abstract void checkArg(boolean varargs, Type actual, Type formal, DeferredAttrContext deferredAttrContext, Warner warn); 1.90 - 1.91 - protected void reportMC(MethodCheckDiag diag, InferenceContext inferenceContext, Object... args) { 1.92 + abstract void checkArg(DiagnosticPosition pos, boolean varargs, Type actual, Type formal, DeferredAttrContext deferredAttrContext, Warner warn); 1.93 + 1.94 + protected void reportMC(DiagnosticPosition pos, MethodCheckDiag diag, InferenceContext inferenceContext, Object... args) { 1.95 boolean inferDiag = inferenceContext != infer.emptyContext; 1.96 InapplicableMethodException ex = inferDiag ? 1.97 infer.inferenceException : inapplicableMethodException; 1.98 @@ -738,7 +753,8 @@ 1.99 args2[0] = inferenceContext.inferenceVars(); 1.100 args = args2; 1.101 } 1.102 - throw ex.setMessage(inferDiag ? diag.inferKey : diag.basicKey, args); 1.103 + String key = inferDiag ? diag.inferKey : diag.basicKey; 1.104 + throw ex.setMessage(diags.create(DiagnosticType.FRAGMENT, log.currentSource(), pos, key, args)); 1.105 } 1.106 1.107 public MethodCheck mostSpecificCheck(List<Type> actuals, boolean strict) { 1.108 @@ -752,7 +768,7 @@ 1.109 */ 1.110 MethodCheck arityMethodCheck = new AbstractMethodCheck() { 1.111 @Override 1.112 - void checkArg(boolean varargs, Type actual, Type formal, DeferredAttrContext deferredAttrContext, Warner warn) { 1.113 + void checkArg(DiagnosticPosition pos, boolean varargs, Type actual, Type formal, DeferredAttrContext deferredAttrContext, Warner warn) { 1.114 //do nothing - actual always compatible to formals 1.115 } 1.116 }; 1.117 @@ -778,9 +794,9 @@ 1.118 MethodCheck resolveMethodCheck = new AbstractMethodCheck() { 1.119 1.120 @Override 1.121 - void checkArg(boolean varargs, Type actual, Type formal, DeferredAttrContext deferredAttrContext, Warner warn) { 1.122 + void checkArg(DiagnosticPosition pos, boolean varargs, Type actual, Type formal, DeferredAttrContext deferredAttrContext, Warner warn) { 1.123 ResultInfo mresult = methodCheckResult(varargs, formal, deferredAttrContext, warn); 1.124 - mresult.check(null, actual); 1.125 + mresult.check(pos, actual); 1.126 } 1.127 1.128 @Override 1.129 @@ -809,7 +825,7 @@ 1.130 } else { 1.131 if (!isAccessible(env, t)) { 1.132 Symbol location = env.enclClass.sym; 1.133 - reportMC(MethodCheckDiag.INACCESSIBLE_VARARGS, inferenceContext, t, Kinds.kindName(location), location); 1.134 + reportMC(env.tree, MethodCheckDiag.INACCESSIBLE_VARARGS, inferenceContext, t, Kinds.kindName(location), location); 1.135 } 1.136 } 1.137 } 1.138 @@ -822,7 +838,7 @@ 1.139 1.140 @Override 1.141 public void report(DiagnosticPosition pos, JCDiagnostic details) { 1.142 - reportMC(methodDiag, deferredAttrContext.inferenceContext, details); 1.143 + reportMC(pos, methodDiag, deferredAttrContext.inferenceContext, details); 1.144 } 1.145 }; 1.146 return new MethodResultInfo(to, checkContext); 1.147 @@ -3327,6 +3343,18 @@ 1.148 } 1.149 else { 1.150 Candidate c = errCandidate(); 1.151 + if (compactMethodDiags) { 1.152 + for (Map.Entry<Template, DiagnosticRewriter> _entry : 1.153 + MethodResolutionDiagHelper.rewriters.entrySet()) { 1.154 + if (_entry.getKey().matches(c.details)) { 1.155 + JCDiagnostic simpleDiag = 1.156 + _entry.getValue().rewriteDiagnostic(diags, pos, 1.157 + log.currentSource(), dkind, c.details); 1.158 + simpleDiag.setFlag(DiagnosticFlag.COMPRESSED); 1.159 + return simpleDiag; 1.160 + } 1.161 + } 1.162 + } 1.163 Symbol ws = c.sym.asMemberOf(site, types); 1.164 return diags.create(dkind, log.currentSource(), pos, 1.165 "cant.apply.symbol", 1.166 @@ -3375,35 +3403,75 @@ 1.167 Name name, 1.168 List<Type> argtypes, 1.169 List<Type> typeargtypes) { 1.170 - if (!resolveContext.candidates.isEmpty()) { 1.171 + Map<Symbol, JCDiagnostic> candidatesMap = mapCandidates(); 1.172 + Map<Symbol, JCDiagnostic> filteredCandidates = filterCandidates(candidatesMap); 1.173 + if (filteredCandidates.isEmpty()) { 1.174 + filteredCandidates = candidatesMap; 1.175 + } 1.176 + boolean truncatedDiag = candidatesMap.size() != filteredCandidates.size(); 1.177 + if (filteredCandidates.size() > 1) { 1.178 JCDiagnostic err = diags.create(dkind, 1.179 + null, 1.180 + truncatedDiag ? 1.181 + EnumSet.of(DiagnosticFlag.COMPRESSED) : 1.182 + EnumSet.noneOf(DiagnosticFlag.class), 1.183 log.currentSource(), 1.184 pos, 1.185 "cant.apply.symbols", 1.186 name == names.init ? KindName.CONSTRUCTOR : absentKind(kind), 1.187 name == names.init ? site.tsym.name : name, 1.188 methodArguments(argtypes)); 1.189 - return new JCDiagnostic.MultilineDiagnostic(err, candidateDetails(site)); 1.190 + return new JCDiagnostic.MultilineDiagnostic(err, candidateDetails(filteredCandidates, site)); 1.191 + } else if (filteredCandidates.size() == 1) { 1.192 + JCDiagnostic d = new InapplicableSymbolError(resolveContext).getDiagnostic(dkind, pos, 1.193 + location, site, name, argtypes, typeargtypes); 1.194 + if (truncatedDiag) { 1.195 + d.setFlag(DiagnosticFlag.COMPRESSED); 1.196 + } 1.197 + return d; 1.198 } else { 1.199 return new SymbolNotFoundError(ABSENT_MTH).getDiagnostic(dkind, pos, 1.200 location, site, name, argtypes, typeargtypes); 1.201 } 1.202 } 1.203 - 1.204 //where 1.205 - List<JCDiagnostic> candidateDetails(Type site) { 1.206 - Map<Symbol, JCDiagnostic> details = new LinkedHashMap<Symbol, JCDiagnostic>(); 1.207 - for (Candidate c : resolveContext.candidates) { 1.208 - if (c.isApplicable()) continue; 1.209 - JCDiagnostic detailDiag = diags.fragment("inapplicable.method", 1.210 - Kinds.kindName(c.sym), 1.211 - c.sym.location(site, types), 1.212 - c.sym.asMemberOf(site, types), 1.213 - c.details); 1.214 - details.put(c.sym, detailDiag); 1.215 + private Map<Symbol, JCDiagnostic> mapCandidates() { 1.216 + Map<Symbol, JCDiagnostic> candidates = new LinkedHashMap<Symbol, JCDiagnostic>(); 1.217 + for (Candidate c : resolveContext.candidates) { 1.218 + if (c.isApplicable()) continue; 1.219 + candidates.put(c.sym, c.details); 1.220 + } 1.221 + return candidates; 1.222 } 1.223 - return List.from(details.values()); 1.224 - } 1.225 + 1.226 + Map<Symbol, JCDiagnostic> filterCandidates(Map<Symbol, JCDiagnostic> candidatesMap) { 1.227 + Map<Symbol, JCDiagnostic> candidates = new LinkedHashMap<Symbol, JCDiagnostic>(); 1.228 + for (Map.Entry<Symbol, JCDiagnostic> _entry : candidatesMap.entrySet()) { 1.229 + JCDiagnostic d = _entry.getValue(); 1.230 + if (!compactMethodDiags || 1.231 + !new Template(MethodCheckDiag.ARITY_MISMATCH.regex()).matches(d)) { 1.232 + candidates.put(_entry.getKey(), d); 1.233 + } 1.234 + } 1.235 + return candidates; 1.236 + } 1.237 + 1.238 + private List<JCDiagnostic> candidateDetails(Map<Symbol, JCDiagnostic> candidatesMap, Type site) { 1.239 + List<JCDiagnostic> details = List.nil(); 1.240 + for (Map.Entry<Symbol, JCDiagnostic> _entry : candidatesMap.entrySet()) { 1.241 + Symbol sym = _entry.getKey(); 1.242 + JCDiagnostic detailDiag = diags.fragment("inapplicable.method", 1.243 + Kinds.kindName(sym), 1.244 + sym.location(site, types), 1.245 + sym.asMemberOf(site, types), 1.246 + _entry.getValue()); 1.247 + details = details.prepend(detailDiag); 1.248 + } 1.249 + //typically members are visited in reverse order (see Scope) 1.250 + //so we need to reverse the candidate list so that candidates 1.251 + //conform to source order 1.252 + return details; 1.253 + } 1.254 } 1.255 1.256 /** 1.257 @@ -3624,6 +3692,105 @@ 1.258 } 1.259 } 1.260 1.261 + /** 1.262 + * Helper class for method resolution diagnostic simplification. 1.263 + * Certain resolution diagnostic are rewritten as simpler diagnostic 1.264 + * where the enclosing resolution diagnostic (i.e. 'inapplicable method') 1.265 + * is stripped away, as it doesn't carry additional info. The logic 1.266 + * for matching a given diagnostic is given in terms of a template 1.267 + * hierarchy: a diagnostic template can be specified programmatically, 1.268 + * so that only certain diagnostics are matched. Each templete is then 1.269 + * associated with a rewriter object that carries out the task of rewtiting 1.270 + * the diagnostic to a simpler one. 1.271 + */ 1.272 + static class MethodResolutionDiagHelper { 1.273 + 1.274 + /** 1.275 + * A diagnostic rewriter transforms a method resolution diagnostic 1.276 + * into a simpler one 1.277 + */ 1.278 + interface DiagnosticRewriter { 1.279 + JCDiagnostic rewriteDiagnostic(JCDiagnostic.Factory diags, 1.280 + DiagnosticPosition preferedPos, DiagnosticSource preferredSource, 1.281 + DiagnosticType preferredKind, JCDiagnostic d); 1.282 + } 1.283 + 1.284 + /** 1.285 + * A diagnostic template is made up of two ingredients: (i) a regular 1.286 + * expression for matching a diagnostic key and (ii) a list of sub-templates 1.287 + * for matching diagnostic arguments. 1.288 + */ 1.289 + static class Template { 1.290 + 1.291 + /** regex used to match diag key */ 1.292 + String regex; 1.293 + 1.294 + /** templates used to match diagnostic args */ 1.295 + Template[] subTemplates; 1.296 + 1.297 + Template(String key, Template... subTemplates) { 1.298 + this.regex = key; 1.299 + this.subTemplates = subTemplates; 1.300 + } 1.301 + 1.302 + /** 1.303 + * Returns true if the regex matches the diagnostic key and if 1.304 + * all diagnostic arguments are matches by corresponding sub-templates. 1.305 + */ 1.306 + boolean matches(Object o) { 1.307 + JCDiagnostic d = (JCDiagnostic)o; 1.308 + Object[] args = d.getArgs(); 1.309 + if (!d.getCode().matches(regex) || 1.310 + subTemplates.length != d.getArgs().length) { 1.311 + return false; 1.312 + } 1.313 + for (int i = 0; i < args.length ; i++) { 1.314 + if (!subTemplates[i].matches(args[i])) { 1.315 + return false; 1.316 + } 1.317 + } 1.318 + return true; 1.319 + } 1.320 + } 1.321 + 1.322 + /** a dummy template that match any diagnostic argument */ 1.323 + static final Template skip = new Template("") { 1.324 + @Override 1.325 + boolean matches(Object d) { 1.326 + return true; 1.327 + } 1.328 + }; 1.329 + 1.330 + /** rewriter map used for method resolution simplification */ 1.331 + static final Map<Template, DiagnosticRewriter> rewriters = 1.332 + new LinkedHashMap<Template, DiagnosticRewriter>(); 1.333 + 1.334 + static { 1.335 + String argMismatchRegex = MethodCheckDiag.ARG_MISMATCH.regex(); 1.336 + rewriters.put(new Template(argMismatchRegex, new Template("(.*)(bad.arg.types.in.lambda)", skip, skip)), 1.337 + new DiagnosticRewriter() { 1.338 + @Override 1.339 + public JCDiagnostic rewriteDiagnostic(JCDiagnostic.Factory diags, 1.340 + DiagnosticPosition preferedPos, DiagnosticSource preferredSource, 1.341 + DiagnosticType preferredKind, JCDiagnostic d) { 1.342 + return (JCDiagnostic)((JCDiagnostic)d.getArgs()[0]).getArgs()[1]; 1.343 + } 1.344 + }); 1.345 + 1.346 + rewriters.put(new Template(argMismatchRegex, skip), 1.347 + new DiagnosticRewriter() { 1.348 + @Override 1.349 + public JCDiagnostic rewriteDiagnostic(JCDiagnostic.Factory diags, 1.350 + DiagnosticPosition preferedPos, DiagnosticSource preferredSource, 1.351 + DiagnosticType preferredKind, JCDiagnostic d) { 1.352 + JCDiagnostic cause = (JCDiagnostic)d.getArgs()[0]; 1.353 + return diags.create(preferredKind, preferredSource, d.getDiagnosticPosition(), 1.354 + "prob.found.req", cause); 1.355 + } 1.356 + }); 1.357 + } 1.358 + } 1.359 + 1.360 enum MethodResolutionPhase { 1.361 BASIC(false, false), 1.362 BOX(true, false),