From 1aacdfa3bbb2fc6a72849f40057643aa6a423a17 Mon Sep 17 00:00:00 2001 From: Matan Lurey Date: Fri, 13 Oct 2017 08:37:22 -0700 Subject: [PATCH] Boom expression args (#133) * Initial support for Expression(s). * More in changelog. * Add context everywhere. * All tests passing. * More cleanup. * More expression work. * Improve calls, add property access. * Update README. * Actually update it. * Be nice to users. * Add literalString, literalMap. --- CHANGELOG.md | 22 ++-- README.md | 8 +- example/animal.dart | 2 +- example/scope.dart | 4 +- lib/code_builder.dart | 5 +- lib/src/emitter.dart | 29 ++++++ lib/src/specs/code.dart | 3 +- lib/src/specs/expression.dart | 139 +++++++++++++++++++++----- lib/src/specs/expression/binary.dart | 8 +- lib/src/specs/expression/invoke.dart | 25 ++++- lib/src/specs/expression/literal.dart | 54 ++++++++++ lib/src/specs/reference.dart | 5 + test/allocator_test.dart | 14 +-- test/e2e/injection_test.dart | 12 ++- test/specs/class_test.dart | 12 +-- test/specs/code/expression_test.dart | 99 ++++++++++++++++-- test/specs/field_test.dart | 2 +- test/specs/file_test.dart | 9 +- test/specs/method_test.dart | 16 +-- 19 files changed, 380 insertions(+), 88 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b01d3d7..51465cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,14 +8,10 @@ * 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`. -* `SpecVisitor`'s methods all have an optional `[T context]` parameter now. - * This makes it much easier to avoid allocating extra `StringBuffer`s. -* Removed `SimpleSpecVisitor` (it was unused). -* Removed `implements Reference` from `Method` and `Field`; not a lot of value. -* `equalsDart` removes insignificant white space before comparing results. - -* In process of adding classes/methods for writing bodies of `Code` fluently: +* Added many classes/methods for writing bodies of `Code` fluently: * `Expression` * `LiteralExpression` * `literal` @@ -23,10 +19,20 @@ * `literalBool` * `literalTrue` * `literalFalse` - * `literalList` + * `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. diff --git a/README.md b/README.md index 0ecfd00..b06de52 100644 --- a/README.md +++ b/README.md @@ -40,11 +40,11 @@ import 'package:dart_style/dart_style.dart'; void main() { final animal = new Class((b) => b ..name = 'Animal' - ..extend = const Reference('Organism').toType() + ..extend = refer('Organism').toType() ..methods.add(new Method.returnsVoid((b) => b ..name = 'eat' ..lambda = true - ..body = new Code((b) => b..code = 'print(\'Yum\')')))); + ..body = const Code('print(\'Yum\')')))); final emitter = const DartEmitter(); print(new DartFormatter().format('${animal.accept(emitter)}')); } @@ -70,11 +70,11 @@ void main() { new Method((b) => b ..body = new Code((b) => b.code = '') ..name = 'doThing' - ..returns = const Reference('Thing', 'package:a/a.dart')), + ..returns = refer('Thing', 'package:a/a.dart')), new Method((b) => b ..body = new Code((b) => b..code = '') ..name = 'doOther' - ..returns = const Reference('Other', 'package:b/b.dart')), + ..returns = refer('Other', 'package:b/b.dart')), ])); final emitter = new DartEmitter(new Allocator.simplePrefixing()); print(new DartFormatter().format('${library.accept(emitter)}')); diff --git a/example/animal.dart b/example/animal.dart index 9bee3b0..15fd3a6 100644 --- a/example/animal.dart +++ b/example/animal.dart @@ -8,7 +8,7 @@ import 'package:dart_style/dart_style.dart'; void main() { final animal = new Class((b) => b ..name = 'Animal' - ..extend = const Reference('Organism') + ..extend = refer('Organism') ..methods.add(new Method.returnsVoid((b) => b ..name = 'eat' ..lambda = true diff --git a/example/scope.dart b/example/scope.dart index 9ba85d6..90207a8 100644 --- a/example/scope.dart +++ b/example/scope.dart @@ -10,11 +10,11 @@ void main() { new Method((b) => b ..body = const Code('') ..name = 'doThing' - ..returns = const Reference('Thing', 'package:a/a.dart')), + ..returns = refer('Thing', 'package:a/a.dart')), new Method((b) => b ..body = const Code('') ..name = 'doOther' - ..returns = const Reference('Other', 'package:b/b.dart')), + ..returns = refer('Other', 'package:b/b.dart')), ])); final emitter = new DartEmitter(new Allocator.simplePrefixing()); print(new DartFormatter().format('${library.accept(emitter)}')); diff --git a/lib/code_builder.dart b/lib/code_builder.dart index cf41252..c52a078 100644 --- a/lib/code_builder.dart +++ b/lib/code_builder.dart @@ -28,6 +28,9 @@ export 'src/specs/expression.dart' literalBool, literalList, literalConstList, + literalMap, + literalConstMap, + literalString, literalTrue, literalFalse; export 'src/specs/field.dart' show Field, FieldBuilder, FieldModifier; @@ -40,5 +43,5 @@ export 'src/specs/method.dart' MethodType, Parameter, ParameterBuilder; -export 'src/specs/reference.dart' show Reference; +export 'src/specs/reference.dart' show refer, Reference; export 'src/specs/type_reference.dart' show TypeReference, TypeReferenceBuilder; diff --git a/lib/src/emitter.dart b/lib/src/emitter.dart index 6a4141a..d408571 100644 --- a/lib/src/emitter.dart +++ b/lib/src/emitter.dart @@ -2,6 +2,8 @@ // 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 'allocator.dart'; import 'base.dart'; import 'specs/annotation.dart'; @@ -17,6 +19,33 @@ import 'specs/reference.dart'; import 'specs/type_reference.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. +@visibleForTesting +void visitAll( + Iterable elements, + StringSink output, + void visit(T element), [ + 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; + } + final iterator = elements.iterator..moveNext(); + visit(iterator.current); + while (iterator.moveNext()) { + output.write(separator); + visit(iterator.current); + } +} + class DartEmitter extends Object with CodeEmitter, ExpressionEmitter implements SpecVisitor { diff --git a/lib/src/specs/code.dart b/lib/src/specs/code.dart index c290516..afbd5da 100644 --- a/lib/src/specs/code.dart +++ b/lib/src/specs/code.dart @@ -8,9 +8,10 @@ import 'package:meta/meta.dart'; import '../allocator.dart'; import '../base.dart'; -import '../specs/reference.dart'; import '../visitors.dart'; +import 'reference.dart'; + /// Returns a scoped symbol to [Reference], with an import prefix if needed. /// /// This is short-hand for [Allocator.allocate] in most implementations. diff --git a/lib/src/specs/expression.dart b/lib/src/specs/expression.dart index 7827db5..5daf622 100644 --- a/lib/src/specs/expression.dart +++ b/lib/src/specs/expression.dart @@ -7,6 +7,7 @@ library code_builder.src.specs.expression; import 'package:meta/meta.dart'; import '../base.dart'; +import '../emitter.dart'; import '../visitors.dart'; import 'code.dart'; import 'reference.dart'; @@ -25,24 +26,58 @@ abstract class Expression implements Spec { @override R accept(covariant ExpressionVisitor visitor, [R context]); + /// Returns the expression as a valid [Code] block. + Code asCode() => new _AsExpressionCode(this); + /// Returns the result of [this] `&&` [other]. Expression and(Expression other) { return new BinaryExpression._(toExpression(), other, '&&'); } /// Call this expression as a method. - Expression call() { - return new InvokeExpression._(this); + Expression call( + List positionalArguments, [ + Map namedArguments = const {}, + ]) { + return new InvokeExpression._( + this, + positionalArguments, + namedArguments, + ); + } + + /// Returns an expression accessing `.` on this expression. + Expression property(String name) { + return new BinaryExpression._( + this, + new LiteralExpression._(name), + '.', + false, + ); } /// Returns a new instance of this expression. - Expression newInstance() { - return new InvokeExpression._new(this); + Expression newInstance( + List positionalArguments, [ + Map namedArguments = const {}, + ]) { + return new InvokeExpression._new( + this, + positionalArguments, + namedArguments, + ); } /// Returns a const instance of this expression. - Expression constInstance() { - return new InvokeExpression._const(this); + Expression constInstance( + List positionalArguments, [ + Map namedArguments = const {}, + ]) { + return new InvokeExpression._const( + this, + positionalArguments, + namedArguments, + ); } /// May be overriden to support other types implementing [Expression]. @@ -50,6 +85,18 @@ abstract class Expression implements Spec { Expression toExpression() => this; } +/// Represents a [code] block that wraps an [Expression]. +class _AsExpressionCode implements Code { + final Expression code; + + const _AsExpressionCode(this.code); + + @override + R accept(CodeVisitor visitor, [R context]) { + return code.accept(visitor as ExpressionVisitor, context); + } +} + /// Knowledge of different types of expressions in Dart. /// /// **INTERNAL ONLY**. @@ -59,6 +106,7 @@ abstract class ExpressionVisitor implements SpecVisitor { T visitInvokeExpression(InvokeExpression expression, [T context]); T visitLiteralExpression(LiteralExpression expression, [T context]); T visitLiteralListExpression(LiteralListExpression expression, [T context]); + T visitLiteralMapExpression(LiteralMapExpression expression, [T context]); } /// Knowledge of how to write valid Dart code from [ExpressionVisitor]. @@ -68,12 +116,16 @@ abstract class ExpressionEmitter implements ExpressionVisitor { @override visitBinaryExpression(BinaryExpression expression, [StringSink output]) { output ??= new StringBuffer(); - return output - ..write(expression.left.accept(this)) - ..write(' ') - ..write(expression.operator) - ..write(' ') - ..write(expression.right.accept(this)); + expression.left.accept(this, output); + if (expression.addSpace) { + output.write(' '); + } + output.write(expression.operator); + if (expression.addSpace) { + output.write(' '); + } + expression.right.accept(this, output); + return output; } @override @@ -95,7 +147,19 @@ abstract class ExpressionEmitter implements ExpressionVisitor { break; } expression.target.accept(this, output); - return output..write('()'); + output.write('('); + visitAll(expression.positionalArguments, output, (spec) { + spec.accept(this, output); + }); + if (expression.positionalArguments.isNotEmpty && + expression.namedArguments.isNotEmpty) { + output.write(', '); + } + visitAll(expression.namedArguments.keys, output, (name) { + output..write(name)..write(': '); + expression.namedArguments[name].accept(this, output); + }); + return output..write(')'); } @override @@ -104,6 +168,14 @@ abstract class ExpressionEmitter implements ExpressionVisitor { 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); + } + @override visitLiteralListExpression( LiteralListExpression expression, [ @@ -119,18 +191,39 @@ abstract class ExpressionEmitter implements ExpressionVisitor { output.write('>'); } output.write('['); - // ignore: prefer_final_locals - for (var i = 0, l = expression.values.length; i < l; i++) { - final value = expression.values[i]; - if (value is Spec) { - value.accept(this, output); + visitAll(expression.values, output, (value) { + _acceptLiteral(value, output); + }); + return output..write(']'); + } + + @override + visitLiteralMapExpression( + LiteralMapExpression expression, [ + StringSink output, + ]) { + output ??= new StringBuffer(); + if (expression.isConst) { + output.write('const '); + } + if (expression.keyType != null) { + output.write('<'); + expression.keyType.accept(this, output); + output.write(', '); + if (expression.valueType == null) { + const Reference('dynamic', 'dart:core').accept(this, output); } else { - literal(value).accept(this, output); - } - if (i < l - 1) { - output.write(', '); + expression.valueType.accept(this, output); } + output.write('>'); } - return output..write(']'); + output.write('{'); + visitAll(expression.values.keys, output, (key) { + final value = expression.values[key]; + _acceptLiteral(key, output); + output.write(': '); + _acceptLiteral(value, output); + }); + return output..write('}'); } } diff --git a/lib/src/specs/expression/binary.dart b/lib/src/specs/expression/binary.dart index 8464bd0..aefd86d 100644 --- a/lib/src/specs/expression/binary.dart +++ b/lib/src/specs/expression/binary.dart @@ -9,8 +9,14 @@ class BinaryExpression extends Expression { final Expression left; final Expression right; final String operator; + final bool addSpace; - const BinaryExpression._(this.left, this.right, this.operator); + const BinaryExpression._( + this.left, + this.right, + this.operator, [ + this.addSpace = true, + ]); @override R accept(ExpressionVisitor visitor, [R context]) { diff --git a/lib/src/specs/expression/invoke.dart b/lib/src/specs/expression/invoke.dart index 207d657..b9e0edb 100644 --- a/lib/src/specs/expression/invoke.dart +++ b/lib/src/specs/expression/invoke.dart @@ -12,12 +12,29 @@ class InvokeExpression extends Expression { /// Optional; type of invocation. final InvokeExpressionType type; - const InvokeExpression._(this.target) : type = null; - - const InvokeExpression._new(this.target) + final List positionalArguments; + + final Map namedArguments; + + const InvokeExpression._( + this.target, + this.positionalArguments, [ + this.namedArguments = const {}, + ]) + : type = null; + + const InvokeExpression._new( + this.target, + this.positionalArguments, [ + this.namedArguments = const {}, + ]) : type = InvokeExpressionType.newInstance; - const InvokeExpression._const(this.target) + const InvokeExpression._const( + this.target, + this.positionalArguments, [ + this.namedArguments = const {}, + ]) : type = InvokeExpressionType.constInstance; @override diff --git a/lib/src/specs/expression/literal.dart b/lib/src/specs/expression/literal.dart index 60b6bd8..fc02d80 100644 --- a/lib/src/specs/expression/literal.dart +++ b/lib/src/specs/expression/literal.dart @@ -11,6 +11,15 @@ Expression literal(Object literal, {Expression onError(Object value)}) { 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 == null) { return literalNull; } @@ -32,6 +41,14 @@ Expression literalBool(bool value) => value ? literalTrue : literalFalse; /// Represents the literal value `null`. const Expression literalNull = const LiteralExpression._('null'); +/// Create a literal expression from a number [value]. +Expression literalNum(num value) => new LiteralExpression._('$value'); + +/// Create a literal expression from a string [value]. +/// +/// **NOTE**: The string is always formatted `''`. +Expression literalString(String value) => new LiteralExpression._("'$value'"); + /// Creates a literal list expression from [values]. LiteralListExpression literalList(List values, [Reference type]) { return new LiteralListExpression._(false, values, type); @@ -42,6 +59,24 @@ LiteralListExpression literalConstList(List values, [Reference type]) { return new LiteralListExpression._(true, values, type); } +/// Create a literal map expression from [values]. +LiteralMapExpression literalMap( + Map values, [ + Reference keyType, + Reference valueType, +]) { + return new LiteralMapExpression._(false, values, keyType, valueType); +} + +/// Create a literal `const` map expression from [values]. +LiteralMapExpression literalConstMap( + Map values, [ + Reference keyType, + Reference valueType, +]) { + return new LiteralMapExpression._(true, values, keyType, valueType); +} + /// Represents a literal value in Dart source code. /// /// For example, `new LiteralExpression('null')` should emit `null`. @@ -74,3 +109,22 @@ class LiteralListExpression extends Expression { return visitor.visitLiteralListExpression(this, context); } } + +class LiteralMapExpression extends Expression { + 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]) { + return visitor.visitLiteralMapExpression(this, context); + } +} diff --git a/lib/src/specs/reference.dart b/lib/src/specs/reference.dart index 1be28f0..78aa220 100644 --- a/lib/src/specs/reference.dart +++ b/lib/src/specs/reference.dart @@ -13,6 +13,11 @@ import 'code.dart'; import 'expression.dart'; import 'type_reference.dart'; +/// Short-hand for `new Reference(symbol, url)`. +Reference refer(String symbol, [String url]) { + return new 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 diff --git a/test/allocator_test.dart b/test/allocator_test.dart index dbd366d..0b7dab5 100644 --- a/test/allocator_test.dart +++ b/test/allocator_test.dart @@ -11,14 +11,14 @@ void main() { test('should return the exact (non-prefixed) symbol', () { allocator = new Allocator(); - expect(allocator.allocate(const Reference('Foo', 'package:foo')), 'Foo'); + expect(allocator.allocate(refer('Foo', 'package:foo')), 'Foo'); }); test('should collect import URLs', () { allocator = new Allocator() - ..allocate(const Reference('List', 'dart:core')) - ..allocate(const Reference('LinkedHashMap', 'dart:collection')) - ..allocate(const Reference('someSymbol')); + ..allocate(refer('List', 'dart:core')) + ..allocate(refer('LinkedHashMap', 'dart:collection')) + ..allocate(refer('someSymbol')); expect(allocator.imports.map((d) => d.url), [ 'dart:core', 'dart:collection', @@ -27,18 +27,18 @@ void main() { test('.none should do nothing', () { allocator = Allocator.none; - expect(allocator.allocate(const Reference('Foo', 'package:foo')), 'Foo'); + expect(allocator.allocate(refer('Foo', 'package:foo')), 'Foo'); expect(allocator.imports, isEmpty); }); test('.simplePrefixing should add import prefixes', () { allocator = new Allocator.simplePrefixing(); expect( - allocator.allocate(const Reference('List', 'dart:core')), + allocator.allocate(refer('List', 'dart:core')), 'List', ); expect( - allocator.allocate(const Reference('LinkedHashMap', 'dart:collection')), + allocator.allocate(refer('LinkedHashMap', 'dart:collection')), '_1.LinkedHashMap', ); expect(allocator.imports.map((d) => '${d.url} as ${d.as}'), [ diff --git a/test/e2e/injection_test.dart b/test/e2e/injection_test.dart index 36ee531..dc45ef4 100644 --- a/test/e2e/injection_test.dart +++ b/test/e2e/injection_test.dart @@ -8,9 +8,9 @@ import 'package:test/test.dart'; void main() { test('should generate a complex generated file', () { // Imports from an existing Dart library. - final $App = const Reference('App', 'package:app/app.dart'); - final $Module = const Reference('Module', 'package:app/module.dart'); - final $Thing = const Reference('Thing', 'package:app/thing.dart'); + 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 = new ClassBuilder() ..name = 'Injector' @@ -25,8 +25,10 @@ void main() { ..toThis = true)))) ..methods.add(new Method((b) => b ..name = 'getThing' - ..body = new Code.scope( - (a) => 'new ${a($Thing)}(_module.get1(), _module.get2())') + ..body = $Thing.newInstance([ + refer('_module').property('get1').call([]), + refer('_module').property('get2').call([]), + ]).asCode() ..lambda = true ..returns = $Thing ..annotations diff --git a/test/specs/class_test.dart b/test/specs/class_test.dart index 11a607f..54ba0a3 100644 --- a/test/specs/class_test.dart +++ b/test/specs/class_test.dart @@ -45,7 +45,7 @@ void main() { }); test('should create a class with annotations', () { - final $deprecated = const Reference('deprecated'); + final $deprecated = refer('deprecated'); expect( new Class( (b) => b @@ -67,7 +67,7 @@ void main() { expect( new Class((b) => b ..name = 'List' - ..types.add(const Reference('T'))), + ..types.add(refer('T'))), equalsDart(r''' class List {} '''), @@ -80,8 +80,8 @@ void main() { (b) => b ..name = 'Map' ..types.addAll([ - const Reference('K'), - const Reference('V'), + refer('K'), + refer('V'), ]), ), equalsDart(r''' @@ -98,7 +98,7 @@ void main() { ..symbol = 'T' ..bound = new TypeReference((b) => b ..symbol = 'Comparable' - ..types.add(const Reference('T').toType()))))), + ..types.add(refer('T').toType()))))), equalsDart(r''' class Comparable> {} '''), @@ -237,7 +237,7 @@ void main() { ..name = 'Foo' ..constructors.add(new Constructor((b) => b ..factory = true - ..redirect = const Reference('_Foo')))), + ..redirect = refer('_Foo')))), equalsDart(r''' class Foo { factory Foo() = _Foo; diff --git a/test/specs/code/expression_test.dart b/test/specs/code/expression_test.dart index a149618..a12323b 100644 --- a/test/specs/code/expression_test.dart +++ b/test/specs/code/expression_test.dart @@ -22,37 +22,114 @@ void main() { expect(literalConstList([]), equalsDart('const []')); }); - test('should emit a typed list', () { - expect(literalList([], const Reference('int')), equalsDart('[]')); + test('should emit an explicitly typed list', () { + expect(literalList([], 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, new Map(): null}"), + ); }); test('should emit a list of other literals and expressions', () { expect( - literalList([ - literalList([]), - literalTrue, - literalNull, - const Reference('Map').toExpression().newInstance() - ]), + literalList([[], true, null, refer('Map').newInstance([])]), equalsDart('[[], true, null, new Map()]'), ); }); test('should emit a type as an expression', () { - expect(const Reference('Map').toExpression(), equalsDart('Map')); + expect(refer('Map'), equalsDart('Map')); }); test('should emit a scoped type as an expression', () { expect( - const Reference('Foo', 'package:foo/foo.dart').toExpression(), + refer('Foo', 'package:foo/foo.dart'), equalsDart('_1.Foo', new DartEmitter(new Allocator.simplePrefixing())), ); }); test('should emit invoking new Type()', () { expect( - const Reference('Map').toExpression().newInstance(), + refer('Map').newInstance([]), equalsDart('new Map()'), ); }); + + test('should emit invoking a property accessor', () { + expect(refer('foo').property('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)'), + ); + }); } diff --git a/test/specs/field_test.dart b/test/specs/field_test.dart index 3cd4fb7..2f132ea 100644 --- a/test/specs/field_test.dart +++ b/test/specs/field_test.dart @@ -19,7 +19,7 @@ void main() { expect( new Field((b) => b ..name = 'foo' - ..type = const Reference('String')), + ..type = refer('String')), equalsDart(r''' String foo; '''), diff --git a/test/specs/file_test.dart b/test/specs/file_test.dart index fb32f60..cfdf3e2 100644 --- a/test/specs/file_test.dart +++ b/test/specs/file_test.dart @@ -7,7 +7,7 @@ import 'package:test/test.dart'; void main() { group('File', () { - const $linkedHashMap = const Reference('LinkedHashMap', 'dart:collection'); + final $LinkedHashMap = refer('LinkedHashMap', 'dart:collection'); test('should emit a source file with manual imports', () { expect( @@ -16,8 +16,7 @@ void main() { ..body.add(new Field((b) => b ..name = 'test' ..modifier = FieldModifier.final$ - ..assignment = - new Code.scope((a) => 'new ${a($linkedHashMap)}()')))), + ..assignment = $LinkedHashMap.newInstance([]).asCode()))), equalsDart(r''' import 'dart:collection'; @@ -84,7 +83,7 @@ void main() { ..name = 'test' ..modifier = FieldModifier.final$ ..assignment = - new Code.scope((a) => 'new ${a($linkedHashMap)}()')))), + new Code.scope((a) => 'new ${a($LinkedHashMap)}()')))), equalsDart(r''' import 'dart:collection'; @@ -100,7 +99,7 @@ void main() { ..name = 'test' ..modifier = FieldModifier.final$ ..assignment = - new Code.scope((a) => 'new ${a($linkedHashMap)}()')))), + new Code.scope((a) => 'new ${a($LinkedHashMap)}()')))), equalsDart(r''' import 'dart:collection' as _1; diff --git a/test/specs/method_test.dart b/test/specs/method_test.dart index 91dd5f7..f73f2fd 100644 --- a/test/specs/method_test.dart +++ b/test/specs/method_test.dart @@ -83,7 +83,7 @@ void main() { expect( new Method((b) => b ..name = 'foo' - ..returns = const Reference('String')), + ..returns = refer('String')), equalsDart(r''' String foo(); '''), @@ -103,7 +103,7 @@ void main() { expect( new Method((b) => b ..name = 'foo' - ..types.add(const Reference('T'))), + ..types.add(refer('T'))), equalsDart(r''' foo(); '''), @@ -147,12 +147,12 @@ void main() { }); test('should create a method with a body with references', () { - final linkedHashMap = const Reference('LinkedHashMap', 'dart:collection'); + final $LinkedHashMap = refer('LinkedHashMap', 'dart:collection'); expect( new Method((b) => b ..name = 'foo' ..body = new Code.scope( - (a) => 'return new ${a(linkedHashMap)}();', + (a) => 'return new ${a($LinkedHashMap)}();', )), equalsDart(r''' foo() { @@ -204,7 +204,7 @@ void main() { new Parameter( (b) => b ..name = 'i' - ..type = const Reference('int').toType(), + ..type = refer('int').toType(), ), ), ), @@ -221,18 +221,18 @@ void main() { ..name = 'foo' ..types.add(new TypeReference((b) => b ..symbol = 'T' - ..bound = const Reference('Iterable'))) + ..bound = refer('Iterable'))) ..requiredParameters.addAll([ new Parameter( (b) => b ..name = 't' - ..type = const Reference('T'), + ..type = refer('T'), ), new Parameter((b) => b ..name = 'x' ..type = new TypeReference((b) => b ..symbol = 'X' - ..types.add(const Reference('T')))), + ..types.add(refer('T')))), ]), ), equalsDart(r'''