Skip to content

Instantly share code, notes, and snippets.

@munificent
Created February 1, 2023 00:44
Show Gist options
  • Select an option

  • Save munificent/e03728874aae4d16a9760f207aecb16c to your computer and use it in GitHub Desktop.

Select an option

Save munificent/e03728874aae4d16a9760f207aecb16c to your computer and use it in GitHub Desktop.

Revisions

  1. munificent created this gist Feb 1, 2023.
    254 changes: 254 additions & 0 deletions wildcards.dart
    Original 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);
    }
    }