Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

use_decorated_box #3061

Merged
merged 3 commits into from
Nov 17, 2021
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
3 changes: 2 additions & 1 deletion AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,5 @@ TAKAHASHI Shuuji <[email protected]>
Cameron Steffen <[email protected]>
Danny Tuppeny <[email protected]>
Ivan Tugay <[email protected]>
Rohan Vanheusden <[email protected]>
Rohan Vanheusden <[email protected]>
Minh Dao <[email protected]>
1 change: 1 addition & 0 deletions example/all.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ linter:
- unrelated_type_equality_checks
- unsafe_html
- use_build_context_synchronously
- use_decorated_box
- use_full_hex_values_for_flutter_colors
- use_function_type_syntax_for_parameters
- use_if_null_to_convert_nulls_to_bools
Expand Down
2 changes: 2 additions & 0 deletions lib/src/rules.dart
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ import 'rules/unnecessary_this.dart';
import 'rules/unrelated_type_equality_checks.dart';
import 'rules/unsafe_html.dart';
import 'rules/use_build_context_synchronously.dart';
import 'rules/use_decorated_box.dart';
import 'rules/use_full_hex_values_for_flutter_colors.dart';
import 'rules/use_function_type_syntax_for_parameters.dart';
import 'rules/use_if_null_to_convert_nulls_to_bools.dart';
Expand Down Expand Up @@ -388,6 +389,7 @@ void registerLintRules({bool inTestMode = false}) {
..register(UnrelatedTypeEqualityChecks())
..register(UnsafeHtml())
..register(UseBuildContextSynchronously(inTestMode: inTestMode))
..register(UseDecoratedBox())
..register(UseFullHexValuesForFlutterColors())
..register(UseFunctionTypeSyntaxForParameters())
..register(UseIfNullToConvertNullsToBools())
Expand Down
114 changes: 114 additions & 0 deletions lib/src/rules/use_decorated_box.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// Copyright (c) 2021, 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'Use `DecoratedBox`.';

const _details = r'''

**DO** use `DecoratedBox` when `Container` has only a `Decoration`.

A `Container` is a heavier Widget than a `DecoratedBox`, and as bonus,
`DecoratedBox` has a `const` constructor.

**BAD:**
```dart
Widget buildArea() {
return Container(
decoration: const BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.all(
Radius.circular(5),
),
),
child: const Text('...'),
);
}
```

**GOOD:**
```dart
Widget buildArea() {
return const DecoratedBox(
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.all(
Radius.circular(5),
),
),
child: Text('...'),
);
}
```
''';

class UseDecoratedBox extends LintRule {
UseDecoratedBox()
: super(
name: 'use_decorated_box',
description: _desc,
details: _details,
group: Group.style);

@override
void registerNodeProcessors(
NodeLintRegistry registry, LinterContext context) {
var 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;
}

var data = _ArgumentData(node.argumentList);

if (data.additionalArgumentsFound || data.positionalArgumentsFound) {
return;
}

if (data.hasDecoration) {
rule.reportLint(node.constructorName);
}
}
}

class _ArgumentData {
var positionalArgumentsFound = false;
var additionalArgumentsFound = false;
var hasDecoration = false;

_ArgumentData(ArgumentList node) {
for (var argument in node.arguments) {
if (argument is! NamedExpression) {
positionalArgumentsFound = true;
return;
}
var label = argument.name.label;
if (label.name == 'decoration') {
hasDecoration = true;
} else if (label.name == 'child') {
// Ignore child
} else if (label.name == 'key') {
// Ignore key
} else {
additionalArgumentsFound = true;
}
}
}
}
5 changes: 5 additions & 0 deletions test_data/mock_packages/flutter/lib/painting.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Copyright (c) 2021, 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.

export 'src/painting/box_decoration.dart';
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Copyright (c) 2021, 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.

class BoxDecoration {}
81 changes: 81 additions & 0 deletions test_data/rules/use_decorated_box.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Copyright (c) 2021, 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/ `dart test -N use_decorated_box`
pq marked this conversation as resolved.
Show resolved Hide resolved

import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter/painting.dart';

Widget containerWithoutArguments() {
return Container(); // OK
}

Widget containerWithKey() {
return Container( // OK
key: Key('abc'),
);
}

Widget containerWithDecoration() {
return Container( // LINT
decoration: BoxDecoration(),
);
}

Widget containerWithChild() {
return Container( // OK
child: SizedBox(),
);
}

Widget containerWithKeyAndChild() {
return Container( // OK
key: Key('abc'),
child: SizedBox(),
);
}

Widget containerWithKeyAndDecoration() {
return Container( // LINT
key: Key('abc'),
decoration: BoxDecoration(),
);
}

Widget containerWithDecorationAndChild() {
return Container( // LINT
decoration: BoxDecoration(),
child: SizedBox(),
);
}

Widget containerWithKeyAndDecorationAndChild() {
return Container( // LINT
key: Key('abc'),
decoration: BoxDecoration(),
child: SizedBox(),
);
}

Widget containerWithAnotherArgument() {
return Container( // OK
width: 20,
);
}

Widget containerWithDecorationAndAdditionalArgument() {
return Container( // OK
decoration: BoxDecoration(),
width: 20,
);
}

Widget containerWithDecorationAndAdditionalArgumentAndChild() {
return Container( // OK
decoration: BoxDecoration(),
width: 20,
child: SizedBox(),
);
}