RichDiagnosticFormatter.java revision 2734:b96d74fa60aa
1135446Strhodes/* 2193149Sdougb * Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved. 3135446Strhodes * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4135446Strhodes * 5174187Sdougb * This code is free software; you can redistribute it and/or modify it 6135446Strhodes * under the terms of the GNU General Public License version 2 only, as 7135446Strhodes * published by the Free Software Foundation. Oracle designates this 8135446Strhodes * particular file as subject to the "Classpath" exception as provided 9135446Strhodes * by Oracle in the LICENSE file that accompanied this code. 10135446Strhodes * 11135446Strhodes * This code is distributed in the hope that it will be useful, but WITHOUT 12135446Strhodes * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13135446Strhodes * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14135446Strhodes * version 2 for more details (a copy is included in the LICENSE file that 15135446Strhodes * accompanied this code). 16135446Strhodes * 17135446Strhodes * You should have received a copy of the GNU General Public License version 18193149Sdougb * 2 along with this work; if not, write to the Free Software Foundation, 19135446Strhodes * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20135446Strhodes * 21135446Strhodes * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22135446Strhodes * or visit www.oracle.com if you need additional information or have any 23135446Strhodes * questions. 24135446Strhodes */ 25135446Strhodespackage com.sun.tools.javac.util; 26135446Strhodes 27193149Sdougbimport java.nio.file.Path; 28170222Sdougbimport java.util.EnumMap; 29135446Strhodesimport java.util.EnumSet; 30135446Strhodesimport java.util.HashMap; 31135446Strhodesimport java.util.LinkedHashMap; 32135446Strhodesimport java.util.Locale; 33135446Strhodesimport java.util.Map; 34170222Sdougb 35135446Strhodesimport com.sun.tools.javac.code.Printer; 36135446Strhodesimport com.sun.tools.javac.code.Symbol; 37170222Sdougbimport com.sun.tools.javac.code.Symbol.*; 38135446Strhodesimport com.sun.tools.javac.code.Symtab; 39135446Strhodesimport com.sun.tools.javac.code.Type; 40135446Strhodesimport com.sun.tools.javac.code.Type.*; 41170222Sdougbimport com.sun.tools.javac.code.Types; 42135446Strhodes 43135446Strhodesimport static com.sun.tools.javac.code.Flags.*; 44170222Sdougbimport static com.sun.tools.javac.code.TypeTag.*; 45135446Strhodesimport static com.sun.tools.javac.code.Kinds.*; 46135446Strhodesimport static com.sun.tools.javac.code.Kinds.Kind.*; 47170222Sdougbimport static com.sun.tools.javac.util.LayoutCharacters.*; 48135446Strhodesimport static com.sun.tools.javac.util.RichDiagnosticFormatter.RichConfiguration.*; 49135446Strhodes 50170222Sdougb/** 51135446Strhodes * A rich diagnostic formatter is a formatter that provides better integration 52135446Strhodes * with javac's type system. A diagostic is first preprocessed in order to keep 53135446Strhodes * track of each types/symbols in it; after these informations are collected, 54135446Strhodes * the diagnostic is rendered using a standard formatter, whose type/symbol printer 55135446Strhodes * has been replaced by a more refined version provided by this rich formatter. 56135446Strhodes * The rich formatter currently enables three different features: (i) simple class 57135446Strhodes * names - that is class names are displayed used a non qualified name (thus 58135446Strhodes * omitting package info) whenever possible - (ii) where clause list - a list of 59135446Strhodes * additional subdiagnostics that provide specific info about type-variables, 60135446Strhodes * captured types, intersection types that occur in the diagnostic that is to be 61135446Strhodes * formatted and (iii) type-variable disambiguation - when the diagnostic refers 62135446Strhodes * to two different type-variables with the same name, their representation is 63135446Strhodes * disambiguated by appending an index to the type variable name. 64135446Strhodes * 65135446Strhodes * <p><b>This is NOT part of any supported API. 66135446Strhodes * If you write code that depends on this, you do so at your own risk. 67135446Strhodes * This code and its internal interfaces are subject to change or 68135446Strhodes * deletion without notice.</b> 69135446Strhodes */ 70135446Strhodespublic class RichDiagnosticFormatter extends 71135446Strhodes ForwardingDiagnosticFormatter<JCDiagnostic, AbstractDiagnosticFormatter> { 72135446Strhodes 73135446Strhodes final Symtab syms; 74135446Strhodes final Types types; 75135446Strhodes final JCDiagnostic.Factory diags; 76135446Strhodes final JavacMessages messages; 77135446Strhodes 78170222Sdougb /* name simplifier used by this formatter */ 79170222Sdougb protected ClassNameSimplifier nameSimplifier; 80135446Strhodes 81135446Strhodes /* type/symbol printer used by this formatter */ 82135446Strhodes private RichPrinter printer; 83135446Strhodes 84135446Strhodes /* map for keeping track of a where clause associated to a given type */ 85135446Strhodes Map<WhereClauseKind, Map<Type, JCDiagnostic>> whereClauses; 86135446Strhodes 87135446Strhodes /** Get the DiagnosticFormatter instance for this context. */ 88135446Strhodes public static RichDiagnosticFormatter instance(Context context) { 89135446Strhodes RichDiagnosticFormatter instance = context.get(RichDiagnosticFormatter.class); 90135446Strhodes if (instance == null) 91135446Strhodes instance = new RichDiagnosticFormatter(context); 92135446Strhodes return instance; 93135446Strhodes } 94135446Strhodes 95135446Strhodes protected RichDiagnosticFormatter(Context context) { 96135446Strhodes super((AbstractDiagnosticFormatter)Log.instance(context).getDiagnosticFormatter()); 97135446Strhodes setRichPrinter(new RichPrinter()); 98135446Strhodes this.syms = Symtab.instance(context); 99135446Strhodes this.diags = JCDiagnostic.Factory.instance(context); 100135446Strhodes this.types = Types.instance(context); 101135446Strhodes this.messages = JavacMessages.instance(context); 102135446Strhodes whereClauses = new EnumMap<>(WhereClauseKind.class); 103135446Strhodes configuration = new RichConfiguration(Options.instance(context), formatter); 104135446Strhodes for (WhereClauseKind kind : WhereClauseKind.values()) 105135446Strhodes whereClauses.put(kind, new LinkedHashMap<Type, JCDiagnostic>()); 106135446Strhodes } 107135446Strhodes 108135446Strhodes @Override 109135446Strhodes public String format(JCDiagnostic diag, Locale l) { 110135446Strhodes StringBuilder sb = new StringBuilder(); 111135446Strhodes nameSimplifier = new ClassNameSimplifier(); 112135446Strhodes for (WhereClauseKind kind : WhereClauseKind.values()) 113135446Strhodes whereClauses.get(kind).clear(); 114193149Sdougb preprocessDiagnostic(diag); 115135446Strhodes sb.append(formatter.format(diag, l)); 116135446Strhodes if (getConfiguration().isEnabled(RichFormatterFeature.WHERE_CLAUSES)) { 117135446Strhodes List<JCDiagnostic> clauses = getWhereClauses(); 118135446Strhodes String indent = formatter.isRaw() ? "" : 119135446Strhodes formatter.indentString(DetailsInc); 120135446Strhodes for (JCDiagnostic d : clauses) { 121135446Strhodes String whereClause = formatter.format(d, l); 122135446Strhodes if (whereClause.length() > 0) { 123135446Strhodes sb.append('\n' + indent + whereClause); 124135446Strhodes } 125135446Strhodes } 126135446Strhodes } 127135446Strhodes return sb.toString(); 128135446Strhodes } 129135446Strhodes 130135446Strhodes @Override 131135446Strhodes public String formatMessage(JCDiagnostic diag, Locale l) { 132135446Strhodes nameSimplifier = new ClassNameSimplifier(); 133135446Strhodes preprocessDiagnostic(diag); 134135446Strhodes return super.formatMessage(diag, l); 135135446Strhodes } 136135446Strhodes 137135446Strhodes /** 138135446Strhodes * Sets the type/symbol printer used by this formatter. 139135446Strhodes * @param printer the rich printer to be set 140135446Strhodes */ 141135446Strhodes protected void setRichPrinter(RichPrinter printer) { 142135446Strhodes this.printer = printer; 143135446Strhodes formatter.setPrinter(printer); 144135446Strhodes } 145135446Strhodes 146135446Strhodes /** 147170222Sdougb * Returns the type/symbol printer used by this formatter. 148193149Sdougb * @return type/symbol rich printer 149193149Sdougb */ 150193149Sdougb protected RichPrinter getRichPrinter() { 151193149Sdougb return printer; 152193149Sdougb } 153193149Sdougb 154193149Sdougb /** 155193149Sdougb * Preprocess a given diagnostic by looking both into its arguments and into 156193149Sdougb * its subdiagnostics (if any). This preprocessing is responsible for 157193149Sdougb * generating info corresponding to features like where clauses, name 158193149Sdougb * simplification, etc. 159193149Sdougb * 160193149Sdougb * @param diag the diagnostic to be preprocessed 161193149Sdougb */ 162193149Sdougb protected void preprocessDiagnostic(JCDiagnostic diag) { 163193149Sdougb for (Object o : diag.getArgs()) { 164193149Sdougb if (o != null) { 165193149Sdougb preprocessArgument(o); 166193149Sdougb } 167193149Sdougb } 168193149Sdougb if (diag.isMultiline()) { 169193149Sdougb for (JCDiagnostic d : diag.getSubdiagnostics()) 170135446Strhodes preprocessDiagnostic(d); 171135446Strhodes } 172135446Strhodes } 173135446Strhodes 174135446Strhodes /** 175135446Strhodes * Preprocess a diagnostic argument. A type/symbol argument is 176135446Strhodes * preprocessed by specialized type/symbol preprocessors. 177193149Sdougb * 178135446Strhodes * @param arg the argument to be translated 179135446Strhodes */ 180135446Strhodes protected void preprocessArgument(Object arg) { 181170222Sdougb if (arg instanceof Type) { 182135446Strhodes preprocessType((Type)arg); 183135446Strhodes } 184170222Sdougb else if (arg instanceof Symbol) { 185135446Strhodes preprocessSymbol((Symbol)arg); 186135446Strhodes } 187135446Strhodes else if (arg instanceof JCDiagnostic) { 188135446Strhodes preprocessDiagnostic((JCDiagnostic)arg); 189135446Strhodes } 190135446Strhodes else if (arg instanceof Iterable<?> && !(arg instanceof Path)) { 191135446Strhodes for (Object o : (Iterable<?>)arg) { 192135446Strhodes preprocessArgument(o); 193135446Strhodes } 194135446Strhodes } 195135446Strhodes } 196135446Strhodes 197135446Strhodes /** 198135446Strhodes * Build a list of multiline diagnostics containing detailed info about 199135446Strhodes * type-variables, captured types, and intersection types 200135446Strhodes * 201135446Strhodes * @return where clause list 202135446Strhodes */ 203135446Strhodes protected List<JCDiagnostic> getWhereClauses() { 204170222Sdougb List<JCDiagnostic> clauses = List.nil(); 205170222Sdougb for (WhereClauseKind kind : WhereClauseKind.values()) { 206135446Strhodes List<JCDiagnostic> lines = List.nil(); 207135446Strhodes for (Map.Entry<Type, JCDiagnostic> entry : whereClauses.get(kind).entrySet()) { 208135446Strhodes lines = lines.prepend(entry.getValue()); 209135446Strhodes } 210135446Strhodes if (!lines.isEmpty()) { 211135446Strhodes String key = kind.key(); 212135446Strhodes if (lines.size() > 1) 213135446Strhodes key += ".1"; 214135446Strhodes JCDiagnostic d = diags.fragment(key, whereClauses.get(kind).keySet()); 215193149Sdougb d = new JCDiagnostic.MultilineDiagnostic(d, lines.reverse()); 216170222Sdougb clauses = clauses.prepend(d); 217135446Strhodes } 218170222Sdougb } 219170222Sdougb return clauses.reverse(); 220135446Strhodes } 221135446Strhodes 222135446Strhodes private int indexOf(Type type, WhereClauseKind kind) { 223135446Strhodes int index = 1; 224135446Strhodes for (Type t : whereClauses.get(kind).keySet()) { 225135446Strhodes if (t.tsym == type.tsym) { 226170222Sdougb return index; 227135446Strhodes } 228170222Sdougb if (kind != WhereClauseKind.TYPEVAR || 229135446Strhodes t.toString().equals(type.toString())) { 230135446Strhodes index++; 231135446Strhodes } 232135446Strhodes } 233193149Sdougb return -1; 234193149Sdougb } 235193149Sdougb 236193149Sdougb private boolean unique(TypeVar typevar) { 237193149Sdougb int found = 0; 238193149Sdougb for (Type t : whereClauses.get(WhereClauseKind.TYPEVAR).keySet()) { 239193149Sdougb if (t.toString().equals(typevar.toString())) { 240193149Sdougb found++; 241193149Sdougb } 242135446Strhodes } 243135446Strhodes if (found < 1) 244135446Strhodes throw new AssertionError("Missing type variable in where clause " + typevar); 245135446Strhodes return found == 1; 246135446Strhodes } 247135446Strhodes //where 248135446Strhodes /** 249135446Strhodes * This enum defines all posssible kinds of where clauses that can be 250135446Strhodes * attached by a rich diagnostic formatter to a given diagnostic 251135446Strhodes */ 252135446Strhodes enum WhereClauseKind { 253135446Strhodes 254170222Sdougb /** where clause regarding a type variable */ 255135446Strhodes TYPEVAR("where.description.typevar"), 256135446Strhodes /** where clause regarding a captured type */ 257135446Strhodes CAPTURED("where.description.captured"), 258170222Sdougb /** where clause regarding an intersection type */ 259135446Strhodes INTERSECTION("where.description.intersection"); 260135446Strhodes 261135446Strhodes /** resource key for this where clause kind */ 262135446Strhodes private final String key; 263170222Sdougb 264135446Strhodes WhereClauseKind(String key) { 265135446Strhodes this.key = key; 266135446Strhodes } 267170222Sdougb 268135446Strhodes String key() { 269170222Sdougb return key; 270135446Strhodes } 271170222Sdougb } 272135446Strhodes 273135446Strhodes // <editor-fold defaultstate="collapsed" desc="name simplifier"> 274135446Strhodes /** 275170222Sdougb * A name simplifier keeps track of class names usages in order to determine 276135446Strhodes * whether a class name can be compacted or not. Short names are not used 277135446Strhodes * if a conflict is detected, e.g. when two classes with the same simple 278135446Strhodes * name belong to different packages - in this case the formatter reverts 279135446Strhodes * to fullnames as compact names might lead to a confusing diagnostic. 280135446Strhodes */ 281170222Sdougb protected class ClassNameSimplifier { 282170222Sdougb 283170222Sdougb /* table for keeping track of all short name usages */ 284135446Strhodes Map<Name, List<Symbol>> nameClashes = new HashMap<>(); 285170222Sdougb 286135446Strhodes /** 287135446Strhodes * Add a name usage to the simplifier's internal cache 288135446Strhodes */ 289135446Strhodes protected void addUsage(Symbol sym) { 290135446Strhodes Name n = sym.getSimpleName(); 291170222Sdougb List<Symbol> conflicts = nameClashes.get(n); 292135446Strhodes if (conflicts == null) { 293135446Strhodes conflicts = List.nil(); 294135446Strhodes } 295135446Strhodes if (!conflicts.contains(sym)) 296170222Sdougb nameClashes.put(n, conflicts.append(sym)); 297135446Strhodes } 298170222Sdougb 299135446Strhodes public String simplify(Symbol s) { 300135446Strhodes String name = s.getQualifiedName().toString(); 301135446Strhodes if (!s.type.isCompound() && !s.type.isPrimitive()) { 302170222Sdougb List<Symbol> conflicts = nameClashes.get(s.getSimpleName()); 303135446Strhodes if (conflicts == null || 304135446Strhodes (conflicts.size() == 1 && 305135446Strhodes conflicts.contains(s))) { 306135446Strhodes List<Name> l = List.nil(); 307170222Sdougb Symbol s2 = s; 308135446Strhodes while (s2.type.hasTag(CLASS) && 309135446Strhodes s2.type.getEnclosingType().hasTag(CLASS) && 310135446Strhodes s2.owner.kind == TYP) { 311135446Strhodes l = l.prepend(s2.getSimpleName()); 312170222Sdougb s2 = s2.owner; 313135446Strhodes } 314135446Strhodes l = l.prepend(s2.getSimpleName()); 315135446Strhodes StringBuilder buf = new StringBuilder(); 316170222Sdougb String sep = ""; 317135446Strhodes for (Name n2 : l) { 318170222Sdougb buf.append(sep); 319170222Sdougb buf.append(n2); 320135446Strhodes sep = "."; 321135446Strhodes } 322135446Strhodes name = buf.toString(); 323135446Strhodes } 324170222Sdougb } 325135446Strhodes return name; 326135446Strhodes } 327135446Strhodes } 328135446Strhodes // </editor-fold> 329135446Strhodes 330135446Strhodes // <editor-fold defaultstate="collapsed" desc="rich printer"> 331135446Strhodes /** 332135446Strhodes * Enhanced type/symbol printer that provides support for features like simple names 333135446Strhodes * and type variable disambiguation. This enriched printer exploits the info 334135446Strhodes * discovered during type/symbol preprocessing. This printer is set on the delegate 335170222Sdougb * formatter so that rich type/symbol info can be properly rendered. 336135446Strhodes */ 337135446Strhodes protected class RichPrinter extends Printer { 338135446Strhodes 339135446Strhodes @Override 340170222Sdougb public String localize(Locale locale, String key, Object... args) { 341135446Strhodes return formatter.localize(locale, key, args); 342135446Strhodes } 343170222Sdougb 344170222Sdougb @Override 345135446Strhodes public String capturedVarId(CapturedType t, Locale locale) { 346135446Strhodes return indexOf(t, WhereClauseKind.CAPTURED) + ""; 347135446Strhodes } 348135446Strhodes 349170222Sdougb @Override 350135446Strhodes public String visitType(Type t, Locale locale) { 351135446Strhodes String s = super.visitType(t, locale); 352135446Strhodes if (t == syms.botType) 353135446Strhodes s = localize(locale, "compiler.misc.type.null"); 354170222Sdougb return s; 355135446Strhodes } 356135446Strhodes 357170222Sdougb @Override 358170222Sdougb public String visitCapturedType(CapturedType t, Locale locale) { 359135446Strhodes if (getConfiguration().isEnabled(RichFormatterFeature.WHERE_CLAUSES)) { 360135446Strhodes return localize(locale, 361135446Strhodes "compiler.misc.captured.type", 362135446Strhodes indexOf(t, WhereClauseKind.CAPTURED)); 363170222Sdougb } 364135446Strhodes else 365135446Strhodes return super.visitCapturedType(t, locale); 366135446Strhodes } 367135446Strhodes 368170222Sdougb @Override 369135446Strhodes public String visitClassType(ClassType t, Locale locale) { 370135446Strhodes if (t.isCompound() && 371170222Sdougb getConfiguration().isEnabled(RichFormatterFeature.WHERE_CLAUSES)) { 372170222Sdougb return localize(locale, 373135446Strhodes "compiler.misc.intersection.type", 374135446Strhodes indexOf(t, WhereClauseKind.INTERSECTION)); 375135446Strhodes } 376135446Strhodes else 377170222Sdougb return super.visitClassType(t, locale); 378135446Strhodes } 379135446Strhodes 380135446Strhodes @Override 381135446Strhodes protected String className(ClassType t, boolean longform, Locale locale) { 382170222Sdougb Symbol sym = t.tsym; 383135446Strhodes if (sym.name.length() == 0 || 384135446Strhodes !getConfiguration().isEnabled(RichFormatterFeature.SIMPLE_NAMES)) { 385170222Sdougb return super.className(t, longform, locale); 386170222Sdougb } 387135446Strhodes else if (longform) 388135446Strhodes return nameSimplifier.simplify(sym).toString(); 389193149Sdougb else 390193149Sdougb return sym.name.toString(); 391193149Sdougb } 392193149Sdougb 393193149Sdougb @Override 394193149Sdougb public String visitTypeVar(TypeVar t, Locale locale) { 395193149Sdougb if (unique(t) || 396193149Sdougb !getConfiguration().isEnabled(RichFormatterFeature.UNIQUE_TYPEVAR_NAMES)) { 397193149Sdougb return t.toString(); 398193149Sdougb } 399193149Sdougb else { 400193149Sdougb return localize(locale, 401193149Sdougb "compiler.misc.type.var", 402193149Sdougb t.toString(), indexOf(t, WhereClauseKind.TYPEVAR)); 403135446Strhodes } 404135446Strhodes } 405170222Sdougb 406135446Strhodes @Override 407135446Strhodes public String visitClassSymbol(ClassSymbol s, Locale locale) { 408135446Strhodes if (s.type.isCompound()) { 409135446Strhodes return visit(s.type, locale); 410135446Strhodes } 411135446Strhodes String name = nameSimplifier.simplify(s); 412170222Sdougb if (name.length() == 0 || 413135446Strhodes !getConfiguration().isEnabled(RichFormatterFeature.SIMPLE_NAMES)) { 414135446Strhodes return super.visitClassSymbol(s, locale); 415135446Strhodes } 416170222Sdougb else { 417135446Strhodes return name; 418135446Strhodes } 419135446Strhodes } 420135446Strhodes 421170222Sdougb @Override 422135446Strhodes public String visitMethodSymbol(MethodSymbol s, Locale locale) { 423135446Strhodes String ownerName = visit(s.owner, locale); 424135446Strhodes if (s.isStaticOrInstanceInit()) { 425135446Strhodes return ownerName; 426170222Sdougb } else { 427135446Strhodes String ms = (s.name == s.name.table.names.init) 428135446Strhodes ? ownerName 429135446Strhodes : s.name.toString(); 430170222Sdougb if (s.type != null) { 431135446Strhodes if (s.type.hasTag(FORALL)) { 432135446Strhodes ms = "<" + visitTypes(s.type.getTypeArguments(), locale) + ">" + ms; 433135446Strhodes } 434135446Strhodes ms += "(" + printMethodArgs( 435135446Strhodes s.type.getParameterTypes(), 436170222Sdougb (s.flags() & VARARGS) != 0, 437135446Strhodes locale) + ")"; 438135446Strhodes } 439135446Strhodes return ms; 440135446Strhodes } 441170222Sdougb } 442135446Strhodes } 443170222Sdougb // </editor-fold> 444135446Strhodes 445170222Sdougb // <editor-fold defaultstate="collapsed" desc="type scanner"> 446135446Strhodes /** 447170222Sdougb * Preprocess a given type looking for (i) additional info (where clauses) to be 448135446Strhodes * added to the main diagnostic (ii) names to be compacted. 449135446Strhodes */ 450135446Strhodes protected void preprocessType(Type t) { 451170222Sdougb typePreprocessor.visit(t); 452135446Strhodes } 453135446Strhodes //where 454135446Strhodes protected Types.UnaryVisitor<Void> typePreprocessor = 455135446Strhodes new Types.UnaryVisitor<Void>() { 456135446Strhodes 457170222Sdougb public Void visit(List<Type> ts) { 458170222Sdougb for (Type t : ts) 459135446Strhodes visit(t); 460170222Sdougb return null; 461135446Strhodes } 462135446Strhodes 463135446Strhodes @Override 464135446Strhodes public Void visitForAll(ForAll t, Void ignored) { 465135446Strhodes visit(t.tvars); 466170222Sdougb visit(t.qtype); 467135446Strhodes return null; 468135446Strhodes } 469135446Strhodes 470135446Strhodes @Override 471170222Sdougb public Void visitMethodType(MethodType t, Void ignored) { 472135446Strhodes visit(t.argtypes); 473170222Sdougb visit(t.restype); 474135446Strhodes return null; 475135446Strhodes } 476135446Strhodes 477170222Sdougb @Override 478135446Strhodes public Void visitErrorType(ErrorType t, Void ignored) { 479135446Strhodes Type ot = t.getOriginalType(); 480135446Strhodes if (ot != null) 481170222Sdougb visit(ot); 482170222Sdougb return null; 483135446Strhodes } 484170222Sdougb 485135446Strhodes @Override 486135446Strhodes public Void visitArrayType(ArrayType t, Void ignored) { 487135446Strhodes visit(t.elemtype); 488135446Strhodes return null; 489135446Strhodes } 490170222Sdougb 491170222Sdougb @Override 492170222Sdougb public Void visitWildcardType(WildcardType t, Void ignored) { 493170222Sdougb visit(t.type); 494135446Strhodes return null; 495135446Strhodes } 496135446Strhodes 497170222Sdougb public Void visitType(Type t, Void ignored) { 498135446Strhodes return null; 499170222Sdougb } 500135446Strhodes 501135446Strhodes @Override 502135446Strhodes public Void visitCapturedType(CapturedType t, Void ignored) { 503170222Sdougb if (indexOf(t, WhereClauseKind.CAPTURED) == -1) { 504135446Strhodes String suffix = t.lower == syms.botType ? ".1" : ""; 505135446Strhodes JCDiagnostic d = diags.fragment("where.captured"+ suffix, t, t.bound, t.lower, t.wildcard); 506135446Strhodes whereClauses.get(WhereClauseKind.CAPTURED).put(t, d); 507170222Sdougb visit(t.wildcard); 508135446Strhodes visit(t.lower); 509170222Sdougb visit(t.bound); 510135446Strhodes } 511135446Strhodes return null; 512135446Strhodes } 513170222Sdougb 514170222Sdougb @Override 515135446Strhodes public Void visitClassType(ClassType t, Void ignored) { 516170222Sdougb if (t.isCompound()) { 517135446Strhodes if (indexOf(t, WhereClauseKind.INTERSECTION) == -1) { 518135446Strhodes Type supertype = types.supertype(t); 519135446Strhodes List<Type> interfaces = types.interfaces(t); 520135446Strhodes JCDiagnostic d = diags.fragment("where.intersection", t, interfaces.prepend(supertype)); 521135446Strhodes whereClauses.get(WhereClauseKind.INTERSECTION).put(t, d); 522170222Sdougb visit(supertype); 523170222Sdougb visit(interfaces); 524170222Sdougb } 525170222Sdougb } else if (t.tsym.name.isEmpty()) { 526170222Sdougb //anon class 527135446Strhodes ClassType norm = (ClassType) t.tsym.type; 528135446Strhodes if (norm != null) { 529135446Strhodes if (norm.interfaces_field != null && norm.interfaces_field.nonEmpty()) { 530135446Strhodes visit(norm.interfaces_field.head); 531170222Sdougb } else { 532135446Strhodes visit(norm.supertype_field); 533170222Sdougb } 534135446Strhodes } 535135446Strhodes } 536135446Strhodes nameSimplifier.addUsage(t.tsym); 537170222Sdougb visit(t.getTypeArguments()); 538170222Sdougb if (t.getEnclosingType() != Type.noType) 539135446Strhodes visit(t.getEnclosingType()); 540170222Sdougb return null; 541135446Strhodes } 542135446Strhodes 543135446Strhodes @Override 544135446Strhodes public Void visitTypeVar(TypeVar t, Void ignored) { 545135446Strhodes if (indexOf(t, WhereClauseKind.TYPEVAR) == -1) { 546135446Strhodes //access the bound type and skip error types 547135446Strhodes Type bound = t.bound; 548135446Strhodes while ((bound instanceof ErrorType)) 549135446Strhodes bound = ((ErrorType)bound).getOriginalType(); 550170222Sdougb //retrieve the bound list - if the type variable 551135446Strhodes //has not been attributed the bound is not set 552135446Strhodes List<Type> bounds = (bound != null) && 553135446Strhodes (bound.hasTag(CLASS) || bound.hasTag(TYPEVAR)) ? 554135446Strhodes types.getBounds(t) : 555170222Sdougb List.<Type>nil(); 556135446Strhodes 557170222Sdougb nameSimplifier.addUsage(t.tsym); 558135446Strhodes 559135446Strhodes boolean boundErroneous = bounds.head == null || 560135446Strhodes bounds.head.hasTag(NONE) || 561170222Sdougb bounds.head.hasTag(ERROR); 562135446Strhodes 563135446Strhodes if ((t.tsym.flags() & SYNTHETIC) == 0) { 564135446Strhodes //this is a true typevar 565135446Strhodes JCDiagnostic d = diags.fragment("where.typevar" + 566135446Strhodes (boundErroneous ? ".1" : ""), t, bounds, 567170222Sdougb kindName(t.tsym.location()), t.tsym.location()); 568135446Strhodes whereClauses.get(WhereClauseKind.TYPEVAR).put(t, d); 569135446Strhodes symbolPreprocessor.visit(t.tsym.location(), null); 570135446Strhodes visit(bounds); 571135446Strhodes } else { 572170222Sdougb Assert.check(!boundErroneous); 573135446Strhodes //this is a fresh (synthetic) tvar 574170222Sdougb JCDiagnostic d = diags.fragment("where.fresh.typevar", t, bounds); 575135446Strhodes whereClauses.get(WhereClauseKind.TYPEVAR).put(t, d); 576135446Strhodes visit(bounds); 577135446Strhodes } 578170222Sdougb 579135446Strhodes } 580135446Strhodes return null; 581135446Strhodes } 582170222Sdougb }; 583170222Sdougb // </editor-fold> 584135446Strhodes 585170222Sdougb // <editor-fold defaultstate="collapsed" desc="symbol scanner"> 586135446Strhodes /** 587135446Strhodes * Preprocess a given symbol looking for (i) additional info (where clauses) to be 588135446Strhodes * added to the main diagnostic (ii) names to be compacted 589135446Strhodes */ 590135446Strhodes protected void preprocessSymbol(Symbol s) { 591135446Strhodes symbolPreprocessor.visit(s, null); 592170222Sdougb } 593135446Strhodes //where 594135446Strhodes protected Types.DefaultSymbolVisitor<Void, Void> symbolPreprocessor = 595135446Strhodes new Types.DefaultSymbolVisitor<Void, Void>() { 596135446Strhodes 597170222Sdougb @Override 598135446Strhodes public Void visitClassSymbol(ClassSymbol s, Void ignored) { 599170222Sdougb if (s.type.isCompound()) { 600135446Strhodes typePreprocessor.visit(s.type); 601170222Sdougb } else { 602135446Strhodes nameSimplifier.addUsage(s); 603135446Strhodes } 604135446Strhodes return null; 605170222Sdougb } 606135446Strhodes 607135446Strhodes @Override 608135446Strhodes public Void visitSymbol(Symbol s, Void ignored) { 609135446Strhodes return null; 610135446Strhodes } 611170222Sdougb 612135446Strhodes @Override 613135446Strhodes public Void visitMethodSymbol(MethodSymbol s, Void ignored) { 614135446Strhodes visit(s.owner, null); 615135446Strhodes if (s.type != null) 616135446Strhodes typePreprocessor.visit(s.type); 617135446Strhodes return null; 618135446Strhodes } 619135446Strhodes }; 620135446Strhodes // </editor-fold> 621170222Sdougb 622135446Strhodes @Override 623170222Sdougb public RichConfiguration getConfiguration() { 624135446Strhodes //the following cast is always safe - see init 625170222Sdougb return (RichConfiguration)configuration; 626135446Strhodes } 627135446Strhodes 628135446Strhodes /** 629135446Strhodes * Configuration object provided by the rich formatter. 630135446Strhodes */ 631170222Sdougb public static class RichConfiguration extends ForwardingDiagnosticFormatter.ForwardingConfiguration { 632135446Strhodes 633170222Sdougb /** set of enabled rich formatter's features */ 634135446Strhodes protected java.util.EnumSet<RichFormatterFeature> features; 635135446Strhodes 636135446Strhodes @SuppressWarnings("fallthrough") 637135446Strhodes public RichConfiguration(Options options, AbstractDiagnosticFormatter formatter) { 638135446Strhodes super(formatter.getConfiguration()); 639135446Strhodes features = formatter.isRaw() ? EnumSet.noneOf(RichFormatterFeature.class) : 640135446Strhodes EnumSet.of(RichFormatterFeature.SIMPLE_NAMES, 641135446Strhodes RichFormatterFeature.WHERE_CLAUSES, 642135446Strhodes RichFormatterFeature.UNIQUE_TYPEVAR_NAMES); 643135446Strhodes String diagOpts = options.get("diags"); 644135446Strhodes if (diagOpts != null) { 645135446Strhodes for (String args: diagOpts.split(",")) { 646170222Sdougb if (args.equals("-where")) { 647135446Strhodes features.remove(RichFormatterFeature.WHERE_CLAUSES); 648135446Strhodes } 649135446Strhodes else if (args.equals("where")) { 650170222Sdougb features.add(RichFormatterFeature.WHERE_CLAUSES); 651135446Strhodes } 652135446Strhodes if (args.equals("-simpleNames")) { 653170222Sdougb features.remove(RichFormatterFeature.SIMPLE_NAMES); 654135446Strhodes } 655135446Strhodes else if (args.equals("simpleNames")) { 656135446Strhodes features.add(RichFormatterFeature.SIMPLE_NAMES); 657135446Strhodes } 658135446Strhodes if (args.equals("-disambiguateTvars")) { 659135446Strhodes features.remove(RichFormatterFeature.UNIQUE_TYPEVAR_NAMES); 660170222Sdougb } 661135446Strhodes else if (args.equals("disambiguateTvars")) { 662170222Sdougb features.add(RichFormatterFeature.UNIQUE_TYPEVAR_NAMES); 663135446Strhodes } 664170222Sdougb } 665135446Strhodes } 666135446Strhodes } 667135446Strhodes 668170222Sdougb /** 669135446Strhodes * Returns a list of all the features supported by the rich formatter. 670135446Strhodes * @return list of supported features 671135446Strhodes */ 672170222Sdougb public RichFormatterFeature[] getAvailableFeatures() { 673170222Sdougb return RichFormatterFeature.values(); 674193149Sdougb } 675135446Strhodes 676170222Sdougb /** 677135446Strhodes * Enable a specific feature on this rich formatter. 678135446Strhodes * @param feature feature to be enabled 679135446Strhodes */ 680135446Strhodes public void enable(RichFormatterFeature feature) { 681135446Strhodes features.add(feature); 682135446Strhodes } 683135446Strhodes 684135446Strhodes /** 685170222Sdougb * Disable a specific feature on this rich formatter. 686135446Strhodes * @param feature feature to be disabled 687135446Strhodes */ 688135446Strhodes public void disable(RichFormatterFeature feature) { 689135446Strhodes features.remove(feature); 690170222Sdougb } 691135446Strhodes 692170222Sdougb /** 693170222Sdougb * Is a given feature enabled on this formatter? 694135446Strhodes * @param feature feature to be tested 695135446Strhodes */ 696135446Strhodes public boolean isEnabled(RichFormatterFeature feature) { 697170222Sdougb return features.contains(feature); 698135446Strhodes } 699135446Strhodes 700135446Strhodes /** 701170222Sdougb * The advanced formatting features provided by the rich formatter 702135446Strhodes */ 703135446Strhodes public enum RichFormatterFeature { 704170222Sdougb /** a list of additional info regarding a given type/symbol */ 705135446Strhodes WHERE_CLAUSES, 706135446Strhodes /** full class names simplification (where possible) */ 707135446Strhodes SIMPLE_NAMES, 708135446Strhodes /** type-variable names disambiguation */ 709170222Sdougb UNIQUE_TYPEVAR_NAMES 710135446Strhodes } 711135446Strhodes } 712135446Strhodes} 713135446Strhodes