linter: Remove unused ConditionScopeVisitor Change-Id: Ie002ffb6bdaffd8ec3b6332c692b4304bc39cfe6 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/353060 Auto-Submit: Samuel Rawlins <srawlins@google.com> Commit-Queue: Phil Quitslund <pquitslund@google.com> Reviewed-by: Phil Quitslund <pquitslund@google.com>
diff --git a/pkg/linter/lib/src/util/condition_scope_visitor.dart b/pkg/linter/lib/src/util/condition_scope_visitor.dart deleted file mode 100644 index ab7227e..0000000 --- a/pkg/linter/lib/src/util/condition_scope_visitor.dart +++ /dev/null
@@ -1,443 +0,0 @@ -// Copyright (c) 2018, 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/token.dart'; -import 'package:analyzer/dart/ast/visitor.dart'; -import 'package:analyzer/dart/element/element.dart'; - -import '../analyzer.dart'; -import '../extensions.dart'; - -Element? _getLeftElement(AssignmentExpression assignment) => - assignment.writeElement?.canonicalElement; - -List<Expression?> _splitConjunctions(Expression? rawExpression) { - var expression = rawExpression?.unParenthesized; - if (expression is BinaryExpression && - expression.operator.type == TokenType.AMPERSAND_AMPERSAND) { - return _splitConjunctions(expression.leftOperand) - ..addAll(_splitConjunctions(expression.rightOperand)); - } - return [expression]; -} - -class BreakScope { - var environment = <BreakStatement>[]; - - void add(BreakStatement element) { - if (element.target != null) { - environment.add(element); - } - } - - void deleteBreaksWithTarget(AstNode node) { - environment = environment.where((e) => e.target != node).toList(); - } - - bool hasBreak(AstNode node) => environment.any((e) => e.target == node); -} - -class ConditionScope { - final environment = <ExpressionBox>[]; - final ConditionScope? outer; - - ConditionScope(this.outer); - - void add(ExpressionBox e) { - environment.add(e); - } - - void addAll(Iterable<ExpressionBox> expressions) { - environment.addAll(expressions); - } - - Iterable<Expression> getExpressions(Iterable<Element?> elements, - {bool? value}) { - var expressions = <Expression>[]; - _recursiveGetExpressions(expressions, elements, value); - return expressions; - } - - Iterable<ExpressionBox> getUndefinedExpressions() => - environment.whereType<_UndefinedExpression>(); - - void _recursiveGetExpressions( - List<Expression> expressions, Iterable<Element?> elements, bool? value) { - for (var element in environment.reversed) { - if (element.haveToStop(elements)) { - return; - } - if (element is _ConditionExpression && element.value == value) { - var expression = element.expression; - if (expression != null) { - expressions.add(expression); - } - } - } - outer?._recursiveGetExpressions(expressions, elements, value); - } -} - -/// An AST visitor that keeps the conditions that are currently evaluated -/// to true or false. -/// -/// Subclasses that override a visit method must either invoke the overridden -/// visit method or must explicitly ask the visited node to visit its children. -/// Failure to do so will cause the children of the visited node to not be -/// visited. -/// -/// Subclasses that override a visit method must either invoke the overridden -/// visit method and keep the scopes behavior consistent to its changes. -/// -/// When add a new local scope: -/// - Visiting a non-empty function body: [BlockFunctionBody] [ExpressionFunctionBody]. -/// - Visiting a flow-control statement: [IfStatement] ElseStatement. -/// - Visiting loop statements: [DoStatement] [WhileStatement] [ForStatement]. -/// -/// When call the abstract method visitCondition(node.condition): -/// - After visiting a conditional statements: [IfStatement] [DoStatement] [WhileStatement]. -/// -/// When undefine an element: -/// - Visiting reassignments of variables: [AssignmentExpression] [PrefixExpression] [PostfixExpression]. -/// -/// When undefine all elements: -/// - Visiting a non-empty function body: [BlockFunctionBody] [ExpressionFunctionBody]. -/// - Visiting clauses that generates dead_code: [ReturnStatement] [ThrowExpression] [RethrowExpression]. -/// - Visiting if/else with exit clauses inside in both cases (also generates dead code). -/// -/// When propagate undefined elements: -/// - After visiting a flow-control statement: [IfStatement] ElseStatement. -/// - After visiting loop statements: [DoStatement] [WhileStatement] [ForStatement]. -/// -/// When add a Condition as true condition: -/// - Inside an if body and after an else body with exit clause. -/// - Inside pre evaluated conditional loops: [ForStatement] [WhileStatement] -/// -/// When add a Condition as false condition: -/// - Inside an else body and after a then body with exit clause. -/// - Outside pre evaluated conditional loops without breaks: [ForStatement] [WhileStatement] -/// -/// When add a BreakStatement in the breakScope. -/// - When visiting a BreakStatement. -/// -/// When remove a BreakStatement in the breakScope. -/// - After visiting the target of the BreakStatement. -/// -/// Clients may extend this class. -abstract class ConditionScopeVisitor extends RecursiveAstVisitor { - ConditionScope? outerScope; - final breakScope = BreakScope(); - - // TODO(pq): here and w/ getTrueExpressions, consider an empty iterable - Iterable<Expression>? getFalseExpressions(Iterable<Element?> elements) => - _getExpressions(elements, value: false); - - Iterable<Expression>? getTrueExpressions(Iterable<Element?> elements) => - _getExpressions(elements); - - @override - void visitAssignmentExpression(AssignmentExpression node) { - _addElementToEnvironment( - _UndefinedExpression.forElement(_getLeftElement(node))); - node.visitChildren(this); - } - - @override - void visitBlockFunctionBody(BlockFunctionBody node) { - _addScope(); - _addElementToEnvironment(_UndefinedAllExpression()); - node.visitChildren(this); - _removeLastScope(); - } - - @override - void visitBreakStatement(BreakStatement node) { - breakScope.add(node); - node.visitChildren(this); - } - - void visitCondition(Expression? node); - - @override - void visitDoStatement(DoStatement node) { - _addScope(); - visitCondition(node.condition); - node.visitChildren(this); - _propagateUndefinedExpressions(_removeLastScope()); - // If a do statement do not have breaks inside, that means the condition - // after the loop is false. - if (!breakScope.hasBreak(node)) { - _addFalseCondition(node.condition); - } - breakScope.deleteBreaksWithTarget(node); - } - - @override - void visitExpressionFunctionBody(ExpressionFunctionBody node) { - _addScope(); - _addElementToEnvironment(_UndefinedAllExpression()); - node.visitChildren(this); - _removeLastScope(); - } - - @override - void visitForStatement(ForStatement node) { - _addScope(); - var loopParts = node.forLoopParts; - if (loopParts is ForParts) { - _addTrueCondition(loopParts.condition); - - if (loopParts is ForPartsWithDeclarations) { - loopParts.variables.accept(this); - } else if (loopParts is ForPartsWithExpression) { - loopParts.initialization?.accept(this); - } - - visitCondition(loopParts.condition); - loopParts.condition?.accept(this); - _addTrueCondition(loopParts.condition); - loopParts.updaters.accept(this); - node.body.accept(this); - _propagateUndefinedExpressions(_removeLastScope()); - if (_isRelevantOutsideOfForStatement(node)) { - _addFalseCondition(loopParts.condition); - } - breakScope.deleteBreaksWithTarget(node); - } else if (loopParts is ForEachParts) { - node.visitChildren(this); - _propagateUndefinedExpressions(_removeLastScope()); - } else { - throw StateError('unsupported loop parts type'); - } - } - - @override - void visitIfStatement(IfStatement node) { - var elseScope = _visitElseStatement(node.elseStatement, node.expression); - _visitIfStatement(node); - if (elseScope != null) { - _propagateUndefinedExpressions(elseScope); - } - var addFalseCondition = _isLastStatementAnExitStatement(node.thenStatement); - var addTrueCondition = _isLastStatementAnExitStatement(node.elseStatement); - // If addTrueCondition and addFalseCondition are true at the same time, - // then the rest of the block is dead code. - if (addTrueCondition && addFalseCondition) { - _addElementToEnvironment(_UndefinedAllExpression()); - return; - } - if (addFalseCondition) { - _addFalseCondition(node.expression); - } - if (addTrueCondition) { - _addTrueCondition(node.expression); - } - } - - @override - void visitPostfixExpression(PostfixExpression node) { - var operand = node.operand; - if (operand is SimpleIdentifier) { - _addElementToEnvironment( - _UndefinedExpression.forElement(operand.staticElement)); - } - node.visitChildren(this); - } - - @override - void visitPrefixExpression(PrefixExpression node) { - var operand = node.operand; - if (operand is SimpleIdentifier) { - _addElementToEnvironment( - _UndefinedExpression.forElement(operand.staticElement)); - } - node.visitChildren(this); - } - - @override - void visitRethrowExpression(RethrowExpression node) { - node.visitChildren(this); - _addElementToEnvironment(_UndefinedAllExpression()); - } - - @override - void visitReturnStatement(ReturnStatement node) { - node.visitChildren(this); - _addElementToEnvironment(_UndefinedAllExpression()); - } - - @override - void visitThrowExpression(ThrowExpression node) { - node.visitChildren(this); - _addElementToEnvironment(_UndefinedAllExpression()); - } - - @override - void visitVariableDeclaration(VariableDeclaration node) { - _addElementToEnvironment( - _UndefinedExpression.forElement(node.declaredElement)); - node.visitChildren(this); - } - - @override - void visitWhileStatement(WhileStatement node) { - _addScope(); - visitCondition(node.condition); - node.condition.accept(this); - _addTrueCondition(node.condition); - node.body.accept(this); - _propagateUndefinedExpressions(_removeLastScope()); - // If a while statement do not have breaks inside, that means the condition - // after the loop is false. - if (!breakScope.hasBreak(node)) { - _addFalseCondition(node.condition); - } - breakScope.deleteBreaksWithTarget(node); - } - - void _addElementToEnvironment(ExpressionBox? e) { - if (e != null) { - outerScope?.add(e); - } - } - - void _addFalseCondition(Expression? expression) { - _addElementToEnvironment(_ConditionExpression(expression, value: false)); - } - - void _addScope() { - outerScope = ConditionScope(outerScope); - } - - void _addTrueCondition(Expression? expression) { - _splitConjunctions(expression).forEach((e) { - _addElementToEnvironment(_ConditionExpression(e)); - }); - } - - Iterable<Expression>? _getExpressions(Iterable<Element?> elements, - {bool value = true}) => - outerScope?.getExpressions(elements, value: value); - - bool _isLastStatementAnExitStatement(Statement? statement) { - if (statement is Block) { - return _isLastStatementAnExitStatement(statement.lastStatement); - } else { - if (statement is BreakStatement) { - return statement.label == null; - } else if (statement is ContinueStatement) { - return statement.label == null; - } else if (statement is ReturnStatement) { - return true; - } - return statement != null && ExitDetector.exits(statement); - } - } - - /// If any of the variables is declared inside the for statement then it does - /// not mean anything afterwards. - bool _isRelevantOutsideOfForStatement(ForStatement node) { - if (breakScope.hasBreak(node)) { - return false; - } - - var loopParts = node.forLoopParts; - if (loopParts is ForParts) { - var condition = loopParts.condition; - if (condition == null) { - return false; - } - - // TODO(pq): migrate away from `traverseNodesInDFS` (https://github.com/dart-lang/linter/issues/3745) - // ignore: deprecated_member_use_from_same_package - for (var ref in condition.traverseNodesInDFS()) { - if (ref is SimpleIdentifier) { - var element = ref.staticElement; - if (element == null) { - return false; - } - var refOffset = element.nameOffset; - if (refOffset > node.offset && refOffset < node.end) { - return false; - } - } - } - } - - return true; - } - - void _propagateUndefinedExpressions(ConditionScope? scope) { - if (scope != null) { - outerScope?.addAll(scope.getUndefinedExpressions()); - } - } - - ConditionScope? _removeLastScope() { - var deletedScope = outerScope; - outerScope = outerScope?.outer; - return deletedScope; - } - - ConditionScope? _visitElseStatement( - Statement? elseStatement, Expression condition) { - _addScope(); - _addFalseCondition(condition); - elseStatement?.accept(this); - return _removeLastScope(); - } - - void _visitIfStatement(IfStatement node) { - _addScope(); - node.expression.accept(this); - visitCondition(node.expression); - _addTrueCondition(node.expression); - node.thenStatement.accept(this); - _propagateUndefinedExpressions(_removeLastScope()); - } -} - -abstract class ExpressionBox { - bool haveToStop(Iterable<Element?> elements); -} - -class _ConditionExpression extends ExpressionBox { - final Expression? expression; - final bool value; - - _ConditionExpression(this.expression, {this.value = true}); - - @override - bool haveToStop(Iterable<Element?> elements) => false; - - @override - String toString() => '$expression is $value'; -} - -class _UndefinedAllExpression extends ExpressionBox { - @override - bool haveToStop(Iterable<Element?> elements) => true; - - @override - String toString() => '*All* got undefined'; -} - -class _UndefinedExpression extends ExpressionBox { - final Element element; - - _UndefinedExpression._internal(this.element); - - @override - bool haveToStop(Iterable<Element?> elements) => elements.contains(element); - - @override - String toString() => '$element got undefined'; - - static _UndefinedExpression? forElement(Element? element) { - var canonicalElement = element?.canonicalElement; - if (canonicalElement == null) return null; - return _UndefinedExpression._internal(canonicalElement); - } -}