Resolve values of duplicate named arguments.

Change-Id: I3a84db8e7c7e29f730ec0687aee4ed0a62b84f94
Reviewed-on: https://dart-review.googlesource.com/66280
Reviewed-by: Paul Berry <paulberry@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
Konstantin Shcheglov 2018-07-24 21:30:50 +00:00 committed by commit-bot@chromium.org
parent e2dd823912
commit 1fe0518d3b
12 changed files with 151 additions and 66 deletions

View file

@ -357,12 +357,6 @@ class CompileTimeErrorCodeTest_Kernel extends CompileTimeErrorCodeTest_Driver {
return super.test_duplicateDefinition_inPart();
}
@override
@failingTest
test_duplicateNamedArgument() async {
await super.test_duplicateNamedArgument();
}
@override
@failingTest
@FastaProblem('https://github.com/dart-lang/sdk/issues/30959')

View file

@ -150,20 +150,6 @@ class NonErrorResolverTest_Kernel extends NonErrorResolverTest_Driver {
return super.test_constConstructorWithNonConstSuper_unresolved();
}
@override
@failingTest
test_fieldFormalParameter_genericFunctionTyped() {
// Expected 0 errors of type ParserErrorCode.EXPECTED_TOKEN, found 1 (88)
return super.test_fieldFormalParameter_genericFunctionTyped();
}
@override
@failingTest
test_fieldFormalParameter_genericFunctionTyped_named() {
// Expected 0 errors of type ParserErrorCode.EXPECTED_TOKEN, found 1 (89)
return super.test_fieldFormalParameter_genericFunctionTyped_named();
}
@override
@failingTest
@potentialAnalyzerProblem

View file

