Implement Search.declarations().

R=brianwilkerson@google.com

Change-Id: Id14cf6c1f4cbf51fa8398d257515b96ee3b0d4a6
Reviewed-on: https://dart-review.googlesource.com/42181
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
Konstantin Shcheglov 2018-02-19 00:06:32 +00:00 committed by commit-bot@chromium.org
parent 2d851eb444
commit 00225dedfe
2 changed files with 224 additions and 1 deletions

View file

@ -25,6 +25,40 @@ Element _getEnclosingElement(CompilationUnitElement unitElement, int offset) {
return finder.containingElement;
}
/**
* An element declaration.
*/
class Declaration {
final int fileIndex;
final String name;
final DeclarationKind kind;
final int offset;
final int line;
final int column;
final String className;
Declaration(this.fileIndex, this.name, this.kind, this.offset, this.line,
this.column, this.className);
}
/**
* The kind of a [Declaration].
*/
enum DeclarationKind {
CLASS,
CLASS_TYPE_ALIAS,
CONSTRUCTOR,
ENUM,
ENUM_CONSTANT,
FIELD,
FUNCTION,
FUNCTION_TYPE_ALIAS,
GETTER,
METHOD,
SETTER,
VARIABLE
}
/**
* Search support for an [AnalysisDriver].
*/
@ -59,6 +93,104 @@ class Search {
return elements;
}
/**
* Return top-level and class member declarations.
*
* The path of each file with at least one declaration is added to [files].
* The list is for searched, there might be duplicates, but this is OK,
* we just want reduce amount of data, not to make it absolute minimum.
*/
Future<List<Declaration>> declarations(List<String> files) async {
List<Declaration> declarations = <Declaration>[];
DeclarationKind getExecutableKind(
UnlinkedExecutable executable, bool topLevel) {
switch (executable.kind) {
case UnlinkedExecutableKind.constructor:
return DeclarationKind.CONSTRUCTOR;
case UnlinkedExecutableKind.functionOrMethod:
if (topLevel) {
return DeclarationKind.FUNCTION;
}
return DeclarationKind.METHOD;
case UnlinkedExecutableKind.getter:
return DeclarationKind.GETTER;
break;
default:
return DeclarationKind.SETTER;
}
}
for (String path in _driver.addedFiles) {
FileState file = _driver.fsState.getFileForPath(path);
int fileIndex;
void addDeclaration(String name, DeclarationKind kind, int offset,
[String className]) {
if (fileIndex == null) {
fileIndex = files.length;
files.add(file.path);
}
if (name.endsWith('=')) {
name = name.substring(0, name.length - 1);
}
var location = file.lineInfo.getLocation(offset);
declarations.add(new Declaration(fileIndex, name, kind, offset,
location.lineNumber, location.columnNumber, className));
}
for (var class_ in file.unlinked.classes) {
String className = class_.name;
addDeclaration(
className,
class_.isMixinApplication
? DeclarationKind.CLASS_TYPE_ALIAS
: DeclarationKind.CLASS,
class_.nameOffset);
for (var field in class_.fields) {
addDeclaration(
field.name, DeclarationKind.FIELD, field.nameOffset, className);
}
for (var executable in class_.executables) {
if (executable.name.isNotEmpty) {
addDeclaration(
executable.name,
getExecutableKind(executable, false),
executable.nameOffset,
className);
}
}
}
for (var enum_ in file.unlinked.enums) {
addDeclaration(enum_.name, DeclarationKind.ENUM, enum_.nameOffset);
for (var value in enum_.values) {
addDeclaration(
value.name, DeclarationKind.ENUM_CONSTANT, value.nameOffset);
}
}
for (var executable in file.unlinked.executables) {
addDeclaration(executable.name, getExecutableKind(executable, true),
executable.nameOffset);
}
for (var typedef_ in file.unlinked.typedefs) {
addDeclaration(typedef_.name, DeclarationKind.FUNCTION_TYPE_ALIAS,
typedef_.nameOffset);
}
for (var variable in file.unlinked.variables) {
addDeclaration(
variable.name, DeclarationKind.VARIABLE, variable.nameOffset);
}
}
return declarations;
}
/**
* Returns references to the [element].
*/

View file

