Skip to content

Commit b3a5222

Browse files
authored
Start adding LibraryReader.prefixForType. (#296)
* Start adding LibraryReader.prefixForType. * Update docs. * More stable testing.
1 parent 5496758 commit b3a5222

File tree

2 files changed

+131
-5
lines changed

2 files changed

+131
-5
lines changed

lib/src/library.dart

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -168,18 +168,60 @@ class LibraryReader {
168168
Iterable<ClassElement> get classElements =>
169169
element.definingCompilationUnit.types;
170170

171-
Iterable<Element> _getElements(CompilationUnitMember member) {
171+
static Iterable<Element> _getElements(CompilationUnitMember member) {
172172
if (member is TopLevelVariableDeclaration) {
173173
return member.variables.variables
174174
.map(resolutionMap.elementDeclaredByVariableDeclaration);
175175
}
176176
var element = resolutionMap.elementDeclaredByDeclaration(member);
177-
178177
if (element == null) {
179-
print([member, member.runtimeType, member.element]);
180-
throw new Exception('Could not find any elements for the provided unit.');
178+
throw new StateError(
179+
'Could not find any elements for the provided unit.');
181180
}
182-
183181
return [element];
184182
}
183+
184+
/// Returns the identifier prefix of [element] if it is referenced by one.
185+
///
186+
/// For example in the following file:
187+
/// ```
188+
/// import 'bar.dart' as bar;
189+
///
190+
/// bar.Bar b;
191+
/// ```
192+
///
193+
/// ... we'd assume that `b`'s type has a prefix of `bar`.
194+
///
195+
/// If there is no prefix, one could not be computed, `null` is returned.
196+
///
197+
/// If there is an attempt to read a prefix of a file _other_ than the current
198+
/// library being read this will throw [StateError], as it is not always
199+
/// possible to read details of the source file from other inputs.
200+
String _prefixForType(Element element) {
201+
final astNode = element.computeNode();
202+
if (astNode is VariableDeclaration) {
203+
final parentNode = astNode.parent as VariableDeclarationList;
204+
return _prefixForTypeAnnotation(parentNode.type);
205+
}
206+
return null;
207+
}
208+
209+
static String _prefixForTypeAnnotation(TypeAnnotation astNode) {
210+
if (astNode is NamedType) {
211+
return _prefixForIdentifier(astNode.name);
212+
}
213+
return null;
214+
}
215+
216+
static String _prefixForIdentifier(Identifier id) {
217+
return id is PrefixedIdentifier ? id.prefix.name : null;
218+
}
185219
}
220+
221+
// Testing-only access to LibraryReader._prefixFor.
222+
//
223+
// This is used to iterate on the tests without launching the feature.
224+
// Additionally it looks ike `computeNode()` will be deprecated, so we might
225+
// have to rewrite the entire body of the function in the near future; at least
226+
// the tests can help for correctness.
227+
String testingPrefixForType(LibraryReader r, Element e) => r._prefixForType(e);
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
// Increase timeouts on this test which resolves source code and can be slow.
6+
@Timeout.factor(2.0)
7+
import 'dart:async';
8+
9+
import 'package:analyzer/dart/element/element.dart';
10+
import 'package:build/build.dart';
11+
import 'package:build_test/build_test.dart';
12+
import 'package:source_gen/source_gen.dart';
13+
import 'package:test/test.dart';
14+
15+
import 'package:source_gen/src/library.dart' show testingPrefixForType;
16+
17+
void main() {
18+
// Holds the analyzer open until all of the tests complete.
19+
final doneWithResolver = new Completer<Null>();
20+
21+
LibraryReader b;
22+
23+
setUpAll(() async {
24+
final resolver = await resolveSources(
25+
{
26+
// The order here is important; pkg/build_test has a bug where only the
27+
// first library can be accessed via "libraryFor", the others throw that
28+
// the file is not a library.
29+
//
30+
// See https://github.com/dart-lang/build/issues/843.
31+
'test_lib|lib/b.dart': sourceB,
32+
'test_lib|lib/a.dart': sourceA,
33+
},
34+
(r) => r,
35+
tearDown: doneWithResolver.future,
36+
);
37+
b = new LibraryReader(
38+
await resolver.libraryFor(new AssetId('test_lib', 'lib/b.dart')),
39+
);
40+
expect(b.element, isNotNull);
41+
});
42+
43+
tearDownAll(doneWithResolver.complete);
44+
45+
group('top level fields', () {
46+
List<Element> topLevelFieldTypes;
47+
48+
setUpAll(() {
49+
topLevelFieldTypes = b.element.definingCompilationUnit.topLevelVariables;
50+
});
51+
52+
Element field(String name) =>
53+
topLevelFieldTypes.firstWhere((e) => e.name == name,
54+
orElse: () => throw new ArgumentError.value(
55+
name, 'name', 'Could not find a field named $name.'));
56+
57+
test('should read the prefix of a type', () {
58+
expect(
59+
testingPrefixForType(b, field('topLevelFieldWithPrefix')),
60+
'a_prefixed',
61+
);
62+
});
63+
64+
test('should read null when the type is not prefixed', () {
65+
expect(
66+
testingPrefixForType(b, field('topLevelFieldWithoutPrefix')),
67+
isNull,
68+
);
69+
});
70+
});
71+
}
72+
73+
const sourceA = r'''
74+
class A {}
75+
class B {}
76+
''';
77+
78+
const sourceB = r'''
79+
import 'a.dart' show B;
80+
import 'a.dart' as a_prefixed;
81+
82+
a_prefixed.A topLevelFieldWithPrefix; // [0]
83+
B topLevelFieldWithoutPrefix; // [1]
84+
''';

0 commit comments

Comments
 (0)