no_logic_in_create_state (#1877)
* no_logic_in_create_state
* unnecessary local
* relative import
diff --git a/example/all.yaml b/example/all.yaml
index 509fb7f..6e2d683 100644
--- a/example/all.yaml
+++ b/example/all.yaml
@@ -72,6 +72,7 @@
- literal_only_boolean_expressions
- no_adjacent_strings_in_list
- no_duplicate_case_values
+ - no_logic_in_create_state
- non_constant_identifier_names
- null_closures
- omit_local_variable_types
diff --git a/lib/src/rules.dart b/lib/src/rules.dart
index c523f20..ae51e86 100644
--- a/lib/src/rules.dart
+++ b/lib/src/rules.dart
@@ -73,6 +73,7 @@
import 'rules/literal_only_boolean_expressions.dart';
import 'rules/no_adjacent_strings_in_list.dart';
import 'rules/no_duplicate_case_values.dart';
+import 'rules/no_logic_in_create_state.dart';
import 'rules/non_constant_identifier_names.dart';
import 'rules/null_closures.dart';
import 'rules/omit_local_variable_types.dart';
@@ -234,6 +235,7 @@
..register(NoAdjacentStringsInList())
..register(NoDuplicateCaseValues())
..register(NonConstantIdentifierNames())
+ ..register(NoLogicInCreateState())
..register(NullClosures())
..register(OmitLocalVariableTypes())
..register(OneMemberAbstracts())
diff --git a/lib/src/rules/no_logic_in_create_state.dart b/lib/src/rules/no_logic_in_create_state.dart
new file mode 100644
index 0000000..9c4d235
--- /dev/null
+++ b/lib/src/rules/no_logic_in_create_state.dart
@@ -0,0 +1,114 @@
+// 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/visitor.dart';
+
+import '../analyzer.dart';
+import '../util/flutter_utils.dart';
+
+const _desc = r"Don't put any logic in createState.";
+
+const _details = r'''
+**DON'T** put any logic in `createState()`.
+
+Implementations of `createState()` should return a new instance
+of a State object and do nothing more. Since state access is prefered
+via the `widget` field, passing data to `State` objects using custom
+constructor parameters should also be avoided and so further, the State
+constructor is required to be passed no arguments.
+
+**BAD:**
+```
+MyState global;
+
+class MyStateful extends StatefulWidget {
+ @override
+ MyState createState() {
+ global = MyState();
+ return global;
+ }
+}
+```
+
+```
+class MyStateful extends StatefulWidget {
+ @override
+ MyState createState() => MyState()..field = 42;
+}
+```
+
+```
+class MyStateful extends StatefulWidget {
+ @override
+ MyState createState() => MyState(42);
+}
+```
+
+
+**GOOD:**
+```
+class MyStateful extends StatefulWidget {
+ @override
+ MyState createState() {
+ return MyState();
+ }
+}
+```
+''';
+
+class NoLogicInCreateState extends LintRule implements NodeLintRule {
+ NoLogicInCreateState()
+ : super(
+ name: 'no_logic_in_create_state',
+ description: _desc,
+ details: _details,
+ group: Group.errors);
+
+ @override
+ void registerNodeProcessors(NodeLintRegistry registry,
+ [LinterContext context]) {
+ final visitor = _Visitor(this);
+ registry.addMethodDeclaration(this, visitor);
+ }
+}
+
+class _Visitor extends SimpleAstVisitor {
+ final LintRule rule;
+
+ _Visitor(this.rule);
+
+ @override
+ void visitMethodDeclaration(MethodDeclaration node) {
+ if (node.name.name != 'createState') {
+ return;
+ }
+
+ final parent = node.parent;
+ if (parent is! ClassDeclaration ||
+ !isStatefulWidget((parent as ClassDeclaration).declaredElement)) {
+ return;
+ }
+ final body = node.body;
+ Expression expressionToTest;
+ if (body is BlockFunctionBody) {
+ final statements = body.block.statements;
+ if (statements.length == 1) {
+ final statement = statements[0];
+ if (statement is ReturnStatement) {
+ expressionToTest = statement.expression;
+ }
+ }
+ } else if (body is ExpressionFunctionBody) {
+ expressionToTest = body.expression;
+ }
+
+ if (expressionToTest is InstanceCreationExpression) {
+ if (expressionToTest.argumentList.arguments.isEmpty) {
+ return;
+ }
+ }
+ rule.reportLint(expressionToTest ?? body);
+ }
+}
diff --git a/lib/src/util/flutter_utils.dart b/lib/src/util/flutter_utils.dart
index 5dff23c..65d12f7 100644
--- a/lib/src/util/flutter_utils.dart
+++ b/lib/src/util/flutter_utils.dart
@@ -22,6 +22,9 @@
bool isExactWidgetTypeContainer(DartType type) =>
_flutter.isExactWidgetTypeContainer(type);
+bool isStatefulWidget(ClassElement element) =>
+ _flutter.isStatefulWidget(element);
+
bool isWidgetProperty(DartType type) {
if (isWidgetType(type)) {
return true;
@@ -38,6 +41,7 @@
/// See: analysis_server/lib/src/utilities/flutter.dart
class _Flutter {
+ static const _nameStatefulWidget = 'StatefulWidget';
static const _nameWidget = 'Widget';
static const _nameContainer = 'Container';
@@ -56,6 +60,21 @@
type is InterfaceType &&
_isExactWidget(type.element, _nameContainer, _uriContainer);
+ bool isStatefulWidget(ClassElement element) {
+ if (element == null) {
+ return false;
+ }
+ if (_isExactWidget(element, _nameStatefulWidget, _uriFramework)) {
+ return true;
+ }
+ for (var type in element.allSupertypes) {
+ if (_isExactWidget(type.element, _nameStatefulWidget, _uriFramework)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
bool isWidget(ClassElement element) {
if (element == null) {
return false;
diff --git a/test/rules/no_logic_in_create_state.dart b/test/rules/no_logic_in_create_state.dart
new file mode 100644
index 0000000..eb5145d
--- /dev/null
+++ b/test/rules/no_logic_in_create_state.dart
@@ -0,0 +1,67 @@
+// 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.
+
+// test w/ `pub run test -N no_logic_in_create_state`
+
+
+import 'package:flutter/widgets.dart';
+
+class MyState extends State {
+ int field;
+}
+
+class MyStatefulOK extends StatefulWidget {
+ @override
+ MyState createState() {
+ return MyState();
+ }
+}
+
+class MyStatefulOK2 extends StatefulWidget {
+ @override
+ MyState createState() => MyState();
+}
+
+
+MyState global;
+
+class MyStatefulBad extends StatefulWidget {
+ @override
+ MyState createState() { // LINT
+ // ignore: join_return_with_assignment
+ global = MyState();
+ return global;
+ }
+}
+
+class MyStatefulBad2 extends StatefulWidget {
+ MyState instance = MyState();
+ @override
+ MyState createState() {
+ return instance; // LINT
+ }
+}
+
+class MyStatefulBad3 extends StatefulWidget {
+ @override
+ MyState createState() {
+ return MyState()..field = 0; // LINT
+ }
+}
+
+class MyStatefulBad4 extends StatefulWidget {
+ @override
+ MyState createState() =>
+ MyState()..field = 0; // LINT
+}
+
+class MyState2 extends State {
+ int field;
+ MyState2(this.field);
+}
+
+class MyStatefulBad5 extends StatefulWidget {
+ @override
+ MyState2 createState() => MyState2(1); // LINT
+}