Index: pkg/compiler/lib/src/resolution/resolution_strategy.dart |
diff --git a/pkg/compiler/lib/src/resolution/resolution_strategy.dart b/pkg/compiler/lib/src/resolution/resolution_strategy.dart |
index d9eb53f5bf92f319fc095e9cfb567e1b8eb2e5f8..6fadb4e2bb6f1c989707e34d3a2492a99133073d 100644 |
--- a/pkg/compiler/lib/src/resolution/resolution_strategy.dart |
+++ b/pkg/compiler/lib/src/resolution/resolution_strategy.dart |
@@ -4,6 +4,8 @@ |
library dart2js.resolution_strategy; |
+import 'package:front_end/src/fasta/scanner.dart' show Token; |
+ |
import '../common.dart'; |
import '../common_elements.dart'; |
import '../common/backend_api.dart'; |
@@ -31,6 +33,7 @@ import '../js_backend/native_data.dart'; |
import '../js_backend/no_such_method_registry.dart'; |
import '../library_loader.dart'; |
import '../native/resolver.dart'; |
+import '../tree/tree.dart' show Node; |
import '../serialization/task.dart'; |
import '../patch_parser.dart'; |
import '../resolved_uri_translator.dart'; |
@@ -212,6 +215,155 @@ class ResolutionFrontEndStrategy implements FrontEndStrategy { |
} |
return mainMethod; |
} |
+ |
+ SourceSpan spanFromToken(Element currentElement, Token token) => |
+ _spanFromTokens(currentElement, token, token); |
+ |
+ SourceSpan _spanFromTokens(Element currentElement, Token begin, Token end, |
+ [Uri uri]) { |
+ if (begin == null || end == null) { |
+ // TODO(ahe): We can almost always do better. Often it is only |
+ // end that is null. Otherwise, we probably know the current |
+ // URI. |
+ throw 'Cannot find tokens to produce error message.'; |
+ } |
+ if (uri == null && currentElement != null) { |
+ if (currentElement is! Element) { |
+ throw 'Can only find tokens from an Element.'; |
+ } |
+ Element element = currentElement; |
+ uri = element.compilationUnit.script.resourceUri; |
+ assert(invariant(currentElement, () { |
+ bool sameToken(Token token, Token sought) { |
+ if (token == sought) return true; |
+ if (token.stringValue == '>>') { |
+ // `>>` is converted to `>` in the parser when needed. |
+ return sought.stringValue == '>' && |
+ token.charOffset <= sought.charOffset && |
+ sought.charOffset < token.charEnd; |
+ } |
+ return false; |
+ } |
+ |
+ /// Check that [begin] and [end] can be found between [from] and [to]. |
+ validateToken(Token from, Token to) { |
+ if (from == null || to == null) return true; |
+ bool foundBegin = false; |
+ bool foundEnd = false; |
+ Token token = from; |
+ while (true) { |
+ if (sameToken(token, begin)) { |
+ foundBegin = true; |
+ } |
+ if (sameToken(token, end)) { |
+ foundEnd = true; |
+ } |
+ if (foundBegin && foundEnd) { |
+ return true; |
+ } |
+ if (token == to || token == token.next || token.next == null) { |
+ break; |
+ } |
+ token = token.next; |
+ } |
+ |
+ // Create a good message for when the tokens were not found. |
+ StringBuffer sb = new StringBuffer(); |
+ sb.write('Invalid current element: $element. '); |
+ sb.write('Looking for '); |
+ sb.write('[${begin} (${begin.hashCode}),'); |
+ sb.write('${end} (${end.hashCode})] in'); |
+ |
+ token = from; |
+ while (true) { |
+ sb.write('\n ${token} (${token.hashCode})'); |
+ if (token == to || token == token.next || token.next == null) { |
+ break; |
+ } |
+ token = token.next; |
+ } |
+ return sb.toString(); |
+ } |
+ |
+ if (element.enclosingClass != null && |
+ element.enclosingClass.isEnumClass) { |
+ // Enums ASTs are synthesized (and give messed up messages). |
+ return true; |
+ } |
+ |
+ if (element is AstElement) { |
+ AstElement astElement = element; |
+ if (astElement.hasNode) { |
+ Token from = astElement.node.getBeginToken(); |
+ Token to = astElement.node.getEndToken(); |
+ if (astElement.metadata.isNotEmpty) { |
+ if (!astElement.metadata.first.hasNode) { |
+ // We might try to report an error while parsing the metadata |
+ // itself. |
+ return true; |
+ } |
+ from = astElement.metadata.first.node.getBeginToken(); |
+ } |
+ return validateToken(from, to); |
+ } |
+ } |
+ return true; |
+ }, message: "Invalid current element: $element [$begin,$end].")); |
+ } |
+ return new SourceSpan.fromTokens(uri, begin, end); |
+ } |
+ |
+ SourceSpan _spanFromNode(Element currentElement, Node node) { |
+ return _spanFromTokens( |
+ currentElement, node.getBeginToken(), node.getPrefixEndToken()); |
+ } |
+ |
+ SourceSpan _spanFromElement(Element currentElement, Element element) { |
+ if (element != null && element.sourcePosition != null) { |
+ return element.sourcePosition; |
+ } |
+ while (element != null && element.isSynthesized) { |
+ element = element.enclosingElement; |
+ } |
+ if (element != null && |
+ element.sourcePosition == null && |
+ !element.isLibrary && |
+ !element.isCompilationUnit) { |
+ // Sometimes, the backend fakes up elements that have no |
+ // position. So we use the enclosing element instead. It is |
+ // not a good error location, but cancel really is "internal |
+ // error" or "not implemented yet", so the vicinity is good |
+ // enough for now. |
+ element = element.enclosingElement; |
+ // TODO(ahe): I plan to overhaul this infrastructure anyways. |
+ } |
+ if (element == null) { |
+ element = currentElement; |
+ } |
+ if (element == null) { |
+ return null; |
+ } |
+ |
+ if (element.sourcePosition != null) { |
+ return element.sourcePosition; |
+ } |
+ Token position = element.position; |
+ Uri uri = element.compilationUnit.script.resourceUri; |
+ return (position == null) |
+ ? new SourceSpan(uri, 0, 0) |
+ : _spanFromTokens(currentElement, position, position, uri); |
+ } |
+ |
+ SourceSpan spanFromSpannable(Spannable node, Entity currentElement) { |
+ if (node is Node) { |
+ return _spanFromNode(currentElement, node); |
+ } else if (node is Element) { |
+ return _spanFromElement(currentElement, node); |
+ } else if (node is MetadataAnnotation) { |
+ return node.sourcePosition; |
+ } |
+ return null; |
+ } |
} |
/// An element environment base on a [Compiler]. |