Add new lint, sized_box_for_whitespace (#2049)
* Add new lint, prefer SizedBox over Container
* Rename lint
* Fix tests, thanks to @a14n
diff --git a/example/all.yaml b/example/all.yaml
index 423bc3c..8598d91 100644
--- a/example/all.yaml
+++ b/example/all.yaml
@@ -129,6 +129,7 @@
- provide_deprecation_message
- public_member_api_docs
- recursive_getters
+ - sized_box_for_whitespace
- slash_for_doc_comments
- sort_child_properties_last
- sort_constructors_first
diff --git a/lib/src/rules.dart b/lib/src/rules.dart
index 286ec02..5bc3f38 100644
--- a/lib/src/rules.dart
+++ b/lib/src/rules.dart
@@ -132,6 +132,7 @@
import 'rules/pub/sort_pub_dependencies.dart';
import 'rules/public_member_api_docs.dart';
import 'rules/recursive_getters.dart';
+import 'rules/sized_box_for_whitespace.dart';
import 'rules/slash_for_doc_comments.dart';
import 'rules/sort_child_properties_last.dart';
import 'rules/sort_constructors_first.dart';
@@ -302,6 +303,7 @@
..register(PublicMemberApiDocs())
..register(PubPackageNames())
..register(RecursiveGetters())
+ ..register(SizedBoxForWhitespace())
..register(SlashForDocComments())
..register(SortChildPropertiesLast())
..register(SortConstructorsFirst())
diff --git a/lib/src/rules/sized_box_for_whitespace.dart b/lib/src/rules/sized_box_for_whitespace.dart
new file mode 100644
index 0000000..50c8644
--- /dev/null
+++ b/lib/src/rules/sized_box_for_whitespace.dart
@@ -0,0 +1,100 @@
+// 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:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/ast/visitor.dart';
+
+import '../analyzer.dart';
+import '../util/flutter_utils.dart';
+
+const _desc = r'SizedBox for whitespace.';
+
+const _details = r'''Use SizedBox to add whitespace to a layout.
+
+A `Container` is a heavier Widget than a `SizedBox`, and as bonus, `SizedBox`
+has a `const` constructor.
+
+**BAD:**
+```
+Widget buildRow() {
+ return Row(
+ children: <Widget>[
+ const MyLogo(),
+ Container(width: 4),
+ const Expanded(
+ child: Text('...'),
+ ),
+ ],
+ );
+}
+```
+
+**GOOD:**
+```
+Widget buildRow() {
+ return Row(
+ children: const <Widget>[
+ MyLogo(),
+ SizedBox(width: 4),
+ Expanded(
+ child: Text('...'),
+ ),
+ ],
+ );
+}
+```
+''';
+
+class SizedBoxForWhitespace extends LintRule implements NodeLintRule {
+ SizedBoxForWhitespace()
+ : super(
+ name: 'sized_box_for_whitespace',
+ description: _desc,
+ details: _details,
+ group: Group.style);
+
+ @override
+ void registerNodeProcessors(NodeLintRegistry registry,
+ [LinterContext context]) {
+ final visitor = _Visitor(this);
+
+ registry.addInstanceCreationExpression(this, visitor);
+ }
+}
+
+class _Visitor extends SimpleAstVisitor {
+ final LintRule rule;
+
+ _Visitor(this.rule);
+
+ @override
+ void visitInstanceCreationExpression(InstanceCreationExpression node) {
+ if (!isExactWidgetTypeContainer(node.staticType)) {
+ return;
+ }
+
+ final visitor = _WidthOrHeightArgumentVisitor();
+ node.visitChildren(visitor);
+ if (visitor.seenWidthOrHeight && !visitor.seenOtherParams) {
+ rule.reportLint(node.constructorName);
+ }
+ }
+}
+
+class _WidthOrHeightArgumentVisitor extends SimpleAstVisitor<void> {
+ var seenWidthOrHeight = false;
+ var seenOtherParams = false;
+
+ @override
+ void visitArgumentList(ArgumentList node) {
+ for (final arg in node.arguments) {
+ if (arg is NamedExpression &&
+ (arg.name.label.name == 'width' || arg.name.label.name == 'height')) {
+ seenWidthOrHeight = true;
+ } else {
+ seenOtherParams = true;
+ }
+ }
+ }
+}
diff --git a/test/rules/sized_box_for_whitespace.dart b/test/rules/sized_box_for_whitespace.dart
new file mode 100644
index 0000000..32f0ef9
--- /dev/null
+++ b/test/rules/sized_box_for_whitespace.dart
@@ -0,0 +1,59 @@
+// 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.
+
+// test w/ `pub run test -N sized_box_for_whitespace`
+
+import 'package:flutter/widgets.dart';
+
+Widget containerWithChild() {
+ return Container( // OK
+ child: Row(),
+ );
+}
+
+Widget containerWithChildAndWidth() {
+ return Container( // OK
+ width: 10,
+ child: Row(),
+ );
+}
+
+Widget containerWithChildAndHeight() {
+ return Container( // OK
+ height: 10,
+ child: Column(),
+ );
+}
+
+Widget containerWithChildWidthAndHeight() {
+ return Container( // OK
+ width: 10,
+ height: 10,
+ child: Row(),
+ );
+}
+
+Widget emptyContainer() {
+ return Container( // OK
+ );
+}
+
+Widget emptyContainerWithWidth() {
+ return Container( // LINT
+ width: 10,
+ );
+}
+
+Widget emptyContainerWithHeight() {
+ return Container( // LINT
+ height:10,
+ );
+}
+
+Widget emptyContainerWithWidthAndHeight() {
+ return Container( // LINT
+ width: 10,
+ height: 10,
+ );
+}