mcimadamore@288: /* jjh@1305: * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved. mcimadamore@288: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. mcimadamore@288: * mcimadamore@288: * This code is free software; you can redistribute it and/or modify it mcimadamore@288: * under the terms of the GNU General Public License version 2 only, as ohair@554: * published by the Free Software Foundation. Oracle designates this mcimadamore@288: * particular file as subject to the "Classpath" exception as provided ohair@554: * by Oracle in the LICENSE file that accompanied this code. mcimadamore@288: * mcimadamore@288: * This code is distributed in the hope that it will be useful, but WITHOUT mcimadamore@288: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or mcimadamore@288: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License mcimadamore@288: * version 2 for more details (a copy is included in the LICENSE file that mcimadamore@288: * accompanied this code). mcimadamore@288: * mcimadamore@288: * You should have received a copy of the GNU General Public License version mcimadamore@288: * 2 along with this work; if not, write to the Free Software Foundation, mcimadamore@288: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. mcimadamore@288: * ohair@554: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ohair@554: * or visit www.oracle.com if you need additional information or have any ohair@554: * questions. mcimadamore@288: */ mcimadamore@288: package com.sun.tools.javac.util; mcimadamore@288: mcimadamore@288: import java.util.EnumSet; mcimadamore@288: import java.util.HashMap; mcimadamore@288: import java.util.LinkedHashMap; mcimadamore@288: import java.util.Locale; mcimadamore@288: import java.util.Map; mcimadamore@288: mcimadamore@288: import com.sun.tools.javac.code.Kinds; mcimadamore@288: import com.sun.tools.javac.code.Printer; mcimadamore@288: import com.sun.tools.javac.code.Symbol; mcimadamore@288: import com.sun.tools.javac.code.Symbol.*; mcimadamore@288: import com.sun.tools.javac.code.Symtab; mcimadamore@288: import com.sun.tools.javac.code.Type; mcimadamore@288: import com.sun.tools.javac.code.Type.*; mcimadamore@288: import com.sun.tools.javac.code.Types; mcimadamore@288: jjg@1374: import static com.sun.tools.javac.code.TypeTag.*; mcimadamore@288: import static com.sun.tools.javac.code.Flags.*; mcimadamore@288: import static com.sun.tools.javac.util.LayoutCharacters.*; mcimadamore@288: import static com.sun.tools.javac.util.RichDiagnosticFormatter.RichConfiguration.*; mcimadamore@288: mcimadamore@288: /** mcimadamore@288: * A rich diagnostic formatter is a formatter that provides better integration mcimadamore@288: * with javac's type system. A diagostic is first preprocessed in order to keep mcimadamore@288: * track of each types/symbols in it; after these informations are collected, mcimadamore@288: * the diagnostic is rendered using a standard formatter, whose type/symbol printer mcimadamore@288: * has been replaced by a more refined version provided by this rich formatter. mcimadamore@288: * The rich formatter currently enables three different features: (i) simple class mcimadamore@288: * names - that is class names are displayed used a non qualified name (thus mcimadamore@288: * omitting package info) whenever possible - (ii) where clause list - a list of mcimadamore@288: * additional subdiagnostics that provide specific info about type-variables, mcimadamore@288: * captured types, intersection types that occur in the diagnostic that is to be mcimadamore@288: * formatted and (iii) type-variable disambiguation - when the diagnostic refers mcimadamore@288: * to two different type-variables with the same name, their representation is mcimadamore@288: * disambiguated by appending an index to the type variable name. jjg@333: * jjg@581: *

This is NOT part of any supported API. jjg@333: * If you write code that depends on this, you do so at your own risk. jjg@333: * This code and its internal interfaces are subject to change or jjg@333: * deletion without notice. mcimadamore@288: */ mcimadamore@288: public class RichDiagnosticFormatter extends mcimadamore@288: ForwardingDiagnosticFormatter { mcimadamore@288: mcimadamore@288: final Symtab syms; mcimadamore@288: final Types types; mcimadamore@288: final JCDiagnostic.Factory diags; mcimadamore@288: final JavacMessages messages; mcimadamore@288: mcimadamore@288: /* name simplifier used by this formatter */ mcimadamore@304: protected ClassNameSimplifier nameSimplifier; mcimadamore@304: mcimadamore@304: /* type/symbol printer used by this formatter */ mcimadamore@304: private RichPrinter printer; mcimadamore@288: mcimadamore@288: /* map for keeping track of a where clause associated to a given type */ mcimadamore@288: Map> whereClauses; mcimadamore@288: mcimadamore@288: /** Get the DiagnosticFormatter instance for this context. */ mcimadamore@288: public static RichDiagnosticFormatter instance(Context context) { mcimadamore@288: RichDiagnosticFormatter instance = context.get(RichDiagnosticFormatter.class); mcimadamore@288: if (instance == null) mcimadamore@288: instance = new RichDiagnosticFormatter(context); mcimadamore@288: return instance; mcimadamore@288: } mcimadamore@288: mcimadamore@288: protected RichDiagnosticFormatter(Context context) { mcimadamore@288: super((AbstractDiagnosticFormatter)Log.instance(context).getDiagnosticFormatter()); mcimadamore@304: setRichPrinter(new RichPrinter()); mcimadamore@288: this.syms = Symtab.instance(context); mcimadamore@288: this.diags = JCDiagnostic.Factory.instance(context); mcimadamore@288: this.types = Types.instance(context); mcimadamore@288: this.messages = JavacMessages.instance(context); mcimadamore@288: whereClauses = new LinkedHashMap>(); mcimadamore@288: configuration = new RichConfiguration(Options.instance(context), formatter); mcimadamore@288: for (WhereClauseKind kind : WhereClauseKind.values()) mcimadamore@288: whereClauses.put(kind, new LinkedHashMap()); mcimadamore@288: } mcimadamore@288: mcimadamore@288: @Override mcimadamore@288: public String format(JCDiagnostic diag, Locale l) { mcimadamore@288: StringBuilder sb = new StringBuilder(); mcimadamore@288: nameSimplifier = new ClassNameSimplifier(); mcimadamore@288: for (WhereClauseKind kind : WhereClauseKind.values()) mcimadamore@288: whereClauses.get(kind).clear(); mcimadamore@288: preprocessDiagnostic(diag); mcimadamore@288: sb.append(formatter.format(diag, l)); mcimadamore@288: if (getConfiguration().isEnabled(RichFormatterFeature.WHERE_CLAUSES)) { mcimadamore@288: List clauses = getWhereClauses(); mcimadamore@288: String indent = formatter.isRaw() ? "" : mcimadamore@288: formatter.indentString(DetailsInc); mcimadamore@288: for (JCDiagnostic d : clauses) { mcimadamore@288: String whereClause = formatter.format(d, l); mcimadamore@288: if (whereClause.length() > 0) { mcimadamore@288: sb.append('\n' + indent + whereClause); mcimadamore@288: } mcimadamore@288: } mcimadamore@288: } mcimadamore@288: return sb.toString(); mcimadamore@288: } mcimadamore@288: mcimadamore@288: /** mcimadamore@304: * Sets the type/symbol printer used by this formatter. mcimadamore@304: * @param printer the rich printer to be set mcimadamore@304: */ mcimadamore@304: protected void setRichPrinter(RichPrinter printer) { mcimadamore@304: this.printer = printer; mcimadamore@304: formatter.setPrinter(printer); mcimadamore@304: } mcimadamore@304: mcimadamore@304: /** mcimadamore@304: * Gets the type/symbol printer used by this formatter. mcimadamore@304: * @return type/symbol rich printer mcimadamore@304: */ mcimadamore@304: protected RichPrinter getRichPrinter() { mcimadamore@304: return printer; mcimadamore@304: } mcimadamore@304: mcimadamore@304: /** mcimadamore@288: * Preprocess a given diagnostic by looking both into its arguments and into mcimadamore@288: * its subdiagnostics (if any). This preprocessing is responsible for mcimadamore@288: * generating info corresponding to features like where clauses, name mcimadamore@288: * simplification, etc. mcimadamore@288: * mcimadamore@288: * @param diag the diagnostic to be preprocessed mcimadamore@288: */ mcimadamore@288: protected void preprocessDiagnostic(JCDiagnostic diag) { mcimadamore@288: for (Object o : diag.getArgs()) { mcimadamore@288: if (o != null) { mcimadamore@288: preprocessArgument(o); mcimadamore@288: } mcimadamore@288: } mcimadamore@288: if (diag.isMultiline()) { mcimadamore@288: for (JCDiagnostic d : diag.getSubdiagnostics()) mcimadamore@288: preprocessDiagnostic(d); mcimadamore@288: } mcimadamore@288: } mcimadamore@288: mcimadamore@288: /** mcimadamore@288: * Preprocess a diagnostic argument. A type/symbol argument is mcimadamore@288: * preprocessed by specialized type/symbol preprocessors. mcimadamore@288: * mcimadamore@288: * @param arg the argument to be translated mcimadamore@288: */ mcimadamore@288: protected void preprocessArgument(Object arg) { mcimadamore@288: if (arg instanceof Type) { mcimadamore@288: preprocessType((Type)arg); mcimadamore@288: } mcimadamore@288: else if (arg instanceof Symbol) { mcimadamore@288: preprocessSymbol((Symbol)arg); mcimadamore@288: } mcimadamore@288: else if (arg instanceof JCDiagnostic) { mcimadamore@288: preprocessDiagnostic((JCDiagnostic)arg); mcimadamore@288: } mcimadamore@288: else if (arg instanceof Iterable) { mcimadamore@288: for (Object o : (Iterable)arg) { mcimadamore@288: preprocessArgument(o); mcimadamore@288: } mcimadamore@288: } mcimadamore@288: } mcimadamore@288: mcimadamore@288: /** mcimadamore@288: * Build a list of multiline diagnostics containing detailed info about mcimadamore@288: * type-variables, captured types, and intersection types mcimadamore@288: * mcimadamore@288: * @return where clause list mcimadamore@288: */ mcimadamore@288: protected List getWhereClauses() { mcimadamore@288: List clauses = List.nil(); mcimadamore@288: for (WhereClauseKind kind : WhereClauseKind.values()) { mcimadamore@288: List lines = List.nil(); mcimadamore@288: for (Map.Entry entry : whereClauses.get(kind).entrySet()) { mcimadamore@288: lines = lines.prepend(entry.getValue()); mcimadamore@288: } mcimadamore@288: if (!lines.isEmpty()) { mcimadamore@288: String key = kind.key(); mcimadamore@288: if (lines.size() > 1) mcimadamore@288: key += ".1"; mcimadamore@288: JCDiagnostic d = diags.fragment(key, whereClauses.get(kind).keySet()); mcimadamore@288: d = new JCDiagnostic.MultilineDiagnostic(d, lines.reverse()); mcimadamore@288: clauses = clauses.prepend(d); mcimadamore@288: } mcimadamore@288: } mcimadamore@288: return clauses.reverse(); mcimadamore@288: } mcimadamore@342: mcimadamore@342: private int indexOf(Type type, WhereClauseKind kind) { mcimadamore@342: int index = 1; mcimadamore@342: for (Type t : whereClauses.get(kind).keySet()) { mcimadamore@342: if (t.tsym == type.tsym) { mcimadamore@342: return index; mcimadamore@342: } mcimadamore@342: if (kind != WhereClauseKind.TYPEVAR || mcimadamore@342: t.toString().equals(type.toString())) { mcimadamore@342: index++; mcimadamore@342: } mcimadamore@342: } mcimadamore@342: return -1; mcimadamore@342: } mcimadamore@342: mcimadamore@342: private boolean unique(TypeVar typevar) { mcimadamore@342: int found = 0; mcimadamore@342: for (Type t : whereClauses.get(WhereClauseKind.TYPEVAR).keySet()) { mcimadamore@342: if (t.toString().equals(typevar.toString())) { mcimadamore@342: found++; mcimadamore@342: } mcimadamore@342: } mcimadamore@342: if (found < 1) mcimadamore@342: throw new AssertionError("Missing type variable in where clause " + typevar); mcimadamore@342: return found == 1; mcimadamore@342: } mcimadamore@288: //where mcimadamore@288: /** mcimadamore@288: * This enum defines all posssible kinds of where clauses that can be mcimadamore@288: * attached by a rich diagnostic formatter to a given diagnostic mcimadamore@288: */ mcimadamore@288: enum WhereClauseKind { mcimadamore@288: mcimadamore@288: /** where clause regarding a type variable */ mcimadamore@288: TYPEVAR("where.description.typevar"), mcimadamore@288: /** where clause regarding a captured type */ mcimadamore@288: CAPTURED("where.description.captured"), mcimadamore@288: /** where clause regarding an intersection type */ mcimadamore@288: INTERSECTION("where.description.intersection"); mcimadamore@288: mcimadamore@288: /** resource key for this where clause kind */ vromero@1442: private final String key; mcimadamore@288: mcimadamore@288: WhereClauseKind(String key) { mcimadamore@288: this.key = key; mcimadamore@288: } mcimadamore@288: mcimadamore@288: String key() { mcimadamore@288: return key; mcimadamore@288: } mcimadamore@288: } mcimadamore@288: mcimadamore@288: // mcimadamore@288: /** mcimadamore@288: * A name simplifier keeps track of class names usages in order to determine mcimadamore@288: * whether a class name can be compacted or not. Short names are not used mcimadamore@288: * if a conflict is detected, e.g. when two classes with the same simple mcimadamore@288: * name belong to different packages - in this case the formatter reverts mcimadamore@288: * to fullnames as compact names might lead to a confusing diagnostic. mcimadamore@288: */ mcimadamore@304: protected class ClassNameSimplifier { mcimadamore@288: mcimadamore@288: /* table for keeping track of all short name usages */ mcimadamore@288: Map> nameClashes = new HashMap>(); mcimadamore@288: mcimadamore@288: /** mcimadamore@288: * Add a name usage to the simplifier's internal cache mcimadamore@288: */ mcimadamore@288: protected void addUsage(Symbol sym) { mcimadamore@288: Name n = sym.getSimpleName(); mcimadamore@288: List conflicts = nameClashes.get(n); mcimadamore@288: if (conflicts == null) { mcimadamore@288: conflicts = List.nil(); mcimadamore@288: } mcimadamore@288: if (!conflicts.contains(sym)) mcimadamore@288: nameClashes.put(n, conflicts.append(sym)); mcimadamore@288: } mcimadamore@288: mcimadamore@288: public String simplify(Symbol s) { mcimadamore@288: String name = s.getQualifiedName().toString(); mcimadamore@288: if (!s.type.isCompound()) { mcimadamore@288: List conflicts = nameClashes.get(s.getSimpleName()); mcimadamore@288: if (conflicts == null || mcimadamore@288: (conflicts.size() == 1 && mcimadamore@288: conflicts.contains(s))) { mcimadamore@288: List l = List.nil(); mcimadamore@288: Symbol s2 = s; jjg@1374: while (s2.type.getEnclosingType().hasTag(CLASS) mcimadamore@288: && s2.owner.kind == Kinds.TYP) { mcimadamore@288: l = l.prepend(s2.getSimpleName()); mcimadamore@288: s2 = s2.owner; mcimadamore@288: } mcimadamore@288: l = l.prepend(s2.getSimpleName()); mcimadamore@288: StringBuilder buf = new StringBuilder(); mcimadamore@288: String sep = ""; mcimadamore@288: for (Name n2 : l) { mcimadamore@288: buf.append(sep); mcimadamore@288: buf.append(n2); mcimadamore@288: sep = "."; mcimadamore@288: } mcimadamore@288: name = buf.toString(); mcimadamore@288: } mcimadamore@288: } mcimadamore@288: return name; mcimadamore@288: } mcimadamore@288: }; mcimadamore@288: // mcimadamore@288: mcimadamore@288: // mcimadamore@288: /** mcimadamore@288: * Enhanced type/symbol printer that provides support for features like simple names mcimadamore@288: * and type variable disambiguation. This enriched printer exploits the info mcimadamore@288: * discovered during type/symbol preprocessing. This printer is set on the delegate mcimadamore@288: * formatter so that rich type/symbol info can be properly rendered. mcimadamore@288: */ mcimadamore@304: protected class RichPrinter extends Printer { mcimadamore@288: mcimadamore@288: @Override mcimadamore@288: public String localize(Locale locale, String key, Object... args) { mcimadamore@288: return formatter.localize(locale, key, args); mcimadamore@288: } mcimadamore@288: mcimadamore@288: @Override mcimadamore@288: public String capturedVarId(CapturedType t, Locale locale) { mcimadamore@288: return indexOf(t, WhereClauseKind.CAPTURED) + ""; mcimadamore@288: } mcimadamore@288: mcimadamore@288: @Override mcimadamore@288: public String visitType(Type t, Locale locale) { mcimadamore@288: String s = super.visitType(t, locale); mcimadamore@288: if (t == syms.botType) mcimadamore@288: s = localize(locale, "compiler.misc.type.null"); mcimadamore@288: return s; mcimadamore@288: } mcimadamore@288: mcimadamore@288: @Override mcimadamore@288: public String visitCapturedType(CapturedType t, Locale locale) { mcimadamore@288: if (getConfiguration().isEnabled(RichFormatterFeature.WHERE_CLAUSES)) { mcimadamore@288: return localize(locale, mcimadamore@288: "compiler.misc.captured.type", mcimadamore@288: indexOf(t, WhereClauseKind.CAPTURED)); mcimadamore@288: } mcimadamore@288: else mcimadamore@288: return super.visitCapturedType(t, locale); mcimadamore@288: } mcimadamore@288: mcimadamore@288: @Override mcimadamore@288: public String visitClassType(ClassType t, Locale locale) { mcimadamore@288: if (t.isCompound() && mcimadamore@288: getConfiguration().isEnabled(RichFormatterFeature.WHERE_CLAUSES)) { mcimadamore@288: return localize(locale, mcimadamore@288: "compiler.misc.intersection.type", mcimadamore@288: indexOf(t, WhereClauseKind.INTERSECTION)); mcimadamore@288: } mcimadamore@288: else mcimadamore@288: return super.visitClassType(t, locale); mcimadamore@288: } mcimadamore@288: mcimadamore@288: @Override mcimadamore@288: protected String className(ClassType t, boolean longform, Locale locale) { mcimadamore@288: Symbol sym = t.tsym; mcimadamore@288: if (sym.name.length() == 0 || mcimadamore@288: !getConfiguration().isEnabled(RichFormatterFeature.SIMPLE_NAMES)) { mcimadamore@288: return super.className(t, longform, locale); mcimadamore@288: } mcimadamore@288: else if (longform) mcimadamore@288: return nameSimplifier.simplify(sym).toString(); mcimadamore@288: else mcimadamore@288: return sym.name.toString(); mcimadamore@288: } mcimadamore@288: mcimadamore@288: @Override mcimadamore@288: public String visitTypeVar(TypeVar t, Locale locale) { mcimadamore@288: if (unique(t) || mcimadamore@288: !getConfiguration().isEnabled(RichFormatterFeature.UNIQUE_TYPEVAR_NAMES)) { mcimadamore@288: return t.toString(); mcimadamore@288: } mcimadamore@288: else { mcimadamore@288: return localize(locale, mcimadamore@288: "compiler.misc.type.var", mcimadamore@288: t.toString(), indexOf(t, WhereClauseKind.TYPEVAR)); mcimadamore@288: } mcimadamore@288: } mcimadamore@288: mcimadamore@288: @Override mcimadamore@288: public String visitClassSymbol(ClassSymbol s, Locale locale) { mcimadamore@288: String name = nameSimplifier.simplify(s); mcimadamore@288: if (name.length() == 0 || mcimadamore@288: !getConfiguration().isEnabled(RichFormatterFeature.SIMPLE_NAMES)) { mcimadamore@288: return super.visitClassSymbol(s, locale); mcimadamore@288: } mcimadamore@288: else { mcimadamore@288: return name; mcimadamore@288: } mcimadamore@288: } mcimadamore@288: mcimadamore@288: @Override mcimadamore@288: public String visitMethodSymbol(MethodSymbol s, Locale locale) { mcimadamore@288: String ownerName = visit(s.owner, locale); mcimadamore@1085: if (s.isStaticOrInstanceInit()) { mcimadamore@288: return ownerName; mcimadamore@288: } else { mcimadamore@288: String ms = (s.name == s.name.table.names.init) mcimadamore@288: ? ownerName mcimadamore@288: : s.name.toString(); mcimadamore@288: if (s.type != null) { jjg@1374: if (s.type.hasTag(FORALL)) { mcimadamore@288: ms = "<" + visitTypes(s.type.getTypeArguments(), locale) + ">" + ms; mcimadamore@288: } mcimadamore@288: ms += "(" + printMethodArgs( mcimadamore@288: s.type.getParameterTypes(), mcimadamore@288: (s.flags() & VARARGS) != 0, mcimadamore@288: locale) + ")"; mcimadamore@288: } mcimadamore@288: return ms; mcimadamore@288: } mcimadamore@288: } mcimadamore@288: }; mcimadamore@288: // mcimadamore@288: mcimadamore@288: // mcimadamore@288: /** mcimadamore@288: * Preprocess a given type looking for (i) additional info (where clauses) to be mcimadamore@288: * added to the main diagnostic (ii) names to be compacted. mcimadamore@288: */ mcimadamore@288: protected void preprocessType(Type t) { mcimadamore@288: typePreprocessor.visit(t); mcimadamore@288: } mcimadamore@288: //where mcimadamore@288: protected Types.UnaryVisitor typePreprocessor = mcimadamore@288: new Types.UnaryVisitor() { mcimadamore@288: mcimadamore@288: public Void visit(List ts) { mcimadamore@288: for (Type t : ts) mcimadamore@288: visit(t); mcimadamore@288: return null; mcimadamore@288: } mcimadamore@288: mcimadamore@288: @Override mcimadamore@288: public Void visitForAll(ForAll t, Void ignored) { mcimadamore@288: visit(t.tvars); mcimadamore@288: visit(t.qtype); mcimadamore@288: return null; mcimadamore@288: } mcimadamore@288: mcimadamore@288: @Override mcimadamore@288: public Void visitMethodType(MethodType t, Void ignored) { mcimadamore@288: visit(t.argtypes); mcimadamore@288: visit(t.restype); mcimadamore@288: return null; mcimadamore@288: } mcimadamore@288: mcimadamore@288: @Override mcimadamore@288: public Void visitErrorType(ErrorType t, Void ignored) { mcimadamore@288: Type ot = t.getOriginalType(); mcimadamore@288: if (ot != null) mcimadamore@288: visit(ot); mcimadamore@288: return null; mcimadamore@288: } mcimadamore@288: mcimadamore@288: @Override mcimadamore@288: public Void visitArrayType(ArrayType t, Void ignored) { mcimadamore@288: visit(t.elemtype); mcimadamore@288: return null; mcimadamore@288: } mcimadamore@288: mcimadamore@288: @Override mcimadamore@288: public Void visitWildcardType(WildcardType t, Void ignored) { mcimadamore@288: visit(t.type); mcimadamore@288: return null; mcimadamore@288: } mcimadamore@288: mcimadamore@288: public Void visitType(Type t, Void ignored) { mcimadamore@288: return null; mcimadamore@288: } mcimadamore@288: mcimadamore@288: @Override mcimadamore@288: public Void visitCapturedType(CapturedType t, Void ignored) { mcimadamore@342: if (indexOf(t, WhereClauseKind.CAPTURED) == -1) { mcimadamore@288: String suffix = t.lower == syms.botType ? ".1" : ""; mcimadamore@288: JCDiagnostic d = diags.fragment("where.captured"+ suffix, t, t.bound, t.lower, t.wildcard); mcimadamore@288: whereClauses.get(WhereClauseKind.CAPTURED).put(t, d); mcimadamore@288: visit(t.wildcard); mcimadamore@288: visit(t.lower); mcimadamore@288: visit(t.bound); mcimadamore@288: } mcimadamore@288: return null; mcimadamore@288: } mcimadamore@288: mcimadamore@288: @Override mcimadamore@288: public Void visitClassType(ClassType t, Void ignored) { mcimadamore@288: if (t.isCompound()) { mcimadamore@342: if (indexOf(t, WhereClauseKind.INTERSECTION) == -1) { mcimadamore@288: Type supertype = types.supertype(t); mcimadamore@288: List interfaces = types.interfaces(t); mcimadamore@288: JCDiagnostic d = diags.fragment("where.intersection", t, interfaces.prepend(supertype)); mcimadamore@288: whereClauses.get(WhereClauseKind.INTERSECTION).put(t, d); mcimadamore@288: visit(supertype); mcimadamore@288: visit(interfaces); mcimadamore@288: } mcimadamore@288: } mcimadamore@288: nameSimplifier.addUsage(t.tsym); mcimadamore@288: visit(t.getTypeArguments()); mcimadamore@288: if (t.getEnclosingType() != Type.noType) mcimadamore@288: visit(t.getEnclosingType()); mcimadamore@288: return null; mcimadamore@288: } mcimadamore@288: mcimadamore@288: @Override mcimadamore@288: public Void visitTypeVar(TypeVar t, Void ignored) { mcimadamore@342: if (indexOf(t, WhereClauseKind.TYPEVAR) == -1) { mcimadamore@343: //access the bound type and skip error types mcimadamore@288: Type bound = t.bound; mcimadamore@288: while ((bound instanceof ErrorType)) mcimadamore@288: bound = ((ErrorType)bound).getOriginalType(); mcimadamore@343: //retrieve the bound list - if the type variable mcimadamore@343: //has not been attributed the bound is not set mcimadamore@1415: List bounds = (bound != null) && mcimadamore@1415: (bound.hasTag(CLASS) || bound.hasTag(TYPEVAR)) ? mcimadamore@343: types.getBounds(t) : mcimadamore@343: List.nil(); mcimadamore@343: mcimadamore@288: nameSimplifier.addUsage(t.tsym); mcimadamore@288: mcimadamore@288: boolean boundErroneous = bounds.head == null || jjg@1374: bounds.head.hasTag(NONE) || jjg@1374: bounds.head.hasTag(ERROR); mcimadamore@288: mcimadamore@1251: if ((t.tsym.flags() & SYNTHETIC) == 0) { mcimadamore@1251: //this is a true typevar mcimadamore@1251: JCDiagnostic d = diags.fragment("where.typevar" + mcimadamore@288: (boundErroneous ? ".1" : ""), t, bounds, mcimadamore@288: Kinds.kindName(t.tsym.location()), t.tsym.location()); mcimadamore@1251: whereClauses.get(WhereClauseKind.TYPEVAR).put(t, d); mcimadamore@1251: symbolPreprocessor.visit(t.tsym.location(), null); mcimadamore@1251: visit(bounds); mcimadamore@1251: } else { mcimadamore@1251: Assert.check(!boundErroneous); mcimadamore@1251: //this is a fresh (synthetic) tvar mcimadamore@1251: JCDiagnostic d = diags.fragment("where.fresh.typevar", t, bounds); mcimadamore@1251: whereClauses.get(WhereClauseKind.TYPEVAR).put(t, d); mcimadamore@1251: visit(bounds); mcimadamore@1251: } mcimadamore@1251: mcimadamore@288: } mcimadamore@288: return null; mcimadamore@288: } mcimadamore@288: }; mcimadamore@288: // mcimadamore@288: mcimadamore@288: // mcimadamore@288: /** mcimadamore@288: * Preprocess a given symbol looking for (i) additional info (where clauses) to be mcimadamore@288: * asdded to the main diagnostic (ii) names to be compacted mcimadamore@288: */ mcimadamore@288: protected void preprocessSymbol(Symbol s) { mcimadamore@288: symbolPreprocessor.visit(s, null); mcimadamore@288: } mcimadamore@288: //where mcimadamore@288: protected Types.DefaultSymbolVisitor symbolPreprocessor = mcimadamore@288: new Types.DefaultSymbolVisitor() { mcimadamore@288: mcimadamore@288: @Override mcimadamore@288: public Void visitClassSymbol(ClassSymbol s, Void ignored) { mcimadamore@288: nameSimplifier.addUsage(s); mcimadamore@288: return null; mcimadamore@288: } mcimadamore@288: mcimadamore@288: @Override mcimadamore@288: public Void visitSymbol(Symbol s, Void ignored) { mcimadamore@288: return null; mcimadamore@288: } mcimadamore@288: mcimadamore@288: @Override mcimadamore@288: public Void visitMethodSymbol(MethodSymbol s, Void ignored) { mcimadamore@288: visit(s.owner, null); mcimadamore@326: if (s.type != null) mcimadamore@326: typePreprocessor.visit(s.type); mcimadamore@288: return null; mcimadamore@288: } mcimadamore@288: }; mcimadamore@288: // mcimadamore@288: mcimadamore@288: @Override mcimadamore@288: public RichConfiguration getConfiguration() { mcimadamore@288: //the following cast is always safe - see init mcimadamore@288: return (RichConfiguration)configuration; mcimadamore@288: } mcimadamore@288: mcimadamore@288: /** mcimadamore@288: * Configuration object provided by the rich formatter. mcimadamore@288: */ mcimadamore@288: public static class RichConfiguration extends ForwardingDiagnosticFormatter.ForwardingConfiguration { mcimadamore@288: mcimadamore@288: /** set of enabled rich formatter's features */ mcimadamore@288: protected java.util.EnumSet features; mcimadamore@288: mcimadamore@288: @SuppressWarnings("fallthrough") mcimadamore@288: public RichConfiguration(Options options, AbstractDiagnosticFormatter formatter) { mcimadamore@288: super(formatter.getConfiguration()); mcimadamore@288: features = formatter.isRaw() ? EnumSet.noneOf(RichFormatterFeature.class) : mcimadamore@288: EnumSet.of(RichFormatterFeature.SIMPLE_NAMES, mcimadamore@288: RichFormatterFeature.WHERE_CLAUSES, mcimadamore@288: RichFormatterFeature.UNIQUE_TYPEVAR_NAMES); mcimadamore@288: String diagOpts = options.get("diags"); mcimadamore@288: if (diagOpts != null) { mcimadamore@288: for (String args: diagOpts.split(",")) { mcimadamore@288: if (args.equals("-where")) { mcimadamore@288: features.remove(RichFormatterFeature.WHERE_CLAUSES); mcimadamore@288: } mcimadamore@288: else if (args.equals("where")) { mcimadamore@288: features.add(RichFormatterFeature.WHERE_CLAUSES); mcimadamore@288: } mcimadamore@288: if (args.equals("-simpleNames")) { mcimadamore@288: features.remove(RichFormatterFeature.SIMPLE_NAMES); mcimadamore@288: } mcimadamore@288: else if (args.equals("simpleNames")) { mcimadamore@288: features.add(RichFormatterFeature.SIMPLE_NAMES); mcimadamore@288: } mcimadamore@288: if (args.equals("-disambiguateTvars")) { mcimadamore@288: features.remove(RichFormatterFeature.UNIQUE_TYPEVAR_NAMES); mcimadamore@288: } mcimadamore@288: else if (args.equals("disambiguateTvars")) { mcimadamore@288: features.add(RichFormatterFeature.UNIQUE_TYPEVAR_NAMES); mcimadamore@288: } mcimadamore@288: } mcimadamore@288: } mcimadamore@288: } mcimadamore@288: mcimadamore@288: /** mcimadamore@288: * Returns a list of all the features supported by the rich formatter. mcimadamore@288: * @return list of supported features mcimadamore@288: */ mcimadamore@288: public RichFormatterFeature[] getAvailableFeatures() { mcimadamore@288: return RichFormatterFeature.values(); mcimadamore@288: } mcimadamore@288: mcimadamore@288: /** mcimadamore@288: * Enable a specific feature on this rich formatter. mcimadamore@288: * @param feature feature to be enabled mcimadamore@288: */ mcimadamore@288: public void enable(RichFormatterFeature feature) { mcimadamore@288: features.add(feature); mcimadamore@288: } mcimadamore@288: mcimadamore@288: /** mcimadamore@288: * Disable a specific feature on this rich formatter. mcimadamore@288: * @param feature feature to be disabled mcimadamore@288: */ mcimadamore@288: public void disable(RichFormatterFeature feature) { mcimadamore@288: features.remove(feature); mcimadamore@288: } mcimadamore@288: mcimadamore@288: /** mcimadamore@288: * Is a given feature enabled on this formatter? mcimadamore@288: * @param feature feature to be tested mcimadamore@288: */ mcimadamore@288: public boolean isEnabled(RichFormatterFeature feature) { mcimadamore@288: return features.contains(feature); mcimadamore@288: } mcimadamore@288: mcimadamore@288: /** mcimadamore@288: * The advanced formatting features provided by the rich formatter mcimadamore@288: */ mcimadamore@288: public enum RichFormatterFeature { mcimadamore@288: /** a list of additional info regarding a given type/symbol */ mcimadamore@288: WHERE_CLAUSES, mcimadamore@288: /** full class names simplification (where possible) */ mcimadamore@288: SIMPLE_NAMES, mcimadamore@288: /** type-variable names disambiguation */ mcimadamore@288: UNIQUE_TYPEVAR_NAMES; mcimadamore@288: } mcimadamore@288: } mcimadamore@288: }