Created
February 1, 2023 00:44
-
-
Save munificent/e03728874aae4d16a9760f207aecb16c to your computer and use it in GitHub Desktop.
Analyze a Dart corpus for uses of "_" as an identifier
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:analyzer/dart/ast/ast.dart'; | |
import 'package:analyzer/dart/ast/token.dart'; | |
import 'package:scrape/scrape.dart'; | |
final wildcardPattern = RegExp(r'^_+$'); | |
final typeNamePattern = RegExp(r'^_*[$A-Z]'); | |
void main(List<String> arguments) { | |
Scrape() | |
..addHistogram('Declaration') | |
..addHistogram('Use') | |
..addVisitor(() => WildcardVisitor()) | |
..runCommandLine(arguments); | |
} | |
class WildcardVisitor extends ScrapeVisitor { | |
final Set<Token> _visited = {}; | |
final Set<Token> _unvisited = {}; | |
@override | |
void visitCompilationUnit(CompilationUnit node) { | |
_visited.clear(); | |
_unvisited.clear(); | |
// Collect every wildcard token in the file. This way, we make sure that we | |
// find every single one in the parser. | |
var token = startToken; | |
while (token.type != TokenType.EOF) { | |
if (token.type == TokenType.IDENTIFIER && | |
wildcardPattern.hasMatch(token.lexeme)) { | |
_unvisited.add(token); | |
} | |
token = token.next!; | |
} | |
super.visitCompilationUnit(node); | |
// Validate that we didn't miss any wildcards. | |
if (_unvisited.isNotEmpty) { | |
log('$path missing:'); | |
for (var token in _unvisited) { | |
printToken(token); | |
} | |
} | |
} | |
@override | |
void visitAssignmentExpression(AssignmentExpression node) { | |
var target = node.leftHandSide; | |
if (target is SimpleIdentifier) { | |
_checkWildcard(target.token, 'Use', 'Assignment target'); | |
} | |
super.visitAssignmentExpression(node); | |
} | |
@override | |
void visitCatchClauseParameter(CatchClauseParameter node) { | |
_checkWildcard(node.name, 'Declaration', 'Catch parameter'); | |
super.visitCatchClauseParameter(node); | |
} | |
@override | |
void visitConstructorDeclaration(ConstructorDeclaration node) { | |
_checkWildcard(node.name, 'Declaration', 'Constructor name'); | |
_checkConstructorName(node.redirectedConstructor, 'Use', | |
'Factory constructor redirecting to private name'); | |
super.visitConstructorDeclaration(node); | |
} | |
@override | |
void visitConstructorFieldInitializer(ConstructorFieldInitializer node) { | |
_checkWildcard(node.fieldName.token, 'Use', 'Field initializer'); | |
super.visitConstructorFieldInitializer(node); | |
} | |
@override | |
void visitDefaultFormalParameter(DefaultFormalParameter node) { | |
super.visitDefaultFormalParameter(node); | |
} | |
@override | |
void visitEnumConstantDeclaration(EnumConstantDeclaration node) { | |
_checkWildcard(node.name, 'Declaration', 'Enum value name'); | |
_checkWildcard(node.arguments?.constructorSelector?.name.token, 'Use', | |
'Private constructor invocation'); | |
super.visitEnumConstantDeclaration(node); | |
} | |
@override | |
void visitExtensionDeclaration(ExtensionDeclaration node) { | |
_checkWildcard(node.name, 'Declaration', 'Extension name'); | |
super.visitExtensionDeclaration(node); | |
} | |
@override | |
void visitForEachPartsWithDeclaration(ForEachPartsWithDeclaration node) { | |
_checkWildcard(node.loopVariable.name, 'Declaration', 'Loop variable'); | |
super.visitForEachPartsWithDeclaration(node); | |
} | |
@override | |
void visitFieldDeclaration(FieldDeclaration node) { | |
for (var variable in node.fields.variables) { | |
_checkWildcard(variable.name, 'Declaration', | |
node.isStatic ? 'Static field' : 'Instance field'); | |
} | |
super.visitFieldDeclaration(node); | |
} | |
@override | |
void visitFieldFormalParameter(FieldFormalParameter node) { | |
super.visitFieldFormalParameter(node); | |
} | |
@override | |
void visitFunctionDeclaration(FunctionDeclaration node) { | |
_checkWildcard(node.name, 'Declaration', 'Function name'); | |
super.visitFunctionDeclaration(node); | |
} | |
@override | |
void visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) { | |
super.visitFunctionTypedFormalParameter(node); | |
} | |
@override | |
void visitInstanceCreationExpression(InstanceCreationExpression node) { | |
_checkConstructorName( | |
node.constructorName, 'Use', 'Private constructor invocation'); | |
super.visitInstanceCreationExpression(node); | |
} | |
@override | |
void visitMethodDeclaration(MethodDeclaration node) { | |
_checkWildcard(node.name, 'Declaration', 'Method name'); | |
super.visitMethodDeclaration(node); | |
} | |
@override | |
void visitMethodInvocation(MethodInvocation node) { | |
var target = node.target; | |
if (target is SimpleIdentifier && typeNamePattern.hasMatch(target.name)) { | |
_checkWildcard( | |
node.methodName.token, 'Use', 'Private constructor invocation'); | |
} | |
super.visitMethodInvocation(node); | |
} | |
@override | |
void visitNamedType(NamedType node) { | |
var name = node.name; | |
if (name is SimpleIdentifier) { | |
_checkWildcard(name.token, 'Use', 'Type annotation'); | |
} else { | |
_checkWildcard((name as PrefixedIdentifier).prefix.token, 'Use', | |
'Type annotation prefix'); | |
_checkWildcard((name as PrefixedIdentifier).identifier.token, 'Use', | |
'Prefixed type annotation'); | |
} | |
super.visitNamedType(node); | |
} | |
@override | |
void visitRedirectingConstructorInvocation( | |
RedirectingConstructorInvocation node) { | |
_checkWildcard(node.constructorName?.token, 'Use', | |
'Redirection to private constructor'); | |
super.visitRedirectingConstructorInvocation(node); | |
} | |
@override | |
void visitSimpleIdentifier(SimpleIdentifier node) { | |
_checkWildcard(node.token, 'Use', 'Identifier expression'); | |
} | |
@override | |
void visitSimpleFormalParameter(SimpleFormalParameter node) { | |
_checkWildcard(node.name, 'Declaration', 'Parameter name'); | |
super.visitSimpleFormalParameter(node); | |
} | |
@override | |
void visitSuperConstructorInvocation(SuperConstructorInvocation node) { | |
_checkWildcard(node.constructorName?.token, 'Use', | |
'Private superclass constructor invocation'); | |
super.visitSuperConstructorInvocation(node); | |
} | |
@override | |
void visitSuperFormalParameter(SuperFormalParameter node) { | |
super.visitSuperFormalParameter(node); | |
} | |
@override | |
void visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { | |
for (var variable in node.variables.variables) { | |
_checkWildcard(variable.name, 'Declaration', 'Top level variable'); | |
} | |
super.visitTopLevelVariableDeclaration(node); | |
} | |
@override | |
void visitTypeParameter(TypeParameter node) { | |
_checkWildcard(node.name, 'Declaration', 'Type parameter'); | |
super.visitTypeParameter(node); | |
} | |
@override | |
void visitVariableDeclaration(VariableDeclaration node) { | |
_checkWildcard(node.name, 'Declaration', | |
'Unknown variable declaration ${node.parent!.parent.runtimeType}'); | |
super.visitVariableDeclaration(node); | |
} | |
@override | |
void visitVariableDeclarationStatement(VariableDeclarationStatement node) { | |
for (var variable in node.variables.variables) { | |
_checkWildcard(variable.name, 'Declaration', 'Local variable'); | |
} | |
super.visitVariableDeclarationStatement(node); | |
} | |
void _checkConstructorName( | |
ConstructorName? name, String category, String type) { | |
if (name == null) return; | |
_checkWildcard(name.name?.token, category, type); | |
var typeName = name.type.name; | |
if (typeName is PrefixedIdentifier) { | |
_checkWildcard(typeName.identifier.token, category, type); | |
} | |
} | |
void _checkWildcard(Token? token, String category, String type) { | |
if (token == null) return; | |
if (!wildcardPattern.hasMatch(token.lexeme)) return; | |
// Don't visit the same node multiple times. If a higher level AST node | |
// has already categorized it, we're done. | |
if (_visited.contains(token)) return; | |
record(category, type); | |
_unvisited.remove(token); | |
_visited.add(token); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment