// Copyright (c) 2021, 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:scrape/scrape.dart'; void main(List arguments) { Scrape() ..addHistogram('Constructor type') ..addHistogram('Constructor name') ..addHistogram('Non-factory body') ..addHistogram('Factory body') ..addHistogram('Super initializer') ..addHistogram('Constructor parameters') ..addHistogram('Constructor count', order: SortOrder.numeric) ..addHistogram('Field initialization') ..addHistogram('Field mutability') ..addHistogram('Fields in initializer lists') ..addVisitor(() => ConstructorVisitor()) ..runCommandLine(arguments); } class ConstructorVisitor extends ScrapeVisitor { final Map> fields = {}; int constructorCount = 0; @override void visitClassDeclaration(ClassDeclaration node) { fields.clear(); constructorCount = 0; super.visitClassDeclaration(node); // See how the fields were initialized. fields.forEach((name, initializer) { var sorted = initializer.toList()..sort(); if (sorted.isEmpty) { record('Field initialization', 'not initialized'); } else { record('Field initialization', sorted.join(', ')); } }); record('Constructor count', constructorCount); } @override void visitFieldDeclaration(FieldDeclaration node) { // Only care about instance fields. if (node.isStatic) return; var mutability = [ if (node.fields.isLate) 'late', if (node.fields.isFinal) 'final' ]; if (mutability.isEmpty) { record('Field mutability', 'var'); } else { record('Field mutability', mutability.join(' ')); } for (var field in node.fields.variables) { if (field.initializer != null) { fields.putIfAbsent(field.name.name, () => {}).add('at declaration'); } else { fields.putIfAbsent(field.name.name, () => {}); } } } @override void visitConstructorDeclaration(ConstructorDeclaration node) { constructorCount++; if (node.constKeyword != null) { if (node.factoryKeyword != null) { record('Constructor type', 'const factory'); } else { record('Constructor type', 'const'); } } else { if (node.factoryKeyword != null) { record('Constructor type', 'non-const factory'); } else { record('Constructor type', 'non-const'); } } if (node.name != null) { record('Constructor name', 'named'); } else { record('Constructor name', 'unnamed'); } var hasPositional = false; var hasOptional = false; var hasNamed = false; for (var parameter in node.parameters.parameters) { if (parameter.isRequiredPositional) hasPositional = true; if (parameter.isOptionalPositional) hasOptional = true; if (parameter.isNamed) hasNamed = true; if (parameter is DefaultFormalParameter) { parameter = parameter.parameter; } if (parameter is FieldFormalParameter) { fields .putIfAbsent(parameter.identifier.name, () => {}) .add('`this.` parameter'); } } var sections = [ if (hasPositional) 'positional', if (hasOptional) 'optional positional', if (hasNamed) 'named' ]; if (sections.isEmpty) { record('Constructor parameters', 'no parameters'); } else if (sections.length == 1) { record('Constructor parameters', 'only ${sections.first} parameters'); } else { record('Constructor parameters', 'both ${sections.join(" and ")} parameters'); } if (node.initializers.isNotEmpty) { var hasSuper = false; for (var initializer in node.initializers) { if (initializer is SuperConstructorInvocation) { hasSuper = true; } else if (initializer is ConstructorFieldInitializer) { fields .putIfAbsent(initializer.fieldName.name, () => {}) .add('initializer list'); var expr = initializer.expression; if (expr is SimpleIdentifier && '_${expr.name}' == initializer.fieldName.name) { record('Fields in initializer lists', '_field = field'); } else if (expr is NullLiteral || expr is BooleanLiteral || expr is IntegerLiteral || expr is DoubleLiteral || expr is StringLiteral) { record('Fields in initializer lists', 'field = literal'); } else { record('Fields in initializer lists', 'other'); } } } if (hasSuper) { record('Super initializer', 'has super()'); } else { record('Super initializer', 'none'); } } var bodyPrefix = node.factoryKeyword == null ? 'Non-factory' : 'Factory'; var body = node.body; if (body is EmptyFunctionBody) { record('$bodyPrefix body', ';'); } else if (body is ExpressionFunctionBody) { record('$bodyPrefix body', '=>'); } else if (body is BlockFunctionBody) { if (body.block.statements.isEmpty) { record('$bodyPrefix body', '{} (empty)'); } else { record('$bodyPrefix body', '{...}'); } } else { record('$bodyPrefix body', 'WTF'); } } }