| Index: pkg/compiler/lib/src/mirrors_used.dart
|
| diff --git a/pkg/compiler/lib/src/mirrors_used.dart b/pkg/compiler/lib/src/mirrors_used.dart
|
| deleted file mode 100644
|
| index 208904edeb8b8e7ddec86332d5eae1a27ef3f694..0000000000000000000000000000000000000000
|
| --- a/pkg/compiler/lib/src/mirrors_used.dart
|
| +++ /dev/null
|
| @@ -1,595 +0,0 @@
|
| -// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
| -// for details. All rights reserved. Use of this source code is governed by a
|
| -// BSD-style license that can be found in the LICENSE file.
|
| -
|
| -library dart2js.mirrors_used;
|
| -
|
| -import 'constants/expressions.dart';
|
| -import 'constants/values.dart' show
|
| - ConstantValue,
|
| - ConstructedConstantValue,
|
| - ListConstantValue,
|
| - StringConstantValue,
|
| - TypeConstantValue;
|
| -
|
| -import 'dart_types.dart' show
|
| - DartType,
|
| - InterfaceType,
|
| - TypeKind;
|
| -
|
| -import 'dart2jslib.dart' show
|
| - Compiler,
|
| - CompilerTask,
|
| - ConstantCompiler,
|
| - MessageKind,
|
| - TreeElements,
|
| - invariant;
|
| -
|
| -import 'elements/elements.dart' show
|
| - ClassElement,
|
| - Element,
|
| - LibraryElement,
|
| - MetadataAnnotation,
|
| - ScopeContainerElement,
|
| - VariableElement;
|
| -
|
| -import 'tree/tree.dart' show
|
| - Import,
|
| - LibraryTag,
|
| - NamedArgument,
|
| - NewExpression,
|
| - Node;
|
| -
|
| -import 'util/util.dart' show
|
| - Link,
|
| - Spannable;
|
| -
|
| -/**
|
| - * Compiler task that analyzes MirrorsUsed annotations.
|
| - *
|
| - * When importing 'dart:mirrors', it is possible to annotate the import with
|
| - * MirrorsUsed annotation. This is a way to declare what elements will be
|
| - * reflected on at runtime. Such elements, even they would normally be
|
| - * discarded by the implicit tree-shaking algorithm must be preserved in the
|
| - * final output.
|
| - *
|
| - * Since some libraries cannot tell exactly what they will be reflecting on, it
|
| - * is possible for one library to specify a MirrorsUsed annotation that applies
|
| - * to another library. For example:
|
| - *
|
| - * Mirror utility library that cannot tell what it is reflecting on:
|
| - * library mirror_utils;
|
| - * import 'dart:mirrors';
|
| - * ...
|
| - *
|
| - * The main app which knows how it use the mirror utility library:
|
| - * library main_app;
|
| - * @MirrorsUsed(override='mirror_utils')
|
| - * import 'dart:mirrors';
|
| - * import 'mirror_utils.dart';
|
| - * ...
|
| - *
|
| - * In this case, we say that @MirrorsUsed in main_app overrides @MirrorsUsed in
|
| - * mirror_utils.
|
| - *
|
| - * It is possible to override all libraries using override='*'. If multiple
|
| - * catch-all overrides like this, they are merged together.
|
| - *
|
| - * It is possible for library "a" to declare that it overrides library "b", and
|
| - * vice versa. In this case, both annotations will be discarded and the
|
| - * compiler will emit a hint (that is, a warning that is not specified by the
|
| - * language specification).
|
| - *
|
| - * After applying all the overrides, we can iterate over libraries that import
|
| - * 'dart:mirrors'. If a library does not have an associated MirrorsUsed
|
| - * annotation, then we have to discard all MirrorsUsed annotations and assume
|
| - * everything can be reflected on.
|
| - *
|
| - * On the other hand, if all libraries importing dart:mirrors have a
|
| - * MirrorsUsed annotation, these annotations are merged.
|
| - *
|
| - * MERGING MIRRORSUSED
|
| - *
|
| - * TBD.
|
| - */
|
| -class MirrorUsageAnalyzerTask extends CompilerTask {
|
| - Set<LibraryElement> librariesWithUsage;
|
| - MirrorUsageAnalyzer analyzer;
|
| -
|
| - MirrorUsageAnalyzerTask(Compiler compiler)
|
| - : super(compiler) {
|
| - analyzer = new MirrorUsageAnalyzer(compiler, this);
|
| - }
|
| -
|
| - /// Collect @MirrorsUsed annotations in all libraries. Called by the
|
| - /// compiler after all libraries are loaded, but before resolution.
|
| - void analyzeUsage(LibraryElement mainApp) {
|
| - if (mainApp == null || compiler.mirrorsLibrary == null) return;
|
| - measure(analyzer.run);
|
| - List<String> symbols = analyzer.mergedMirrorUsage.symbols;
|
| - List<Element> targets = analyzer.mergedMirrorUsage.targets;
|
| - List<Element> metaTargets = analyzer.mergedMirrorUsage.metaTargets;
|
| - compiler.backend.registerMirrorUsage(
|
| - symbols == null ? null : new Set<String>.from(symbols),
|
| - targets == null ? null : new Set<Element>.from(targets),
|
| - metaTargets == null ? null : new Set<Element>.from(metaTargets));
|
| - librariesWithUsage = analyzer.librariesWithUsage;
|
| - }
|
| -
|
| - /// Is there a @MirrorsUsed annotation in the library of [element]? Used by
|
| - /// the resolver to suppress hints about using new Symbol or
|
| - /// MirrorSystem.getName.
|
| - bool hasMirrorUsage(Element element) {
|
| - LibraryElement library = element.library;
|
| - // Internal libraries always have implicit mirror usage.
|
| - return library.isInternalLibrary
|
| - || (librariesWithUsage != null
|
| - && librariesWithUsage.contains(library));
|
| - }
|
| -
|
| - /// Call-back from the resolver to analyze MirorsUsed annotations. The result
|
| - /// is stored in [analyzer] and later used to compute
|
| - /// [:analyzer.mergedMirrorUsage:].
|
| - void validate(NewExpression node, TreeElements mapping) {
|
| - for (Node argument in node.send.arguments) {
|
| - NamedArgument named = argument.asNamedArgument();
|
| - if (named == null) continue;
|
| - ConstantCompiler constantCompiler = compiler.resolver.constantCompiler;
|
| - ConstantValue value =
|
| - constantCompiler.compileNode(named.expression, mapping).value;
|
| -
|
| - MirrorUsageBuilder builder =
|
| - new MirrorUsageBuilder(
|
| - analyzer, mapping.analyzedElement.library, named.expression,
|
| - value, mapping);
|
| -
|
| - if (named.name.source == 'symbols') {
|
| - analyzer.cachedStrings[value] =
|
| - builder.convertConstantToUsageList(value, onlyStrings: true);
|
| - } else if (named.name.source == 'targets') {
|
| - analyzer.cachedElements[value] =
|
| - builder.resolveUsageList(builder.convertConstantToUsageList(value));
|
| - } else if (named.name.source == 'metaTargets') {
|
| - analyzer.cachedElements[value] =
|
| - builder.resolveUsageList(builder.convertConstantToUsageList(value));
|
| - } else if (named.name.source == 'override') {
|
| - analyzer.cachedElements[value] =
|
| - builder.resolveUsageList(builder.convertConstantToUsageList(value));
|
| - }
|
| - }
|
| - }
|
| -}
|
| -
|
| -class MirrorUsageAnalyzer {
|
| - final Compiler compiler;
|
| - final MirrorUsageAnalyzerTask task;
|
| - List<LibraryElement> wildcard;
|
| - final Set<LibraryElement> librariesWithUsage;
|
| - final Map<ConstantValue, List<String>> cachedStrings;
|
| - final Map<ConstantValue, List<Element>> cachedElements;
|
| - MirrorUsage mergedMirrorUsage;
|
| -
|
| - MirrorUsageAnalyzer(Compiler compiler, this.task)
|
| - : compiler = compiler,
|
| - librariesWithUsage = new Set<LibraryElement>(),
|
| - cachedStrings = new Map<ConstantValue, List<String>>(),
|
| - cachedElements = new Map<ConstantValue, List<Element>>();
|
| -
|
| - /// Collect and merge all @MirrorsUsed annotations. As a side-effect, also
|
| - /// compute which libraries have the annotation (which is used by
|
| - /// [MirrorUsageAnalyzerTask.hasMirrorUsage]).
|
| - void run() {
|
| - wildcard = compiler.libraryLoader.libraries.toList();
|
| - Map<LibraryElement, List<MirrorUsage>> usageMap =
|
| - collectMirrorsUsedAnnotation();
|
| - propagateOverrides(usageMap);
|
| - Set<LibraryElement> librariesWithoutUsage = new Set<LibraryElement>();
|
| - usageMap.forEach((LibraryElement library, List<MirrorUsage> usage) {
|
| - if (usage.isEmpty) librariesWithoutUsage.add(library);
|
| - });
|
| - if (librariesWithoutUsage.isEmpty) {
|
| - mergedMirrorUsage = mergeUsages(usageMap);
|
| - } else {
|
| - mergedMirrorUsage = new MirrorUsage(null, null, null, null);
|
| - }
|
| - }
|
| -
|
| - /// Collect all @MirrorsUsed from all libraries and represent them as
|
| - /// [MirrorUsage].
|
| - Map<LibraryElement, List<MirrorUsage>> collectMirrorsUsedAnnotation() {
|
| - Map<LibraryElement, List<MirrorUsage>> result =
|
| - new Map<LibraryElement, List<MirrorUsage>>();
|
| - for (LibraryElement library in compiler.libraryLoader.libraries) {
|
| - if (library.isInternalLibrary) continue;
|
| - for (LibraryTag tag in library.tags) {
|
| - Import importTag = tag.asImport();
|
| - if (importTag == null) continue;
|
| - compiler.withCurrentElement(library, () {
|
| - List<MirrorUsage> usages =
|
| - mirrorsUsedOnLibraryTag(library, importTag);
|
| - if (usages != null) {
|
| - List<MirrorUsage> existing = result[library];
|
| - if (existing != null) {
|
| - existing.addAll(usages);
|
| - } else {
|
| - result[library] = usages;
|
| - }
|
| - }
|
| - });
|
| - }
|
| - }
|
| - return result;
|
| - }
|
| -
|
| - /// Apply [MirrorUsage] with 'override' to libraries they override.
|
| - void propagateOverrides(Map<LibraryElement, List<MirrorUsage>> usageMap) {
|
| - Map<LibraryElement, List<MirrorUsage>> propagatedOverrides =
|
| - new Map<LibraryElement, List<MirrorUsage>>();
|
| - usageMap.forEach((LibraryElement library, List<MirrorUsage> usages) {
|
| - for (MirrorUsage usage in usages) {
|
| - List<Element> override = usage.override;
|
| - if (override == null) continue;
|
| - if (override == wildcard) {
|
| - for (LibraryElement overridden in wildcard) {
|
| - if (overridden != library) {
|
| - List<MirrorUsage> overriddenUsages = propagatedOverrides
|
| - .putIfAbsent(overridden, () => <MirrorUsage>[]);
|
| - overriddenUsages.add(usage);
|
| - }
|
| - }
|
| - } else {
|
| - for (Element overridden in override) {
|
| - List<MirrorUsage> overriddenUsages = propagatedOverrides
|
| - .putIfAbsent(overridden, () => <MirrorUsage>[]);
|
| - overriddenUsages.add(usage);
|
| - }
|
| - }
|
| - }
|
| - });
|
| - propagatedOverrides.forEach((LibraryElement overridden,
|
| - List<MirrorUsage> overriddenUsages) {
|
| - List<MirrorUsage> usages =
|
| - usageMap.putIfAbsent(overridden, () => <MirrorUsage>[]);
|
| - usages.addAll(overriddenUsages);
|
| - });
|
| - }
|
| -
|
| - /// Find @MirrorsUsed annotations on the given import [tag] in [library]. The
|
| - /// annotations are represented as [MirrorUsage].
|
| - List<MirrorUsage> mirrorsUsedOnLibraryTag(LibraryElement library,
|
| - Import tag) {
|
| - LibraryElement importedLibrary = library.getLibraryFromTag(tag);
|
| - if (importedLibrary != compiler.mirrorsLibrary) {
|
| - return null;
|
| - }
|
| - List<MirrorUsage> result = <MirrorUsage>[];
|
| - for (MetadataAnnotation metadata in tag.metadata) {
|
| - metadata.ensureResolved(compiler);
|
| - Element element = metadata.constant.value.computeType(compiler).element;
|
| - if (element == compiler.mirrorsUsedClass) {
|
| - result.add(buildUsage(metadata.constant.value));
|
| - }
|
| - }
|
| - return result;
|
| - }
|
| -
|
| - /// Merge all [MirrorUsage] instances accross all libraries.
|
| - MirrorUsage mergeUsages(Map<LibraryElement, List<MirrorUsage>> usageMap) {
|
| - Set<MirrorUsage> usagesToMerge = new Set<MirrorUsage>();
|
| - usageMap.forEach((LibraryElement library, List<MirrorUsage> usages) {
|
| - librariesWithUsage.add(library);
|
| - usagesToMerge.addAll(usages);
|
| - });
|
| - if (usagesToMerge.isEmpty) {
|
| - return new MirrorUsage(null, wildcard, null, null);
|
| - } else {
|
| - MirrorUsage result = new MirrorUsage(null, null, null, null);
|
| - for (MirrorUsage usage in usagesToMerge) {
|
| - result = merge(result, usage);
|
| - }
|
| - return result;
|
| - }
|
| - }
|
| -
|
| - /// Merge [a] with [b]. The resulting [MirrorUsage] simply has the symbols,
|
| - /// targets, and metaTargets of [a] and [b] concatenated. 'override' is
|
| - /// ignored.
|
| - MirrorUsage merge(MirrorUsage a, MirrorUsage b) {
|
| - // TOOO(ahe): Should be an instance method on MirrorUsage.
|
| - if (a.symbols == null && a.targets == null && a.metaTargets == null) {
|
| - return b;
|
| - } else if (
|
| - b.symbols == null && b.targets == null && b.metaTargets == null) {
|
| - return a;
|
| - }
|
| - // TODO(ahe): Test the following cases.
|
| - List<String> symbols = a.symbols;
|
| - if (symbols == null) {
|
| - symbols = b.symbols;
|
| - } else if (b.symbols != null) {
|
| - symbols.addAll(b.symbols);
|
| - }
|
| - List<Element> targets = a.targets;
|
| - if (targets == null) {
|
| - targets = b.targets;
|
| - } else if (targets != wildcard && b.targets != null) {
|
| - targets.addAll(b.targets);
|
| - }
|
| - List<Element> metaTargets = a.metaTargets;
|
| - if (metaTargets == null) {
|
| - metaTargets = b.metaTargets;
|
| - } else if (metaTargets != wildcard && b.metaTargets != null) {
|
| - metaTargets.addAll(b.metaTargets);
|
| - }
|
| - return new MirrorUsage(symbols, targets, metaTargets, null);
|
| - }
|
| -
|
| - /// Convert a [constant] to an instance of [MirrorUsage] using information
|
| - /// that was resolved during [MirrorUsageAnalyzerTask.validate].
|
| - MirrorUsage buildUsage(ConstructedConstantValue constant) {
|
| - Map<Element, ConstantValue> fields = constant.fieldElements;
|
| - VariableElement symbolsField = compiler.mirrorsUsedClass.lookupLocalMember(
|
| - 'symbols');
|
| - VariableElement targetsField = compiler.mirrorsUsedClass.lookupLocalMember(
|
| - 'targets');
|
| - VariableElement metaTargetsField =
|
| - compiler.mirrorsUsedClass.lookupLocalMember(
|
| - 'metaTargets');
|
| - VariableElement overrideField = compiler.mirrorsUsedClass.lookupLocalMember(
|
| - 'override');
|
| -
|
| - return new MirrorUsage(
|
| - cachedStrings[fields[symbolsField]],
|
| - cachedElements[fields[targetsField]],
|
| - cachedElements[fields[metaTargetsField]],
|
| - cachedElements[fields[overrideField]]);
|
| - }
|
| -}
|
| -
|
| -/// Used to represent a resolved MirrorsUsed constant.
|
| -class MirrorUsage {
|
| - final List<String> symbols;
|
| - final List<Element> targets;
|
| - final List<Element> metaTargets;
|
| - final List<Element> override;
|
| -
|
| - MirrorUsage(this.symbols, this.targets, this.metaTargets, this.override);
|
| -
|
| - String toString() {
|
| - return
|
| - 'MirrorUsage('
|
| - 'symbols = $symbols, '
|
| - 'targets = $targets, '
|
| - 'metaTargets = $metaTargets, '
|
| - 'override = $override'
|
| - ')';
|
| -
|
| - }
|
| -}
|
| -
|
| -class MirrorUsageBuilder {
|
| - final MirrorUsageAnalyzer analyzer;
|
| - final LibraryElement enclosingLibrary;
|
| - final Spannable spannable;
|
| - final ConstantValue constant;
|
| - final TreeElements elements;
|
| -
|
| - MirrorUsageBuilder(
|
| - this.analyzer,
|
| - this.enclosingLibrary,
|
| - this.spannable,
|
| - this.constant,
|
| - this.elements);
|
| -
|
| - Compiler get compiler => analyzer.compiler;
|
| -
|
| - /// Convert a constant to a list of [String] and [Type] values. If the
|
| - /// constant is a single [String], it is assumed to be a comma-separated list
|
| - /// of qualified names. If the constant is a [Type] t, the result is [:[t]:].
|
| - /// Otherwise, the constant is assumed to represent a list of strings (each a
|
| - /// qualified name) and types, and such a list is constructed. If
|
| - /// [onlyStrings] is true, the returned list is a [:List<String>:] and any
|
| - /// [Type] values are treated as an error (meaning that the value is ignored
|
| - /// and a hint is emitted).
|
| - List convertConstantToUsageList(
|
| - ConstantValue constant, { bool onlyStrings: false }) {
|
| - if (constant.isNull) {
|
| - return null;
|
| - } else if (constant.isList) {
|
| - ListConstantValue list = constant;
|
| - List result = onlyStrings ? <String> [] : [];
|
| - for (ConstantValue entry in list.entries) {
|
| - if (entry.isString) {
|
| - StringConstantValue string = entry;
|
| - result.add(string.primitiveValue.slowToString());
|
| - } else if (!onlyStrings && entry.isType) {
|
| - TypeConstantValue type = entry;
|
| - result.add(type.representedType);
|
| - } else {
|
| - Spannable node = positionOf(entry);
|
| - MessageKind kind = onlyStrings
|
| - ? MessageKind.MIRRORS_EXPECTED_STRING
|
| - : MessageKind.MIRRORS_EXPECTED_STRING_OR_TYPE;
|
| - compiler.reportHint(
|
| - node,
|
| - kind, {'name': node, 'type': apiTypeOf(entry)});
|
| - }
|
| - }
|
| - return result;
|
| - } else if (!onlyStrings && constant.isType) {
|
| - TypeConstantValue type = constant;
|
| - return [type.representedType];
|
| - } else if (constant.isString) {
|
| - StringConstantValue string = constant;
|
| - var iterable =
|
| - string.primitiveValue.slowToString().split(',').map((e) => e.trim());
|
| - return onlyStrings ? new List<String>.from(iterable) : iterable.toList();
|
| - } else {
|
| - Spannable node = positionOf(constant);
|
| - MessageKind kind = onlyStrings
|
| - ? MessageKind.MIRRORS_EXPECTED_STRING_OR_LIST
|
| - : MessageKind.MIRRORS_EXPECTED_STRING_TYPE_OR_LIST;
|
| - compiler.reportHint(
|
| - node,
|
| - kind, {'name': node, 'type': apiTypeOf(constant)});
|
| - return null;
|
| - }
|
| - }
|
| -
|
| - /// Find the first non-implementation interface of constant.
|
| - DartType apiTypeOf(ConstantValue constant) {
|
| - DartType type = constant.computeType(compiler);
|
| - LibraryElement library = type.element.library;
|
| - if (type.isInterfaceType && library.isInternalLibrary) {
|
| - InterfaceType interface = type;
|
| - ClassElement cls = type.element;
|
| - cls.ensureResolved(compiler);
|
| - for (DartType supertype in cls.allSupertypes) {
|
| - if (supertype.isInterfaceType
|
| - && !supertype.element.library.isInternalLibrary) {
|
| - return interface.asInstanceOf(supertype.element);
|
| - }
|
| - }
|
| - }
|
| - return type;
|
| - }
|
| -
|
| - /// Convert a list of strings and types to a list of elements. Types are
|
| - /// converted to their corresponding element, and strings are resolved as
|
| - /// follows:
|
| - ///
|
| - /// First find the longest library name that is a prefix of the string, if
|
| - /// there are none, resolve using [resolveExpression]. Otherwise, resolve the
|
| - /// rest of the string using [resolveLocalExpression].
|
| - List<Element> resolveUsageList(List list) {
|
| - if (list == null) return null;
|
| - if (list.length == 1 && list[0] == '*') {
|
| - return analyzer.wildcard;
|
| - }
|
| - List<Element> result = <Element>[];
|
| - for (var entry in list) {
|
| - if (entry is DartType) {
|
| - DartType type = entry;
|
| - result.add(type.element);
|
| - } else {
|
| - String string = entry;
|
| - LibraryElement libraryCandiate;
|
| - String libraryNameCandiate;
|
| - for (LibraryElement l in compiler.libraryLoader.libraries) {
|
| - if (l.hasLibraryName()) {
|
| - String libraryName = l.getLibraryOrScriptName();
|
| - if (string == libraryName) {
|
| - // Found an exact match.
|
| - libraryCandiate = l;
|
| - libraryNameCandiate = libraryName;
|
| - break;
|
| - } else if (string.startsWith('$libraryName.')) {
|
| - if (libraryNameCandiate == null
|
| - || libraryNameCandiate.length < libraryName.length) {
|
| - // Found a better candiate
|
| - libraryCandiate = l;
|
| - libraryNameCandiate = libraryName;
|
| - }
|
| - }
|
| - }
|
| - }
|
| - Element e;
|
| - if (libraryNameCandiate == string) {
|
| - e = libraryCandiate;
|
| - } else if (libraryNameCandiate != null) {
|
| - e = resolveLocalExpression(
|
| - libraryCandiate,
|
| - string.substring(libraryNameCandiate.length + 1).split('.'));
|
| - } else {
|
| - e = resolveExpression(string);
|
| - }
|
| - if (e != null) result.add(e);
|
| - }
|
| - }
|
| - return result;
|
| - }
|
| -
|
| - /// Resolve [expression] in [enclosingLibrary]'s import scope.
|
| - Element resolveExpression(String expression) {
|
| - List<String> identifiers = expression.split('.');
|
| - Element element = enclosingLibrary.find(identifiers[0]);
|
| - if (element == null) {
|
| - compiler.reportHint(
|
| - spannable, MessageKind.MIRRORS_CANNOT_RESOLVE_IN_CURRENT_LIBRARY,
|
| - {'name': expression});
|
| - return null;
|
| - } else {
|
| - if (identifiers.length == 1) return element;
|
| - return resolveLocalExpression(element, identifiers.sublist(1));
|
| - }
|
| - }
|
| -
|
| - /// Resolve [identifiers] in [element]'s local members.
|
| - Element resolveLocalExpression(Element element, List<String> identifiers) {
|
| - Element current = element;
|
| - for (String identifier in identifiers) {
|
| - Element e = findLocalMemberIn(current, identifier);
|
| - if (e == null) {
|
| - if (current.isLibrary) {
|
| - LibraryElement library = current;
|
| - compiler.reportHint(
|
| - spannable, MessageKind.MIRRORS_CANNOT_RESOLVE_IN_LIBRARY,
|
| - {'name': identifiers[0],
|
| - 'library': library.getLibraryOrScriptName()});
|
| - } else {
|
| - compiler.reportHint(
|
| - spannable, MessageKind.MIRRORS_CANNOT_FIND_IN_ELEMENT,
|
| - {'name': identifier, 'element': current.name});
|
| - }
|
| - return current;
|
| - }
|
| - current = e;
|
| - }
|
| - return current;
|
| - }
|
| -
|
| - /// Helper method to lookup members in a [ScopeContainerElement]. If
|
| - /// [element] is not a ScopeContainerElement, return null.
|
| - Element findLocalMemberIn(Element element, String name) {
|
| - if (element is ScopeContainerElement) {
|
| - ScopeContainerElement scope = element;
|
| - if (element.isClass) {
|
| - ClassElement cls = element;
|
| - cls.ensureResolved(compiler);
|
| - }
|
| - return scope.localLookup(name);
|
| - }
|
| - return null;
|
| - }
|
| -
|
| - /// Attempt to find a [Spannable] corresponding to constant.
|
| - Spannable positionOf(ConstantValue constant) {
|
| - Node node;
|
| - elements.forEachConstantNode((Node n, ConstantExpression c) {
|
| - if (node == null && c.value == constant) {
|
| - node = n;
|
| - }
|
| - });
|
| - if (node == null) {
|
| - // TODO(ahe): Returning [spannable] here leads to confusing error
|
| - // messages. For example, consider:
|
| - // @MirrorsUsed(targets: fisk)
|
| - // import 'dart:mirrors';
|
| - //
|
| - // const fisk = const [main];
|
| - //
|
| - // main() {}
|
| - //
|
| - // The message is:
|
| - // example.dart:1:23: Hint: Can't use 'fisk' here because ...
|
| - // Did you forget to add quotes?
|
| - // @MirrorsUsed(targets: fisk)
|
| - // ^^^^
|
| - //
|
| - // Instead of saying 'fisk' should pretty print the problematic constant
|
| - // value.
|
| - return spannable;
|
| - }
|
| - return node;
|
| - }
|
| -}
|
|
|