From 0c1925078628417292bfca3e7dc9d114c54625eb Mon Sep 17 00:00:00 2001 From: Matan Lurey Date: Mon, 30 Jan 2017 15:45:55 -0800 Subject: [PATCH 1/3] Add support for generic types and as cast --- lib/dart/core.dart | 3 ++ lib/src/builders/expression.dart | 25 +++++++++--- lib/src/builders/expression/cast.dart | 24 +++++++++++ lib/src/builders/expression/invocation.dart | 28 +++++++++---- lib/src/builders/type.dart | 44 +++++++-------------- lib/src/builders/type/new_instance.dart | 34 +++++++++++----- test/builders/expression_test.dart | 20 ++++++++++ test/builders/type_test.dart | 10 ++--- test/e2e_test.dart | 2 +- 9 files changed, 132 insertions(+), 58 deletions(-) create mode 100644 lib/src/builders/expression/cast.dart diff --git a/lib/dart/core.dart b/lib/dart/core.dart index fbbe89f..5066e2c 100644 --- a/lib/dart/core.dart +++ b/lib/dart/core.dart @@ -53,6 +53,9 @@ class DartCore { /// References [dart_core.int]. final ReferenceBuilder int = _ref('int'); + /// References [dart_core.num]. + final ReferenceBuilder num = _ref('num'); + /// References [dart_core.AbstractClassInstantiationError]. final ReferenceBuilder AbstractClassInstantiationError = _ref('AbstractClassInstantiationError'); diff --git a/lib/src/builders/expression.dart b/lib/src/builders/expression.dart index b63fe06..f9cada2 100644 --- a/lib/src/builders/expression.dart +++ b/lib/src/builders/expression.dart @@ -23,6 +23,7 @@ part 'expression/assert.dart'; part 'expression/assign.dart'; part 'expression/await.dart'; part 'expression/cascade.dart'; +part 'expression/cast.dart'; part 'expression/index.dart'; part 'expression/invocation.dart'; part 'expression/negate.dart'; @@ -225,6 +226,9 @@ abstract class AbstractExpressionMixin implements ExpressionBuilder { return invocation; } + @override + ExpressionBuilder castAs(TypeBuilder type) => new _AsCast(this, type); + @override ExpressionBuilder cascade( Iterable create(ExpressionBuilder self), @@ -261,10 +265,15 @@ abstract class AbstractExpressionMixin implements ExpressionBuilder { @override InvocationBuilder invoke( String method, - Iterable positionalArguments, [ - Map namedArguments = const {}, - ]) { - final invocation = new InvocationBuilder._on(this, method); + Iterable positionalArguments, { + Iterable genericTypes: const [], + Map namedArguments: const {}, + }) { + final invocation = new InvocationBuilder._on( + this, + method, + genericTypes.toList(), + ); positionalArguments.forEach(invocation.addPositionalArgument); namedArguments.forEach(invocation.addNamedArgument); return invocation; @@ -385,6 +394,9 @@ abstract class ExpressionBuilder Map namedArguments, ]); + /// Returns the expression casted as [type]. + ExpressionBuilder castAs(TypeBuilder type); + /// Return as an [ExpressionBuilder] with `..` appended. ExpressionBuilder cascade( Iterable create(ExpressionBuilder self), @@ -405,9 +417,10 @@ abstract class ExpressionBuilder /// Returns as an [InvocationBuilder] on [method] of this expression. InvocationBuilder invoke( String method, - Iterable positionalArguments, [ + Iterable positionalArguments, { + Iterable genericTypes, Map namedArguments, - ]); + }); /// Returns as an [ExpressionBuilder] negating using the `!` operator. ExpressionBuilder negate(); diff --git a/lib/src/builders/expression/cast.dart b/lib/src/builders/expression/cast.dart new file mode 100644 index 0000000..e18ecfa --- /dev/null +++ b/lib/src/builders/expression/cast.dart @@ -0,0 +1,24 @@ +// 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 code_builder.src.builders.expression; + +class _AsCast extends TopLevelMixin with AbstractExpressionMixin { + final ExpressionBuilder _target; + final TypeBuilder _type; + + _AsCast(this._target, this._type); + + @override + AstNode buildAst([Scope scope]) => buildExpression(scope); + + @override + Expression buildExpression([Scope scope]) { + return astFactory.asExpression( + _target.buildExpression(scope), + $as, + _type.buildType(scope), + ); + } +} diff --git a/lib/src/builders/expression/invocation.dart b/lib/src/builders/expression/invocation.dart index 684b215..d0958af 100644 --- a/lib/src/builders/expression/invocation.dart +++ b/lib/src/builders/expression/invocation.dart @@ -20,10 +20,13 @@ abstract class AbstractInvocationBuilderMixin implements InvocationBuilder { } /// Returns an [ArgumentList] AST. - ArgumentList buildArgumentList([Scope scope]) { + ArgumentList buildArgumentList({ + Scope scope, + }) { final allArguments = []; allArguments.addAll( - _positional.map/**/((e) => e.buildExpression(scope))); + _positional.map/**/((e) => e.buildExpression(scope)), + ); _named.forEach((name, e) { allArguments.add(astFactory.namedExpression( astFactory.label( @@ -51,8 +54,12 @@ abstract class InvocationBuilder return new _FunctionInvocationBuilder(target); } - factory InvocationBuilder._on(ExpressionBuilder target, String method) { - return new _MethodInvocationBuilder(target, method); + factory InvocationBuilder._on( + ExpressionBuilder target, + String method, + List generics, + ) { + return new _MethodInvocationBuilder(target, method, generics); } /// Adds [argument] as a [name]d argument to this method call. @@ -74,7 +81,7 @@ class _FunctionInvocationBuilder extends Object return astFactory.functionExpressionInvocation( _target.buildExpression(scope), null, - buildArgumentList(scope), + buildArgumentList(scope: scope), ); } } @@ -82,10 +89,11 @@ class _FunctionInvocationBuilder extends Object class _MethodInvocationBuilder extends Object with AbstractInvocationBuilderMixin, AbstractExpressionMixin, TopLevelMixin implements InvocationBuilder { + final List _generics; final String _method; final ExpressionBuilder _target; - _MethodInvocationBuilder(this._target, this._method); + _MethodInvocationBuilder(this._target, this._method, this._generics); @override Expression buildExpression([Scope scope]) { @@ -93,8 +101,12 @@ class _MethodInvocationBuilder extends Object _target.buildExpression(scope), $period, stringIdentifier(_method), - null, - buildArgumentList(scope), + _generics.isNotEmpty ? new TypeArgumentList( + $openBracket, + _generics.map((t) => t.buildType(scope)).toList(), + $closeBracket, + ) : null, + buildArgumentList(scope: scope), ); } } diff --git a/lib/src/builders/type.dart b/lib/src/builders/type.dart index 7653c4c..0aa1f9d 100644 --- a/lib/src/builders/type.dart +++ b/lib/src/builders/type.dart @@ -23,42 +23,28 @@ part 'type/new_instance.dart'; abstract class AbstractTypeBuilderMixin { /// Invokes `const` on this type. NewInstanceBuilder constInstance( - Iterable positional, [ - Map named = const {}, - ]) { - final builder = new NewInstanceBuilder._const(this); - _addArguments(builder, positional, named); - return builder; - } - - /// Invokes `const` on this type with a [name]d constructor. - NewInstanceBuilder namedConstInstance( + Iterable positionalArguments, { String name, - Iterable positional, [ - Map named = const {}, - ]) { - final builder = new NewInstanceBuilder._const(this, name); - _addArguments(builder, positional, named); + Map namedArguments: const {}, + }) { + final builder = new NewInstanceBuilder._const( + this, + name: name, + ); + _addArguments(builder, positionalArguments, namedArguments); return builder; } /// Invokes `new` on this type. NewInstanceBuilder newInstance( - Iterable positional, [ - Map named = const {}, - ]) { - final builder = new NewInstanceBuilder._new(this); - _addArguments(builder, positional, named); - return builder; - } - - /// Invokes `new` on this type with a [name]d constructor. - NewInstanceBuilder namedNewInstance( + Iterable positional, { String name, - Iterable positional, [ - Map named = const {}, - ]) { - final builder = new NewInstanceBuilder._new(this, name); + Map named: const {}, + }) { + final builder = new NewInstanceBuilder._new( + this, + name: name, + ); _addArguments(builder, positional, named); return builder; } diff --git a/lib/src/builders/type/new_instance.dart b/lib/src/builders/type/new_instance.dart index 9c48bc8..84dc1d4 100644 --- a/lib/src/builders/type/new_instance.dart +++ b/lib/src/builders/type/new_instance.dart @@ -8,17 +8,29 @@ part of code_builder.src.builders.type; /// /// See [TypeBuilder]: /// - [TypeBuilder.constInstance] -/// - [TypeBuilder.namedConstInstance] /// - [TypeBuilder.newInstance] -/// - [TypeBuilder.namedNewInstance] abstract class NewInstanceBuilder implements AnnotationBuilder, InvocationBuilder { - factory NewInstanceBuilder._const(TypeBuilder type, [String name]) { - return new _NewInvocationBuilderImpl(Keyword.CONST, type, name); + factory NewInstanceBuilder._const( + TypeBuilder type, { + String name, + }) { + return new _NewInvocationBuilderImpl( + Keyword.CONST, + type, + name, + ); } - factory NewInstanceBuilder._new(TypeBuilder type, [String name]) { - return new _NewInvocationBuilderImpl(Keyword.NEW, type, name); + factory NewInstanceBuilder._new( + TypeBuilder type, { + String name, + }) { + return new _NewInvocationBuilderImpl( + Keyword.NEW, + type, + name, + ); } } @@ -29,7 +41,11 @@ class _NewInvocationBuilderImpl extends Object final Keyword _keyword; final TypeBuilder _type; - _NewInvocationBuilderImpl(this._keyword, this._type, [this._name]); + _NewInvocationBuilderImpl( + this._keyword, + this._type, + this._name, + ); @override Annotation buildAnnotation([Scope scope]) { @@ -38,7 +54,7 @@ class _NewInvocationBuilderImpl extends Object _type.buildType(scope).name, $period, _name != null ? stringIdentifier(_name) : null, - buildArgumentList(scope), + buildArgumentList(scope: scope), ); } @@ -51,7 +67,7 @@ class _NewInvocationBuilderImpl extends Object _name != null ? $period : null, _name != null ? stringIdentifier(_name) : null, ), - buildArgumentList(scope), + buildArgumentList(scope: scope), ); } } diff --git a/test/builders/expression_test.dart b/test/builders/expression_test.dart index 99c6670..b60e8a4 100644 --- a/test/builders/expression_test.dart +++ b/test/builders/expression_test.dart @@ -178,6 +178,17 @@ void main() { ); }); + test('should call a method on an expression with generic parameters', () { + expect( + explicitThis.invoke('doThing', [literal(true)], genericTypes: [ + lib$core.bool, + ]), + equalsSource(r''' + this.doThing(true) + '''), + ); + }); + test('should emit an identical() expression', () { expect( literal(true).identical(literal(false)), @@ -364,4 +375,13 @@ void main() { '''), ); }); + + test('should emit casted as another type', () { + expect( + literal(1.0).castAs(lib$core.num), + equalsSource(r''' + 1.0 as num + '''), + ); + }); } diff --git a/test/builders/type_test.dart b/test/builders/type_test.dart index 436edf0..58943d5 100644 --- a/test/builders/type_test.dart +++ b/test/builders/type_test.dart @@ -43,9 +43,9 @@ void main() { test('emits a new List.from', () { expect( - lib$core.List.namedNewInstance('from', [ + lib$core.List.newInstance( [ literal([1, 2, 3]), - ]), + ], name: 'from'), equalsSource(r''' new List.from([1, 2, 3]) '''), @@ -80,10 +80,10 @@ void main() { test('emits a named const constructor as an annotation', () { expect( clazz('Animal', [ - reference('Component').namedConstInstance( - 'stateful', + reference('Component').constInstance( [], - { + name: 'stateful', + namedArguments: { 'selector': literal('animal'), }, ), diff --git a/test/e2e_test.dart b/test/e2e_test.dart index 9a00f7e..1904812 100644 --- a/test/e2e_test.dart +++ b/test/e2e_test.dart @@ -88,7 +88,7 @@ void main() { ..addMethod(new MethodBuilder( 'instantiateAndReturnNamedThing', returnType: thingRef, - )..addStatement(thingRef.namedNewInstance('named', []).asReturn()))); + )..addStatement(thingRef.newInstance([], name: 'named').asReturn()))); expect( lib, equalsSource( From 699746c0357db7b349d02a2c739395de64cb0c6a Mon Sep 17 00:00:00 2001 From: Matan Lurey Date: Mon, 30 Jan 2017 15:57:09 -0800 Subject: [PATCH 2/3] Update CHANGELOG and pubspec. --- CHANGELOG.md | 65 ++++++++++++++++++++- lib/src/builders/expression.dart | 10 ++-- lib/src/builders/expression/invocation.dart | 12 ++-- pubspec.yaml | 2 +- test/builders/expression_test.dart | 24 +++++++- test/builders/type_test.dart | 2 +- 6 files changed, 98 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f26dbe..cf0580e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,67 @@ +## 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 `name: `: + +```dart +expect( + reference('Foo').newInstance([], name: '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 CHANGE: `MethodModifier.async`, `MethodModifier.asyncStar` and - `MethodModifier.syncStar` are now `MethodModifier.asAsync`, - `MethodModifier.asAsyncStar` and `MethodModifier.asSyncStar` +### 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 diff --git a/lib/src/builders/expression.dart b/lib/src/builders/expression.dart index f9cada2..213c744 100644 --- a/lib/src/builders/expression.dart +++ b/lib/src/builders/expression.dart @@ -217,9 +217,9 @@ abstract class AbstractExpressionMixin implements ExpressionBuilder { @override InvocationBuilder call( - Iterable positionalArguments, [ - Map namedArguments = const {}, - ]) { + Iterable positionalArguments, { + Map namedArguments: const {}, + }) { final invocation = new InvocationBuilder._(this); positionalArguments.forEach(invocation.addPositionalArgument); namedArguments.forEach(invocation.addNamedArgument); @@ -390,9 +390,9 @@ abstract class ExpressionBuilder /// Returns as an [InvocationBuilder] with arguments added. InvocationBuilder call( - Iterable positionalArguments, [ + Iterable positionalArguments, { Map namedArguments, - ]); + }); /// Returns the expression casted as [type]. ExpressionBuilder castAs(TypeBuilder type); diff --git a/lib/src/builders/expression/invocation.dart b/lib/src/builders/expression/invocation.dart index d0958af..2868280 100644 --- a/lib/src/builders/expression/invocation.dart +++ b/lib/src/builders/expression/invocation.dart @@ -101,11 +101,13 @@ class _MethodInvocationBuilder extends Object _target.buildExpression(scope), $period, stringIdentifier(_method), - _generics.isNotEmpty ? new TypeArgumentList( - $openBracket, - _generics.map((t) => t.buildType(scope)).toList(), - $closeBracket, - ) : null, + _generics.isNotEmpty + ? new TypeArgumentList( + $openBracket, + _generics.map((t) => t.buildType(scope)).toList(), + $closeBracket, + ) + : null, buildArgumentList(scope: scope), ); } diff --git a/pubspec.yaml b/pubspec.yaml index b148031..9ed6f6b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: code_builder -version: 1.0.0-beta+2 +version: 1.0.0-beta+3 description: A fluent API for generating Dart code author: Dart Team homepage: https://github.com/dart-lang/code_builder diff --git a/test/builders/expression_test.dart b/test/builders/expression_test.dart index b60e8a4..fdf621e 100644 --- a/test/builders/expression_test.dart +++ b/test/builders/expression_test.dart @@ -159,7 +159,7 @@ void main() { expect( reference('doThing').call( [literal(true)], - { + namedArguments: { 'otherFlag': literal(false), }, ), @@ -180,7 +180,9 @@ void main() { test('should call a method on an expression with generic parameters', () { expect( - explicitThis.invoke('doThing', [literal(true)], genericTypes: [ + explicitThis.invoke('doThing', [ + literal(true) + ], genericTypes: [ lib$core.bool, ]), equalsSource(r''' @@ -324,6 +326,24 @@ void main() { ); }); + test('should emit a newInstance with a named constructor', () { + expect( + reference('Foo').newInstance([], name: 'other'), + equalsSource(r''' + new Foo.other() + '''), + ); + }); + + test('should emit a constInstance with a named constructor', () { + expect( + reference('Foo').constInstance([], name: 'other'), + equalsSource(r''' + const Foo.other() + '''), + ); + }); + test('should scope on newInstance', () { expect( reference('Foo', 'package:foo/foo.dart').newInstance([]).asReturn(), diff --git a/test/builders/type_test.dart b/test/builders/type_test.dart index 58943d5..2f10148 100644 --- a/test/builders/type_test.dart +++ b/test/builders/type_test.dart @@ -43,7 +43,7 @@ void main() { test('emits a new List.from', () { expect( - lib$core.List.newInstance( [ + lib$core.List.newInstance([ literal([1, 2, 3]), ], name: 'from'), equalsSource(r''' From 33a11077ac67c98a5f30780674afc96175d5e2da Mon Sep 17 00:00:00 2001 From: Matan Lurey Date: Tue, 31 Jan 2017 13:24:56 -0800 Subject: [PATCH 3/3] Rename name: to constructor: --- CHANGELOG.md | 4 ++-- lib/src/builders/type.dart | 8 ++++---- lib/src/builders/type/new_instance.dart | 18 +++++++++--------- test/builders/expression_test.dart | 4 ++-- test/builders/type_test.dart | 4 ++-- test/e2e_test.dart | 4 +++- 6 files changed, 22 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cf0580e..b98c0d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,11 +26,11 @@ expect( ### BREAKING CHANGES -- Removed `namedNewInstance` and `namedConstInstance`, replaced with `name: `: +- Removed `namedNewInstance` and `namedConstInstance`, replaced with `constructor: `: ```dart expect( - reference('Foo').newInstance([], name: 'other'), + reference('Foo').newInstance([], constructor: 'other'), equalsSource(r''' new Foo.other() '''), diff --git a/lib/src/builders/type.dart b/lib/src/builders/type.dart index 0aa1f9d..f913655 100644 --- a/lib/src/builders/type.dart +++ b/lib/src/builders/type.dart @@ -24,12 +24,12 @@ abstract class AbstractTypeBuilderMixin { /// Invokes `const` on this type. NewInstanceBuilder constInstance( Iterable positionalArguments, { - String name, + String constructor, Map namedArguments: const {}, }) { final builder = new NewInstanceBuilder._const( this, - name: name, + constructor: constructor, ); _addArguments(builder, positionalArguments, namedArguments); return builder; @@ -38,12 +38,12 @@ abstract class AbstractTypeBuilderMixin { /// Invokes `new` on this type. NewInstanceBuilder newInstance( Iterable positional, { - String name, + String constructor, Map named: const {}, }) { final builder = new NewInstanceBuilder._new( this, - name: name, + constructor: constructor, ); _addArguments(builder, positional, named); return builder; diff --git a/lib/src/builders/type/new_instance.dart b/lib/src/builders/type/new_instance.dart index 84dc1d4..0a0441c 100644 --- a/lib/src/builders/type/new_instance.dart +++ b/lib/src/builders/type/new_instance.dart @@ -13,23 +13,23 @@ abstract class NewInstanceBuilder implements AnnotationBuilder, InvocationBuilder { factory NewInstanceBuilder._const( TypeBuilder type, { - String name, + String constructor, }) { return new _NewInvocationBuilderImpl( Keyword.CONST, type, - name, + constructor, ); } factory NewInstanceBuilder._new( TypeBuilder type, { - String name, + String constructor, }) { return new _NewInvocationBuilderImpl( Keyword.NEW, type, - name, + constructor, ); } } @@ -37,14 +37,14 @@ abstract class NewInstanceBuilder class _NewInvocationBuilderImpl extends Object with AbstractExpressionMixin, AbstractInvocationBuilderMixin, TopLevelMixin implements NewInstanceBuilder { - final String _name; + final String _constructor; final Keyword _keyword; final TypeBuilder _type; _NewInvocationBuilderImpl( this._keyword, this._type, - this._name, + this._constructor, ); @override @@ -53,7 +53,7 @@ class _NewInvocationBuilderImpl extends Object $at, _type.buildType(scope).name, $period, - _name != null ? stringIdentifier(_name) : null, + _constructor != null ? stringIdentifier(_constructor) : null, buildArgumentList(scope: scope), ); } @@ -64,8 +64,8 @@ class _NewInvocationBuilderImpl extends Object new KeywordToken(_keyword, 0), astFactory.constructorName( _type.buildType(scope), - _name != null ? $period : null, - _name != null ? stringIdentifier(_name) : null, + _constructor != null ? $period : null, + _constructor != null ? stringIdentifier(_constructor) : null, ), buildArgumentList(scope: scope), ); diff --git a/test/builders/expression_test.dart b/test/builders/expression_test.dart index fdf621e..571d0b9 100644 --- a/test/builders/expression_test.dart +++ b/test/builders/expression_test.dart @@ -328,7 +328,7 @@ void main() { test('should emit a newInstance with a named constructor', () { expect( - reference('Foo').newInstance([], name: 'other'), + reference('Foo').newInstance([], constructor: 'other'), equalsSource(r''' new Foo.other() '''), @@ -337,7 +337,7 @@ void main() { test('should emit a constInstance with a named constructor', () { expect( - reference('Foo').constInstance([], name: 'other'), + reference('Foo').constInstance([], constructor: 'other'), equalsSource(r''' const Foo.other() '''), diff --git a/test/builders/type_test.dart b/test/builders/type_test.dart index 2f10148..14383d8 100644 --- a/test/builders/type_test.dart +++ b/test/builders/type_test.dart @@ -45,7 +45,7 @@ void main() { expect( lib$core.List.newInstance([ literal([1, 2, 3]), - ], name: 'from'), + ], constructor: 'from'), equalsSource(r''' new List.from([1, 2, 3]) '''), @@ -82,7 +82,7 @@ void main() { clazz('Animal', [ reference('Component').constInstance( [], - name: 'stateful', + constructor: 'stateful', namedArguments: { 'selector': literal('animal'), }, diff --git a/test/e2e_test.dart b/test/e2e_test.dart index 1904812..7060a1e 100644 --- a/test/e2e_test.dart +++ b/test/e2e_test.dart @@ -88,7 +88,9 @@ void main() { ..addMethod(new MethodBuilder( 'instantiateAndReturnNamedThing', returnType: thingRef, - )..addStatement(thingRef.newInstance([], name: 'named').asReturn()))); + ) + ..addStatement( + thingRef.newInstance([], constructor: 'named').asReturn()))); expect( lib, equalsSource(