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
+}