checkValidity method
Verify that this looks like a correct Intl.message/plural/gender/... invocation.
We expect an invocation like
outerName(x) => Intl.message("foo \$x", ...)
The node
parameter is the Intl.message invocation node in the AST,
arguments
is the list of arguments to that node (also reachable as
node.argumentList.arguments), outerName
is the name of the containing
function, e.g. "outerName" in this case and outerArgs
is the list of
arguments to that function. Of the optional parameters
nameAndArgsGenerated
indicates if we are generating names and arguments
while rewriting the code in the transformer or a development-time rewrite,
so we should not expect them to be present. The examplesRequired
parameter indicates if we will fail if parameter examples are not provided
for messages with parameters.
Implementation
String? checkValidity(MethodInvocation node, List arguments,
String? outerName, FormalParameterList outerArgs,
{bool nameAndArgsGenerated: false, bool examplesRequired: false}) {
// If we have parameters, we must specify args and name.
NamedExpression? args = arguments.firstWhereOrNull(
(each) => each is NamedExpression && each.name.label.name == 'args');
var parameterNames =
outerArgs.parameters.map((x) => x.name!.lexeme).toList();
var hasArgs = args != null;
var hasParameters = !outerArgs.parameters.isEmpty;
if (!nameAndArgsGenerated && !hasArgs && hasParameters) {
return "The 'args' argument for Intl.message must be specified for "
"messages with parameters. Consider using rewrite_intl_messages.dart";
}
if (!checkArgs(args, parameterNames)) {
return "The 'args' argument must match the message arguments,"
" e.g. args: ${parameterNames}";
}
var messageNameArgument = arguments.firstWhereOrNull((eachArg) =>
eachArg is NamedExpression && eachArg.name.label.name == 'name');
var nameExpression = messageNameArgument?.expression;
String? messageName;
String? givenName;
//TODO(alanknight): If we generalize this to messages with parameters
// this check will need to change.
if (nameExpression == null) {
if (!hasParameters) {
// No name supplied, no parameters. Use the message as the name.
messageName = _evaluateAsString(arguments[0]);
outerName = messageName;
} else {
// We have no name and parameters, but the transformer generates the
// name.
if (nameAndArgsGenerated) {
givenName = outerName;
messageName = givenName;
} else {
return "The 'name' argument for Intl.message must be supplied for "
"messages with parameters. Consider using "
"rewrite_intl_messages.dart";
}
}
} else {
// Name argument is supplied, use it.
givenName = _evaluateAsString(nameExpression);
messageName = givenName;
}
if (messageName == null) {
return "The 'name' argument for Intl.message must be a string literal";
}
var hasOuterName = outerName != null;
var simpleMatch = outerName == givenName || givenName == null;
var classPlusMethod = Message.classPlusMethodName(node, outerName);
var classMatch = classPlusMethod != null && (givenName == classPlusMethod);
if (!(hasOuterName && (simpleMatch || classMatch))) {
return "The 'name' argument for Intl.message must match either "
"the name of the containing function or <ClassName>_<methodName> ("
"was '$givenName' but must be '$outerName' or '$classPlusMethod')";
}
var simpleArguments = arguments.where((each) =>
each is NamedExpression &&
["desc", "name"].contains(each.name.label.name));
var values = simpleArguments.map((each) => each.expression).toList();
for (var arg in values) {
if (_evaluateAsString(arg) == null) {
return ("Intl.message arguments must be string literals: $arg");
}
}
if (hasParameters) {
var exampleArg = arguments.where((each) =>
each is NamedExpression && each.name.label.name == "examples");
var examples = exampleArg.map((each) => each.expression).toList();
if (examples.isEmpty && examplesRequired) {
return "Examples must be provided for messages with parameters";
}
if (examples.isNotEmpty) {
var example = examples.first;
var map = _evaluateAsMap(example);
if (map == null) {
return "Examples must be a const Map literal.";
}
if (example.constKeyword == null) {
return "Examples must be const.";
}
}
}
return null;
}