context.report(INVALID, call, location, message, null);
return;
}
Iterator<Expression> argIterator = args.iterator();
Expression first = argIterator.next();
Expression second = argIterator.hasNext() ? argIterator.next() : null;
boolean specifiesLocale;
TypeReference parameterType;
lombok.ast.Node resolved = context.parser.resolve(context, first);
if (resolved != null) {
parameterType = context.parser.getType(context, resolved);
} else {
parameterType = context.parser.getType(context, first);
}
if (parameterType != null) {
specifiesLocale = parameterType.getTypeName().equals("java.util.Locale"); //$NON-NLS-1$
} else if (!call.astName().astValue().equals(FORMAT_METHOD)) {
specifiesLocale = false;
} else {
// No type information with this AST; use string patterns instead to make
// an educated guess
String firstName = first.toString();
specifiesLocale = firstName.startsWith("Locale.") //$NON-NLS-1$
|| firstName.contains("locale") //$NON-NLS-1$
|| firstName.equals("null") //$NON-NLS-1$
|| (second != null && second.toString().contains("getString") //$NON-NLS-1$
&& !firstName.contains("getString") //$NON-NLS-1$
&& !firstName.contains(R_PREFIX)
&& !(first instanceof StringLiteral));
}
List<Pair<Handle, String>> list = mFormatStrings.get(name);
if (list != null) {
for (Pair<Handle, String> pair : list) {
String s = pair.getSecond();
int count = getFormatArgumentCount(s, null);
Handle handle = pair.getFirst();
if (count != args.size() - 1 - (specifiesLocale ? 1 : 0)) {
Location location = context.parser.getLocation(context, call);
Location secondary = handle.resolve();
secondary.setMessage(String.format("This definition requires %1$d arguments",
count));
location.setSecondary(secondary);
String message = String.format(
"Wrong argument count, format string %1$s requires %2$d but format " +
"call supplies %3$d",
name, count, args.size() - 1 - (specifiesLocale ? 1 : 0));
context.report(ARG_TYPES, method, location, message, null);
} else {
for (int i = 1; i <= count; i++) {
int argumentIndex = i + (specifiesLocale ? 1 : 0);
Class<?> type = tracker.getArgumentType(argumentIndex);
if (type != null) {
boolean valid = true;
String formatType = getFormatArgumentType(s, i);
char last = formatType.charAt(formatType.length() - 1);
if (formatType.length() >= 2 &&
Character.toLowerCase(
formatType.charAt(formatType.length() - 2)) == 't') {
// Date time conversion.
// TODO
continue;
}
switch (last) {
// Booleans. It's okay to pass objects to these;
// it will print "true" if non-null, but it's
// unusual and probably not intended.
case 'b':
case 'B':
valid = type == Boolean.TYPE;
break;
// Numeric: integer and floats in various formats
case 'x':
case 'X':
case 'd':
case 'o':
case 'e':
case 'E':
case 'f':
case 'g':
case 'G':
case 'a':
case 'A':
valid = type == Integer.TYPE
|| type == Float.TYPE;
break;
case 'c':
case 'C':
// Unicode character
valid = type == Character.TYPE;
break;
case 'h':
case 'H': // Hex print of hash code of objects
case 's':
case 'S':
// String. Can pass anything, but warn about
// numbers since you may have meant more
// specific formatting. Use special issue
// explanation for this?
valid = type != Boolean.TYPE &&
!type.isAssignableFrom(Number.class);
break;
}
if (!valid) {
IJavaParser parser = context.parser;
Expression argument = tracker.getArgument(argumentIndex);
Location location = parser.getLocation(context, argument);
Location secondary = handle.resolve();
secondary.setMessage("Conflicting argument declaration here");
location.setSecondary(secondary);