| // 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 'package:analyzer/dart/element/element.dart'; |
| import 'package:analyzer/src/generated/source.dart'; |
| import 'package:analyzer_plugin/utilities/range_factory.dart'; |
| |
| /// Computer of local elements and source ranges in which they are visible. |
| class VisibleRangesComputer extends GeneralizingAstVisitor<void> { |
| final Map<LocalElement, SourceRange> _map = {}; |
| |
| @override |
| void visitCatchClause(CatchClause node) { |
| _addLocalVariable(node, node.exceptionParameter?.staticElement); |
| _addLocalVariable(node, node.stackTraceParameter?.staticElement); |
| node.body.accept(this); |
| } |
| |
| @override |
| void visitFormalParameter(FormalParameter node) { |
| var element = node.declaredElement; |
| if (element is ParameterElement) { |
| var body = _getFunctionBody(node); |
| if (body is BlockFunctionBody) { |
| _map[element] = range.node(body); |
| } else if (body is ExpressionFunctionBody) { |
| _map[element] = range.node(body); |
| } |
| } |
| } |
| |
| @override |
| void visitForPartsWithDeclarations(ForPartsWithDeclarations node) { |
| var loop = node.parent; |
| if (loop != null) { |
| for (var variable in node.variables.variables) { |
| _addLocalVariable(loop, variable.declaredElement); |
| variable.initializer?.accept(this); |
| } |
| } |
| } |
| |
| @override |
| void visitFunctionDeclaration(FunctionDeclaration node) { |
| var block = node.parent?.parent; |
| if (block is Block) { |
| var element = node.declaredElement as FunctionElement; |
| _map[element] = range.node(block); |
| } |
| |
| super.visitFunctionDeclaration(node); |
| } |
| |
| @override |
| void visitVariableDeclarationStatement(VariableDeclarationStatement node) { |
| var block = node.parent; |
| if (block != null) { |
| for (var variable in node.variables.variables) { |
| _addLocalVariable(block, variable.declaredElement); |
| variable.initializer?.accept(this); |
| } |
| } |
| } |
| |
| void _addLocalVariable(AstNode scopeNode, Element? element) { |
| if (element is LocalVariableElement) { |
| _map[element] = range.node(scopeNode); |
| } |
| } |
| |
| static Map<LocalElement, SourceRange> forNode(AstNode unit) { |
| var computer = VisibleRangesComputer(); |
| unit.accept(computer); |
| return computer._map; |
| } |
| |
| /// Return the body of the function that contains the given [parameter], or |
| /// `null` if no function body could be found. |
| static FunctionBody? _getFunctionBody(FormalParameter parameter) { |
| var parent = parameter.parent?.parent; |
| if (parent is ConstructorDeclaration) { |
| return parent.body; |
| } else if (parent is FunctionExpression) { |
| return parent.body; |
| } else if (parent is MethodDeclaration) { |
| return parent.body; |
| } |
| return null; |
| } |
| } |