// Copyright (c) 2020, 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 'dart:math' as math; import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/dart/ast/token.dart'; import 'package:scrape/scrape.dart'; /// The paths to each build() method and its maximum nesting level. final buildMethods = {}; bool simplifyNames = false; void main(List arguments) { arguments = arguments.toList(); simplifyNames = arguments.remove('--simplify'); var allCode = arguments.remove('--all'); Scrape() // The number of levels of nesting active when each argument appears. ..addHistogram('Nesting depth') // The number of levels of nesting active when each argument appears without // counting lists. ..addHistogram('Ignoring lists') // The number of levels of "child" or "children" named parameter nesting // when each argument appears. ..addHistogram('Child nesting depth') ..addHistogram('Argument names') // Strings that describe the structure of each nested argument. ..addHistogram('Argument nesting') ..addVisitor(() => NestingVisitor(allCode: allCode)) ..runCommandLine(arguments); var methods = buildMethods.keys.toList(); methods.sort((a, b) => buildMethods[b]!.compareTo(buildMethods[a]!)); for (var method in methods) { print('${buildMethods[method].toString().padLeft(3)}: $method'); } print('${buildMethods.length} build() methods'); } class NestingVisitor extends ScrapeVisitor { final List _stack = []; final bool _allCode; bool _pushed = false; int _deepestNesting = 0; new({bool? allCode}) : _allCode = allCode ?? false; @override void beforeVisitBuildMethod(Declaration node) { _deepestNesting = 0; } @override void afterVisitBuildMethod(Declaration node) { var startLine = lineInfo.getLocation(node.offset).lineNumber; buildMethods['$path:$startLine'] = _deepestNesting; } @override void visitArgumentList(ArgumentList node) { // Only argument lists with trailing commas get indentation. if (node.arguments.isNotEmpty && node.arguments.last.endToken.next!.type == TokenType.COMMA) { String name; var parent = node.parent; if (parent is MethodInvocation) { name = parent.methodName.name; } else if (parent is InstanceCreationExpression) { name = parent.constructorName.toString(); } else if (parent is SuperConstructorInvocation) { name = 'super.${parent.constructorName}'; } else { name = '?(${parent.runtimeType})?'; } if (simplifyNames) { name = ''; } for (var argument in node.arguments) { var argName = argument is NamedArgument ? argument.name.lexeme : ''; if (_allCode || isInFlutterBuildMethod) { record('Argument names', argName); } if (simplifyNames && argName != 'child' && argName != 'children') { argName = '_'; } _push('$name($argName:'); argument.accept(this); _pop(); } } else { node.visitChildren(this); } } @override void visitBlock(Block node) { var isFunction = node.parent is BlockFunctionBody; if (!isFunction) _push('{'); node.visitChildren(this); if (!isFunction) _pop(); } @override void visitExpressionFunctionBody(ExpressionFunctionBody node) { _push('=>'); node.visitChildren(this); _pop(); } @override void visitFunctionExpression(FunctionExpression node) { var isDeclaration = node.parent is FunctionDeclaration; if (!isDeclaration) _push('(){'); node.visitChildren(this); if (!isDeclaration) _pop(); } @override void visitListLiteral(ListLiteral node) { for (var element in node.elements) { _push('['); element.accept(this); _pop(); } } @override void visitSetOrMapLiteral(SetOrMapLiteral node) { for (var element in node.elements) { _push('{'); element.accept(this); _pop(); } } void _push(String string) { _stack.add(string); _pushed = true; _deepestNesting = math.max(_deepestNesting, _stack.length); } void _pop() { if (_pushed && (_allCode || isInFlutterBuildMethod)) { record('Argument nesting', _stack.join(' ')); record('Nesting depth', _stack.length); record('Ignoring lists', _stack.where((s) => s != '[').length); record( 'Child nesting depth', _stack.where((s) => s.contains('child')).length, ); } _pushed = false; _stack.removeLast(); } }