|
25 | 25 | import static com.google.errorprone.matchers.method.MethodMatchers.staticMethod;
|
26 | 26 | import static com.google.errorprone.util.ASTHelpers.constValue;
|
27 | 27 |
|
| 28 | +import com.google.common.base.CharMatcher; |
28 | 29 | import com.google.common.collect.ImmutableSet;
|
29 | 30 | import com.google.errorprone.BugPattern;
|
30 | 31 | import com.google.errorprone.VisitorState;
|
|
37 | 38 | import com.sun.source.tree.ExpressionTree;
|
38 | 39 | import com.sun.source.tree.MethodInvocationTree;
|
39 | 40 | import com.sun.source.tree.NewClassTree;
|
40 |
| -import java.util.Objects; |
41 | 41 | import java.util.function.Supplier;
|
42 | 42 |
|
43 | 43 | /** A {@link BugChecker}; see the associated {@link BugPattern} annotation for details. */
|
44 | 44 | @BugPattern(
|
45 | 45 | summary =
|
46 |
| - "Prefer InetAddress.getAllName to APIs that convert a hostname to a single IP address", |
| 46 | + "Prefer InetAddress.getAllByName to APIs that convert a hostname to a single IP address", |
47 | 47 | severity = WARNING)
|
48 | 48 | public final class AddressSelection extends BugChecker
|
49 | 49 | implements NewClassTreeMatcher, MethodInvocationTreeMatcher {
|
@@ -98,13 +98,36 @@ public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState
|
98 | 98 | private Description handleMatch(
|
99 | 99 | ExpressionTree argument, ExpressionTree replacement, Supplier<SuggestedFix> fix) {
|
100 | 100 | String value = constValue(argument, String.class);
|
101 |
| - if (Objects.equals(value, "localhost")) { |
| 101 | + // If it's a numeric loopback address, suggest using the method for that. |
| 102 | + if (LOOPBACK.contains(value)) { |
| 103 | + return describeMatch(replacement, fix.get()); |
| 104 | + } |
| 105 | + // If it isn't a constant, or it's "localhost", or it looks like a numeric IP address, then |
| 106 | + // we don't say anything. |
| 107 | + if (value == null || value.equals("localhost") || isNumericIp(value)) { |
102 | 108 | return NO_MATCH;
|
103 | 109 | }
|
104 |
| - Description.Builder description = buildDescription(replacement); |
105 |
| - if (LOOPBACK.contains(value)) { |
106 |
| - description.addFix(fix.get()); |
| 110 | + // Otherwise flag it but don't suggest a fix. |
| 111 | + return describeMatch(replacement); |
| 112 | + } |
| 113 | + |
| 114 | + /** |
| 115 | + * Returns true if this string looks like it might be a numeric IP address. The matching here is |
| 116 | + * very approximate. We want every numeric IP address to return true, but it's OK if some strings |
| 117 | + * return true even though they are not actually valid numeric IP addresses. Actually parsing |
| 118 | + * numeric IPv6 addresses in all their glory is more than we need here. |
| 119 | + */ |
| 120 | + private static boolean isNumericIp(String value) { |
| 121 | + if (value.isEmpty()) { |
| 122 | + return false; |
| 123 | + } |
| 124 | + if (value.contains(":")) { |
| 125 | + return true; // Every numeric IPv6 address contains a colon and no non-numeric hostname does. |
107 | 126 | }
|
108 |
| - return description.build(); |
| 127 | + return ASCII_DIGIT.matches(value.charAt(0)) |
| 128 | + && ASCII_DIGIT.matches(value.charAt(value.length() - 1)); |
| 129 | + // Every numeric IPv4 address begins and ends with an ASCII digit. |
109 | 130 | }
|
| 131 | + |
| 132 | + private static final CharMatcher ASCII_DIGIT = CharMatcher.inRange('0', '9'); |
110 | 133 | }
|
0 commit comments