Sat, 14 Sep 2013 19:04:47 +0100
7047734: javac, the LVT is not generated correctly in several scenarios
Reviewed-by: jjg, mcimadamore
1 /*
2 * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25 package com.sun.tools.javac.util;
27 import java.util.EnumMap;
28 import java.util.EnumSet;
29 import java.util.HashMap;
30 import java.util.LinkedHashMap;
31 import java.util.Locale;
32 import java.util.Map;
34 import com.sun.tools.javac.code.Kinds;
35 import com.sun.tools.javac.code.Printer;
36 import com.sun.tools.javac.code.Symbol;
37 import com.sun.tools.javac.code.Symbol.*;
38 import com.sun.tools.javac.code.Symtab;
39 import com.sun.tools.javac.code.Type;
40 import com.sun.tools.javac.code.Type.*;
41 import com.sun.tools.javac.code.Types;
43 import static com.sun.tools.javac.code.TypeTag.*;
44 import static com.sun.tools.javac.code.Flags.*;
45 import static com.sun.tools.javac.util.LayoutCharacters.*;
46 import static com.sun.tools.javac.util.RichDiagnosticFormatter.RichConfiguration.*;
48 /**
49 * A rich diagnostic formatter is a formatter that provides better integration
50 * with javac's type system. A diagostic is first preprocessed in order to keep
51 * track of each types/symbols in it; after these informations are collected,
52 * the diagnostic is rendered using a standard formatter, whose type/symbol printer
53 * has been replaced by a more refined version provided by this rich formatter.
54 * The rich formatter currently enables three different features: (i) simple class
55 * names - that is class names are displayed used a non qualified name (thus
56 * omitting package info) whenever possible - (ii) where clause list - a list of
57 * additional subdiagnostics that provide specific info about type-variables,
58 * captured types, intersection types that occur in the diagnostic that is to be
59 * formatted and (iii) type-variable disambiguation - when the diagnostic refers
60 * to two different type-variables with the same name, their representation is
61 * disambiguated by appending an index to the type variable name.
62 *
63 * <p><b>This is NOT part of any supported API.
64 * If you write code that depends on this, you do so at your own risk.
65 * This code and its internal interfaces are subject to change or
66 * deletion without notice.</b>
67 */
68 public class RichDiagnosticFormatter extends
69 ForwardingDiagnosticFormatter<JCDiagnostic, AbstractDiagnosticFormatter> {
71 final Symtab syms;
72 final Types types;
73 final JCDiagnostic.Factory diags;
74 final JavacMessages messages;
76 /* name simplifier used by this formatter */
77 protected ClassNameSimplifier nameSimplifier;
79 /* type/symbol printer used by this formatter */
80 private RichPrinter printer;
82 /* map for keeping track of a where clause associated to a given type */
83 Map<WhereClauseKind, Map<Type, JCDiagnostic>> whereClauses;
85 /** Get the DiagnosticFormatter instance for this context. */
86 public static RichDiagnosticFormatter instance(Context context) {
87 RichDiagnosticFormatter instance = context.get(RichDiagnosticFormatter.class);
88 if (instance == null)
89 instance = new RichDiagnosticFormatter(context);
90 return instance;
91 }
93 protected RichDiagnosticFormatter(Context context) {
94 super((AbstractDiagnosticFormatter)Log.instance(context).getDiagnosticFormatter());
95 setRichPrinter(new RichPrinter());
96 this.syms = Symtab.instance(context);
97 this.diags = JCDiagnostic.Factory.instance(context);
98 this.types = Types.instance(context);
99 this.messages = JavacMessages.instance(context);
100 whereClauses = new EnumMap<WhereClauseKind, Map<Type, JCDiagnostic>>(WhereClauseKind.class);
101 configuration = new RichConfiguration(Options.instance(context), formatter);
102 for (WhereClauseKind kind : WhereClauseKind.values())
103 whereClauses.put(kind, new LinkedHashMap<Type, JCDiagnostic>());
104 }
106 @Override
107 public String format(JCDiagnostic diag, Locale l) {
108 StringBuilder sb = new StringBuilder();
109 nameSimplifier = new ClassNameSimplifier();
110 for (WhereClauseKind kind : WhereClauseKind.values())
111 whereClauses.get(kind).clear();
112 preprocessDiagnostic(diag);
113 sb.append(formatter.format(diag, l));
114 if (getConfiguration().isEnabled(RichFormatterFeature.WHERE_CLAUSES)) {
115 List<JCDiagnostic> clauses = getWhereClauses();
116 String indent = formatter.isRaw() ? "" :
117 formatter.indentString(DetailsInc);
118 for (JCDiagnostic d : clauses) {
119 String whereClause = formatter.format(d, l);
120 if (whereClause.length() > 0) {
121 sb.append('\n' + indent + whereClause);
122 }
123 }
124 }
125 return sb.toString();
126 }
128 @Override
129 public String formatMessage(JCDiagnostic diag, Locale l) {
130 nameSimplifier = new ClassNameSimplifier();
131 preprocessDiagnostic(diag);
132 return super.formatMessage(diag, l);
133 }
135 /**
136 * Sets the type/symbol printer used by this formatter.
137 * @param printer the rich printer to be set
138 */
139 protected void setRichPrinter(RichPrinter printer) {
140 this.printer = printer;
141 formatter.setPrinter(printer);
142 }
144 /**
145 * Gets the type/symbol printer used by this formatter.
146 * @return type/symbol rich printer
147 */
148 protected RichPrinter getRichPrinter() {
149 return printer;
150 }
152 /**
153 * Preprocess a given diagnostic by looking both into its arguments and into
154 * its subdiagnostics (if any). This preprocessing is responsible for
155 * generating info corresponding to features like where clauses, name
156 * simplification, etc.
157 *
158 * @param diag the diagnostic to be preprocessed
159 */
160 protected void preprocessDiagnostic(JCDiagnostic diag) {
161 for (Object o : diag.getArgs()) {
162 if (o != null) {
163 preprocessArgument(o);
164 }
165 }
166 if (diag.isMultiline()) {
167 for (JCDiagnostic d : diag.getSubdiagnostics())
168 preprocessDiagnostic(d);
169 }
170 }
172 /**
173 * Preprocess a diagnostic argument. A type/symbol argument is
174 * preprocessed by specialized type/symbol preprocessors.
175 *
176 * @param arg the argument to be translated
177 */
178 protected void preprocessArgument(Object arg) {
179 if (arg instanceof Type) {
180 preprocessType((Type)arg);
181 }
182 else if (arg instanceof Symbol) {
183 preprocessSymbol((Symbol)arg);
184 }
185 else if (arg instanceof JCDiagnostic) {
186 preprocessDiagnostic((JCDiagnostic)arg);
187 }
188 else if (arg instanceof Iterable<?>) {
189 for (Object o : (Iterable<?>)arg) {
190 preprocessArgument(o);
191 }
192 }
193 }
195 /**
196 * Build a list of multiline diagnostics containing detailed info about
197 * type-variables, captured types, and intersection types
198 *
199 * @return where clause list
200 */
201 protected List<JCDiagnostic> getWhereClauses() {
202 List<JCDiagnostic> clauses = List.nil();
203 for (WhereClauseKind kind : WhereClauseKind.values()) {
204 List<JCDiagnostic> lines = List.nil();
205 for (Map.Entry<Type, JCDiagnostic> entry : whereClauses.get(kind).entrySet()) {
206 lines = lines.prepend(entry.getValue());
207 }
208 if (!lines.isEmpty()) {
209 String key = kind.key();
210 if (lines.size() > 1)
211 key += ".1";
212 JCDiagnostic d = diags.fragment(key, whereClauses.get(kind).keySet());
213 d = new JCDiagnostic.MultilineDiagnostic(d, lines.reverse());
214 clauses = clauses.prepend(d);
215 }
216 }
217 return clauses.reverse();
218 }
220 private int indexOf(Type type, WhereClauseKind kind) {
221 int index = 1;
222 for (Type t : whereClauses.get(kind).keySet()) {
223 if (t.tsym == type.tsym) {
224 return index;
225 }
226 if (kind != WhereClauseKind.TYPEVAR ||
227 t.toString().equals(type.toString())) {
228 index++;
229 }
230 }
231 return -1;
232 }
234 private boolean unique(TypeVar typevar) {
235 int found = 0;
236 for (Type t : whereClauses.get(WhereClauseKind.TYPEVAR).keySet()) {
237 if (t.toString().equals(typevar.toString())) {
238 found++;
239 }
240 }
241 if (found < 1)
242 throw new AssertionError("Missing type variable in where clause " + typevar);
243 return found == 1;
244 }
245 //where
246 /**
247 * This enum defines all posssible kinds of where clauses that can be
248 * attached by a rich diagnostic formatter to a given diagnostic
249 */
250 enum WhereClauseKind {
252 /** where clause regarding a type variable */
253 TYPEVAR("where.description.typevar"),
254 /** where clause regarding a captured type */
255 CAPTURED("where.description.captured"),
256 /** where clause regarding an intersection type */
257 INTERSECTION("where.description.intersection");
259 /** resource key for this where clause kind */
260 private final String key;
262 WhereClauseKind(String key) {
263 this.key = key;
264 }
266 String key() {
267 return key;
268 }
269 }
271 // <editor-fold defaultstate="collapsed" desc="name simplifier">
272 /**
273 * A name simplifier keeps track of class names usages in order to determine
274 * whether a class name can be compacted or not. Short names are not used
275 * if a conflict is detected, e.g. when two classes with the same simple
276 * name belong to different packages - in this case the formatter reverts
277 * to fullnames as compact names might lead to a confusing diagnostic.
278 */
279 protected class ClassNameSimplifier {
281 /* table for keeping track of all short name usages */
282 Map<Name, List<Symbol>> nameClashes = new HashMap<Name, List<Symbol>>();
284 /**
285 * Add a name usage to the simplifier's internal cache
286 */
287 protected void addUsage(Symbol sym) {
288 Name n = sym.getSimpleName();
289 List<Symbol> conflicts = nameClashes.get(n);
290 if (conflicts == null) {
291 conflicts = List.nil();
292 }
293 if (!conflicts.contains(sym))
294 nameClashes.put(n, conflicts.append(sym));
295 }
297 public String simplify(Symbol s) {
298 String name = s.getQualifiedName().toString();
299 if (!s.type.isCompound() && !s.type.isPrimitive()) {
300 List<Symbol> conflicts = nameClashes.get(s.getSimpleName());
301 if (conflicts == null ||
302 (conflicts.size() == 1 &&
303 conflicts.contains(s))) {
304 List<Name> l = List.nil();
305 Symbol s2 = s;
306 while (s2.type.hasTag(CLASS) &&
307 s2.type.getEnclosingType().hasTag(CLASS) &&
308 s2.owner.kind == Kinds.TYP) {
309 l = l.prepend(s2.getSimpleName());
310 s2 = s2.owner;
311 }
312 l = l.prepend(s2.getSimpleName());
313 StringBuilder buf = new StringBuilder();
314 String sep = "";
315 for (Name n2 : l) {
316 buf.append(sep);
317 buf.append(n2);
318 sep = ".";
319 }
320 name = buf.toString();
321 }
322 }
323 return name;
324 }
325 };
326 // </editor-fold>
328 // <editor-fold defaultstate="collapsed" desc="rich printer">
329 /**
330 * Enhanced type/symbol printer that provides support for features like simple names
331 * and type variable disambiguation. This enriched printer exploits the info
332 * discovered during type/symbol preprocessing. This printer is set on the delegate
333 * formatter so that rich type/symbol info can be properly rendered.
334 */
335 protected class RichPrinter extends Printer {
337 @Override
338 public String localize(Locale locale, String key, Object... args) {
339 return formatter.localize(locale, key, args);
340 }
342 @Override
343 public String capturedVarId(CapturedType t, Locale locale) {
344 return indexOf(t, WhereClauseKind.CAPTURED) + "";
345 }
347 @Override
348 public String visitType(Type t, Locale locale) {
349 String s = super.visitType(t, locale);
350 if (t == syms.botType)
351 s = localize(locale, "compiler.misc.type.null");
352 return s;
353 }
355 @Override
356 public String visitCapturedType(CapturedType t, Locale locale) {
357 if (getConfiguration().isEnabled(RichFormatterFeature.WHERE_CLAUSES)) {
358 return localize(locale,
359 "compiler.misc.captured.type",
360 indexOf(t, WhereClauseKind.CAPTURED));
361 }
362 else
363 return super.visitCapturedType(t, locale);
364 }
366 @Override
367 public String visitClassType(ClassType t, Locale locale) {
368 if (t.isCompound() &&
369 getConfiguration().isEnabled(RichFormatterFeature.WHERE_CLAUSES)) {
370 return localize(locale,
371 "compiler.misc.intersection.type",
372 indexOf(t, WhereClauseKind.INTERSECTION));
373 }
374 else
375 return super.visitClassType(t, locale);
376 }
378 @Override
379 protected String className(ClassType t, boolean longform, Locale locale) {
380 Symbol sym = t.tsym;
381 if (sym.name.length() == 0 ||
382 !getConfiguration().isEnabled(RichFormatterFeature.SIMPLE_NAMES)) {
383 return super.className(t, longform, locale);
384 }
385 else if (longform)
386 return nameSimplifier.simplify(sym).toString();
387 else
388 return sym.name.toString();
389 }
391 @Override
392 public String visitTypeVar(TypeVar t, Locale locale) {
393 if (unique(t) ||
394 !getConfiguration().isEnabled(RichFormatterFeature.UNIQUE_TYPEVAR_NAMES)) {
395 return t.toString();
396 }
397 else {
398 return localize(locale,
399 "compiler.misc.type.var",
400 t.toString(), indexOf(t, WhereClauseKind.TYPEVAR));
401 }
402 }
404 @Override
405 public String visitClassSymbol(ClassSymbol s, Locale locale) {
406 if (s.type.isCompound()) {
407 return visit(s.type, locale);
408 }
409 String name = nameSimplifier.simplify(s);
410 if (name.length() == 0 ||
411 !getConfiguration().isEnabled(RichFormatterFeature.SIMPLE_NAMES)) {
412 return super.visitClassSymbol(s, locale);
413 }
414 else {
415 return name;
416 }
417 }
419 @Override
420 public String visitMethodSymbol(MethodSymbol s, Locale locale) {
421 String ownerName = visit(s.owner, locale);
422 if (s.isStaticOrInstanceInit()) {
423 return ownerName;
424 } else {
425 String ms = (s.name == s.name.table.names.init)
426 ? ownerName
427 : s.name.toString();
428 if (s.type != null) {
429 if (s.type.hasTag(FORALL)) {
430 ms = "<" + visitTypes(s.type.getTypeArguments(), locale) + ">" + ms;
431 }
432 ms += "(" + printMethodArgs(
433 s.type.getParameterTypes(),
434 (s.flags() & VARARGS) != 0,
435 locale) + ")";
436 }
437 return ms;
438 }
439 }
440 };
441 // </editor-fold>
443 // <editor-fold defaultstate="collapsed" desc="type scanner">
444 /**
445 * Preprocess a given type looking for (i) additional info (where clauses) to be
446 * added to the main diagnostic (ii) names to be compacted.
447 */
448 protected void preprocessType(Type t) {
449 typePreprocessor.visit(t);
450 }
451 //where
452 protected Types.UnaryVisitor<Void> typePreprocessor =
453 new Types.UnaryVisitor<Void>() {
455 public Void visit(List<Type> ts) {
456 for (Type t : ts)
457 visit(t);
458 return null;
459 }
461 @Override
462 public Void visitForAll(ForAll t, Void ignored) {
463 visit(t.tvars);
464 visit(t.qtype);
465 return null;
466 }
468 @Override
469 public Void visitMethodType(MethodType t, Void ignored) {
470 visit(t.argtypes);
471 visit(t.restype);
472 return null;
473 }
475 @Override
476 public Void visitErrorType(ErrorType t, Void ignored) {
477 Type ot = t.getOriginalType();
478 if (ot != null)
479 visit(ot);
480 return null;
481 }
483 @Override
484 public Void visitArrayType(ArrayType t, Void ignored) {
485 visit(t.elemtype);
486 return null;
487 }
489 @Override
490 public Void visitWildcardType(WildcardType t, Void ignored) {
491 visit(t.type);
492 return null;
493 }
495 public Void visitType(Type t, Void ignored) {
496 return null;
497 }
499 @Override
500 public Void visitCapturedType(CapturedType t, Void ignored) {
501 if (indexOf(t, WhereClauseKind.CAPTURED) == -1) {
502 String suffix = t.lower == syms.botType ? ".1" : "";
503 JCDiagnostic d = diags.fragment("where.captured"+ suffix, t, t.bound, t.lower, t.wildcard);
504 whereClauses.get(WhereClauseKind.CAPTURED).put(t, d);
505 visit(t.wildcard);
506 visit(t.lower);
507 visit(t.bound);
508 }
509 return null;
510 }
512 @Override
513 public Void visitClassType(ClassType t, Void ignored) {
514 if (t.isCompound()) {
515 if (indexOf(t, WhereClauseKind.INTERSECTION) == -1) {
516 Type supertype = types.supertype(t);
517 List<Type> interfaces = types.interfaces(t);
518 JCDiagnostic d = diags.fragment("where.intersection", t, interfaces.prepend(supertype));
519 whereClauses.get(WhereClauseKind.INTERSECTION).put(t, d);
520 visit(supertype);
521 visit(interfaces);
522 }
523 } else if (t.tsym.name.isEmpty()) {
524 //anon class
525 ClassType norm = (ClassType) t.tsym.type;
526 if (norm != null) {
527 if (norm.interfaces_field != null && norm.interfaces_field.nonEmpty()) {
528 visit(norm.interfaces_field.head);
529 } else {
530 visit(norm.supertype_field);
531 }
532 }
533 }
534 nameSimplifier.addUsage(t.tsym);
535 visit(t.getTypeArguments());
536 if (t.getEnclosingType() != Type.noType)
537 visit(t.getEnclosingType());
538 return null;
539 }
541 @Override
542 public Void visitTypeVar(TypeVar t, Void ignored) {
543 if (indexOf(t, WhereClauseKind.TYPEVAR) == -1) {
544 //access the bound type and skip error types
545 Type bound = t.bound;
546 while ((bound instanceof ErrorType))
547 bound = ((ErrorType)bound).getOriginalType();
548 //retrieve the bound list - if the type variable
549 //has not been attributed the bound is not set
550 List<Type> bounds = (bound != null) &&
551 (bound.hasTag(CLASS) || bound.hasTag(TYPEVAR)) ?
552 types.getBounds(t) :
553 List.<Type>nil();
555 nameSimplifier.addUsage(t.tsym);
557 boolean boundErroneous = bounds.head == null ||
558 bounds.head.hasTag(NONE) ||
559 bounds.head.hasTag(ERROR);
561 if ((t.tsym.flags() & SYNTHETIC) == 0) {
562 //this is a true typevar
563 JCDiagnostic d = diags.fragment("where.typevar" +
564 (boundErroneous ? ".1" : ""), t, bounds,
565 Kinds.kindName(t.tsym.location()), t.tsym.location());
566 whereClauses.get(WhereClauseKind.TYPEVAR).put(t, d);
567 symbolPreprocessor.visit(t.tsym.location(), null);
568 visit(bounds);
569 } else {
570 Assert.check(!boundErroneous);
571 //this is a fresh (synthetic) tvar
572 JCDiagnostic d = diags.fragment("where.fresh.typevar", t, bounds);
573 whereClauses.get(WhereClauseKind.TYPEVAR).put(t, d);
574 visit(bounds);
575 }
577 }
578 return null;
579 }
580 };
581 // </editor-fold>
583 // <editor-fold defaultstate="collapsed" desc="symbol scanner">
584 /**
585 * Preprocess a given symbol looking for (i) additional info (where clauses) to be
586 * added to the main diagnostic (ii) names to be compacted
587 */
588 protected void preprocessSymbol(Symbol s) {
589 symbolPreprocessor.visit(s, null);
590 }
591 //where
592 protected Types.DefaultSymbolVisitor<Void, Void> symbolPreprocessor =
593 new Types.DefaultSymbolVisitor<Void, Void>() {
595 @Override
596 public Void visitClassSymbol(ClassSymbol s, Void ignored) {
597 if (s.type.isCompound()) {
598 typePreprocessor.visit(s.type);
599 } else {
600 nameSimplifier.addUsage(s);
601 }
602 return null;
603 }
605 @Override
606 public Void visitSymbol(Symbol s, Void ignored) {
607 return null;
608 }
610 @Override
611 public Void visitMethodSymbol(MethodSymbol s, Void ignored) {
612 visit(s.owner, null);
613 if (s.type != null)
614 typePreprocessor.visit(s.type);
615 return null;
616 }
617 };
618 // </editor-fold>
620 @Override
621 public RichConfiguration getConfiguration() {
622 //the following cast is always safe - see init
623 return (RichConfiguration)configuration;
624 }
626 /**
627 * Configuration object provided by the rich formatter.
628 */
629 public static class RichConfiguration extends ForwardingDiagnosticFormatter.ForwardingConfiguration {
631 /** set of enabled rich formatter's features */
632 protected java.util.EnumSet<RichFormatterFeature> features;
634 @SuppressWarnings("fallthrough")
635 public RichConfiguration(Options options, AbstractDiagnosticFormatter formatter) {
636 super(formatter.getConfiguration());
637 features = formatter.isRaw() ? EnumSet.noneOf(RichFormatterFeature.class) :
638 EnumSet.of(RichFormatterFeature.SIMPLE_NAMES,
639 RichFormatterFeature.WHERE_CLAUSES,
640 RichFormatterFeature.UNIQUE_TYPEVAR_NAMES);
641 String diagOpts = options.get("diags");
642 if (diagOpts != null) {
643 for (String args: diagOpts.split(",")) {
644 if (args.equals("-where")) {
645 features.remove(RichFormatterFeature.WHERE_CLAUSES);
646 }
647 else if (args.equals("where")) {
648 features.add(RichFormatterFeature.WHERE_CLAUSES);
649 }
650 if (args.equals("-simpleNames")) {
651 features.remove(RichFormatterFeature.SIMPLE_NAMES);
652 }
653 else if (args.equals("simpleNames")) {
654 features.add(RichFormatterFeature.SIMPLE_NAMES);
655 }
656 if (args.equals("-disambiguateTvars")) {
657 features.remove(RichFormatterFeature.UNIQUE_TYPEVAR_NAMES);
658 }
659 else if (args.equals("disambiguateTvars")) {
660 features.add(RichFormatterFeature.UNIQUE_TYPEVAR_NAMES);
661 }
662 }
663 }
664 }
666 /**
667 * Returns a list of all the features supported by the rich formatter.
668 * @return list of supported features
669 */
670 public RichFormatterFeature[] getAvailableFeatures() {
671 return RichFormatterFeature.values();
672 }
674 /**
675 * Enable a specific feature on this rich formatter.
676 * @param feature feature to be enabled
677 */
678 public void enable(RichFormatterFeature feature) {
679 features.add(feature);
680 }
682 /**
683 * Disable a specific feature on this rich formatter.
684 * @param feature feature to be disabled
685 */
686 public void disable(RichFormatterFeature feature) {
687 features.remove(feature);
688 }
690 /**
691 * Is a given feature enabled on this formatter?
692 * @param feature feature to be tested
693 */
694 public boolean isEnabled(RichFormatterFeature feature) {
695 return features.contains(feature);
696 }
698 /**
699 * The advanced formatting features provided by the rich formatter
700 */
701 public enum RichFormatterFeature {
702 /** a list of additional info regarding a given type/symbol */
703 WHERE_CLAUSES,
704 /** full class names simplification (where possible) */
705 SIMPLE_NAMES,
706 /** type-variable names disambiguation */
707 UNIQUE_TYPEVAR_NAMES;
708 }
709 }
710 }