Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 47 additions & 5 deletions lib/src/library.dart
Original file line number Diff line number Diff line change
Expand Up @@ -168,18 +168,60 @@ class LibraryReader {
Iterable<ClassElement> get classElements =>
element.definingCompilationUnit.types;

Iterable<Element> _getElements(CompilationUnitMember member) {
static Iterable<Element> _getElements(CompilationUnitMember member) {
if (member is TopLevelVariableDeclaration) {
return member.variables.variables
.map(resolutionMap.elementDeclaredByVariableDeclaration);
}
var element = resolutionMap.elementDeclaredByDeclaration(member);

if (element == null) {
print([member, member.runtimeType, member.element]);
throw new Exception('Could not find any elements for the provided unit.');
throw new StateError(
'Could not find any elements for the provided unit.');
}

return [element];
}

/// Returns the identifier prefix of [element] if it is referenced by one.
///
/// For example in the following file:
/// ```
/// import 'bar.dart' as bar;
///
/// bar.Bar b;
/// ```
///
/// ... we'd assume that `b`'s type has a prefix of `bar`.
///
/// If there is no prefix, one could not be computed, `null` is returned.
///
/// If there is an attempt to read a prefix of a file _other_ than the current
/// library being read this will throw [StateError], as it is not always
/// possible to read details of the source file from other inputs.
String _prefixForType(Element element) {
final astNode = element.computeNode();
if (astNode is VariableDeclaration) {
final parentNode = astNode.parent as VariableDeclarationList;
return _prefixForTypeAnnotation(parentNode.type);
}
return null;
}

static String _prefixForTypeAnnotation(TypeAnnotation astNode) {
if (astNode is NamedType) {
return _prefixForIdentifier(astNode.name);
}
return null;
}

static String _prefixForIdentifier(Identifier id) {
return id is PrefixedIdentifier ? id.prefix.name : null;
}
}

// Testing-only access to LibraryReader._prefixFor.
//
// This is used to iterate on the tests without launching the feature.
// Additionally it looks ike `computeNode()` will be deprecated, so we might
// have to rewrite the entire body of the function in the near future; at least
// the tests can help for correctness.
String testingPrefixForType(LibraryReader r, Element e) => r._prefixForType(e);
84 changes: 84 additions & 0 deletions test/library/prefix_for_type_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

// Increase timeouts on this test which resolves source code and can be slow.
@Timeout.factor(2.0)
import 'dart:async';

import 'package:analyzer/dart/element/element.dart';
import 'package:build/build.dart';
import 'package:build_test/build_test.dart';
import 'package:source_gen/source_gen.dart';
import 'package:test/test.dart';

import 'package:source_gen/src/library.dart' show testingPrefixForType;

void main() {
// Holds the analyzer open until all of the tests complete.
final doneWithResolver = new Completer<Null>();

LibraryReader b;

setUpAll(() async {
final resolver = await resolveSources(
{
// The order here is important; pkg/build_test has a bug where only the
// first library can be accessed via "libraryFor", the others throw that
// the file is not a library.
//
// See https://github.com/dart-lang/build/issues/843.
'test_lib|lib/b.dart': sourceB,
'test_lib|lib/a.dart': sourceA,
},
(r) => r,
tearDown: doneWithResolver.future,
);
b = new LibraryReader(
await resolver.libraryFor(new AssetId('test_lib', 'lib/b.dart')),
);
expect(b.element, isNotNull);
});

tearDownAll(doneWithResolver.complete);

group('top level fields', () {
List<Element> topLevelFieldTypes;

setUpAll(() {
topLevelFieldTypes = b.element.definingCompilationUnit.topLevelVariables;
});

Element field(String name) =>
topLevelFieldTypes.firstWhere((e) => e.name == name,
orElse: () => throw new ArgumentError.value(
name, 'name', 'Could not find a field named $name.'));

test('should read the prefix of a type', () {
expect(
testingPrefixForType(b, field('topLevelFieldWithPrefix')),
'a_prefixed',
);
});

test('should read null when the type is not prefixed', () {
expect(
testingPrefixForType(b, field('topLevelFieldWithoutPrefix')),
isNull,
);
});
});
}

const sourceA = r'''
class A {}
class B {}
''';

const sourceB = r'''
import 'a.dart' show B;
import 'a.dart' as a_prefixed;

a_prefixed.A topLevelFieldWithPrefix; // [0]
B topLevelFieldWithoutPrefix; // [1]
''';