Skip to content

Generate for directive #738

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Mar 17, 2025
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
32 changes: 16 additions & 16 deletions .github/workflows/dart.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Cache Pub hosted dependencies
uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a
uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf
with:
path: "~/.pub-cache/hosted"
key: "os:ubuntu-latest;pub-cache-hosted;sdk:stable"
restore-keys: |
os:ubuntu-latest;pub-cache-hosted
os:ubuntu-latest
- name: Setup Dart SDK
uses: dart-lang/setup-dart@e630b99d28a3b71860378cafdc2a067c71107f94
uses: dart-lang/setup-dart@e51d8e571e22473a2ddebf0ef8a2123f0ab2c02c
with:
sdk: stable
- id: checkout
Expand All @@ -44,7 +44,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Cache Pub hosted dependencies
uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a
uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf
with:
path: "~/.pub-cache/hosted"
key: "os:ubuntu-latest;pub-cache-hosted;sdk:3.6.0;packages:source_gen;commands:analyze_1"
Expand All @@ -54,7 +54,7 @@ jobs:
os:ubuntu-latest;pub-cache-hosted
os:ubuntu-latest
- name: Setup Dart SDK
uses: dart-lang/setup-dart@e630b99d28a3b71860378cafdc2a067c71107f94
uses: dart-lang/setup-dart@e51d8e571e22473a2ddebf0ef8a2123f0ab2c02c
with:
sdk: "3.6.0"
- id: checkout
Expand All @@ -74,7 +74,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Cache Pub hosted dependencies
uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a
uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf
with:
path: "~/.pub-cache/hosted"
key: "os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:_test_annotations-example-example_usage-source_gen;commands:format-analyze_0"
Expand All @@ -84,7 +84,7 @@ jobs:
os:ubuntu-latest;pub-cache-hosted
os:ubuntu-latest
- name: Setup Dart SDK
uses: dart-lang/setup-dart@e630b99d28a3b71860378cafdc2a067c71107f94
uses: dart-lang/setup-dart@e51d8e571e22473a2ddebf0ef8a2123f0ab2c02c
with:
sdk: dev
- id: checkout
Expand Down Expand Up @@ -147,7 +147,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Cache Pub hosted dependencies
uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a
uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf
with:
path: "~/.pub-cache/hosted"
key: "os:ubuntu-latest;pub-cache-hosted;sdk:3.6.0;packages:example_usage;commands:test_0"
Expand All @@ -157,7 +157,7 @@ jobs:
os:ubuntu-latest;pub-cache-hosted
os:ubuntu-latest
- name: Setup Dart SDK
uses: dart-lang/setup-dart@e630b99d28a3b71860378cafdc2a067c71107f94
uses: dart-lang/setup-dart@e51d8e571e22473a2ddebf0ef8a2123f0ab2c02c
with:
sdk: "3.6.0"
- id: checkout
Expand All @@ -181,7 +181,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Cache Pub hosted dependencies
uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a
uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf
with:
path: "~/.pub-cache/hosted"
key: "os:ubuntu-latest;pub-cache-hosted;sdk:3.6.0;packages:source_gen;commands:test_1"
Expand All @@ -191,7 +191,7 @@ jobs:
os:ubuntu-latest;pub-cache-hosted
os:ubuntu-latest
- name: Setup Dart SDK
uses: dart-lang/setup-dart@e630b99d28a3b71860378cafdc2a067c71107f94
uses: dart-lang/setup-dart@e51d8e571e22473a2ddebf0ef8a2123f0ab2c02c
with:
sdk: "3.6.0"
- id: checkout
Expand All @@ -215,7 +215,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Cache Pub hosted dependencies
uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a
uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf
with:
path: "~/.pub-cache/hosted"
key: "os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:example_usage;commands:test_0"
Expand All @@ -225,7 +225,7 @@ jobs:
os:ubuntu-latest;pub-cache-hosted
os:ubuntu-latest
- name: Setup Dart SDK
uses: dart-lang/setup-dart@e630b99d28a3b71860378cafdc2a067c71107f94
uses: dart-lang/setup-dart@e51d8e571e22473a2ddebf0ef8a2123f0ab2c02c
with:
sdk: dev
- id: checkout
Expand All @@ -249,7 +249,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Cache Pub hosted dependencies
uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a
uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf
with:
path: "~/.pub-cache/hosted"
key: "os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:source_gen;commands:test_1"
Expand All @@ -259,7 +259,7 @@ jobs:
os:ubuntu-latest;pub-cache-hosted
os:ubuntu-latest
- name: Setup Dart SDK
uses: dart-lang/setup-dart@e630b99d28a3b71860378cafdc2a067c71107f94
uses: dart-lang/setup-dart@e51d8e571e22473a2ddebf0ef8a2123f0ab2c02c
with:
sdk: dev
- id: checkout
Expand All @@ -283,7 +283,7 @@ jobs:
runs-on: windows-latest
steps:
- name: Setup Dart SDK
uses: dart-lang/setup-dart@e630b99d28a3b71860378cafdc2a067c71107f94
uses: dart-lang/setup-dart@e51d8e571e22473a2ddebf0ef8a2123f0ab2c02c
with:
sdk: "3.6.0"
- id: checkout
Expand All @@ -307,7 +307,7 @@ jobs:
runs-on: windows-latest
steps:
- name: Setup Dart SDK
uses: dart-lang/setup-dart@e630b99d28a3b71860378cafdc2a067c71107f94
uses: dart-lang/setup-dart@e51d8e571e22473a2ddebf0ef8a2123f0ab2c02c
with:
sdk: dev
- id: checkout
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/no-response.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-latest
if: ${{ github.repository_owner == 'dart-lang' }}
steps:
- uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e
- uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639
with:
# Don't automatically mark inactive issues+PRs as stale.
days-before-stale: -1
Expand Down
1 change: 0 additions & 1 deletion analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ 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/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ environment:
sdk: ^3.6.0

