New NodeLocator which uses positions of characters, not between them.
R=brianwilkerson@google.com BUG= Review URL: https://codereview.chromium.org/1490933002 .
This commit is contained in:
@@ -12945,6 +12945,95 @@ class NodeLocator extends UnifyingAstVisitor<Object> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<Object> {
|
||||
/**
|
||||
* 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.
|
||||
|
||||
@@ -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<Type>^name = ...
|
||||
// So, we need to use an offset within (or right after) the name:
|
||||
// Type n^ame =
|
||||
// List<Type>n^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) {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user