@ -4,7 +4,7 @@
import 'dart:async';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/ast.dart' hide Declaration;
import 'package:analyzer/dart/ast/standard_resolution_map.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/src/dart/analysis/driver.dart';
@ -98,6 +98,71 @@ import 'not-dart.txt';
expect(await driver.search.classMembers('test'), isEmpty);
}
test_declarations_class() async {
await _resolveTestUnit('''
class C {
int f;
C();
C.named();
int get g => 0;
void set s(_) {}
void m() {}
}
''');
var files = <String>[];
List<Declaration> declarations = await driver.search.declarations(files);
_assertHasDeclaration(declarations, 'C', DeclarationKind.CLASS, 6);
_assertHasDeclaration(declarations, 'f', DeclarationKind.FIELD, 16, 'C');
_assertHasDeclaration(
declarations, 'named', DeclarationKind.CONSTRUCTOR, 30, 'C');
_assertHasDeclaration(declarations, 'g', DeclarationKind.GETTER, 49, 'C');
_assertHasDeclaration(declarations, 's', DeclarationKind.SETTER, 68, 'C');
_assertHasDeclaration(declarations, 'm', DeclarationKind.METHOD, 83, 'C');
}
test_declarations_enum() async {
await _resolveTestUnit('''
enum E {
a, b, c
}
''');
var files = <String>[];
List<Declaration> declarations = await driver.search.declarations(files);
_assertHasDeclaration(declarations, 'E', DeclarationKind.ENUM, 5);
_assertHasDeclaration(declarations, 'a', DeclarationKind.ENUM_CONSTANT, 11);
_assertHasDeclaration(declarations, 'b', DeclarationKind.ENUM_CONSTANT, 14);
_assertHasDeclaration(declarations, 'c', DeclarationKind.ENUM_CONSTANT, 17);
}
test_declarations_top() async {
await _resolveTestUnit('''
int get g => 0;
void set s(_) {}
void f(int p) {}
int v;
typedef void tf1();
typedef tf2<T> = int Function<S>(T tp, S sp);
''');
var files = <String>[];
List<Declaration> declarations = await driver.search.declarations(files);
_assertHasDeclaration(declarations, 'g', DeclarationKind.GETTER, 8);
_assertHasDeclaration(declarations, 's', DeclarationKind.SETTER, 25);
_assertHasDeclaration(declarations, 'f', DeclarationKind.FUNCTION, 38);
_assertHasDeclaration(declarations, 'v', DeclarationKind.VARIABLE, 54);
_assertHasDeclaration(
declarations, 'tf1', DeclarationKind.FUNCTION_TYPE_ALIAS, 70);
_assertHasDeclaration(
declarations, 'tf2', DeclarationKind.FUNCTION_TYPE_ALIAS, 85);
// No declaration for type variables.
_assertNoDeclaration(declarations, 'T');
_assertNoDeclaration(declarations, 'S');
// No declarations for parameters.
_assertNoDeclaration(declarations, '_');
_assertNoDeclaration(declarations, 'p');
_assertNoDeclaration(declarations, 'tp');
_assertNoDeclaration(declarations, 'sp');
}
test_searchMemberReferences_qualified_resolved() async {
await _resolveTestUnit('''
class C {
@ -1183,6 +1248,32 @@ class NoMatchABCDE {}
unorderedEquals([a, b, c, d, e]));
}
void _assertHasDeclaration(List<Declaration> declarations, String name,
DeclarationKind kind, int offset,
[String className]) {
for (var declaration in declarations) {
if (declaration.name == name &&
declaration.kind == kind &&
declaration.offset == offset &&
declaration.className == className) {
return;
}
}
var actual = declarations
.map((d) => '(name=${d.name}, kind=${d.kind}, offset=${d.offset})')
.join('\n');
fail(
'Exected to find (name=$name, kind=$kind, offset=$offset) in\n$actual');
}
void _assertNoDeclaration(List<Declaration> declarations, String name) {
for (var declaration in declarations) {
if (declaration.name == name) {
fail('Unexpected declaration $name');
}
}
}
ExpectedResult _expectId(
Element enclosingElement, SearchResultKind kind, String search,
{int length, bool isResolved: true, bool isQualified: false}) {