dependencies:
analyzer: '>=5.2.0 <7.0.0'
analyzer: '>=6.9.0 <8.0.0'
build: ^2.0.0
source_gen: any

Expand Down
2 changes: 1 addition & 1 deletion example_usage/test/ensure_build_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ import 'package:test/test.dart';
void main() {
test(
'ensure_build',
() => expectBuildClean(packageRelativeDirectory: 'example_usage'),
() async => expectBuildClean(packageRelativeDirectory: 'example_usage'),
);
}
12 changes: 9 additions & 3 deletions source_gen/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## 2.0.0-wip
## 2.0.0

- **DO NOT PUBLISH**: Only land into `analyzer-element2` branch.
When all clients migrate, replace all APIs.
- **Breaking Change**: Change `formatOutput` function to accept a language
version parameter.
- **Formatting Change**: Generated code will no longer apply any fixes by
Expand All @@ -8,13 +10,17 @@
- Document deduplication behavior for the output of
`GeneratorForAnnotation.generateForAnnotatedElement`.
- Support all the glob quotes.
- Require `analyzer: ^6.9.0`
- Require Dart 3.6.0
- Require `analyzer: '>=7.2.0 <8.0.0'`
- Support the latest `package:dart_style`
Copy link
Contributor

Choose a reason for hiding this comment

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

We should mention the new API here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I wrote something, but I'm not sure if this is exactly what you expected.

- Add `generateForAnnotatedDirective`, which now must be used instead of
`generateForAnnotatedElement` in order to support annotations on directives
(imports, exports, parts).
- `LibraryBuilder`, `PartBuilder`, and `SharedPartBuilder` now take an optional
`writeDescriptions` boolean. When set to `false`, headers and generator
descriptions for the files will not be included in the builder output.
- Include `//dart format width=80` comments in files generated by a
`LibraryBuilder` or `PartBuilder` and formatted with the default callback.
- Require Dart 3.6.0

## 1.5.0

Expand Down
3 changes: 2 additions & 1 deletion source_gen/lib/source_gen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ export 'src/constants/revive.dart' show Revivable;
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/library.dart'
show AnnotatedDirective, AnnotatedElement, LibraryReader;
export 'src/span_for_element.dart' show spanForElement, spanForElement2;
export 'src/type_checker.dart' show TypeChecker, UnresolvedAnnotationException;
export 'src/utils.dart' show typeNameOf;
4 changes: 2 additions & 2 deletions source_gen/lib/src/constants/reader.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.

