diff --git a/CHANGELOG.md b/CHANGELOG.md index 1603a922..5fb32315 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## 0.7.4-dev +* Added `typeNameOf`, which is a safe way to get the name of a `DartType`, + even when the type is a `FunctionType`, which has a `null` name in newer + versions of the Dart analyzer. + * Added `LibraryReader.pathToUrl(Uri|String)`, which computes the `import` or `export` path necessary to reach the provided URL from the current library. Also added `pathToAsset` and `pathToElement` as convenience functions. diff --git a/lib/source_gen.dart b/lib/source_gen.dart index 9f791c1c..6cefe040 100644 --- a/lib/source_gen.dart +++ b/lib/source_gen.dart @@ -12,3 +12,4 @@ export 'src/generator_for_annotation.dart'; export 'src/library.dart' show AnnotatedElement, LibraryReader; export 'src/span_for_element.dart' show spanForElement; export 'src/type_checker.dart' show TypeChecker; +export 'src/utils.dart' show typeNameOf; diff --git a/lib/src/utils.dart b/lib/src/utils.dart index fa314f21..dbf4bd30 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -3,6 +3,7 @@ // BSD-style license that can be found in the LICENSE file. import 'package:analyzer/dart/element/element.dart'; +import 'package:analyzer/dart/element/type.dart'; import 'package:build/build.dart'; import 'package:path/path.dart' as p; @@ -39,6 +40,26 @@ String friendlyNameForElement(Element element) { return names.join(' '); } +/// Returns a non-null name for the provided [type]. +/// +/// In newer versions of the Dart analyzer, a `typedef` does not keep the +/// existing `name`, because it is used an alias: +/// ``` +/// // Used to return `VoidFunc` for name, is now `null`. +/// typedef VoidFunc = void Function(); +/// ``` +/// +/// This function will return `'VoidFunc'`, unlike [DartType.name]. +String typeNameOf(DartType type) { + if (type is FunctionType) { + final element = type.element; + if (element is GenericFunctionTypeElement) { + return element.enclosingElement.name; + } + } + return type.name; +} + /// Returns a name suitable for `part of "..."` when pointing to [element]. /// /// Returns `null` if [element] is missing identifier. diff --git a/test/utils_test.dart b/test/utils_test.dart new file mode 100644 index 00000000..9d2f59c5 --- /dev/null +++ b/test/utils_test.dart @@ -0,0 +1,43 @@ +// 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 'package:analyzer/dart/element/element.dart'; +import 'package:build_test/build_test.dart'; +import 'package:source_gen/source_gen.dart'; +import 'package:test/test.dart'; + +void main() { + ClassElement example; + + setUpAll(() async { + const source = r''' + library example; + + abstract class Example { + ClassType classType(); + FunctionType functionType(); + } + + class ClassType {} + typedef FunctionType(); + '''; + example = await resolveSource( + source, + (resolver) => resolver + .findLibraryByName('example') + .then((e) => e.getType('Example'))); + }); + + test('should return the name of a class type', () { + final classType = example.methods.first.returnType; + expect(typeNameOf(classType), 'ClassType'); + }); + + test('should return the name of a function type', () { + final functionType = example.methods.last.returnType; + expect(typeNameOf(functionType), 'FunctionType'); + }); +}