diff --git a/.github/ISSUE_TEMPLATE/code_builder.md b/.github/ISSUE_TEMPLATE/code_builder.md new file mode 100644 index 000000000..6c4217f7f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/code_builder.md @@ -0,0 +1,5 @@ +--- +name: "package:code_builder" +about: "Create a bug or file a feature request against package:code_builder." +labels: "package:code_builder" +--- \ No newline at end of file diff --git a/.github/labeler.yml b/.github/labeler.yml index 75218dbfa..91184961c 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -32,6 +32,10 @@ - changed-files: - any-glob-to-any-file: 'pkgs/clock/**' +'package:code_builder': + - changed-files: + - any-glob-to-any-file: 'pkgs/code_builder/**' + 'package:coverage': - changed-files: - any-glob-to-any-file: 'pkgs/coverage/**' diff --git a/.github/workflows/code_builder.yaml b/.github/workflows/code_builder.yaml new file mode 100644 index 000000000..eb6592297 --- /dev/null +++ b/.github/workflows/code_builder.yaml @@ -0,0 +1,68 @@ +name: package:code_builder + +on: + # Run on PRs and pushes to the default branch. + push: + branches: [ main ] + paths: + - '.github/workflows/code_builder.yml' + - 'pkgs/code_builder/**' + pull_request: + branches: [ main ] + paths: + - '.github/workflows/code_builder.yml' + - 'pkgs/code_builder/**' + schedule: + - cron: "0 0 * * 0" + +env: + PUB_ENVIRONMENT: bot.github + + +defaults: + run: + working-directory: pkgs/code_builder/ + +jobs: + # Check code formatting and static analysis on a single OS (linux) + # against Dart dev and an earlier stable version. + analyze: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + sdk: [dev] + steps: + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + - uses: dart-lang/setup-dart@0a8a0fc875eb934c15d08629302413c671d3f672 + with: + sdk: ${{ matrix.sdk }} + - id: install + name: Install dependencies + run: dart pub get + - name: Check formatting + run: dart format --output=none --set-exit-if-changed . + if: always() && steps.install.outcome == 'success' + - name: Analyze code + run: dart analyze --fatal-infos . + if: always() && steps.install.outcome == 'success' + + test: + needs: analyze + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest] + sdk: [3.5.0, dev] + steps: + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + - uses: dart-lang/setup-dart@0a8a0fc875eb934c15d08629302413c671d3f672 + with: + sdk: ${{ matrix.sdk }} + - id: install + name: Install dependencies + run: dart pub get + - name: Run VM tests + run: dart test --platform vm + if: always() && steps.install.outcome == 'success' diff --git a/README.md b/README.md index 83f5b16f9..dc8401823 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ don't naturally belong to other topic monorepos (like | [cli_config](pkgs/cli_config/) | A library to take config values from configuration files, CLI arguments, and environment variables. | [![pub package](https://img.shields.io/pub/v/cli_config.svg)](https://pub.dev/packages/cli_config) | | [cli_util](pkgs/cli_util/) | A library to help in building Dart command-line apps. | [![pub package](https://img.shields.io/pub/v/cli_util.svg)](https://pub.dev/packages/cli_util) | | [clock](pkgs/clock/) | A fakeable wrapper for dart:core clock APIs. | [![pub package](https://img.shields.io/pub/v/clock.svg)](https://pub.dev/packages/clock) | +| [code_builder](pkgs/code_builder/) | A fluent, builder-based library for generating valid Dart code. | [![pub package](https://img.shields.io/pub/v/code_builder.svg)](https://pub.dev/packages/code_builder) | | [coverage](pkgs/coverage/) | Coverage data manipulation and formatting. | [![pub package](https://img.shields.io/pub/v/coverage.svg)](https://pub.dev/packages/coverage) | | [csslib](pkgs/csslib/) | A library for parsing and analyzing CSS (Cascading Style Sheets). | [![pub package](https://img.shields.io/pub/v/csslib.svg)](https://pub.dev/packages/csslib) | | [extension_discovery](pkgs/extension_discovery/) | A convention and utilities for package extension discovery. | [![pub package](https://img.shields.io/pub/v/extension_discovery.svg)](https://pub.dev/packages/extension_discovery) | diff --git a/pkgs/code_builder/.gitignore b/pkgs/code_builder/.gitignore new file mode 100644 index 000000000..feb089dd9 --- /dev/null +++ b/pkgs/code_builder/.gitignore @@ -0,0 +1,5 @@ +# Files and directories created by pub +.dart_tool +.packages +.pub +pubspec.lock diff --git a/pkgs/code_builder/AUTHORS b/pkgs/code_builder/AUTHORS new file mode 100644 index 000000000..a904f0c02 --- /dev/null +++ b/pkgs/code_builder/AUTHORS @@ -0,0 +1,7 @@ +# Below is a list of people and organizations that have contributed +# to the project. Names should be added to the list like so: +# +# Name/Organization + +Nikolas Rimikis +Google Inc. diff --git a/pkgs/code_builder/CHANGELOG.md b/pkgs/code_builder/CHANGELOG.md new file mode 100644 index 000000000..5e5521cfb --- /dev/null +++ b/pkgs/code_builder/CHANGELOG.md @@ -0,0 +1,857 @@ +## 4.10.1 + +* Require Dart `^3.5.0` +* Upgrade to `dart_style` 2.3.7. +* Move to `dart-lang/tools` monorepo. + +## 4.10.0 + +* Add `Library.docs` to support emitting doc comments on libraries. +* Export `RepresentationDeclaration` and `RepresentationDeclarationBuilder` so + they can be used when generating extension types. +* Upgrade to `dart_style` 2.3.4. + +## 4.9.0 + +* Add `Library.generatedByComment` to support emitting 'generated by' comments. +* Support emitting an unnamed library with annotations. + +## 4.8.0 + +* Add `Expression.operatorSubtract` +* Deprecate `Expression.operatorSubstract` +* Add `Expression.operatorIntDivide` +* Add `Expression.operatorUnaryPrefixIncrement` +* Add `Expression.operatorUnaryPostfixIncrement` +* Add `Expression.operatorUnaryMinus` +* Add `Expression.operatorUnaryPrefixDecrement` +* Add `Expression.operatorUnaryPostfixDecrement` +* Add `Expression.operatorBitwiseAnd` +* Add `Expression.operatorBitwiseOr` +* Add `Expression.operatorBitwiseXor` +* Add `Expression.operatorUnaryBitwiseComplement` +* Add `Expression.operatorShiftLeft` +* Add `Expression.operatorShiftRight` +* Add `Expression.operatorShiftRightUnsigned` +* Add `Expression.addAssign` +* Add `Expression.subtractAssign` +* Add `Expression.multiplyAssign` +* Add `Expression.divideAssign` +* Add `Expression.intDivideAssign` +* Add `Expression.euclideanModuloAssign` +* Add `Expression.shiftLeftAssign` +* Add `Expression.shiftRightAssign` +* Add `Expression.shiftRightUnsignedAssign` +* Add `Expression.bitwiseAndAssign` +* Add `Expression.bitwiseXorAssign` +* Add `Expression.bitwiseOrAssign` +* Allow passing an `Expression` through `literal` without an exception. +* Add support for extension types. +* Update SDK version constraints to `>=3.0.0`. + +## 4.7.0 + +* Add a newline after lambdas. + +## 4.6.0 + +* Add support for named arguments in `enum` classes +* Add support for external keyword on fields. +* Add `Expression.parenthesized` to manually wrap an expression in parenthesis. + +## 4.5.0 + +* Require Dart 2.19 +* Add support for emitting type parameters for typedefs. +* Add support for class modifiers. +* Add support for records (both types and record literals). +* Add `literalSpread` and `literalNullSafeSpread` to support adding spreads to + `literalMap`. + +```dart +void main() { + // Creates a map + // { + // ...one, + // 2: two, + // ...?three, + // } + final map = literalMap({ + literalSpread(): refer('one'), + 2: refer('two'), + literalNullSafeSpread(): refer('three'), + }); +} +``` + +## 4.4.0 + +* Mention how the `allocator` argument relates to imports in the `DartEmitter` + constructor doc. +* Add support for emitting typedefs. +* Add support for emitting leading line comments for libraries. +* Add support for emitting `ignore_for_file` analyzer directive comments. + +## 4.3.0 + +* Add support for adding more implementation in `enum` classes. +* Only emit `late` keyword when using null safety syntax. +* Use implicit `const` when assigning to a `declareConst` variable. +* Deprecate `assignVar`, `assignConst`, and `assignFinal`. +* Add trailing commas to any parameter list, argument list, or collection + literal which has more than one element. + +## 4.2.0 + +* Add an ignore for a lint from the `package:lints` recommended set. The lint, + `no_leading_underscores_for_library_prefixes` is most useful for hand edited + code where the appearance of a private name that is already not visible + outside the library is confusing. +* Fix the docs for `Expression.assign`, `ifNullThen`, and `assignNullAware` + which had the argument and receiver flipped. +* Add `declareConst`, `declareFinal`, and `declareVar` to replace + `Expression.assignConst`, `assignFinal`, and `assignVar`. Add support for late + variables with the `declare*` utilities. +* Add `ParameterBuilder.toSuper` so support super formal parameters language + feature. + +## 4.1.0 + +* Add `Expression.spread` for the spread operator `...`. +* Add support 'late' field modifier. +* Add support for `Expression.nullChecked` to add a null assertion operator. +* Add support for creating `mixin`s. +* Add `Expression.nullSafeSpread` for the null aware spread operator `...?`. +* A `Library` can now be annotated. + +## 4.0.0 + +* Migrate to null safety. +* Changed the `DartEmitter` constructor to use named optional parameters. +* Add `ParenthesizedExpression` and + `ExpressionVisitor.visitParenthesizedExpression.` + +## 3.7.0 + +* Add support for converting a Method to a generic closure, with + `Method.genericClosure`. + +## 3.6.0 + +* Add support for creating `extension` methods. +* Expand constraint on `built_value` to allow null safe migrated version. + +## 3.5.0 + +* Add support for defining enums. +* Fix keyword ordering for `const factory` constructors. + +## 3.4.1 + +* Fix confusing mismatch description from `equalsDart`. + https://github.com/dart-lang/code_builder/issues/293 + +## 3.4.0 + +* Introduce `Expression.thrown` for throwing an expression. +* Introduce `FunctionType.isNullable`. +* Update SDK requirement to `>=2.7.0 <3.0.0`. + +## 3.3.0 + +* Add `??` null-aware operator. +* Add `..` cascade assignment operator. +* Add `part` directive. +* Introduce `TypeReference.isNullable`. +* Add an option in `DartEmitter` to emit nullable types with trailing `?` + characters. + +## 3.2.2 + +* Require minimum Dart SDK of `2.6.0`. + +## 3.2.1 + +* Escape newlines in String literals. +* Introduce `Expression.or` for boolean OR. +* Introduce `Expression.negate` for boolean NOT. +* No longer emits redundant `,`s in `FunctionType`s. +* Added support for `literalSet` and `literalConstSet`. +* Depend on the latest `package:built_value`. + +## 3.2.0 + +* Emit `=` instead of `:` for named parameter default values. +* The `new` keyword will not be used in generated code. +* The `const` keyword will be omitted when it can be inferred. +* Add an option in `DartEmitter` to order directives. +* `DartEmitter` added a `startConstCode` function to track the creation of + constant expression trees. +* `BinaryExpression` added the `final bool isConst` field. + +## 3.1.3 + +* Bump dependency on built_collection to include v4.0.0. + +## 3.1.2 + +* Set max SDK version to `<3.0.0`. + +## 3.1.1 + +* `Expression.asA` is now wrapped with parenthesis so that further calls may be + made on it as an expression. + + +## 3.1.0 + +* Added `Expression.asA` for creating explicit casts: + +```dart +void main() { + test('should emit an explicit cast', () { + expect( + refer('foo').asA(refer('String')), + equalsDart('foo as String'), + ); + }); +} +``` + +## 3.0.3 + +* Fix a bug that caused all downstream users of `code_builder` to crash due to + `build_runner` trying to import our private builder (in `tool/`). Sorry for + the inconvenience. + +## 3.0.2 + +* Require `source_gen: ^0.7.5`. + +## 3.0.1 + +* Upgrade to `built_value` 5.1.0. +* Export the `literalNum` function. +* **BUG FIX**: `literal` supports a `Map`. + +## 3.0.0 + +* Also infer `Constructor.lambda` for `factory` constructors. + +## 3.0.0-alpha + +* Using `equalsDart` no longer formats automatically with `dartfmt`. + +* Removed deprecated `Annotation` and `File` classes. + +* `Method.lambda` is inferred based on `Method.body` where possible and now + defaults to `null`. + +## 2.4.0 + +* Add `equalTo`, `notEqualTo`, `greaterThan`, `lessThan`, `greateOrEqualTo`, and + `lessOrEqualTo` to `Expression`. + +## 2.3.0 + +* Using `equalsDart` and expecting `dartfmt` by default is *deprecated*. This + requires this package to have a direct dependency on specific versions of + `dart_style` (and transitively `analyzer`), which is problematic just for + testing infrastructure. To future proof, we've exposed the `EqualsDart` class + with a `format` override: + +```dart +// 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. + +import 'package:code_builder/code_builder.dart'; +import 'package:dart_style/dart_style.dart'; + +final DartFormatter _dartfmt = new DartFormatter(); +String _format(String source) { + try { + return _dartfmt.format(source); + } on FormatException catch (_) { + return _dartfmt.formatStatement(source); + } +} + +/// Should be invoked in `main()` of every test in `test/**_test.dart`. +void useDartfmt() => EqualsDart.format = _format; +``` + +* Added `Expression.isA` and `Expression.isNotA`: + +```dart +void main() { + test('should emit an is check', () { + expect( + refer('foo').isA(refer('String')), + equalsDart('foo is String'), + ); + }); +} +``` + +* Deprecated `Annotation`. It is now legal to simply pass any `Expression` as + a metadata annotation to `Class`, `Method`, `Field,` and `Parameter`. In + `3.0.0`, the `Annotation` class will be completely removed: + +```dart +void main() { + test('should create a class with a annotated constructor', () { + expect( + new Class((b) => b + ..name = 'Foo' + ..constructors.add( + new Constructor((b) => b..annotations.add(refer('deprecated'))))), + equalsDart(r''' + class Foo { + @deprecated + Foo(); + } + '''), + ); + }); +} +``` + +* Added inference support for `Method.lambda` and `Constructor.lambda`. If not + explicitly provided and the body of the function originated from an + `Expression` then `lambda` is inferred to be true. This is not a breaking + change yet, as it requires an explicit `null` value. In `3.0.0` this will be + the default: + +```dart +void main() { + final animal = new Class((b) => b + ..name = 'Animal' + ..extend = refer('Organism') + ..methods.add(new Method.returnsVoid((b) => b + ..name = 'eat' + // In 3.0.0, this may be omitted and still is inferred. + ..lambda = null + ..body = refer('print').call([literalString('Yum!')]).code))); + final emitter = new DartEmitter(); + print(new DartFormatter().format('${animal.accept(emitter)}')); +} +``` + +* Added `nullSafeProperty` to `Expression` to access properties with `?.` +* Added `conditional` to `Expression` to use the ternary operator `? : ` +* Methods taking `positionalArguments` accept `Iterable` +* **BUG FIX**: Parameters can take a `FunctionType` as a `type`. + `Reference.type` now returns a `Reference`. Note that this change is + technically breaking but should not impacts most clients. + +## 2.2.0 + +* Imports are prefixed with `_i1` rather than `_1` which satisfies the lint + `lowercase_with_underscores`. While not a strictly breaking change you may + have to fix/regenerate golden file-like tests. We added documentation that + the specific prefix is not considered stable. + +* Added `Expression.index` for accessing the `[]` operator: + +```dart +void main() { + test('should emit an index operator', () { + expect( + refer('bar').index(literalTrue).assignVar('foo').statement, + equalsDart('var foo = bar[true];'), + ); + }); + + test('should emit an index operator set', () { + expect( + refer('bar') + .index(literalTrue) + .assign(literalFalse) + .assignVar('foo') + .statement, + equalsDart('var foo = bar[true] = false;'), + ); + }); +} +``` + +* `literalList` accepts an `Iterable` argument. + +* Fixed an NPE when a method had a return type of a `FunctionType`: + +```dart +void main() { + test('should create a method with a function type return type', () { + expect( + new Method((b) => b + ..name = 'foo' + ..returns = new FunctionType((b) => b + ..returnType = refer('String') + ..requiredParameters.addAll([ + refer('int'), + ]))), + equalsDart(r''' + String Function(int) foo(); + '''), + ); + }); +} +``` + +## 2.1.0 + +We now require the Dart 2.0-dev branch SDK (`>= 2.0.0-dev`). + +* Added support for raw `String` literals. +* Automatically escapes single quotes in now-raw `String` literals. +* Deprecated `File`, which is now a redirect to the preferred class, `Library`. + +This helps avoid symbol clashes when used with `dart:io`, a popular library. It +is now safe to do the following and get full access to the `code_builder` API: + +```dart +import 'dart:io'; + +import 'package:code_builder/code_builder.dart' hide File; +``` + +We will remove `File` in `3.0.0`, so use `Library` instead. + +## 2.0.0 + +Re-released without a direct dependency on `package:analyzer`! + +For users of the `1.x` branch of `code_builder`, this is a pretty big breaking +change but ultimately is for the better - it's easier to evolve this library +now and even add your own builders on top of the library. + +```dart +// 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. + +import 'package:code_builder/code_builder.dart'; +import 'package:dart_style/dart_style.dart'; + +void main() { + final animal = new Class((b) => b + ..name = 'Animal' + ..extend = refer('Organism') + ..methods.add(new Method.returnsVoid((b) => b + ..name = 'eat' + ..lambda = true + ..body = const Code('print(\'Yum\')')))); + final emitter = new DartEmitter(); + print(new DartFormatter().format('${animal.accept(emitter)}')); +} +``` + +...outputs... + +```dart +class Animal extends Organism { + void eat() => print('Yum!'); +} +``` + +**Major changes**: + +* Builders now use `built_value`, and have a more consistent, friendly API. +* Builders are now consistent - they don't perform work work until code is + emitted. +* It's possible to overwrite the built-in code emitting, formatting, etc by + providing your own visitors. See `DartEmitter` as an example of the built-in + visitor/emitter. +* Most of the expression and statement-level helpers were removed; in practice, + they were difficult to write and maintain, and many users commonly asked for + opt-out type APIs. See the `Code` example below: + +```dart +void main() { + var code = new Code('x + y = z'); + code.expression; + code.statement; +} +``` + +See the commit log, examples, and tests for full details. While we want to try +and avoid breaking changes, suggestions, new features, and incremental updates +are welcome! + +## 2.0.0-beta + +* Added `lazySpec` and `lazyCode` to lazily create code on visit [#145](https://github.com/dart-lang/code_builder/issues/145). + +* **BUG FIX**: `equalsDart` emits the failing source code [#147](https://github.com/dart-lang/code_builder/issues/147). +* **BUG FIX**: Top-level `lambda` `Method`s no longer emit invalid code [#146](https://github.com/dart-lang/code_builder/issues/146). + +## 2.0.0-alpha+3 + +* Added `Expression.annotation` and `Expression.annotationNamed`. +* Added `Method.closure` to create an `Expression`. +* Added `FunctionType`. +* Added `{new|const}InstanceNamed` to `Expression` [#135](https://github.com/dart-lang/code_builder/issues/135). + * Also added a `typeArguments` option to all invocations. +* Added `assign{...}` variants to `Expression` [#137](https://github.com/dart-lang/code_builder/issues/137). +* Added `.awaited` and `.returned` to `Expression` [#138](https://github.com/dart-lang/code_builder/issues/138). + +* **BUG FIX**: `Block` now implements `Code` [#136](https://github.com/dart-lang/code_builder/issues/136). +* **BUG FIX**: `new DartEmitter.scoped()` applies prefixing [#139](https://github.com/dart-lang/code_builder/issues/139). + +* Renamed many of the `.asFoo(...)` and `.toFoo(...)` methods to single getter: + * `asCode()` to `code` + * `asStatement()` to `statement` + * `toExpression()` to `expression` + +* Moved `{new|const}Instance{[Named]}` from `Expression` to `Reference`. + +## 2.0.0-alpha+2 + +* Upgraded `build_runner` from `^0.3.0` to `>=0.4.0 <0.6.0`. +* Upgraded `build_value{_generator}` from `^1.0.0` to `>=2.0.0 <5.0.0`. +* Upgraded `source_gen` from `>=0.5.0 <0.7.0` to `^0.7.0`. + +* Added `MethodModifier` to allow emit a `Method` with `async|async*|sync*`. +* Added `show|hide` to `Directive`. +* Added `Directive.importDeferredAs`. +* Added a new line character after emitting some types (class, method, etc). +* Added `refer` as a short-hand for `new Reference(...)`. + * `Reference` now implements `Expression`. + +* Added many classes/methods for writing bodies of `Code` fluently: + * `Expression` + * `LiteralExpression` + * `literal` + * `literalNull` + * `literalBool` + * `literalTrue` + * `literalFalse` + * `literalNum` + * `literalString` + * `literalList` and `literalConstList` + * `literalMap` and `literalConstMap` + * `const Code(staticString)` + * `const Code.scope((allocate) => '')` + +* Removed `SimpleSpecVisitor` (it was unused). +* Removed `implements Reference` from `Method` and `Field`; not a lot of value. + +* `SpecVisitor`'s methods all have an optional `[T context]` parameter now. + * This makes it much easier to avoid allocating extra `StringBuffer`s. +* `equalsDart` removes insignificant white space before comparing results. + +## 2.0.0-alpha+1 + +* Removed `Reference.localScope`. Just use `Reference(symbol)` now. +* Allow `Reference` instead of an explicit `TypeReference` in most APIs. + * `toType()` is performed for you as part the emitter process + +```dart +final animal = new Class((b) => b + ..name = 'Animal' + // Used to need a suffix of .toType(). + ..extend = const Reference('Organism') + ..methods.add(new Method.returnsVoid((b) => b + ..name = 'eat' + ..lambda = true + ..body = new Code((b) => b..code = 'print(\'Yum\')')))); +``` + +* We now support the Dart 2.0 pre-release SDKs (`<2.0.0-dev.infinity`) +* Removed the ability to treat `Class` as a `TypeReference`. + * Was required for compilation to `dart2js`, which is now tested on travis. + +## 2.0.0-alpha + +* Complete re-write to not use `package:analyzer`. +* Code generation now properly uses the _builder_ pattern (via `built_value`). +* See examples and tests for details. + +## 1.0.4 + +* Added `isInstanceOf` to `ExpressionBuilder`, which performs an `is` check: + +```dart +expect( + reference('foo').isInstanceOf(_barType), + equalsSource('foo is Bar'), +); +``` + +## 1.0.3 + +* Support latest `pkg/analyzer` and `pkg/func`. + +## 1.0.2 + +* Update internals to use newer analyzer API + +## 1.0.1 + +* Support the latest version of `package:dart_style`. + +## 1.0.0 + +First full release. At this point, all changes until `2.0.0` will be backward +compatible (new features) or bug fixes that are not breaking. This doesn't mean +that the entire Dart language is buildable with our API, though. + +**Contributions are welcome.** + +- Exposed `uri` in `ImportBuilder`, `ExportBuilder`, and `Part[Of]Builder`. + +## 1.0.0-beta+7 + +- Added `ExpressionBuilder#ternary`. + +## 1.0.0-beta+6 + +- Added `TypeDefBuilder`. +- Added `FunctionParameterBuilder`. +- Added `asAbstract` to various `MethodBuilder` constructors. + +## 1.0.0-beta+5 + +- Re-published the package without merge conflicts. + +## 1.0.0-beta+4 + +- Renamed `PartBuilder` to `PartOfBuilder`. +- Added a new class, `PartBuilder`, to represent `part '...dart'` directives. +- Added the `HasAnnotations` interface to all library/part/directive builders. +- Added `asFactory` and `asConst` to `ConstructorBuilder`. +- Added `ConstructorBuilder.redirectTo` for a redirecting factory constructor. +- Added a `name` getter to `ReferenceBuilder`. +- Supplying an empty constructor name (`''`) is equivalent to `null` (default). +- Automatically encodes string literals with multiple lines as `'''`. +- Added `asThrow` to `ExpressionBuilder`. +- Fixed a bug that prevented `FieldBuilder` from being used at the top-level. + +## 1.0.0-beta+3 + +- Added support for `genericTypes` parameter for `ExpressionBuilder#invoke`: + +```dart +expect( + explicitThis.invoke('doThing', [literal(true)], genericTypes: [ + lib$core.bool, + ]), + equalsSource(r''' + this.doThing(true) + '''), +); +``` + +- Added a `castAs` method to `ExpressionBuilder`: + +```dart +expect( + literal(1.0).castAs(lib$core.num), + equalsSource(r''' + 1.0 as num + '''), +); +``` + +### BREAKING CHANGES + +- Removed `namedNewInstance` and `namedConstInstance`, replaced with `constructor: `: + +```dart +expect( + reference('Foo').newInstance([], constructor: 'other'), + equalsSource(r''' + new Foo.other() + '''), +); +``` + +- Renamed `named` parameter to `namedArguments`: + +```dart +expect( + reference('doThing').call( + [literal(true)], + namedArguments: { + 'otherFlag': literal(false), + }, + ), + equalsSource(r''' + doThing(true, otherFlag: false) + '''), +); +``` + +## 1.0.0-beta+2 + +### BREAKING CHANGES + +Avoid creating symbols that can collide with the Dart language: + +- `MethodModifier.async` -> `MethodModifier.asAsync` +- `MethodModifier.asyncStar` -> `MethodModifier.asAsyncStar` +- `MethodModifier.syncStar` -> `MethodModifier.asSyncStar` + +## 1.0.0-beta+1 + +- Add support for `switch` statements +- Add support for a raw expression and statement + - `new ExpressionBuilder.raw(...)` + - `new StatemnetBuilder.raw(...)` + +This should help cover any cases not covered with builders today. + +- Allow referring to a `ClassBuilder` and `TypeBuilder` as an expression +- Add support for accessing the index `[]` operator on an expression + +### BREAKING CHANGES + +- Changed `ExpressionBuilder.asAssign` to always take an `ExpressionBuilder` as + target and removed the `value` property. Most changes are pretty simple, and + involve just using `reference(...)`. For example: + +```dart +literal(true).asAssign(reference('flag')) +``` + +... emits `flag = true`. + +## 1.0.0-beta + +- Add support for `async`, `sync`, `sync*` functions +- Add support for expression `asAwait`, `asYield`, `asYieldStar` +- Add `toExportBuilder` and `toImportBuilder` to types and references +- Fix an import scoping bug in `return` statements and named constructor invocations. +- Added constructor initializer support +- Add `while` and `do {} while` loop support +- Add `for` and `for-in` support +- Added a `name` getter for `ParameterBuilder` + +## 1.0.0-alpha+7 + +- Make use of the new analyzer APIs in preparation for analyzer version 0.30. + +## 1.0.0-alpha+6 + +- `MethodBuilder.closure` emits properly as a top-level function + +## 1.0.0-alpha+5 + +- MethodBuilder with no statements will create an empty block instead of + a semicolon. + +```dart +// main() {} +method('main') +``` + +- Fix lambdas and closures to not include a trailing semicolon when used + as an expression. + +```dart + // () => false + new MethodBuilder.closure(returns: literal(false)); +``` + +## 1.0.0-alpha+4 + +- Add support for the latest `pkg/analyzer`. + +## 1.0.0-alpha+3 + +- BREAKING CHANGE: Added generics support to `TypeBuilder`: + +`importFrom` becomes a _named_, not a positional argument, and the named +argument `genericTypes` is added (`Iterable`). + +```dart +// List +new TypeBuilder('List', genericTypes: [reference('String')]) +``` + +- Added generic support to `ReferenceBuilder`: + +```dart +// List +reference('List').toTyped([reference('String')]) +``` + +- Fixed a bug where `ReferenceBuilder.buildAst` was not implemented +- Added `and` and `or` methods to `ExpressionBuilder`: + +```dart +// true || false +literal(true).or(literal(false)); + +// true && false +literal(true).and(literal(false)); +``` + +- Added support for creating closures - `MethodBuilder.closure`: + +```dart +// () => true +new MethodBuilder.closure( + returns: literal(true), + returnType: lib$core.bool, +) +``` + +## 1.0.0-alpha+2 + +- Added `returnVoid` to well, `return;` +- Added support for top-level field assignments: + +```dart +new LibraryBuilder()..addMember(literal(false).asConst('foo')) +``` + +- Added support for specifying a `target` when using `asAssign`: + +```dart +// Outputs bank.bar = goldBar +reference('goldBar').asAssign('bar', target: reference('bank')) +``` + +- Added support for the cascade operator: + +```dart +// Outputs foo..doThis()..doThat() +reference('foo').cascade((c) => [ + c.invoke('doThis', []), + c.invoke('doThat', []), +]); +``` + +- Added support for accessing a property + +```dart +// foo.bar +reference('foo').property('bar'); +``` + +## 1.0.0-alpha+1 + +- Slight updates to confusing documentation. +- Added support for null-aware assignments. +- Added `show` and `hide` support to `ImportBuilder` +- Added `deferred` support to `ImportBuilder` +- Added `ExportBuilder` +- Added `list` and `map` literals that support generic types + +## 1.0.0-alpha + +- Large refactor that makes the library more feature complete. + +## 0.1.1 + +- Add the concept of `Scope` and change `toAst` to support it + +Now your entire AST tree can be scoped and import directives +automatically added to a `LibraryBuilder` for you if you use +`LibraryBuilder.scope`. + +## 0.1.0 + +- Initial version diff --git a/pkgs/code_builder/LICENSE b/pkgs/code_builder/LICENSE new file mode 100644 index 000000000..237243134 --- /dev/null +++ b/pkgs/code_builder/LICENSE @@ -0,0 +1,27 @@ +Copyright 2016, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/pkgs/code_builder/README.md b/pkgs/code_builder/README.md new file mode 100644 index 000000000..86decb594 --- /dev/null +++ b/pkgs/code_builder/README.md @@ -0,0 +1,114 @@ +[![Build Status](https://github.com/dart-lang/tools/actions/workflows/code_builder.yaml/badge.svg)](https://github.com/dart-lang/tools/actions/workflows/code_builder.yaml) +[![Pub package](https://img.shields.io/pub/v/code_builder.svg)](https://pub.dev/packages/code_builder) +[![package publisher](https://img.shields.io/pub/publisher/code_builder.svg)](https://pub.dev/packages/code_builder/publisher) +[![Gitter chat](https://badges.gitter.im/dart-lang/build.svg)](https://gitter.im/dart-lang/build) + +A fluent, builder-based library for generating valid Dart code. + +## Usage + +`code_builder` has a narrow and user-friendly API. + +See the `example` and `test` folders for additional examples. + +For example creating a class with a method: + +```dart +import 'package:code_builder/code_builder.dart'; +import 'package:dart_style/dart_style.dart'; + +void main() { + final animal = Class((b) => b + ..name = 'Animal' + ..extend = refer('Organism') + ..methods.add(Method.returnsVoid((b) => b + ..name = 'eat' + ..body = const Code("print('Yum!');")))); + final emitter = DartEmitter(); + print( + DartFormatter(languageVersion: DartFormatter.latestLanguageVersion) + .format('${animal.accept(emitter)}'), + ); +} +``` + +Outputs: + +```dart +class Animal extends Organism { + void eat() => print('Yum!'); +} +``` + +Have a complicated set of dependencies for your generated code? `code_builder` +supports automatic scoping of your ASTs to automatically use prefixes to avoid +symbol conflicts: + +```dart +import 'package:code_builder/code_builder.dart'; +import 'package:dart_style/dart_style.dart'; + +void main() { + final library = Library((b) => b.body.addAll([ + Method((b) => b + ..body = const Code('') + ..name = 'doThing' + ..returns = refer('Thing', 'package:a/a.dart')), + Method((b) => b + ..body = const Code('') + ..name = 'doOther' + ..returns = refer('Other', 'package:b/b.dart')), + ])); + final emitter = DartEmitter.scoped(); + print( + DartFormatter(languageVersion: DartFormatter.latestLanguageVersion) + .format('${library.accept(emitter)}'), + ); +} +``` + +Outputs: + +```dart +import 'package:a/a.dart' as _i1; +import 'package:b/b.dart' as _i2; + +_i1.Thing doThing() {} +_i2.Other doOther() {} +``` + +## Contributing + +- Read and help us document common patterns over [in the docs][docs]. +- Is there a _bug_ in the code? [File an issue][issue]. + +If a feature is missing (the Dart language is always evolving) or you'd like an +easier or better way to do something, consider [opening a pull request][pull]. +You can always [file an issue][issue], but generally speaking, feature requests +will be on a best-effort basis. + +> **NOTE**: Due to the evolving Dart SDK the local `dartfmt` must be used to +> format this repository. You can run it simply from the command-line: +> +> ```sh +> $ dart run dart_style:format -w . +> ``` + +[issue]: https://github.com/dart-lang/tools/issues +[pull]: https://github.com/dart-lang/tools/pulls + +### Updating generated (`.g.dart`) files + +> **NOTE**: There is currently a limitation in `build_runner` that requires a +> workaround for developing this package since it is a dependency of the build +> system. + +Make a snapshot of the generated [`build_runner`][build_runner] build script and +run from the snapshot instead of from source to avoid problems with deleted +files. These steps must be run without deleting the source files. + +```bash +./tool/regenerate.sh +``` + +[build_runner]: https://pub.dev/packages/build_runner diff --git a/pkgs/code_builder/analysis_options.yaml b/pkgs/code_builder/analysis_options.yaml new file mode 100644 index 000000000..d052c68a8 --- /dev/null +++ b/pkgs/code_builder/analysis_options.yaml @@ -0,0 +1,25 @@ +include: package:dart_flutter_team_lints/analysis_options.yaml + +analyzer: + language: + strict-casts: true + strict-inference: true + strict-raw-types: true + +linter: + rules: + - avoid_private_typedef_functions + - avoid_redundant_argument_values + - avoid_unused_constructor_parameters + - cancel_subscriptions + - cascade_invocations + - join_return_with_assignment + - literal_only_boolean_expressions + - missing_whitespace_between_adjacent_strings + - no_adjacent_strings_in_list + - no_runtimeType_toString + - prefer_const_declarations + - prefer_expression_function_bodies + - prefer_final_locals + - unnecessary_await_in_return + - use_string_buffers diff --git a/pkgs/code_builder/doc/Getting-Started.md b/pkgs/code_builder/doc/Getting-Started.md new file mode 100644 index 000000000..b82da6cfc --- /dev/null +++ b/pkgs/code_builder/doc/Getting-Started.md @@ -0,0 +1,35 @@ +# Common patterns + +## Classes + +### Creating a simple class + +> ```dart +> class Animal {} +> ``` + +```dart +new ClassBuilder( + 'Animal', +) +``` + +### Creating an abstract class with a method + +> ```dart +> abstract class Animal { +> void eat(); +> } +> ``` + +```dart +new ClassBuilder( + 'Animal', + asAbstract: true, +)..addMethod( + new MethodBuilder.returnVoid( + 'eat', + asAbstract: true, + ), +) +``` \ No newline at end of file diff --git a/pkgs/code_builder/doc/Home.md b/pkgs/code_builder/doc/Home.md new file mode 100644 index 000000000..72e87a8d7 --- /dev/null +++ b/pkgs/code_builder/doc/Home.md @@ -0,0 +1,6 @@ +The [`code_builder`][code_builder] package helps generate valid [Dart][] source code programmatically, usually as part of a build process or generation step. Most of the API currently takes a little exploring to get used to, it's recommended to read the [API documentation][docs], or read the [getting started][] guide. + +[code_builder]: pub.dartlang.org/packages/code_builder +[Dart]: https://www.dartlang.org/ +[docs]: https://www.dartdocs.org/documentation/code_builder/latest +[getting started]: Getting-Started diff --git a/pkgs/code_builder/example/example.dart b/pkgs/code_builder/example/example.dart new file mode 100644 index 000000000..6f73598e9 --- /dev/null +++ b/pkgs/code_builder/example/example.dart @@ -0,0 +1,86 @@ +// 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. + +import 'package:code_builder/code_builder.dart'; +import 'package:dart_style/dart_style.dart'; + +final _dartfmt = DartFormatter( + languageVersion: DartFormatter.latestLanguageVersion, +); + +void main() { + print('animalClass():\n${'=' * 40}\n${animalClass()}'); + print('scopedLibrary():\n${'=' * 40}\n${scopedLibrary()}'); + print('jsonEnum():\n${'=' * 40}\n${jsonEnum()}'); +} + +/// Outputs: +/// +/// ```dart +/// class Animal extends Organism { +/// void eat() => print('Yum!'); +/// } +/// ``` +String animalClass() { + final animal = Class((b) => b + ..name = 'Animal' + ..extend = refer('Organism') + ..methods.add(Method.returnsVoid((b) => b + ..name = 'eat' + ..body = refer('print').call([literalString('Yum!')]).code))); + return _dartfmt.format('${animal.accept(DartEmitter())}'); +} + +/// Outputs: +/// +/// ```dart +/// import 'package:a/a.dart' as _i1; +/// import 'package:b/b.dart' as _i2; +/// +/// _i1.Thing doThing() {} +/// _i2.Other doOther() {} +/// ``` +String scopedLibrary() { + final methods = [ + Method((b) => b + ..body = const Code('') + ..name = 'doThing' + ..returns = refer('Thing', 'package:a/a.dart')), + Method((b) => b + ..body = const Code('') + ..name = 'doOther' + ..returns = refer('Other', 'package:b/b.dart')), + ]; + final library = Library((b) => b.body.addAll(methods)); + return _dartfmt.format('${library.accept(DartEmitter.scoped())}'); +} + +/// Outputs: +/// +/// ```dart +/// enum Unit { +/// @JsonKey('m') +/// metric, +/// @JsonKey('i') +/// imperial +/// } +/// ``` +String jsonEnum() { + final values = [ + EnumValue((b) => b + ..name = 'metric' + ..annotations.addAll([ + refer('JsonKey').call([literalString('m')]) + ])), + EnumValue((b) => b + ..name = 'imperial' + ..annotations.addAll([ + refer('JsonKey').call([literalString('i')]) + ])), + ]; + final e = Enum((b) => b + ..name = 'Unit' + ..values.addAll(values)); + return _dartfmt.format('${e.accept(DartEmitter())}'); +} diff --git a/pkgs/code_builder/lib/code_builder.dart b/pkgs/code_builder/lib/code_builder.dart new file mode 100644 index 000000000..9cd15b958 --- /dev/null +++ b/pkgs/code_builder/lib/code_builder.dart @@ -0,0 +1,72 @@ +// 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. + +export 'src/allocator.dart' show Allocator; +export 'src/base.dart' show Spec, lazySpec; +export 'src/emitter.dart' show DartEmitter; +export 'src/matchers.dart' show EqualsDart, equalsDart; +export 'src/specs/class.dart' show Class, ClassBuilder, ClassModifier; +export 'src/specs/code.dart' + show Block, BlockBuilder, Code, ScopedCode, StaticCode, lazyCode; +export 'src/specs/constructor.dart' show Constructor, ConstructorBuilder; +export 'src/specs/directive.dart' + show Directive, DirectiveBuilder, DirectiveType; +export 'src/specs/enum.dart' + show Enum, EnumBuilder, EnumValue, EnumValueBuilder; +export 'src/specs/expression.dart' + show + BinaryExpression, + CodeExpression, + Expression, + ExpressionEmitter, + ExpressionVisitor, + InvokeExpression, + InvokeExpressionType, + LiteralExpression, + LiteralListExpression, + ParenthesizedExpression, + ToCodeExpression, + declareConst, + declareFinal, + declareVar, + literal, + literalBool, + literalConstList, + literalConstMap, + literalConstRecord, + literalConstSet, + literalFalse, + literalList, + literalMap, + literalNull, + literalNullSafeSpread, + literalNum, + literalRecord, + literalSet, + literalSpread, + literalString, + literalTrue; +export 'src/specs/extension.dart' show Extension, ExtensionBuilder; +export 'src/specs/extension_type.dart' + show + ExtensionType, + ExtensionTypeBuilder, + RepresentationDeclaration, + RepresentationDeclarationBuilder; +export 'src/specs/field.dart' show Field, FieldBuilder, FieldModifier; +export 'src/specs/library.dart' show Library, LibraryBuilder; +export 'src/specs/method.dart' + show + Method, + MethodBuilder, + MethodModifier, + MethodType, + Parameter, + ParameterBuilder; +export 'src/specs/mixin.dart' show Mixin, MixinBuilder; +export 'src/specs/reference.dart' show Reference, refer; +export 'src/specs/type_function.dart' show FunctionType, FunctionTypeBuilder; +export 'src/specs/type_record.dart' show RecordType, RecordTypeBuilder; +export 'src/specs/type_reference.dart' show TypeReference, TypeReferenceBuilder; +export 'src/specs/typedef.dart' show TypeDef, TypeDefBuilder; diff --git a/pkgs/code_builder/lib/src/allocator.dart b/pkgs/code_builder/lib/src/allocator.dart new file mode 100644 index 000000000..18d2d8c0d --- /dev/null +++ b/pkgs/code_builder/lib/src/allocator.dart @@ -0,0 +1,96 @@ +// 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. + +import 'specs/directive.dart'; +import 'specs/reference.dart'; + +/// Collects references and automatically allocates prefixes and imports. +/// +/// `Allocator` takes out the manual work of deciding whether a symbol will +/// clash with other imports in your generated code, or what imports are needed +/// to resolve all symbols in your generated code. +abstract class Allocator { + /// An allocator that does not prefix symbols nor collects imports. + static const Allocator none = _NullAllocator(); + + /// Creates a new default allocator that applies no prefixing. + factory Allocator() = _Allocator; + + /// Creates a new allocator that applies naive prefixing to avoid conflicts. + /// + /// This implementation is not optimized for any particular code generation + /// style and instead takes a conservative approach of prefixing _every_ + /// import except references to `dart:core` (which are considered always + /// imported). + /// + /// The prefixes are not guaranteed to be stable and cannot be expected to + /// have any particular value. + factory Allocator.simplePrefixing() = _PrefixedAllocator; + + /// Returns a reference string given a [reference] object. + /// + /// For example, a no-op implementation: + /// ```dart + /// allocate(const Reference('List', 'dart:core')); // Returns 'List'. + /// ``` + /// + /// Where-as an implementation that prefixes imports might output: + /// ```dart + /// allocate(const Reference('Foo', 'package:foo')); // Returns '_i1.Foo'. + /// ``` + String allocate(Reference reference); + + /// All imports that have so far been added implicitly via [allocate]. + Iterable get imports; +} + +class _Allocator implements Allocator { + final _imports = {}; + + @override + String allocate(Reference reference) { + final url = reference.url; + if (url != null) { + _imports.add(url); + } + return reference.symbol!; + } + + @override + Iterable get imports => _imports.map(Directive.import); +} + +class _NullAllocator implements Allocator { + const _NullAllocator(); + + @override + String allocate(Reference reference) => reference.symbol!; + + @override + Iterable get imports => const []; +} + +class _PrefixedAllocator implements Allocator { + static const _doNotPrefix = ['dart:core']; + + final _imports = {}; + var _keys = 1; + + @override + String allocate(Reference reference) { + final symbol = reference.symbol; + final url = reference.url; + if (url == null || _doNotPrefix.contains(url)) { + return symbol!; + } + return '_i${_imports.putIfAbsent(url, _nextKey)}.$symbol'; + } + + int _nextKey() => _keys++; + + @override + Iterable get imports => _imports.keys.map( + (u) => Directive.import(u, as: '_i${_imports[u]}'), + ); +} diff --git a/pkgs/code_builder/lib/src/base.dart b/pkgs/code_builder/lib/src/base.dart new file mode 100644 index 000000000..216b9a982 --- /dev/null +++ b/pkgs/code_builder/lib/src/base.dart @@ -0,0 +1,22 @@ +// 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. + +import 'visitors.dart'; + +abstract class Spec { + R accept(SpecVisitor visitor, [R? context]); +} + +/// Returns a generic [Spec] that is lazily generated when visited. +Spec lazySpec(Spec Function() generate) => _LazySpec(generate); + +class _LazySpec implements Spec { + final Spec Function() generate; + + const _LazySpec(this.generate); + + @override + R accept(SpecVisitor visitor, [R? context]) => + generate().accept(visitor, context); +} diff --git a/pkgs/code_builder/lib/src/emitter.dart b/pkgs/code_builder/lib/src/emitter.dart new file mode 100644 index 000000000..12109cc38 --- /dev/null +++ b/pkgs/code_builder/lib/src/emitter.dart @@ -0,0 +1,944 @@ +// 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. + +import 'allocator.dart'; +import 'base.dart'; +import 'specs/class.dart'; +import 'specs/code.dart'; +import 'specs/constructor.dart'; +import 'specs/directive.dart'; +import 'specs/enum.dart'; +import 'specs/expression.dart'; +import 'specs/extension.dart'; +import 'specs/extension_type.dart'; +import 'specs/field.dart'; +import 'specs/library.dart'; +import 'specs/method.dart'; +import 'specs/mixin.dart'; +import 'specs/reference.dart'; +import 'specs/type_function.dart'; +import 'specs/type_record.dart'; +import 'specs/type_reference.dart'; +import 'specs/typedef.dart'; +import 'visitors.dart'; + +/// Helper method improving on [StringSink.writeAll]. +/// +/// For every `Spec` in [elements], executing [visit]. +/// +/// If [elements] is at least 2 elements, inserts [separator] delimiting them. +StringSink visitAll( + Iterable elements, + StringSink output, + void Function(T) visit, [ + String separator = ', ', +]) { + // Basically, this whole method is an improvement on + // output.writeAll(specs.map((s) => s.accept(visitor)); + // + // ... which would allocate more StringBuffer(s) for a one-time use. + if (elements.isEmpty) { + return output; + } + final iterator = elements.iterator..moveNext(); + visit(iterator.current); + while (iterator.moveNext()) { + output.write(separator); + visit(iterator.current); + } + return output; +} + +class DartEmitter extends Object + with CodeEmitter, ExpressionEmitter + implements SpecVisitor { + @override + final Allocator allocator; + + /// If directives should be ordered while emitting. + /// + /// Ordering rules follow the guidance in + /// [Effective Dart](https://dart.dev/guides/language/effective-dart/style#ordering) + /// and the + /// [directives_ordering](https://dart-lang.github.io/linter/lints/directives_ordering.html) + /// lint. + final bool orderDirectives; + + /// If nullable types should be emitted with the nullable suffix ("?"). + /// + /// Null safety syntax should only be enabled if the output will be used with + /// a Dart language version which supports it. + final bool _useNullSafetySyntax; + + /// Creates a new instance of [DartEmitter]. + /// + /// May specify an [Allocator] to use for references and imports, + /// otherwise uses [Allocator.none] which never prefixes references and will + /// not automatically emit import directives. + DartEmitter( + {this.allocator = Allocator.none, + this.orderDirectives = false, + bool useNullSafetySyntax = false}) + : _useNullSafetySyntax = useNullSafetySyntax; + + /// Creates a new instance of [DartEmitter] with simple automatic imports. + factory DartEmitter.scoped( + {bool orderDirectives = false, bool useNullSafetySyntax = false}) => + DartEmitter( + allocator: Allocator.simplePrefixing(), + orderDirectives: orderDirectives, + useNullSafetySyntax: useNullSafetySyntax); + + static bool _isLambdaBody(Code? code) => + code is ToCodeExpression && !code.isStatement; + + /// Whether the provided [method] is considered a lambda method. + static bool _isLambdaMethod(Method method) => + method.lambda ?? _isLambdaBody(method.body); + + /// Whether the provided [constructor] is considered a lambda method. + static bool _isLambdaConstructor(Constructor constructor) => + constructor.lambda ?? + constructor.factory && _isLambdaBody(constructor.body); + + @override + StringSink visitAnnotation(Expression spec, [StringSink? output]) { + (output ??= StringBuffer()).write('@'); + spec.accept(this, output); + output.write(' '); + return output; + } + + @override + StringSink visitClass(Class spec, [StringSink? output]) { + final out = output ??= StringBuffer(); + spec.docs.forEach(out.writeln); + for (var a in spec.annotations) { + visitAnnotation(a, out); + } + + void writeModifier() { + if (spec.modifier != null) { + out.write('${spec.modifier!.name} '); + } + } + + if (spec.sealed) { + out.write('sealed '); + } else { + if (spec.abstract) { + out.write('abstract '); + } + writeModifier(); + if (spec.mixin) { + out.write('mixin '); + } + } + out.write('class ${spec.name}'); + visitTypeParameters(spec.types.map((r) => r.type), out); + if (spec.extend != null) { + out.write(' extends '); + spec.extend!.type.accept(this, out); + } + if (spec.mixins.isNotEmpty) { + out + ..write(' with ') + ..writeAll( + spec.mixins.map((m) => m.type.accept(this)), ','); + } + if (spec.implements.isNotEmpty) { + out + ..write(' implements ') + ..writeAll( + spec.implements.map((m) => m.type.accept(this)), ','); + } + out.write(' {'); + for (var c in spec.constructors) { + visitConstructor(c, spec.name, out); + out.writeln(); + } + for (var f in spec.fields) { + visitField(f, out); + out.writeln(); + } + for (var m in spec.methods) { + visitMethod(m, out); + if (_isLambdaMethod(m)) { + out.writeln(';'); + } + out.writeln(); + } + out.writeln(' }'); + return out; + } + + @override + StringSink visitMixin(Mixin spec, [StringSink? output]) { + final out = output ??= StringBuffer(); + spec.docs.forEach(out.writeln); + for (var a in spec.annotations) { + visitAnnotation(a, out); + } + + if (spec.base) { + out.write('base '); + } + out.write('mixin ${spec.name}'); + visitTypeParameters(spec.types.map((r) => r.type), out); + if (spec.on != null) { + out.write(' on '); + spec.on!.type.accept(this, out); + } + if (spec.implements.isNotEmpty) { + out + ..write(' implements ') + ..writeAll( + spec.implements.map((m) => m.type.accept(this)), ','); + } + out.write(' {'); + for (var f in spec.fields) { + visitField(f, out); + out.writeln(); + } + for (var m in spec.methods) { + visitMethod(m, out); + if (_isLambdaMethod(m)) { + out.write(';'); + } + out.writeln(); + } + out.write(' }'); + return out; + } + + @override + StringSink visitConstructor(Constructor spec, String clazz, + [StringSink? output]) { + output ??= StringBuffer(); + spec.docs.forEach(output.writeln); + for (var a in spec.annotations) { + visitAnnotation(a, output); + } + if (spec.external) { + output.write('external '); + } + if (spec.constant) { + output.write('const '); + } + if (spec.factory) { + output.write('factory '); + } + output.write(clazz); + if (spec.name != null) { + output + ..write('.') + ..write(spec.name); + } + output.write('('); + final hasMultipleParameters = + spec.requiredParameters.length + spec.optionalParameters.length > 1; + if (spec.requiredParameters.isNotEmpty) { + var count = 0; + for (final p in spec.requiredParameters) { + count++; + _visitParameter(p, output); + if (hasMultipleParameters || + spec.requiredParameters.length != count || + spec.optionalParameters.isNotEmpty) { + output.write(', '); + } + } + } + if (spec.optionalParameters.isNotEmpty) { + final named = spec.optionalParameters.any((p) => p.named); + if (named) { + output.write('{'); + } else { + output.write('['); + } + var count = 0; + for (final p in spec.optionalParameters) { + count++; + _visitParameter(p, output, optional: true, named: named); + if (hasMultipleParameters || spec.optionalParameters.length != count) { + output.write(', '); + } + } + if (named) { + output.write('}'); + } else { + output.write(']'); + } + } + output.write(')'); + if (spec.initializers.isNotEmpty) { + output.write(' : '); + var count = 0; + for (final initializer in spec.initializers) { + count++; + initializer.accept(this, output); + if (count != spec.initializers.length) { + output.write(', '); + } + } + } + if (spec.redirect != null) { + output.write(' = '); + spec.redirect!.type.accept(this, output); + output.write(';'); + } else if (spec.body != null) { + if (_isLambdaConstructor(spec)) { + output.write(' => '); + spec.body!.accept(this, output); + output.write(';'); + } else { + output.write(' { '); + spec.body!.accept(this, output); + output.write(' }'); + } + } else { + output.write(';'); + } + output.writeln(); + return output; + } + + @override + StringSink visitExtension(Extension spec, [StringSink? output]) { + final out = output ??= StringBuffer(); + spec.docs.forEach(out.writeln); + for (var a in spec.annotations) { + visitAnnotation(a, out); + } + + out.write('extension'); + if (spec.name != null) { + out.write(' ${spec.name}'); + } + visitTypeParameters(spec.types.map((r) => r.type), out); + if (spec.on != null) { + out.write(' on '); + spec.on!.type.accept(this, out); + } + out.write(' {'); + for (var f in spec.fields) { + visitField(f, out); + out.writeln(); + } + for (var m in spec.methods) { + visitMethod(m, out); + if (_isLambdaMethod(m)) { + out.write(';'); + } + out.writeln(); + } + out.writeln(' }'); + return out; + } + + @override + StringSink visitExtensionType(ExtensionType spec, [StringSink? output]) { + final out = output ??= StringBuffer(); + spec.docs.forEach(out.writeln); + for (var a in spec.annotations) { + visitAnnotation(a, out); + } + + out.write('extension type '); + if (spec.constant) out.write('const '); + out.write(spec.name); + visitTypeParameters(spec.types.map((r) => r.type), out); + if (spec.primaryConstructorName.isNotEmpty) { + out.write('.${spec.primaryConstructorName}'); + } + out.write('('); + _visitRepresentationDeclaration(spec.representationDeclaration, out); + out.write(')'); + + if (spec.implements.isNotEmpty) { + out + ..write(' implements ') + ..writeAll( + spec.implements.map((m) => m.type.accept(this)), ','); + } + + out.writeln(' {'); + for (var c in spec.constructors) { + visitConstructor(c, spec.name, out); + out.writeln(); + } + for (var f in spec.fields) { + visitField(f, out); + out.writeln(); + } + for (var m in spec.methods) { + visitMethod(m, out); + if (_isLambdaMethod(m)) { + out.writeln(';'); + } + out.writeln(); + } + out.writeln('}'); + return out; + } + + void _visitRepresentationDeclaration( + RepresentationDeclaration spec, StringSink out) { + spec.docs.forEach(out.writeln); + for (var a in spec.annotations) { + visitAnnotation(a, out); + } + spec.declaredRepresentationType.accept(this, out); + out.write(' ${spec.name}'); + } + + @override + StringSink visitDirective(Directive spec, [StringSink? output]) { + output ??= StringBuffer(); + switch (spec.type) { + case DirectiveType.import: + output.write('import '); + break; + case DirectiveType.export: + output.write('export '); + break; + case DirectiveType.part: + output.write('part '); + break; + case DirectiveType.partOf: + output.write('part of '); + break; + } + output.write("'${spec.url}'"); + if (spec.as != null) { + if (spec.deferred) { + output.write(' deferred '); + } + output.write(' as ${spec.as}'); + } + if (spec.show.isNotEmpty) { + output + ..write(' show ') + ..writeAll(spec.show, ', '); + } else if (spec.hide.isNotEmpty) { + output + ..write(' hide ') + ..writeAll(spec.hide, ', '); + } + output.write(';'); + return output; + } + + @override + StringSink visitField(Field spec, [StringSink? output]) { + output ??= StringBuffer(); + spec.docs.forEach(output.writeln); + for (var a in spec.annotations) { + visitAnnotation(a, output); + } + if (spec.static) { + output.write('static '); + } + if (spec.late && _useNullSafetySyntax) { + output.write('late '); + } + if (spec.external) { + output.write('external '); + } + switch (spec.modifier) { + case FieldModifier.var$: + if (spec.type == null) { + output.write('var '); + } + break; + case FieldModifier.final$: + output.write('final '); + break; + case FieldModifier.constant: + output.write('const '); + break; + } + if (spec.type != null) { + spec.type!.type.accept(this, output); + output.write(' '); + } + output.write(spec.name); + if (spec.assignment != null) { + output.write(' = '); + startConstCode(spec.modifier == FieldModifier.constant, () { + spec.assignment!.accept(this, output); + }); + } + output.writeln(';'); + return output; + } + + @override + StringSink visitLibrary(Library spec, [StringSink? output]) { + output ??= StringBuffer(); + + if (spec.comments.isNotEmpty) { + spec.comments.map((line) => '// $line').forEach(output.writeln); + output.writeln(); + } + + if (spec.generatedByComment != null) { + output + ..writeln('// ${spec.generatedByComment}') + ..writeln(); + } + + if (spec.ignoreForFile.isNotEmpty) { + final ignores = spec.ignoreForFile.toList()..sort(); + final lines = ['// ignore_for_file: ${ignores.first}']; + for (var ignore in ignores.skip(1)) { + if (lines.last.length + 2 + ignore.length > 80) { + lines.add('// ignore_for_file: $ignore'); + } else { + lines[lines.length - 1] = '${lines.last}, $ignore'; + } + } + lines.forEach(output.writeln); + output.writeln(); + } + + // Process the body first in order to prime the allocators. + final body = StringBuffer(); + for (final spec in spec.body) { + spec.accept(this, body); + if (spec is Method && _isLambdaMethod(spec)) { + body.write(';'); + } + } + + spec.docs.forEach(output.writeln); + for (var a in spec.annotations) { + visitAnnotation(a, output); + } + if (spec.name != null) { + output.write('library ${spec.name!};'); + } else if (spec.annotations.isNotEmpty || spec.docs.isNotEmpty) { + // An explicit _unnamed_ library directive is only required if there are + // annotations or doc comments on the library. + output.write('library;'); + } + + final directives = [...allocator.imports, ...spec.directives]; + + if (orderDirectives) { + directives.sort(); + } + + Directive? previous; + if (directives.any((d) => d.as?.startsWith('_') ?? false)) { + output.writeln( + '// ignore_for_file: no_leading_underscores_for_library_prefixes'); + } + for (final directive in directives) { + if (_newLineBetween(orderDirectives, previous, directive)) { + // Note: dartfmt handles creating new lines between directives. + // 2 lines are written here. The first one comes after the previous + // directive `;`, the second is the empty line. + output + ..writeln() + ..writeln(); + } + directive.accept(this, output); + previous = directive; + } + output.write(body); + return output; + } + + @override + StringSink visitFunctionType(FunctionType spec, [StringSink? output]) { + final out = output ??= StringBuffer(); + if (spec.returnType != null) { + spec.returnType!.accept(this, out); + out.write(' '); + } + out.write('Function'); + if (spec.types.isNotEmpty) { + out.write('<'); + visitAll(spec.types, out, (spec) { + spec.accept(this, out); + }); + out.write('>'); + } + out.write('('); + final needsTrailingComma = spec.requiredParameters.length + + spec.optionalParameters.length + + spec.namedRequiredParameters.length + + spec.namedParameters.length > + 1; + visitAll(spec.requiredParameters, out, (spec) { + spec.accept(this, out); + }); + final hasNamedParameters = spec.namedRequiredParameters.isNotEmpty || + spec.namedParameters.isNotEmpty; + if (spec.requiredParameters.isNotEmpty && + (needsTrailingComma || + spec.optionalParameters.isNotEmpty || + hasNamedParameters)) { + out.write(', '); + } + if (spec.optionalParameters.isNotEmpty) { + out.write('['); + visitAll(spec.optionalParameters, out, (spec) { + spec.accept(this, out); + }); + if (needsTrailingComma) { + out.write(', '); + } + out.write(']'); + } else if (hasNamedParameters) { + out.write('{'); + visitAll(spec.namedRequiredParameters.keys, out, (name) { + out.write('required '); + spec.namedRequiredParameters[name]!.accept(this, out); + out + ..write(' ') + ..write(name); + }); + if (spec.namedRequiredParameters.isNotEmpty && + spec.namedParameters.isNotEmpty) { + out.write(', '); + } + visitAll(spec.namedParameters.keys, out, (name) { + spec.namedParameters[name]!.accept(this, out); + out + ..write(' ') + ..write(name); + }); + if (needsTrailingComma) { + out.write(', '); + } + out.write('}'); + } + out.write(')'); + if (_useNullSafetySyntax && (spec.isNullable ?? false)) { + out.write('?'); + } + return out; + } + + @override + StringSink visitRecordType(RecordType spec, [StringSink? output]) { + final out = (output ??= StringBuffer())..write('('); + visitAll(spec.positionalFieldTypes, out, (spec) { + spec.accept(this, out); + }); + if (spec.namedFieldTypes.isNotEmpty) { + if (spec.positionalFieldTypes.isNotEmpty) { + out.write(', '); + } + out.write('{'); + visitAll>(spec.namedFieldTypes.entries, out, + (entry) { + entry.value.accept(this, out); + out.write(' ${entry.key}'); + }); + out.write('}'); + } else if (spec.positionalFieldTypes.length == 1) { + out.write(','); + } + out.write(')'); + // It doesn't really make sense to use records without + // `_useNullSafetySyntax`, but since code_builder is generally very + // permissive, follow it here too. + if (_useNullSafetySyntax && (spec.isNullable ?? false)) { + out.write('?'); + } + return out; + } + + @override + StringSink visitTypeDef(TypeDef spec, [StringSink? output]) { + final out = output ??= StringBuffer(); + spec.docs.forEach(out.writeln); + for (var a in spec.annotations) { + visitAnnotation(a, out); + } + out.write('typedef ${spec.name}'); + visitTypeParameters(spec.types.map((r) => r.type), out); + out.write(' = '); + spec.definition.accept(this, out); + out.writeln(';'); + return out; + } + + @override + StringSink visitMethod(Method spec, [StringSink? output]) { + output ??= StringBuffer(); + spec.docs.forEach(output.writeln); + for (var a in spec.annotations) { + visitAnnotation(a, output); + } + if (spec.external) { + output.write('external '); + } + if (spec.static) { + output.write('static '); + } + if (spec.returns != null) { + spec.returns!.accept(this, output); + output.write(' '); + } + if (spec.type == MethodType.getter) { + output + ..write('get ') + ..write(spec.name); + } else { + if (spec.type == MethodType.setter) { + output.write('set '); + } + if (spec.name != null) { + output.write(spec.name); + } + visitTypeParameters(spec.types.map((r) => r.type), output); + output.write('('); + final hasMultipleParameters = + spec.requiredParameters.length + spec.optionalParameters.length > 1; + if (spec.requiredParameters.isNotEmpty) { + var count = 0; + for (final p in spec.requiredParameters) { + count++; + _visitParameter(p, output); + if (hasMultipleParameters || + spec.requiredParameters.length != count || + spec.optionalParameters.isNotEmpty) { + output.write(', '); + } + } + } + if (spec.optionalParameters.isNotEmpty) { + final named = spec.optionalParameters.any((p) => p.named); + if (named) { + output.write('{'); + } else { + output.write('['); + } + var count = 0; + for (final p in spec.optionalParameters) { + count++; + _visitParameter(p, output, optional: true, named: named); + if (hasMultipleParameters || + spec.optionalParameters.length != count) { + output.write(', '); + } + } + if (named) { + output.write('}'); + } else { + output.write(']'); + } + } + output.write(')'); + } + if (spec.body != null) { + if (spec.modifier != null) { + switch (spec.modifier!) { + case MethodModifier.async: + output.write(' async '); + break; + case MethodModifier.asyncStar: + output.write(' async* '); + break; + case MethodModifier.syncStar: + output.write(' sync* '); + break; + } + } + if (_isLambdaMethod(spec)) { + output.write(' => '); + } else { + output.write(' { '); + } + spec.body!.accept(this, output); + if (!_isLambdaMethod(spec)) { + output.write(' } '); + } + } else { + output.write(';'); + } + return output; + } + + // Expose as a first-class visit function only if needed. + void _visitParameter( + Parameter spec, + StringSink output, { + bool optional = false, + bool named = false, + }) { + spec.docs.forEach(output.writeln); + for (var a in spec.annotations) { + visitAnnotation(a, output); + } + // The `required` keyword must precede the `covariant` keyword. + if (spec.required) { + output.write('required '); + } + if (spec.covariant) { + output.write('covariant '); + } + if (spec.type != null) { + spec.type!.type.accept(this, output); + output.write(' '); + } + if (spec.toThis) { + output.write('this.'); + } + if (spec.toSuper) { + output.write('super.'); + } + output.write(spec.name); + if (optional && spec.defaultTo != null) { + output.write(' = '); + spec.defaultTo!.accept(this, output); + } + } + + @override + StringSink visitReference(Reference spec, [StringSink? output]) => + (output ??= StringBuffer())..write(allocator.allocate(spec)); + + @override + StringSink visitSpec(Spec spec, [StringSink? output]) => + spec.accept(this, output); + + @override + StringSink visitType(TypeReference spec, [StringSink? output]) { + output ??= StringBuffer(); + // Intentionally not .accept to avoid stack overflow. + visitReference(spec, output); + if (spec.bound != null) { + output.write(' extends '); + spec.bound!.type.accept(this, output); + } + visitTypeParameters(spec.types.map((r) => r.type), output); + if (_useNullSafetySyntax && (spec.isNullable ?? false)) { + output.write('?'); + } + return output; + } + + @override + StringSink visitTypeParameters(Iterable specs, + [StringSink? output]) { + output ??= StringBuffer(); + if (specs.isNotEmpty) { + output + ..write('<') + ..writeAll(specs.map((s) => s.accept(this)), ',') + ..write('>'); + } + return output; + } + + @override + StringSink visitEnum(Enum spec, [StringSink? output]) { + final out = output ??= StringBuffer(); + spec.docs.forEach(out.writeln); + for (var a in spec.annotations) { + visitAnnotation(a, out); + } + out.write('enum ${spec.name}'); + visitTypeParameters(spec.types.map((r) => r.type), out); + if (spec.mixins.isNotEmpty) { + out + ..write(' with ') + ..writeAll( + spec.mixins.map((m) => m.type.accept(this)), ', '); + } + if (spec.implements.isNotEmpty) { + out + ..write(' implements ') + ..writeAll( + spec.implements.map((m) => m.type.accept(this)), ', '); + } + out.write(' { '); + for (var v in spec.values) { + v.docs.forEach(out.writeln); + for (var a in v.annotations) { + visitAnnotation(a, out); + } + out.write(v.name); + if (v.constructorName != null) { + out.write('.${v.constructorName}'); + } + visitTypeParameters(v.types.map((r) => r.type), out); + final takesArguments = v.constructorName != null || + v.arguments.isNotEmpty || + v.namedArguments.isNotEmpty; + if (takesArguments) { + out.write('('); + } + if (v.arguments.isNotEmpty) { + out.writeAll( + v.arguments.map((arg) => arg.accept(this)), ', '); + } + if (v.arguments.isNotEmpty && v.namedArguments.isNotEmpty) { + out.write(', '); + } + visitAll(v.namedArguments.keys, out, (name) { + out + ..write(name) + ..write(': '); + v.namedArguments[name]!.accept(this, out); + }); + if (takesArguments) { + out.write(')'); + } + if (v != spec.values.last) { + out.writeln(','); + } else if (spec.constructors.isNotEmpty || + spec.fields.isNotEmpty || + spec.methods.isNotEmpty) { + out.writeln(';'); + } + } + for (var c in spec.constructors) { + visitConstructor(c, spec.name, out); + out.writeln(); + } + for (var f in spec.fields) { + visitField(f, out); + out.writeln(); + } + for (var m in spec.methods) { + visitMethod(m, out); + if (_isLambdaMethod(m)) { + out.write(';'); + } + out.writeln(); + } + out.writeln(' }'); + return out; + } +} + +/// Returns `true` if: +/// +/// * [ordered] is `true` +/// * [a] is non-`null` +/// * If there should be an empty line before [b] if it's emitted after [a]. +bool _newLineBetween(bool ordered, Directive? a, Directive? b) { + if (!ordered) return false; + if (a == null) return false; + + assert(b != null); + + // Put a line between imports and exports + if (a.type != b!.type) return true; + + // Within exports, don't put in extra blank lines + if (a.type == DirectiveType.export) { + assert(b.type == DirectiveType.export); + return false; + } + + // Return `true` if the schemes for [a] and [b] are different + return !Uri.parse(a.url).isScheme(Uri.parse(b.url).scheme); +} diff --git a/pkgs/code_builder/lib/src/matchers.dart b/pkgs/code_builder/lib/src/matchers.dart new file mode 100644 index 000000000..0cdf73997 --- /dev/null +++ b/pkgs/code_builder/lib/src/matchers.dart @@ -0,0 +1,73 @@ +// 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. + +import 'package:matcher/matcher.dart'; + +import 'base.dart'; +import 'emitter.dart'; + +/// Encodes [spec] as Dart source code. +String _dart(Spec spec, DartEmitter emitter) => + EqualsDart._format(spec.accept(emitter).toString()); + +/// Returns a matcher for [Spec] objects that emit code matching [source]. +/// +/// Both [source] and the result emitted from the compared [Spec] are formatted +/// with [EqualsDart.format]. A plain [DartEmitter] is used by default and may +/// be overridden with [emitter]. +Matcher equalsDart( + String source, [ + DartEmitter? emitter, +]) => + EqualsDart._(EqualsDart._format(source), emitter ?? DartEmitter()); + +/// Implementation detail of using the [equalsDart] matcher. +/// +/// See [EqualsDart.format] to specify the default source code formatter. +class EqualsDart extends Matcher { + /// May override to provide a function to format Dart on [equalsDart]. + /// + /// By default, uses [collapseWhitespace], but it is recommended to instead + /// use `dart_style` (dartfmt) where possible. See `test/common.dart` for an + /// example. + static String Function(String) format = collapseWhitespace; + + static String _format(String source) { + try { + return format(source).trim(); + } catch (_) { + // Ignored on purpose, probably not exactly valid Dart code. + return collapseWhitespace(source).trim(); + } + } + + final DartEmitter _emitter; + final String _expectedSource; + + const EqualsDart._(this._expectedSource, this._emitter); + + @override + Description describe(Description description) => + description.add(_expectedSource); + + @override + Description describeMismatch( + covariant Spec item, + Description mismatchDescription, + Map matchState, + bool verbose, + ) { + final actualSource = _dart(item, _emitter); + return equals(_expectedSource).describeMismatch( + actualSource, + mismatchDescription, + matchState, + verbose, + ); + } + + @override + bool matches(covariant Spec item, Object? matchState) => + _dart(item, _emitter) == _expectedSource; +} diff --git a/pkgs/code_builder/lib/src/mixins/annotations.dart b/pkgs/code_builder/lib/src/mixins/annotations.dart new file mode 100644 index 000000000..7ce6a20b9 --- /dev/null +++ b/pkgs/code_builder/lib/src/mixins/annotations.dart @@ -0,0 +1,19 @@ +// 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. + +import 'package:built_collection/built_collection.dart'; + +import '../specs/expression.dart'; + +/// A type of AST node that can have metadata [annotations]. +abstract mixin class HasAnnotations { + /// Annotations as metadata on the node. + BuiltList get annotations; +} + +/// Compliment to the [HasAnnotations] mixin for metadata [annotations]. +abstract mixin class HasAnnotationsBuilder { + /// Annotations as metadata on the node. + abstract ListBuilder annotations; +} diff --git a/pkgs/code_builder/lib/src/mixins/dartdoc.dart b/pkgs/code_builder/lib/src/mixins/dartdoc.dart new file mode 100644 index 000000000..78144f6fd --- /dev/null +++ b/pkgs/code_builder/lib/src/mixins/dartdoc.dart @@ -0,0 +1,15 @@ +// 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. + +import 'package:built_collection/built_collection.dart'; + +abstract mixin class HasDartDocs { + /// Dart docs. + BuiltList get docs; +} + +abstract mixin class HasDartDocsBuilder { + /// Dart docs. + abstract ListBuilder docs; +} diff --git a/pkgs/code_builder/lib/src/mixins/generics.dart b/pkgs/code_builder/lib/src/mixins/generics.dart new file mode 100644 index 000000000..b18a858c3 --- /dev/null +++ b/pkgs/code_builder/lib/src/mixins/generics.dart @@ -0,0 +1,17 @@ +// 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. + +import 'package:built_collection/built_collection.dart'; + +import '../specs/reference.dart'; + +abstract mixin class HasGenerics { + /// Generic type parameters. + BuiltList get types; +} + +abstract mixin class HasGenericsBuilder { + /// Generic type parameters. + abstract ListBuilder types; +} diff --git a/pkgs/code_builder/lib/src/specs/class.dart b/pkgs/code_builder/lib/src/specs/class.dart new file mode 100644 index 000000000..4c1915eaf --- /dev/null +++ b/pkgs/code_builder/lib/src/specs/class.dart @@ -0,0 +1,128 @@ +// 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. + +import 'package:built_collection/built_collection.dart'; +import 'package:built_value/built_value.dart'; +import 'package:meta/meta.dart'; + +import '../base.dart'; +import '../mixins/annotations.dart'; +import '../mixins/dartdoc.dart'; +import '../mixins/generics.dart'; +import '../visitors.dart'; +import 'constructor.dart'; +import 'expression.dart'; +import 'field.dart'; +import 'method.dart'; +import 'reference.dart'; + +part 'class.g.dart'; + +@immutable +abstract class Class extends Object + with HasAnnotations, HasDartDocs, HasGenerics + implements Built, Spec { + factory Class([void Function(ClassBuilder) updates]) = _$Class; + + Class._(); + + /// Whether the class is `abstract`. + bool get abstract; + + /// Whether the class is `sealed`. + bool get sealed; + + /// Whether the class is a `mixin class`. + bool get mixin; + + /// The class modifier, i.e. `base`, `final`, `interface`. + ClassModifier? get modifier; + + @override + BuiltList get annotations; + + @override + BuiltList get docs; + + Reference? get extend; + + BuiltList get implements; + + BuiltList get mixins; + + @override + BuiltList get types; + + BuiltList get constructors; + BuiltList get methods; + BuiltList get fields; + + /// Name of the class. + String get name; + + @override + R accept( + SpecVisitor visitor, [ + R? context, + ]) => + visitor.visitClass(this, context); +} + +enum ClassModifier { + base, + final$, + interface; + + String get name => switch (this) { + ClassModifier.base => 'base', + ClassModifier.final$ => 'final', + ClassModifier.interface => 'interface' + }; +} + +abstract class ClassBuilder extends Object + with HasAnnotationsBuilder, HasDartDocsBuilder, HasGenericsBuilder + implements Builder { + factory ClassBuilder() = _$ClassBuilder; + + ClassBuilder._(); + + @override + void update(void Function(ClassBuilder)? updates) { + updates?.call(this); + } + + /// Whether the class is `abstract`. + bool abstract = false; + + /// Whether the class is `sealed`. + bool sealed = false; + + /// Whether the class is a `mixin class`. + bool mixin = false; + + /// The class modifier, i.e. `base`, `final`, `interface`. + ClassModifier? modifier; + + @override + ListBuilder annotations = ListBuilder(); + + @override + ListBuilder docs = ListBuilder(); + + Reference? extend; + + ListBuilder implements = ListBuilder(); + ListBuilder mixins = ListBuilder(); + + @override + ListBuilder types = ListBuilder(); + + ListBuilder constructors = ListBuilder(); + ListBuilder methods = ListBuilder(); + ListBuilder fields = ListBuilder(); + + /// Name of the class. + String? name; +} diff --git a/pkgs/code_builder/lib/src/specs/class.g.dart b/pkgs/code_builder/lib/src/specs/class.g.dart new file mode 100644 index 000000000..423f576ce --- /dev/null +++ b/pkgs/code_builder/lib/src/specs/class.g.dart @@ -0,0 +1,405 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'class.dart'; + +// ************************************************************************** +// BuiltValueGenerator +// ************************************************************************** + +class _$Class extends Class { + @override + final bool abstract; + @override + final bool sealed; + @override + final bool mixin; + @override + final ClassModifier? modifier; + @override + final BuiltList annotations; + @override + final BuiltList docs; + @override + final Reference? extend; + @override + final BuiltList implements; + @override + final BuiltList mixins; + @override + final BuiltList types; + @override + final BuiltList constructors; + @override + final BuiltList methods; + @override + final BuiltList fields; + @override + final String name; + + factory _$Class([void Function(ClassBuilder)? updates]) => + (new ClassBuilder()..update(updates)).build() as _$Class; + + _$Class._( + {required this.abstract, + required this.sealed, + required this.mixin, + this.modifier, + required this.annotations, + required this.docs, + this.extend, + required this.implements, + required this.mixins, + required this.types, + required this.constructors, + required this.methods, + required this.fields, + required this.name}) + : super._() { + BuiltValueNullFieldError.checkNotNull(abstract, r'Class', 'abstract'); + BuiltValueNullFieldError.checkNotNull(sealed, r'Class', 'sealed'); + BuiltValueNullFieldError.checkNotNull(mixin, r'Class', 'mixin'); + BuiltValueNullFieldError.checkNotNull(annotations, r'Class', 'annotations'); + BuiltValueNullFieldError.checkNotNull(docs, r'Class', 'docs'); + BuiltValueNullFieldError.checkNotNull(implements, r'Class', 'implements'); + BuiltValueNullFieldError.checkNotNull(mixins, r'Class', 'mixins'); + BuiltValueNullFieldError.checkNotNull(types, r'Class', 'types'); + BuiltValueNullFieldError.checkNotNull( + constructors, r'Class', 'constructors'); + BuiltValueNullFieldError.checkNotNull(methods, r'Class', 'methods'); + BuiltValueNullFieldError.checkNotNull(fields, r'Class', 'fields'); + BuiltValueNullFieldError.checkNotNull(name, r'Class', 'name'); + } + + @override + Class rebuild(void Function(ClassBuilder) updates) => + (toBuilder()..update(updates)).build(); + + @override + _$ClassBuilder toBuilder() => new _$ClassBuilder()..replace(this); + + @override + bool operator ==(Object other) { + if (identical(other, this)) return true; + return other is Class && + abstract == other.abstract && + sealed == other.sealed && + mixin == other.mixin && + modifier == other.modifier && + annotations == other.annotations && + docs == other.docs && + extend == other.extend && + implements == other.implements && + mixins == other.mixins && + types == other.types && + constructors == other.constructors && + methods == other.methods && + fields == other.fields && + name == other.name; + } + + @override + int get hashCode { + var _$hash = 0; + _$hash = $jc(_$hash, abstract.hashCode); + _$hash = $jc(_$hash, sealed.hashCode); + _$hash = $jc(_$hash, mixin.hashCode); + _$hash = $jc(_$hash, modifier.hashCode); + _$hash = $jc(_$hash, annotations.hashCode); + _$hash = $jc(_$hash, docs.hashCode); + _$hash = $jc(_$hash, extend.hashCode); + _$hash = $jc(_$hash, implements.hashCode); + _$hash = $jc(_$hash, mixins.hashCode); + _$hash = $jc(_$hash, types.hashCode); + _$hash = $jc(_$hash, constructors.hashCode); + _$hash = $jc(_$hash, methods.hashCode); + _$hash = $jc(_$hash, fields.hashCode); + _$hash = $jc(_$hash, name.hashCode); + _$hash = $jf(_$hash); + return _$hash; + } + + @override + String toString() { + return (newBuiltValueToStringHelper(r'Class') + ..add('abstract', abstract) + ..add('sealed', sealed) + ..add('mixin', mixin) + ..add('modifier', modifier) + ..add('annotations', annotations) + ..add('docs', docs) + ..add('extend', extend) + ..add('implements', implements) + ..add('mixins', mixins) + ..add('types', types) + ..add('constructors', constructors) + ..add('methods', methods) + ..add('fields', fields) + ..add('name', name)) + .toString(); + } +} + +class _$ClassBuilder extends ClassBuilder { + _$Class? _$v; + + @override + bool get abstract { + _$this; + return super.abstract; + } + + @override + set abstract(bool abstract) { + _$this; + super.abstract = abstract; + } + + @override + bool get sealed { + _$this; + return super.sealed; + } + + @override + set sealed(bool sealed) { + _$this; + super.sealed = sealed; + } + + @override + bool get mixin { + _$this; + return super.mixin; + } + + @override + set mixin(bool mixin) { + _$this; + super.mixin = mixin; + } + + @override + ClassModifier? get modifier { + _$this; + return super.modifier; + } + + @override + set modifier(ClassModifier? modifier) { + _$this; + super.modifier = modifier; + } + + @override + ListBuilder get annotations { + _$this; + return super.annotations; + } + + @override + set annotations(ListBuilder annotations) { + _$this; + super.annotations = annotations; + } + + @override + ListBuilder get docs { + _$this; + return super.docs; + } + + @override + set docs(ListBuilder docs) { + _$this; + super.docs = docs; + } + + @override + Reference? get extend { + _$this; + return super.extend; + } + + @override + set extend(Reference? extend) { + _$this; + super.extend = extend; + } + + @override + ListBuilder get implements { + _$this; + return super.implements; + } + + @override + set implements(ListBuilder implements) { + _$this; + super.implements = implements; + } + + @override + ListBuilder get mixins { + _$this; + return super.mixins; + } + + @override + set mixins(ListBuilder mixins) { + _$this; + super.mixins = mixins; + } + + @override + ListBuilder get types { + _$this; + return super.types; + } + + @override + set types(ListBuilder types) { + _$this; + super.types = types; + } + + @override + ListBuilder get constructors { + _$this; + return super.constructors; + } + + @override + set constructors(ListBuilder constructors) { + _$this; + super.constructors = constructors; + } + + @override + ListBuilder get methods { + _$this; + return super.methods; + } + + @override + set methods(ListBuilder methods) { + _$this; + super.methods = methods; + } + + @override + ListBuilder get fields { + _$this; + return super.fields; + } + + @override + set fields(ListBuilder fields) { + _$this; + super.fields = fields; + } + + @override + String? get name { + _$this; + return super.name; + } + + @override + set name(String? name) { + _$this; + super.name = name; + } + + _$ClassBuilder() : super._(); + + ClassBuilder get _$this { + final $v = _$v; + if ($v != null) { + super.abstract = $v.abstract; + super.sealed = $v.sealed; + super.mixin = $v.mixin; + super.modifier = $v.modifier; + super.annotations = $v.annotations.toBuilder(); + super.docs = $v.docs.toBuilder(); + super.extend = $v.extend; + super.implements = $v.implements.toBuilder(); + super.mixins = $v.mixins.toBuilder(); + super.types = $v.types.toBuilder(); + super.constructors = $v.constructors.toBuilder(); + super.methods = $v.methods.toBuilder(); + super.fields = $v.fields.toBuilder(); + super.name = $v.name; + _$v = null; + } + return this; + } + + @override + void replace(Class other) { + ArgumentError.checkNotNull(other, 'other'); + _$v = other as _$Class; + } + + @override + void update(void Function(ClassBuilder)? updates) { + if (updates != null) updates(this); + } + + @override + Class build() => _build(); + + _$Class _build() { + _$Class _$result; + try { + _$result = _$v ?? + new _$Class._( + abstract: BuiltValueNullFieldError.checkNotNull( + abstract, r'Class', 'abstract'), + sealed: BuiltValueNullFieldError.checkNotNull( + sealed, r'Class', 'sealed'), + mixin: BuiltValueNullFieldError.checkNotNull( + mixin, r'Class', 'mixin'), + modifier: modifier, + annotations: annotations.build(), + docs: docs.build(), + extend: extend, + implements: implements.build(), + mixins: mixins.build(), + types: types.build(), + constructors: constructors.build(), + methods: methods.build(), + fields: fields.build(), + name: BuiltValueNullFieldError.checkNotNull( + name, r'Class', 'name')); + } catch (_) { + late String _$failedField; + try { + _$failedField = 'annotations'; + annotations.build(); + _$failedField = 'docs'; + docs.build(); + + _$failedField = 'implements'; + implements.build(); + _$failedField = 'mixins'; + mixins.build(); + _$failedField = 'types'; + types.build(); + _$failedField = 'constructors'; + constructors.build(); + _$failedField = 'methods'; + methods.build(); + _$failedField = 'fields'; + fields.build(); + } catch (e) { + throw new BuiltValueNestedFieldError( + r'Class', _$failedField, e.toString()); + } + rethrow; + } + replace(_$result); + return _$result; + } +} + +// ignore_for_file: deprecated_member_use_from_same_package,type=lint diff --git a/pkgs/code_builder/lib/src/specs/code.dart b/pkgs/code_builder/lib/src/specs/code.dart new file mode 100644 index 000000000..521eb1fe0 --- /dev/null +++ b/pkgs/code_builder/lib/src/specs/code.dart @@ -0,0 +1,165 @@ +// 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. + +import 'package:built_collection/built_collection.dart'; +import 'package:built_value/built_value.dart'; +import 'package:meta/meta.dart'; + +import '../allocator.dart'; +import '../base.dart'; +import '../emitter.dart'; +import '../visitors.dart'; +import 'expression.dart'; +import 'reference.dart'; + +part 'code.g.dart'; + +/// Returns a scoped symbol to [Reference], with an import prefix if needed. +/// +/// This is short-hand for [Allocator.allocate] in most implementations. +typedef Allocate = String Function(Reference); + +/// Represents arbitrary Dart code (either expressions or statements). +/// +/// See the various constructors for details. +abstract class Code implements Spec { + /// Create a simple code body based on a static string. + const factory Code(String code) = StaticCode._; + + /// Create a code based that may use a provided [Allocator] for scoping: + /// + /// ```dart + /// // Emits `_i123.FooType()`, where `_i123` is the import prefix. + /// + /// Code.scope((a) { + /// return '${a.allocate(fooType)}()' + /// }); + /// ``` + const factory Code.scope( + String Function(Allocate) scope, + ) = ScopedCode._; + + @override + R accept(covariant CodeVisitor visitor, [R? context]); +} + +/// Represents blocks of statements of Dart code. +abstract class Block implements Built, Code, Spec { + factory Block([void Function(BlockBuilder) updates]) = _$Block; + + factory Block.of(Iterable statements) => + Block((b) => b..statements.addAll(statements)); + + Block._(); + + @override + R accept(covariant CodeVisitor visitor, [R? context]) => + visitor.visitBlock(this, context); + + BuiltList get statements; +} + +abstract class BlockBuilder implements Builder { + factory BlockBuilder() = _$BlockBuilder; + + BlockBuilder._(); + + /// Adds an [expression] to [statements]. + /// + /// **NOTE**: Not all expressions are _useful_ statements. + void addExpression(Expression expression) { + statements.add(expression.statement); + } + + ListBuilder statements = ListBuilder(); +} + +/// Knowledge of different types of blocks of code in Dart. +/// +/// **INTERNAL ONLY**. +abstract class CodeVisitor implements SpecVisitor { + T visitBlock(Block code, [T? context]); + + T visitStaticCode(StaticCode code, [T? context]); + + T visitScopedCode(ScopedCode code, [T? context]); +} + +/// Knowledge of how to write valid Dart code from [CodeVisitor]. +abstract mixin class CodeEmitter implements CodeVisitor { + @protected + Allocator get allocator; + + @override + StringSink visitBlock(Block block, [StringSink? output]) { + output ??= StringBuffer(); + return visitAll(block.statements, output, (statement) { + statement.accept(this, output); + }, '\n'); + } + + @override + StringSink visitStaticCode(StaticCode code, [StringSink? output]) { + output ??= StringBuffer(); + return output..write(code.code); + } + + @override + StringSink visitScopedCode(ScopedCode code, [StringSink? output]) { + output ??= StringBuffer(); + return output..write(code.code(allocator.allocate)); + } +} + +/// Represents a code block that requires lazy visiting. +class LazyCode implements Code { + final Spec Function(SpecVisitor) generate; + + const LazyCode._(this.generate); + + @override + R accept(CodeVisitor visitor, [R? context]) => + generate(visitor).accept(visitor, context); +} + +/// Returns a generic [Code] that is lazily generated when visited. +Code lazyCode(Code Function() generate) => _LazyCode(generate); + +class _LazyCode implements Code { + final Code Function() generate; + + const _LazyCode(this.generate); + + @override + R accept(CodeVisitor visitor, [R? context]) => + generate().accept(visitor, context); +} + +/// Represents a simple, literal code block to be inserted as-is. +class StaticCode implements Code { + final String code; + + const StaticCode._(this.code); + + @override + R accept(CodeVisitor visitor, [R? context]) => + visitor.visitStaticCode(this, context); + + @override + String toString() => code; +} + +/// Represents a code block that may require scoping. +class ScopedCode implements Code { + final String Function(Allocate) code; + + const ScopedCode._(this.code); + + @override + R accept(CodeVisitor visitor, [R? context]) => + visitor.visitScopedCode(this, context); + + @override + String toString() => code((ref) => ref.symbol!); +} diff --git a/pkgs/code_builder/lib/src/specs/code.g.dart b/pkgs/code_builder/lib/src/specs/code.g.dart new file mode 100644 index 000000000..7b5ba7825 --- /dev/null +++ b/pkgs/code_builder/lib/src/specs/code.g.dart @@ -0,0 +1,109 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'code.dart'; + +// ************************************************************************** +// BuiltValueGenerator +// ************************************************************************** + +class _$Block extends Block { + @override + final BuiltList statements; + + factory _$Block([void Function(BlockBuilder)? updates]) => + (new BlockBuilder()..update(updates)).build() as _$Block; + + _$Block._({required this.statements}) : super._() { + BuiltValueNullFieldError.checkNotNull(statements, r'Block', 'statements'); + } + + @override + Block rebuild(void Function(BlockBuilder) updates) => + (toBuilder()..update(updates)).build(); + + @override + _$BlockBuilder toBuilder() => new _$BlockBuilder()..replace(this); + + @override + bool operator ==(Object other) { + if (identical(other, this)) return true; + return other is Block && statements == other.statements; + } + + @override + int get hashCode { + var _$hash = 0; + _$hash = $jc(_$hash, statements.hashCode); + _$hash = $jf(_$hash); + return _$hash; + } + + @override + String toString() { + return (newBuiltValueToStringHelper(r'Block') + ..add('statements', statements)) + .toString(); + } +} + +class _$BlockBuilder extends BlockBuilder { + _$Block? _$v; + + @override + ListBuilder get statements { + _$this; + return super.statements; + } + + @override + set statements(ListBuilder statements) { + _$this; + super.statements = statements; + } + + _$BlockBuilder() : super._(); + + BlockBuilder get _$this { + final $v = _$v; + if ($v != null) { + super.statements = $v.statements.toBuilder(); + _$v = null; + } + return this; + } + + @override + void replace(Block other) { + ArgumentError.checkNotNull(other, 'other'); + _$v = other as _$Block; + } + + @override + void update(void Function(BlockBuilder)? updates) { + if (updates != null) updates(this); + } + + @override + Block build() => _build(); + + _$Block _build() { + _$Block _$result; + try { + _$result = _$v ?? new _$Block._(statements: statements.build()); + } catch (_) { + late String _$failedField; + try { + _$failedField = 'statements'; + statements.build(); + } catch (e) { + throw new BuiltValueNestedFieldError( + r'Block', _$failedField, e.toString()); + } + rethrow; + } + replace(_$result); + return _$result; + } +} + +// ignore_for_file: deprecated_member_use_from_same_package,type=lint diff --git a/pkgs/code_builder/lib/src/specs/constructor.dart b/pkgs/code_builder/lib/src/specs/constructor.dart new file mode 100644 index 000000000..1c98eb516 --- /dev/null +++ b/pkgs/code_builder/lib/src/specs/constructor.dart @@ -0,0 +1,106 @@ +// 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. + +import 'package:built_collection/built_collection.dart'; +import 'package:built_value/built_value.dart'; +import 'package:meta/meta.dart'; + +import '../mixins/annotations.dart'; +import '../mixins/dartdoc.dart'; +import 'code.dart'; +import 'expression.dart'; +import 'method.dart'; +import 'reference.dart'; + +part 'constructor.g.dart'; + +@immutable +abstract class Constructor extends Object + with HasAnnotations, HasDartDocs + implements Built { + factory Constructor([void Function(ConstructorBuilder) updates]) = + _$Constructor; + + Constructor._(); + + @override + BuiltList get annotations; + + @override + BuiltList get docs; + + /// Optional parameters. + BuiltList get optionalParameters; + + /// Required parameters. + BuiltList get requiredParameters; + + /// Constructor initializer statements. + BuiltList get initializers; + + /// Body of the method. + Code? get body; + + /// Whether the constructor should be prefixed with `external`. + bool get external; + + /// Whether the constructor should be prefixed with `const`. + bool get constant; + + /// Whether this constructor should be prefixed with `factory`. + bool get factory; + + /// Whether this constructor is a simple lambda expression. + bool? get lambda; + + /// Name of the constructor - optional. + String? get name; + + /// If non-null, redirect to this constructor. + Reference? get redirect; +} + +abstract class ConstructorBuilder extends Object + with HasAnnotationsBuilder, HasDartDocsBuilder + implements Builder { + factory ConstructorBuilder() = _$ConstructorBuilder; + + ConstructorBuilder._(); + + @override + ListBuilder annotations = ListBuilder(); + + @override + ListBuilder docs = ListBuilder(); + + /// Optional parameters. + ListBuilder optionalParameters = ListBuilder(); + + /// Required parameters. + ListBuilder requiredParameters = ListBuilder(); + + /// Constructor initializer statements. + ListBuilder initializers = ListBuilder(); + + /// Body of the constructor. + Code? body; + + /// Whether the constructor should be prefixed with `const`. + bool constant = false; + + /// Whether the constructor should be prefixed with `external`. + bool external = false; + + /// Whether this constructor should be prefixed with `factory`. + bool factory = false; + + /// Whether this constructor is a simple lambda expression. + bool? lambda; + + /// Name of the constructor - optional. + String? name; + + /// If non-null, redirect to this constructor. + Reference? redirect; +} diff --git a/pkgs/code_builder/lib/src/specs/constructor.g.dart b/pkgs/code_builder/lib/src/specs/constructor.g.dart new file mode 100644 index 000000000..3f0693272 --- /dev/null +++ b/pkgs/code_builder/lib/src/specs/constructor.g.dart @@ -0,0 +1,356 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'constructor.dart'; + +// ************************************************************************** +// BuiltValueGenerator +// ************************************************************************** + +class _$Constructor extends Constructor { + @override + final BuiltList annotations; + @override + final BuiltList docs; + @override + final BuiltList optionalParameters; + @override + final BuiltList requiredParameters; + @override + final BuiltList initializers; + @override + final Code? body; + @override + final bool external; + @override + final bool constant; + @override + final bool factory; + @override + final bool? lambda; + @override + final String? name; + @override + final Reference? redirect; + + factory _$Constructor([void Function(ConstructorBuilder)? updates]) => + (new ConstructorBuilder()..update(updates)).build() as _$Constructor; + + _$Constructor._( + {required this.annotations, + required this.docs, + required this.optionalParameters, + required this.requiredParameters, + required this.initializers, + this.body, + required this.external, + required this.constant, + required this.factory, + this.lambda, + this.name, + this.redirect}) + : super._() { + BuiltValueNullFieldError.checkNotNull( + annotations, r'Constructor', 'annotations'); + BuiltValueNullFieldError.checkNotNull(docs, r'Constructor', 'docs'); + BuiltValueNullFieldError.checkNotNull( + optionalParameters, r'Constructor', 'optionalParameters'); + BuiltValueNullFieldError.checkNotNull( + requiredParameters, r'Constructor', 'requiredParameters'); + BuiltValueNullFieldError.checkNotNull( + initializers, r'Constructor', 'initializers'); + BuiltValueNullFieldError.checkNotNull(external, r'Constructor', 'external'); + BuiltValueNullFieldError.checkNotNull(constant, r'Constructor', 'constant'); + BuiltValueNullFieldError.checkNotNull(factory, r'Constructor', 'factory'); + } + + @override + Constructor rebuild(void Function(ConstructorBuilder) updates) => + (toBuilder()..update(updates)).build(); + + @override + _$ConstructorBuilder toBuilder() => new _$ConstructorBuilder()..replace(this); + + @override + bool operator ==(Object other) { + if (identical(other, this)) return true; + return other is Constructor && + annotations == other.annotations && + docs == other.docs && + optionalParameters == other.optionalParameters && + requiredParameters == other.requiredParameters && + initializers == other.initializers && + body == other.body && + external == other.external && + constant == other.constant && + factory == other.factory && + lambda == other.lambda && + name == other.name && + redirect == other.redirect; + } + + @override + int get hashCode { + var _$hash = 0; + _$hash = $jc(_$hash, annotations.hashCode); + _$hash = $jc(_$hash, docs.hashCode); + _$hash = $jc(_$hash, optionalParameters.hashCode); + _$hash = $jc(_$hash, requiredParameters.hashCode); + _$hash = $jc(_$hash, initializers.hashCode); + _$hash = $jc(_$hash, body.hashCode); + _$hash = $jc(_$hash, external.hashCode); + _$hash = $jc(_$hash, constant.hashCode); + _$hash = $jc(_$hash, factory.hashCode); + _$hash = $jc(_$hash, lambda.hashCode); + _$hash = $jc(_$hash, name.hashCode); + _$hash = $jc(_$hash, redirect.hashCode); + _$hash = $jf(_$hash); + return _$hash; + } + + @override + String toString() { + return (newBuiltValueToStringHelper(r'Constructor') + ..add('annotations', annotations) + ..add('docs', docs) + ..add('optionalParameters', optionalParameters) + ..add('requiredParameters', requiredParameters) + ..add('initializers', initializers) + ..add('body', body) + ..add('external', external) + ..add('constant', constant) + ..add('factory', factory) + ..add('lambda', lambda) + ..add('name', name) + ..add('redirect', redirect)) + .toString(); + } +} + +class _$ConstructorBuilder extends ConstructorBuilder { + _$Constructor? _$v; + + @override + ListBuilder get annotations { + _$this; + return super.annotations; + } + + @override + set annotations(ListBuilder annotations) { + _$this; + super.annotations = annotations; + } + + @override + ListBuilder get docs { + _$this; + return super.docs; + } + + @override + set docs(ListBuilder docs) { + _$this; + super.docs = docs; + } + + @override + ListBuilder get optionalParameters { + _$this; + return super.optionalParameters; + } + + @override + set optionalParameters(ListBuilder optionalParameters) { + _$this; + super.optionalParameters = optionalParameters; + } + + @override + ListBuilder get requiredParameters { + _$this; + return super.requiredParameters; + } + + @override + set requiredParameters(ListBuilder requiredParameters) { + _$this; + super.requiredParameters = requiredParameters; + } + + @override + ListBuilder get initializers { + _$this; + return super.initializers; + } + + @override + set initializers(ListBuilder initializers) { + _$this; + super.initializers = initializers; + } + + @override + Code? get body { + _$this; + return super.body; + } + + @override + set body(Code? body) { + _$this; + super.body = body; + } + + @override + bool get external { + _$this; + return super.external; + } + + @override + set external(bool external) { + _$this; + super.external = external; + } + + @override + bool get constant { + _$this; + return super.constant; + } + + @override + set constant(bool constant) { + _$this; + super.constant = constant; + } + + @override + bool get factory { + _$this; + return super.factory; + } + + @override + set factory(bool factory) { + _$this; + super.factory = factory; + } + + @override + bool? get lambda { + _$this; + return super.lambda; + } + + @override + set lambda(bool? lambda) { + _$this; + super.lambda = lambda; + } + + @override + String? get name { + _$this; + return super.name; + } + + @override + set name(String? name) { + _$this; + super.name = name; + } + + @override + Reference? get redirect { + _$this; + return super.redirect; + } + + @override + set redirect(Reference? redirect) { + _$this; + super.redirect = redirect; + } + + _$ConstructorBuilder() : super._(); + + ConstructorBuilder get _$this { + final $v = _$v; + if ($v != null) { + super.annotations = $v.annotations.toBuilder(); + super.docs = $v.docs.toBuilder(); + super.optionalParameters = $v.optionalParameters.toBuilder(); + super.requiredParameters = $v.requiredParameters.toBuilder(); + super.initializers = $v.initializers.toBuilder(); + super.body = $v.body; + super.external = $v.external; + super.constant = $v.constant; + super.factory = $v.factory; + super.lambda = $v.lambda; + super.name = $v.name; + super.redirect = $v.redirect; + _$v = null; + } + return this; + } + + @override + void replace(Constructor other) { + ArgumentError.checkNotNull(other, 'other'); + _$v = other as _$Constructor; + } + + @override + void update(void Function(ConstructorBuilder)? updates) { + if (updates != null) updates(this); + } + + @override + Constructor build() => _build(); + + _$Constructor _build() { + _$Constructor _$result; + try { + _$result = _$v ?? + new _$Constructor._( + annotations: annotations.build(), + docs: docs.build(), + optionalParameters: optionalParameters.build(), + requiredParameters: requiredParameters.build(), + initializers: initializers.build(), + body: body, + external: BuiltValueNullFieldError.checkNotNull( + external, r'Constructor', 'external'), + constant: BuiltValueNullFieldError.checkNotNull( + constant, r'Constructor', 'constant'), + factory: BuiltValueNullFieldError.checkNotNull( + factory, r'Constructor', 'factory'), + lambda: lambda, + name: name, + redirect: redirect); + } catch (_) { + late String _$failedField; + try { + _$failedField = 'annotations'; + annotations.build(); + _$failedField = 'docs'; + docs.build(); + _$failedField = 'optionalParameters'; + optionalParameters.build(); + _$failedField = 'requiredParameters'; + requiredParameters.build(); + _$failedField = 'initializers'; + initializers.build(); + } catch (e) { + throw new BuiltValueNestedFieldError( + r'Constructor', _$failedField, e.toString()); + } + rethrow; + } + replace(_$result); + return _$result; + } +} + +// ignore_for_file: deprecated_member_use_from_same_package,type=lint diff --git a/pkgs/code_builder/lib/src/specs/directive.dart b/pkgs/code_builder/lib/src/specs/directive.dart new file mode 100644 index 000000000..e10c0ea2a --- /dev/null +++ b/pkgs/code_builder/lib/src/specs/directive.dart @@ -0,0 +1,158 @@ +// 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. + +import 'package:built_value/built_value.dart'; +import 'package:collection/collection.dart'; +import 'package:meta/meta.dart'; + +import '../base.dart'; +import '../visitors.dart'; + +part 'directive.g.dart'; + +@immutable +abstract class Directive + implements Built, Spec, Comparable { + factory Directive([void Function(DirectiveBuilder) updates]) = _$Directive; + + factory Directive.import( + String url, { + String? as, + List show = const [], + List hide = const [], + }) => + Directive((builder) => builder + ..as = as + ..type = DirectiveType.import + ..url = url + ..show.addAll(show) + ..hide.addAll(hide)); + + factory Directive.importDeferredAs( + String url, + String as, { + List show = const [], + List hide = const [], + }) => + Directive((builder) => builder + ..as = as + ..type = DirectiveType.import + ..url = url + ..deferred = true + ..show.addAll(show) + ..hide.addAll(hide)); + + factory Directive.export( + String url, { + List show = const [], + List hide = const [], + }) => + Directive((builder) => builder + ..type = DirectiveType.export + ..url = url + ..show.addAll(show) + ..hide.addAll(hide)); + + factory Directive.part(String url) => Directive((builder) => builder + ..type = DirectiveType.part + ..url = url); + + factory Directive.partOf(String url) => Directive((builder) => builder + ..type = DirectiveType.partOf + ..url = url); + + Directive._(); + + String? get as; + + String get url; + + DirectiveType get type; + + List get show; + + List get hide; + + bool get deferred; + + @override + R accept( + SpecVisitor visitor, [ + R? context, + ]) => + visitor.visitDirective(this, context); + + @override + int compareTo(Directive other) => _compareDirectives(this, other); +} + +abstract class DirectiveBuilder + implements Builder { + factory DirectiveBuilder() = _$DirectiveBuilder; + + DirectiveBuilder._(); + + bool deferred = false; + + String? as; + + String? url; + + List show = []; + + List hide = []; + + DirectiveType? type; +} + +enum DirectiveType { + import, + export, + part, + partOf, +} + +/// Sort import URIs represented by [a] and [b] to honor the +/// "Effective Dart" ordering rules which are enforced by the +/// `directives_ordering` lint. +/// +/// 1. `import`s before `export`s +/// 2. `dart:` +/// 3. `package:` +/// 4. relative +/// 5. `part`s +int _compareDirectives(Directive a, Directive b) { + // NOTE: using the fact that `import` is before `export` in the + // `DirectiveType` enum – which allows us to compare using `indexOf`. + var value = DirectiveType.values + .indexOf(a.type) + .compareTo(DirectiveType.values.indexOf(b.type)); + + if (value == 0) { + final uriA = Uri.parse(a.url); + final uriB = Uri.parse(b.url); + + if (uriA.hasScheme) { + if (uriB.hasScheme) { + // If both import URIs have schemes, compare them based on scheme + // `dart` will sort before `package` which is what we want + // schemes are case-insensitive, so compare accordingly + value = compareAsciiLowerCase(uriA.scheme, uriB.scheme); + } else { + value = -1; + } + } else if (uriB.hasScheme) { + value = 1; + } + + // If both schemes are the same, compare based on path + if (value == 0) { + value = compareAsciiLowerCase(uriA.path, uriB.path); + } + + assert((value == 0) == (a.url == b.url)); + } + + return value; +} diff --git a/pkgs/code_builder/lib/src/specs/directive.g.dart b/pkgs/code_builder/lib/src/specs/directive.g.dart new file mode 100644 index 000000000..b28158e07 --- /dev/null +++ b/pkgs/code_builder/lib/src/specs/directive.g.dart @@ -0,0 +1,210 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'directive.dart'; + +// ************************************************************************** +// BuiltValueGenerator +// ************************************************************************** + +class _$Directive extends Directive { + @override + final String? as; + @override + final String url; + @override + final DirectiveType type; + @override + final List show; + @override + final List hide; + @override + final bool deferred; + + factory _$Directive([void Function(DirectiveBuilder)? updates]) => + (new DirectiveBuilder()..update(updates)).build() as _$Directive; + + _$Directive._( + {this.as, + required this.url, + required this.type, + required this.show, + required this.hide, + required this.deferred}) + : super._() { + BuiltValueNullFieldError.checkNotNull(url, r'Directive', 'url'); + BuiltValueNullFieldError.checkNotNull(type, r'Directive', 'type'); + BuiltValueNullFieldError.checkNotNull(show, r'Directive', 'show'); + BuiltValueNullFieldError.checkNotNull(hide, r'Directive', 'hide'); + BuiltValueNullFieldError.checkNotNull(deferred, r'Directive', 'deferred'); + } + + @override + Directive rebuild(void Function(DirectiveBuilder) updates) => + (toBuilder()..update(updates)).build(); + + @override + _$DirectiveBuilder toBuilder() => new _$DirectiveBuilder()..replace(this); + + @override + bool operator ==(Object other) { + if (identical(other, this)) return true; + return other is Directive && + as == other.as && + url == other.url && + type == other.type && + show == other.show && + hide == other.hide && + deferred == other.deferred; + } + + @override + int get hashCode { + var _$hash = 0; + _$hash = $jc(_$hash, as.hashCode); + _$hash = $jc(_$hash, url.hashCode); + _$hash = $jc(_$hash, type.hashCode); + _$hash = $jc(_$hash, show.hashCode); + _$hash = $jc(_$hash, hide.hashCode); + _$hash = $jc(_$hash, deferred.hashCode); + _$hash = $jf(_$hash); + return _$hash; + } + + @override + String toString() { + return (newBuiltValueToStringHelper(r'Directive') + ..add('as', as) + ..add('url', url) + ..add('type', type) + ..add('show', show) + ..add('hide', hide) + ..add('deferred', deferred)) + .toString(); + } +} + +class _$DirectiveBuilder extends DirectiveBuilder { + _$Directive? _$v; + + @override + String? get as { + _$this; + return super.as; + } + + @override + set as(String? as) { + _$this; + super.as = as; + } + + @override + String? get url { + _$this; + return super.url; + } + + @override + set url(String? url) { + _$this; + super.url = url; + } + + @override + DirectiveType? get type { + _$this; + return super.type; + } + + @override + set type(DirectiveType? type) { + _$this; + super.type = type; + } + + @override + List get show { + _$this; + return super.show; + } + + @override + set show(List show) { + _$this; + super.show = show; + } + + @override + List get hide { + _$this; + return super.hide; + } + + @override + set hide(List hide) { + _$this; + super.hide = hide; + } + + @override + bool get deferred { + _$this; + return super.deferred; + } + + @override + set deferred(bool deferred) { + _$this; + super.deferred = deferred; + } + + _$DirectiveBuilder() : super._(); + + DirectiveBuilder get _$this { + final $v = _$v; + if ($v != null) { + super.as = $v.as; + super.url = $v.url; + super.type = $v.type; + super.show = $v.show; + super.hide = $v.hide; + super.deferred = $v.deferred; + _$v = null; + } + return this; + } + + @override + void replace(Directive other) { + ArgumentError.checkNotNull(other, 'other'); + _$v = other as _$Directive; + } + + @override + void update(void Function(DirectiveBuilder)? updates) { + if (updates != null) updates(this); + } + + @override + Directive build() => _build(); + + _$Directive _build() { + final _$result = _$v ?? + new _$Directive._( + as: as, + url: + BuiltValueNullFieldError.checkNotNull(url, r'Directive', 'url'), + type: BuiltValueNullFieldError.checkNotNull( + type, r'Directive', 'type'), + show: BuiltValueNullFieldError.checkNotNull( + show, r'Directive', 'show'), + hide: BuiltValueNullFieldError.checkNotNull( + hide, r'Directive', 'hide'), + deferred: BuiltValueNullFieldError.checkNotNull( + deferred, r'Directive', 'deferred')); + replace(_$result); + return _$result; + } +} + +// ignore_for_file: deprecated_member_use_from_same_package,type=lint diff --git a/pkgs/code_builder/lib/src/specs/enum.dart b/pkgs/code_builder/lib/src/specs/enum.dart new file mode 100644 index 000000000..c58f92cb0 --- /dev/null +++ b/pkgs/code_builder/lib/src/specs/enum.dart @@ -0,0 +1,139 @@ +// Copyright (c) 2020, 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. + +import 'package:built_collection/built_collection.dart'; +import 'package:built_value/built_value.dart'; +import 'package:meta/meta.dart'; + +import '../../code_builder.dart'; +import '../mixins/annotations.dart'; +import '../mixins/dartdoc.dart'; +import '../mixins/generics.dart'; +import '../visitors.dart'; + +part 'enum.g.dart'; + +@immutable +abstract class Enum extends Object + with HasAnnotations, HasDartDocs, HasGenerics + implements Built, Spec { + factory Enum([void Function(EnumBuilder) updates]) = _$Enum; + + Enum._(); + + String get name; + + BuiltList get values; + + @override + BuiltList get annotations; + + @override + BuiltList get docs; + + BuiltList get implements; + + BuiltList get mixins; + + @override + BuiltList get types; + + BuiltList get constructors; + BuiltList get methods; + BuiltList get fields; + + @override + R accept( + SpecVisitor visitor, [ + R? context, + ]) => + visitor.visitEnum(this, context); +} + +abstract class EnumBuilder extends Object + with HasAnnotationsBuilder, HasDartDocsBuilder, HasGenericsBuilder + implements Builder { + factory EnumBuilder() = _$EnumBuilder; + + EnumBuilder._(); + + String? name; + + ListBuilder values = ListBuilder(); + + @override + ListBuilder annotations = ListBuilder(); + + @override + ListBuilder docs = ListBuilder(); + + ListBuilder implements = ListBuilder(); + ListBuilder mixins = ListBuilder(); + + @override + ListBuilder types = ListBuilder(); + + ListBuilder constructors = ListBuilder(); + ListBuilder methods = ListBuilder(); + ListBuilder fields = ListBuilder(); +} + +@immutable +abstract class EnumValue extends Object + with HasAnnotations, HasDartDocs, HasGenerics + implements Built { + factory EnumValue([void Function(EnumValueBuilder) updates]) = _$EnumValue; + + EnumValue._(); + + String get name; + + @override + BuiltList get annotations; + + @override + BuiltList get docs; + + /// The name of the constructor to target. + /// + /// If `null` uses the unnamed constructor. + String? get constructorName; + + @override + BuiltList get types; + + /// Arguments to the constructor. + BuiltList get arguments; + + /// Named arguments to the constructor. + BuiltMap get namedArguments; +} + +abstract class EnumValueBuilder extends Object + with HasAnnotationsBuilder, HasDartDocsBuilder, HasGenericsBuilder + implements Builder { + factory EnumValueBuilder() = _$EnumValueBuilder; + + EnumValueBuilder._(); + + String? name; + + @override + ListBuilder annotations = ListBuilder(); + + @override + ListBuilder docs = ListBuilder(); + + /// The name of the constructor to target. + String? constructorName; + + @override + ListBuilder types = ListBuilder(); + + /// Arguments to the constructor. + ListBuilder arguments = ListBuilder(); + + /// Named arguments to the constructor. + MapBuilder namedArguments = MapBuilder(); +} diff --git a/pkgs/code_builder/lib/src/specs/enum.g.dart b/pkgs/code_builder/lib/src/specs/enum.g.dart new file mode 100644 index 000000000..651cc610a --- /dev/null +++ b/pkgs/code_builder/lib/src/specs/enum.g.dart @@ -0,0 +1,563 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'enum.dart'; + +// ************************************************************************** +// BuiltValueGenerator +// ************************************************************************** + +class _$Enum extends Enum { + @override + final String name; + @override + final BuiltList values; + @override + final BuiltList annotations; + @override + final BuiltList docs; + @override + final BuiltList implements; + @override + final BuiltList mixins; + @override + final BuiltList types; + @override + final BuiltList constructors; + @override + final BuiltList methods; + @override + final BuiltList fields; + + factory _$Enum([void Function(EnumBuilder)? updates]) => + (new EnumBuilder()..update(updates)).build() as _$Enum; + + _$Enum._( + {required this.name, + required this.values, + required this.annotations, + required this.docs, + required this.implements, + required this.mixins, + required this.types, + required this.constructors, + required this.methods, + required this.fields}) + : super._() { + BuiltValueNullFieldError.checkNotNull(name, r'Enum', 'name'); + BuiltValueNullFieldError.checkNotNull(values, r'Enum', 'values'); + BuiltValueNullFieldError.checkNotNull(annotations, r'Enum', 'annotations'); + BuiltValueNullFieldError.checkNotNull(docs, r'Enum', 'docs'); + BuiltValueNullFieldError.checkNotNull(implements, r'Enum', 'implements'); + BuiltValueNullFieldError.checkNotNull(mixins, r'Enum', 'mixins'); + BuiltValueNullFieldError.checkNotNull(types, r'Enum', 'types'); + BuiltValueNullFieldError.checkNotNull( + constructors, r'Enum', 'constructors'); + BuiltValueNullFieldError.checkNotNull(methods, r'Enum', 'methods'); + BuiltValueNullFieldError.checkNotNull(fields, r'Enum', 'fields'); + } + + @override + Enum rebuild(void Function(EnumBuilder) updates) => + (toBuilder()..update(updates)).build(); + + @override + _$EnumBuilder toBuilder() => new _$EnumBuilder()..replace(this); + + @override + bool operator ==(Object other) { + if (identical(other, this)) return true; + return other is Enum && + name == other.name && + values == other.values && + annotations == other.annotations && + docs == other.docs && + implements == other.implements && + mixins == other.mixins && + types == other.types && + constructors == other.constructors && + methods == other.methods && + fields == other.fields; + } + + @override + int get hashCode { + var _$hash = 0; + _$hash = $jc(_$hash, name.hashCode); + _$hash = $jc(_$hash, values.hashCode); + _$hash = $jc(_$hash, annotations.hashCode); + _$hash = $jc(_$hash, docs.hashCode); + _$hash = $jc(_$hash, implements.hashCode); + _$hash = $jc(_$hash, mixins.hashCode); + _$hash = $jc(_$hash, types.hashCode); + _$hash = $jc(_$hash, constructors.hashCode); + _$hash = $jc(_$hash, methods.hashCode); + _$hash = $jc(_$hash, fields.hashCode); + _$hash = $jf(_$hash); + return _$hash; + } + + @override + String toString() { + return (newBuiltValueToStringHelper(r'Enum') + ..add('name', name) + ..add('values', values) + ..add('annotations', annotations) + ..add('docs', docs) + ..add('implements', implements) + ..add('mixins', mixins) + ..add('types', types) + ..add('constructors', constructors) + ..add('methods', methods) + ..add('fields', fields)) + .toString(); + } +} + +class _$EnumBuilder extends EnumBuilder { + _$Enum? _$v; + + @override + String? get name { + _$this; + return super.name; + } + + @override + set name(String? name) { + _$this; + super.name = name; + } + + @override + ListBuilder get values { + _$this; + return super.values; + } + + @override + set values(ListBuilder values) { + _$this; + super.values = values; + } + + @override + ListBuilder get annotations { + _$this; + return super.annotations; + } + + @override + set annotations(ListBuilder annotations) { + _$this; + super.annotations = annotations; + } + + @override + ListBuilder get docs { + _$this; + return super.docs; + } + + @override + set docs(ListBuilder docs) { + _$this; + super.docs = docs; + } + + @override + ListBuilder get implements { + _$this; + return super.implements; + } + + @override + set implements(ListBuilder implements) { + _$this; + super.implements = implements; + } + + @override + ListBuilder get mixins { + _$this; + return super.mixins; + } + + @override + set mixins(ListBuilder mixins) { + _$this; + super.mixins = mixins; + } + + @override + ListBuilder get types { + _$this; + return super.types; + } + + @override + set types(ListBuilder types) { + _$this; + super.types = types; + } + + @override + ListBuilder get constructors { + _$this; + return super.constructors; + } + + @override + set constructors(ListBuilder constructors) { + _$this; + super.constructors = constructors; + } + + @override + ListBuilder get methods { + _$this; + return super.methods; + } + + @override + set methods(ListBuilder methods) { + _$this; + super.methods = methods; + } + + @override + ListBuilder get fields { + _$this; + return super.fields; + } + + @override + set fields(ListBuilder fields) { + _$this; + super.fields = fields; + } + + _$EnumBuilder() : super._(); + + EnumBuilder get _$this { + final $v = _$v; + if ($v != null) { + super.name = $v.name; + super.values = $v.values.toBuilder(); + super.annotations = $v.annotations.toBuilder(); + super.docs = $v.docs.toBuilder(); + super.implements = $v.implements.toBuilder(); + super.mixins = $v.mixins.toBuilder(); + super.types = $v.types.toBuilder(); + super.constructors = $v.constructors.toBuilder(); + super.methods = $v.methods.toBuilder(); + super.fields = $v.fields.toBuilder(); + _$v = null; + } + return this; + } + + @override + void replace(Enum other) { + ArgumentError.checkNotNull(other, 'other'); + _$v = other as _$Enum; + } + + @override + void update(void Function(EnumBuilder)? updates) { + if (updates != null) updates(this); + } + + @override + Enum build() => _build(); + + _$Enum _build() { + _$Enum _$result; + try { + _$result = _$v ?? + new _$Enum._( + name: + BuiltValueNullFieldError.checkNotNull(name, r'Enum', 'name'), + values: values.build(), + annotations: annotations.build(), + docs: docs.build(), + implements: implements.build(), + mixins: mixins.build(), + types: types.build(), + constructors: constructors.build(), + methods: methods.build(), + fields: fields.build()); + } catch (_) { + late String _$failedField; + try { + _$failedField = 'values'; + values.build(); + _$failedField = 'annotations'; + annotations.build(); + _$failedField = 'docs'; + docs.build(); + _$failedField = 'implements'; + implements.build(); + _$failedField = 'mixins'; + mixins.build(); + _$failedField = 'types'; + types.build(); + _$failedField = 'constructors'; + constructors.build(); + _$failedField = 'methods'; + methods.build(); + _$failedField = 'fields'; + fields.build(); + } catch (e) { + throw new BuiltValueNestedFieldError( + r'Enum', _$failedField, e.toString()); + } + rethrow; + } + replace(_$result); + return _$result; + } +} + +class _$EnumValue extends EnumValue { + @override + final String name; + @override + final BuiltList annotations; + @override + final BuiltList docs; + @override + final String? constructorName; + @override + final BuiltList types; + @override + final BuiltList arguments; + @override + final BuiltMap namedArguments; + + factory _$EnumValue([void Function(EnumValueBuilder)? updates]) => + (new EnumValueBuilder()..update(updates)).build() as _$EnumValue; + + _$EnumValue._( + {required this.name, + required this.annotations, + required this.docs, + this.constructorName, + required this.types, + required this.arguments, + required this.namedArguments}) + : super._() { + BuiltValueNullFieldError.checkNotNull(name, r'EnumValue', 'name'); + BuiltValueNullFieldError.checkNotNull( + annotations, r'EnumValue', 'annotations'); + BuiltValueNullFieldError.checkNotNull(docs, r'EnumValue', 'docs'); + BuiltValueNullFieldError.checkNotNull(types, r'EnumValue', 'types'); + BuiltValueNullFieldError.checkNotNull(arguments, r'EnumValue', 'arguments'); + BuiltValueNullFieldError.checkNotNull( + namedArguments, r'EnumValue', 'namedArguments'); + } + + @override + EnumValue rebuild(void Function(EnumValueBuilder) updates) => + (toBuilder()..update(updates)).build(); + + @override + _$EnumValueBuilder toBuilder() => new _$EnumValueBuilder()..replace(this); + + @override + bool operator ==(Object other) { + if (identical(other, this)) return true; + return other is EnumValue && + name == other.name && + annotations == other.annotations && + docs == other.docs && + constructorName == other.constructorName && + types == other.types && + arguments == other.arguments && + namedArguments == other.namedArguments; + } + + @override + int get hashCode { + var _$hash = 0; + _$hash = $jc(_$hash, name.hashCode); + _$hash = $jc(_$hash, annotations.hashCode); + _$hash = $jc(_$hash, docs.hashCode); + _$hash = $jc(_$hash, constructorName.hashCode); + _$hash = $jc(_$hash, types.hashCode); + _$hash = $jc(_$hash, arguments.hashCode); + _$hash = $jc(_$hash, namedArguments.hashCode); + _$hash = $jf(_$hash); + return _$hash; + } + + @override + String toString() { + return (newBuiltValueToStringHelper(r'EnumValue') + ..add('name', name) + ..add('annotations', annotations) + ..add('docs', docs) + ..add('constructorName', constructorName) + ..add('types', types) + ..add('arguments', arguments) + ..add('namedArguments', namedArguments)) + .toString(); + } +} + +class _$EnumValueBuilder extends EnumValueBuilder { + _$EnumValue? _$v; + + @override + String? get name { + _$this; + return super.name; + } + + @override + set name(String? name) { + _$this; + super.name = name; + } + + @override + ListBuilder get annotations { + _$this; + return super.annotations; + } + + @override + set annotations(ListBuilder annotations) { + _$this; + super.annotations = annotations; + } + + @override + ListBuilder get docs { + _$this; + return super.docs; + } + + @override + set docs(ListBuilder docs) { + _$this; + super.docs = docs; + } + + @override + String? get constructorName { + _$this; + return super.constructorName; + } + + @override + set constructorName(String? constructorName) { + _$this; + super.constructorName = constructorName; + } + + @override + ListBuilder get types { + _$this; + return super.types; + } + + @override + set types(ListBuilder types) { + _$this; + super.types = types; + } + + @override + ListBuilder get arguments { + _$this; + return super.arguments; + } + + @override + set arguments(ListBuilder arguments) { + _$this; + super.arguments = arguments; + } + + @override + MapBuilder get namedArguments { + _$this; + return super.namedArguments; + } + + @override + set namedArguments(MapBuilder namedArguments) { + _$this; + super.namedArguments = namedArguments; + } + + _$EnumValueBuilder() : super._(); + + EnumValueBuilder get _$this { + final $v = _$v; + if ($v != null) { + super.name = $v.name; + super.annotations = $v.annotations.toBuilder(); + super.docs = $v.docs.toBuilder(); + super.constructorName = $v.constructorName; + super.types = $v.types.toBuilder(); + super.arguments = $v.arguments.toBuilder(); + super.namedArguments = $v.namedArguments.toBuilder(); + _$v = null; + } + return this; + } + + @override + void replace(EnumValue other) { + ArgumentError.checkNotNull(other, 'other'); + _$v = other as _$EnumValue; + } + + @override + void update(void Function(EnumValueBuilder)? updates) { + if (updates != null) updates(this); + } + + @override + EnumValue build() => _build(); + + _$EnumValue _build() { + _$EnumValue _$result; + try { + _$result = _$v ?? + new _$EnumValue._( + name: BuiltValueNullFieldError.checkNotNull( + name, r'EnumValue', 'name'), + annotations: annotations.build(), + docs: docs.build(), + constructorName: constructorName, + types: types.build(), + arguments: arguments.build(), + namedArguments: namedArguments.build()); + } catch (_) { + late String _$failedField; + try { + _$failedField = 'annotations'; + annotations.build(); + _$failedField = 'docs'; + docs.build(); + + _$failedField = 'types'; + types.build(); + _$failedField = 'arguments'; + arguments.build(); + _$failedField = 'namedArguments'; + namedArguments.build(); + } catch (e) { + throw new BuiltValueNestedFieldError( + r'EnumValue', _$failedField, e.toString()); + } + rethrow; + } + replace(_$result); + return _$result; + } +} + +// ignore_for_file: deprecated_member_use_from_same_package,type=lint diff --git a/pkgs/code_builder/lib/src/specs/expression.dart b/pkgs/code_builder/lib/src/specs/expression.dart new file mode 100644 index 000000000..b9193e6f3 --- /dev/null +++ b/pkgs/code_builder/lib/src/specs/expression.dart @@ -0,0 +1,793 @@ +// 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. + +import 'package:meta/meta.dart'; + +import '../base.dart'; +import '../emitter.dart'; +import '../visitors.dart'; +import 'code.dart'; +import 'method.dart'; +import 'reference.dart'; +import 'type_function.dart'; + +part 'expression/binary.dart'; +part 'expression/closure.dart'; +part 'expression/code.dart'; +part 'expression/invoke.dart'; +part 'expression/literal.dart'; +part 'expression/parenthesized.dart'; + +/// Represents a [code] block that wraps an [Expression]. + +/// Represents a Dart expression. +/// +/// See various concrete implementations for details. +abstract class Expression implements Spec { + const Expression(); + + /// An empty expression. + static const _empty = CodeExpression(Code('')); + + /// Whether this expression implies a const context for sub expressions. + /// + /// Collection literals that are const imply const for all values. + /// Assignment to a const variable implies a const value. + /// Invoking a const constructor implies const for all arguments. + /// + /// The implied const context is used to omit redundant `const` keywords. + /// A value of `false` does not imply that the expression cannot be used in a + /// const context. + bool get isConst => false; + + @override + R accept(covariant ExpressionVisitor visitor, [R? context]); + + /// The expression as a valid [Code] block. + /// + /// Also see [statement]. + Code get code => ToCodeExpression(this); + + /// The expression as a valid [Code] block with a trailing `;`. + Code get statement => ToCodeExpression(this, true); + + /// Returns the result of `this` `&&` [other]. + Expression and(Expression other) => + BinaryExpression._(expression, other, '&&'); + + /// Returns the result of `this` `||` [other]. + Expression or(Expression other) => + BinaryExpression._(expression, other, '||'); + + /// Returns the result of `!this`. + Expression negate() => + BinaryExpression._(_empty, expression, '!', addSpace: false); + + /// Returns the result of `this` `as` [other]. + Expression asA(Expression other) => + ParenthesizedExpression._(BinaryExpression._( + expression, + other, + 'as', + )); + + /// Returns accessing the index operator (`[]`) on `this`. + Expression index(Expression index) => BinaryExpression._( + expression, + CodeExpression(Block.of([ + const Code('['), + index.code, + const Code(']'), + ])), + '', + ); + + /// Returns the result of `this` `is` [other]. + Expression isA(Expression other) => BinaryExpression._( + expression, + other, + 'is', + ); + + /// Returns the result of `this` `is!` [other]. + Expression isNotA(Expression other) => BinaryExpression._( + expression, + other, + 'is!', + ); + + /// Returns the result of `this` `==` [other]. + Expression equalTo(Expression other) => BinaryExpression._( + expression, + other, + '==', + ); + + /// Returns the result of `this` `!=` [other]. + Expression notEqualTo(Expression other) => BinaryExpression._( + expression, + other, + '!=', + ); + + /// Returns the result of `this` `>` [other]. + Expression greaterThan(Expression other) => BinaryExpression._( + expression, + other, + '>', + ); + + /// Returns the result of `this` `<` [other]. + Expression lessThan(Expression other) => BinaryExpression._( + expression, + other, + '<', + ); + + /// Returns the result of `this` `>=` [other]. + Expression greaterOrEqualTo(Expression other) => BinaryExpression._( + expression, + other, + '>=', + ); + + /// Returns the result of `this` `<=` [other]. + Expression lessOrEqualTo(Expression other) => BinaryExpression._( + expression, + other, + '<=', + ); + + /// Returns the result of `this` `+` [other]. + Expression operatorAdd(Expression other) => BinaryExpression._( + expression, + other, + '+', + ); + + /// Returns the result of `this` `-` [other]. + Expression operatorSubtract(Expression other) => BinaryExpression._( + expression, + other, + '-', + ); + + @Deprecated('Use `operatorSubtract` instead') + Expression operatorSubstract(Expression other) => operatorSubtract(other); + + /// Returns the result of `this` `/` [other]. + Expression operatorDivide(Expression other) => BinaryExpression._( + expression, + other, + '/', + ); + + /// Returns the result of `this` `*` [other]. + Expression operatorMultiply(Expression other) => BinaryExpression._( + expression, + other, + '*', + ); + + /// Returns the result of `this` `%` [other]. + Expression operatorEuclideanModulo(Expression other) => BinaryExpression._( + expression, + other, + '%', + ); + + /// Returns the result of `this` `~/` [other]. + Expression operatorIntDivide(Expression other) => + BinaryExpression._(expression, other, '~/'); + + Expression conditional(Expression whenTrue, Expression whenFalse) => + BinaryExpression._( + expression, + BinaryExpression._(whenTrue, whenFalse, ':'), + '?', + ); + + /// This expression preceded by `await`. + Expression get awaited => BinaryExpression._( + _empty, + this, + 'await', + ); + + /// Returns the result of `++this`. + Expression operatorUnaryPrefixIncrement() => + BinaryExpression._(_empty, expression, '++', addSpace: false); + + /// Return the result of `this++`. + Expression operatorUnaryPostfixIncrement() => + BinaryExpression._(expression, _empty, '++', addSpace: false); + + /// Returns the result of `-this`. + Expression operatorUnaryMinus() => + BinaryExpression._(_empty, expression, '-', addSpace: false); + + /// Returns the result of `--this`. + Expression operatorUnaryPrefixDecrement() => + BinaryExpression._(_empty, expression, '--', addSpace: false); + + /// Return the result of `this--`. + Expression operatorUnaryPostfixDecrement() => + BinaryExpression._(expression, _empty, '--', addSpace: false); + + /// Returns the result of `this` `&` [other]. + Expression operatorBitwiseAnd(Expression other) => + BinaryExpression._(expression, other, '&'); + + /// Returns the result of `this` `|` [other]. + Expression operatorBitwiseOr(Expression other) => + BinaryExpression._(expression, other, '|'); + + /// Returns the result of `this` `^` [other]. + Expression operatorBitwiseXor(Expression other) => + BinaryExpression._(expression, other, '^'); + + /// Returns the result of `~this`. + Expression operatorUnaryBitwiseComplement() => + BinaryExpression._(_empty, expression, '~', addSpace: false); + + /// Returns the result of `this` `<<` [other]. + Expression operatorShiftLeft(Expression other) => + BinaryExpression._(expression, other, '<<'); + + /// Returns the result of `this` `>>` [other]. + Expression operatorShiftRight(Expression other) => + BinaryExpression._(expression, other, '>>'); + + /// Returns the result of `this` `>>>` [other]. + Expression operatorShiftRightUnsigned(Expression other) => + BinaryExpression._(expression, other, '>>>'); + + /// Return `{this} = {other}`. + Expression assign(Expression other) => + BinaryExpression._(this, other, '=', isConst: isConst); + + /// Return `this` += [other]. + Expression addAssign(Expression other) => + BinaryExpression._(this, other, '+='); + + /// Return `this` -= [other]. + Expression subtractAssign(Expression other) => + BinaryExpression._(this, other, '-='); + + /// Return `this` *= [other]. + Expression multiplyAssign(Expression other) => + BinaryExpression._(this, other, '*='); + + /// Return `this` /= [other]. + Expression divideAssign(Expression other) => + BinaryExpression._(this, other, '/='); + + /// Return `this` ~/= [other]. + Expression intDivideAssign(Expression other) => + BinaryExpression._(this, other, '~/='); + + /// Return `this` %= [other]. + Expression euclideanModuloAssign(Expression other) => + BinaryExpression._(this, other, '%='); + + /// Return `this` <<= [other]. + Expression shiftLeftAssign(Expression other) => + BinaryExpression._(this, other, '<<='); + + /// Return `this` >>= [other]. + Expression shiftRightAssign(Expression other) => + BinaryExpression._(this, other, '>>='); + + /// Return `this` >>>= [other]. + Expression shiftRightUnsignedAssign(Expression other) => + BinaryExpression._(this, other, '>>>='); + + /// Return `this` &= [other]. + Expression bitwiseAndAssign(Expression other) => + BinaryExpression._(this, other, '&='); + + /// Return `this` ^= [other]. + Expression bitwiseXorAssign(Expression other) => + BinaryExpression._(this, other, '^='); + + /// Return `this` |= [other]. + Expression bitwiseOrAssign(Expression other) => + BinaryExpression._(this, other, '|='); + + /// Return `{this} ?? {other}`. + Expression ifNullThen(Expression other) => BinaryExpression._( + this, + other, + '??', + ); + + /// Return `{this} ??= {other}`. + Expression assignNullAware(Expression other) => BinaryExpression._( + this, + other, + '??=', + ); + + /// Return `var {name} = {this}`. + @Deprecated('Use `declareVar(name).assign(expression)`') + Expression assignVar(String name, [Reference? type]) => BinaryExpression._( + type == null + ? LiteralExpression._('var $name') + : BinaryExpression._( + type.expression, + LiteralExpression._(name), + '', + ), + this, + '=', + ); + + /// Return `final {name} = {this}`. + @Deprecated('Use `declareFinal(name).assign(expression)`') + Expression assignFinal(String name, [Reference? type]) => BinaryExpression._( + type == null + ? const LiteralExpression._('final') + : BinaryExpression._( + const LiteralExpression._('final'), + type.expression, + '', + ), + this, + '$name =', + ); + + /// Return `const {name} = {this}`. + @Deprecated('Use `declareConst(name).assign(expression)`') + Expression assignConst(String name, [Reference? type]) => BinaryExpression._( + type == null + ? const LiteralExpression._('const') + : BinaryExpression._( + const LiteralExpression._('const'), + type.expression, + '', + ), + this, + '$name =', + isConst: true, + ); + + /// Call this expression as a method. + Expression call( + Iterable positionalArguments, [ + Map namedArguments = const {}, + List typeArguments = const [], + ]) => + InvokeExpression._( + this, + positionalArguments.toList(), + namedArguments, + typeArguments, + ); + + /// Returns an expression accessing `.` on this expression. + Expression property(String name) => BinaryExpression._( + this, + LiteralExpression._(name), + '.', + addSpace: false, + ); + + /// Returns an expression accessing `..` on this expression. + Expression cascade(String name) => BinaryExpression._( + this, + LiteralExpression._(name), + '..', + addSpace: false, + ); + + /// Returns an expression accessing `?.` on this expression. + Expression nullSafeProperty(String name) => BinaryExpression._( + this, + LiteralExpression._(name), + '?.', + addSpace: false, + ); + + /// Applies the null check operator on this expression, returning `this` `!`. + /// + /// Please note that this is only valid when emitting code with the null + /// safety syntax enabled. + Expression get nullChecked => BinaryExpression._( + this, + const LiteralExpression._('!'), + '', + addSpace: false, + ); + + /// This expression preceded by `return`. + Expression get returned => BinaryExpression._( + const LiteralExpression._('return'), + this, + '', + ); + + /// This expression preceded by the spread operator `...`. + Expression get spread => BinaryExpression._( + const LiteralExpression._('...'), + this, + '', + addSpace: false, + ); + + /// This expression preceded by the null safe spread operator `?...`. + Expression get nullSafeSpread => BinaryExpression._( + const LiteralExpression._('...?'), + this, + '', + addSpace: false, + ); + + /// This expression preceded by `throw`. + Expression get thrown => BinaryExpression._( + const LiteralExpression._('throw'), + this, + '', + ); + + /// May be overridden to support other types implementing [Expression]. + @visibleForOverriding + Expression get expression => this; + + /// Returns this expression wrapped in parenthesis. + ParenthesizedExpression get parenthesized => ParenthesizedExpression._(this); +} + +/// Declare a const variable named [variableName]. +/// +/// Returns `const {variableName}`, or `const {type} {variableName}`. +Expression declareConst(String variableName, {Reference? type}) => + BinaryExpression._( + const LiteralExpression._('const'), + type == null + ? LiteralExpression._(variableName) + : _typedVar(variableName, type), + '', + isConst: true); + +/// Declare a final variable named [variableName]. +/// +/// Returns `final {variableName}`, or `final {type} {variableName}`. +/// If [late] is true the declaration is prefixed with `late`. +Expression declareFinal(String variableName, + {Reference? type, bool late = false}) => + _late( + late, + type == null + ? LiteralExpression._('final $variableName') + : BinaryExpression._(const LiteralExpression._('final'), + _typedVar(variableName, type), '')); + +/// Declare a variable named [variableName]. +/// +/// Returns `var {variableName}`, or `{type} {variableName}`. +/// If [late] is true the declaration is prefixed with `late`. +Expression declareVar(String variableName, + {Reference? type, bool late = false}) => + _late( + late, + type == null + ? LiteralExpression._('var $variableName') + : _typedVar(variableName, type)); + +Expression _typedVar(String variableName, Reference type) => + BinaryExpression._(type.expression, LiteralExpression._(variableName), ''); + +Expression _late(bool late, Expression expression) => late + ? BinaryExpression._(const LiteralExpression._('late'), expression, '') + : expression; + +/// Creates `typedef {name} =`. +Code createTypeDef(String name, FunctionType type) => BinaryExpression._( + LiteralExpression._('typedef $name'), type.expression, '=') + .statement; + +class ToCodeExpression implements Code { + final Expression code; + + /// Whether this code should be considered a _statement_. + final bool isStatement; + + @visibleForTesting + const ToCodeExpression(this.code, [this.isStatement = false]); + + @override + R accept(CodeVisitor visitor, [R? context]) => + (visitor as ExpressionVisitor).visitToCodeExpression(this, context); + + @override + String toString() => code.toString(); +} + +/// Knowledge of different types of expressions in Dart. +/// +/// **INTERNAL ONLY**. +abstract class ExpressionVisitor implements SpecVisitor { + T visitToCodeExpression(ToCodeExpression code, [T? context]); + T visitBinaryExpression(BinaryExpression expression, [T? context]); + T visitClosureExpression(ClosureExpression expression, [T? context]); + T visitCodeExpression(CodeExpression expression, [T? context]); + T visitInvokeExpression(InvokeExpression expression, [T? context]); + T visitLiteralExpression(LiteralExpression expression, [T? context]); + T visitLiteralListExpression(LiteralListExpression expression, [T? context]); + T visitLiteralSetExpression(LiteralSetExpression expression, [T? context]); + T visitLiteralMapExpression(LiteralMapExpression expression, [T? context]); + T visitLiteralRecordExpression(LiteralRecordExpression expression, + [T? context]); + T visitParenthesizedExpression(ParenthesizedExpression expression, + [T? context]); +} + +/// Knowledge of how to write valid Dart code from [ExpressionVisitor]. +/// +/// **INTERNAL ONLY**. +abstract mixin class ExpressionEmitter + implements ExpressionVisitor { + @override + StringSink visitToCodeExpression(ToCodeExpression expression, + [StringSink? output]) { + output ??= StringBuffer(); + expression.code.accept(this, output); + if (expression.isStatement) { + output.write(';'); + } + return output; + } + + @override + StringSink visitBinaryExpression(BinaryExpression expression, + [StringSink? output]) { + output ??= StringBuffer(); + expression.left.accept(this, output); + if (expression.addSpace) { + output.write(' '); + } + output.write(expression.operator); + if (expression.addSpace) { + output.write(' '); + } + startConstCode(expression.isConst, () { + expression.right.accept(this, output); + }); + return output; + } + + @override + StringSink visitClosureExpression(ClosureExpression expression, + [StringSink? output]) { + output ??= StringBuffer(); + return expression.method.accept(this, output); + } + + @override + StringSink visitCodeExpression(CodeExpression expression, + [StringSink? output]) { + output ??= StringBuffer(); + final visitor = this as CodeVisitor; + return expression.code.accept(visitor, output); + } + + @override + StringSink visitInvokeExpression(InvokeExpression expression, + [StringSink? output]) { + final out = output ??= StringBuffer(); + return _writeConstExpression(out, expression.isConst, () { + expression.target.accept(this, out); + if (expression.name != null) { + out + ..write('.') + ..write(expression.name); + } + if (expression.typeArguments.isNotEmpty) { + out.write('<'); + visitAll(expression.typeArguments, out, (type) { + type.accept(this, out); + }); + out.write('>'); + } + out.write('('); + visitAll(expression.positionalArguments, out, (spec) { + spec.accept(this, out); + }); + if (expression.positionalArguments.isNotEmpty && + expression.namedArguments.isNotEmpty) { + out.write(', '); + } + visitAll(expression.namedArguments.keys, out, (name) { + out + ..write(name) + ..write(': '); + expression.namedArguments[name]!.accept(this, out); + }); + final argumentCount = expression.positionalArguments.length + + expression.namedArguments.length; + if (argumentCount > 1) { + out.write(', '); + } + return out..write(')'); + }); + } + + @override + StringSink visitLiteralExpression(LiteralExpression expression, + [StringSink? output]) { + output ??= StringBuffer(); + return output..write(expression.literal); + } + + void _acceptLiteral(Object? literalOrSpec, StringSink output) { + if (literalOrSpec is Spec) { + literalOrSpec.accept(this, output); + return; + } + literal(literalOrSpec).accept(this, output); + } + + bool _withInConstExpression = false; + + @override + StringSink visitLiteralListExpression( + LiteralListExpression expression, [ + StringSink? output, + ]) { + final out = output ??= StringBuffer(); + + return _writeConstExpression(output, expression.isConst, () { + if (expression.type != null) { + out.write('<'); + expression.type!.accept(this, output); + out.write('>'); + } + out.write('['); + visitAll(expression.values, out, (value) { + _acceptLiteral(value, out); + }); + if (expression.values.length > 1) { + out.write(', '); + } + return out..write(']'); + }); + } + + @override + StringSink visitLiteralSetExpression( + LiteralSetExpression expression, [ + StringSink? output, + ]) { + final out = output ??= StringBuffer(); + + return _writeConstExpression(output, expression.isConst, () { + if (expression.type != null) { + out.write('<'); + expression.type!.accept(this, output); + out.write('>'); + } + out.write('{'); + visitAll(expression.values, out, (value) { + _acceptLiteral(value, out); + }); + if (expression.values.length > 1) { + out.write(', '); + } + return out..write('}'); + }); + } + + @override + StringSink visitLiteralMapExpression( + LiteralMapExpression expression, [ + StringSink? output, + ]) { + final out = output ??= StringBuffer(); + return _writeConstExpression(out, expression.isConst, () { + if (expression.keyType != null) { + out.write('<'); + expression.keyType!.accept(this, out); + out.write(', '); + if (expression.valueType == null) { + const Reference('dynamic', 'dart:core').accept(this, out); + } else { + expression.valueType!.accept(this, out); + } + out.write('>'); + } + out.write('{'); + visitAll(expression.values.keys, out, (key) { + final value = expression.values[key]; + _acceptLiteral(key, out); + if (key is! LiteralSpreadExpression) { + out.write(': '); + } + _acceptLiteral(value, out); + }); + if (expression.values.length > 1) { + out.write(', '); + } + return out..write('}'); + }); + } + + @override + StringSink visitLiteralRecordExpression( + LiteralRecordExpression expression, [ + StringSink? output, + ]) { + final out = output ??= StringBuffer(); + return _writeConstExpression(out, expression.isConst, () { + out.write('('); + visitAll(expression.positionalFieldValues, out, (value) { + _acceptLiteral(value, out); + }); + if (expression.namedFieldValues.isNotEmpty) { + if (expression.positionalFieldValues.isNotEmpty) { + out.write(', '); + } + } else if (expression.positionalFieldValues.length == 1) { + out.write(','); + } + visitAll>( + expression.namedFieldValues.entries, out, (entry) { + out.write('${entry.key}: '); + _acceptLiteral(entry.value, out); + }); + return out..write(')'); + }); + } + + @override + StringSink visitParenthesizedExpression( + ParenthesizedExpression expression, [ + StringSink? output, + ]) { + output ??= StringBuffer(); + output.write('('); + expression.inner.accept(this, output); + output.write(')'); + return output; + } + + /// Executes [visit] within a context which may alter the output if [isConst] + /// is `true`. + /// + /// This allows constant expressions to omit the `const` keyword if they + /// are already within a constant expression. + void startConstCode( + bool isConst, + Null Function() visit, + ) { + final previousConstContext = _withInConstExpression; + if (isConst) { + _withInConstExpression = true; + } + + visit(); + _withInConstExpression = previousConstContext; + } + + /// Similar to [startConstCode], but handles writing `"const "` if [isConst] + /// is `true` and the invocation is not nested under other invocations where + /// [isConst] is true. + StringSink _writeConstExpression( + StringSink sink, + bool isConst, + StringSink Function() visitExpression, + ) { + final previousConstContext = _withInConstExpression; + if (isConst) { + if (!_withInConstExpression) { + sink.write('const '); + } + _withInConstExpression = true; + } + + final returnedSink = visitExpression(); + assert(identical(returnedSink, sink)); + _withInConstExpression = previousConstContext; + return sink; + } +} diff --git a/pkgs/code_builder/lib/src/specs/expression/binary.dart b/pkgs/code_builder/lib/src/specs/expression/binary.dart new file mode 100644 index 000000000..d02a2a6a9 --- /dev/null +++ b/pkgs/code_builder/lib/src/specs/expression/binary.dart @@ -0,0 +1,27 @@ +// 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. + +part of '../expression.dart'; + +/// Represents two expressions ([left] and [right]) and an [operator]. +class BinaryExpression extends Expression { + final Expression left; + final Expression right; + final String operator; + final bool addSpace; + @override + final bool isConst; + + const BinaryExpression._( + this.left, + this.right, + this.operator, { + this.addSpace = true, + this.isConst = false, + }); + + @override + R accept(ExpressionVisitor visitor, [R? context]) => + visitor.visitBinaryExpression(this, context); +} diff --git a/pkgs/code_builder/lib/src/specs/expression/closure.dart b/pkgs/code_builder/lib/src/specs/expression/closure.dart new file mode 100644 index 000000000..706600c14 --- /dev/null +++ b/pkgs/code_builder/lib/src/specs/expression/closure.dart @@ -0,0 +1,32 @@ +// 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. + +part of '../expression.dart'; + +/// Returns [method] as closure, removing its return type and type parameters. +Expression toClosure(Method method) { + final withoutTypes = method.rebuild((b) { + b.returns = null; + b.types.clear(); + }); + return ClosureExpression._(withoutTypes); +} + +/// Returns [method] as a (possibly) generic closure, removing its return type. +Expression toGenericClosure(Method method) { + final withoutReturnType = method.rebuild((b) { + b.returns = null; + }); + return ClosureExpression._(withoutReturnType); +} + +class ClosureExpression extends Expression { + final Method method; + + const ClosureExpression._(this.method); + + @override + R accept(ExpressionVisitor visitor, [R? context]) => + visitor.visitClosureExpression(this, context); +} diff --git a/pkgs/code_builder/lib/src/specs/expression/code.dart b/pkgs/code_builder/lib/src/specs/expression/code.dart new file mode 100644 index 000000000..1529edb78 --- /dev/null +++ b/pkgs/code_builder/lib/src/specs/expression/code.dart @@ -0,0 +1,18 @@ +// 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. + +part of '../expression.dart'; + +/// Represents a [Code] block as an [Expression]. +class CodeExpression extends Expression { + @override + final Code code; + + /// **INTERNAL ONLY**: Used to wrap [Code] as an [Expression]. + const CodeExpression(this.code); + + @override + R accept(ExpressionVisitor visitor, [R? context]) => + visitor.visitCodeExpression(this, context); +} diff --git a/pkgs/code_builder/lib/src/specs/expression/invoke.dart b/pkgs/code_builder/lib/src/specs/expression/invoke.dart new file mode 100644 index 000000000..f54dc0ecd --- /dev/null +++ b/pkgs/code_builder/lib/src/specs/expression/invoke.dart @@ -0,0 +1,65 @@ +// 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. + +// ignore_for_file: deprecated_member_use_from_same_package + +part of '../expression.dart'; + +/// Represents invoking [target] as a method with arguments. +class InvokeExpression extends Expression { + /// Target of the method invocation. + final Expression target; + + /// Optional; type of invocation. + @Deprecated('Use isConst instead') + final InvokeExpressionType? type; + + @override + final bool isConst; + + final List positionalArguments; + final Map namedArguments; + final List typeArguments; + final String? name; + + const InvokeExpression._( + this.target, + this.positionalArguments, + this.namedArguments, + this.typeArguments, + ) : name = null, + type = null, + isConst = false; + + const InvokeExpression.newOf( + this.target, + this.positionalArguments, [ + this.namedArguments = const {}, + this.typeArguments = const [], + this.name, + ]) : type = InvokeExpressionType.newInstance, + isConst = false; + + const InvokeExpression.constOf( + this.target, + this.positionalArguments, [ + this.namedArguments = const {}, + this.typeArguments = const [], + this.name, + ]) : type = InvokeExpressionType.constInstance, + isConst = true; + + @override + R accept(ExpressionVisitor visitor, [R? context]) => + visitor.visitInvokeExpression(this, context); + + @override + String toString() => + '${type ?? ''} $target($positionalArguments, $namedArguments)'; +} + +enum InvokeExpressionType { + newInstance, + constInstance, +} diff --git a/pkgs/code_builder/lib/src/specs/expression/literal.dart b/pkgs/code_builder/lib/src/specs/expression/literal.dart new file mode 100644 index 000000000..ced6cf90f --- /dev/null +++ b/pkgs/code_builder/lib/src/specs/expression/literal.dart @@ -0,0 +1,218 @@ +// 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. + +part of '../expression.dart'; + +/// Converts a runtime Dart [literal] value into an [Expression]. +/// +/// Supported Dart types are translated into literal expressions. +/// If the [literal] is already an [Expression] it is returned without change to +/// allow operating on a collection of mixed simple literals and more complex +/// expressions. +/// Unsupported inputs invoke the [onError] callback. +Expression literal(Object? literal, {Expression Function(Object)? onError}) { + if (literal is Expression) return literal; + if (literal is bool) return literalBool(literal); + if (literal is num) return literalNum(literal); + if (literal is String) return literalString(literal); + if (literal is List) return literalList(literal); + if (literal is Set) return literalSet(literal); + if (literal is Map) return literalMap(literal); + if (literal == null) return literalNull; + if (onError != null) return onError(literal); + throw UnsupportedError('Not a supported literal type: $literal.'); +} + +/// Represents the literal value `true`. +const Expression literalTrue = LiteralExpression._('true'); + +/// Represents the literal value `false`. +const Expression literalFalse = LiteralExpression._('false'); + +/// Create a literal expression from a boolean [value]. +Expression literalBool(bool value) => value ? literalTrue : literalFalse; + +/// Represents the literal value `null`. +const Expression literalNull = LiteralExpression._('null'); + +/// Create a literal expression from a number [value]. +Expression literalNum(num value) => LiteralExpression._('$value'); + +/// Create a literal expression from a string [value]. +/// +/// **NOTE**: The string is always formatted `''`. +/// +/// If [raw] is `true`, creates a raw String formatted `r''` and the +/// value may not contain a single quote. +/// Escapes single quotes and newlines in the value. +Expression literalString(String value, {bool raw = false}) { + if (raw && value.contains('\'')) { + throw ArgumentError('Cannot include a single quote in a raw string'); + } + final escaped = value.replaceAll('\'', '\\\'').replaceAll('\n', '\\n'); + return LiteralExpression._("${raw ? 'r' : ''}'$escaped'"); +} + +/// Create a literal `...` operator for use when creating a Map literal. +/// +/// *NOTE* This is used as a sentinel when constructing a `literalMap` or a +/// or `literalConstMap` to signify that the value should be spread. Do NOT +/// reuse the value when creating a Map with multiple spreads. +Expression literalSpread() => LiteralSpreadExpression._(false); + +/// Create a literal `...?` operator for use when creating a Map literal. +/// +/// *NOTE* This is used as a sentinel when constructing a `literalMap` or a +/// or `literalConstMap` to signify that the value should be spread. Do NOT +/// reuse the value when creating a Map with multiple spreads. +Expression literalNullSafeSpread() => LiteralSpreadExpression._(true); + +/// Creates a literal list expression from [values]. +LiteralListExpression literalList(Iterable values, + [Reference? type]) => + LiteralListExpression._(false, values.toList(), type); + +/// Creates a literal `const` list expression from [values]. +LiteralListExpression literalConstList(List values, + [Reference? type]) => + LiteralListExpression._(true, values, type); + +/// Creates a literal set expression from [values]. +LiteralSetExpression literalSet(Iterable values, [Reference? type]) => + LiteralSetExpression._(false, values.toSet(), type); + +/// Creates a literal `const` set expression from [values]. +LiteralSetExpression literalConstSet(Set values, [Reference? type]) => + LiteralSetExpression._(true, values, type); + +/// Create a literal map expression from [values]. +LiteralMapExpression literalMap( + Map values, [ + Reference? keyType, + Reference? valueType, +]) => + LiteralMapExpression._(false, values, keyType, valueType); + +/// Create a literal `const` map expression from [values]. +LiteralMapExpression literalConstMap( + Map values, [ + Reference? keyType, + Reference? valueType, +]) => + LiteralMapExpression._(true, values, keyType, valueType); + +/// Create a literal record expression from [positionalFieldValues] and +/// [namedFieldValues]. +LiteralRecordExpression literalRecord(List positionalFieldValues, + Map namedFieldValues) => + LiteralRecordExpression._(false, positionalFieldValues, namedFieldValues); + +/// Create a literal `const` record expression from [positionalFieldValues] and +/// [namedFieldValues]. +LiteralRecordExpression literalConstRecord(List positionalFieldValues, + Map namedFieldValues) => + LiteralRecordExpression._(true, positionalFieldValues, namedFieldValues); + +/// Represents a literal value in Dart source code. +/// +/// For example, `LiteralExpression('null')` should emit `null`. +/// +/// Some common literals and helpers are available as methods/fields: +/// * [literal] +/// * [literalBool] and [literalTrue], [literalFalse] +/// * [literalNull] +/// * [literalList] and [literalConstList] +/// * [literalSet] and [literalConstSet] +class LiteralExpression extends Expression { + final String literal; + + const LiteralExpression._(this.literal); + + @override + R accept(ExpressionVisitor visitor, [R? context]) => + visitor.visitLiteralExpression(this, context); + + @override + String toString() => literal; +} + +class LiteralSpreadExpression extends LiteralExpression { + LiteralSpreadExpression._(bool nullAware) + : super._('...${nullAware ? '?' : ''}'); +} + +class LiteralListExpression extends Expression { + @override + final bool isConst; + final List values; + final Reference? type; + + const LiteralListExpression._(this.isConst, this.values, this.type); + + @override + R accept(ExpressionVisitor visitor, [R? context]) => + visitor.visitLiteralListExpression(this, context); + + @override + String toString() => '[${values.map(literal).join(', ')}]'; +} + +class LiteralSetExpression extends Expression { + @override + final bool isConst; + final Set values; + final Reference? type; + + const LiteralSetExpression._(this.isConst, this.values, this.type); + + @override + R accept(ExpressionVisitor visitor, [R? context]) => + visitor.visitLiteralSetExpression(this, context); + + @override + String toString() => '{${values.map(literal).join(', ')}}'; +} + +class LiteralMapExpression extends Expression { + @override + final bool isConst; + final Map values; + final Reference? keyType; + final Reference? valueType; + + const LiteralMapExpression._( + this.isConst, + this.values, + this.keyType, + this.valueType, + ); + + @override + R accept(ExpressionVisitor visitor, [R? context]) => + visitor.visitLiteralMapExpression(this, context); + + @override + String toString() => '{$values}'; +} + +class LiteralRecordExpression extends Expression { + @override + final bool isConst; + final List positionalFieldValues; + final Map namedFieldValues; + + const LiteralRecordExpression._( + this.isConst, this.positionalFieldValues, this.namedFieldValues); + + @override + R accept(ExpressionVisitor visitor, [R? context]) => + visitor.visitLiteralRecordExpression(this, context); + + @override + String toString() { + final allFields = positionalFieldValues.map((v) => v.toString()).followedBy( + namedFieldValues.entries.map((e) => '${e.key}: ${e.value}')); + return '(${allFields.join(', ')})'; + } +} diff --git a/pkgs/code_builder/lib/src/specs/expression/parenthesized.dart b/pkgs/code_builder/lib/src/specs/expression/parenthesized.dart new file mode 100644 index 000000000..ea34ae82f --- /dev/null +++ b/pkgs/code_builder/lib/src/specs/expression/parenthesized.dart @@ -0,0 +1,16 @@ +// Copyright (c) 2021, 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. + +part of '../expression.dart'; + +/// An [Expression] wrapped with parenthesis. +class ParenthesizedExpression extends Expression { + final Expression inner; + + const ParenthesizedExpression._(this.inner); + + @override + R accept(ExpressionVisitor visitor, [R? context]) => + visitor.visitParenthesizedExpression(this, context); +} diff --git a/pkgs/code_builder/lib/src/specs/extension.dart b/pkgs/code_builder/lib/src/specs/extension.dart new file mode 100644 index 000000000..395994fc7 --- /dev/null +++ b/pkgs/code_builder/lib/src/specs/extension.dart @@ -0,0 +1,78 @@ +// Copyright (c) 2020, 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. + +import 'package:built_collection/built_collection.dart'; +import 'package:built_value/built_value.dart'; +import 'package:meta/meta.dart'; + +import '../base.dart'; +import '../mixins/annotations.dart'; +import '../mixins/dartdoc.dart'; +import '../mixins/generics.dart'; +import '../visitors.dart'; +import 'expression.dart'; +import 'field.dart'; +import 'method.dart'; +import 'reference.dart'; + +part 'extension.g.dart'; + +@immutable +abstract class Extension extends Object + with HasAnnotations, HasDartDocs, HasGenerics + implements Built, Spec { + factory Extension([void Function(ExtensionBuilder b) updates]) = _$Extension; + + Extension._(); + + @override + BuiltList get annotations; + + @override + BuiltList get docs; + + Reference? get on; + + @override + BuiltList get types; + + BuiltList get methods; + + BuiltList get fields; + + /// Name of the extension - optional. + String? get name; + + @override + R accept( + SpecVisitor visitor, [ + R? context, + ]) => + visitor.visitExtension(this, context); +} + +abstract class ExtensionBuilder extends Object + with HasAnnotationsBuilder, HasDartDocsBuilder, HasGenericsBuilder + implements Builder { + factory ExtensionBuilder() = _$ExtensionBuilder; + + ExtensionBuilder._(); + + @override + ListBuilder annotations = ListBuilder(); + + @override + ListBuilder docs = ListBuilder(); + + Reference? on; + + @override + ListBuilder types = ListBuilder(); + + ListBuilder methods = ListBuilder(); + ListBuilder fields = ListBuilder(); + + /// Name of the extension - optional. + String? name; +} diff --git a/pkgs/code_builder/lib/src/specs/extension.g.dart b/pkgs/code_builder/lib/src/specs/extension.g.dart new file mode 100644 index 000000000..83de17614 --- /dev/null +++ b/pkgs/code_builder/lib/src/specs/extension.g.dart @@ -0,0 +1,248 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'extension.dart'; + +// ************************************************************************** +// BuiltValueGenerator +// ************************************************************************** + +class _$Extension extends Extension { + @override + final BuiltList annotations; + @override + final BuiltList docs; + @override + final Reference? on; + @override + final BuiltList types; + @override + final BuiltList methods; + @override + final BuiltList fields; + @override + final String? name; + + factory _$Extension([void Function(ExtensionBuilder)? updates]) => + (new ExtensionBuilder()..update(updates)).build() as _$Extension; + + _$Extension._( + {required this.annotations, + required this.docs, + this.on, + required this.types, + required this.methods, + required this.fields, + this.name}) + : super._() { + BuiltValueNullFieldError.checkNotNull( + annotations, r'Extension', 'annotations'); + BuiltValueNullFieldError.checkNotNull(docs, r'Extension', 'docs'); + BuiltValueNullFieldError.checkNotNull(types, r'Extension', 'types'); + BuiltValueNullFieldError.checkNotNull(methods, r'Extension', 'methods'); + BuiltValueNullFieldError.checkNotNull(fields, r'Extension', 'fields'); + } + + @override + Extension rebuild(void Function(ExtensionBuilder) updates) => + (toBuilder()..update(updates)).build(); + + @override + _$ExtensionBuilder toBuilder() => new _$ExtensionBuilder()..replace(this); + + @override + bool operator ==(Object other) { + if (identical(other, this)) return true; + return other is Extension && + annotations == other.annotations && + docs == other.docs && + on == other.on && + types == other.types && + methods == other.methods && + fields == other.fields && + name == other.name; + } + + @override + int get hashCode { + var _$hash = 0; + _$hash = $jc(_$hash, annotations.hashCode); + _$hash = $jc(_$hash, docs.hashCode); + _$hash = $jc(_$hash, on.hashCode); + _$hash = $jc(_$hash, types.hashCode); + _$hash = $jc(_$hash, methods.hashCode); + _$hash = $jc(_$hash, fields.hashCode); + _$hash = $jc(_$hash, name.hashCode); + _$hash = $jf(_$hash); + return _$hash; + } + + @override + String toString() { + return (newBuiltValueToStringHelper(r'Extension') + ..add('annotations', annotations) + ..add('docs', docs) + ..add('on', on) + ..add('types', types) + ..add('methods', methods) + ..add('fields', fields) + ..add('name', name)) + .toString(); + } +} + +class _$ExtensionBuilder extends ExtensionBuilder { + _$Extension? _$v; + + @override + ListBuilder get annotations { + _$this; + return super.annotations; + } + + @override + set annotations(ListBuilder annotations) { + _$this; + super.annotations = annotations; + } + + @override + ListBuilder get docs { + _$this; + return super.docs; + } + + @override + set docs(ListBuilder docs) { + _$this; + super.docs = docs; + } + + @override + Reference? get on { + _$this; + return super.on; + } + + @override + set on(Reference? on) { + _$this; + super.on = on; + } + + @override + ListBuilder get types { + _$this; + return super.types; + } + + @override + set types(ListBuilder types) { + _$this; + super.types = types; + } + + @override + ListBuilder get methods { + _$this; + return super.methods; + } + + @override + set methods(ListBuilder methods) { + _$this; + super.methods = methods; + } + + @override + ListBuilder get fields { + _$this; + return super.fields; + } + + @override + set fields(ListBuilder fields) { + _$this; + super.fields = fields; + } + + @override + String? get name { + _$this; + return super.name; + } + + @override + set name(String? name) { + _$this; + super.name = name; + } + + _$ExtensionBuilder() : super._(); + + ExtensionBuilder get _$this { + final $v = _$v; + if ($v != null) { + super.annotations = $v.annotations.toBuilder(); + super.docs = $v.docs.toBuilder(); + super.on = $v.on; + super.types = $v.types.toBuilder(); + super.methods = $v.methods.toBuilder(); + super.fields = $v.fields.toBuilder(); + super.name = $v.name; + _$v = null; + } + return this; + } + + @override + void replace(Extension other) { + ArgumentError.checkNotNull(other, 'other'); + _$v = other as _$Extension; + } + + @override + void update(void Function(ExtensionBuilder)? updates) { + if (updates != null) updates(this); + } + + @override + Extension build() => _build(); + + _$Extension _build() { + _$Extension _$result; + try { + _$result = _$v ?? + new _$Extension._( + annotations: annotations.build(), + docs: docs.build(), + on: on, + types: types.build(), + methods: methods.build(), + fields: fields.build(), + name: name); + } catch (_) { + late String _$failedField; + try { + _$failedField = 'annotations'; + annotations.build(); + _$failedField = 'docs'; + docs.build(); + + _$failedField = 'types'; + types.build(); + _$failedField = 'methods'; + methods.build(); + _$failedField = 'fields'; + fields.build(); + } catch (e) { + throw new BuiltValueNestedFieldError( + r'Extension', _$failedField, e.toString()); + } + rethrow; + } + replace(_$result); + return _$result; + } +} + +// ignore_for_file: deprecated_member_use_from_same_package,type=lint diff --git a/pkgs/code_builder/lib/src/specs/extension_type.dart b/pkgs/code_builder/lib/src/specs/extension_type.dart new file mode 100644 index 000000000..ec3bd3355 --- /dev/null +++ b/pkgs/code_builder/lib/src/specs/extension_type.dart @@ -0,0 +1,152 @@ +// Copyright (c) 2023, 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. + +import 'package:built_collection/built_collection.dart'; +import 'package:built_value/built_value.dart'; +import 'package:meta/meta.dart'; + +import '../base.dart'; +import '../mixins/annotations.dart'; +import '../mixins/dartdoc.dart'; +import '../mixins/generics.dart'; +import '../visitors.dart'; +import 'constructor.dart'; +import 'expression.dart'; +import 'field.dart'; +import 'method.dart'; +import 'reference.dart'; + +part 'extension_type.g.dart'; + +@immutable +abstract class ExtensionType extends Object + with HasAnnotations, HasDartDocs, HasGenerics + implements Built, Spec { + factory ExtensionType([void Function(ExtensionTypeBuilder)? updates]) = + _$ExtensionType; + + ExtensionType._(); + + @override + BuiltList get annotations; + + @override + BuiltList get docs; + + /// Whether this extension type is declared as `const`. + bool get constant; + + String get name; + + @override + BuiltList get types; + + /// Name of the extension type's primary constructor. An empty string + /// will make it unnamed. + String get primaryConstructorName; + + RepresentationDeclaration get representationDeclaration; + + BuiltList get implements; + + BuiltList get constructors; + + BuiltList get fields; + + BuiltList get methods; + + @override + R accept( + SpecVisitor visitor, [ + R? context, + ]) => + visitor.visitExtensionType(this, context); +} + +abstract class ExtensionTypeBuilder extends Object + with HasAnnotationsBuilder, HasDartDocsBuilder, HasGenericsBuilder + implements Builder { + factory ExtensionTypeBuilder() = _$ExtensionTypeBuilder; + + ExtensionTypeBuilder._(); + + @override + void update(void Function(ExtensionTypeBuilder)? updates) { + updates?.call(this); + } + + @override + ListBuilder annotations = ListBuilder(); + + @override + ListBuilder docs = ListBuilder(); + + /// Whether this extension type is declared as `const`. + bool constant = false; + + String? name; + + @override + ListBuilder types = ListBuilder(); + + /// Name of the extension type's primary constructor. An empty string + /// will make it unnamed. + String primaryConstructorName = ''; + + RepresentationDeclaration? representationDeclaration; + + ListBuilder implements = ListBuilder(); + + ListBuilder constructors = ListBuilder(); + + ListBuilder fields = ListBuilder(); + + ListBuilder methods = ListBuilder(); +} + +abstract class RepresentationDeclaration extends Object + with HasAnnotations, HasDartDocs + implements + Built { + factory RepresentationDeclaration( + [void Function(RepresentationDeclarationBuilder)? updates]) = + _$RepresentationDeclaration; + + RepresentationDeclaration._(); + + @override + BuiltList get annotations; + + @override + BuiltList get docs; + + Reference get declaredRepresentationType; + + String get name; +} + +abstract class RepresentationDeclarationBuilder extends Object + with HasAnnotationsBuilder, HasDartDocsBuilder + implements + Builder { + factory RepresentationDeclarationBuilder() = + _$RepresentationDeclarationBuilder; + + RepresentationDeclarationBuilder._(); + + @override + void update(void Function(RepresentationDeclarationBuilder)? updates) { + updates?.call(this); + } + + @override + ListBuilder annotations = ListBuilder(); + + @override + ListBuilder docs = ListBuilder(); + + Reference? declaredRepresentationType; + + String? name; +} diff --git a/pkgs/code_builder/lib/src/specs/extension_type.g.dart b/pkgs/code_builder/lib/src/specs/extension_type.g.dart new file mode 100644 index 000000000..576984032 --- /dev/null +++ b/pkgs/code_builder/lib/src/specs/extension_type.g.dart @@ -0,0 +1,537 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'extension_type.dart'; + +// ************************************************************************** +// BuiltValueGenerator +// ************************************************************************** + +class _$ExtensionType extends ExtensionType { + @override + final BuiltList annotations; + @override + final BuiltList docs; + @override + final bool constant; + @override + final String name; + @override + final BuiltList types; + @override + final String primaryConstructorName; + @override + final RepresentationDeclaration representationDeclaration; + @override + final BuiltList implements; + @override + final BuiltList constructors; + @override + final BuiltList fields; + @override + final BuiltList methods; + + factory _$ExtensionType([void Function(ExtensionTypeBuilder)? updates]) => + (new ExtensionTypeBuilder()..update(updates)).build() as _$ExtensionType; + + _$ExtensionType._( + {required this.annotations, + required this.docs, + required this.constant, + required this.name, + required this.types, + required this.primaryConstructorName, + required this.representationDeclaration, + required this.implements, + required this.constructors, + required this.fields, + required this.methods}) + : super._() { + BuiltValueNullFieldError.checkNotNull( + annotations, r'ExtensionType', 'annotations'); + BuiltValueNullFieldError.checkNotNull(docs, r'ExtensionType', 'docs'); + BuiltValueNullFieldError.checkNotNull( + constant, r'ExtensionType', 'constant'); + BuiltValueNullFieldError.checkNotNull(name, r'ExtensionType', 'name'); + BuiltValueNullFieldError.checkNotNull(types, r'ExtensionType', 'types'); + BuiltValueNullFieldError.checkNotNull( + primaryConstructorName, r'ExtensionType', 'primaryConstructorName'); + BuiltValueNullFieldError.checkNotNull(representationDeclaration, + r'ExtensionType', 'representationDeclaration'); + BuiltValueNullFieldError.checkNotNull( + implements, r'ExtensionType', 'implements'); + BuiltValueNullFieldError.checkNotNull( + constructors, r'ExtensionType', 'constructors'); + BuiltValueNullFieldError.checkNotNull(fields, r'ExtensionType', 'fields'); + BuiltValueNullFieldError.checkNotNull(methods, r'ExtensionType', 'methods'); + } + + @override + ExtensionType rebuild(void Function(ExtensionTypeBuilder) updates) => + (toBuilder()..update(updates)).build(); + + @override + _$ExtensionTypeBuilder toBuilder() => + new _$ExtensionTypeBuilder()..replace(this); + + @override + bool operator ==(Object other) { + if (identical(other, this)) return true; + return other is ExtensionType && + annotations == other.annotations && + docs == other.docs && + constant == other.constant && + name == other.name && + types == other.types && + primaryConstructorName == other.primaryConstructorName && + representationDeclaration == other.representationDeclaration && + implements == other.implements && + constructors == other.constructors && + fields == other.fields && + methods == other.methods; + } + + @override + int get hashCode { + var _$hash = 0; + _$hash = $jc(_$hash, annotations.hashCode); + _$hash = $jc(_$hash, docs.hashCode); + _$hash = $jc(_$hash, constant.hashCode); + _$hash = $jc(_$hash, name.hashCode); + _$hash = $jc(_$hash, types.hashCode); + _$hash = $jc(_$hash, primaryConstructorName.hashCode); + _$hash = $jc(_$hash, representationDeclaration.hashCode); + _$hash = $jc(_$hash, implements.hashCode); + _$hash = $jc(_$hash, constructors.hashCode); + _$hash = $jc(_$hash, fields.hashCode); + _$hash = $jc(_$hash, methods.hashCode); + _$hash = $jf(_$hash); + return _$hash; + } + + @override + String toString() { + return (newBuiltValueToStringHelper(r'ExtensionType') + ..add('annotations', annotations) + ..add('docs', docs) + ..add('constant', constant) + ..add('name', name) + ..add('types', types) + ..add('primaryConstructorName', primaryConstructorName) + ..add('representationDeclaration', representationDeclaration) + ..add('implements', implements) + ..add('constructors', constructors) + ..add('fields', fields) + ..add('methods', methods)) + .toString(); + } +} + +class _$ExtensionTypeBuilder extends ExtensionTypeBuilder { + _$ExtensionType? _$v; + + @override + ListBuilder get annotations { + _$this; + return super.annotations; + } + + @override + set annotations(ListBuilder annotations) { + _$this; + super.annotations = annotations; + } + + @override + ListBuilder get docs { + _$this; + return super.docs; + } + + @override + set docs(ListBuilder docs) { + _$this; + super.docs = docs; + } + + @override + bool get constant { + _$this; + return super.constant; + } + + @override + set constant(bool constant) { + _$this; + super.constant = constant; + } + + @override + String? get name { + _$this; + return super.name; + } + + @override + set name(String? name) { + _$this; + super.name = name; + } + + @override + ListBuilder get types { + _$this; + return super.types; + } + + @override + set types(ListBuilder types) { + _$this; + super.types = types; + } + + @override + String get primaryConstructorName { + _$this; + return super.primaryConstructorName; + } + + @override + set primaryConstructorName(String primaryConstructorName) { + _$this; + super.primaryConstructorName = primaryConstructorName; + } + + @override + RepresentationDeclaration? get representationDeclaration { + _$this; + return super.representationDeclaration; + } + + @override + set representationDeclaration( + RepresentationDeclaration? representationDeclaration) { + _$this; + super.representationDeclaration = representationDeclaration; + } + + @override + ListBuilder get implements { + _$this; + return super.implements; + } + + @override + set implements(ListBuilder implements) { + _$this; + super.implements = implements; + } + + @override + ListBuilder get constructors { + _$this; + return super.constructors; + } + + @override + set constructors(ListBuilder constructors) { + _$this; + super.constructors = constructors; + } + + @override + ListBuilder get fields { + _$this; + return super.fields; + } + + @override + set fields(ListBuilder fields) { + _$this; + super.fields = fields; + } + + @override + ListBuilder get methods { + _$this; + return super.methods; + } + + @override + set methods(ListBuilder methods) { + _$this; + super.methods = methods; + } + + _$ExtensionTypeBuilder() : super._(); + + ExtensionTypeBuilder get _$this { + final $v = _$v; + if ($v != null) { + super.annotations = $v.annotations.toBuilder(); + super.docs = $v.docs.toBuilder(); + super.constant = $v.constant; + super.name = $v.name; + super.types = $v.types.toBuilder(); + super.primaryConstructorName = $v.primaryConstructorName; + super.representationDeclaration = $v.representationDeclaration; + super.implements = $v.implements.toBuilder(); + super.constructors = $v.constructors.toBuilder(); + super.fields = $v.fields.toBuilder(); + super.methods = $v.methods.toBuilder(); + _$v = null; + } + return this; + } + + @override + void replace(ExtensionType other) { + ArgumentError.checkNotNull(other, 'other'); + _$v = other as _$ExtensionType; + } + + @override + void update(void Function(ExtensionTypeBuilder)? updates) { + if (updates != null) updates(this); + } + + @override + ExtensionType build() => _build(); + + _$ExtensionType _build() { + _$ExtensionType _$result; + try { + _$result = _$v ?? + new _$ExtensionType._( + annotations: annotations.build(), + docs: docs.build(), + constant: BuiltValueNullFieldError.checkNotNull( + constant, r'ExtensionType', 'constant'), + name: BuiltValueNullFieldError.checkNotNull( + name, r'ExtensionType', 'name'), + types: types.build(), + primaryConstructorName: BuiltValueNullFieldError.checkNotNull( + primaryConstructorName, + r'ExtensionType', + 'primaryConstructorName'), + representationDeclaration: BuiltValueNullFieldError.checkNotNull( + representationDeclaration, + r'ExtensionType', + 'representationDeclaration'), + implements: implements.build(), + constructors: constructors.build(), + fields: fields.build(), + methods: methods.build()); + } catch (_) { + late String _$failedField; + try { + _$failedField = 'annotations'; + annotations.build(); + _$failedField = 'docs'; + docs.build(); + + _$failedField = 'types'; + types.build(); + + _$failedField = 'implements'; + implements.build(); + _$failedField = 'constructors'; + constructors.build(); + _$failedField = 'fields'; + fields.build(); + _$failedField = 'methods'; + methods.build(); + } catch (e) { + throw new BuiltValueNestedFieldError( + r'ExtensionType', _$failedField, e.toString()); + } + rethrow; + } + replace(_$result); + return _$result; + } +} + +class _$RepresentationDeclaration extends RepresentationDeclaration { + @override + final BuiltList annotations; + @override + final BuiltList docs; + @override + final Reference declaredRepresentationType; + @override + final String name; + + factory _$RepresentationDeclaration( + [void Function(RepresentationDeclarationBuilder)? updates]) => + (new RepresentationDeclarationBuilder()..update(updates)).build() + as _$RepresentationDeclaration; + + _$RepresentationDeclaration._( + {required this.annotations, + required this.docs, + required this.declaredRepresentationType, + required this.name}) + : super._() { + BuiltValueNullFieldError.checkNotNull( + annotations, r'RepresentationDeclaration', 'annotations'); + BuiltValueNullFieldError.checkNotNull( + docs, r'RepresentationDeclaration', 'docs'); + BuiltValueNullFieldError.checkNotNull(declaredRepresentationType, + r'RepresentationDeclaration', 'declaredRepresentationType'); + BuiltValueNullFieldError.checkNotNull( + name, r'RepresentationDeclaration', 'name'); + } + + @override + RepresentationDeclaration rebuild( + void Function(RepresentationDeclarationBuilder) updates) => + (toBuilder()..update(updates)).build(); + + @override + _$RepresentationDeclarationBuilder toBuilder() => + new _$RepresentationDeclarationBuilder()..replace(this); + + @override + bool operator ==(Object other) { + if (identical(other, this)) return true; + return other is RepresentationDeclaration && + annotations == other.annotations && + docs == other.docs && + declaredRepresentationType == other.declaredRepresentationType && + name == other.name; + } + + @override + int get hashCode { + var _$hash = 0; + _$hash = $jc(_$hash, annotations.hashCode); + _$hash = $jc(_$hash, docs.hashCode); + _$hash = $jc(_$hash, declaredRepresentationType.hashCode); + _$hash = $jc(_$hash, name.hashCode); + _$hash = $jf(_$hash); + return _$hash; + } + + @override + String toString() { + return (newBuiltValueToStringHelper(r'RepresentationDeclaration') + ..add('annotations', annotations) + ..add('docs', docs) + ..add('declaredRepresentationType', declaredRepresentationType) + ..add('name', name)) + .toString(); + } +} + +class _$RepresentationDeclarationBuilder + extends RepresentationDeclarationBuilder { + _$RepresentationDeclaration? _$v; + + @override + ListBuilder get annotations { + _$this; + return super.annotations; + } + + @override + set annotations(ListBuilder annotations) { + _$this; + super.annotations = annotations; + } + + @override + ListBuilder get docs { + _$this; + return super.docs; + } + + @override + set docs(ListBuilder docs) { + _$this; + super.docs = docs; + } + + @override + Reference? get declaredRepresentationType { + _$this; + return super.declaredRepresentationType; + } + + @override + set declaredRepresentationType(Reference? declaredRepresentationType) { + _$this; + super.declaredRepresentationType = declaredRepresentationType; + } + + @override + String? get name { + _$this; + return super.name; + } + + @override + set name(String? name) { + _$this; + super.name = name; + } + + _$RepresentationDeclarationBuilder() : super._(); + + RepresentationDeclarationBuilder get _$this { + final $v = _$v; + if ($v != null) { + super.annotations = $v.annotations.toBuilder(); + super.docs = $v.docs.toBuilder(); + super.declaredRepresentationType = $v.declaredRepresentationType; + super.name = $v.name; + _$v = null; + } + return this; + } + + @override + void replace(RepresentationDeclaration other) { + ArgumentError.checkNotNull(other, 'other'); + _$v = other as _$RepresentationDeclaration; + } + + @override + void update(void Function(RepresentationDeclarationBuilder)? updates) { + if (updates != null) updates(this); + } + + @override + RepresentationDeclaration build() => _build(); + + _$RepresentationDeclaration _build() { + _$RepresentationDeclaration _$result; + try { + _$result = _$v ?? + new _$RepresentationDeclaration._( + annotations: annotations.build(), + docs: docs.build(), + declaredRepresentationType: BuiltValueNullFieldError.checkNotNull( + declaredRepresentationType, + r'RepresentationDeclaration', + 'declaredRepresentationType'), + name: BuiltValueNullFieldError.checkNotNull( + name, r'RepresentationDeclaration', 'name')); + } catch (_) { + late String _$failedField; + try { + _$failedField = 'annotations'; + annotations.build(); + _$failedField = 'docs'; + docs.build(); + } catch (e) { + throw new BuiltValueNestedFieldError( + r'RepresentationDeclaration', _$failedField, e.toString()); + } + rethrow; + } + replace(_$result); + return _$result; + } +} + +// ignore_for_file: deprecated_member_use_from_same_package,type=lint diff --git a/pkgs/code_builder/lib/src/specs/field.dart b/pkgs/code_builder/lib/src/specs/field.dart new file mode 100644 index 000000000..7930bf6fa --- /dev/null +++ b/pkgs/code_builder/lib/src/specs/field.dart @@ -0,0 +1,101 @@ +// 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. + +import 'package:built_collection/built_collection.dart'; +import 'package:built_value/built_value.dart'; +import 'package:meta/meta.dart'; + +import '../base.dart'; +import '../mixins/annotations.dart'; +import '../mixins/dartdoc.dart'; +import '../visitors.dart'; +import 'code.dart'; +import 'expression.dart'; +import 'reference.dart'; + +part 'field.g.dart'; + +@immutable +abstract class Field extends Object + with HasAnnotations, HasDartDocs + implements Built, Spec { + factory Field([void Function(FieldBuilder) updates]) = _$Field; + + Field._(); + + @override + BuiltList get annotations; + + @override + BuiltList get docs; + + /// Field assignment, if any. + Code? get assignment; + + /// Whether this field should be prefixed with `static`. + /// + /// This is only valid within classes. + bool get static; + + /// Whether this field should be prefixed with `late`. + bool get late; + + /// Whether the field should be prefixed with `external`. + bool get external; + + /// Name of the field. + String get name; + + Reference? get type; + + FieldModifier get modifier; + + @override + R accept( + SpecVisitor visitor, [ + R? context, + ]) => + visitor.visitField(this, context); +} + +enum FieldModifier { + var$, + final$, + constant, +} + +abstract class FieldBuilder extends Object + with HasAnnotationsBuilder, HasDartDocsBuilder + implements Builder { + factory FieldBuilder() = _$FieldBuilder; + + FieldBuilder._(); + + @override + ListBuilder annotations = ListBuilder(); + + @override + ListBuilder docs = ListBuilder(); + + /// Field assignment, if any. + Code? assignment; + + /// Whether this field should be prefixed with `static`. + /// + /// This is only valid within classes. + bool static = false; + + /// Whether this field should be prefixed with `late`. + bool late = false; + + /// Whether the field should be prefixed with `external`. + bool external = false; + + /// Name of the field. + String? name; + + Reference? type; + + FieldModifier modifier = FieldModifier.var$; +} diff --git a/pkgs/code_builder/lib/src/specs/field.g.dart b/pkgs/code_builder/lib/src/specs/field.g.dart new file mode 100644 index 000000000..d15f1c7dc --- /dev/null +++ b/pkgs/code_builder/lib/src/specs/field.g.dart @@ -0,0 +1,287 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'field.dart'; + +// ************************************************************************** +// BuiltValueGenerator +// ************************************************************************** + +class _$Field extends Field { + @override + final BuiltList annotations; + @override + final BuiltList docs; + @override + final Code? assignment; + @override + final bool static; + @override + final bool late; + @override + final bool external; + @override + final String name; + @override + final Reference? type; + @override + final FieldModifier modifier; + + factory _$Field([void Function(FieldBuilder)? updates]) => + (new FieldBuilder()..update(updates)).build() as _$Field; + + _$Field._( + {required this.annotations, + required this.docs, + this.assignment, + required this.static, + required this.late, + required this.external, + required this.name, + this.type, + required this.modifier}) + : super._() { + BuiltValueNullFieldError.checkNotNull(annotations, r'Field', 'annotations'); + BuiltValueNullFieldError.checkNotNull(docs, r'Field', 'docs'); + BuiltValueNullFieldError.checkNotNull(static, r'Field', 'static'); + BuiltValueNullFieldError.checkNotNull(late, r'Field', 'late'); + BuiltValueNullFieldError.checkNotNull(external, r'Field', 'external'); + BuiltValueNullFieldError.checkNotNull(name, r'Field', 'name'); + BuiltValueNullFieldError.checkNotNull(modifier, r'Field', 'modifier'); + } + + @override + Field rebuild(void Function(FieldBuilder) updates) => + (toBuilder()..update(updates)).build(); + + @override + _$FieldBuilder toBuilder() => new _$FieldBuilder()..replace(this); + + @override + bool operator ==(Object other) { + if (identical(other, this)) return true; + return other is Field && + annotations == other.annotations && + docs == other.docs && + assignment == other.assignment && + static == other.static && + late == other.late && + external == other.external && + name == other.name && + type == other.type && + modifier == other.modifier; + } + + @override + int get hashCode { + var _$hash = 0; + _$hash = $jc(_$hash, annotations.hashCode); + _$hash = $jc(_$hash, docs.hashCode); + _$hash = $jc(_$hash, assignment.hashCode); + _$hash = $jc(_$hash, static.hashCode); + _$hash = $jc(_$hash, late.hashCode); + _$hash = $jc(_$hash, external.hashCode); + _$hash = $jc(_$hash, name.hashCode); + _$hash = $jc(_$hash, type.hashCode); + _$hash = $jc(_$hash, modifier.hashCode); + _$hash = $jf(_$hash); + return _$hash; + } + + @override + String toString() { + return (newBuiltValueToStringHelper(r'Field') + ..add('annotations', annotations) + ..add('docs', docs) + ..add('assignment', assignment) + ..add('static', static) + ..add('late', late) + ..add('external', external) + ..add('name', name) + ..add('type', type) + ..add('modifier', modifier)) + .toString(); + } +} + +class _$FieldBuilder extends FieldBuilder { + _$Field? _$v; + + @override + ListBuilder get annotations { + _$this; + return super.annotations; + } + + @override + set annotations(ListBuilder annotations) { + _$this; + super.annotations = annotations; + } + + @override + ListBuilder get docs { + _$this; + return super.docs; + } + + @override + set docs(ListBuilder docs) { + _$this; + super.docs = docs; + } + + @override + Code? get assignment { + _$this; + return super.assignment; + } + + @override + set assignment(Code? assignment) { + _$this; + super.assignment = assignment; + } + + @override + bool get static { + _$this; + return super.static; + } + + @override + set static(bool static) { + _$this; + super.static = static; + } + + @override + bool get late { + _$this; + return super.late; + } + + @override + set late(bool late) { + _$this; + super.late = late; + } + + @override + bool get external { + _$this; + return super.external; + } + + @override + set external(bool external) { + _$this; + super.external = external; + } + + @override + String? get name { + _$this; + return super.name; + } + + @override + set name(String? name) { + _$this; + super.name = name; + } + + @override + Reference? get type { + _$this; + return super.type; + } + + @override + set type(Reference? type) { + _$this; + super.type = type; + } + + @override + FieldModifier get modifier { + _$this; + return super.modifier; + } + + @override + set modifier(FieldModifier modifier) { + _$this; + super.modifier = modifier; + } + + _$FieldBuilder() : super._(); + + FieldBuilder get _$this { + final $v = _$v; + if ($v != null) { + super.annotations = $v.annotations.toBuilder(); + super.docs = $v.docs.toBuilder(); + super.assignment = $v.assignment; + super.static = $v.static; + super.late = $v.late; + super.external = $v.external; + super.name = $v.name; + super.type = $v.type; + super.modifier = $v.modifier; + _$v = null; + } + return this; + } + + @override + void replace(Field other) { + ArgumentError.checkNotNull(other, 'other'); + _$v = other as _$Field; + } + + @override + void update(void Function(FieldBuilder)? updates) { + if (updates != null) updates(this); + } + + @override + Field build() => _build(); + + _$Field _build() { + _$Field _$result; + try { + _$result = _$v ?? + new _$Field._( + annotations: annotations.build(), + docs: docs.build(), + assignment: assignment, + static: BuiltValueNullFieldError.checkNotNull( + static, r'Field', 'static'), + late: + BuiltValueNullFieldError.checkNotNull(late, r'Field', 'late'), + external: BuiltValueNullFieldError.checkNotNull( + external, r'Field', 'external'), + name: + BuiltValueNullFieldError.checkNotNull(name, r'Field', 'name'), + type: type, + modifier: BuiltValueNullFieldError.checkNotNull( + modifier, r'Field', 'modifier')); + } catch (_) { + late String _$failedField; + try { + _$failedField = 'annotations'; + annotations.build(); + _$failedField = 'docs'; + docs.build(); + } catch (e) { + throw new BuiltValueNestedFieldError( + r'Field', _$failedField, e.toString()); + } + rethrow; + } + replace(_$result); + return _$result; + } +} + +// ignore_for_file: deprecated_member_use_from_same_package,type=lint diff --git a/pkgs/code_builder/lib/src/specs/library.dart b/pkgs/code_builder/lib/src/specs/library.dart new file mode 100644 index 000000000..bbfbf3f86 --- /dev/null +++ b/pkgs/code_builder/lib/src/specs/library.dart @@ -0,0 +1,79 @@ +// 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. + +import 'package:built_collection/built_collection.dart'; +import 'package:built_value/built_value.dart'; +import 'package:meta/meta.dart'; + +import '../base.dart'; +import '../mixins/annotations.dart'; +import '../mixins/dartdoc.dart'; +import '../visitors.dart'; +import 'directive.dart'; +import 'expression.dart'; + +part 'library.g.dart'; + +@immutable +abstract class Library + with HasAnnotations, HasDartDocs + implements Built, Spec { + factory Library([void Function(LibraryBuilder) updates]) = _$Library; + Library._(); + + @override + BuiltList get annotations; + + @override + BuiltList get docs; + + BuiltList get directives; + BuiltList get body; + + /// Line comments to place at the start of the library. + BuiltList get comments; + + /// A comment indicating the tool this library was generated by. + /// + /// This is typically of the form `Generated by xxx.`; it should exclude + /// leading line comment characters. + String? get generatedByComment; + + /// A list of analysis issues to ignore (`ignore_for_file: ...`). + BuiltList get ignoreForFile; + + /// Name of the library. + /// + /// May be `null` when no [annotations] are specified. + String? get name; + + @override + R accept( + SpecVisitor visitor, [ + R? context, + ]) => + visitor.visitLibrary(this, context); +} + +abstract class LibraryBuilder + with HasAnnotationsBuilder, HasDartDocsBuilder + implements Builder { + factory LibraryBuilder() = _$LibraryBuilder; + LibraryBuilder._(); + + @override + ListBuilder annotations = ListBuilder(); + + @override + ListBuilder docs = ListBuilder(); + + ListBuilder body = ListBuilder(); + ListBuilder directives = ListBuilder(); + + ListBuilder comments = ListBuilder(); + String? generatedByComment; + ListBuilder ignoreForFile = ListBuilder(); + + String? name; +} diff --git a/pkgs/code_builder/lib/src/specs/library.g.dart b/pkgs/code_builder/lib/src/specs/library.g.dart new file mode 100644 index 000000000..63cfc200b --- /dev/null +++ b/pkgs/code_builder/lib/src/specs/library.g.dart @@ -0,0 +1,272 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'library.dart'; + +// ************************************************************************** +// BuiltValueGenerator +// ************************************************************************** + +class _$Library extends Library { + @override + final BuiltList annotations; + @override + final BuiltList docs; + @override + final BuiltList directives; + @override + final BuiltList body; + @override + final BuiltList comments; + @override + final String? generatedByComment; + @override + final BuiltList ignoreForFile; + @override + final String? name; + + factory _$Library([void Function(LibraryBuilder)? updates]) => + (new LibraryBuilder()..update(updates)).build() as _$Library; + + _$Library._( + {required this.annotations, + required this.docs, + required this.directives, + required this.body, + required this.comments, + this.generatedByComment, + required this.ignoreForFile, + this.name}) + : super._() { + BuiltValueNullFieldError.checkNotNull( + annotations, r'Library', 'annotations'); + BuiltValueNullFieldError.checkNotNull(docs, r'Library', 'docs'); + BuiltValueNullFieldError.checkNotNull(directives, r'Library', 'directives'); + BuiltValueNullFieldError.checkNotNull(body, r'Library', 'body'); + BuiltValueNullFieldError.checkNotNull(comments, r'Library', 'comments'); + BuiltValueNullFieldError.checkNotNull( + ignoreForFile, r'Library', 'ignoreForFile'); + } + + @override + Library rebuild(void Function(LibraryBuilder) updates) => + (toBuilder()..update(updates)).build(); + + @override + _$LibraryBuilder toBuilder() => new _$LibraryBuilder()..replace(this); + + @override + bool operator ==(Object other) { + if (identical(other, this)) return true; + return other is Library && + annotations == other.annotations && + docs == other.docs && + directives == other.directives && + body == other.body && + comments == other.comments && + generatedByComment == other.generatedByComment && + ignoreForFile == other.ignoreForFile && + name == other.name; + } + + @override + int get hashCode { + var _$hash = 0; + _$hash = $jc(_$hash, annotations.hashCode); + _$hash = $jc(_$hash, docs.hashCode); + _$hash = $jc(_$hash, directives.hashCode); + _$hash = $jc(_$hash, body.hashCode); + _$hash = $jc(_$hash, comments.hashCode); + _$hash = $jc(_$hash, generatedByComment.hashCode); + _$hash = $jc(_$hash, ignoreForFile.hashCode); + _$hash = $jc(_$hash, name.hashCode); + _$hash = $jf(_$hash); + return _$hash; + } + + @override + String toString() { + return (newBuiltValueToStringHelper(r'Library') + ..add('annotations', annotations) + ..add('docs', docs) + ..add('directives', directives) + ..add('body', body) + ..add('comments', comments) + ..add('generatedByComment', generatedByComment) + ..add('ignoreForFile', ignoreForFile) + ..add('name', name)) + .toString(); + } +} + +class _$LibraryBuilder extends LibraryBuilder { + _$Library? _$v; + + @override + ListBuilder get annotations { + _$this; + return super.annotations; + } + + @override + set annotations(ListBuilder annotations) { + _$this; + super.annotations = annotations; + } + + @override + ListBuilder get docs { + _$this; + return super.docs; + } + + @override + set docs(ListBuilder docs) { + _$this; + super.docs = docs; + } + + @override + ListBuilder get directives { + _$this; + return super.directives; + } + + @override + set directives(ListBuilder directives) { + _$this; + super.directives = directives; + } + + @override + ListBuilder get body { + _$this; + return super.body; + } + + @override + set body(ListBuilder body) { + _$this; + super.body = body; + } + + @override + ListBuilder get comments { + _$this; + return super.comments; + } + + @override + set comments(ListBuilder comments) { + _$this; + super.comments = comments; + } + + @override + String? get generatedByComment { + _$this; + return super.generatedByComment; + } + + @override + set generatedByComment(String? generatedByComment) { + _$this; + super.generatedByComment = generatedByComment; + } + + @override + ListBuilder get ignoreForFile { + _$this; + return super.ignoreForFile; + } + + @override + set ignoreForFile(ListBuilder ignoreForFile) { + _$this; + super.ignoreForFile = ignoreForFile; + } + + @override + String? get name { + _$this; + return super.name; + } + + @override + set name(String? name) { + _$this; + super.name = name; + } + + _$LibraryBuilder() : super._(); + + LibraryBuilder get _$this { + final $v = _$v; + if ($v != null) { + super.annotations = $v.annotations.toBuilder(); + super.docs = $v.docs.toBuilder(); + super.directives = $v.directives.toBuilder(); + super.body = $v.body.toBuilder(); + super.comments = $v.comments.toBuilder(); + super.generatedByComment = $v.generatedByComment; + super.ignoreForFile = $v.ignoreForFile.toBuilder(); + super.name = $v.name; + _$v = null; + } + return this; + } + + @override + void replace(Library other) { + ArgumentError.checkNotNull(other, 'other'); + _$v = other as _$Library; + } + + @override + void update(void Function(LibraryBuilder)? updates) { + if (updates != null) updates(this); + } + + @override + Library build() => _build(); + + _$Library _build() { + _$Library _$result; + try { + _$result = _$v ?? + new _$Library._( + annotations: annotations.build(), + docs: docs.build(), + directives: directives.build(), + body: body.build(), + comments: comments.build(), + generatedByComment: generatedByComment, + ignoreForFile: ignoreForFile.build(), + name: name); + } catch (_) { + late String _$failedField; + try { + _$failedField = 'annotations'; + annotations.build(); + _$failedField = 'docs'; + docs.build(); + _$failedField = 'directives'; + directives.build(); + _$failedField = 'body'; + body.build(); + _$failedField = 'comments'; + comments.build(); + + _$failedField = 'ignoreForFile'; + ignoreForFile.build(); + } catch (e) { + throw new BuiltValueNestedFieldError( + r'Library', _$failedField, e.toString()); + } + rethrow; + } + replace(_$result); + return _$result; + } +} + +// ignore_for_file: deprecated_member_use_from_same_package,type=lint diff --git a/pkgs/code_builder/lib/src/specs/method.dart b/pkgs/code_builder/lib/src/specs/method.dart new file mode 100644 index 000000000..2c0a645dc --- /dev/null +++ b/pkgs/code_builder/lib/src/specs/method.dart @@ -0,0 +1,271 @@ +// 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. + +import 'package:built_collection/built_collection.dart'; +import 'package:built_value/built_value.dart'; +import 'package:meta/meta.dart'; + +import '../base.dart'; +import '../mixins/annotations.dart'; +import '../mixins/dartdoc.dart'; +import '../mixins/generics.dart'; +import '../visitors.dart'; +import 'code.dart'; +import 'expression.dart'; +import 'reference.dart'; + +part 'method.g.dart'; + +const _$void = Reference('void'); + +@immutable +abstract class Method extends Object + with HasAnnotations, HasGenerics, HasDartDocs + implements Built, Spec { + factory Method([void Function(MethodBuilder) updates]) = _$Method; + + factory Method.returnsVoid([void Function(MethodBuilder)? updates]) => + Method((b) { + if (updates != null) { + updates(b); + } + b.returns = _$void; + }); + + Method._(); + + @override + BuiltList get annotations; + + @override + BuiltList get docs; + + @override + BuiltList get types; + + /// Optional parameters. + BuiltList get optionalParameters; + + /// Required parameters. + BuiltList get requiredParameters; + + /// Body of the method. + Code? get body; + + /// Whether the method should be prefixed with `external`. + bool get external; + + /// Whether this method is a simple lambda expression. + /// + /// May be `null` to be inferred based on the value of [body]. + bool? get lambda; + + /// Whether this method should be prefixed with `static`. + /// + /// This is only valid within classes. + bool get static; + + /// Name of the method or function. + /// + /// May be `null` when being used as a [closure]. + String? get name; + + /// Whether this is a getter or setter. + MethodType? get type; + + /// Whether this method is `async`, `async*`, or `sync*`. + MethodModifier? get modifier; + + Reference? get returns; + + @override + R accept( + SpecVisitor visitor, [ + R? context, + ]) => + visitor.visitMethod(this, context); + + /// This method as a closure. + Expression get closure => toClosure(this); + + /// This method as a (possibly) generic closure. + Expression get genericClosure => toGenericClosure(this); +} + +abstract class MethodBuilder extends Object + with HasAnnotationsBuilder, HasGenericsBuilder, HasDartDocsBuilder + implements Builder { + factory MethodBuilder() = _$MethodBuilder; + + MethodBuilder._(); + + @override + void update(void Function(MethodBuilder)? updates) { + updates?.call(this); + } + + @override + ListBuilder annotations = ListBuilder(); + + @override + ListBuilder docs = ListBuilder(); + + @override + ListBuilder types = ListBuilder(); + + /// Optional parameters. + ListBuilder optionalParameters = ListBuilder(); + + /// Required parameters. + ListBuilder requiredParameters = ListBuilder(); + + /// Body of the method. + Code? body; + + /// Whether the method should be prefixed with `external`. + bool external = false; + + /// Whether this method is a simple lambda expression. + /// + /// If not specified this is inferred from the [body]. + bool? lambda; + + /// Whether this method should be prefixed with `static`. + /// + /// This is only valid within classes. + bool static = false; + + /// Name of the method or function. + String? name; + + /// Whether this is a getter or setter. + MethodType? type; + + /// Whether this method is `async`, `async*`, or `sync*`. + MethodModifier? modifier; + + Reference? returns; +} + +enum MethodType { + getter, + setter, +} + +enum MethodModifier { + async, + asyncStar, + syncStar, +} + +abstract class Parameter extends Object + with HasAnnotations, HasGenerics, HasDartDocs + implements Built { + factory Parameter([void Function(ParameterBuilder) updates]) = _$Parameter; + + Parameter._(); + + /// If not `null`, a default assignment if the parameter is optional. + Code? get defaultTo; + + /// Name of the parameter. + String get name; + + /// Whether this parameter should be named, if optional. + bool get named; + + /// Whether this parameter should be field formal (i.e. `this.`). + /// + /// This is only valid on constructors; + bool get toThis; + + /// Whether this parameter should be passed to super\ + /// constructor (i.e. `super.`). + /// + /// This is only valid on constructors; + bool get toSuper; + + @override + BuiltList get annotations; + + @override + BuiltList get docs; + + @override + BuiltList get types; + + /// Type of the parameter; + Reference? get type; + + /// Whether this parameter should be annotated with the `required` keyword. + /// + /// This is only valid on named parameters. + /// + /// This is only valid when the output is targeting a Dart language version + /// that supports null safety. + bool get required; + + /// Whether this parameter should be annotated with the `covariant` keyword. + /// + /// This is only valid on instance methods. + bool get covariant; +} + +abstract class ParameterBuilder extends Object + with HasAnnotationsBuilder, HasGenericsBuilder, HasDartDocsBuilder + implements Builder { + factory ParameterBuilder() = _$ParameterBuilder; + + ParameterBuilder._(); + + @override + void update(void Function(ParameterBuilder)? updates) { + updates?.call(this); + } + + /// If not `null`, a default assignment if the parameter is optional. + Code? defaultTo; + + /// Name of the parameter. + late final String name; + + /// Whether this parameter should be named, if optional. + bool named = false; + + /// Whether this parameter should be field formal (i.e. `this.`). + /// + /// This is only valid on constructors; + bool toThis = false; + + /// Whether this parameter should be passed to super\ + /// constructor (i.e. `super.`). + /// + /// This is only valid on constructors; + bool toSuper = false; + + @override + ListBuilder annotations = ListBuilder(); + + @override + ListBuilder docs = ListBuilder(); + + @override + ListBuilder types = ListBuilder(); + + /// Type of the parameter; + Reference? type; + + /// Whether this parameter should be annotated with the `required` keyword. + /// + /// This is only valid on named parameters. + /// + /// This is only valid when the output is targeting a Dart language version + /// that supports null safety. + bool required = false; + + /// Whether this parameter should be annotated with the `covariant` keyword. + /// + /// This is only valid on instance methods. + bool covariant = false; +} diff --git a/pkgs/code_builder/lib/src/specs/method.g.dart b/pkgs/code_builder/lib/src/specs/method.g.dart new file mode 100644 index 000000000..214f6b214 --- /dev/null +++ b/pkgs/code_builder/lib/src/specs/method.g.dart @@ -0,0 +1,697 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'method.dart'; + +// ************************************************************************** +// BuiltValueGenerator +// ************************************************************************** + +class _$Method extends Method { + @override + final BuiltList annotations; + @override + final BuiltList docs; + @override + final BuiltList types; + @override + final BuiltList optionalParameters; + @override + final BuiltList requiredParameters; + @override + final Code? body; + @override + final bool external; + @override + final bool? lambda; + @override + final bool static; + @override + final String? name; + @override + final MethodType? type; + @override + final MethodModifier? modifier; + @override + final Reference? returns; + + factory _$Method([void Function(MethodBuilder)? updates]) => + (new MethodBuilder()..update(updates)).build() as _$Method; + + _$Method._( + {required this.annotations, + required this.docs, + required this.types, + required this.optionalParameters, + required this.requiredParameters, + this.body, + required this.external, + this.lambda, + required this.static, + this.name, + this.type, + this.modifier, + this.returns}) + : super._() { + BuiltValueNullFieldError.checkNotNull( + annotations, r'Method', 'annotations'); + BuiltValueNullFieldError.checkNotNull(docs, r'Method', 'docs'); + BuiltValueNullFieldError.checkNotNull(types, r'Method', 'types'); + BuiltValueNullFieldError.checkNotNull( + optionalParameters, r'Method', 'optionalParameters'); + BuiltValueNullFieldError.checkNotNull( + requiredParameters, r'Method', 'requiredParameters'); + BuiltValueNullFieldError.checkNotNull(external, r'Method', 'external'); + BuiltValueNullFieldError.checkNotNull(static, r'Method', 'static'); + } + + @override + Method rebuild(void Function(MethodBuilder) updates) => + (toBuilder()..update(updates)).build(); + + @override + _$MethodBuilder toBuilder() => new _$MethodBuilder()..replace(this); + + @override + bool operator ==(Object other) { + if (identical(other, this)) return true; + return other is Method && + annotations == other.annotations && + docs == other.docs && + types == other.types && + optionalParameters == other.optionalParameters && + requiredParameters == other.requiredParameters && + body == other.body && + external == other.external && + lambda == other.lambda && + static == other.static && + name == other.name && + type == other.type && + modifier == other.modifier && + returns == other.returns; + } + + @override + int get hashCode { + var _$hash = 0; + _$hash = $jc(_$hash, annotations.hashCode); + _$hash = $jc(_$hash, docs.hashCode); + _$hash = $jc(_$hash, types.hashCode); + _$hash = $jc(_$hash, optionalParameters.hashCode); + _$hash = $jc(_$hash, requiredParameters.hashCode); + _$hash = $jc(_$hash, body.hashCode); + _$hash = $jc(_$hash, external.hashCode); + _$hash = $jc(_$hash, lambda.hashCode); + _$hash = $jc(_$hash, static.hashCode); + _$hash = $jc(_$hash, name.hashCode); + _$hash = $jc(_$hash, type.hashCode); + _$hash = $jc(_$hash, modifier.hashCode); + _$hash = $jc(_$hash, returns.hashCode); + _$hash = $jf(_$hash); + return _$hash; + } + + @override + String toString() { + return (newBuiltValueToStringHelper(r'Method') + ..add('annotations', annotations) + ..add('docs', docs) + ..add('types', types) + ..add('optionalParameters', optionalParameters) + ..add('requiredParameters', requiredParameters) + ..add('body', body) + ..add('external', external) + ..add('lambda', lambda) + ..add('static', static) + ..add('name', name) + ..add('type', type) + ..add('modifier', modifier) + ..add('returns', returns)) + .toString(); + } +} + +class _$MethodBuilder extends MethodBuilder { + _$Method? _$v; + + @override + ListBuilder get annotations { + _$this; + return super.annotations; + } + + @override + set annotations(ListBuilder annotations) { + _$this; + super.annotations = annotations; + } + + @override + ListBuilder get docs { + _$this; + return super.docs; + } + + @override + set docs(ListBuilder docs) { + _$this; + super.docs = docs; + } + + @override + ListBuilder get types { + _$this; + return super.types; + } + + @override + set types(ListBuilder types) { + _$this; + super.types = types; + } + + @override + ListBuilder get optionalParameters { + _$this; + return super.optionalParameters; + } + + @override + set optionalParameters(ListBuilder optionalParameters) { + _$this; + super.optionalParameters = optionalParameters; + } + + @override + ListBuilder get requiredParameters { + _$this; + return super.requiredParameters; + } + + @override + set requiredParameters(ListBuilder requiredParameters) { + _$this; + super.requiredParameters = requiredParameters; + } + + @override + Code? get body { + _$this; + return super.body; + } + + @override + set body(Code? body) { + _$this; + super.body = body; + } + + @override + bool get external { + _$this; + return super.external; + } + + @override + set external(bool external) { + _$this; + super.external = external; + } + + @override + bool? get lambda { + _$this; + return super.lambda; + } + + @override + set lambda(bool? lambda) { + _$this; + super.lambda = lambda; + } + + @override + bool get static { + _$this; + return super.static; + } + + @override + set static(bool static) { + _$this; + super.static = static; + } + + @override + String? get name { + _$this; + return super.name; + } + + @override + set name(String? name) { + _$this; + super.name = name; + } + + @override + MethodType? get type { + _$this; + return super.type; + } + + @override + set type(MethodType? type) { + _$this; + super.type = type; + } + + @override + MethodModifier? get modifier { + _$this; + return super.modifier; + } + + @override + set modifier(MethodModifier? modifier) { + _$this; + super.modifier = modifier; + } + + @override + Reference? get returns { + _$this; + return super.returns; + } + + @override + set returns(Reference? returns) { + _$this; + super.returns = returns; + } + + _$MethodBuilder() : super._(); + + MethodBuilder get _$this { + final $v = _$v; + if ($v != null) { + super.annotations = $v.annotations.toBuilder(); + super.docs = $v.docs.toBuilder(); + super.types = $v.types.toBuilder(); + super.optionalParameters = $v.optionalParameters.toBuilder(); + super.requiredParameters = $v.requiredParameters.toBuilder(); + super.body = $v.body; + super.external = $v.external; + super.lambda = $v.lambda; + super.static = $v.static; + super.name = $v.name; + super.type = $v.type; + super.modifier = $v.modifier; + super.returns = $v.returns; + _$v = null; + } + return this; + } + + @override + void replace(Method other) { + ArgumentError.checkNotNull(other, 'other'); + _$v = other as _$Method; + } + + @override + void update(void Function(MethodBuilder)? updates) { + if (updates != null) updates(this); + } + + @override + Method build() => _build(); + + _$Method _build() { + _$Method _$result; + try { + _$result = _$v ?? + new _$Method._( + annotations: annotations.build(), + docs: docs.build(), + types: types.build(), + optionalParameters: optionalParameters.build(), + requiredParameters: requiredParameters.build(), + body: body, + external: BuiltValueNullFieldError.checkNotNull( + external, r'Method', 'external'), + lambda: lambda, + static: BuiltValueNullFieldError.checkNotNull( + static, r'Method', 'static'), + name: name, + type: type, + modifier: modifier, + returns: returns); + } catch (_) { + late String _$failedField; + try { + _$failedField = 'annotations'; + annotations.build(); + _$failedField = 'docs'; + docs.build(); + _$failedField = 'types'; + types.build(); + _$failedField = 'optionalParameters'; + optionalParameters.build(); + _$failedField = 'requiredParameters'; + requiredParameters.build(); + } catch (e) { + throw new BuiltValueNestedFieldError( + r'Method', _$failedField, e.toString()); + } + rethrow; + } + replace(_$result); + return _$result; + } +} + +class _$Parameter extends Parameter { + @override + final Code? defaultTo; + @override + final String name; + @override + final bool named; + @override + final bool toThis; + @override + final bool toSuper; + @override + final BuiltList annotations; + @override + final BuiltList docs; + @override + final BuiltList types; + @override + final Reference? type; + @override + final bool required; + @override + final bool covariant; + + factory _$Parameter([void Function(ParameterBuilder)? updates]) => + (new ParameterBuilder()..update(updates)).build() as _$Parameter; + + _$Parameter._( + {this.defaultTo, + required this.name, + required this.named, + required this.toThis, + required this.toSuper, + required this.annotations, + required this.docs, + required this.types, + this.type, + required this.required, + required this.covariant}) + : super._() { + BuiltValueNullFieldError.checkNotNull(name, r'Parameter', 'name'); + BuiltValueNullFieldError.checkNotNull(named, r'Parameter', 'named'); + BuiltValueNullFieldError.checkNotNull(toThis, r'Parameter', 'toThis'); + BuiltValueNullFieldError.checkNotNull(toSuper, r'Parameter', 'toSuper'); + BuiltValueNullFieldError.checkNotNull( + annotations, r'Parameter', 'annotations'); + BuiltValueNullFieldError.checkNotNull(docs, r'Parameter', 'docs'); + BuiltValueNullFieldError.checkNotNull(types, r'Parameter', 'types'); + BuiltValueNullFieldError.checkNotNull(required, r'Parameter', 'required'); + BuiltValueNullFieldError.checkNotNull(covariant, r'Parameter', 'covariant'); + } + + @override + Parameter rebuild(void Function(ParameterBuilder) updates) => + (toBuilder()..update(updates)).build(); + + @override + _$ParameterBuilder toBuilder() => new _$ParameterBuilder()..replace(this); + + @override + bool operator ==(Object other) { + if (identical(other, this)) return true; + return other is Parameter && + defaultTo == other.defaultTo && + name == other.name && + named == other.named && + toThis == other.toThis && + toSuper == other.toSuper && + annotations == other.annotations && + docs == other.docs && + types == other.types && + type == other.type && + required == other.required && + covariant == other.covariant; + } + + @override + int get hashCode { + var _$hash = 0; + _$hash = $jc(_$hash, defaultTo.hashCode); + _$hash = $jc(_$hash, name.hashCode); + _$hash = $jc(_$hash, named.hashCode); + _$hash = $jc(_$hash, toThis.hashCode); + _$hash = $jc(_$hash, toSuper.hashCode); + _$hash = $jc(_$hash, annotations.hashCode); + _$hash = $jc(_$hash, docs.hashCode); + _$hash = $jc(_$hash, types.hashCode); + _$hash = $jc(_$hash, type.hashCode); + _$hash = $jc(_$hash, required.hashCode); + _$hash = $jc(_$hash, covariant.hashCode); + _$hash = $jf(_$hash); + return _$hash; + } + + @override + String toString() { + return (newBuiltValueToStringHelper(r'Parameter') + ..add('defaultTo', defaultTo) + ..add('name', name) + ..add('named', named) + ..add('toThis', toThis) + ..add('toSuper', toSuper) + ..add('annotations', annotations) + ..add('docs', docs) + ..add('types', types) + ..add('type', type) + ..add('required', required) + ..add('covariant', covariant)) + .toString(); + } +} + +class _$ParameterBuilder extends ParameterBuilder { + _$Parameter? _$v; + + @override + Code? get defaultTo { + _$this; + return super.defaultTo; + } + + @override + set defaultTo(Code? defaultTo) { + _$this; + super.defaultTo = defaultTo; + } + + @override + String get name { + _$this; + return super.name; + } + + @override + set name(String name) { + _$this; + super.name = name; + } + + @override + bool get named { + _$this; + return super.named; + } + + @override + set named(bool named) { + _$this; + super.named = named; + } + + @override + bool get toThis { + _$this; + return super.toThis; + } + + @override + set toThis(bool toThis) { + _$this; + super.toThis = toThis; + } + + @override + bool get toSuper { + _$this; + return super.toSuper; + } + + @override + set toSuper(bool toSuper) { + _$this; + super.toSuper = toSuper; + } + + @override + ListBuilder get annotations { + _$this; + return super.annotations; + } + + @override + set annotations(ListBuilder annotations) { + _$this; + super.annotations = annotations; + } + + @override + ListBuilder get docs { + _$this; + return super.docs; + } + + @override + set docs(ListBuilder docs) { + _$this; + super.docs = docs; + } + + @override + ListBuilder get types { + _$this; + return super.types; + } + + @override + set types(ListBuilder types) { + _$this; + super.types = types; + } + + @override + Reference? get type { + _$this; + return super.type; + } + + @override + set type(Reference? type) { + _$this; + super.type = type; + } + + @override + bool get required { + _$this; + return super.required; + } + + @override + set required(bool required) { + _$this; + super.required = required; + } + + @override + bool get covariant { + _$this; + return super.covariant; + } + + @override + set covariant(bool covariant) { + _$this; + super.covariant = covariant; + } + + _$ParameterBuilder() : super._(); + + ParameterBuilder get _$this { + final $v = _$v; + if ($v != null) { + super.defaultTo = $v.defaultTo; + super.name = $v.name; + super.named = $v.named; + super.toThis = $v.toThis; + super.toSuper = $v.toSuper; + super.annotations = $v.annotations.toBuilder(); + super.docs = $v.docs.toBuilder(); + super.types = $v.types.toBuilder(); + super.type = $v.type; + super.required = $v.required; + super.covariant = $v.covariant; + _$v = null; + } + return this; + } + + @override + void replace(Parameter other) { + ArgumentError.checkNotNull(other, 'other'); + _$v = other as _$Parameter; + } + + @override + void update(void Function(ParameterBuilder)? updates) { + if (updates != null) updates(this); + } + + @override + Parameter build() => _build(); + + _$Parameter _build() { + _$Parameter _$result; + try { + _$result = _$v ?? + new _$Parameter._( + defaultTo: defaultTo, + name: BuiltValueNullFieldError.checkNotNull( + name, r'Parameter', 'name'), + named: BuiltValueNullFieldError.checkNotNull( + named, r'Parameter', 'named'), + toThis: BuiltValueNullFieldError.checkNotNull( + toThis, r'Parameter', 'toThis'), + toSuper: BuiltValueNullFieldError.checkNotNull( + toSuper, r'Parameter', 'toSuper'), + annotations: annotations.build(), + docs: docs.build(), + types: types.build(), + type: type, + required: BuiltValueNullFieldError.checkNotNull( + required, r'Parameter', 'required'), + covariant: BuiltValueNullFieldError.checkNotNull( + covariant, r'Parameter', 'covariant')); + } catch (_) { + late String _$failedField; + try { + _$failedField = 'annotations'; + annotations.build(); + _$failedField = 'docs'; + docs.build(); + _$failedField = 'types'; + types.build(); + } catch (e) { + throw new BuiltValueNestedFieldError( + r'Parameter', _$failedField, e.toString()); + } + rethrow; + } + replace(_$result); + return _$result; + } +} + +// ignore_for_file: deprecated_member_use_from_same_package,type=lint diff --git a/pkgs/code_builder/lib/src/specs/mixin.dart b/pkgs/code_builder/lib/src/specs/mixin.dart new file mode 100644 index 000000000..e666bddaa --- /dev/null +++ b/pkgs/code_builder/lib/src/specs/mixin.dart @@ -0,0 +1,88 @@ +// Copyright (c) 2021, 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. + +import 'package:built_collection/built_collection.dart'; +import 'package:built_value/built_value.dart'; +import 'package:meta/meta.dart'; + +import '../base.dart'; +import '../mixins/annotations.dart'; +import '../mixins/dartdoc.dart'; +import '../mixins/generics.dart'; +import '../visitors.dart'; +import 'expression.dart'; +import 'field.dart'; +import 'method.dart'; +import 'reference.dart'; + +part 'mixin.g.dart'; + +@immutable +abstract class Mixin extends Object + with HasAnnotations, HasDartDocs, HasGenerics + implements Built, Spec { + factory Mixin([void Function(MixinBuilder b) updates]) = _$Mixin; + + Mixin._(); + + /// Whether the mixin is a `base mixin`. + bool get base; + + @override + BuiltList get annotations; + + @override + BuiltList get docs; + + Reference? get on; + + BuiltList get implements; + + @override + BuiltList get types; + + BuiltList get methods; + BuiltList get fields; + + /// Name of the mixin. + String get name; + + @override + R accept( + SpecVisitor visitor, [ + R? context, + ]) => + visitor.visitMixin(this, context); +} + +abstract class MixinBuilder extends Object + with HasAnnotationsBuilder, HasDartDocsBuilder, HasGenericsBuilder + implements Builder { + factory MixinBuilder() = _$MixinBuilder; + + MixinBuilder._(); + + /// Whether the mixin is a `base mixin`. + bool base = false; + + @override + ListBuilder annotations = ListBuilder(); + + @override + ListBuilder docs = ListBuilder(); + + Reference? on; + + ListBuilder implements = ListBuilder(); + + @override + ListBuilder types = ListBuilder(); + + ListBuilder methods = ListBuilder(); + + ListBuilder fields = ListBuilder(); + + /// Name of the mixin. + String? name; +} diff --git a/pkgs/code_builder/lib/src/specs/mixin.g.dart b/pkgs/code_builder/lib/src/specs/mixin.g.dart new file mode 100644 index 000000000..28c7356c4 --- /dev/null +++ b/pkgs/code_builder/lib/src/specs/mixin.g.dart @@ -0,0 +1,294 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'mixin.dart'; + +// ************************************************************************** +// BuiltValueGenerator +// ************************************************************************** + +class _$Mixin extends Mixin { + @override + final bool base; + @override + final BuiltList annotations; + @override + final BuiltList docs; + @override + final Reference? on; + @override + final BuiltList implements; + @override + final BuiltList types; + @override + final BuiltList methods; + @override + final BuiltList fields; + @override + final String name; + + factory _$Mixin([void Function(MixinBuilder)? updates]) => + (new MixinBuilder()..update(updates)).build() as _$Mixin; + + _$Mixin._( + {required this.base, + required this.annotations, + required this.docs, + this.on, + required this.implements, + required this.types, + required this.methods, + required this.fields, + required this.name}) + : super._() { + BuiltValueNullFieldError.checkNotNull(base, r'Mixin', 'base'); + BuiltValueNullFieldError.checkNotNull(annotations, r'Mixin', 'annotations'); + BuiltValueNullFieldError.checkNotNull(docs, r'Mixin', 'docs'); + BuiltValueNullFieldError.checkNotNull(implements, r'Mixin', 'implements'); + BuiltValueNullFieldError.checkNotNull(types, r'Mixin', 'types'); + BuiltValueNullFieldError.checkNotNull(methods, r'Mixin', 'methods'); + BuiltValueNullFieldError.checkNotNull(fields, r'Mixin', 'fields'); + BuiltValueNullFieldError.checkNotNull(name, r'Mixin', 'name'); + } + + @override + Mixin rebuild(void Function(MixinBuilder) updates) => + (toBuilder()..update(updates)).build(); + + @override + _$MixinBuilder toBuilder() => new _$MixinBuilder()..replace(this); + + @override + bool operator ==(Object other) { + if (identical(other, this)) return true; + return other is Mixin && + base == other.base && + annotations == other.annotations && + docs == other.docs && + on == other.on && + implements == other.implements && + types == other.types && + methods == other.methods && + fields == other.fields && + name == other.name; + } + + @override + int get hashCode { + var _$hash = 0; + _$hash = $jc(_$hash, base.hashCode); + _$hash = $jc(_$hash, annotations.hashCode); + _$hash = $jc(_$hash, docs.hashCode); + _$hash = $jc(_$hash, on.hashCode); + _$hash = $jc(_$hash, implements.hashCode); + _$hash = $jc(_$hash, types.hashCode); + _$hash = $jc(_$hash, methods.hashCode); + _$hash = $jc(_$hash, fields.hashCode); + _$hash = $jc(_$hash, name.hashCode); + _$hash = $jf(_$hash); + return _$hash; + } + + @override + String toString() { + return (newBuiltValueToStringHelper(r'Mixin') + ..add('base', base) + ..add('annotations', annotations) + ..add('docs', docs) + ..add('on', on) + ..add('implements', implements) + ..add('types', types) + ..add('methods', methods) + ..add('fields', fields) + ..add('name', name)) + .toString(); + } +} + +class _$MixinBuilder extends MixinBuilder { + _$Mixin? _$v; + + @override + bool get base { + _$this; + return super.base; + } + + @override + set base(bool base) { + _$this; + super.base = base; + } + + @override + ListBuilder get annotations { + _$this; + return super.annotations; + } + + @override + set annotations(ListBuilder annotations) { + _$this; + super.annotations = annotations; + } + + @override + ListBuilder get docs { + _$this; + return super.docs; + } + + @override + set docs(ListBuilder docs) { + _$this; + super.docs = docs; + } + + @override + Reference? get on { + _$this; + return super.on; + } + + @override + set on(Reference? on) { + _$this; + super.on = on; + } + + @override + ListBuilder get implements { + _$this; + return super.implements; + } + + @override + set implements(ListBuilder implements) { + _$this; + super.implements = implements; + } + + @override + ListBuilder get types { + _$this; + return super.types; + } + + @override + set types(ListBuilder types) { + _$this; + super.types = types; + } + + @override + ListBuilder get methods { + _$this; + return super.methods; + } + + @override + set methods(ListBuilder methods) { + _$this; + super.methods = methods; + } + + @override + ListBuilder get fields { + _$this; + return super.fields; + } + + @override + set fields(ListBuilder fields) { + _$this; + super.fields = fields; + } + + @override + String? get name { + _$this; + return super.name; + } + + @override + set name(String? name) { + _$this; + super.name = name; + } + + _$MixinBuilder() : super._(); + + MixinBuilder get _$this { + final $v = _$v; + if ($v != null) { + super.base = $v.base; + super.annotations = $v.annotations.toBuilder(); + super.docs = $v.docs.toBuilder(); + super.on = $v.on; + super.implements = $v.implements.toBuilder(); + super.types = $v.types.toBuilder(); + super.methods = $v.methods.toBuilder(); + super.fields = $v.fields.toBuilder(); + super.name = $v.name; + _$v = null; + } + return this; + } + + @override + void replace(Mixin other) { + ArgumentError.checkNotNull(other, 'other'); + _$v = other as _$Mixin; + } + + @override + void update(void Function(MixinBuilder)? updates) { + if (updates != null) updates(this); + } + + @override + Mixin build() => _build(); + + _$Mixin _build() { + _$Mixin _$result; + try { + _$result = _$v ?? + new _$Mixin._( + base: + BuiltValueNullFieldError.checkNotNull(base, r'Mixin', 'base'), + annotations: annotations.build(), + docs: docs.build(), + on: on, + implements: implements.build(), + types: types.build(), + methods: methods.build(), + fields: fields.build(), + name: BuiltValueNullFieldError.checkNotNull( + name, r'Mixin', 'name')); + } catch (_) { + late String _$failedField; + try { + _$failedField = 'annotations'; + annotations.build(); + _$failedField = 'docs'; + docs.build(); + + _$failedField = 'implements'; + implements.build(); + _$failedField = 'types'; + types.build(); + _$failedField = 'methods'; + methods.build(); + _$failedField = 'fields'; + fields.build(); + } catch (e) { + throw new BuiltValueNestedFieldError( + r'Mixin', _$failedField, e.toString()); + } + rethrow; + } + replace(_$result); + return _$result; + } +} + +// ignore_for_file: deprecated_member_use_from_same_package,type=lint diff --git a/pkgs/code_builder/lib/src/specs/reference.dart b/pkgs/code_builder/lib/src/specs/reference.dart new file mode 100644 index 000000000..06032ad3c --- /dev/null +++ b/pkgs/code_builder/lib/src/specs/reference.dart @@ -0,0 +1,120 @@ +// 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. + +import 'package:built_value/built_value.dart'; +import 'package:meta/meta.dart'; + +import '../base.dart'; +import '../visitors.dart'; +import 'code.dart'; +import 'expression.dart'; +import 'type_reference.dart'; + +/// Short-hand for `Reference(symbol, url)`. +Reference refer(String symbol, [String? url]) => Reference(symbol, url); + +/// A reference to [symbol], such as a class, or top-level method or field. +/// +/// References can be collected and collated in order to automatically generate +/// `import` statements for all used symbols. +@immutable +class Reference extends Expression implements Spec { + /// Relative, `package:` or `dart:` URL of the library. + /// + /// May be omitted (`null`) in order to express "same library". + final String? url; + + /// Name of the class, method, or field. + /// + /// May be `null` for references without symbols, for instance a function type + /// has no symbol. + final String? symbol; + + /// Create a reference to [symbol] in [url]. + const Reference(this.symbol, [this.url]); + + @override + R accept( + SpecVisitor visitor, [ + R? context, + ]) => + visitor.visitReference(this, context); + + @override + int get hashCode => '$url#$symbol'.hashCode; + + @override + bool operator ==(Object other) => + other is Reference && other.url == url && other.symbol == symbol; + + /// Returns a new instance of this expression. + Expression newInstance( + Iterable positionalArguments, [ + Map namedArguments = const {}, + List typeArguments = const [], + ]) => + InvokeExpression.newOf( + this, + positionalArguments.toList(), + namedArguments, + typeArguments, + ); + + /// Returns a new instance of this expression with a named constructor. + Expression newInstanceNamed( + String name, + Iterable positionalArguments, [ + Map namedArguments = const {}, + List typeArguments = const [], + ]) => + InvokeExpression.newOf( + this, + positionalArguments.toList(), + namedArguments, + typeArguments, + name, + ); + + /// Returns a const instance of this expression. + Expression constInstance( + Iterable positionalArguments, [ + Map namedArguments = const {}, + List typeArguments = const [], + ]) => + InvokeExpression.constOf( + this, + positionalArguments.toList(), + namedArguments, + typeArguments, + ); + + /// Returns a const instance of this expression with a named constructor. + Expression constInstanceNamed( + String name, + Iterable positionalArguments, [ + Map namedArguments = const {}, + List typeArguments = const [], + ]) => + InvokeExpression.constOf( + this, + positionalArguments.toList(), + namedArguments, + typeArguments, + name, + ); + + @override + Expression get expression => CodeExpression(Code.scope((a) => a(this))); + + @override + String toString() => (newBuiltValueToStringHelper('Reference') + ..add('url', url) + ..add('symbol', symbol)) + .toString(); + + /// Returns as a [TypeReference], which allows adding generic type parameters. + Reference get type => TypeReference((b) => b + ..url = url + ..symbol = symbol); +} diff --git a/pkgs/code_builder/lib/src/specs/type_function.dart b/pkgs/code_builder/lib/src/specs/type_function.dart new file mode 100644 index 000000000..be7c3eacc --- /dev/null +++ b/pkgs/code_builder/lib/src/specs/type_function.dart @@ -0,0 +1,133 @@ +// 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. + +import 'package:built_collection/built_collection.dart'; +import 'package:built_value/built_value.dart'; +import 'package:meta/meta.dart'; + +import '../base.dart'; +import '../mixins/generics.dart'; +import '../visitors.dart'; +import 'code.dart'; +import 'expression.dart'; +import 'reference.dart'; + +part 'type_function.g.dart'; + +@immutable +abstract class FunctionType extends Expression + with HasGenerics + implements Built, Reference, Spec { + factory FunctionType([ + void Function(FunctionTypeBuilder) updates, + ]) = _$FunctionType; + + FunctionType._(); + + @override + R accept( + SpecVisitor visitor, [ + R? context, + ]) => + visitor.visitFunctionType(this, context); + + /// Return type. + Reference? get returnType; + + @override + BuiltList get types; + + /// Required positional parameters of this function type. + BuiltList get requiredParameters; + + /// Optional positional parameters of this function type. + BuiltList get optionalParameters; + + /// Named optional parameters of this function type. + BuiltMap get namedParameters; + + /// Named required parameters of this function type. + BuiltMap get namedRequiredParameters; + + @override + String? get url => null; + + @override + String? get symbol => null; + + @override + Reference get type => this; + + /// Optional nullability. + /// + /// An emitter may ignore this if the output is not targeting a Dart language + /// version that supports null safety. + bool? get isNullable; + + @override + Expression newInstance( + Iterable positionalArguments, [ + Map namedArguments = const {}, + List typeArguments = const [], + ]) => + throw UnsupportedError('Cannot instantiate a function type.'); + + @override + Expression newInstanceNamed( + String name, + Iterable positionalArguments, [ + Map namedArguments = const {}, + List typeArguments = const [], + ]) => + throw UnsupportedError('Cannot instantiate a function type.'); + + @override + Expression constInstance( + Iterable positionalArguments, [ + Map namedArguments = const {}, + List typeArguments = const [], + ]) => + throw UnsupportedError('Cannot "const" a function type.'); + + @override + Expression constInstanceNamed( + String name, + Iterable positionalArguments, [ + Map namedArguments = const {}, + List typeArguments = const [], + ]) => + throw UnsupportedError('Cannot "const" a function type.'); + + /// A typedef assignment to this type. + Code toTypeDef(String name) => createTypeDef(name, this); +} + +abstract class FunctionTypeBuilder extends Object + with HasGenericsBuilder + implements Builder { + factory FunctionTypeBuilder() = _$FunctionTypeBuilder; + + FunctionTypeBuilder._(); + + Reference? returnType; + + @override + ListBuilder types = ListBuilder(); + + ListBuilder requiredParameters = ListBuilder(); + + ListBuilder optionalParameters = ListBuilder(); + + MapBuilder namedParameters = + MapBuilder(); + + MapBuilder namedRequiredParameters = + MapBuilder(); + + bool? isNullable; + + String? url; + + String? symbol; +} diff --git a/pkgs/code_builder/lib/src/specs/type_function.g.dart b/pkgs/code_builder/lib/src/specs/type_function.g.dart new file mode 100644 index 000000000..d09f59b03 --- /dev/null +++ b/pkgs/code_builder/lib/src/specs/type_function.g.dart @@ -0,0 +1,252 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'type_function.dart'; + +// ************************************************************************** +// BuiltValueGenerator +// ************************************************************************** + +class _$FunctionType extends FunctionType { + @override + final Reference? returnType; + @override + final BuiltList types; + @override + final BuiltList requiredParameters; + @override + final BuiltList optionalParameters; + @override + final BuiltMap namedParameters; + @override + final BuiltMap namedRequiredParameters; + @override + final bool? isNullable; + + factory _$FunctionType([void Function(FunctionTypeBuilder)? updates]) => + (new FunctionTypeBuilder()..update(updates)).build() as _$FunctionType; + + _$FunctionType._( + {this.returnType, + required this.types, + required this.requiredParameters, + required this.optionalParameters, + required this.namedParameters, + required this.namedRequiredParameters, + this.isNullable}) + : super._() { + BuiltValueNullFieldError.checkNotNull(types, r'FunctionType', 'types'); + BuiltValueNullFieldError.checkNotNull( + requiredParameters, r'FunctionType', 'requiredParameters'); + BuiltValueNullFieldError.checkNotNull( + optionalParameters, r'FunctionType', 'optionalParameters'); + BuiltValueNullFieldError.checkNotNull( + namedParameters, r'FunctionType', 'namedParameters'); + BuiltValueNullFieldError.checkNotNull( + namedRequiredParameters, r'FunctionType', 'namedRequiredParameters'); + } + + @override + FunctionType rebuild(void Function(FunctionTypeBuilder) updates) => + (toBuilder()..update(updates)).build(); + + @override + _$FunctionTypeBuilder toBuilder() => + new _$FunctionTypeBuilder()..replace(this); + + @override + bool operator ==(Object other) { + if (identical(other, this)) return true; + return other is FunctionType && + returnType == other.returnType && + types == other.types && + requiredParameters == other.requiredParameters && + optionalParameters == other.optionalParameters && + namedParameters == other.namedParameters && + namedRequiredParameters == other.namedRequiredParameters && + isNullable == other.isNullable; + } + + @override + int get hashCode { + var _$hash = 0; + _$hash = $jc(_$hash, returnType.hashCode); + _$hash = $jc(_$hash, types.hashCode); + _$hash = $jc(_$hash, requiredParameters.hashCode); + _$hash = $jc(_$hash, optionalParameters.hashCode); + _$hash = $jc(_$hash, namedParameters.hashCode); + _$hash = $jc(_$hash, namedRequiredParameters.hashCode); + _$hash = $jc(_$hash, isNullable.hashCode); + _$hash = $jf(_$hash); + return _$hash; + } + + @override + String toString() { + return (newBuiltValueToStringHelper(r'FunctionType') + ..add('returnType', returnType) + ..add('types', types) + ..add('requiredParameters', requiredParameters) + ..add('optionalParameters', optionalParameters) + ..add('namedParameters', namedParameters) + ..add('namedRequiredParameters', namedRequiredParameters) + ..add('isNullable', isNullable)) + .toString(); + } +} + +class _$FunctionTypeBuilder extends FunctionTypeBuilder { + _$FunctionType? _$v; + + @override + Reference? get returnType { + _$this; + return super.returnType; + } + + @override + set returnType(Reference? returnType) { + _$this; + super.returnType = returnType; + } + + @override + ListBuilder get types { + _$this; + return super.types; + } + + @override + set types(ListBuilder types) { + _$this; + super.types = types; + } + + @override + ListBuilder get requiredParameters { + _$this; + return super.requiredParameters; + } + + @override + set requiredParameters(ListBuilder requiredParameters) { + _$this; + super.requiredParameters = requiredParameters; + } + + @override + ListBuilder get optionalParameters { + _$this; + return super.optionalParameters; + } + + @override + set optionalParameters(ListBuilder optionalParameters) { + _$this; + super.optionalParameters = optionalParameters; + } + + @override + MapBuilder get namedParameters { + _$this; + return super.namedParameters; + } + + @override + set namedParameters(MapBuilder namedParameters) { + _$this; + super.namedParameters = namedParameters; + } + + @override + MapBuilder get namedRequiredParameters { + _$this; + return super.namedRequiredParameters; + } + + @override + set namedRequiredParameters( + MapBuilder namedRequiredParameters) { + _$this; + super.namedRequiredParameters = namedRequiredParameters; + } + + @override + bool? get isNullable { + _$this; + return super.isNullable; + } + + @override + set isNullable(bool? isNullable) { + _$this; + super.isNullable = isNullable; + } + + _$FunctionTypeBuilder() : super._(); + + FunctionTypeBuilder get _$this { + final $v = _$v; + if ($v != null) { + super.returnType = $v.returnType; + super.types = $v.types.toBuilder(); + super.requiredParameters = $v.requiredParameters.toBuilder(); + super.optionalParameters = $v.optionalParameters.toBuilder(); + super.namedParameters = $v.namedParameters.toBuilder(); + super.namedRequiredParameters = $v.namedRequiredParameters.toBuilder(); + super.isNullable = $v.isNullable; + _$v = null; + } + return this; + } + + @override + void replace(FunctionType other) { + ArgumentError.checkNotNull(other, 'other'); + _$v = other as _$FunctionType; + } + + @override + void update(void Function(FunctionTypeBuilder)? updates) { + if (updates != null) updates(this); + } + + @override + FunctionType build() => _build(); + + _$FunctionType _build() { + _$FunctionType _$result; + try { + _$result = _$v ?? + new _$FunctionType._( + returnType: returnType, + types: types.build(), + requiredParameters: requiredParameters.build(), + optionalParameters: optionalParameters.build(), + namedParameters: namedParameters.build(), + namedRequiredParameters: namedRequiredParameters.build(), + isNullable: isNullable); + } catch (_) { + late String _$failedField; + try { + _$failedField = 'types'; + types.build(); + _$failedField = 'requiredParameters'; + requiredParameters.build(); + _$failedField = 'optionalParameters'; + optionalParameters.build(); + _$failedField = 'namedParameters'; + namedParameters.build(); + _$failedField = 'namedRequiredParameters'; + namedRequiredParameters.build(); + } catch (e) { + throw new BuiltValueNestedFieldError( + r'FunctionType', _$failedField, e.toString()); + } + rethrow; + } + replace(_$result); + return _$result; + } +} + +// ignore_for_file: deprecated_member_use_from_same_package,type=lint diff --git a/pkgs/code_builder/lib/src/specs/type_record.dart b/pkgs/code_builder/lib/src/specs/type_record.dart new file mode 100644 index 000000000..2f0594c77 --- /dev/null +++ b/pkgs/code_builder/lib/src/specs/type_record.dart @@ -0,0 +1,99 @@ +// Copyright (c) 2023, 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. + +import 'package:built_collection/built_collection.dart'; +import 'package:built_value/built_value.dart'; +import 'package:meta/meta.dart'; + +import '../base.dart'; +import '../visitors.dart'; +import 'expression.dart'; +import 'reference.dart'; + +part 'type_record.g.dart'; + +@immutable +abstract class RecordType extends Expression + implements Built, Reference, Spec { + factory RecordType([ + void Function(RecordTypeBuilder) updates, + ]) = _$RecordType; + + RecordType._(); + + @override + R accept( + SpecVisitor visitor, [ + R? context, + ]) => + visitor.visitRecordType(this, context); + + BuiltList get positionalFieldTypes; + + BuiltMap get namedFieldTypes; + + @override + String? get url => null; + + @override + String? get symbol => null; + + @override + Reference get type => this; + + /// Optional nullability. + bool? get isNullable; + + @override + Expression newInstance( + Iterable positionalArguments, [ + Map namedArguments = const {}, + List typeArguments = const [], + ]) => + throw UnsupportedError('Cannot instantiate a record type.'); + + @override + Expression newInstanceNamed( + String name, + Iterable positionalArguments, [ + Map namedArguments = const {}, + List typeArguments = const [], + ]) => + throw UnsupportedError('Cannot instantiate a record type.'); + + @override + Expression constInstance( + Iterable positionalArguments, [ + Map namedArguments = const {}, + List typeArguments = const [], + ]) => + throw UnsupportedError('Cannot "const" a record type.'); + + @override + Expression constInstanceNamed( + String name, + Iterable positionalArguments, [ + Map namedArguments = const {}, + List typeArguments = const [], + ]) => + throw UnsupportedError('Cannot "const" a record type.'); +} + +abstract class RecordTypeBuilder extends Object + implements Builder { + factory RecordTypeBuilder() = _$RecordTypeBuilder; + + RecordTypeBuilder._(); + + ListBuilder positionalFieldTypes = ListBuilder(); + + MapBuilder namedFieldTypes = + MapBuilder(); + + bool? isNullable; + + String? url; + + String? symbol; +} diff --git a/pkgs/code_builder/lib/src/specs/type_record.g.dart b/pkgs/code_builder/lib/src/specs/type_record.g.dart new file mode 100644 index 000000000..b1d47dfbd --- /dev/null +++ b/pkgs/code_builder/lib/src/specs/type_record.g.dart @@ -0,0 +1,159 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'type_record.dart'; + +// ************************************************************************** +// BuiltValueGenerator +// ************************************************************************** + +class _$RecordType extends RecordType { + @override + final BuiltList positionalFieldTypes; + @override + final BuiltMap namedFieldTypes; + @override + final bool? isNullable; + + factory _$RecordType([void Function(RecordTypeBuilder)? updates]) => + (new RecordTypeBuilder()..update(updates)).build() as _$RecordType; + + _$RecordType._( + {required this.positionalFieldTypes, + required this.namedFieldTypes, + this.isNullable}) + : super._() { + BuiltValueNullFieldError.checkNotNull( + positionalFieldTypes, r'RecordType', 'positionalFieldTypes'); + BuiltValueNullFieldError.checkNotNull( + namedFieldTypes, r'RecordType', 'namedFieldTypes'); + } + + @override + RecordType rebuild(void Function(RecordTypeBuilder) updates) => + (toBuilder()..update(updates)).build(); + + @override + _$RecordTypeBuilder toBuilder() => new _$RecordTypeBuilder()..replace(this); + + @override + bool operator ==(Object other) { + if (identical(other, this)) return true; + return other is RecordType && + positionalFieldTypes == other.positionalFieldTypes && + namedFieldTypes == other.namedFieldTypes && + isNullable == other.isNullable; + } + + @override + int get hashCode { + var _$hash = 0; + _$hash = $jc(_$hash, positionalFieldTypes.hashCode); + _$hash = $jc(_$hash, namedFieldTypes.hashCode); + _$hash = $jc(_$hash, isNullable.hashCode); + _$hash = $jf(_$hash); + return _$hash; + } + + @override + String toString() { + return (newBuiltValueToStringHelper(r'RecordType') + ..add('positionalFieldTypes', positionalFieldTypes) + ..add('namedFieldTypes', namedFieldTypes) + ..add('isNullable', isNullable)) + .toString(); + } +} + +class _$RecordTypeBuilder extends RecordTypeBuilder { + _$RecordType? _$v; + + @override + ListBuilder get positionalFieldTypes { + _$this; + return super.positionalFieldTypes; + } + + @override + set positionalFieldTypes(ListBuilder positionalFieldTypes) { + _$this; + super.positionalFieldTypes = positionalFieldTypes; + } + + @override + MapBuilder get namedFieldTypes { + _$this; + return super.namedFieldTypes; + } + + @override + set namedFieldTypes(MapBuilder namedFieldTypes) { + _$this; + super.namedFieldTypes = namedFieldTypes; + } + + @override + bool? get isNullable { + _$this; + return super.isNullable; + } + + @override + set isNullable(bool? isNullable) { + _$this; + super.isNullable = isNullable; + } + + _$RecordTypeBuilder() : super._(); + + RecordTypeBuilder get _$this { + final $v = _$v; + if ($v != null) { + super.positionalFieldTypes = $v.positionalFieldTypes.toBuilder(); + super.namedFieldTypes = $v.namedFieldTypes.toBuilder(); + super.isNullable = $v.isNullable; + _$v = null; + } + return this; + } + + @override + void replace(RecordType other) { + ArgumentError.checkNotNull(other, 'other'); + _$v = other as _$RecordType; + } + + @override + void update(void Function(RecordTypeBuilder)? updates) { + if (updates != null) updates(this); + } + + @override + RecordType build() => _build(); + + _$RecordType _build() { + _$RecordType _$result; + try { + _$result = _$v ?? + new _$RecordType._( + positionalFieldTypes: positionalFieldTypes.build(), + namedFieldTypes: namedFieldTypes.build(), + isNullable: isNullable); + } catch (_) { + late String _$failedField; + try { + _$failedField = 'positionalFieldTypes'; + positionalFieldTypes.build(); + _$failedField = 'namedFieldTypes'; + namedFieldTypes.build(); + } catch (e) { + throw new BuiltValueNestedFieldError( + r'RecordType', _$failedField, e.toString()); + } + rethrow; + } + replace(_$result); + return _$result; + } +} + +// ignore_for_file: deprecated_member_use_from_same_package,type=lint diff --git a/pkgs/code_builder/lib/src/specs/type_reference.dart b/pkgs/code_builder/lib/src/specs/type_reference.dart new file mode 100644 index 000000000..74c7840a3 --- /dev/null +++ b/pkgs/code_builder/lib/src/specs/type_reference.dart @@ -0,0 +1,138 @@ +// 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. + +import 'package:built_collection/built_collection.dart'; +import 'package:built_value/built_value.dart'; +import 'package:meta/meta.dart'; + +import '../base.dart'; +import '../mixins/generics.dart'; +import '../visitors.dart'; +import 'code.dart'; +import 'expression.dart'; +import 'reference.dart'; + +part 'type_reference.g.dart'; + +@immutable +abstract class TypeReference extends Expression + with HasGenerics + implements Built, Reference, Spec { + factory TypeReference([ + void Function(TypeReferenceBuilder) updates, + ]) = _$TypeReference; + + TypeReference._(); + + @override + String get symbol; + + @override + String? get url; + + /// Optional bound generic. + Reference? get bound; + + @override + BuiltList get types; + + /// Optional nullability. + /// + /// An emitter may ignore this if the output is not targeting a Dart language + /// version that supports null safety. + bool? get isNullable; + + @override + R accept( + SpecVisitor visitor, [ + R? context, + ]) => + visitor.visitType(this, context); + + @override + Expression get expression => CodeExpression(Code.scope((a) => a(this))); + + @override + TypeReference get type => this; + + @override + Expression newInstance( + Iterable positionalArguments, [ + Map namedArguments = const {}, + List typeArguments = const [], + ]) => + InvokeExpression.newOf( + this, + positionalArguments.toList(), + namedArguments, + typeArguments, + ); + + @override + Expression newInstanceNamed( + String name, + Iterable positionalArguments, [ + Map namedArguments = const {}, + List typeArguments = const [], + ]) => + InvokeExpression.newOf( + this, + positionalArguments.toList(), + namedArguments, + typeArguments, + name, + ); + + @override + Expression constInstance( + Iterable positionalArguments, [ + Map namedArguments = const {}, + List typeArguments = const [], + ]) => + InvokeExpression.constOf( + this, + positionalArguments.toList(), + namedArguments, + typeArguments, + ); + + @override + Expression constInstanceNamed( + String name, + Iterable positionalArguments, [ + Map namedArguments = const {}, + List typeArguments = const [], + ]) => + InvokeExpression.constOf( + this, + positionalArguments.toList(), + namedArguments, + typeArguments, + name, + ); +} + +abstract class TypeReferenceBuilder extends Object + with HasGenericsBuilder + implements Builder { + factory TypeReferenceBuilder() = _$TypeReferenceBuilder; + + TypeReferenceBuilder._(); + + String? symbol; + + String? url; + + /// Optional bound generic. + Reference? bound; + + @override + ListBuilder types = ListBuilder(); + + /// Optional nullability. + /// + /// An emitter may ignore this if the output is not targeting a Dart language + /// version that supports null safety. + bool? isNullable; +} diff --git a/pkgs/code_builder/lib/src/specs/type_reference.g.dart b/pkgs/code_builder/lib/src/specs/type_reference.g.dart new file mode 100644 index 000000000..124e8b4f0 --- /dev/null +++ b/pkgs/code_builder/lib/src/specs/type_reference.g.dart @@ -0,0 +1,197 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'type_reference.dart'; + +// ************************************************************************** +// BuiltValueGenerator +// ************************************************************************** + +class _$TypeReference extends TypeReference { + @override + final String symbol; + @override + final String? url; + @override + final Reference? bound; + @override + final BuiltList types; + @override + final bool? isNullable; + + factory _$TypeReference([void Function(TypeReferenceBuilder)? updates]) => + (new TypeReferenceBuilder()..update(updates)).build() as _$TypeReference; + + _$TypeReference._( + {required this.symbol, + this.url, + this.bound, + required this.types, + this.isNullable}) + : super._() { + BuiltValueNullFieldError.checkNotNull(symbol, r'TypeReference', 'symbol'); + BuiltValueNullFieldError.checkNotNull(types, r'TypeReference', 'types'); + } + + @override + TypeReference rebuild(void Function(TypeReferenceBuilder) updates) => + (toBuilder()..update(updates)).build(); + + @override + _$TypeReferenceBuilder toBuilder() => + new _$TypeReferenceBuilder()..replace(this); + + @override + bool operator ==(Object other) { + if (identical(other, this)) return true; + return other is TypeReference && + symbol == other.symbol && + url == other.url && + bound == other.bound && + types == other.types && + isNullable == other.isNullable; + } + + @override + int get hashCode { + var _$hash = 0; + _$hash = $jc(_$hash, symbol.hashCode); + _$hash = $jc(_$hash, url.hashCode); + _$hash = $jc(_$hash, bound.hashCode); + _$hash = $jc(_$hash, types.hashCode); + _$hash = $jc(_$hash, isNullable.hashCode); + _$hash = $jf(_$hash); + return _$hash; + } + + @override + String toString() { + return (newBuiltValueToStringHelper(r'TypeReference') + ..add('symbol', symbol) + ..add('url', url) + ..add('bound', bound) + ..add('types', types) + ..add('isNullable', isNullable)) + .toString(); + } +} + +class _$TypeReferenceBuilder extends TypeReferenceBuilder { + _$TypeReference? _$v; + + @override + String? get symbol { + _$this; + return super.symbol; + } + + @override + set symbol(String? symbol) { + _$this; + super.symbol = symbol; + } + + @override + String? get url { + _$this; + return super.url; + } + + @override + set url(String? url) { + _$this; + super.url = url; + } + + @override + Reference? get bound { + _$this; + return super.bound; + } + + @override + set bound(Reference? bound) { + _$this; + super.bound = bound; + } + + @override + ListBuilder get types { + _$this; + return super.types; + } + + @override + set types(ListBuilder types) { + _$this; + super.types = types; + } + + @override + bool? get isNullable { + _$this; + return super.isNullable; + } + + @override + set isNullable(bool? isNullable) { + _$this; + super.isNullable = isNullable; + } + + _$TypeReferenceBuilder() : super._(); + + TypeReferenceBuilder get _$this { + final $v = _$v; + if ($v != null) { + super.symbol = $v.symbol; + super.url = $v.url; + super.bound = $v.bound; + super.types = $v.types.toBuilder(); + super.isNullable = $v.isNullable; + _$v = null; + } + return this; + } + + @override + void replace(TypeReference other) { + ArgumentError.checkNotNull(other, 'other'); + _$v = other as _$TypeReference; + } + + @override + void update(void Function(TypeReferenceBuilder)? updates) { + if (updates != null) updates(this); + } + + @override + TypeReference build() => _build(); + + _$TypeReference _build() { + _$TypeReference _$result; + try { + _$result = _$v ?? + new _$TypeReference._( + symbol: BuiltValueNullFieldError.checkNotNull( + symbol, r'TypeReference', 'symbol'), + url: url, + bound: bound, + types: types.build(), + isNullable: isNullable); + } catch (_) { + late String _$failedField; + try { + _$failedField = 'types'; + types.build(); + } catch (e) { + throw new BuiltValueNestedFieldError( + r'TypeReference', _$failedField, e.toString()); + } + rethrow; + } + replace(_$result); + return _$result; + } +} + +// ignore_for_file: deprecated_member_use_from_same_package,type=lint diff --git a/pkgs/code_builder/lib/src/specs/typedef.dart b/pkgs/code_builder/lib/src/specs/typedef.dart new file mode 100644 index 000000000..fe5d8f12c --- /dev/null +++ b/pkgs/code_builder/lib/src/specs/typedef.dart @@ -0,0 +1,62 @@ +// Copyright (c) 2022, 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. + +import 'package:built_collection/built_collection.dart'; +import 'package:built_value/built_value.dart'; +import 'package:meta/meta.dart'; + +import '../base.dart'; +import '../mixins/annotations.dart'; +import '../mixins/dartdoc.dart'; +import '../mixins/generics.dart'; +import '../visitors.dart'; +import 'expression.dart'; +import 'reference.dart'; + +part 'typedef.g.dart'; + +@immutable +abstract class TypeDef extends Object + with HasAnnotations, HasDartDocs, HasGenerics + implements Built, Spec { + factory TypeDef([void Function(TypeDefBuilder)? updates]) = _$TypeDef; + + TypeDef._(); + + /// Name of the typedef. + String get name; + + /// The right hand side of the typedef. + /// + /// Typically a reference to a type, or a Function type. + Expression get definition; + + @override + R accept( + SpecVisitor visitor, [ + R? context, + ]) => + visitor.visitTypeDef(this, context); +} + +abstract class TypeDefBuilder extends Object + with HasAnnotationsBuilder, HasDartDocsBuilder, HasGenericsBuilder + implements Builder { + factory TypeDefBuilder() = _$TypeDefBuilder; + + TypeDefBuilder._(); + + @override + ListBuilder annotations = ListBuilder(); + + @override + ListBuilder docs = ListBuilder(); + + @override + ListBuilder types = ListBuilder(); + + String? name; + + Expression? definition; +} diff --git a/pkgs/code_builder/lib/src/specs/typedef.g.dart b/pkgs/code_builder/lib/src/specs/typedef.g.dart new file mode 100644 index 000000000..8c2a16c2d --- /dev/null +++ b/pkgs/code_builder/lib/src/specs/typedef.g.dart @@ -0,0 +1,205 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'typedef.dart'; + +// ************************************************************************** +// BuiltValueGenerator +// ************************************************************************** + +class _$TypeDef extends TypeDef { + @override + final String name; + @override + final Expression definition; + @override + final BuiltList annotations; + @override + final BuiltList docs; + @override + final BuiltList types; + + factory _$TypeDef([void Function(TypeDefBuilder)? updates]) => + (new TypeDefBuilder()..update(updates)).build() as _$TypeDef; + + _$TypeDef._( + {required this.name, + required this.definition, + required this.annotations, + required this.docs, + required this.types}) + : super._() { + BuiltValueNullFieldError.checkNotNull(name, r'TypeDef', 'name'); + BuiltValueNullFieldError.checkNotNull(definition, r'TypeDef', 'definition'); + BuiltValueNullFieldError.checkNotNull( + annotations, r'TypeDef', 'annotations'); + BuiltValueNullFieldError.checkNotNull(docs, r'TypeDef', 'docs'); + BuiltValueNullFieldError.checkNotNull(types, r'TypeDef', 'types'); + } + + @override + TypeDef rebuild(void Function(TypeDefBuilder) updates) => + (toBuilder()..update(updates)).build(); + + @override + _$TypeDefBuilder toBuilder() => new _$TypeDefBuilder()..replace(this); + + @override + bool operator ==(Object other) { + if (identical(other, this)) return true; + return other is TypeDef && + name == other.name && + definition == other.definition && + annotations == other.annotations && + docs == other.docs && + types == other.types; + } + + @override + int get hashCode { + var _$hash = 0; + _$hash = $jc(_$hash, name.hashCode); + _$hash = $jc(_$hash, definition.hashCode); + _$hash = $jc(_$hash, annotations.hashCode); + _$hash = $jc(_$hash, docs.hashCode); + _$hash = $jc(_$hash, types.hashCode); + _$hash = $jf(_$hash); + return _$hash; + } + + @override + String toString() { + return (newBuiltValueToStringHelper(r'TypeDef') + ..add('name', name) + ..add('definition', definition) + ..add('annotations', annotations) + ..add('docs', docs) + ..add('types', types)) + .toString(); + } +} + +class _$TypeDefBuilder extends TypeDefBuilder { + _$TypeDef? _$v; + + @override + String? get name { + _$this; + return super.name; + } + + @override + set name(String? name) { + _$this; + super.name = name; + } + + @override + Expression? get definition { + _$this; + return super.definition; + } + + @override + set definition(Expression? definition) { + _$this; + super.definition = definition; + } + + @override + ListBuilder get annotations { + _$this; + return super.annotations; + } + + @override + set annotations(ListBuilder annotations) { + _$this; + super.annotations = annotations; + } + + @override + ListBuilder get docs { + _$this; + return super.docs; + } + + @override + set docs(ListBuilder docs) { + _$this; + super.docs = docs; + } + + @override + ListBuilder get types { + _$this; + return super.types; + } + + @override + set types(ListBuilder types) { + _$this; + super.types = types; + } + + _$TypeDefBuilder() : super._(); + + TypeDefBuilder get _$this { + final $v = _$v; + if ($v != null) { + super.name = $v.name; + super.definition = $v.definition; + super.annotations = $v.annotations.toBuilder(); + super.docs = $v.docs.toBuilder(); + super.types = $v.types.toBuilder(); + _$v = null; + } + return this; + } + + @override + void replace(TypeDef other) { + ArgumentError.checkNotNull(other, 'other'); + _$v = other as _$TypeDef; + } + + @override + void update(void Function(TypeDefBuilder)? updates) { + if (updates != null) updates(this); + } + + @override + TypeDef build() => _build(); + + _$TypeDef _build() { + _$TypeDef _$result; + try { + _$result = _$v ?? + new _$TypeDef._( + name: BuiltValueNullFieldError.checkNotNull( + name, r'TypeDef', 'name'), + definition: BuiltValueNullFieldError.checkNotNull( + definition, r'TypeDef', 'definition'), + annotations: annotations.build(), + docs: docs.build(), + types: types.build()); + } catch (_) { + late String _$failedField; + try { + _$failedField = 'annotations'; + annotations.build(); + _$failedField = 'docs'; + docs.build(); + _$failedField = 'types'; + types.build(); + } catch (e) { + throw new BuiltValueNestedFieldError( + r'TypeDef', _$failedField, e.toString()); + } + rethrow; + } + replace(_$result); + return _$result; + } +} + +// ignore_for_file: deprecated_member_use_from_same_package,type=lint diff --git a/pkgs/code_builder/lib/src/visitors.dart b/pkgs/code_builder/lib/src/visitors.dart new file mode 100644 index 000000000..10fe0d815 --- /dev/null +++ b/pkgs/code_builder/lib/src/visitors.dart @@ -0,0 +1,64 @@ +// 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. + +import 'package:meta/meta.dart'; + +import 'base.dart'; +import 'specs/class.dart'; +import 'specs/constructor.dart'; +import 'specs/directive.dart'; +import 'specs/enum.dart'; +import 'specs/expression.dart'; +import 'specs/extension.dart'; +import 'specs/extension_type.dart'; +import 'specs/field.dart'; +import 'specs/library.dart'; +import 'specs/method.dart'; +import 'specs/mixin.dart'; +import 'specs/reference.dart'; +import 'specs/type_function.dart'; +import 'specs/type_record.dart'; +import 'specs/type_reference.dart'; +import 'specs/typedef.dart'; + +@optionalTypeArgs +abstract class SpecVisitor { + const SpecVisitor._(); + + T visitAnnotation(Expression spec, [T? context]); + + T visitClass(Class spec, [T? context]); + + T visitMixin(Mixin spec, [T? context]); + + T visitExtension(Extension spec, [T? context]); + + T visitExtensionType(ExtensionType spec, [T? context]); + + T visitEnum(Enum spec, [T? context]); + + T visitConstructor(Constructor spec, String clazz, [T? context]); + + T visitDirective(Directive spec, [T? context]); + + T visitField(Field spec, [T? context]); + + T visitLibrary(Library spec, [T? context]); + + T visitFunctionType(FunctionType spec, [T? context]); + + T visitTypeDef(TypeDef spec, [T? context]); + + T visitMethod(Method spec, [T? context]); + + T visitRecordType(RecordType spec, [T? context]); + + T visitReference(Reference spec, [T? context]); + + T visitSpec(Spec spec, [T? context]); + + T visitType(TypeReference spec, [T? context]); + + T visitTypeParameters(Iterable specs, [T? context]); +} diff --git a/pkgs/code_builder/pubspec.yaml b/pkgs/code_builder/pubspec.yaml new file mode 100644 index 000000000..50302ec4e --- /dev/null +++ b/pkgs/code_builder/pubspec.yaml @@ -0,0 +1,23 @@ +name: code_builder +version: 4.10.1 +description: A fluent, builder-based library for generating valid Dart code. +repository: https://github.com/dart-lang/tools/tree/main/pkgs/code_builder + +environment: + sdk: ^3.5.0 + +dependencies: + built_collection: ^5.0.0 + built_value: ^8.0.0 + collection: ^1.15.0 + matcher: ^0.12.10 + meta: ^1.3.0 + +dev_dependencies: + build: ^2.0.0 + build_runner: ^2.0.3 + built_value_generator: ^8.0.0 + dart_flutter_team_lints: ^3.0.0 + dart_style: ^2.3.7 + source_gen: ^1.0.0 + test: ^1.16.0 diff --git a/pkgs/code_builder/test/allocator_test.dart b/pkgs/code_builder/test/allocator_test.dart new file mode 100644 index 000000000..09f135c56 --- /dev/null +++ b/pkgs/code_builder/test/allocator_test.dart @@ -0,0 +1,53 @@ +// 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. + +import 'package:code_builder/code_builder.dart'; +import 'package:test/test.dart'; + +import 'common.dart'; + +void main() { + useDartfmt(); + + group('Allocator', () { + Allocator allocator; + + test('should return the exact (non-prefixed) symbol', () { + allocator = Allocator(); + expect(allocator.allocate(refer('Foo', 'package:foo')), 'Foo'); + }); + + test('should collect import URLs', () { + allocator = Allocator() + ..allocate(refer('List', 'dart:core')) + ..allocate(refer('LinkedHashMap', 'dart:collection')) + ..allocate(refer('someSymbol')); + expect(allocator.imports.map((d) => d.url), [ + 'dart:core', + 'dart:collection', + ]); + }); + + test('.none should do nothing', () { + allocator = Allocator.none; + expect(allocator.allocate(refer('Foo', 'package:foo')), 'Foo'); + expect(allocator.imports, isEmpty); + }); + + test('.simplePrefixing should add import prefixes', () { + allocator = Allocator.simplePrefixing(); + expect( + allocator.allocate(refer('List', 'dart:core')), + 'List', + ); + expect( + allocator.allocate(refer('LinkedHashMap', 'dart:collection')), + '_i1.LinkedHashMap', + ); + expect(allocator.imports.map((d) => '${d.url} as ${d.as}'), [ + 'dart:collection as _i1', + ]); + }); + }); +} diff --git a/pkgs/code_builder/test/common.dart b/pkgs/code_builder/test/common.dart new file mode 100644 index 000000000..b1db8d025 --- /dev/null +++ b/pkgs/code_builder/test/common.dart @@ -0,0 +1,21 @@ +// 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. + +import 'package:code_builder/code_builder.dart'; +import 'package:dart_style/dart_style.dart'; + +String _format(String source) { + final formatter = DartFormatter( + languageVersion: DartFormatter.latestLanguageVersion, + ); + + try { + return formatter.format(source); + } on FormatterException catch (_) { + return formatter.formatStatement(source); + } +} + +/// Should be invoked in `main()` of every test in `test/**_test.dart`. +void useDartfmt() => EqualsDart.format = _format; diff --git a/pkgs/code_builder/test/const_test.dart b/pkgs/code_builder/test/const_test.dart new file mode 100644 index 000000000..c4d69c61b --- /dev/null +++ b/pkgs/code_builder/test/const_test.dart @@ -0,0 +1,69 @@ +// 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. + +import 'package:code_builder/code_builder.dart'; +import 'package:test/test.dart'; + +import 'common.dart'; + +void main() { + useDartfmt(); + + final constMap = literalConstMap({ + 'list': literalConstList([]), + 'duration': refer('Duration').constInstance([]), + }); + + test('expression', () { + expect(constMap, equalsDart(r''' + const {'list': [], 'duration': Duration(), }''')); + }); + + test('assignConst', () { + expect( + // ignore: deprecated_member_use_from_same_package + constMap.assignConst('constField'), + equalsDart(r''' + const constField = {'list': [], 'duration': Duration(), }''', + DartEmitter.scoped()), + ); + }); + + test('assign to declared constant', () { + expect( + declareConst('constField').assign(constMap), + equalsDart(r''' + const constField = {'list': [], 'duration': Duration(), }''', + DartEmitter.scoped()), + ); + }); + + test('assign to declared non-constant', () { + expect( + declareVar('varField').assign(constMap), + equalsDart(r''' + var varField = const {'list': [], 'duration': Duration(), }''', + DartEmitter.scoped())); + }); + + final library = Library((b) => b + ..body.add(Field((b) => b + ..name = 'val1' + ..modifier = FieldModifier.constant + ..assignment = refer('ConstClass').constInstance([]).code)) + ..body.add(Field((b) => b + ..name = 'val2' + ..modifier = FieldModifier.constant + ..assignment = + refer('ConstClass').constInstanceNamed('other', []).code))); + + test('should emit a source file with imports in defined order', () { + expect( + library, + equalsDart(r''' + const val1 = ConstClass(); + const val2 = ConstClass.other();'''), + ); + }); +} diff --git a/pkgs/code_builder/test/directive_test.dart b/pkgs/code_builder/test/directive_test.dart new file mode 100644 index 000000000..0aee6ae57 --- /dev/null +++ b/pkgs/code_builder/test/directive_test.dart @@ -0,0 +1,81 @@ +// 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. + +import 'package:code_builder/code_builder.dart'; +import 'package:test/test.dart'; + +import 'common.dart'; + +void main() { + useDartfmt(); + + final $LinkedHashMap = refer('LinkedHashMap', 'dart:collection'); + + final library = Library((b) => b + ..directives.add(Directive.export('../relative.dart')) + ..directives.add(Directive.export('package:foo/foo.dart')) + ..directives.add(Directive.part('lib.g.dart')) + ..body.add(Field((b) => b + ..name = 'relativeRef' + ..modifier = FieldModifier.final$ + ..assignment = + refer('Relative', '../relative.dart').newInstance([]).code)) + ..body.add(Field((b) => b + ..name = 'pkgRefFoo' + ..modifier = FieldModifier.final$ + ..assignment = refer('Foo', 'package:foo/foo.dart').newInstance([]).code)) + ..body.add(Field((b) => b + ..name = 'pkgRefBar' + ..modifier = FieldModifier.final$ + ..assignment = refer('Bar', 'package:foo/bar.dart').newInstance([]).code)) + ..body.add(Field((b) => b + ..name = 'collectionRef' + ..modifier = FieldModifier.final$ + ..assignment = $LinkedHashMap.newInstance([]).code))); + + test('should emit a source file with imports in defined order', () { + expect( + library, + equalsDart(r''' + // ignore_for_file: no_leading_underscores_for_library_prefixes + import '../relative.dart' as _i1; + import 'package:foo/foo.dart' as _i2; + import 'package:foo/bar.dart' as _i3; + import 'dart:collection' as _i4; + export '../relative.dart'; + export 'package:foo/foo.dart'; + part 'lib.g.dart'; + + final relativeRef = _i1.Relative(); + final pkgRefFoo = _i2.Foo(); + final pkgRefBar = _i3.Bar(); + final collectionRef = _i4.LinkedHashMap();''', DartEmitter.scoped()), + ); + }); + + test('should emit a source file with ordered', () { + expect( + library, + equalsDart(r''' + // ignore_for_file: no_leading_underscores_for_library_prefixes + import 'dart:collection' as _i4; + + import 'package:foo/bar.dart' as _i3; + import 'package:foo/foo.dart' as _i2; + + import '../relative.dart' as _i1; + + export 'package:foo/foo.dart'; + export '../relative.dart'; + + part 'lib.g.dart'; + + final relativeRef = _i1.Relative(); + final pkgRefFoo = _i2.Foo(); + final pkgRefBar = _i3.Bar(); + final collectionRef = _i4.LinkedHashMap();''', + DartEmitter.scoped(orderDirectives: true)), + ); + }); +} diff --git a/pkgs/code_builder/test/e2e/injection_test.dart b/pkgs/code_builder/test/e2e/injection_test.dart new file mode 100644 index 000000000..129f02cd1 --- /dev/null +++ b/pkgs/code_builder/test/e2e/injection_test.dart @@ -0,0 +1,67 @@ +// 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. + +import 'package:code_builder/code_builder.dart'; +import 'package:test/test.dart'; + +import '../common.dart'; + +void main() { + useDartfmt(); + + test('should generate a complex generated file', () { + // Imports from an existing Dart library. + final $App = refer('App', 'package:app/app.dart'); + final $Module = refer('Module', 'package:app/module.dart'); + final $Thing = refer('Thing', 'package:app/thing.dart'); + + final clazz = ClassBuilder() + ..name = 'Injector' + ..implements.add($App) + ..fields.add(Field((b) => b + ..modifier = FieldModifier.final$ + ..name = '_module' + ..type = $Module.type)) + ..constructors.add(Constructor((b) => b + ..requiredParameters.add(Parameter((b) => b + ..name = '_module' + ..toThis = true)))) + ..methods.add(Method((b) => b + ..name = 'getThing' + ..body = $Thing.newInstance([ + refer('_module').property('get1').call([]), + refer('_module').property('get2').call([]), + ]).code + ..returns = $Thing + ..annotations.add(refer('override')))); + + expect( + clazz.build(), + equalsDart(r''' + class Injector implements App { + Injector(this._module); + + final Module _module; + + @override + Thing getThing() => Thing(_module.get1(), _module.get2(), ); + } + '''), + ); + + expect( + clazz.build(), + equalsDart(r''' + class Injector implements _i1.App { + Injector(this._module); + + final _i2.Module _module; + + @override + _i3.Thing getThing() => _i3.Thing(_module.get1(), _module.get2(), ); + } + ''', DartEmitter(allocator: Allocator.simplePrefixing())), + ); + }); +} diff --git a/pkgs/code_builder/test/matcher_test.dart b/pkgs/code_builder/test/matcher_test.dart new file mode 100644 index 000000000..c9475a847 --- /dev/null +++ b/pkgs/code_builder/test/matcher_test.dart @@ -0,0 +1,30 @@ +// Copyright (c) 2020, 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. + +import 'package:code_builder/code_builder.dart'; +import 'package:test/test.dart'; + +void main() { + test('describes mismatches', () { + const actual = Code('final x=1;'); + equalsDart('final y=2;').expectMismatch(actual, ''' + Expected: final y=2; + Actual: StaticCode: + Which: is different. + Expected: final y=2; + Actual: final x=1; + ^ + Differ at offset 6 +'''); + }); +} + +extension on Matcher { + void expectMismatch(dynamic actual, String mismatch) { + expect( + () => expect(actual, this), + throwsA(isA().having( + (e) => e.message, 'message', equalsIgnoringWhitespace(mismatch)))); + } +} diff --git a/pkgs/code_builder/test/specs/class_test.dart b/pkgs/code_builder/test/specs/class_test.dart new file mode 100644 index 000000000..562bca4c6 --- /dev/null +++ b/pkgs/code_builder/test/specs/class_test.dart @@ -0,0 +1,484 @@ +// 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. + +import 'package:code_builder/code_builder.dart'; +import 'package:test/test.dart'; + +import '../common.dart'; + +void main() { + useDartfmt(); + + test('should create a class', () { + expect( + Class((b) => b..name = 'Foo'), + equalsDart(r''' + class Foo {} + '''), + ); + }); + + test('should create an abstract class', () { + expect( + Class((b) => b + ..name = 'Foo' + ..abstract = true), + equalsDart(r''' + abstract class Foo {} + '''), + ); + }); + + test('should create an abstract base class', () { + expect( + Class((b) => b + ..name = 'Foo' + ..abstract = true + ..modifier = ClassModifier.base), + equalsDart(r''' + abstract base class Foo {} + '''), + ); + }); + + test('should create a final class', () { + expect( + Class((b) => b + ..name = 'Foo' + ..modifier = ClassModifier.final$), + equalsDart(r''' + final class Foo {} + '''), + ); + }); + + test('should create a sealed class', () { + expect( + Class((b) => b + ..name = 'Foo' + ..sealed = true), + equalsDart(r''' + sealed class Foo {} + '''), + ); + }); + + test('should create an abstract interface class', () { + expect( + Class((b) => b + ..name = 'Foo' + ..abstract = true + ..modifier = ClassModifier.interface), + equalsDart(r''' + abstract interface class Foo {} + '''), + ); + }); + + test('should create a mixin class', () { + expect( + Class((b) => b + ..name = 'Foo' + ..mixin = true), + equalsDart(r''' + mixin class Foo {} + '''), + ); + }); + + test('should create an abstract mixin class', () { + expect( + Class((b) => b + ..name = 'Foo' + ..abstract = true + ..mixin = true), + equalsDart(r''' + abstract mixin class Foo {} + '''), + ); + }); + + test('should create a base mixin class', () { + expect( + Class((b) => b + ..name = 'Foo' + ..mixin = true + ..modifier = ClassModifier.base), + equalsDart(r''' + base mixin class Foo {} + '''), + ); + }); + + test('should create an abstract base mixin class', () { + expect( + Class((b) => b + ..name = 'Foo' + ..abstract = true + ..mixin = true + ..modifier = ClassModifier.base), + equalsDart(r''' + abstract base mixin class Foo {} + '''), + ); + }); + + test('should create a class with documentations', () { + expect( + Class( + (b) => b + ..name = 'Foo' + ..docs.addAll( + const [ + '/// My favorite class.', + ], + ), + ), + equalsDart(r''' + /// My favorite class. + class Foo {} + '''), + ); + }); + + test('should create a class with annotations', () { + expect( + Class( + (b) => b + ..name = 'Foo' + ..annotations.addAll([ + refer('deprecated'), + refer('Deprecated').call([literalString('This is an old class')]) + ]), + ), + equalsDart(r''' + @deprecated + @Deprecated('This is an old class') + class Foo {} + '''), + ); + }); + + test('should create a class with a generic type', () { + expect( + Class((b) => b + ..name = 'List' + ..types.add(refer('T'))), + equalsDart(r''' + class List {} + '''), + ); + }); + + test('should create a class with multiple generic types', () { + expect( + Class( + (b) => b + ..name = 'Map' + ..types.addAll([ + refer('K'), + refer('V'), + ]), + ), + equalsDart(r''' + class Map {} + '''), + ); + }); + + test('should create a class with a bound generic type', () { + expect( + Class((b) => b + ..name = 'Comparable' + ..types.add(TypeReference((b) => b + ..symbol = 'T' + ..bound = TypeReference((b) => b + ..symbol = 'Comparable' + ..types.add(refer('T').type))))), + equalsDart(r''' + class Comparable> {} + '''), + ); + }); + + test('should create a class extending another class', () { + expect( + Class((b) => b + ..name = 'Foo' + ..extend = TypeReference((b) => b.symbol = 'Bar')), + equalsDart(r''' + class Foo extends Bar {} + '''), + ); + }); + + test('should create a class mixing in another class', () { + expect( + Class((b) => b + ..name = 'Foo' + ..extend = TypeReference((b) => b.symbol = 'Bar') + ..mixins.add(TypeReference((b) => b.symbol = 'Foo'))), + equalsDart(r''' + class Foo extends Bar with Foo {} + '''), + ); + }); + + test('should create a class implementing another class', () { + expect( + Class((b) => b + ..name = 'Foo' + ..extend = TypeReference((b) => b.symbol = 'Bar') + ..implements.add(TypeReference((b) => b.symbol = 'Foo'))), + equalsDart(r''' + class Foo extends Bar implements Foo {} + '''), + ); + }); + + test('should create a class with a constructor', () { + expect( + Class((b) => b + ..name = 'Foo' + ..constructors.add(Constructor())), + equalsDart(r''' + class Foo { + Foo(); + } + '''), + ); + }); + + test('should create a class with a constructor with initializers', () { + expect( + Class( + (b) => b + ..name = 'Foo' + ..constructors.add( + Constructor( + (b) => b + ..initializers.addAll([ + const Code('a = 5'), + const Code('super()'), + ]), + ), + ), + ), + equalsDart(r''' + class Foo { + Foo() : a = 5, super(); + } + '''), + ); + }); + + test('should create a class with a annotated constructor', () { + expect( + Class((b) => b + ..name = 'Foo' + ..constructors + .add(Constructor((b) => b..annotations.add(refer('deprecated'))))), + equalsDart(r''' + class Foo { + @deprecated + Foo(); + } + '''), + ); + }); + + test('should create a class with a named constructor', () { + expect( + Class((b) => b + ..name = 'Foo' + ..constructors.add(Constructor((b) => b..name = 'named'))), + equalsDart(r''' + class Foo { + Foo.named(); + } + '''), + ); + }); + + test('should create a class with a const constructor', () { + expect( + Class((b) => b + ..name = 'Foo' + ..constructors.add(Constructor((b) => b..constant = true))), + equalsDart(r''' + class Foo { + const Foo(); + } + '''), + ); + }); + + test('should create a class with an external constructor', () { + expect( + Class((b) => b + ..name = 'Foo' + ..constructors.add(Constructor((b) => b..external = true))), + equalsDart(r''' + class Foo { + external Foo(); + } + '''), + ); + }); + + test('should create a class with a factory constructor', () { + expect( + Class((b) => b + ..name = 'Foo' + ..constructors.add(Constructor((b) => b + ..factory = true + ..redirect = refer('_Foo')))), + equalsDart(r''' + class Foo { + factory Foo() = _Foo; + } + '''), + ); + }); + + test('should create a class with a const factory constructor', () { + expect( + Class((b) => b + ..name = 'Foo' + ..constructors.add(Constructor((b) => b + ..factory = true + ..constant = true + ..redirect = refer('_Foo')))), + equalsDart(r''' + class Foo { + const factory Foo() = _Foo; + } + '''), + ); + }); + + test('should create a class with a factory lambda constructor', () { + expect( + Class((b) => b + ..name = 'Foo' + ..constructors.add(Constructor((b) => b + ..factory = true + ..lambda = true + ..body = const Code('_Foo()')))), + equalsDart(r''' + class Foo { + factory Foo() => _Foo(); + } + '''), + ); + }); + + test('should create a class with an implicit factory lambda constructor', () { + expect( + Class((b) => b + ..name = 'Foo' + ..constructors.add(Constructor((b) => b + ..factory = true + ..body = refer('_Foo').newInstance([]).code))), + equalsDart(r''' + class Foo { + factory Foo() => _Foo(); + } + '''), + ); + }); + + test('should create a class with a constructor with a body', () { + expect( + Class((b) => b + ..name = 'Foo' + ..constructors.add(Constructor((b) => b + ..factory = true + ..body = const Code('return _Foo();')))), + equalsDart(r''' + class Foo { + factory Foo() { + return _Foo(); + } + } + '''), + ); + }); + + test('should create a class with a constructor with parameters', () { + expect( + Class((b) => b + ..name = 'Foo' + ..constructors.add(Constructor((b) => b + ..requiredParameters.addAll([ + Parameter((b) => b..name = 'a'), + Parameter((b) => b..name = 'b'), + ]) + ..optionalParameters.addAll([ + Parameter((b) => b + ..name = 'c' + ..named = true), + ])))), + equalsDart(r''' + class Foo { + Foo(a, b, {c, }); + } + '''), + ); + }); + + test('should create a class with a constructor+field-formal parameters', () { + expect( + Class((b) => b + ..name = 'Foo' + ..constructors.add(Constructor((b) => b + ..requiredParameters.addAll([ + Parameter((b) => b + ..name = 'a' + ..toThis = true), + Parameter((b) => b + ..name = 'b' + ..toThis = true), + ]) + ..optionalParameters.addAll([ + Parameter((b) => b + ..name = 'c' + ..named = true + ..toThis = true), + ])))), + equalsDart(r''' + class Foo { + Foo(this.a, this.b, {this.c, }); + } + '''), + ); + }); + + test('should create a class with a constructor+super-formal parameters', () { + expect( + Class((b) => b + ..name = 'Foo' + ..constructors.add(Constructor((b) => b + ..requiredParameters.addAll([ + Parameter((b) => b + ..name = 'a' + ..toSuper = true), + Parameter((b) => b + ..name = 'b' + ..toSuper = true), + ]) + ..optionalParameters.addAll([ + Parameter((b) => b + ..name = 'c' + ..named = true + ..toSuper = true), + ])))), + equalsDart(r''' + class Foo { + Foo(super.a, super.b, {super.c, }); + } + '''), + ); + }); +} diff --git a/pkgs/code_builder/test/specs/code/expression_test.dart b/pkgs/code_builder/test/specs/code/expression_test.dart new file mode 100644 index 000000000..4ce9ebaf5 --- /dev/null +++ b/pkgs/code_builder/test/specs/code/expression_test.dart @@ -0,0 +1,955 @@ +// 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. + +import 'package:code_builder/code_builder.dart'; +import 'package:test/test.dart'; + +import '../../common.dart'; + +void main() { + useDartfmt(); + + test('should emit a simple expression', () { + expect(literalNull, equalsDart('null')); + }); + + group('literal', () { + test('forwards values that are already expressions', () { + expect(literal(refer('foo')), equalsDart('foo')); + expect(literal([refer('foo')]), equalsDart('[foo]')); + }); + group('wraps', () { + test('bool values', () { + expect(literal(true), equalsDart('true')); + }); + test('numeric values', () { + expect(literal(1), equalsDart('1')); + expect(literal(1.0), equalsDart('1.0')); + }); + test('string values', () { + expect(literal('foo'), equalsDart("'foo'")); + }); + test('list values', () { + expect(literal([1]), equalsDart('[1]')); + }); + test('set values', () { + expect(literal({1}), equalsDart('{1}')); + }); + test('map values', () { + expect(literal({'foo': 1}), equalsDart("{'foo': 1}")); + }); + test('null', () { + expect(literal(null), equalsDart('null')); + }); + }); + test('uses `onError` for unhandled types', () { + expect( + literal(Uri.https('google.com'), onError: (value) { + if (value is Uri) { + return refer('Uri') + .newInstanceNamed('parse', [literalString(value.toString())]); + } + throw UnsupportedError('Not supported: $value'); + }), + equalsDart("Uri.parse('https://google.com')")); + }); + }); + + test('should emit a String', () { + expect(literalString(r'$monkey'), equalsDart(r"'$monkey'")); + }); + + test('should emit a raw String', () { + expect(literalString(r'$monkey', raw: true), equalsDart(r"r'$monkey'")); + }); + + test('should escape single quotes in a String', () { + expect(literalString(r"don't"), equalsDart(r"'don\'t'")); + }); + + test('does not allow single quote in raw string', () { + expect(() => literalString(r"don't", raw: true), throwsArgumentError); + }); + + test('should escape a newline in a string', () { + expect(literalString('some\nthing'), equalsDart(r"'some\nthing'")); + }); + + test('should emit a && expression', () { + expect(literalTrue.and(literalFalse), equalsDart('true && false')); + }); + + test('should emit a || expression', () { + expect(literalTrue.or(literalFalse), equalsDart('true || false')); + }); + + test('should emit a ! expression', () { + expect(literalTrue.negate(), equalsDart('!true')); + }); + + test('should emit a list', () { + expect(literalList([]), equalsDart('[]')); + }); + + test('should emit a const list', () { + expect(literalConstList([]), equalsDart('const []')); + }); + + test('should emit an explicitly typed list', () { + expect(literalList([], refer('int')), equalsDart('[]')); + }); + + test('should emit a set', () { + // ignore: prefer_collection_literals + expect(literalSet(Set()), equalsDart('{}')); + }); + + test('should emit a const set', () { + // ignore: prefer_collection_literals + expect(literalConstSet(Set()), equalsDart('const {}')); + }); + + test('should emit an explicitly typed set', () { + // ignore: prefer_collection_literals + expect(literalSet(Set(), refer('int')), equalsDart('{}')); + }); + + test('should emit a map', () { + expect(literalMap({}), equalsDart('{}')); + }); + + test('should emit a const map', () { + expect(literalConstMap({}), equalsDart('const {}')); + }); + + test('should emit an explicitly typed map', () { + expect( + literalMap({}, refer('int'), refer('bool')), + equalsDart('{}'), + ); + }); + + test('should emit a map of other literals and expressions', () { + expect( + literalMap({ + 1: 'one', + 2: refer('two'), + refer('three'): 3, + refer('Map').newInstance([]): null, + }), + equalsDart(r"{1: 'one', 2: two, three: 3, Map(): null, }"), + ); + }); + + test('should emit a map with spreads', () { + expect( + literalMap({ + literalSpread(): refer('one'), + 2: refer('two'), + literalNullSafeSpread(): refer('three'), + refer('Map').newInstance([]): null, + }), + equalsDart('{...one, 2: two, ...?three, Map(): null, }'), + ); + }); + + test('should emit a list of other literals and expressions', () { + expect( + literalList([ + [], + // ignore: prefer_collection_literals + Set(), + true, + null, + refer('Map').newInstance([]) + ]), + equalsDart('[[], {}, true, null, Map(), ]'), + ); + }); + + test('can toString a list literal with an expression as a value', () { + expect(literalList([refer('foo')]).toString, isNot(throwsA(anything))); + }); + + test('should emit a set of other literals and expressions', () { + expect( + // ignore: prefer_collection_literals + literalSet([ + [], + // ignore: prefer_collection_literals + Set(), + true, + null, + refer('Map').newInstance([]) + ]), + equalsDart('{[], {}, true, null, Map(), }'), + ); + }); + + test('should emit an empty record', () { + expect(literalRecord([], {}), equalsDart('()')); + }); + + test('should emit a const empty record', () { + expect(literalConstRecord([], {}), equalsDart('const ()')); + }); + + test('should emit a record with only positional fields', () { + expect(literalRecord([1, ''], {}), equalsDart("(1, '')")); + }); + + test('should correctly emit a record with a single positional field', () { + expect(literalRecord([1], {}), equalsDart('(1,)')); + }); + + test('should emit a record with only named fields', () { + expect(literalRecord([], {'named': 1, 'other': []}), + equalsDart('(named: 1, other: [])')); + }); + + test('should emit a record with both positional and named fields', () { + expect(literalRecord([0], {'x': true, 'y': 0}), + equalsDart('(0, x: true, y: 0)')); + }); + + test('should emit a record of other literals and expressions', () { + expect( + literalRecord([ + 1, + refer('one'), + 'one' + ], { + 'named': refer('Foo').newInstance([literalNum(1)]) + }), + equalsDart("(1, one, 'one', named: Foo(1))")); + }); + + test('should emit a type as an expression', () { + expect(refer('Map'), equalsDart('Map')); + }); + + test('should emit a scoped type as an expression', () { + expect( + refer('Foo', 'package:foo/foo.dart'), + equalsDart( + '_i1.Foo', DartEmitter(allocator: Allocator.simplePrefixing())), + ); + }); + + test('should emit invoking Type()', () { + expect( + refer('Map').newInstance([]), + equalsDart('Map()'), + ); + }); + + test('should emit invoking named constructor', () { + expect( + refer('Foo').newInstanceNamed('bar', []), + equalsDart('Foo.bar()'), + ); + }); + + test('should emit invoking const Type()', () { + expect( + refer('Object').constInstance([]), + equalsDart('const Object()'), + ); + }); + + test('should emit invoking a property accessor', () { + expect(refer('foo').property('bar'), equalsDart('foo.bar')); + }); + + test('should emit invoking a cascade property accessor', () { + expect(refer('foo').cascade('bar'), equalsDart('foo..bar')); + }); + + test('should emit invoking a null safe property accessor', () { + expect(refer('foo').nullSafeProperty('bar'), equalsDart('foo?.bar')); + }); + + test('should emit invoking a method with a single positional argument', () { + expect( + refer('foo').call([ + literal(1), + ]), + equalsDart('foo(1)'), + ); + }); + + test('should emit invoking a method with positional arguments', () { + expect( + refer('foo').call([ + literal(1), + literal(2), + literal(3), + ]), + equalsDart('foo(1, 2, 3, )'), + ); + }); + + test('should emit invoking a method with a single named argument', () { + expect( + refer('foo').call([], { + 'bar': literal(1), + }), + equalsDart('foo(bar: 1)'), + ); + }); + + test('should emit invoking a method with named arguments', () { + expect( + refer('foo').call([], { + 'bar': literal(1), + 'baz': literal(2), + }), + equalsDart('foo(bar: 1, baz: 2, )'), + ); + }); + + test('should emit invoking a method with positional and named arguments', () { + expect( + refer('foo').call([ + literal(1) + ], { + 'bar': literal(2), + 'baz': literal(3), + }), + equalsDart('foo(1, bar: 2, baz: 3, )'), + ); + }); + + test('should emit invoking a method with a single type argument', () { + expect( + refer('foo').call( + [], + {}, + [ + refer('String'), + ], + ), + equalsDart('foo()'), + ); + }); + + test('should emit invoking a method with type arguments', () { + expect( + refer('foo').call( + [], + {}, + [ + refer('String'), + refer('int'), + ], + ), + equalsDart('foo()'), + ); + }); + + test('should emit a function type', () { + expect( + FunctionType((b) => b.returnType = refer('void')), + equalsDart('void Function()'), + ); + }); + + test('should emit a typedef statement', () { + expect( + FunctionType((b) => b.returnType = refer('void')).toTypeDef('Void0'), + equalsDart('typedef Void0 = void Function();'), + ); + }); + + test('should emit a function type with type parameters', () { + expect( + FunctionType((b) => b + ..returnType = refer('T') + ..types.add(refer('T'))), + equalsDart('T Function()'), + ); + }); + + test('should emit a function type a single parameter', () { + expect( + FunctionType((b) => b..requiredParameters.add(refer('String'))), + equalsDart('Function(String)'), + ); + }); + + test('should emit a function type with parameters', () { + expect( + FunctionType((b) => b + ..requiredParameters.add(refer('String')) + ..optionalParameters.add(refer('int'))), + equalsDart('Function(String, [int, ])'), + ); + }); + + test('should emit a function type with named parameters', () { + expect( + FunctionType((b) => b + ..namedParameters.addAll({ + 'x': refer('int'), + 'y': refer('int'), + })), + equalsDart('Function({int x, int y, })'), + ); + }); + + test( + 'should emit a function type with named required and optional parameters', + () { + expect( + FunctionType((b) => b + ..namedRequiredParameters.addAll({ + 'x': refer('int'), + }) + ..namedParameters.addAll({ + 'y': refer('int'), + })), + equalsDart('Function({required int x, int y, })'), + ); + }); + + test('should emit a function type with named required parameters', () { + expect( + FunctionType((b) => b + ..namedRequiredParameters.addAll({ + 'x': refer('int'), + 'y': refer('int'), + })), + equalsDart('Function({required int x, required int y, })'), + ); + }); + + test('should emit a nullable function type in a Null Safety library', () { + final emitter = DartEmitter.scoped(useNullSafetySyntax: true); + expect( + FunctionType((b) => b + ..requiredParameters.add(refer('String')) + ..isNullable = true), + equalsDart('Function(String)?', emitter), + ); + }); + + test('should emit a nullable function type in pre-Null Safety library', () { + expect( + FunctionType((b) => b + ..requiredParameters.add(refer('String')) + ..isNullable = true), + equalsDart('Function(String)'), + ); + }); + + test('should emit a non-nullable function type in a Null Safety library', () { + final emitter = DartEmitter.scoped(useNullSafetySyntax: true); + expect( + FunctionType((b) => b + ..requiredParameters.add(refer('String')) + ..isNullable = false), + equalsDart('Function(String)', emitter), + ); + }); + + test('should emit a non-nullable function type in pre-Null Safety library', + () { + expect( + FunctionType((b) => b + ..requiredParameters.add(refer('String')) + ..isNullable = false), + equalsDart('Function(String)'), + ); + }); + + test('should emit a closure', () { + expect( + refer('map').property('putIfAbsent').call([ + literalString('foo'), + Method((b) => b..body = literalTrue.code).closure, + ]), + equalsDart("map.putIfAbsent('foo', () => true, )"), + ); + }); + + test('should emit a generic closure', () { + expect( + refer('map').property('putIfAbsent').call([ + literalString('foo'), + Method((b) => b + ..types.add(refer('T')) + ..body = literalTrue.code).genericClosure, + ]), + equalsDart("map.putIfAbsent('foo', () => true, )"), + ); + }); + + test('should emit an assignment', () { + expect( + refer('foo').assign(literalTrue), + equalsDart('foo = true'), + ); + }); + + test('should emit an if null assignment', () { + expect( + refer('foo').ifNullThen(literalTrue), + equalsDart('foo ?? true'), + ); + }); + + test('should emit a null check', () { + expect(refer('foo').nullChecked, equalsDart('foo!')); + }); + + test('should emit an if null index operator set', () { + expect( + refer('bar') + .index(literalTrue) + .ifNullThen(literalFalse) + // ignore: deprecated_member_use_from_same_package + .assignVar('foo') + .statement, + equalsDart('var foo = bar[true] ?? false;'), + ); + }); + + test('should emit a null-aware assignment', () { + expect( + refer('foo').assignNullAware(literalTrue), + equalsDart('foo ??= true'), + ); + }); + + test('should emit an index operator', () { + expect( + // ignore: deprecated_member_use_from_same_package + refer('bar').index(literalString('key')).assignVar('foo').statement, + equalsDart("var foo = bar['key'];"), + ); + }); + + test('should emit an index operator set', () { + expect( + refer('bar') + .index(literalString('key')) + .assign(literalFalse) + // ignore: deprecated_member_use_from_same_package + .assignVar('foo') + .statement, + equalsDart("var foo = bar['key'] = false;"), + ); + }); + + test('should emit a null-aware index operator set', () { + expect( + refer('bar') + .index(literalTrue) + .assignNullAware(literalFalse) + // ignore: deprecated_member_use_from_same_package + .assignVar('foo') + .statement, + equalsDart('var foo = bar[true] ??= false;'), + ); + }); + + test('should emit assigning to a var', () { + expect( + // ignore: deprecated_member_use_from_same_package + literalTrue.assignVar('foo'), + equalsDart('var foo = true'), + ); + }); + + test('should emit assigning to a type', () { + expect( + // ignore: deprecated_member_use_from_same_package + literalTrue.assignVar('foo', refer('bool')), + equalsDart('bool foo = true'), + ); + }); + + test('should emit assigning to a final', () { + expect( + // ignore: deprecated_member_use_from_same_package + literalTrue.assignFinal('foo'), + equalsDart('final foo = true'), + ); + }); + + test('should emit assigning to a const', () { + expect( + // ignore: deprecated_member_use_from_same_package + literalTrue.assignConst('foo'), + equalsDart('const foo = true'), + ); + }); + + test('should emit await', () { + expect( + refer('future').awaited, + equalsDart('await future'), + ); + }); + + test('should emit return', () { + expect( + literalNull.returned, + equalsDart('return null'), + ); + }); + + test('should emit spread', () { + expect( + refer('foo').spread, + equalsDart('...foo'), + ); + }); + + test('should emit null safe spread', () { + expect( + refer('foo').nullSafeSpread, + equalsDart('...?foo'), + ); + }); + + test('should emit throw', () { + expect( + literalNull.thrown, + equalsDart('throw null'), + ); + }); + + test('should emit an explicit cast', () { + expect( + refer('foo').asA(refer('String')).property('length'), + equalsDart('(foo as String).length'), + ); + }); + + test('should emit an is check', () { + expect( + refer('foo').isA(refer('String')), + equalsDart('foo is String'), + ); + }); + + test('should emit an is! check', () { + expect( + refer('foo').isNotA(refer('String')), + equalsDart('foo is! String'), + ); + }); + + test('should emit an equality check', () { + expect( + refer('foo').equalTo(literalString('bar')), + equalsDart("foo == 'bar'"), + ); + }); + + test('should emit an inequality check', () { + expect( + refer('foo').notEqualTo(literalString('bar')), + equalsDart("foo != 'bar'"), + ); + }); + + test('should emit an greater than check', () { + expect( + refer('foo').greaterThan(literalString('bar')), + equalsDart("foo > 'bar'"), + ); + }); + + test('should emit an less than check', () { + expect( + refer('foo').lessThan(literalString('bar')), + equalsDart("foo < 'bar'"), + ); + }); + + test('should emit an greater or equals check', () { + expect( + refer('foo').greaterOrEqualTo(literalString('bar')), + equalsDart("foo >= 'bar'"), + ); + }); + + test('should emit an less or equals check', () { + expect( + refer('foo').lessOrEqualTo(literalString('bar')), + equalsDart("foo <= 'bar'"), + ); + }); + + test('should emit a conditional', () { + expect( + refer('foo').conditional(literal(1), literal(2)), + equalsDart('foo ? 1 : 2'), + ); + }); + + test('should emit an operator add call', () { + expect(refer('foo').operatorAdd(refer('foo2')), equalsDart('foo + foo2')); + }); + + test('should emit an operator subtract call', () { + // ignore: deprecated_member_use_from_same_package + expect(refer('foo').operatorSubstract(refer('foo2')), + equalsDart('foo - foo2')); + + expect( + refer('foo').operatorSubtract(refer('foo2')), + equalsDart('foo - foo2'), + ); + }); + + test('should emit an operator divide call', () { + expect( + refer('foo').operatorDivide(refer('foo2')), equalsDart('foo / foo2')); + }); + + test('should emit an operator multiply call', () { + expect( + refer('foo').operatorMultiply(refer('foo2')), equalsDart('foo * foo2')); + }); + + test('should emit an euclidean modulo operator call', () { + expect(refer('foo').operatorEuclideanModulo(refer('foo2')), + equalsDart('foo % foo2')); + }); + + test('should emit an operator int divide call', () { + expect( + refer('foo').operatorIntDivide(refer('foo2')), + equalsDart('foo ~/ foo2'), + ); + }); + + test('should emit a unary prefix increment operator call', () { + expect(refer('foo').operatorUnaryPrefixIncrement(), equalsDart('++foo')); + }); + + test('should emit a unary postfix increment operator call', () { + expect(refer('foo').operatorUnaryPostfixIncrement(), equalsDart('foo++')); + }); + + test('should emit a unary prefix minus operator call', () { + expect(refer('foo').operatorUnaryMinus(), equalsDart('-foo')); + }); + + test('should emit a unary prefix decrement operator call', () { + expect(refer('foo').operatorUnaryPrefixDecrement(), equalsDart('--foo')); + }); + + test('should emit a unary postfix decrement operator call', () { + expect(refer('foo').operatorUnaryPostfixDecrement(), equalsDart('foo--')); + }); + + test('should emit a bitwise AND operator call', () { + expect( + refer('foo').operatorBitwiseAnd(refer('foo2')), + equalsDart('foo & foo2'), + ); + }); + + test('should emit a bitwise OR operator call', () { + expect( + refer('foo').operatorBitwiseOr(refer('foo2')), + equalsDart('foo | foo2'), + ); + }); + + test('should emit a bitwise XOR operator call', () { + expect( + refer('foo').operatorBitwiseXor(refer('foo2')), + equalsDart('foo ^ foo2'), + ); + }); + + test('should emit a unary bitwise complement operator call', () { + expect( + refer('foo').operatorUnaryBitwiseComplement(), + equalsDart('~foo'), + ); + }); + + test('should emit a shift left operator call', () { + expect( + refer('foo').operatorShiftLeft(refer('foo2')), + equalsDart('foo << foo2'), + ); + }); + + test('should emit a shift right operator call', () { + expect( + refer('foo').operatorShiftRight(refer('foo2')), + equalsDart('foo >> foo2'), + ); + }); + + test('should emit a shift right unsigned operator call', () { + expect( + refer('foo').operatorShiftRightUnsigned(refer('foo2')), + equalsDart('foo >>> foo2'), + ); + }); + + test('should emit a const variable declaration', () { + expect(declareConst('foo').assign(refer('bar')), + equalsDart('const foo = bar')); + }); + + test('should emit a typed const variable declaration', () { + expect(declareConst('foo', type: refer('String')).assign(refer('bar')), + equalsDart('const String foo = bar')); + }); + + test('should emit a final variable declaration', () { + expect(declareFinal('foo').assign(refer('bar')), + equalsDart('final foo = bar')); + }); + + test('should emit a typed final variable declaration', () { + expect(declareFinal('foo', type: refer('String')).assign(refer('bar')), + equalsDart('final String foo = bar')); + }); + + test('should emit a nullable typed final variable declaration', () { + final emitter = DartEmitter.scoped(useNullSafetySyntax: true); + expect( + declareFinal('foo', + type: TypeReference((b) => b + ..symbol = 'String' + ..isNullable = true)).assign(refer('bar')), + equalsDart('final String? foo = bar', emitter)); + }, skip: 'https://github.com/dart-lang/code_builder/issues/315'); + + test('should emit a late final variable declaration', () { + expect(declareFinal('foo', late: true).assign(refer('bar')), + equalsDart('late final foo = bar')); + }); + + test('should emit a late typed final variable declaration', () { + expect( + declareFinal('foo', type: refer('String'), late: true) + .assign(refer('bar')), + equalsDart('late final String foo = bar')); + }); + + test('should emit a variable declaration', () { + expect(declareVar('foo').assign(refer('bar')), equalsDart('var foo = bar')); + }); + + test('should emit a typed variable declaration', () { + expect(declareVar('foo', type: refer('String')).assign(refer('bar')), + equalsDart('String foo = bar')); + }); + + test('should emit a late variable declaration', () { + expect(declareVar('foo', late: true).assign(refer('bar')), + equalsDart('late var foo = bar')); + }); + + test('should emit a late typed variable declaration', () { + expect( + declareVar('foo', type: refer('String'), late: true) + .assign(refer('bar')), + equalsDart('late String foo = bar')); + }); + + test('should emit a perenthesized epression', () { + expect( + refer('foo').ifNullThen(refer('FormatException') + .newInstance([literalString('missing foo')]) + .thrown + .parenthesized), + equalsDart('foo ?? (throw FormatException(\'missing foo\'))')); + }); + + test('should emit an addition assigment expression', () { + expect( + refer('foo').addAssign(refer('bar')), + equalsDart('foo += bar'), + ); + }); + + test('should emit a subtraction assigment expression', () { + expect( + refer('foo').subtractAssign(refer('bar')), + equalsDart('foo -= bar'), + ); + }); + + test('should emit a multiplication assigment expression', () { + expect( + refer('foo').multiplyAssign(refer('bar')), + equalsDart('foo *= bar'), + ); + }); + + test('should emit a division assigment expression', () { + expect( + refer('foo').divideAssign(refer('bar')), + equalsDart('foo /= bar'), + ); + }); + + test('should emit an int division assigment expression', () { + expect( + refer('foo').intDivideAssign(refer('bar')), + equalsDart('foo ~/= bar'), + ); + }); + + test('should emit a euclidean modulo assigment expression', () { + expect( + refer('foo').euclideanModuloAssign(refer('bar')), + equalsDart('foo %= bar'), + ); + }); + + test('should emit a shift left assigment expression', () { + expect( + refer('foo').shiftLeftAssign(refer('bar')), + equalsDart('foo <<= bar'), + ); + }); + + test('should emit a shift right assigment expression', () { + expect( + refer('foo').shiftRightAssign(refer('bar')), + equalsDart('foo >>= bar'), + ); + }); + + test('should emit a shift right unsigned assigment expression', () { + expect( + refer('foo').shiftRightUnsignedAssign(refer('bar')), + equalsDart('foo >>>= bar'), + ); + }); + + test('should emit a bitwise AND assigment expression', () { + expect( + refer('foo').bitwiseAndAssign(refer('bar')), + equalsDart('foo &= bar'), + ); + }); + + test('should emit a bitwise XOR assigment expression', () { + expect( + refer('foo').bitwiseXorAssign(refer('bar')), + equalsDart('foo ^= bar'), + ); + }); + + test('should emit a bitwise OR assigment expression', () { + expect( + refer('foo').bitwiseOrAssign(refer('bar')), + equalsDart('foo |= bar'), + ); + }); +} diff --git a/pkgs/code_builder/test/specs/code/statement_test.dart b/pkgs/code_builder/test/specs/code/statement_test.dart new file mode 100644 index 000000000..cd3d53afc --- /dev/null +++ b/pkgs/code_builder/test/specs/code/statement_test.dart @@ -0,0 +1,63 @@ +// 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. + +import 'package:code_builder/code_builder.dart'; +import 'package:test/test.dart'; + +import '../../common.dart'; + +void main() { + useDartfmt(); + + test('should emit a block of code', () { + expect( + Block.of([ + const Code('if (foo) {'), + const Code(' print(true);'), + const Code('}'), + ]), + equalsDart(r''' + if (foo) { + print(true); + } + '''), + ); + }); + + test('should emit a block of code including expressions', () { + expect( + Block.of([ + const Code('if (foo) {'), + refer('print')([literalTrue]).statement, + const Code('}'), + ]), + equalsDart(r''' + if (foo) { + print(true); + } + '''), + ); + }); + + test('should emit a block of code with lazily invoked generators', () { + expect( + Method((b) => b + ..name = 'main' + ..body = Block.of([ + const Code('if ('), + lazyCode(() => refer('foo').code), + const Code(') {'), + refer('print')([literalTrue]).statement, + const Code('}'), + ])), + equalsDart(r''' + main() { + if (foo) { + print(true); + } + } + '''), + ); + }); +} diff --git a/pkgs/code_builder/test/specs/enum_test.dart b/pkgs/code_builder/test/specs/enum_test.dart new file mode 100644 index 000000000..5a0b19a27 --- /dev/null +++ b/pkgs/code_builder/test/specs/enum_test.dart @@ -0,0 +1,442 @@ +// Copyright (c) 2020, 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. + +import 'package:code_builder/code_builder.dart'; +import 'package:test/test.dart'; + +import '../common.dart'; + +void main() { + useDartfmt(); + + test('should create an enum', () { + expect( + Enum((b) => b + ..name = 'E' + ..values.addAll([ + EnumValue((b) => b..name = 'a'), + EnumValue((b) => b..name = 'b'), + ])), + equalsDart(r''' + enum E { + a, + b + } + ''')); + }); + + test('should create an enum with annotations', () { + expect( + Enum((b) => b + ..annotations.addAll([ + refer('deprecated'), + refer('Deprecated').call([literalString('This is an old enum')]) + ]) + ..name = 'V' + ..values.addAll([ + EnumValue((b) => b..name = 'x'), + ])), + equalsDart(r''' + @deprecated + @Deprecated('This is an old enum') + enum V { + x + } + ''')); + }); + + test('should create an enum with annotated values', () { + expect( + Enum((b) => b + ..name = 'Status' + ..values.addAll([ + EnumValue((b) => b + ..name = 'okay' + ..annotations.addAll([ + refer('deprecated'), + refer('Deprecated').call([literalString('use Good instead')]), + ])), + EnumValue((b) => b + ..name = 'good' + ..annotations.addAll([ + refer('JsonKey').call([literalString('good')]) + ])), + ])), + equalsDart(r''' + enum Status { + @deprecated + @Deprecated('use Good instead') + okay, + @JsonKey('good') + good + } + ''')); + }); + + test('should create an enum which mixes in and implements specs', () { + final myEnum = Enum((b) => b + ..name = 'MyEnum' + ..implements.addAll(const [ + Reference('InterfaceA'), + Reference('InterfaceB'), + ]) + ..mixins.addAll(const [ + Reference('Mixin1'), + Reference('Mixin2'), + ]) + ..values.addAll([ + EnumValue((v) => v..name = 'a'), + EnumValue((v) => v..name = 'b'), + EnumValue((v) => v..name = 'c'), + ])); + expect(myEnum, equalsDart(''' + enum MyEnum with Mixin1, Mixin2 implements InterfaceA, InterfaceB { + a, + b, + c + } + ''')); + }); + + test('should create an enum which targets a named constructor', () { + final myEnum = Enum((b) => b + ..name = 'MyEnum' + ..constructors.addAll([ + Constructor((c) => c..constant = true), + Constructor((c) => c + ..constant = true + ..name = 'named'), + ]) + ..values.addAll([ + EnumValue((v) => v..name = 'a'), + EnumValue((v) => v + ..name = 'b' + ..constructorName = 'named'), + EnumValue((v) => v..name = 'c'), + ])); + expect(myEnum, equalsDart(''' + enum MyEnum { + a, + b.named(), + c; + + const MyEnum(); + + const MyEnum.named(); + } + ''')); + }); + + test('should create an enum which targets a redirecting constructor', () { + final myEnum = Enum((b) => b + ..name = 'MyEnum' + ..constructors.addAll([ + Constructor((c) => c..constant = true), + Constructor((c) => c + ..constant = true + ..name = 'redirect' + ..initializers.add( + refer('this').call([]).code, + )), + ]) + ..values.addAll([ + EnumValue((v) => v..name = 'a'), + EnumValue((v) => v + ..name = 'b' + ..constructorName = 'redirect'), + EnumValue((v) => v..name = 'c'), + ])); + expect(myEnum, equalsDart(''' + enum MyEnum { + a, + b.redirect(), + c; + + const MyEnum(); + + const MyEnum.redirect() : this(); + } + ''')); + }); + + test('should create an enum which targets a redirecting factory constructor', + () { + final myEnum = Enum((b) => b + ..name = 'MyEnum' + ..constructors.addAll([ + Constructor((c) => c..constant = true), + Constructor((c) => c + ..constant = true + ..factory = true + ..name = 'redirect' + ..redirect = refer('MyOtherEnum.named') + ..optionalParameters.addAll([ + Parameter((p) => p + ..type = refer('int?') + ..name = 'myInt'), + Parameter((p) => p + ..type = refer('String?') + ..name = 'myString') + ])) + ]) + ..values.addAll([ + EnumValue((v) => v..name = 'a'), + EnumValue((v) => v + ..name = 'b' + ..constructorName = 'redirect' + ..arguments.addAll([ + literalNum(1), + literalString('abc'), + ])), + EnumValue((v) => v + ..name = 'c' + ..constructorName = 'redirect'), + ])); + expect(myEnum, equalsDart(''' + enum MyEnum { + a, + b.redirect(1, 'abc'), + c.redirect(); + + const MyEnum(); + + const factory MyEnum.redirect([ + int? myInt, + String? myString, + ]) = MyOtherEnum.named; + } + ''')); + }); + + test('should create an enum which targets an unnamed constructor', () { + final myEnum = Enum((b) => b + ..name = 'MyEnum' + ..constructors.add(Constructor((c) => c + ..constant = true + ..optionalParameters.addAll([ + Parameter((p) => p + ..toThis = true + ..name = 'myInt'), + Parameter((p) => p + ..toThis = true + ..name = 'myString') + ]))) + ..fields.addAll([ + Field((f) => f + ..modifier = FieldModifier.final$ + ..type = refer('int?') + ..name = 'myInt'), + Field((f) => f + ..modifier = FieldModifier.final$ + ..type = refer('String?') + ..name = 'myString') + ]) + ..values.addAll([ + EnumValue((v) => v..name = 'a'), + EnumValue((v) => v + ..name = 'b' + ..arguments.addAll([ + literalNum(1), + literalString('abc'), + ])), + EnumValue((v) => v..name = 'c'), + ])); + expect(myEnum, equalsDart(''' + enum MyEnum { + a, + b(1, 'abc'), + c; + + const MyEnum([ + this.myInt, + this.myString, + ]); + + final int? myInt; + + final String? myString; + } + ''')); + }); + + test('should create an enum with generics', () { + final myEnum = Enum((b) => b + ..name = 'MyEnum' + ..types.add(const Reference('T')) + ..constructors.add(Constructor((c) => c + ..constant = true + ..requiredParameters.add(Parameter((p) => p + ..toThis = true + ..name = 'value')))) + ..fields.add( + Field((f) => f + ..modifier = FieldModifier.final$ + ..type = refer('T') + ..name = 'value'), + ) + ..values.addAll([ + EnumValue((v) => v + ..name = 'a' + ..types.add(const Reference('int')) + ..arguments.add(literalNum(123))), + EnumValue((v) => v + ..name = 'b' + ..types.add(const Reference('String')) + ..arguments.add(literalString('abc'))), + EnumValue((v) => v + ..name = 'c' + ..types.add(const Reference('MyEnum')) + ..arguments.add(refer('MyEnum').property('a'))), + ])); + expect(myEnum, equalsDart(''' + enum MyEnum { + a(123), + b('abc'), + c(MyEnum.a); + + const MyEnum(this.value); + + final T value; + } + ''')); + }); + + test('should create an enum with fields', () { + final myEnum = Enum((b) => b + ..name = 'MyEnum' + ..constructors.add(Constructor((c) => c + ..constant = true + ..optionalParameters.add(Parameter((p) => p + ..toThis = true + ..name = 'myInt')))) + ..fields.addAll([ + Field((f) => f + ..modifier = FieldModifier.final$ + ..type = refer('int?') + ..name = 'myInt'), + Field((f) => f + ..static = true + ..modifier = FieldModifier.constant + ..type = refer('String') + ..name = 'myString' + ..assignment = literalString('abc').code), + ]) + ..values.addAll([ + EnumValue((v) => v..name = 'a'), + EnumValue((v) => v..name = 'b'), + EnumValue((v) => v..name = 'c'), + ])); + expect(myEnum, equalsDart(''' + enum MyEnum { + a, + b, + c; + + const MyEnum([this.myInt]); + + final int? myInt; + + static const String myString = 'abc'; + } + ''')); + }); + + test('should create an enum with methods', () { + final myEnum = Enum((b) => b + ..name = 'MyEnum' + ..methods.addAll([ + Method((m) => m + ..returns = refer('int') + ..type = MethodType.getter + ..name = 'myInt' + ..body = literalNum(123).code), + Method((m) => m + ..returns = refer('Iterable') + ..name = 'myStrings' + ..modifier = MethodModifier.syncStar + ..body = Block.of(const [ + Code("yield 'a';"), + Code("yield 'b';"), + Code("yield 'c';"), + ])) + ]) + ..values.addAll([ + EnumValue((v) => v..name = 'a'), + EnumValue((v) => v..name = 'b'), + EnumValue((v) => v..name = 'c'), + ])); + expect(myEnum, equalsDart(''' + enum MyEnum { + a, + b, + c; + + int get myInt => 123; + Iterable myStrings() sync* { + yield 'a'; + yield 'b'; + yield 'c'; + } + } + ''')); + }); + + test('should create an enum which named and unnamed constructor parameters', + () { + final myEnum = Enum((b) => b + ..name = 'MyEnum' + ..constructors.add(Constructor((c) => c + ..constant = true + ..requiredParameters.addAll([ + Parameter((p) => p + ..toThis = true + ..name = 'myInt') + ]) + ..optionalParameters.addAll([ + Parameter((p) => p + ..toThis = true + ..named = true + ..required = true + ..name = 'myString') + ]))) + ..fields.addAll([ + Field((f) => f + ..modifier = FieldModifier.final$ + ..type = refer('int?') + ..name = 'myInt'), + Field((f) => f + ..modifier = FieldModifier.final$ + ..type = refer('String?') + ..name = 'myString') + ]) + ..values.addAll([ + EnumValue((v) => v..name = 'a'), + EnumValue((v) => v + ..name = 'b' + ..arguments.addAll([ + literalNum(1), + ]) + ..namedArguments.addAll({ + 'myString': literalString('abc'), + })), + EnumValue((v) => v..name = 'c'), + ])); + expect(myEnum, equalsDart(''' + enum MyEnum { + a, + b(1, myString: 'abc'), + c; + + const MyEnum( + this.myInt, + {required this.myString, } + ); + + final int? myInt; + + final String? myString; + } + ''')); + }); +} diff --git a/pkgs/code_builder/test/specs/extension_test.dart b/pkgs/code_builder/test/specs/extension_test.dart new file mode 100644 index 000000000..b45fa6580 --- /dev/null +++ b/pkgs/code_builder/test/specs/extension_test.dart @@ -0,0 +1,137 @@ +// 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. + +import 'package:code_builder/code_builder.dart'; +import 'package:test/test.dart'; + +import '../common.dart'; + +void main() { + useDartfmt(); + + test('should create an extension', () { + expect( + Extension((b) => b + ..name = 'Foo' + ..on = TypeReference((b) => b.symbol = 'Bar')), + equalsDart(r''' + extension Foo on Bar {} + '''), + ); + }); + + test('should create an extension without an identifier', () { + expect( + Extension((b) => b..on = TypeReference((b) => b.symbol = 'Bar')), + equalsDart(r''' + extension on Bar {} + '''), + ); + }); + + test('should create an extension with documentation', () { + expect( + Extension( + (b) => b + ..name = 'Foo' + ..on = TypeReference((b) => b.symbol = 'Bar') + ..docs.addAll( + const [ + '/// My favorite extension.', + ], + ), + ), + equalsDart(r''' + /// My favorite extension. + extension Foo on Bar {} + '''), + ); + }); + + test('should create an extension with annotations', () { + expect( + Extension( + (b) => b + ..name = 'Foo' + ..on = TypeReference((b) => b.symbol = 'Bar') + ..annotations.addAll([ + refer('deprecated'), + refer('Deprecated') + .call([literalString('This is an old extension')]) + ]), + ), + equalsDart(r''' + @deprecated + @Deprecated('This is an old extension') + extension Foo on Bar {} + '''), + ); + }); + + test('should create an extension with a generic type', () { + expect( + Extension((b) => b + ..name = 'Foo' + ..on = TypeReference((b) => b.symbol = 'Bar') + ..types.add(refer('T'))), + equalsDart(r''' + extension Foo on Bar {} + '''), + ); + }); + + test('should create an extension with multiple generic types', () { + expect( + Extension( + (b) => b + ..name = 'Map' + ..on = TypeReference((b) => b.symbol = 'Bar') + ..types.addAll([ + refer('K'), + refer('V'), + ]), + ), + equalsDart(r''' + extension Map on Bar {} + '''), + ); + }); + + test('should create an extension with a bound generic type', () { + expect( + Extension((b) => b + ..name = 'Foo' + ..on = TypeReference((b) => b.symbol = 'Bar') + ..types.add(TypeReference((b) => b + ..symbol = 'T' + ..bound = TypeReference((b) => b + ..symbol = 'Comparable' + ..types.add(refer('T').type))))), + equalsDart(r''' + extension Foo> on Bar {} + '''), + ); + }); + + test('should create an extension with a method', () { + expect( + Extension((b) => b + ..name = 'Foo' + ..on = TypeReference((b) => b.symbol = 'Bar') + ..methods.add(Method((b) => b + ..name = 'parseInt' + ..returns = refer('int') + ..body = Code.scope( + (a) => 'return int.parse(this);', + )))), + equalsDart(r''' + extension Foo on Bar { + int parseInt() { + return int.parse(this); + } + } + '''), + ); + }); +} diff --git a/pkgs/code_builder/test/specs/extension_type_test.dart b/pkgs/code_builder/test/specs/extension_type_test.dart new file mode 100644 index 000000000..cc510468f --- /dev/null +++ b/pkgs/code_builder/test/specs/extension_type_test.dart @@ -0,0 +1,244 @@ +// Copyright (c) 2023, 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. + +import 'package:code_builder/code_builder.dart'; +import 'package:test/test.dart'; + +import '../common.dart'; + +void main() { + useDartfmt(); + + test('minimum extension type', () { + expect( + ExtensionType((b) => b + ..name = 'Foo' + ..representationDeclaration = RepresentationDeclaration((b) => b + ..declaredRepresentationType = TypeReference((b) => b.symbol = 'int') + ..name = 'bar')), + equalsDart(r''' + extension type Foo(int bar) { } + '''), + ); + }); + + test('const extension type', () { + expect( + ExtensionType((b) => b + ..name = 'Foo' + ..constant = true + ..representationDeclaration = RepresentationDeclaration((b) => b + ..declaredRepresentationType = TypeReference((b) => b.symbol = 'int') + ..name = 'bar')), + equalsDart(r''' + extension type const Foo(int bar) { } + '''), + ); + }); + + test('extension type with metadata', () { + expect( + ExtensionType((b) => b + ..name = 'Foo' + ..representationDeclaration = RepresentationDeclaration((b) => b + ..declaredRepresentationType = TypeReference((b) => b.symbol = 'int') + ..name = 'bar') + ..docs.add( + '/// My favorite extension type.', + ) + ..annotations.addAll([ + refer('deprecated'), + refer('Deprecated') + .call([literalString('This is an old extension type')]) + ])), + equalsDart(r''' + /// My favorite extension type. + @deprecated + @Deprecated('This is an old extension type') + extension type Foo(int bar) { } + '''), + ); + }); + + test('extension type with generics', () { + expect( + ExtensionType((b) => b + ..name = 'Foo' + ..types.addAll([ + TypeReference((b) => b..symbol = 'T'), + TypeReference((b) => b..symbol = 'U') + ]) + ..representationDeclaration = RepresentationDeclaration((b) => b + ..declaredRepresentationType = TypeReference((b) => b.symbol = 'T') + ..name = 'bar')), + equalsDart(r''' + extension type Foo(T bar) { } + '''), + ); + }); + + test('extension type with generics bound', () { + expect( + ExtensionType((b) => b + ..name = 'Foo' + ..types.add(TypeReference((b) => b + ..symbol = 'T' + ..bound = TypeReference((b) => b..symbol = 'num'))) + ..representationDeclaration = RepresentationDeclaration((b) => b + ..declaredRepresentationType = TypeReference((b) => b.symbol = 'T') + ..name = 'bar')), + equalsDart(r''' + extension type Foo(T bar) { } + '''), + ); + }); + + test('extension type with named primary constructor', () { + expect( + ExtensionType((b) => b + ..name = 'Foo' + ..primaryConstructorName = 'named' + ..representationDeclaration = RepresentationDeclaration((b) => b + ..declaredRepresentationType = TypeReference((b) => b.symbol = 'int') + ..name = 'bar')), + equalsDart(r''' + extension type Foo.named(int bar) { } + '''), + ); + }); + + test('extension type with metadata on field', () { + expect( + ExtensionType((b) => b + ..name = 'Foo' + ..representationDeclaration = RepresentationDeclaration((b) => b + ..declaredRepresentationType = TypeReference((b) => b.symbol = 'int') + ..name = 'bar' + ..docs.add( + '/// My favorite representation declaration.', + ) + ..annotations.addAll([ + refer('deprecated'), + refer('Deprecated').call( + [literalString('This is an old representation declaration')]) + ]))), + equalsDart(r''' + extension type Foo(/// My favorite representation declaration. + @deprecated + @Deprecated('This is an old representation declaration') + int bar) { } + '''), + ); + }); + + test('extension type with implements', () { + expect( + ExtensionType((b) => b + ..name = 'Foo' + ..implements.add(TypeReference((b) => b.symbol = 'num')) + ..representationDeclaration = RepresentationDeclaration((b) => b + ..declaredRepresentationType = TypeReference((b) => b.symbol = 'int') + ..name = 'bar')), + equalsDart(r''' + extension type Foo(int bar) implements num { } + '''), + ); + }); + + test('extension type with multiple implements', () { + expect( + ExtensionType((b) => b + ..name = 'Foo' + ..implements.addAll([ + TypeReference((b) => b.symbol = 'num'), + TypeReference((b) => b.symbol = 'Object') + ]) + ..representationDeclaration = RepresentationDeclaration((b) => b + ..declaredRepresentationType = TypeReference((b) => b.symbol = 'int') + ..name = 'bar')), + equalsDart(r''' + extension type Foo(int bar) implements num,Object { } + '''), + ); + }); + + test('extension type with constructors', () { + expect( + ExtensionType((b) => b + ..name = 'Foo' + ..primaryConstructorName = '_' + ..representationDeclaration = RepresentationDeclaration((b) => b + ..declaredRepresentationType = TypeReference((b) => b.symbol = 'int') + ..name = 'bar') + ..constructors.addAll([ + Constructor((b) => b.requiredParameters.add(Parameter((b) => b + ..toThis = true + ..name = 'bar'))), + Constructor((b) => b + ..name = 'named' + ..factory = true + ..requiredParameters.add(Parameter((b) => b + ..type = TypeReference((b) => b.symbol = 'int') + ..name = 'baz')) + ..body = const Code('return Foo(baz);')) + ])), + equalsDart(r''' + extension type Foo._(int bar) { + Foo(this.bar); + + factory Foo.named(int baz) { + return Foo(baz); + } + } + '''), + ); + }); + + test('extension type with external field', () { + expect( + ExtensionType((b) => b + ..name = 'Foo' + ..representationDeclaration = RepresentationDeclaration((b) => b + ..declaredRepresentationType = TypeReference((b) => b.symbol = 'int') + ..name = 'bar') + ..fields.add(Field((b) => b + ..external = true + ..type = TypeReference((b) => b.symbol = 'int') + ..name = 'property'))), + equalsDart(r''' + extension type Foo(int bar) { + external int property; + } + '''), + ); + }); + + test('extension type with methods', () { + expect( + ExtensionType((b) => b + ..name = 'Foo' + ..representationDeclaration = RepresentationDeclaration((b) => b + ..declaredRepresentationType = TypeReference((b) => b.symbol = 'int') + ..name = 'bar') + ..methods.addAll([ + Method((b) => b + ..type = MethodType.getter + ..returns = TypeReference((b) => b.symbol = 'int') + ..name = 'value' + ..body = const Code('return this.bar;')), + Method((b) => b + ..returns = TypeReference((b) => b.symbol = 'int') + ..name = 'getValue' + ..lambda = true + ..body = const Code('this.bar')) + ])), + equalsDart(r''' + extension type Foo(int bar) { + int get value { return this.bar; } + int getValue() => this.bar; + } + '''), + ); + }); +} diff --git a/pkgs/code_builder/test/specs/field_test.dart b/pkgs/code_builder/test/specs/field_test.dart new file mode 100644 index 000000000..3d53687bc --- /dev/null +++ b/pkgs/code_builder/test/specs/field_test.dart @@ -0,0 +1,113 @@ +// 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. + +import 'package:code_builder/code_builder.dart'; +import 'package:test/test.dart'; + +import '../common.dart'; + +void main() { + useDartfmt(); + + test('should create a field', () { + expect( + Field((b) => b..name = 'foo'), + equalsDart(r''' + var foo; + '''), + ); + }); + + test('should create a typed field', () { + expect( + Field((b) => b + ..name = 'foo' + ..type = refer('String')), + equalsDart(r''' + String foo; + '''), + ); + }); + + test('should create a final field', () { + expect( + Field((b) => b + ..name = 'foo' + ..modifier = FieldModifier.final$), + equalsDart(r''' + final foo; + '''), + ); + }); + + test('should create a constant field', () { + expect( + Field((b) => b + ..name = 'foo' + ..modifier = FieldModifier.constant), + equalsDart(r''' + const foo; + '''), + ); + }); + + test('should create a late field if using null-safety', () { + expect( + Field((b) => b + ..late = true + ..name = 'foo'), + equalsDart(r''' + late var foo; + ''', DartEmitter(useNullSafetySyntax: true)), + ); + }); + + test('should not create a late field if not using null-safety', () { + expect( + Field((b) => b + ..late = true + ..name = 'foo'), + equalsDart(r''' + var foo; + '''), + ); + }); + + test('should create a static late field', () { + expect( + Field((b) => b + ..static = true + ..late = true + ..name = 'foo'), + equalsDart(r''' + static late var foo; + ''', DartEmitter(useNullSafetySyntax: true)), + ); + }); + + test('should create a field with an assignment', () { + expect( + Field((b) => b + ..name = 'foo' + ..assignment = const Code('1')), + equalsDart(r''' + var foo = 1; + '''), + ); + }); + + test('should create a external field', () { + expect( + Field((b) => b + ..name = 'value' + ..external = true + ..type = refer('double') + ..annotations.addAll([refer('Float').call([])])), + equalsDart(r''' + @Float() + external double value; + '''), + ); + }); +} diff --git a/pkgs/code_builder/test/specs/library_test.dart b/pkgs/code_builder/test/specs/library_test.dart new file mode 100644 index 000000000..8ea4c5811 --- /dev/null +++ b/pkgs/code_builder/test/specs/library_test.dart @@ -0,0 +1,313 @@ +// 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. + +import 'package:code_builder/code_builder.dart'; +import 'package:test/test.dart'; + +import '../common.dart'; + +void main() { + useDartfmt(); + + group('File', () { + final $LinkedHashMap = refer('LinkedHashMap', 'dart:collection'); + + test('should emit a source file with leading line comments', () { + expect( + Library( + (b) => b + ..comments.add('Generated by foo.') + ..body.add( + Class((b) => b..name = 'Foo'), + ), + ), + equalsDart(r''' + // Generated by foo. + + class Foo { } + ''', DartEmitter(allocator: Allocator())), + ); + }); + + test('should emit a source file with multiple leading comments', () { + expect( + Library( + (b) => b + ..comments.addAll([ + 'Generated by foo!', + '', + 'Avoid editing by hand.', + ]) + ..body.add( + Class((b) => b..name = 'Foo'), + ), + ), + equalsDart(r''' + // Generated by foo! + // + // Avoid editing by hand. + + class Foo { } + ''', DartEmitter(allocator: Allocator())), + ); + }); + + test('should emit a source file with a generated-by comment', () { + expect( + Library( + (b) => b + ..generatedByComment = 'Generated by fooBar.' + ..body.add( + Class((b) => b..name = 'Foo'), + ), + ), + equalsDart(r''' + // Generated by fooBar. + + class Foo { } + ''', DartEmitter(allocator: Allocator())), + ); + }); + + test('should emit a source file with ignore comments', () { + expect( + Library( + (b) => b + ..ignoreForFile.add('sort_constructors_first') + ..body.add( + Class((b) => b..name = 'Foo'), + ), + ), + equalsDart(r''' + // ignore_for_file: sort_constructors_first + + class Foo { } + ''', DartEmitter(allocator: Allocator())), + ); + }); + + test('should emit a source file with multiple, sorted ignore comments', () { + expect( + Library( + (b) => b + ..ignoreForFile.addAll([ + 'type=lint', + 'sort_constructors_first', + 'implementation_imports', + 'file_names', + ]) + ..body.add( + Class((b) => b..name = 'Foo'), + ), + ), + equalsDart(r''' + // ignore_for_file: file_names, implementation_imports, sort_constructors_first + // ignore_for_file: type=lint + + class Foo { } + ''', DartEmitter(allocator: Allocator())), + ); + }); + + test('should emit with line comments, generated-by, and ignore-for-file', + () { + expect( + Library( + (b) => b + ..comments.add('Generic copyright statement.') + ..generatedByComment = 'Generated by fooBar.' + ..ignoreForFile.add('sort_constructors_first') + ..body.add( + Class((b) => b..name = 'Foo'), + ), + ), + equalsDart(r''' + // Generic copyright statement. + + // Generated by fooBar. + + // ignore_for_file: sort_constructors_first + + class Foo { } + ''', DartEmitter(allocator: Allocator())), + ); + }); + + test('should emit a source file with manual imports', () { + expect( + Library((b) => b + ..directives.add(Directive.import('dart:collection')) + ..body.add(Field((b) => b + ..name = 'test' + ..modifier = FieldModifier.final$ + ..assignment = $LinkedHashMap.newInstance([]).code))), + equalsDart(r''' + import 'dart:collection'; + + final test = LinkedHashMap(); + ''', DartEmitter()), + ); + }); + + test('should emit a source file with a deferred import', () { + expect( + Library( + (b) => b + ..directives.add( + Directive.importDeferredAs( + 'package:foo/foo.dart', + 'foo', + ), + ), + ), + equalsDart(r''' + import 'package:foo/foo.dart' deferred as foo; + '''), + ); + }); + + test('should emit a source file with a "show" combinator', () { + expect( + Library( + (b) => b + ..directives.add( + Directive.import( + 'package:foo/foo.dart', + show: ['Foo', 'Bar'], + ), + ), + ), + equalsDart(r''' + import 'package:foo/foo.dart' show Foo, Bar; + '''), + ); + }); + + test('should emit a source file with a "hide" combinator', () { + expect( + Library( + (b) => b + ..directives.add( + Directive.import( + 'package:foo/foo.dart', + hide: ['Foo', 'Bar'], + ), + ), + ), + equalsDart(r''' + import 'package:foo/foo.dart' hide Foo, Bar; + '''), + ); + }); + + test('should emit a source file with allocation', () { + expect( + Library((b) => b + ..body.add(Field((b) => b + ..name = 'test' + ..modifier = FieldModifier.final$ + ..assignment = Code.scope((a) => '${a($LinkedHashMap)}()')))), + equalsDart(r''' + import 'dart:collection'; + + final test = LinkedHashMap(); + ''', DartEmitter(allocator: Allocator())), + ); + }); + + test('should emit a source file with allocation + prefixing', () { + expect( + Library((b) => b + ..body.add(Field((b) => b + ..name = 'test' + ..modifier = FieldModifier.final$ + ..assignment = Code.scope((a) => '${a($LinkedHashMap)}()')))), + equalsDart(r''' + // ignore_for_file: no_leading_underscores_for_library_prefixes + import 'dart:collection' as _i1; + + final test = _i1.LinkedHashMap(); + ''', DartEmitter(allocator: Allocator.simplePrefixing())), + ); + }); + + test('should emit a source file with part directives', () { + expect( + Library( + (b) => b + ..directives.add( + Directive.part('test.g.dart'), + ), + ), + equalsDart(r''' + part 'test.g.dart'; + ''', DartEmitter()), + ); + }); + + test('should emit a source file with part of directives', () { + expect( + Library( + (b) => b + ..directives.add( + Directive.partOf('test.dart'), + ), + ), + equalsDart(r''' + part of 'test.dart'; + ''', DartEmitter()), + ); + }); + + test('should emit a source file with annotations', () { + expect( + Library( + (b) => b + ..name = 'js_interop' + ..annotations.add( + refer('JS', 'package:js/js.dart').call([]), + ), + ), + equalsDart(r''' + @JS() + library js_interop; + import 'package:js/js.dart'; + ''', DartEmitter(allocator: Allocator())), + ); + }); + + test('should emit an unnamed library source file with annotations', () { + expect( + Library( + (b) => b + ..annotations.add( + refer('JS', 'package:js/js.dart').call([]), + ), + ), + equalsDart(r''' + @JS() + library; + import 'package:js/js.dart'; + ''', DartEmitter(allocator: Allocator())), + ); + }); + + test('should emit an unnamed library source file with documentation', () { + expect( + Library( + (b) => b + ..docs.addAll( + const [ + '/// My favorite library.', + ], + ), + ), + equalsDart(r''' + /// My favorite library. + library; + '''), + ); + }); + }); +} diff --git a/pkgs/code_builder/test/specs/method_test.dart b/pkgs/code_builder/test/specs/method_test.dart new file mode 100644 index 000000000..5621bc52c --- /dev/null +++ b/pkgs/code_builder/test/specs/method_test.dart @@ -0,0 +1,632 @@ +// 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. + +import 'package:code_builder/code_builder.dart'; +import 'package:test/test.dart'; + +import '../common.dart'; + +void main() { + useDartfmt(); + + test('should create a method', () { + expect( + Method((b) => b..name = 'foo'), + equalsDart(r''' + foo(); + '''), + ); + }); + + test('should create an async method', () { + expect( + Method((b) => b + ..name = 'foo' + ..modifier = MethodModifier.async + ..body = literalNull.code), + equalsDart(r''' + foo() async => null + '''), + ); + }); + + test('should create an async* method', () { + expect( + Method((b) => b + ..name = 'foo' + ..modifier = MethodModifier.asyncStar + ..body = literalNull.code), + equalsDart(r''' + foo() async* => null + '''), + ); + }); + + test('should create an sync* method', () { + expect( + Method((b) => b + ..name = 'foo' + ..modifier = MethodModifier.syncStar + ..body = literalNull.code), + equalsDart(r''' + foo() sync* => null + '''), + ); + }); + + test('should create a lambda method implicitly', () { + expect( + Method((b) => b + ..name = 'returnsTrue' + ..returns = refer('bool') + ..body = literalTrue.code), + equalsDart(r''' + bool returnsTrue() => true + '''), + ); + }); + + test('should create a lambda method if the value is cast', () { + expect( + Method((b) => b + ..name = 'returnsCastedValue' + ..returns = refer('Foo') + ..body = refer('bar').asA(refer('Foo')).code), + equalsDart(r''' + Foo returnsCastedValue() => (bar as Foo) + '''), + ); + }); + + test('should create a normal method implicitly', () { + expect( + Method.returnsVoid((b) => b + ..name = 'assignTrue' + ..body = refer('topLevelFoo').assign(literalTrue).statement), + equalsDart(r''' + void assignTrue() { + topLevelFoo = true; + } + '''), + ); + }); + + test('should create a getter', () { + expect( + Method((b) => b + ..name = 'foo' + ..external = true + ..type = MethodType.getter), + equalsDart(r''' + external get foo; + '''), + ); + }); + + test('should create a setter', () { + expect( + Method((b) => b + ..name = 'foo' + ..external = true + ..requiredParameters.add(Parameter((b) => b..name = 'foo')) + ..type = MethodType.setter), + equalsDart(r''' + external set foo(foo); + '''), + ); + }); + + test('should create a method with a return type', () { + expect( + Method((b) => b + ..name = 'foo' + ..returns = refer('String')), + equalsDart(r''' + String foo(); + '''), + ); + }); + + test('should create a method with a void return type', () { + expect( + Method.returnsVoid((b) => b..name = 'foo'), + equalsDart(r''' + void foo(); + '''), + ); + }); + + test('should create a method with a function type return type', () { + expect( + Method((b) => b + ..name = 'foo' + ..returns = FunctionType((b) => b + ..returnType = refer('String') + ..requiredParameters.addAll([ + refer('int'), + ]))), + equalsDart(r''' + String Function(int) foo(); + '''), + ); + }); + + test('should create a function type with an optional positional parameter', + () { + expect( + FunctionType((b) => b + ..returnType = refer('String') + ..optionalParameters.add(refer('int'))), + equalsDart(r''' + String Function([int]) + '''), + ); + }); + + test( + 'should create a function type with a required ' + 'and an optional positional parameter', () { + expect( + FunctionType((b) => b + ..returnType = refer('String') + ..requiredParameters.add(refer('int')) + ..optionalParameters.add(refer('int'))), + equalsDart(r''' + String Function(int, [int, ]) + '''), + ); + }); + + test('should create a function type without parameters', () { + expect( + FunctionType((b) => b..returnType = refer('String')), + equalsDart(r''' + String Function() + '''), + ); + }); + + test('should create a function type with an optional named parameter', () { + expect( + FunctionType((b) => b + ..returnType = refer('String') + ..namedParameters['named'] = refer('int')), + equalsDart(r''' + String Function({int named}) + '''), + ); + }); + + test( + 'should create a function type with a required ' + 'and an optional named parameter', () { + expect( + FunctionType((b) => b + ..returnType = refer('String') + ..requiredParameters.add(refer('int')) + ..namedParameters['named'] = refer('int')), + equalsDart(r''' + String Function(int, {int named, }) + '''), + ); + }); + + test('should create a function type with a required named parameter', () { + expect( + FunctionType((b) => b + ..returnType = refer('String') + ..namedRequiredParameters['named'] = refer('int')), + equalsDart(r''' + String Function({required int named}) + '''), + ); + }); + + test( + 'should create a function type with a required named and an optional ' + 'named parameter', () { + expect( + FunctionType((b) => b + ..returnType = refer('String') + ..namedRequiredParameters['named'] = refer('int') + ..namedParameters['optional'] = refer('int')), + equalsDart(r''' + String Function({required int named, int optional, }) + '''), + ); + }); + + test('should create a typedef to a reference', () { + expect( + TypeDef((b) => b + ..name = 'i32' + ..definition = const Reference('int')), + equalsDart(r''' + typedef i32 = int; + '''), + ); + }); + + test('should create a typedef to a function type', () { + expect( + TypeDef((b) => b + ..name = 'MyMapper' + ..definition = FunctionType((b) => b + ..returnType = refer('String') + ..optionalParameters.add(refer('int')))), + equalsDart(r''' + typedef MyMapper = String Function([int]); + '''), + ); + }); + + test('should create a method with a nested function type return type', () { + expect( + Method((b) => b + ..name = 'foo' + ..returns = FunctionType((b) => b + ..returnType = FunctionType((b) => b + ..returnType = refer('String') + ..requiredParameters.add(refer('String'))) + ..requiredParameters.addAll([ + refer('int'), + ]))), + equalsDart(r''' + String Function(String) Function(int) foo(); + '''), + ); + }); + + test('should create a method with a function type argument', () { + expect( + Method((b) => b + ..name = 'foo' + ..requiredParameters.add(Parameter((b) => b + ..type = FunctionType((b) => b + ..returnType = refer('String') + ..requiredParameters.add(refer('int'))) + ..name = 'argument'))), + equalsDart(r''' + foo(String Function(int) argument); + ''')); + }); + + test('should create a method with a nested function type argument', () { + expect( + Method((b) => b + ..name = 'foo' + ..requiredParameters.add(Parameter((b) => b + ..type = FunctionType((b) => b + ..returnType = FunctionType((b) => b + ..returnType = refer('String') + ..requiredParameters.add(refer('String'))) + ..requiredParameters.add(refer('int'))) + ..name = 'argument'))), + equalsDart(r''' + foo(String Function(String) Function(int) argument); + ''')); + }); + + test('should create a method with generic types', () { + expect( + Method((b) => b + ..name = 'foo' + ..types.add(refer('T'))), + equalsDart(r''' + foo(); + '''), + ); + }); + + test('should create an external method', () { + expect( + Method((b) => b + ..name = 'foo' + ..external = true), + equalsDart(r''' + external foo(); + '''), + ); + }); + + test('should create a method with a body', () { + expect( + Method((b) => b + ..name = 'foo' + ..body = const Code('return 1+ 2;')), + equalsDart(r''' + foo() { + return 1 + 2; + } + '''), + ); + }); + + test('should create a lambda method (explicitly)', () { + expect( + Method((b) => b + ..name = 'foo' + ..lambda = true + ..body = const Code('1 + 2')), + equalsDart(r''' + foo() => 1 + 2 + '''), + ); + }); + + test('should create a method with a body with references', () { + final $LinkedHashMap = refer('LinkedHashMap', 'dart:collection'); + expect( + Method((b) => b + ..name = 'foo' + ..body = Code.scope( + (a) => 'return ${a($LinkedHashMap)}();', + )), + equalsDart(r''' + foo() { + return LinkedHashMap(); + } + '''), + ); + }); + + test('should create a method with a parameter', () { + expect( + Method( + (b) => b + ..name = 'fib' + ..requiredParameters.add( + Parameter((b) => b.name = 'i'), + ), + ), + equalsDart(r''' + fib(i); + '''), + ); + }); + + test('should create a method with an annotated parameter', () { + expect( + Method( + (b) => b + ..name = 'fib' + ..requiredParameters.add( + Parameter((b) => b + ..name = 'i' + ..annotations.add(refer('deprecated'))), + ), + ), + equalsDart(r''' + fib(@deprecated i); + '''), + ); + }); + + test('should create a method with a parameter with a type', () { + expect( + Method( + (b) => b + ..name = 'fib' + ..requiredParameters.add( + Parameter( + (b) => b + ..name = 'i' + ..type = refer('int').type, + ), + ), + ), + equalsDart(r''' + fib(int i); + '''), + ); + }); + + test('should create a method with a covariant parameter with a type', () { + expect( + Method( + (b) => b + ..name = 'fib' + ..requiredParameters.add( + Parameter( + (b) => b + ..name = 'i' + ..covariant = true + ..type = refer('int').type, + ), + ), + ), + equalsDart(r''' + fib(covariant int i); + '''), + ); + }); + + test('should create a method with a parameter with a generic type', () { + expect( + Method( + (b) => b + ..name = 'foo' + ..types.add(TypeReference((b) => b + ..symbol = 'T' + ..bound = refer('Iterable'))) + ..requiredParameters.addAll([ + Parameter( + (b) => b + ..name = 't' + ..type = refer('T'), + ), + Parameter((b) => b + ..name = 'x' + ..type = TypeReference((b) => b + ..symbol = 'X' + ..types.add(refer('T')))), + ]), + ), + equalsDart(r''' + foo(T t, X x, ); + '''), + ); + }); + + test('should create a method with an optional parameter', () { + expect( + Method( + (b) => b + ..name = 'fib' + ..optionalParameters.add( + Parameter((b) => b.name = 'i'), + ), + ), + equalsDart(r''' + fib([i]); + '''), + ); + }); + + test('should create a method with multiple optional parameters', () { + expect( + Method( + (b) => b + ..name = 'foo' + ..optionalParameters.addAll([ + Parameter((b) => b.name = 'a'), + Parameter((b) => b.name = 'b'), + ]), + ), + equalsDart(r''' + foo([a, b, ]); + '''), + ); + }); + + test('should create a method with an optional parameter with a value', () { + expect( + Method( + (b) => b + ..name = 'fib' + ..optionalParameters.add( + Parameter((b) => b + ..name = 'i' + ..defaultTo = const Code('0')), + ), + ), + equalsDart(r''' + fib([i = 0]); + '''), + ); + }); + + test('should create a method with a named required parameter', () { + expect( + Method( + (b) => b + ..name = 'fib' + ..optionalParameters.add( + Parameter( + (b) => b + ..name = 'i' + ..named = true + ..required = true + ..type = refer('int').type, + ), + ), + ), + equalsDart(r''' + fib({required int i}); + '''), + ); + }); + + test('should create a method with a named required covariant parameter', () { + expect( + Method( + (b) => b + ..name = 'fib' + ..optionalParameters.add( + Parameter( + (b) => b + ..name = 'i' + ..named = true + ..required = true + ..covariant = true + ..type = refer('int').type, + ), + ), + ), + equalsDart(r''' + fib({required covariant int i}); + '''), + ); + }); + + test('should create a method with a named optional parameter', () { + expect( + Method( + (b) => b + ..name = 'fib' + ..optionalParameters.add( + Parameter((b) => b + ..named = true + ..name = 'i'), + ), + ), + equalsDart(r''' + fib({i}); + '''), + ); + }); + + test('should create a method with a named optional parameter with value', () { + expect( + Method( + (b) => b + ..name = 'fib' + ..optionalParameters.add( + Parameter((b) => b + ..named = true + ..name = 'i' + ..defaultTo = const Code('0')), + ), + ), + equalsDart(r''' + fib({i = 0}); + '''), + ); + }); + + test('should create a method with a mix of parameters', () { + expect( + Method( + (b) => b + ..name = 'foo' + ..requiredParameters.add( + Parameter((b) => b..name = 'a'), + ) + ..optionalParameters.add( + Parameter((b) => b + ..named = true + ..name = 'b'), + ), + ), + equalsDart(r''' + foo(a, {b, }); + '''), + ); + }); + + test('should create a method as a closure', () { + expect( + Method( + (b) => b + ..requiredParameters.add( + Parameter((b) => b..name = 'a'), + ) + ..body = const Code(''), + ).closure, + equalsDart(r''' + (a) { } + '''), + ); + }); +} diff --git a/pkgs/code_builder/test/specs/mixin_test.dart b/pkgs/code_builder/test/specs/mixin_test.dart new file mode 100644 index 000000000..e167d02a5 --- /dev/null +++ b/pkgs/code_builder/test/specs/mixin_test.dart @@ -0,0 +1,150 @@ +// Copyright (c) 2021, 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. + +import 'package:code_builder/code_builder.dart'; +import 'package:test/test.dart'; + +import '../common.dart'; + +void main() { + useDartfmt(); + + test('should create a mixin', () { + expect( + Mixin((b) => b..name = 'Foo'), + equalsDart(r''' + mixin Foo {} + '''), + ); + }); + + test('should create a base mixin', () { + expect( + Mixin((b) => b + ..name = 'Foo' + ..base = true), + equalsDart(r''' + base mixin Foo {} + '''), + ); + }); + + test('should create a mixin with documentations', () { + expect( + Mixin( + (b) => b + ..name = 'Foo' + ..docs.addAll( + const [ + '/// My favorite mixin.', + ], + ), + ), + equalsDart(r''' + /// My favorite mixin. + mixin Foo {} + '''), + ); + }); + + test('should create a mixin with annotations', () { + expect( + Mixin( + (b) => b + ..name = 'Foo' + ..annotations.addAll([ + refer('deprecated'), + refer('Deprecated').call([literalString('This is an old mixin')]) + ]), + ), + equalsDart(r''' + @deprecated + @Deprecated('This is an old mixin') + mixin Foo {} + '''), + ); + }); + + test('should create a mixin with a generic type', () { + expect( + Mixin((b) => b + ..name = 'List' + ..types.add(refer('T'))), + equalsDart(r''' + mixin List {} + '''), + ); + }); + + test('should create a mixin with multiple generic types', () { + expect( + Mixin( + (b) => b + ..name = 'Map' + ..types.addAll([ + refer('K'), + refer('V'), + ]), + ), + equalsDart(r''' + mixin Map {} + '''), + ); + }); + + test('should create a mixin with a bound generic type', () { + expect( + Mixin((b) => b + ..name = 'Comparable' + ..types.add(TypeReference((b) => b + ..symbol = 'T' + ..bound = TypeReference((b) => b + ..symbol = 'Comparable' + ..types.add(refer('T').type))))), + equalsDart(r''' + mixin Comparable> {} + '''), + ); + }); + + test('should create a mixin on another mixin', () { + expect( + Mixin((b) => b + ..name = 'Foo' + ..on = TypeReference((b) => b.symbol = 'Bar')), + equalsDart(r''' + mixin Foo on Bar {} + '''), + ); + }); + + test('should create a mixin implementing another mixin', () { + expect( + Mixin((b) => b + ..name = 'Foo' + ..on = TypeReference((b) => b.symbol = 'Bar') + ..implements.add(TypeReference((b) => b.symbol = 'Foo'))), + equalsDart(r''' + mixin Foo on Bar implements Foo {} + '''), + ); + }); + + test('should create a mixin with a method', () { + expect( + Mixin((b) => b + ..name = 'Foo' + ..methods.add(Method((b) => b + ..name = 'foo' + ..body = const Code('return 1+ 2;')))), + equalsDart(r''' + mixin Foo { + foo() { + return 1 + 2; + } + } + '''), + ); + }); +} diff --git a/pkgs/code_builder/test/specs/record_type_test.dart b/pkgs/code_builder/test/specs/record_type_test.dart new file mode 100644 index 000000000..0084c507d --- /dev/null +++ b/pkgs/code_builder/test/specs/record_type_test.dart @@ -0,0 +1,68 @@ +// Copyright (c) 2023, 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. + +import 'package:code_builder/code_builder.dart'; +import 'package:test/test.dart'; + +import '../common.dart'; + +void main() { + late DartEmitter emitter; + + useDartfmt(); + + setUp(() => emitter = DartEmitter.scoped(useNullSafetySyntax: true)); + + final intRef = TypeReference((b) => b.symbol = 'int'); + TypeReference listRef(TypeReference argType) => TypeReference((b) => b + ..symbol = 'List' + ..types.add(argType)); + + test('should create an empty record type', () { + expect(RecordType(), equalsDart('()', emitter)); + }); + + test('should create a record type with positional fields', () { + expect( + RecordType((b) => b + ..positionalFieldTypes.addAll( + [intRef, listRef(intRef).rebuild((b) => b..isNullable = true)]) + ..isNullable = true), + equalsDart('(int, List?)?', emitter), + ); + }); + + test('should create a record type with one positional field', () { + expect(RecordType((b) => b..positionalFieldTypes.add(intRef)), + equalsDart('(int,)', emitter)); + }); + + test('should create a record type with named fields', () { + expect( + RecordType((b) => b + ..namedFieldTypes.addAll({ + 'named': intRef, + 'other': listRef(intRef), + })), + equalsDart('({int named, List other})', emitter)); + }); + + test('should create a record type with both positional and named fields', () { + expect( + RecordType((b) => b + ..positionalFieldTypes.add(listRef(intRef)) + ..namedFieldTypes.addAll({'named': intRef}) + ..isNullable = true), + equalsDart('(List, {int named})?', emitter)); + }); + + test('should create a nested record type', () { + expect( + RecordType((b) => b + ..positionalFieldTypes.add(RecordType((b) => b + ..namedFieldTypes.addAll({'named': intRef, 'other': intRef}) + ..isNullable = true))), + equalsDart('(({int named, int other})?,)', emitter)); + }); +} diff --git a/pkgs/code_builder/test/specs/type_reference_test.dart b/pkgs/code_builder/test/specs/type_reference_test.dart new file mode 100644 index 000000000..35365cbf9 --- /dev/null +++ b/pkgs/code_builder/test/specs/type_reference_test.dart @@ -0,0 +1,56 @@ +// Copyright (c) 2020, 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. + +import 'package:code_builder/code_builder.dart'; +import 'package:test/test.dart'; + +import '../common.dart'; + +void main() { + useDartfmt(); + + test('should create a nullable type in a pre-Null Safety library', () { + expect( + TypeReference((b) => b + ..symbol = 'Foo' + ..isNullable = true), + equalsDart(r''' + Foo + '''), + ); + }); + + group('in a Null Safety library', () { + late DartEmitter emitter; + + setUp(() => emitter = DartEmitter.scoped(useNullSafetySyntax: true)); + + test('should create a nullable type', () { + expect( + TypeReference((b) => b + ..symbol = 'Foo' + ..isNullable = true), + equalsDart(r'Foo?', emitter), + ); + }); + + test('should create a non-nullable type', () { + expect( + TypeReference((b) => b.symbol = 'Foo'), + equalsDart(r'Foo', emitter), + ); + }); + + test('should create a type with nullable type arguments', () { + expect( + TypeReference((b) => b + ..symbol = 'List' + ..types.add(TypeReference((b) => b + ..symbol = 'int' + ..isNullable = true))), + equalsDart(r'List', emitter), + ); + }); + }); +} diff --git a/pkgs/code_builder/tool/regenerate.sh b/pkgs/code_builder/tool/regenerate.sh new file mode 100755 index 000000000..722e98aed --- /dev/null +++ b/pkgs/code_builder/tool/regenerate.sh @@ -0,0 +1,7 @@ +# Copyright (c) 2023, 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. + +dart run build_runner generate-build-script +dart compile kernel .dart_tool/build/entrypoint/build.dart +dart .dart_tool/build/entrypoint/build.dill build --delete-conflicting-outputs