import 'package:analyzer/dart/constant/value.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/element2.dart';
import 'package:analyzer/dart/element/type.dart';

import '../type_checker.dart';
Expand Down Expand Up @@ -268,7 +268,7 @@ class _DartObjectConstant extends ConstantReader {
ConstantReader read(String field) {
final reader = peek(field);
if (reader == null) {
assertHasField(objectValue.type!.element as InterfaceElement, field);
assertHasField2(objectValue.type!.element3 as InterfaceElement2, field);
return const _NullConstant();
}
return reader;
Expand Down
25 changes: 25 additions & 0 deletions source_gen/lib/src/constants/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@

import 'package:analyzer/dart/constant/value.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/element2.dart';

/// Throws a [FormatException] if [root] does not have a given field [name].
///
/// Super types [InterfaceElement.supertype] are also checked before throwing.
@Deprecated('Use assertHasField2() instead')
void assertHasField(InterfaceElement root, String name) {
InterfaceElement? element = root;
while (element != null) {
Expand All @@ -28,6 +30,29 @@ void assertHasField(InterfaceElement root, String name) {
);
}

/// Throws a [FormatException] if [root] does not have a given field [name].
///
/// Super types [InterfaceElement.supertype] are also checked before throwing.
void assertHasField2(InterfaceElement2 root, String name) {
InterfaceElement2? element = root;
while (element != null) {
final field = element.getField2(name);
if (field != null) {
return;
}
element = element.supertype?.element3;
}
final allFields = {
...root.fields2,
for (var t in root.allSupertypes) ...t.element3.fields2,
};

throw FormatException(
'Class ${root.name3} does not have field "$name".',
'Fields: \n - ${allFields.map((e) => e.name3).join('\n - ')}',
);
}

/// Returns whether or not [object] is or represents a `null` value.
bool isNullLike(DartObject? object) => object?.isNull != false;

Expand Down
41 changes: 41 additions & 0 deletions source_gen/lib/src/generator_for_annotation.dart
Copy link
Member

Choose a reason for hiding this comment

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

GeneratorForAnnotation should be extends-able but not implements-able.
This is base, right?

Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,21 @@ abstract class GeneratorForAnnotation<T> extends Generator {
FutureOr<String> generate(LibraryReader library, BuildStep buildStep) async {
final values = <String>{};

for (var annotatedDirective in library.libraryDirectivesAnnotatedWith(
typeChecker,
throwOnUnresolved: throwOnUnresolved,
)) {
final generatedValue = generateForAnnotatedDirective(
annotatedDirective.directive,
annotatedDirective.annotation,
buildStep,
);
await for (var value in normalizeGeneratorOutput(generatedValue)) {
assert(value.length == value.trim().length);
values.add(value);
}
}

for (var annotatedElement in library.annotatedWith(
typeChecker,
throwOnUnresolved: throwOnUnresolved,
Expand Down Expand Up @@ -123,4 +138,30 @@ abstract class GeneratorForAnnotation<T> extends Generator {
ConstantReader annotation,
BuildStep buildStep,
) {}

/// Implement to return source code to generate for [directive]:
/// - [LibraryImport]
/// - [LibraryExport]
/// - [PartInclude]
///
/// This method is invoked based on finding directives 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 generateForAnnotatedDirective(
Copy link
Member

Choose a reason for hiding this comment

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

What's the deal w/ this @scheglov ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm sorry, i don't understand the question.
This method is invoked at the line 62.

We cannot use the same method for elements and directive.

Copy link
Member

Choose a reason for hiding this comment

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

I think we can skip this for now if checks is the only known usage that needs it. I can add it later if it becomes necessary.

We will need to be careful not to break internal uses of this and document carefully the impact when we publish externally since this is fairly subtle and it isn't clear in source code exactly when it will matter.

ElementDirective directive,
ConstantReader annotation,
BuildStep buildStep,
) {}
}
Loading