| // Copyright (c) 2016, 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'; |
| |
| /// Compute the set of external names referenced in the [unit]. |
| Set<String> computeReferencedNames(CompilationUnit unit) { |
| _ReferencedNamesComputer computer = _ReferencedNamesComputer(); |
| unit.accept(computer); |
| return computer.names; |
| } |
| |
| /// Compute the set of names which are used in `extends`, `with` or `implements` |
| /// clauses in the file. Import prefixes and type arguments are not included. |
| Set<String> computeSubtypedNames(CompilationUnit unit) { |
| Set<String> subtypedNames = <String>{}; |
| |
| void _addSubtypedName(NamedType? type) { |
| if (type != null) { |
| Identifier name = type.name; |
| if (name is SimpleIdentifier) { |
| subtypedNames.add(name.name); |
| } else if (name is PrefixedIdentifier) { |
| subtypedNames.add(name.identifier.name); |
| } |
| } |
| } |
| |
| void _addSubtypedNames(List<NamedType>? types) { |
| types?.forEach(_addSubtypedName); |
| } |
| |
| for (CompilationUnitMember declaration in unit.declarations) { |
| if (declaration is ClassDeclaration) { |
| _addSubtypedName(declaration.extendsClause?.superclass); |
| _addSubtypedNames(declaration.withClause?.mixinTypes); |
| _addSubtypedNames(declaration.implementsClause?.interfaces); |
| } else if (declaration is ClassTypeAlias) { |
| _addSubtypedName(declaration.superclass); |
| _addSubtypedNames(declaration.withClause.mixinTypes); |
| _addSubtypedNames(declaration.implementsClause?.interfaces); |
| } else if (declaration is EnumDeclaration) { |
| _addSubtypedNames(declaration.withClause?.mixinTypes); |
| _addSubtypedNames(declaration.implementsClause?.interfaces); |
| } else if (declaration is MixinDeclaration) { |
| _addSubtypedNames(declaration.onClause?.superclassConstraints); |
| _addSubtypedNames(declaration.implementsClause?.interfaces); |
| } |
| } |
| |
| return subtypedNames; |
| } |
| |
| /// Chained set of local names, that hide corresponding external names. |
| class _LocalNameScope { |
| final _LocalNameScope? enclosing; |
| Set<String>? names; |
| |
| _LocalNameScope(this.enclosing); |
| |
| factory _LocalNameScope.forBlock(_LocalNameScope enclosing, Block node) { |
| _LocalNameScope scope = _LocalNameScope(enclosing); |
| for (Statement statement in node.statements) { |
| if (statement is FunctionDeclarationStatement) { |
| scope.add(statement.functionDeclaration.name); |
| } else if (statement is VariableDeclarationStatement) { |
| scope.addVariableNames(statement.variables); |
| } |
| } |
| return scope; |
| } |
| |
| factory _LocalNameScope.forClass( |
| _LocalNameScope enclosing, ClassDeclaration node) { |
| _LocalNameScope scope = _LocalNameScope(enclosing); |
| scope.addTypeParameters(node.typeParameters); |
| for (ClassMember member in node.members) { |
| if (member is FieldDeclaration) { |
| scope.addVariableNames(member.fields); |
| } else if (member is MethodDeclaration) { |
| scope.add(member.name); |
| } |
| } |
| return scope; |
| } |
| |
| factory _LocalNameScope.forClassTypeAlias( |
| _LocalNameScope enclosing, ClassTypeAlias node) { |
| _LocalNameScope scope = _LocalNameScope(enclosing); |
| scope.addTypeParameters(node.typeParameters); |
| return scope; |
| } |
| |
| factory _LocalNameScope.forConstructor( |
| _LocalNameScope enclosing, ConstructorDeclaration node) { |
| _LocalNameScope scope = _LocalNameScope(enclosing); |
| scope.addFormalParameters(node.parameters); |
| return scope; |
| } |
| |
| factory _LocalNameScope.forFunction( |
| _LocalNameScope enclosing, FunctionDeclaration node) { |
| _LocalNameScope scope = _LocalNameScope(enclosing); |
| scope.addTypeParameters(node.functionExpression.typeParameters); |
| scope.addFormalParameters(node.functionExpression.parameters); |
| return scope; |
| } |
| |
| factory _LocalNameScope.forFunctionTypeAlias( |
| _LocalNameScope enclosing, FunctionTypeAlias node) { |
| _LocalNameScope scope = _LocalNameScope(enclosing); |
| scope.addTypeParameters(node.typeParameters); |
| return scope; |
| } |
| |
| factory _LocalNameScope.forMethod( |
| _LocalNameScope enclosing, MethodDeclaration node) { |
| _LocalNameScope scope = _LocalNameScope(enclosing); |
| scope.addTypeParameters(node.typeParameters); |
| scope.addFormalParameters(node.parameters); |
| return scope; |
| } |
| |
| factory _LocalNameScope.forUnit(CompilationUnit node) { |
| _LocalNameScope scope = _LocalNameScope(null); |
| for (CompilationUnitMember declaration in node.declarations) { |
| if (declaration is NamedCompilationUnitMember) { |
| scope.add(declaration.name); |
| } else if (declaration is TopLevelVariableDeclaration) { |
| scope.addVariableNames(declaration.variables); |
| } |
| } |
| return scope; |
| } |
| |
| void add(SimpleIdentifier? identifier) { |
| if (identifier != null) { |
| (names ??= <String>{}).add(identifier.name); |
| } |
| } |
| |
| void addFormalParameters(FormalParameterList? parameterList) { |
| if (parameterList != null) { |
| parameterList.parameters |
| .map((p) => p is NormalFormalParameter ? p.identifier : null) |
| .forEach(add); |
| } |
| } |
| |
| void addTypeParameters(TypeParameterList? typeParameterList) { |
| if (typeParameterList != null) { |
| typeParameterList.typeParameters.map((p) => p.name).forEach(add); |
| } |
| } |
| |
| void addVariableNames(VariableDeclarationList variableList) { |
| for (VariableDeclaration variable in variableList.variables) { |
| add(variable.name); |
| } |
| } |
| |
| bool contains(String name) { |
| if (names != null && names!.contains(name)) { |
| return true; |
| } |
| if (enclosing != null) { |
| return enclosing!.contains(name); |
| } |
| return false; |
| } |
| } |
| |
| class _ReferencedNamesComputer extends GeneralizingAstVisitor<void> { |
| final Set<String> names = <String>{}; |
| final Set<String> importPrefixNames = <String>{}; |
| |
| _LocalNameScope localScope = _LocalNameScope(null); |
| |
| @override |
| void visitBlock(Block node) { |
| _LocalNameScope outerScope = localScope; |
| try { |
| localScope = _LocalNameScope.forBlock(localScope, node); |
| super.visitBlock(node); |
| } finally { |
| localScope = outerScope; |
| } |
| } |
| |
| @override |
| void visitClassDeclaration(ClassDeclaration node) { |
| _LocalNameScope outerScope = localScope; |
| try { |
| localScope = _LocalNameScope.forClass(localScope, node); |
| super.visitClassDeclaration(node); |
| } finally { |
| localScope = outerScope; |
| } |
| } |
| |
| @override |
| void visitClassTypeAlias(ClassTypeAlias node) { |
| _LocalNameScope outerScope = localScope; |
| try { |
| localScope = _LocalNameScope.forClassTypeAlias(localScope, node); |
| super.visitClassTypeAlias(node); |
| } finally { |
| localScope = outerScope; |
| } |
| } |
| |
| @override |
| void visitCompilationUnit(CompilationUnit node) { |
| localScope = _LocalNameScope.forUnit(node); |
| super.visitCompilationUnit(node); |
| } |
| |
| @override |
| void visitConstructorDeclaration(ConstructorDeclaration node) { |
| _LocalNameScope outerScope = localScope; |
| try { |
| localScope = _LocalNameScope.forConstructor(localScope, node); |
| super.visitConstructorDeclaration(node); |
| } finally { |
| localScope = outerScope; |
| } |
| } |
| |
| @override |
| void visitConstructorName(ConstructorName node) { |
| if (node.parent is! ConstructorDeclaration) { |
| super.visitConstructorName(node); |
| } |
| } |
| |
| @override |
| void visitFunctionDeclaration(FunctionDeclaration node) { |
| _LocalNameScope outerScope = localScope; |
| try { |
| localScope = _LocalNameScope.forFunction(localScope, node); |
| super.visitFunctionDeclaration(node); |
| } finally { |
| localScope = outerScope; |
| } |
| } |
| |
| @override |
| void visitFunctionTypeAlias(FunctionTypeAlias node) { |
| _LocalNameScope outerScope = localScope; |
| try { |
| localScope = _LocalNameScope.forFunctionTypeAlias(localScope, node); |
| super.visitFunctionTypeAlias(node); |
| } finally { |
| localScope = outerScope; |
| } |
| } |
| |
| @override |
| void visitImportDirective(ImportDirective node) { |
| var prefix = node.prefix; |
| if (prefix != null) { |
| importPrefixNames.add(prefix.name); |
| } |
| super.visitImportDirective(node); |
| } |
| |
| @override |
| void visitMethodDeclaration(MethodDeclaration node) { |
| _LocalNameScope outerScope = localScope; |
| try { |
| localScope = _LocalNameScope.forMethod(localScope, node); |
| super.visitMethodDeclaration(node); |
| } finally { |
| localScope = outerScope; |
| } |
| } |
| |
| @override |
| void visitSimpleIdentifier(SimpleIdentifier node) { |
| // Ignore all declarations. |
| if (node.inDeclarationContext()) { |
| return; |
| } |
| // Ignore class names references from constructors. |
| var parent = node.parent!; |
| if (parent is ConstructorDeclaration && parent.returnType == node) { |
| return; |
| } |
| // Prepare name. |
| String name = node.name; |
| // Ignore names shadowed by local elements. |
| if (node.isQualified || _isNameExpressionLabel(parent)) { |
| // Cannot be local. |
| } else { |
| if (localScope.contains(name)) { |
| return; |
| } |
| if (importPrefixNames.contains(name)) { |
| return; |
| } |
| } |
| // Do add the name. |
| names.add(name); |
| } |
| |
| static bool _isNameExpressionLabel(AstNode parent) { |
| if (parent is Label) { |
| var parent2 = parent.parent; |
| return parent2 is NamedExpression && parent2.name == parent; |
| } |
| return false; |
| } |
| } |