| // Copyright (c) 2017, 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'; | 
 |  | 
 | const _desc = r'Separate the control structure expression from its statement.'; | 
 |  | 
 | const _details = r''' | 
 |  | 
 | From the [flutter style guide](https://flutter.dev/style-guide/): | 
 |  | 
 | **DO** separate the control structure expression from its statement. | 
 |  | 
 | Don't put the statement part of an `if`, `for`, `while`, `do` on the same line | 
 | as the expression, even if it is short.  Doing so makes it unclear that there | 
 | is relevant code there.  This is especially important for early returns. | 
 |  | 
 | **GOOD:** | 
 | ``` | 
 | if (notReady) | 
 |   return; | 
 |  | 
 | if (notReady) | 
 |   return; | 
 | else | 
 |   print('ok') | 
 |  | 
 | while (condition) | 
 |   i += 1; | 
 | ``` | 
 |  | 
 | **BAD:** | 
 | ``` | 
 | if (notReady) return; | 
 |  | 
 | if (notReady) | 
 |   return; | 
 | else print('ok') | 
 |  | 
 | while (condition) i += 1; | 
 | ``` | 
 |  | 
 | '''; | 
 |  | 
 | class AlwaysPutControlBodyOnNewLine extends LintRule implements NodeLintRule { | 
 |   AlwaysPutControlBodyOnNewLine() | 
 |       : super( | 
 |             name: 'always_put_control_body_on_new_line', | 
 |             description: _desc, | 
 |             details: _details, | 
 |             group: Group.style); | 
 |  | 
 |   @override | 
 |   void registerNodeProcessors( | 
 |       NodeLintRegistry registry, LinterContext context) { | 
 |     final visitor = _Visitor(this); | 
 |     registry.addDoStatement(this, visitor); | 
 |     registry.addForStatement(this, visitor); | 
 |     registry.addIfStatement(this, visitor); | 
 |     registry.addWhileStatement(this, visitor); | 
 |   } | 
 | } | 
 |  | 
 | class _Visitor extends SimpleAstVisitor<void> { | 
 |   final LintRule rule; | 
 |  | 
 |   _Visitor(this.rule); | 
 |  | 
 |   @override | 
 |   void visitDoStatement(DoStatement node) { | 
 |     _checkNodeOnNextLine(node.body, node.doKeyword.end); | 
 |   } | 
 |  | 
 |   @override | 
 |   void visitForStatement(ForStatement node) { | 
 |     _checkNodeOnNextLine(node.body, node.rightParenthesis.end); | 
 |   } | 
 |  | 
 |   @override | 
 |   void visitIfStatement(IfStatement node) { | 
 |     _checkNodeOnNextLine(node.thenStatement, node.rightParenthesis.end); | 
 |     if (node.elseKeyword != null && node.elseStatement is! IfStatement) { | 
 |       _checkNodeOnNextLine(node.elseStatement, node.elseKeyword.end); | 
 |     } | 
 |   } | 
 |  | 
 |   @override | 
 |   void visitWhileStatement(WhileStatement node) { | 
 |     _checkNodeOnNextLine(node.body, node.rightParenthesis.end); | 
 |   } | 
 |  | 
 |   void _checkNodeOnNextLine(AstNode node, int controlEnd) { | 
 |     if (node is Block && node.statements.isEmpty) return; | 
 |  | 
 |     final unit = node.root as CompilationUnit; | 
 |     final offsetFirstStatement = | 
 |         node is Block ? node.statements.first.offset : node.offset; | 
 |     if (unit.lineInfo.getLocation(controlEnd).lineNumber == | 
 |         unit.lineInfo.getLocation(offsetFirstStatement).lineNumber) { | 
 |       rule.reportLintForToken(node.beginToken); | 
 |     } | 
 |   } | 
 | } |