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
1 change: 1 addition & 0 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ analyzer:

linter:
rules:
- analyzer_use_new_elements
- avoid_bool_literals_in_conditional_expressions
- avoid_classes_with_only_static_members
- avoid_private_typedef_functions
Expand Down
2 changes: 1 addition & 1 deletion example/lib/src/member_count_library_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class MemberCountLibraryGenerator extends Generator {
final topLevelVarCount = topLevelNumVariables(library).length;

return '''
// Source library: ${library.element.source.uri}
// Source library: ${library.element2.uri}
const topLevelNumVarCount = $topLevelVarCount;
''';
}
Expand Down
8 changes: 4 additions & 4 deletions example/lib/src/multiplier_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,21 @@
// 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.

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

import '../annotations.dart';

class MultiplierGenerator extends GeneratorForAnnotation<Multiplier> {
@override
String generateForAnnotatedElement(
Element element,
String generateForAnnotatedElement2(
Element2 element,
ConstantReader annotation,
BuildStep buildStep,
) {
final numValue = annotation.read('value').literalValue as num;

return 'num ${element.name}Multiplied() => ${element.name} * $numValue;';
return 'num ${element.name3}Multiplied() => ${element.name3} * $numValue;';
}
}
3 changes: 2 additions & 1 deletion example/lib/src/property_product_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ class PropertyProductGenerator extends Generator {
@override
String generate(LibraryReader library, BuildStep buildStep) {
final productNames = topLevelNumVariables(library)
.map((element) => element.name)
.map((element) => element.name3)
.nonNulls
.join(' * ');

return '''
Expand Down
3 changes: 2 additions & 1 deletion example/lib/src/property_sum_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ class PropertySumGenerator extends Generator {
@override
String generate(LibraryReader library, BuildStep buildStep) {
final sumNames = topLevelNumVariables(library)
.map((element) => element.name)
.map((element) => element.name3)
.nonNulls
.join(' + ');

return '''
Expand Down
8 changes: 4 additions & 4 deletions example/lib/src/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
// 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.

import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/element2.dart';
import 'package:source_gen/source_gen.dart';

/// Returns all [TopLevelVariableElement] members in [reader]'s library that
/// Returns all [TopLevelVariableElement2] members in [reader]'s library that
/// have a type of [num].
Iterable<TopLevelVariableElement> topLevelNumVariables(LibraryReader reader) =>
reader.allElements.whereType<TopLevelVariableElement>().where(
Iterable<TopLevelVariableElement2> topLevelNumVariables(LibraryReader reader) =>
reader.allElements2.whereType<TopLevelVariableElement2>().where(
(element) =>
element.type.isDartCoreNum ||
element.type.isDartCoreInt ||
Expand Down
2 changes: 1 addition & 1 deletion source_gen/lib/source_gen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ export 'src/generator.dart'
show Generator, InvalidGenerationSource, InvalidGenerationSourceError;
export 'src/generator_for_annotation.dart' show GeneratorForAnnotation;
export 'src/library.dart' show AnnotatedElement, LibraryReader;
export 'src/span_for_element.dart' show spanForElement;
export 'src/span_for_element.dart' show spanForElement, spanForElement2;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need a changelog update

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will never be published, we will restore the name with different types.

export 'src/type_checker.dart' show TypeChecker, UnresolvedAnnotationException;
export 'src/utils.dart' show typeNameOf;
14 changes: 9 additions & 5 deletions source_gen/lib/src/builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import 'dart:convert';

import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/element2.dart';
// ignore: implementation_imports
import 'package:analyzer/src/utilities/extensions/element.dart';
import 'package:build/build.dart';
import 'package:dart_style/dart_style.dart';
import 'package:pub_semver/pub_semver.dart';
Expand Down Expand Up @@ -103,16 +106,17 @@ class _Builder extends Builder {
}

final lib = await buildStep.resolver
.libraryFor(buildStep.inputId, allowSyntaxErrors: allowSyntaxErrors);
.libraryFor2(buildStep.inputId, allowSyntaxErrors: allowSyntaxErrors);
await _generateForLibrary(lib, buildStep);
}

Future<void> _generateForLibrary(
LibraryElement library,
LibraryElement2 library2,
BuildStep buildStep,
) async {
final library = library2.asElement;
final generatedOutputs =
await _generate(library, _generators, buildStep).toList();
await _generate(library2, _generators, buildStep).toList();

// Don't output useless files.
//
Expand Down Expand Up @@ -353,11 +357,11 @@ class LibraryBuilder extends _Builder {
}

Stream<GeneratedOutput> _generate(
LibraryElement library,
LibraryElement2 library2,
List<Generator> generators,
BuildStep buildStep,
) async* {
final libraryReader = LibraryReader(library);
final libraryReader = LibraryReader.v2(library2);
for (var i = 0; i < generators.length; i++) {
final gen = generators[i];
var msg = 'Running $gen';
Expand Down
18 changes: 15 additions & 3 deletions source_gen/lib/src/generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import 'dart:async';

import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/element2.dart';
// ignore: implementation_imports
import 'package:analyzer/src/utilities/extensions/element.dart';
import 'package:build/build.dart';

import 'library.dart';
Expand Down Expand Up @@ -49,7 +52,7 @@ class InvalidGenerationSource implements Exception {
///
/// May be `null` if the error had no associated element, or if the location
/// was passed with [node].
final Element? element;
final Element2? element2;

/// The AST Node associated with this error.
///
Expand All @@ -60,9 +63,18 @@ class InvalidGenerationSource implements Exception {
InvalidGenerationSource(
this.message, {
this.todo = '',
this.element,
Element? element,
this.node,
});
}) : element2 = element?.asElement2;

InvalidGenerationSource.v2(
this.message, {
this.todo = '',
Element2? element,
this.node,
}) : element2 = element;

Element? get element => element2?.asElement;

@override
String toString() {
Expand Down
33 changes: 31 additions & 2 deletions source_gen/lib/src/generator_for_annotation.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import 'dart:async';

import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/element2.dart';
import 'package:build/build.dart';

import 'constants/reader.dart';
Expand Down Expand Up @@ -58,11 +59,16 @@ abstract class GeneratorForAnnotation<T> extends Generator {
typeChecker,
throwOnUnresolved: throwOnUnresolved,
)) {
final generatedValue = generateForAnnotatedElement(
var generatedValue = generateForAnnotatedElement(
annotatedElement.element,
annotatedElement.annotation,
buildStep,
);
generatedValue ??= generateForAnnotatedElement2(
annotatedElement.element2,
annotatedElement.annotation,
buildStep,
);
await for (var value in normalizeGeneratorOutput(generatedValue)) {
assert(value.length == value.trim().length);
values.add(value);
Expand Down Expand Up @@ -93,5 +99,28 @@ abstract class GeneratorForAnnotation<T> extends Generator {
Element element,
ConstantReader annotation,
BuildStep buildStep,
);
) {}

/// Implement to return source code to generate for [element].
///
/// This method is invoked based on finding elements annotated with an
/// instance of [T]. The [annotation] is provided as a [ConstantReader].
///
/// Supported return values include a single [String] or multiple [String]
/// instances within an [Iterable] or [Stream]. It is also valid to return a
/// [Future] of [String], [Iterable], or [Stream]. When multiple values are
/// returned through an iterable or stream they will be deduplicated.
/// Typically each value will be an independent unit of code and the
/// deduplication prevents re-defining the same member multiple times. For
/// example if multiple annotated elements may need a specific utility method
/// available it can be output for each one, and the single deduplicated
/// definition can be shared.
///
/// Implementations should return `null` when no content is generated. Empty
/// or whitespace-only [String] instances are also ignored.
dynamic generateForAnnotatedElement2(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait, so which version is going to be called?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both, but any specific generator will override only one.
So, both migrated and not-yet-migrated generators will work.

Element2 element,
ConstantReader annotation,
BuildStep buildStep,
) {}
}
89 changes: 61 additions & 28 deletions source_gen/lib/src/library.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
// BSD-style license that can be found in the LICENSE file.

import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/element2.dart';
// ignore: implementation_imports
import 'package:analyzer/src/utilities/extensions/element.dart';
import 'package:build/build.dart';
import 'package:path/path.dart' as p;

Expand All @@ -13,25 +16,29 @@ import 'utils.dart';
/// Result of finding an [annotation] on [element] through [LibraryReader].
class AnnotatedElement {
final ConstantReader annotation;
final Element element;
final Element2 element2;

const AnnotatedElement(this.annotation, this.element);
const AnnotatedElement(this.annotation, this.element2);

Element get element => element2.asElement!;

Metadata? get metadata2 {
if (element2 case final Annotatable annotatable) {
return annotatable.metadata2;
}
return null;
}
}

/// A high-level wrapper API with common functionality for [LibraryElement].
class LibraryReader {
final LibraryElement element;
final LibraryElement2 element2;

LibraryReader(this.element);
LibraryReader(LibraryElement element) : this.v2(element.asElement2);

/// Returns a top-level [ClassElement] publicly visible in by [name].
///
/// Unlike [LibraryElement.getClass], this also correctly traverses
/// identifiers that are accessible via one or more `export` directives.
ClassElement? findType(String name) {
final type = element.exportNamespace.get(name);
return type is ClassElement ? type : null;
}
LibraryReader.v2(this.element2);

LibraryElement get element => element2.asElement;

/// All of the declarations in this library.
Iterable<Element> get allElements => [
Expand All @@ -42,6 +49,22 @@ class LibraryReader {
...element.definingCompilationUnit.parts,
];

/// All of the declarations in this library.
Iterable<Element2> get allElements2 => [element2, ...element2.children2];

/// All of the elements representing classes in this library.
Iterable<ClassElement> get classes =>
element.units.expand((cu) => cu.classes);

/// All of the elements representing classes in this library.
Iterable<ClassElement2> get classes2 => element2.classes;

/// All of the elements representing enums in this library.
Iterable<EnumElement> get enums => element.units.expand((cu) => cu.enums);

/// All of the elements representing enums in this library.
Iterable<EnumElement2> get enums2 => element2.enums;

/// All of the declarations in this library annotated with [checker].
Iterable<AnnotatedElement> annotatedWith(
TypeChecker checker, {
Expand All @@ -52,8 +75,14 @@ class LibraryReader {
element,
throwOnUnresolved: throwOnUnresolved,
);

final element2 = element.asElement2;
if (element2 == null) {
return;
}

if (annotation != null) {
yield AnnotatedElement(ConstantReader(annotation), element);
yield AnnotatedElement(ConstantReader(annotation), element2);
}
}
}
Expand All @@ -69,11 +98,20 @@ class LibraryReader {
throwOnUnresolved: throwOnUnresolved,
);
if (annotation != null) {
yield AnnotatedElement(ConstantReader(annotation), element);
yield AnnotatedElement(ConstantReader(annotation), element.asElement2!);
}
}
}

/// Returns a top-level [ClassElement] publicly visible in by [name].
///
/// Unlike [LibraryElement.getClass], this also correctly traverses
/// identifiers that are accessible via one or more `export` directives.
ClassElement? findType(String name) {
final type = element.exportNamespace.get(name);
return type is ClassElement ? type : null;
}

/// Returns a [Uri] from the current library to the target [asset].
///
/// This is a typed convenience function for using [pathToUrl], and the same
Expand All @@ -86,6 +124,13 @@ class LibraryReader {
/// API restrictions hold around supported schemes and relative paths.
Uri pathToElement(Element element) => pathToUrl(element.source!.uri);

/// Returns a [Uri] from the current library to the target [element].
///
/// This is a typed convenience function for using [pathToUrl], and the same
/// API restrictions hold around supported schemes and relative paths.
Uri pathToElement2(Element2 element) =>
pathToUrl(element.firstFragment.libraryFragment!.source.uri);

/// Returns a [Uri] from the current library to the one provided.
///
/// If possible, a `package:` or `dart:` URL scheme will be used to reference
Expand Down Expand Up @@ -131,15 +176,10 @@ class LibraryReader {
return Uri(path: to.pathSegments.last);
}
final relative = p.toUri(
p.relative(
to.toString(),
from: from.toString(),
),
p.relative(to.toString(), from: from.toString()),
);
// We now have a URL like "../b.dart", but we just want "b.dart".
return relative.replace(
pathSegments: relative.pathSegments.skip(1),
);
return relative.replace(pathSegments: relative.pathSegments.skip(1));
}
throw ArgumentError.value(to, 'to', 'Not relative to $from');
}
Expand All @@ -162,11 +202,4 @@ class LibraryReader {
fromSegments[0] == toSegments[0] &&
fromSegments[1] == toSegments[1];
}

/// All of the elements representing classes in this library.
Iterable<ClassElement> get classes =>
element.units.expand((cu) => cu.classes);

/// All of the elements representing enums in this library.
Iterable<EnumElement> get enums => element.units.expand((cu) => cu.enums);
}
Loading
Loading