diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/bugs/FormatDecode.java b/plugins/InspectionGadgets/src/com/siyeh/ig/bugs/FormatDecode.java
index ec750545ffe5..bfbcdb8550f2 100644
--- a/plugins/InspectionGadgets/src/com/siyeh/ig/bugs/FormatDecode.java
+++ b/plugins/InspectionGadgets/src/com/siyeh/ig/bugs/FormatDecode.java
@@ -17,8 +17,12 @@ package com.siyeh.ig.bugs;
import com.intellij.psi.CommonClassNames;
import com.intellij.psi.PsiType;
+import com.intellij.util.containers.ContainerUtil;
import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -44,6 +48,27 @@ class FormatDecode{
private static final Validator FLOAT_VALIDATOR = new FloatValidator();
+ /**
+ * Holds information about validator replacement rules, i.e. allows to answer if validator of particular type may be
+ * safely replaced by validator of another particular type.
+ *
+ * For example, validator of type {@link AllValidator#type() 'all'} may be safely replaced by validator of any other
+ * type, e.g. {@link DateValidator#type() Date/Time} or {@link CharValidator#type() 'char validator'} may be replaced
+ * by {@link IntValidator#type() 'int validator'} because {@link Formatter java formatter} knows how to
+ * {@link Formatter.FormatSpecifier#printCharacter(Object) print character from integer} etc.
+ *
+ * Generally, current collection holds set of mappings where the key is type of validator that may be safely replaced
+ * by validator of type that is contained at 'values' collection.
+ */
+ private static final Map> REPLACEABLE_VALIDATOR_TYPES = new HashMap>();
+ static {
+ REPLACEABLE_VALIDATOR_TYPES.put(
+ ALL_VALIDATOR.type(),
+ ContainerUtil.set(DATE_VALIDATOR.type(), CHAR_VALIDATOR.type(), INT_VALIDATOR.type(), FLOAT_VALIDATOR.type())
+ );
+ REPLACEABLE_VALIDATOR_TYPES.put(CHAR_VALIDATOR.type(), ContainerUtil.set(INT_VALIDATOR.type()));
+ }
+
public static Validator[] decode(String formatString, int argumentCount){
final ArrayList parameters = new ArrayList();
@@ -108,11 +133,13 @@ class FormatDecode{
int argumentCount){
if(pos < parameters.size()){
final Validator old = parameters.get(pos);
+ Set replaceableTypes = REPLACEABLE_VALIDATOR_TYPES.get(old.type());
+ if (replaceableTypes != null && replaceableTypes.contains(val.type())) {
+ parameters.set(pos, val);
+ }
// it's OK to overwrite ALL with something more specific
// it's OK to ignore overwrite of something else with ALL or itself
- if (old == ALL_VALIDATOR) {
- parameters.set(pos, val);
- } else if (val != ALL_VALIDATOR && val != old) {
+ else if (val != ALL_VALIDATOR && val != old) {
throw new DuplicateFormatFlagsException(
"requires both " + old.type() + " and " + val.type());
}
diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/malformed_format_string/MalformedFormatString.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/malformed_format_string/MalformedFormatString.java
index d8b0e830f731..4835464100b7 100644
--- a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/malformed_format_string/MalformedFormatString.java
+++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/malformed_format_string/MalformedFormatString.java
@@ -21,6 +21,7 @@ public class MalformedFormatString {
String warn = String.format("%s %s", 1); // this is invalid according to the inspector (correct)
String invalid = String.format("%s %s" + local, 1); // this is valid according to the inspector (INCORRECT!)
String interesting = String.format("%s %s" + "hmm", 1); // this is invalid according to the inspector (correct)
+ String intAsChar = String.format("symbol '%1$c' (numeric value %1$d)", 60); // integer->char conversion is ok (correct)
}
public void outOfMemory() {