Skip to content
This repository was archived by the owner on Nov 20, 2024. It is now read-only.

spread_collections experiment #1485

Merged
merged 1 commit into from
Apr 4, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 109 additions & 0 deletions lib/src/rules/experiments/spread_collections.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// Copyright (c) 2019, 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:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:linter/src/analyzer.dart';

const _desc = r'Use spread collections when possible.';

const _details = r'''

Use spread collections when possible.

Collection literals are excellent when you want to create a new collection out
of individual items. But, when existing items are already stored in another
collection, spread collection syntax leads to simpler code.

**BAD:**

```
Widget build(BuildContext context) {
return CupertinoPageScaffold(
child: ListView(
children: [
Tab2Header(),
]..addAll(buildTab2Conversation()),
),
);
}
```

```
var ints = [1, 2, 3];
print(['a']..addAll(ints.map((i) => i.toString()))..addAll(['c']));
```

```
var things;
var l = ['a']..addAll(things ?? const []);
```


**GOOD:**

```
Widget build(BuildContext context) {
return CupertinoPageScaffold(
child: ListView(
children: [
Tab2Header(),
...buildTab2Conversation(),
],
),
);
}
```

```
var ints = [1, 2, 3];
print(['a', ...ints.map((i) => i.toString()), 'c');
```

```
var things;
var l = ['a', ...?things];
```
''';

class SpreadCollections extends LintRule implements NodeLintRule {
SpreadCollections()
: super(
name: 'spread_collections',
description: _desc,
details: _details,
group: Group.style);

@override
void registerNodeProcessors(NodeLintRegistry registry,
[LinterContext context]) {
final visitor = new _Visitor(this);
registry.addMethodInvocation(this, visitor);
}
}

class _Visitor extends SimpleAstVisitor<void> {
final LintRule rule;

_Visitor(this.rule);

@override
void visitMethodInvocation(MethodInvocation invocation) {
if (invocation.methodName.name != 'addAll' ||
!invocation.isCascaded ||
invocation.argumentList.arguments.length != 1) {
return;
}

CascadeExpression cascade = invocation.thisOrAncestorOfType();
NodeList<Expression> sections = cascade.cascadeSections;
Expression target = cascade.target;
if (target is! ListLiteral || sections[0] != invocation) {
return;
}

rule.reportLint(invocation.methodName);
}
}
5 changes: 4 additions & 1 deletion test/integration_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import 'package:test/test.dart';
import 'package:yaml/yaml.dart';

import 'mocks.dart';
import 'rules/experiments/experiments.dart';

main() {
defineTests();
Expand Down Expand Up @@ -820,7 +821,9 @@ defineTests() {
expect(
configuredLints,
unorderedEquals(Analyzer.facade.registeredRules
.where((r) => r.maturity != Maturity.deprecated)
.where((r) =>
r.maturity != Maturity.deprecated &&
!experiments.contains(r))
.map((r) => r.name)));
});
});
Expand Down
17 changes: 17 additions & 0 deletions test/rule_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import 'package:path/path.dart' as p;
import 'package:test/test.dart';

import 'mock_sdk.dart';
import 'rules/experiments/experiments.dart';

main() {
defineSanityTests();
Expand Down Expand Up @@ -56,6 +57,22 @@ defineRuleTests() {
// });
// }
});
group('experiments', () {
registerLintRuleExperiments();

for (var entry
in new Directory(p.join(ruleDir, 'experiments')).listSync()) {
if (entry is! Directory) continue;
var analysisOptionsFile =
new File(p.join(entry.path, 'analysis_options.yaml'));
var analysisOptions = analysisOptionsFile.readAsStringSync();
var ruleName = p.basename(entry.path);
var testFile = new File(p.join(entry.path, '$ruleName.dart'));
testRule(p.basename(ruleName), testFile,
analysisOptions: analysisOptions);
}
});

group('pub', () {
for (var entry in new Directory(p.join(ruleDir, 'pub')).listSync()) {
if (entry is! Directory) continue;
Expand Down
16 changes: 16 additions & 0 deletions test/rules/experiments/experiments.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (c) 2019, 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:linter/src/analyzer.dart';
import 'package:linter/src/rules/experiments/spread_collections.dart';


final experiments = [
new SpreadCollections(),
];

void registerLintRuleExperiments() {
experiments.forEach(Analyzer.facade.register);
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
analyzer:
enable-experiment:
- control-flow-collections
33 changes: 33 additions & 0 deletions test/rules/experiments/spread_collections/spread_collections.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright (c) 2019, 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.


f() {
var ints = [1, 2, 3];
print(['a']..addAll(ints.map((i) => i.toString()))..addAll(['c'])); // LINT
}

var l = ['a']..addAll(['b']); // LINT

var l1 = [];
var l2 = l1..addAll(['b']); //OK

var things;
var l3 = ['a']..addAll(things ?? const []); // LINT
var l4 = ['a']..addAll(things ?? []); //LINT

// Control flow.

bool condition;
var l5 = ['a']..addAll(condition ? things : const []); // LINT
var l6 = ['a']..addAll(condition ? things : []); // LINT

class A {
void addAll(Iterable iterable) { }
}

g() {
new A()..addAll(['a']); // OK
}