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,
+  );
+}