@ -157,12 +157,6 @@ class StaticTypeWarningCodeTest_Kernel
await super.test_returnOfInvalidType_async_future_int_mismatches_int();
}
@override
@failingTest
test_returnOfInvalidType_void() async {
await super.test_returnOfInvalidType_void();
}
@override
@failingTest
test_typeArgumentNotMatchingBounds_classTypeAlias() async {
@ -359,6 +353,12 @@ class StaticTypeWarningCodeTest_Kernel
await super.test_undefinedSuperMethod();
}
@override
@failingTest
test_undefinedSuperOperator_indexSetter() async {
await super.test_undefinedSuperOperator_indexSetter();
}
@override
@failingTest
test_wrongNumberOfTypeArguments_classAlias() async {

View file

@ -74,6 +74,14 @@ class AnalysisDriverResolutionTest extends BaseAnalysisDriverTest {
expect(actual, isNull);
}
void assertIdentifierTopGetRef(SimpleIdentifier ref, String name) {
var getter = findElement.topGet(name);
assertElement(ref, getter);
var type = getter.returnType.toString();
assertType(ref, type);
}
void assertInvokeType(InvocationExpression expression, String expected) {
DartType actual = expression.staticInvokeType;
expect(actual?.toString(), expected);
@ -86,10 +94,9 @@ class AnalysisDriverResolutionTest extends BaseAnalysisDriverTest {
expect(actual.baseElement, same(expectedBase));
}
void assertTopGetRef(String search, String name, String type) {
void assertTopGetRef(String search, String name) {
var ref = findNode.simple(search);
assertElement(ref, findElement.topGet(name));
assertType(ref, type);
assertIdentifierTopGetRef(ref, name);
}
void assertType(Expression expression, String expected) {
@ -3309,9 +3316,9 @@ main() {
var parenthesized = findNode.parenthesized('(a + b)');
assertType(parenthesized, 'int');
assertTopGetRef('a + b', 'a', 'int');
assertTopGetRef('b)', 'b', 'int');
assertTopGetRef('c;', 'c', 'double');
assertTopGetRef('a + b', 'a');
assertTopGetRef('b)', 'b');
assertTopGetRef('c;', 'c');
var assignment = findNode.assignment('= c');
if (useCFE) {
@ -3815,6 +3822,62 @@ main(C c) {
assertType(aRef, 'int');
}
test_invalid_invocation_arguments_named_duplicate2() async {
addTestFile(r'''
void f({p}) {}
int a, b;
main() {
f(p: a, p: b);
}
''');
await resolveTestFile();
expect(result.errors, isNotEmpty);
var f = findElement.function('f');
var invocation = findNode.methodInvocation('f(p: a');
assertElement(invocation.methodName, f);
assertType(invocation.methodName, '({p: dynamic}) → void');
assertType(invocation, 'void');
NamedExpression arg0 = invocation.argumentList.arguments[0];
assertElement(arg0.name.label, f.parameters[0]);
assertIdentifierTopGetRef(arg0.expression, 'a');
NamedExpression arg1 = invocation.argumentList.arguments[1];
assertElement(arg1.name.label, f.parameters[0]);
assertIdentifierTopGetRef(arg1.expression, 'b');
}
test_invalid_invocation_arguments_named_duplicate3() async {
addTestFile(r'''
void f({p}) {}
int a, b, c;
main() {
f(p: a, p: b, p: c);
}
''');
await resolveTestFile();
expect(result.errors, isNotEmpty);
var f = findElement.function('f');
var invocation = findNode.methodInvocation('f(p: a');
assertElement(invocation.methodName, f);
assertType(invocation.methodName, '({p: dynamic}) → void');
assertType(invocation, 'void');
NamedExpression arg0 = invocation.argumentList.arguments[0];
assertElement(arg0.name.label, f.parameters[0]);
assertIdentifierTopGetRef(arg0.expression, 'a');
NamedExpression arg1 = invocation.argumentList.arguments[1];
assertElement(arg1.name.label, f.parameters[0]);
assertIdentifierTopGetRef(arg1.expression, 'b');
NamedExpression arg2 = invocation.argumentList.arguments[2];
assertElement(arg2.name.label, f.parameters[0]);
assertIdentifierTopGetRef(arg2.expression, 'c');
}
test_invalid_invocation_arguments_static_method() async {
addTestFile(r'''
class C {

View file

@ -2053,6 +2053,27 @@ Message _withArgumentsDuplicatedNamePreviouslyUsedCause(String name) {
message: """Previous use of '${name}'.""", arguments: {'name': name});
}
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Template<Message Function(String name)> templateDuplicatedNamedArgument =
const Template<Message Function(String name)>(
messageTemplate: r"""Duplicated named argument '#name'.""",
withArguments: _withArgumentsDuplicatedNamedArgument);
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Code<Message Function(String name)> codeDuplicatedNamedArgument =
const Code<Message Function(String name)>(
"DuplicatedNamedArgument", templateDuplicatedNamedArgument,
analyzerCode: "DUPLICATE_NAMED_ARGUMENT",
dart2jsCode: "*fatal*",
severity: Severity.error);
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
Message _withArgumentsDuplicatedNamedArgument(String name) {
return new Message(codeDuplicatedNamedArgument,
message: """Duplicated named argument '${name}'.""",
arguments: {'name': name});
}
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Template<Message Function(String name)> templateDuplicatedParameterName =
const Template<Message Function(String name)>(

View file

@ -1027,35 +1027,6 @@ abstract class BodyBuilder extends ScopeListener<JumpTarget>
arguments.getRange(0, firstNamedArgumentIndex));
List<NamedExpression> named = new List<NamedExpression>.from(
arguments.getRange(firstNamedArgumentIndex, arguments.length));
if (named.length == 2) {
if (named[0].name == named[1].name) {
named = <NamedExpression>[
new NamedExpression(
named[1].name,
deprecated_buildCompileTimeError(
"Duplicated named argument '${named[1].name}'.",
named[1].fileOffset))
];
}
} else if (named.length > 2) {
Map<String, NamedExpression> seenNames = <String, NamedExpression>{};
bool hasProblem = false;
for (NamedExpression expression in named) {
if (seenNames.containsKey(expression.name)) {
hasProblem = true;
NamedExpression prevNamedExpression = seenNames[expression.name];
prevNamedExpression.value = deprecated_buildCompileTimeError(
"Duplicated named argument '${expression.name}'.",
expression.fileOffset)
..parent = prevNamedExpression;
} else {
seenNames[expression.name] = expression;
}
}
if (hasProblem) {
named = new List<NamedExpression>.from(seenNames.values);
}
}
push(forest.arguments(positional, beginToken, named: named));
} else {
// TODO(kmillikin): Find a way to avoid allocating a second list in the
@ -2873,7 +2844,7 @@ abstract class BodyBuilder extends ScopeListener<JumpTarget>
Set<String> names =
new Set.from(function.namedParameters.map((a) => a.name));
for (NamedExpression argument in named) {
if (!names.remove(argument.name)) {
if (!names.contains(argument.name)) {
return fasta.templateNoSuchNamedParameter
.withArguments(argument.name)
.withLocation(uri, argument.fileOffset, argument.name.length);
@ -2916,7 +2887,7 @@ abstract class BodyBuilder extends ScopeListener<JumpTarget>
Set<String> names =
new Set.from(function.namedParameters.map((a) => a.name));
for (NamedExpression argument in named) {
if (!names.remove(argument.name)) {
if (!names.contains(argument.name)) {
return fasta.templateNoSuchNamedParameter
.withArguments(argument.name)
.withLocation(uri, argument.fileOffset, argument.name.length);

View file

@ -77,6 +77,7 @@ import '../fasta_codes.dart'
messageVoidExpression,
noLength,
templateArgumentTypeNotAssignable,
templateDuplicatedNamedArgument,
templateImplicitCallOfNonMethod,
templateInvalidAssignment,
templateInvalidCastFunctionExpr,
@ -1374,6 +1375,48 @@ abstract class TypeInferrerImpl extends TypeInferrer {
receiverType, expressionType);
}
});
// Check for and remove duplicated named arguments.
var named = arguments.named;
if (named.length == 2) {
if (named[0].name == named[1].name) {
var name = named[1].name;
var error = helper.buildCompileTimeError(
templateDuplicatedNamedArgument.withArguments(name),
named[1].fileOffset,
name.length);
arguments.named = [new kernel.NamedExpression(named[1].name, error)];
formalTypes.removeLast();
actualTypes.removeLast();
}
} else if (named.length > 2) {
var seenNames = <String, kernel.NamedExpression>{};
var hasProblem = false;
var namedTypeIndex = arguments.positional.length;
var uniqueNamed = <kernel.NamedExpression>[];
for (var expression in named) {
var name = expression.name;
if (seenNames.containsKey(name)) {
hasProblem = true;
var prevNamedExpression = seenNames[name];
prevNamedExpression.value = helper.buildCompileTimeError(
templateDuplicatedNamedArgument.withArguments(name),
expression.fileOffset,
name.length)
..parent = prevNamedExpression;
formalTypes.removeAt(namedTypeIndex);
actualTypes.removeAt(namedTypeIndex);
} else {
seenNames[name] = expression;
uniqueNamed.add(expression);
namedTypeIndex++;
}
}
if (hasProblem) {
arguments.named = uniqueNamed;
}
}
if (inferenceNeeded) {
typeSchemaEnvironment.inferGenericFunctionOrType(
returnType,

View file

@ -108,6 +108,7 @@ DuplicatedExportInType/analyzerCode: Fail
DuplicatedExportInType/example: Fail
DuplicatedModifier/script1: Fail
DuplicatedName/example: Fail
DuplicatedNamedArgument/example: Fail
DuplicatedParameterName/example: Fail
Encoding/analyzerCode: Fail
EnumConstantSameNameAsEnclosing/example: Fail

View file

@ -2049,6 +2049,12 @@ DuplicatedNamePreviouslyUsedCause:
analyzerCode: REFERENCED_BEFORE_DECLARATION
severity: CONTEXT
DuplicatedNamedArgument:
template: "Duplicated named argument '#name'."
severity: ERROR
analyzerCode: DUPLICATE_NAMED_ARGUMENT
dart2jsCode: "*fatal*"
DuplicatedParameterName:
template: "Duplicated parameter name '#name'."
severity: ERROR

View file

@ -11,6 +11,6 @@ class C extends core::Object {
static method test() → void {
self::C::m(a: invalid-expression "pkg/front_end/testcases/duplicated_named_args_3.dart:13:19: Error: Duplicated named argument 'a'.
C.m(a: 1, a: 2, a: 3);
^" as{TypeError} core::int);
^");
}
static method main() → dynamic {}

View file

@ -11,6 +11,6 @@ class C extends core::Object {
static method test() → void {
self::C::m(a: invalid-expression "pkg/front_end/testcases/duplicated_named_args_3.dart:13:19: Error: Duplicated named argument 'a'.
C.m(a: 1, a: 2, a: 3);
^" as{TypeError} core::int);
^");
}
static method main() → dynamic {}

View file

@ -2613,7 +2613,7 @@ class Arguments extends TreeNode {
final List<DartType> types;
@coqsingle
final List<Expression> positional;
final List<NamedExpression> named;
List<NamedExpression> named;
Arguments(this.positional,
{List<DartType> types, List<NamedExpression> named})