/* * 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); } }