diff --git a/pkg/analyzer/lib/src/generated/ast.dart b/pkg/analyzer/lib/src/generated/ast.dart index 03cb8f167b6..36e1ccd0e5c 100644 --- a/pkg/analyzer/lib/src/generated/ast.dart +++ b/pkg/analyzer/lib/src/generated/ast.dart @@ -12945,6 +12945,95 @@ class NodeLocator extends UnifyingAstVisitor { } } +/** + * An object used to locate the [AstNode] associated with a source range. + * More specifically, they will return the deepest [AstNode] which completely + * encompasses the specified range. + */ +class NodeLocator2 extends UnifyingAstVisitor { + /** + * The inclusive start offset of the range used to identify the node. + */ + int _startOffset = 0; + + /** + * The inclusive end offset of the range used to identify the node. + */ + int _endOffset = 0; + + /** + * The found node or `null` if there is no such node. + */ + AstNode _foundNode; + + /** + * Initialize a newly created locator to locate the deepest [AstNode] for + * which `node.offset <= [startOffset]` and `[endOffset] < node.end`. + * + * If [endOffset] is not provided, then it is considered the same as the + * given [startOffset]. + */ + NodeLocator2(int startOffset, [int endOffset]) + : this._startOffset = startOffset, + this._endOffset = endOffset == null ? startOffset : endOffset; + + /** + * Search within the given AST [node] and return the node that was found, + * or `null` if no node was found. + */ + AstNode searchWithin(AstNode node) { + if (node == null) { + return null; + } + try { + node.accept(this); + } on NodeLocator_NodeFoundException {} catch (exception, stackTrace) { + AnalysisEngine.instance.logger.logInformation( + "Unable to locate element at offset ($_startOffset - $_endOffset)", + new CaughtException(exception, stackTrace)); + return null; + } + return _foundNode; + } + + @override + Object visitNode(AstNode node) { + Token beginToken = node.beginToken; + Token endToken = node.endToken; + // Don't include synthetic tokens. + while (endToken != beginToken) { + if (endToken.type == TokenType.EOF || !endToken.isSynthetic) { + break; + } + endToken = endToken.previous; + } + int end = endToken.end; + int start = node.offset; + if (end <= _startOffset) { + return null; + } + if (start > _endOffset) { + return null; + } + try { + node.visitChildren(this); + } on NodeLocator_NodeFoundException { + rethrow; + } catch (exception, stackTrace) { + // Ignore the exception and proceed in order to visit the rest of the + // structure. + AnalysisEngine.instance.logger.logInformation( + "Exception caught while traversing an AST structure.", + new CaughtException(exception, stackTrace)); + } + if (start <= _startOffset && _endOffset < end) { + _foundNode = node; + throw new NodeLocator_NodeFoundException(); + } + return null; + } +} + /** * An exception used by [NodeLocator] to cancel visiting after a node has been * found. diff --git a/pkg/analyzer/lib/src/task/dart.dart b/pkg/analyzer/lib/src/task/dart.dart index f347a8a84da..9dbed8671f8 100644 --- a/pkg/analyzer/lib/src/task/dart.dart +++ b/pkg/analyzer/lib/src/task/dart.dart @@ -2953,15 +2953,7 @@ abstract class InferStaticVariableTask extends ConstantEvaluationAnalysisTask { */ VariableDeclaration getDeclaration(CompilationUnit unit) { VariableElement variable = target; - // Usually: Type ^name = ... - // Sometimes there is no space after the type: List^name = ... - // So, we need to use an offset within (or right after) the name: - // Type n^ame = - // Listn^ame = - // Type x^= - int searchOffset = variable.nameOffset + 1; - NodeLocator locator = new NodeLocator(searchOffset); - AstNode node = locator.searchWithin(unit); + AstNode node = new NodeLocator2(variable.nameOffset).searchWithin(unit); VariableDeclaration declaration = node.getAncestor((AstNode ancestor) => ancestor is VariableDeclaration); if (declaration == null || declaration.name != node) { diff --git a/pkg/analyzer/test/generated/ast_test.dart b/pkg/analyzer/test/generated/ast_test.dart index 24958de3a3a..947ca7f102c 100644 --- a/pkg/analyzer/test/generated/ast_test.dart +++ b/pkg/analyzer/test/generated/ast_test.dart @@ -29,6 +29,7 @@ main() { runReflectiveTests(IndexExpressionTest); runReflectiveTests(NodeListTest); runReflectiveTests(NodeLocatorTest); + runReflectiveTests(NodeLocator2Test); runReflectiveTests(SimpleIdentifierTest); runReflectiveTests(SimpleStringLiteralTest); runReflectiveTests(StringInterpolationTest); @@ -944,6 +945,50 @@ class NodeListTest extends EngineTestCase { } } +@reflectiveTest +class NodeLocator2Test extends ParserTestCase { + void test_onlyStartOffset() { + String code = ' int vv; '; + // 012345678 + CompilationUnit unit = ParserTestCase.parseCompilationUnit(code); + TopLevelVariableDeclaration declaration = unit.declarations[0]; + VariableDeclarationList variableList = declaration.variables; + Identifier typeName = variableList.type.name; + SimpleIdentifier varName = variableList.variables[0].name; + expect(new NodeLocator2(0).searchWithin(unit), same(unit)); + expect(new NodeLocator2(1).searchWithin(unit), same(typeName)); + expect(new NodeLocator2(2).searchWithin(unit), same(typeName)); + expect(new NodeLocator2(3).searchWithin(unit), same(typeName)); + expect(new NodeLocator2(4).searchWithin(unit), same(variableList)); + expect(new NodeLocator2(5).searchWithin(unit), same(varName)); + expect(new NodeLocator2(6).searchWithin(unit), same(varName)); + expect(new NodeLocator2(7).searchWithin(unit), same(declaration)); + expect(new NodeLocator2(8).searchWithin(unit), same(unit)); + expect(new NodeLocator2(9).searchWithin(unit), isNull); + expect(new NodeLocator2(100).searchWithin(unit), isNull); + } + + void test_startEndOffset() { + String code = ' int vv; '; + // 012345678 + CompilationUnit unit = ParserTestCase.parseCompilationUnit(code); + TopLevelVariableDeclaration declaration = unit.declarations[0]; + VariableDeclarationList variableList = declaration.variables; + Identifier typeName = variableList.type.name; + SimpleIdentifier varName = variableList.variables[0].name; + expect(new NodeLocator2(-1, 2).searchWithin(unit), isNull); + expect(new NodeLocator2(0, 2).searchWithin(unit), same(unit)); + expect(new NodeLocator2(1, 2).searchWithin(unit), same(typeName)); + expect(new NodeLocator2(1, 3).searchWithin(unit), same(typeName)); + expect(new NodeLocator2(1, 4).searchWithin(unit), same(variableList)); + expect(new NodeLocator2(5, 6).searchWithin(unit), same(varName)); + expect(new NodeLocator2(5, 7).searchWithin(unit), same(declaration)); + expect(new NodeLocator2(5, 8).searchWithin(unit), same(unit)); + expect(new NodeLocator2(5, 100).searchWithin(unit), isNull); + expect(new NodeLocator2(100, 200).searchWithin(unit), isNull); + } +} + @reflectiveTest class NodeLocatorTest extends ParserTestCase { void test_range() {