mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 09:31:58 +00:00
Script to scrape corpus to evaluate "super." parameter proposals.
Change-Id: I7c0635c2fbe7ec3f739c35b8e6a3cd1d0e2bfe22 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/215120 Reviewed-by: Nate Bosch <nbosch@google.com> Commit-Queue: Bob Nystrom <rnystrom@google.com>
This commit is contained in:
parent
f824db7bc2
commit
822140714d
317
pkg/scrape/example/superclass_parameters.dart
Normal file
317
pkg/scrape/example/superclass_parameters.dart
Normal file
|
@ -0,0 +1,317 @@
|
|||
// 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';
|
||||
|
||||
enum ArgumentMatch {
|
||||
noArguments,
|
||||
none,
|
||||
all,
|
||||
some,
|
||||
prefix,
|
||||
suffix,
|
||||
middle,
|
||||
noncontiguous
|
||||
}
|
||||
|
||||
extension on ArgumentMatch {
|
||||
String get description {
|
||||
switch (this) {
|
||||
case ArgumentMatch.noArguments:
|
||||
return 'No arguments to match';
|
||||
case ArgumentMatch.none:
|
||||
return 'Matched none';
|
||||
case ArgumentMatch.all:
|
||||
return 'Matched all';
|
||||
case ArgumentMatch.some:
|
||||
return 'Matched some';
|
||||
case ArgumentMatch.prefix:
|
||||
return 'Matched prefix';
|
||||
case ArgumentMatch.suffix:
|
||||
return 'Matched suffix';
|
||||
case ArgumentMatch.middle:
|
||||
return 'Matched middle';
|
||||
case ArgumentMatch.noncontiguous:
|
||||
return 'Matched noncontiguous';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void main(List<String> arguments) {
|
||||
Scrape()
|
||||
..addHistogram('Potential use')
|
||||
..addHistogram('Individual arguments')
|
||||
..addHistogram('Named arguments')
|
||||
..addHistogram('Positional arguments')
|
||||
..addHistogram('Argument pattern')
|
||||
..addHistogram('Append super args')
|
||||
..addHistogram('Prepend super args')
|
||||
..addHistogram('Insert super args')
|
||||
..addHistogram('Do not merge super args')
|
||||
..addHistogram('No explicit super(), call unnamed')
|
||||
..addHistogram('No explicit super(), call same name')
|
||||
..addVisitor(() => SuperclassParameterVisitor())
|
||||
..runCommandLine(arguments);
|
||||
}
|
||||
|
||||
class SuperclassParameterVisitor extends ScrapeVisitor {
|
||||
@override
|
||||
void visitConstructorDeclaration(ConstructorDeclaration node) {
|
||||
// Whether the constructor might benefit from the feature at all.
|
||||
var initializer = _findSuper(node);
|
||||
if (initializer == null) {
|
||||
record('Potential use', 'No: No initializer');
|
||||
return;
|
||||
}
|
||||
|
||||
if (initializer.argumentList.arguments.isEmpty) {
|
||||
record('Potential use', 'No: Empty super() argument list');
|
||||
return;
|
||||
}
|
||||
|
||||
record('Potential use', 'Yes');
|
||||
|
||||
// If we get here, we have a superclass constructor call with arguments.
|
||||
// See if any of them could use the feature.
|
||||
var positionalParamNames = node.parameters.parameters
|
||||
.where((param) => param.isPositional)
|
||||
.map((param) => param.identifier!.name)
|
||||
.toList();
|
||||
|
||||
var namedParamNames = node.parameters.parameters
|
||||
.where((param) => param.isNamed)
|
||||
.map((param) => param.identifier!.name)
|
||||
.toSet();
|
||||
|
||||
var matchedNamedArguments = 0;
|
||||
var unmatchedNamedArguments = 0;
|
||||
|
||||
var lastPositionalParam = -1;
|
||||
var matchedIndexes = <int>[];
|
||||
var unmatchedPositionalArguments = 0;
|
||||
var positionalArgCount = 0;
|
||||
for (var i = 0; i < initializer.argumentList.arguments.length; i++) {
|
||||
var argument = initializer.argumentList.arguments[i];
|
||||
|
||||
if (argument is NamedExpression) {
|
||||
var expression = argument.expression;
|
||||
if (expression is! SimpleIdentifier) {
|
||||
record('Individual arguments',
|
||||
'Named argument expression is not identifier');
|
||||
unmatchedNamedArguments++;
|
||||
} else if (argument.name.label.name != expression.name) {
|
||||
record('Individual arguments',
|
||||
'Named argument name does not match expression name');
|
||||
unmatchedNamedArguments++;
|
||||
} else if (!namedParamNames.contains(expression.name)) {
|
||||
record('Individual arguments',
|
||||
'Named argument does not match a parameter');
|
||||
unmatchedNamedArguments++;
|
||||
} else {
|
||||
record('Individual arguments', 'Argument matches a parameter');
|
||||
matchedNamedArguments++;
|
||||
}
|
||||
} else {
|
||||
positionalArgCount++;
|
||||
if (argument is! SimpleIdentifier) {
|
||||
record('Individual arguments',
|
||||
'Positional argument expression is not identifier');
|
||||
unmatchedPositionalArguments++;
|
||||
} else {
|
||||
// Start searching after the last matched positional parameter because
|
||||
// we don't allow reordering them. If two arguments are out of order,
|
||||
// that doesn't mean we can't use "super." at all, just that we can
|
||||
// only use it for *one* of those arguments.
|
||||
var index =
|
||||
positionalParamNames.indexOf(argument.name, lastPositionalParam);
|
||||
if (index == -1) {
|
||||
record('Individual arguments',
|
||||
'Positional argument does not match a parameter');
|
||||
} else {
|
||||
record('Individual arguments', 'Argument matches a parameter');
|
||||
lastPositionalParam = index;
|
||||
matchedIndexes.add(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Characterize the positional argument list.
|
||||
ArgumentMatch positionalMatch;
|
||||
if (unmatchedPositionalArguments == 0) {
|
||||
if (matchedIndexes.isEmpty) {
|
||||
positionalMatch = ArgumentMatch.noArguments;
|
||||
} else {
|
||||
positionalMatch = ArgumentMatch.all;
|
||||
}
|
||||
} else if (matchedIndexes.isEmpty) {
|
||||
positionalMatch = ArgumentMatch.none;
|
||||
} else {
|
||||
// If there is any unmatched argument before a matched one, then the
|
||||
// matched arguments are not all at the beginning.
|
||||
var matchedArePrefix = true;
|
||||
for (var i = 1; i < positionalArgCount; i++) {
|
||||
if (!matchedIndexes.contains(i - 1) && matchedIndexes.contains(i)) {
|
||||
matchedArePrefix = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If there is any unmatched argument after a matched one, then the
|
||||
// matched arguments are not all at the end.
|
||||
var matchedAreSuffix = true;
|
||||
for (var i = 0; i < positionalArgCount - 1; i++) {
|
||||
if (!matchedIndexes.contains(i + 1) && matchedIndexes.contains(i)) {
|
||||
matchedAreSuffix = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If any index between the first and last matched arg is not matched,
|
||||
// then the arguments are not contiguous.
|
||||
var matchedAreContiguous = true;
|
||||
if (matchedIndexes.isNotEmpty) {
|
||||
for (var i = matchedIndexes.first; i <= matchedIndexes.last; i++) {
|
||||
if (!matchedIndexes.contains(i)) {
|
||||
matchedAreContiguous = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!matchedAreContiguous) {
|
||||
positionalMatch = ArgumentMatch.noncontiguous;
|
||||
} else if (matchedArePrefix) {
|
||||
positionalMatch = ArgumentMatch.prefix;
|
||||
} else if (matchedAreSuffix) {
|
||||
positionalMatch = ArgumentMatch.suffix;
|
||||
} else {
|
||||
positionalMatch = ArgumentMatch.middle;
|
||||
}
|
||||
}
|
||||
|
||||
record('Positional arguments', positionalMatch.description);
|
||||
|
||||
// Characterize the named argument list.
|
||||
ArgumentMatch namedMatch;
|
||||
if (matchedNamedArguments == 0) {
|
||||
if (unmatchedNamedArguments == 0) {
|
||||
namedMatch = ArgumentMatch.noArguments;
|
||||
} else {
|
||||
namedMatch = ArgumentMatch.none;
|
||||
}
|
||||
} else {
|
||||
if (unmatchedNamedArguments == 0) {
|
||||
namedMatch = ArgumentMatch.all;
|
||||
} else {
|
||||
namedMatch = ArgumentMatch.some;
|
||||
}
|
||||
}
|
||||
|
||||
record('Named arguments', namedMatch.description);
|
||||
|
||||
var pattern = [
|
||||
for (var i = 0; i < positionalArgCount; i++)
|
||||
matchedIndexes.contains(i) ? 's' : '_',
|
||||
for (var i = 0; i < matchedNamedArguments; i++) ':s',
|
||||
for (var i = 0; i < unmatchedNamedArguments; i++) ':_',
|
||||
].join(',');
|
||||
record('Argument pattern', '($pattern)');
|
||||
|
||||
// If none of the arguments could be 'super.', then none of the proposals
|
||||
// apply.
|
||||
if (matchedIndexes.isEmpty && matchedNamedArguments == 0) return;
|
||||
|
||||
var append = true;
|
||||
var prepend = true;
|
||||
var insert = true;
|
||||
var noMerge = true;
|
||||
var allParams = true;
|
||||
|
||||
switch (positionalMatch) {
|
||||
case ArgumentMatch.noArguments:
|
||||
case ArgumentMatch.all:
|
||||
// OK.
|
||||
break;
|
||||
|
||||
case ArgumentMatch.none:
|
||||
allParams = false;
|
||||
break;
|
||||
|
||||
case ArgumentMatch.some:
|
||||
throw Exception('Should not get some for positional args.');
|
||||
|
||||
case ArgumentMatch.prefix:
|
||||
append = false;
|
||||
noMerge = false;
|
||||
allParams = false;
|
||||
break;
|
||||
|
||||
case ArgumentMatch.suffix:
|
||||
prepend = false;
|
||||
noMerge = false;
|
||||
allParams = false;
|
||||
break;
|
||||
|
||||
case ArgumentMatch.middle:
|
||||
append = false;
|
||||
prepend = false;
|
||||
noMerge = false;
|
||||
allParams = false;
|
||||
break;
|
||||
|
||||
case ArgumentMatch.noncontiguous:
|
||||
append = false;
|
||||
prepend = false;
|
||||
insert = false;
|
||||
noMerge = false;
|
||||
allParams = false;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (namedMatch) {
|
||||
case ArgumentMatch.noArguments:
|
||||
case ArgumentMatch.all:
|
||||
// OK.
|
||||
break;
|
||||
|
||||
case ArgumentMatch.none:
|
||||
case ArgumentMatch.some:
|
||||
allParams = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Exception('Unexpected match.');
|
||||
}
|
||||
|
||||
record('Append super args', append ? 'Yes' : 'No');
|
||||
record('Prepend super args', prepend ? 'Yes' : 'No');
|
||||
record('Insert super args', insert ? 'Yes' : 'No');
|
||||
record('Do not merge super args', noMerge ? 'Yes' : 'No');
|
||||
|
||||
var subName = _constructorName(node.name);
|
||||
var superName = _constructorName(initializer.constructorName);
|
||||
|
||||
record('No explicit super(), call same name',
|
||||
(allParams && superName == subName) ? 'Yes' : 'No');
|
||||
|
||||
record('No explicit super(), call unnamed',
|
||||
(allParams && superName == '(unnamed)') ? 'Yes' : 'No');
|
||||
}
|
||||
|
||||
String _constructorName(SimpleIdentifier? name) {
|
||||
if (name == null) return '(unnamed)';
|
||||
return name.name;
|
||||
}
|
||||
|
||||
SuperConstructorInvocation? _findSuper(ConstructorDeclaration node) {
|
||||
for (var initializer in node.initializers) {
|
||||
if (initializer is SuperConstructorInvocation) {
|
||||
return initializer;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue