Created
February 1, 2023 00:44
-
-
Save munificent/e03728874aae4d16a9760f207aecb16c to your computer and use it in GitHub Desktop.
Revisions
-
munificent created this gist
Feb 1, 2023 .There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,254 @@ 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); } }