mcimadamore@288: /*
ohair@554: * Copyright (c) 2009, 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:
mcimadamore@288: import static com.sun.tools.javac.code.TypeTags.*;
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 */
mcimadamore@288: private 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;
mcimadamore@288: while (s2.type.getEnclosingType().tag == 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: protected String printMethodArgs(List args, boolean varArgs, Locale locale) {
mcimadamore@288: return super.printMethodArgs(args, varArgs, locale);
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) {
mcimadamore@288: if (s.type.tag == 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@343: List bounds = bound != null ?
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 ||
mcimadamore@288: bounds.head.tag == NONE ||
mcimadamore@288: bounds.head.tag == ERROR;
mcimadamore@288:
mcimadamore@288:
mcimadamore@288: JCDiagnostic d = diags.fragment("where.typevar" +
mcimadamore@288: (boundErroneous ? ".1" : ""), t, bounds,
mcimadamore@288: Kinds.kindName(t.tsym.location()), t.tsym.location());
mcimadamore@288: whereClauses.get(WhereClauseKind.TYPEVAR).put(t, d);
mcimadamore@288: symbolPreprocessor.visit(t.tsym.location(), null);
mcimadamore@288: visit(bounds);
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: }