blob: d6d6d1351847df5120e49b63811602cdf6d19fd2 [file] [log] [blame]
/*
* Copyright (c) 2013, the Dart project authors.
*
* Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.eclipse.org/legal/epl-v10.html
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.dart.engine.ast.visitor;
import com.google.dart.engine.ast.AstNode;
import com.google.dart.engine.ast.CompilationUnit;
import com.google.dart.engine.ast.ConstructorDeclaration;
import com.google.dart.engine.ast.FunctionDeclaration;
import com.google.dart.engine.ast.InstanceCreationExpression;
import com.google.dart.engine.ast.MethodDeclaration;
import com.google.dart.engine.ast.MethodInvocation;
import com.google.dart.engine.ast.PrefixedIdentifier;
import com.google.dart.engine.ast.SimpleIdentifier;
import com.google.dart.engine.ast.VariableDeclaration;
import com.google.dart.engine.element.ClassElement;
import com.google.dart.engine.element.CompilationUnitElement;
import com.google.dart.engine.element.ConstructorElement;
import com.google.dart.engine.element.Element;
import com.google.dart.engine.element.FieldElement;
import com.google.dart.engine.element.FunctionElement;
import com.google.dart.engine.element.ImportElement;
import com.google.dart.engine.element.LibraryElement;
import com.google.dart.engine.element.MethodElement;
import com.google.dart.engine.element.PropertyAccessorElement;
import com.google.dart.engine.element.TopLevelVariableElement;
import com.google.dart.engine.internal.context.AnalysisOptionsImpl;
import com.google.dart.engine.internal.index.AbstractDartTest;
import com.google.dart.engine.resolver.ResolverTestCase;
import com.google.dart.engine.scanner.Keyword;
import com.google.dart.engine.source.Source;
import static com.google.dart.engine.ast.AstFactory.identifier;
import static com.google.dart.engine.ast.AstFactory.instanceCreationExpression;
import static com.google.dart.engine.ast.AstFactory.typeName;
import static com.google.dart.engine.element.ElementFactory.classElement;
import static com.google.dart.engine.element.ElementFactory.constructorElement;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ElementLocatorTest extends ResolverTestCase {
public void fail_locate_ExportDirective() throws Exception {
AstNode id = findNodeIn("export", "export 'dart:core';");
Element element = ElementLocator.locate(id);
assertInstanceOf(ImportElement.class, element);
}
public void fail_locate_Identifier_libraryDirective() throws Exception {
AstNode id = findNodeIn("foo", "library foo.bar;");
Element element = ElementLocator.locate(id);
assertInstanceOf(LibraryElement.class, element);
}
public void fail_locate_Identifier_partOfDirective() throws Exception {
// Can't resolve the library element without the library declaration.
// AstNode id = findNodeIn("foo", "part of foo.bar;");
// Element element = ElementLocator.locate(id);
// assertInstanceOf(LibraryElement.class, element);
fail("Test this case");
}
public void test_locate_AssignmentExpression() throws Exception {
AstNode id = findNodeIn("+=", //
"int x = 0;",
"void main() {",
" x += 1;",
"}");
Element element = ElementLocator.locate(id);
assertInstanceOf(MethodElement.class, element);
}
public void test_locate_BinaryExpression() throws Exception {
AstNode id = findNodeIn("+", "var x = 3 + 4;");
Element element = ElementLocator.locate(id);
assertInstanceOf(MethodElement.class, element);
}
public void test_locate_ClassDeclaration() throws Exception {
AstNode id = findNodeIn("class", "class A { }");
Element element = ElementLocator.locate(id);
assertInstanceOf(ClassElement.class, element);
}
public void test_locate_CompilationUnit() throws Exception {
CompilationUnit cu = resolveContents("// only comment");
assertNotNull(cu.getElement());
Element element = ElementLocator.locate(cu);
assertSame(cu.getElement(), element);
}
public void test_locate_ConstructorDeclaration() throws Exception {
AstNode id = findNodeIndexedIn("bar", 0, //
"class A {",
" A.bar() {}",
"}");
ConstructorDeclaration declaration = id.getAncestor(ConstructorDeclaration.class);
Element element = ElementLocator.locate(declaration);
assertInstanceOf(ConstructorElement.class, element);
}
public void test_locate_FunctionDeclaration() throws Exception {
AstNode id = findNodeIn("f", "int f() => 3;");
FunctionDeclaration declaration = id.getAncestor(FunctionDeclaration.class);
Element element = ElementLocator.locate(declaration);
assertInstanceOf(FunctionElement.class, element);
}
public void test_locate_Identifier_annotationClass_namedConstructor_forSimpleFormalParameter()
throws Exception {
AstNode id = findNodeIndexedIn("Class", 2, //
"class Class {",
" const Class.name();",
"}",
"void main(@Class.name() parameter) {",
"}");
Element element = ElementLocator.locate(id);
assertInstanceOf(ClassElement.class, element);
}
public void test_locate_Identifier_annotationClass_unnamedConstructor_forSimpleFormalParameter()
throws Exception {
AstNode id = findNodeIndexedIn("Class", 2, //
"class Class {",
" const Class();",
"}",
"void main(@Class() parameter) {",
"}");
Element element = ElementLocator.locate(id);
assertInstanceOf(ConstructorElement.class, element);
}
public void test_locate_Identifier_className() throws Exception {
AstNode id = findNodeIn("A", "class A { }");
Element element = ElementLocator.locate(id);
assertInstanceOf(ClassElement.class, element);
}
public void test_locate_Identifier_constructor_named() throws Exception {
AstNode id = findNodeIndexedIn("bar", 0, //
"class A {",
" A.bar() {}",
"}");
Element element = ElementLocator.locate(id);
assertInstanceOf(ConstructorElement.class, element);
}
public void test_locate_Identifier_constructor_unnamed() throws Exception {
AstNode id = findNodeIndexedIn("A", 1, //
"class A {",
" A() {}",
"}");
Element element = ElementLocator.locate(id);
assertInstanceOf(ConstructorElement.class, element);
}
public void test_locate_Identifier_fieldName() throws Exception {
AstNode id = findNodeIn("x", "class A { var x; }");
Element element = ElementLocator.locate(id);
assertInstanceOf(FieldElement.class, element);
}
public void test_locate_Identifier_propertAccess() throws Exception {
AstNode id = findNodeIn("length", //
"void main() {",
" int x = 'foo'.length;",
"}");
Element element = ElementLocator.locate(id);
assertInstanceOf(PropertyAccessorElement.class, element);
}
public void test_locate_ImportDirective() throws Exception {
AstNode id = findNodeIn("import", "import 'dart:core';");
Element element = ElementLocator.locate(id);
assertInstanceOf(ImportElement.class, element);
}
public void test_locate_IndexExpression() throws Exception {
AstNode id = findNodeIndexedIn("\\[", 1, //
"void main() {",
" List x = [1, 2];",
" var y = x[0];",
"}");
Element element = ElementLocator.locate(id);
assertInstanceOf(MethodElement.class, element);
}
public void test_locate_InstanceCreationExpression() throws Exception {
AstNode node = findNodeIndexedIn("A(", 0, //
"class A {}",
"void main() {",
" new A();",
"}");
Element element = ElementLocator.locate(node);
assertInstanceOf(ConstructorElement.class, element);
}
public void test_locate_InstanceCreationExpression_type_prefixedIdentifier() throws Exception {
// prepare: new pref.A()
SimpleIdentifier identifier = identifier("A");
PrefixedIdentifier prefixedIdentifier = identifier("pref", identifier);
InstanceCreationExpression creation = instanceCreationExpression(
Keyword.NEW,
typeName(prefixedIdentifier));
// set ClassElement
ClassElement classElement = classElement("A");
identifier.setStaticElement(classElement);
// set ConstructorElement
ConstructorElement constructorElement = constructorElement(classElement, null);
creation.getConstructorName().setStaticElement(constructorElement);
// verify that "A" is resolved to ConstructorElement
Element element = ElementLocator.locate(identifier);
assertSame(classElement, element);
}
public void test_locate_InstanceCreationExpression_type_simpleIdentifier() throws Exception {
// prepare: new A()
SimpleIdentifier identifier = identifier("A");
InstanceCreationExpression creation = instanceCreationExpression(
Keyword.NEW,
typeName(identifier));
// set ClassElement
ClassElement classElement = classElement("A");
identifier.setStaticElement(classElement);
// set ConstructorElement
ConstructorElement constructorElement = constructorElement(classElement, null);
creation.getConstructorName().setStaticElement(constructorElement);
// verify that "A" is resolved to ConstructorElement
Element element = ElementLocator.locate(identifier);
assertSame(classElement, element);
}
public void test_locate_LibraryDirective() throws Exception {
AstNode id = findNodeIn("library", "library foo;");
Element element = ElementLocator.locate(id);
assertInstanceOf(LibraryElement.class, element);
}
public void test_locate_MethodDeclaration() throws Exception {
AstNode id = findNodeIn("m", //
"class A {",
" void m() {}",
"}");
MethodDeclaration declaration = id.getAncestor(MethodDeclaration.class);
Element element = ElementLocator.locate(declaration);
assertInstanceOf(MethodElement.class, element);
}
public void test_locate_MethodInvocation_method() throws Exception {
AstNode id = findNodeIndexedIn("bar", 1, //
"class A {",
" int bar() => 42;",
"}",
"void main() {",
" var f = new A().bar();",
"}");
Element element = ElementLocator.locate(id);
assertInstanceOf(MethodElement.class, element);
}
public void test_locate_MethodInvocation_topLevel() throws Exception {
String contents = createSource(//
"foo(x) {}",
"void main() {",
" foo(0);",
"}");
CompilationUnit cu = resolveContents(contents);
MethodInvocation node = AbstractDartTest.findNode(
cu,
contents.indexOf("foo(0)"),
MethodInvocation.class);
Element element = ElementLocator.locate(node);
assertInstanceOf(FunctionElement.class, element);
}
public void test_locate_PostfixExpression() throws Exception {
AstNode id = findNodeIn("++", "int addOne(int x) => x++;");
Element element = ElementLocator.locate(id);
assertInstanceOf(MethodElement.class, element);
}
public void test_locate_PrefixedIdentifier() throws Exception {
AstNode id = findNodeIn("int", //
"import 'dart:core' as core;",
"core.int value;");
PrefixedIdentifier identifier = id.getAncestor(PrefixedIdentifier.class);
Element element = ElementLocator.locate(identifier);
assertInstanceOf(ClassElement.class, element);
}
public void test_locate_PrefixExpression() throws Exception {
AstNode id = findNodeIn("++", "int addOne(int x) => ++x;");
Element element = ElementLocator.locate(id);
assertInstanceOf(MethodElement.class, element);
}
public void test_locate_StringLiteral_exportUri() throws Exception {
addNamedSource("/foo.dart", "library foo;");
AstNode id = findNodeIn("'foo.dart'", "export 'foo.dart';");
Element element = ElementLocator.locate(id);
assertInstanceOf(LibraryElement.class, element);
}
public void test_locate_StringLiteral_expression() throws Exception {
AstNode id = findNodeIn("abc", "var x = 'abc';");
Element element = ElementLocator.locate(id);
assertNull(element);
}
public void test_locate_StringLiteral_importUri() throws Exception {
addNamedSource("/foo.dart", "library foo; class A {}");
AstNode id = findNodeIn("'foo.dart'", "import 'foo.dart'; class B extends A {}");
Element element = ElementLocator.locate(id);
assertInstanceOf(LibraryElement.class, element);
}
public void test_locate_StringLiteral_partUri() throws Exception {
addNamedSource("/foo.dart", "part of app;");
AstNode id = findNodeIn("'foo.dart'", "library app; part 'foo.dart';");
Element element = ElementLocator.locate(id);
assertInstanceOf(CompilationUnitElement.class, element);
}
public void test_locate_VariableDeclaration() throws Exception {
AstNode id = findNodeIn("x", "var x = 'abc';");
VariableDeclaration declaration = id.getAncestor(VariableDeclaration.class);
Element element = ElementLocator.locate(declaration);
assertInstanceOf(TopLevelVariableElement.class, element);
}
public void test_locateWithOffset_BinaryExpression() throws Exception {
AstNode id = findNodeIn("+", "var x = 3 + 4;");
Element element = ElementLocator.locateWithOffset(id, 0);
assertInstanceOf(MethodElement.class, element);
}
public void test_locateWithOffset_StringLiteral() throws Exception {
AstNode id = findNodeIn("abc", "var x = 'abc';");
Element element = ElementLocator.locateWithOffset(id, 1);
assertNull(element);
}
@Override
protected void reset() {
AnalysisOptionsImpl analysisOptions = new AnalysisOptionsImpl();
analysisOptions.setHint(false);
resetWithOptions(analysisOptions);
}
/**
* Find the first AST node matching a pattern in the resolved AST for the given source.
*
* @param nodePattern the (unique) pattern used to identify the node of interest
* @param lines the lines to be merged into a single source string
* @return the matched node in the resolved AST for the given source lines
* @throws Exception if source cannot be verified
*/
private AstNode findNodeIn(String nodePattern, String... lines) throws Exception {
return findNodeIndexedIn(nodePattern, 0, lines);
}
/**
* Find the AST node matching the given indexed occurrence of a pattern in the resolved AST for
* the given source.
*
* @param nodePattern the pattern used to identify the node of interest
* @param index the index of the pattern match of interest
* @param lines the lines to be merged into a single source string
* @return the matched node in the resolved AST for the given source lines
* @throws Exception if source cannot be verified
*/
private AstNode findNodeIndexedIn(String nodePattern, int index, String... lines)
throws Exception {
String contents = createSource(lines);
CompilationUnit cu = resolveContents(contents);
int start = getOffsetOfMatch(contents, nodePattern, index);
int end = start + nodePattern.length();
return new NodeLocator(start, end).searchWithin(cu);
}
private int getOffsetOfMatch(String contents, String pattern, int matchIndex) {
if (matchIndex == 0) {
return contents.indexOf(pattern);
}
Matcher matcher = Pattern.compile(pattern).matcher(contents);
int count = 0;
while (matcher.find()) {
if (count == matchIndex) {
return matcher.start();
}
++count;
}
return -1;
}
/**
* Parse, resolve and verify the given source lines to produce a fully resolved AST.
*
* @param lines the lines to be merged into a single source string
* @return the result of resolving the AST structure representing the content of the source
* @throws Exception if source cannot be verified
*/
private CompilationUnit resolveContents(String... lines) throws Exception {
Source source = addSource(createSource(lines));
LibraryElement library = resolve(source);
assertNoErrors(source);
verify(source);
return getAnalysisContext().resolveCompilationUnit(source, library);
}
}