mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 10:49:00 +00:00
Issue 21965. Add Quick Fix for adding missing named parameter.
R=brianwilkerson@google.com Bug: https://github.com/dart-lang/sdk/issues/21965 Change-Id: I81a39df6fd38f9f562b494483b3fe0f67f9a627b Reviewed-on: https://dart-review.googlesource.com/54360 Commit-Queue: Konstantin Shcheglov <scheglov@google.com> Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
parent
964dae472c
commit
fd9823ae9e
3 changed files with 261 additions and 0 deletions
|
@ -98,6 +98,8 @@ class DartFixKind {
|
|||
'ADD_MISSING_PARAMETER_POSITIONAL',
|
||||
31,
|
||||
"Add optional positional parameter");
|
||||
static const ADD_MISSING_PARAMETER_NAMED = const FixKind(
|
||||
'ADD_MISSING_PARAMETER_NAMED', 30, "Add named parameter '{0}'");
|
||||
static const ADD_MISSING_PARAMETER_REQUIRED = const FixKind(
|
||||
'ADD_MISSING_PARAMETER_REQUIRED', 30, "Add required parameter");
|
||||
static const ADD_MISSING_REQUIRED_ARGUMENT = const FixKind(
|
||||
|
|
|
@ -361,6 +361,9 @@ class FixProcessor {
|
|||
await _addFix_importLibrary_withTopLevelVariable();
|
||||
await _addFix_createLocalVariable();
|
||||
}
|
||||
if (errorCode == StaticWarningCode.UNDEFINED_NAMED_PARAMETER) {
|
||||
await _addFix_addMissingNamedArgument();
|
||||
}
|
||||
if (errorCode == StaticTypeWarningCode.UNDEFINED_METHOD_WITH_CONSTRUCTOR) {
|
||||
await _addFix_undefinedMethodWithContructor();
|
||||
}
|
||||
|
@ -608,6 +611,60 @@ class FixProcessor {
|
|||
}
|
||||
}
|
||||
|
||||
Future<Null> _addFix_addMissingNamedArgument() async {
|
||||
// Prepare the name of the missing parameter.
|
||||
if (this.node is! SimpleIdentifier) {
|
||||
return;
|
||||
}
|
||||
SimpleIdentifier node = this.node;
|
||||
String name = node.name;
|
||||
|
||||
// We expect that the node is part of a NamedExpression.
|
||||
if (node.parent?.parent is! NamedExpression) {
|
||||
return;
|
||||
}
|
||||
NamedExpression namedExpression = node.parent.parent;
|
||||
|
||||
// We should be in an ArgumentList.
|
||||
if (namedExpression.parent is! ArgumentList) {
|
||||
return;
|
||||
}
|
||||
AstNode argumentList = namedExpression.parent;
|
||||
|
||||
// Prepare the invoked element.
|
||||
var context =
|
||||
new _ExecutableParameters(session, astProvider, argumentList.parent);
|
||||
if (context == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We cannot add named parameters when there are positional positional.
|
||||
if (context.optionalPositional.isNotEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
Future<void> addParameter(int offset, String prefix, String suffix) async {
|
||||
if (offset != null) {
|
||||
DartChangeBuilder changeBuilder = await context.addParameter(
|
||||
offset, prefix, namedExpression.staticType, name, suffix);
|
||||
_addFixFromBuilder(
|
||||
changeBuilder, DartFixKind.ADD_MISSING_PARAMETER_NAMED,
|
||||
args: [name]);
|
||||
}
|
||||
}
|
||||
|
||||
if (context.named.isNotEmpty) {
|
||||
var prevNode = await context.getParameterNode(context.named.last);
|
||||
await addParameter(prevNode?.end, ', ', '');
|
||||
} else if (context.required.isNotEmpty) {
|
||||
var prevNode = await context.getParameterNode(context.required.last);
|
||||
await addParameter(prevNode?.end, ', {', '}');
|
||||
} else {
|
||||
var parameterList = await context.getParameterList();
|
||||
await addParameter(parameterList?.leftParenthesis?.end, '{', '}');
|
||||
}
|
||||
}
|
||||
|
||||
Future<Null> _addFix_addMissingParameter() async {
|
||||
if (node is ArgumentList && node.parent is MethodInvocation) {
|
||||
ArgumentList argumentList = node;
|
||||
|
@ -3729,3 +3786,91 @@ class _ClosestElementFinder {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* [ExecutableElement], its parameters, and operations on them.
|
||||
*/
|
||||
class _ExecutableParameters {
|
||||
final AnalysisSession session;
|
||||
final AstProvider astProvider;
|
||||
final ExecutableElement executable;
|
||||
|
||||
final List<ParameterElement> required = [];
|
||||
final List<ParameterElement> optionalPositional = [];
|
||||
final List<ParameterElement> named = [];
|
||||
|
||||
factory _ExecutableParameters(
|
||||
AnalysisSession session, AstProvider astProvider, AstNode invocation) {
|
||||
Element element;
|
||||
if (invocation is InstanceCreationExpression) {
|
||||
element = invocation.staticElement;
|
||||
}
|
||||
if (invocation is MethodInvocation) {
|
||||
element = invocation.methodName.staticElement;
|
||||
}
|
||||
if (element is ExecutableElement) {
|
||||
return new _ExecutableParameters._(session, astProvider, element);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
_ExecutableParameters._(this.session, this.astProvider, this.executable) {
|
||||
for (var parameter in executable.parameters) {
|
||||
if (parameter.isNotOptional) {
|
||||
required.add(parameter);
|
||||
} else if (parameter.isOptionalPositional) {
|
||||
optionalPositional.add(parameter);
|
||||
} else if (parameter.isNamed) {
|
||||
named.add(parameter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the code for a new parameter with the given [type] and [name].
|
||||
*/
|
||||
Future<DartChangeBuilder> addParameter(int offset, String prefix,
|
||||
DartType type, String name, String suffix) async {
|
||||
String targetFile = executable.source.fullName;
|
||||
var changeBuilder = new DartChangeBuilder(session);
|
||||
await changeBuilder.addFileEdit(targetFile, (builder) {
|
||||
builder.addInsertion(offset, (builder) {
|
||||
builder.write(prefix);
|
||||
builder.writeParameter(name, type: type);
|
||||
builder.write(suffix);
|
||||
});
|
||||
});
|
||||
return changeBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the [FormalParameterList] of the [executable], or `null` is cannot
|
||||
* be found.
|
||||
*/
|
||||
Future<FormalParameterList> getParameterList() async {
|
||||
var name = await astProvider.getParsedNameForElement(executable);
|
||||
AstNode targetDeclaration = name?.parent;
|
||||
if (targetDeclaration is FunctionDeclaration) {
|
||||
FunctionExpression function = targetDeclaration.functionExpression;
|
||||
return function.parameters;
|
||||
} else if (targetDeclaration is MethodDeclaration) {
|
||||
return targetDeclaration.parameters;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the [FormalParameter] of the [element] in [FormalParameterList],
|
||||
* or `null` is cannot be found.
|
||||
*/
|
||||
Future<FormalParameter> getParameterNode(ParameterElement element) async {
|
||||
var name = await astProvider.getParsedNameForElement(element);
|
||||
for (AstNode node = name; node != null; node = node.parent) {
|
||||
if (node is FormalParameter && node.parent is FormalParameterList) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -825,6 +825,120 @@ class A {
|
|||
''');
|
||||
}
|
||||
|
||||
test_addMissingParameterNamed_function_hasNamed() async {
|
||||
await resolveTestUnit('''
|
||||
test(int a, {int b: 0}) {}
|
||||
|
||||
main() {
|
||||
test(1, b: 2, named: 3.0);
|
||||
}
|
||||
''');
|
||||
await assertHasFix(DartFixKind.ADD_MISSING_PARAMETER_NAMED, '''
|
||||
test(int a, {int b: 0, double named}) {}
|
||||
|
||||
main() {
|
||||
test(1, b: 2, named: 3.0);
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
test_addMissingParameterNamed_function_hasRequired() async {
|
||||
await resolveTestUnit('''
|
||||
test(int a) {}
|
||||
|
||||
main() {
|
||||
test(1, named: 2.0);
|
||||
}
|
||||
''');
|
||||
await assertHasFix(DartFixKind.ADD_MISSING_PARAMETER_NAMED, '''
|
||||
test(int a, {double named}) {}
|
||||
|
||||
main() {
|
||||
test(1, named: 2.0);
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
test_addMissingParameterNamed_function_noParameters() async {
|
||||
await resolveTestUnit('''
|
||||
test() {}
|
||||
|
||||
main() {
|
||||
test(named: 42);
|
||||
}
|
||||
''');
|
||||
await assertHasFix(DartFixKind.ADD_MISSING_PARAMETER_NAMED, '''
|
||||
test({int named}) {}
|
||||
|
||||
main() {
|
||||
test(named: 42);
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
test_addMissingParameterNamed_method_hasNamed() async {
|
||||
await resolveTestUnit('''
|
||||
class A {
|
||||
test(int a, {int b: 0}) {}
|
||||
|
||||
main() {
|
||||
test(1, b: 2, named: 3.0);
|
||||
}
|
||||
}
|
||||
''');
|
||||
await assertHasFix(DartFixKind.ADD_MISSING_PARAMETER_NAMED, '''
|
||||
class A {
|
||||
test(int a, {int b: 0, double named}) {}
|
||||
|
||||
main() {
|
||||
test(1, b: 2, named: 3.0);
|
||||
}
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
test_addMissingParameterNamed_method_hasRequired() async {
|
||||
await resolveTestUnit('''
|
||||
class A {
|
||||
test(int a) {}
|
||||
|
||||
main() {
|
||||
test(1, named: 2.0);
|
||||
}
|
||||
}
|
||||
''');
|
||||
await assertHasFix(DartFixKind.ADD_MISSING_PARAMETER_NAMED, '''
|
||||
class A {
|
||||
test(int a, {double named}) {}
|
||||
|
||||
main() {
|
||||
test(1, named: 2.0);
|
||||
}
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
test_addMissingParameterNamed_method_noParameters() async {
|
||||
await resolveTestUnit('''
|
||||
class A {
|
||||
test() {}
|
||||
|
||||
main() {
|
||||
test(named: 42);
|
||||
}
|
||||
}
|
||||
''');
|
||||
await assertHasFix(DartFixKind.ADD_MISSING_PARAMETER_NAMED, '''
|
||||
class A {
|
||||
test({int named}) {}
|
||||
|
||||
main() {
|
||||
test(named: 42);
|
||||
}
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
test_addMissingRequiredArg_cons_flutter_children() async {
|
||||
addFlutterPackage();
|
||||
_addMetaPackageSource();
|
||||
|
|
Loading…
Reference in a new issue