lahvacs

changeset 32:0e9ef7ef1706

The Suspicious call to Collection.remove hint has been merged into NetBeans proper.
author Jan Lahoda <jlahoda@netbeans.org>
date Wed May 06 23:26:27 2009 +0200 (3 years ago)
parents d1c05977e165
children 24fea1d42706
files javahints/src/info/lahoda/netbeans/javahints/Bundle.properties javahints/src/info/lahoda/netbeans/javahints/CollectionRemove.java javahints/src/info/lahoda/netbeans/javahints/layer.xml javahints/test/unit/src/info/lahoda/netbeans/javahints/Bundle_test.properties javahints/test/unit/src/info/lahoda/netbeans/javahints/CollectionRemoveTest.java
line diff
1.1 --- a/javahints/src/info/lahoda/netbeans/javahints/Bundle.properties Wed May 06 23:10:55 2009 +0200 1.2 +++ b/javahints/src/info/lahoda/netbeans/javahints/Bundle.properties Wed May 06 23:26:27 2009 +0200 1.3 @@ -1,3 +1,1 @@ 1.4 OpenIDE-Module-Name=Lahvac's Java Hints 1.5 -HINT_SuspiciousCall=Suspicious call to {0}:\nExpected type {2}, actual type {1} 1.6 -HINT_SuspiciousCallIncompatibleTypes=Suspicious call to {0}:\nGiven object cannot contain instances of {1} (expected {2})
2.1 --- a/javahints/src/info/lahoda/netbeans/javahints/CollectionRemove.java Wed May 06 23:10:55 2009 +0200 2.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 2.3 @@ -1,283 +0,0 @@ 2.4 -package info.lahoda.netbeans.javahints; 2.5 - 2.6 -import com.sun.source.tree.ExpressionTree; 2.7 -import com.sun.source.tree.MemberSelectTree; 2.8 -import com.sun.source.tree.MethodInvocationTree; 2.9 -import com.sun.source.tree.Scope; 2.10 -import com.sun.source.tree.Tree.Kind; 2.11 -import com.sun.source.util.TreePath; 2.12 -import java.util.Arrays; 2.13 -import java.util.Collection; 2.14 -import java.util.EnumSet; 2.15 -import java.util.HashMap; 2.16 -import java.util.Iterator; 2.17 -import java.util.LinkedList; 2.18 -import java.util.List; 2.19 -import java.util.Map; 2.20 -import java.util.Map.Entry; 2.21 -import java.util.Set; 2.22 -import java.util.concurrent.atomic.AtomicBoolean; 2.23 -import java.util.regex.Matcher; 2.24 -import java.util.regex.Pattern; 2.25 -import javax.lang.model.element.Element; 2.26 -import javax.lang.model.element.ElementKind; 2.27 -import javax.lang.model.element.ExecutableElement; 2.28 -import javax.lang.model.element.TypeElement; 2.29 -import javax.lang.model.element.VariableElement; 2.30 -import javax.lang.model.type.DeclaredType; 2.31 -import javax.lang.model.type.ExecutableType; 2.32 -import javax.lang.model.type.TypeMirror; 2.33 -import javax.lang.model.util.ElementFilter; 2.34 -import org.netbeans.api.java.source.CompilationInfo; 2.35 -import org.netbeans.modules.editor.java.Utilities; 2.36 -import org.netbeans.modules.java.hints.spi.AbstractHint; 2.37 -import org.netbeans.spi.editor.hints.ErrorDescription; 2.38 -import org.netbeans.spi.editor.hints.ErrorDescriptionFactory; 2.39 -import org.netbeans.spi.editor.hints.Severity; 2.40 -import org.openide.util.NbBundle; 2.41 - 2.42 -/** 2.43 - * 2.44 - * @author Jan Lahoda 2.45 - */ 2.46 -public class CollectionRemove extends AbstractHint { 2.47 - 2.48 - private final AtomicBoolean cancel = new AtomicBoolean(); 2.49 - 2.50 - public CollectionRemove() { 2.51 - super(true, true, HintSeverity.WARNING, "collection-remove"); 2.52 - } 2.53 - 2.54 - @Override 2.55 - public String getDescription() { 2.56 - return "Collection.remove"; 2.57 - } 2.58 - 2.59 - public Set<Kind> getTreeKinds() { 2.60 - return EnumSet.of(Kind.METHOD_INVOCATION); 2.61 - } 2.62 - 2.63 - public List<ErrorDescription> run(CompilationInfo info, TreePath tp) { 2.64 - cancel.set(false); 2.65 - MethodInvocationTree mit = (MethodInvocationTree) tp.getLeaf(); 2.66 - ExpressionTree select = mit.getMethodSelect(); 2.67 - TreePath method = new TreePath(tp, select); 2.68 - Element el = info.getTrees().getElement(method); 2.69 - 2.70 - if (el == null || el.getKind() != ElementKind.METHOD) 2.71 - return null; 2.72 - 2.73 - String simpleName = el.getSimpleName().toString(); 2.74 - ExecutableElement invoked = (ExecutableElement) el; 2.75 - TypeElement owner = (TypeElement) invoked.getEnclosingElement(); 2.76 - Collection<MappingDefinition> mds = CHECKED_METHODS_MAP.get(simpleName); 2.77 - 2.78 - if (mds == null) { 2.79 - return null; 2.80 - } 2.81 - 2.82 - List<ErrorDescription> result = new LinkedList<ErrorDescription>(); 2.83 - 2.84 - for (MappingDefinition md : mds) { 2.85 - if (cancel.get()) 2.86 - return null; 2.87 - 2.88 - ExecutableElement toCheck = resolveMethod(info, md.checkMethod); 2.89 - 2.90 - if (toCheck == null || (invoked != toCheck && !info.getElements().overrides(invoked, toCheck, owner))) { 2.91 - continue; 2.92 - } 2.93 - 2.94 - ExecutableElement toCheckAgainst = resolveMethod(info, md.checkAgainstMethod); 2.95 - 2.96 - if (toCheckAgainst == null) { 2.97 - continue; //XXX: log? 2.98 - } 2.99 - 2.100 - DeclaredType site; 2.101 - 2.102 - OUTER: switch (select.getKind()) { 2.103 - case MEMBER_SELECT: 2.104 - site = (DeclaredType) info.getTrees().getTypeMirror(new TreePath(method, ((MemberSelectTree) select).getExpression())); 2.105 - break; 2.106 - case IDENTIFIER: 2.107 - Scope s = info.getTrees().getScope(tp); 2.108 - 2.109 - while (s != null) { 2.110 - for (ExecutableElement ee : ElementFilter.methodsIn(info.getElements().getAllMembers(s.getEnclosingClass()))) { 2.111 - if (ee == toCheckAgainst || info.getElements().overrides(ee, toCheckAgainst, owner)) { 2.112 - site = (DeclaredType) s.getEnclosingClass().asType(); 2.113 - break OUTER; 2.114 - } 2.115 - } 2.116 - s = s.getEnclosingScope(); 2.117 - } 2.118 - 2.119 - site = null; 2.120 - break; 2.121 - default: 2.122 - throw new IllegalStateException(); 2.123 - } 2.124 - 2.125 - if (site == null) { 2.126 - continue; //XXX: log 2.127 - } 2.128 - 2.129 - ExecutableType againstType = (ExecutableType) info.getTypes().asMemberOf(site, toCheckAgainst); 2.130 - 2.131 - for (Entry<Integer,Integer> e : md.parametersMapping.entrySet()) { 2.132 - TypeMirror actualParam = info.getTrees().getTypeMirror(new TreePath(tp, mit.getArguments().get(e.getKey()))); 2.133 - TypeMirror designedType = againstType.getParameterTypes().get(e.getValue()); 2.134 - 2.135 - if (!info.getTypes().isSubtype(actualParam,designedType)) { 2.136 - String warningKey; 2.137 - 2.138 - if (compatibleTypes(info, actualParam,designedType)) { 2.139 - warningKey = "HINT_SuspiciousCall"; 2.140 - } else { 2.141 - warningKey = "HINT_SuspiciousCallIncompatibleTypes"; 2.142 - } 2.143 - 2.144 - String semiFQN = md.checkMethod.substring(0, md.checkMethod.indexOf('(')); 2.145 - 2.146 - String warning = NbBundle.getMessage(CollectionRemove.class, 2.147 - warningKey, 2.148 - semiFQN, 2.149 - Utilities.getTypeName(actualParam, false), 2.150 - Utilities.getTypeName(designedType, false)); 2.151 - 2.152 - int start = (int) info.getTrees().getSourcePositions().getStartPosition(info.getCompilationUnit(), mit); 2.153 - int end = (int) info.getTrees().getSourcePositions().getEndPosition(info.getCompilationUnit(), mit); 2.154 - 2.155 - result.add(ErrorDescriptionFactory.createErrorDescription(Severity.WARNING, warning, info.getFileObject(), start, end)); 2.156 - } 2.157 - } 2.158 - } 2.159 - 2.160 - return result; 2.161 - } 2.162 - 2.163 - private static boolean compatibleTypes(CompilationInfo info, TypeMirror type1, TypeMirror type2) { 2.164 - type1 = info.getTypes().erasure(type1); 2.165 - type2 = info.getTypes().erasure(type2); 2.166 - 2.167 - return info.getTypeUtilities().isCastable(type1, type2); 2.168 - } 2.169 - 2.170 - private static final Pattern SPLIT = Pattern.compile("(.+)\\.([^.]+)\\((.*)\\)"); 2.171 - 2.172 - private static ExecutableElement resolveMethod(CompilationInfo info, String name) { 2.173 - Matcher m = SPLIT.matcher(name); 2.174 - 2.175 - if (!m.matches()) { 2.176 - throw new IllegalArgumentException(); 2.177 - } 2.178 - 2.179 - String className = m.group(1); 2.180 - String methodName = m.group(2); 2.181 - String paramsSpec = m.group(3); 2.182 - 2.183 - TypeElement te = info.getElements().getTypeElement(className); 2.184 - 2.185 - if (te == null) { 2.186 - return null; 2.187 - } 2.188 - 2.189 - String[] paramList = paramsSpec.split(","); 2.190 - List<TypeMirror> params = new LinkedList<TypeMirror>(); 2.191 - 2.192 - TypeElement topLevel = info.getTopLevelElements().get(0); 2.193 - 2.194 - for (String t : paramList) { 2.195 - params.add(info.getTreeUtilities().parseType(t, topLevel)); 2.196 - } 2.197 - 2.198 - for (ExecutableElement ee : ElementFilter.methodsIn(te.getEnclosedElements())) { 2.199 - if (!methodName.equals(ee.getSimpleName().toString()) || ee.getParameters().size() != params.size()) { 2.200 - continue; 2.201 - } 2.202 - 2.203 - Iterator<TypeMirror> designed = params.iterator(); 2.204 - boolean found = true; 2.205 - 2.206 - for (VariableElement param : ee.getParameters()) { 2.207 - if (!info.getTypes().isSameType(info.getTypes().erasure(param.asType()), designed.next())) { 2.208 - found = false; 2.209 - break; 2.210 - } 2.211 - } 2.212 - 2.213 - if (found) { 2.214 - return ee; 2.215 - } 2.216 - } 2.217 - 2.218 - return null; 2.219 - } 2.220 - 2.221 - public String getId() { 2.222 - return CollectionRemove.class.getName(); 2.223 - } 2.224 - 2.225 - public String getDisplayName() { 2.226 - return "Collection.remove"; 2.227 - } 2.228 - 2.229 - public void cancel() { 2.230 - cancel.set(true); 2.231 - } 2.232 - 2.233 - private static final Iterable<MappingDefinition> MAPPINGS = Arrays.asList( 2.234 - new MappingDefinition("java.util.Collection.remove(java.lang.Object)", "java.util.Collection.add(java.lang.Object)", 0, 0), 2.235 - new MappingDefinition("java.util.Collection.contains(java.lang.Object)", "java.util.Collection.add(java.lang.Object)", 0, 0), 2.236 - new MappingDefinition("java.util.Map.containsKey(java.lang.Object)", "java.util.Map.put(java.lang.Object, java.lang.Object)", 0, 0), 2.237 - new MappingDefinition("java.util.Map.get(java.lang.Object)", "java.util.Map.put(java.lang.Object, java.lang.Object)", 0, 0), 2.238 - new MappingDefinition("java.util.Map.remove(java.lang.Object)", "java.util.Map.put(java.lang.Object, java.lang.Object)", 0, 0), 2.239 - new MappingDefinition("java.util.Map.containsValue(java.lang.Object)", "java.util.Map.put(java.lang.Object, java.lang.Object)", 0, 1) 2.240 - ); 2.241 - 2.242 - private static final Map<String, Collection<MappingDefinition>> CHECKED_METHODS_MAP; 2.243 - 2.244 - static { 2.245 - CHECKED_METHODS_MAP = new HashMap<String, Collection<MappingDefinition>>(); 2.246 - 2.247 - for (MappingDefinition d : MAPPINGS) { 2.248 - Matcher m = SPLIT.matcher(d.checkMethod); 2.249 - 2.250 - if (!m.matches()) { 2.251 - throw new IllegalArgumentException(); 2.252 - } 2.253 - 2.254 - String methodName = m.group(2); 2.255 - Collection<MappingDefinition> c = CHECKED_METHODS_MAP.get(methodName); 2.256 - 2.257 - if (c == null) { 2.258 - CHECKED_METHODS_MAP.put(methodName, c = new LinkedList<MappingDefinition>()); 2.259 - } 2.260 - 2.261 - c.add(d); 2.262 - } 2.263 - } 2.264 - 2.265 - private static final class MappingDefinition { 2.266 - 2.267 - private final String checkMethod; 2.268 - private final String checkAgainstMethod; 2.269 - private final Map<Integer, Integer> parametersMapping; 2.270 - 2.271 - public MappingDefinition(String checkMethod, String checkAgainstMethod, int... parametersMapping) { 2.272 - this.checkMethod = checkMethod; 2.273 - this.checkAgainstMethod = checkAgainstMethod; 2.274 - 2.275 - assert parametersMapping.length % 2 == 0; 2.276 - 2.277 - this.parametersMapping = new HashMap<Integer, Integer>(); 2.278 - 2.279 - for (int cntr = 0; cntr < parametersMapping.length; cntr += 2) { 2.280 - this.parametersMapping.put(parametersMapping[cntr], parametersMapping[cntr + 1]); 2.281 - } 2.282 - } 2.283 - 2.284 - } 2.285 - 2.286 -}
3.1 --- a/javahints/src/info/lahoda/netbeans/javahints/layer.xml Wed May 06 23:10:55 2009 +0200 3.2 +++ b/javahints/src/info/lahoda/netbeans/javahints/layer.xml Wed May 06 23:26:27 2009 +0200 3.3 @@ -5,7 +5,6 @@ 3.4 <folder name="rules"> 3.5 <folder name="hints"> 3.6 <folder name="experimental"> 3.7 - <file name="info-lahoda-netbeans-javahints-CollectionRemove.instance"/> 3.8 <file name="org-netbeans-modules-javahints-NPECheck.instance"/> 3.9 </folder> 3.10 </folder>
4.1 --- a/javahints/test/unit/src/info/lahoda/netbeans/javahints/Bundle_test.properties Wed May 06 23:10:55 2009 +0200 4.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 4.3 @@ -1,2 +0,0 @@ 4.4 -HINT_SuspiciousCall=SC: {0}, {1}, {2} 4.5 -HINT_SuspiciousCallIncompatibleTypes=SCIT: {0}, {1}, {2}
5.1 --- a/javahints/test/unit/src/info/lahoda/netbeans/javahints/CollectionRemoveTest.java Wed May 06 23:10:55 2009 +0200 5.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 5.3 @@ -1,136 +0,0 @@ 5.4 -package info.lahoda.netbeans.javahints; 5.5 - 5.6 -import com.sun.source.tree.Tree.Kind; 5.7 -import com.sun.source.util.TreePath; 5.8 -import java.util.List; 5.9 -import org.netbeans.api.java.source.CompilationInfo; 5.10 -import org.netbeans.modules.java.hints.infrastructure.TreeRuleTestBase; 5.11 -import org.netbeans.spi.editor.hints.ErrorDescription; 5.12 -import org.openide.util.NbBundle; 5.13 - 5.14 -/** 5.15 - * 5.16 - * @author Jan Lahoda 5.17 - */ 5.18 -public class CollectionRemoveTest extends TreeRuleTestBase { 5.19 - 5.20 - public CollectionRemoveTest(String name) { 5.21 - super(name); 5.22 - } 5.23 - 5.24 - public void testSimple1() throws Exception { 5.25 - performAnalysisTest("test/Test.java", 5.26 - "package test;" + 5.27 - "public class Test {" + 5.28 - " private void test () {" + 5.29 - " java.util.List<String> l = null;" + 5.30 - " l.rem|ove(new Object());" + 5.31 - " }" + 5.32 - "}", 5.33 - "0:106-0:128:warning:SC: java.util.Collection.remove, Object, String"); 5.34 - } 5.35 - 5.36 - public void testSimple2() throws Exception { 5.37 - performAnalysisTest("test/Test.java", 5.38 - "package test;" + 5.39 - "public class Test {" + 5.40 - " private void test () {" + 5.41 - " java.util.List<String> l = null;" + 5.42 - " l.con|tains(new Object());" + 5.43 - " }" + 5.44 - "}", 5.45 - "0:106-0:130:warning:SC: java.util.Collection.contains, Object, String"); 5.46 - } 5.47 - 5.48 - public void testSimple3() throws Exception { 5.49 - performAnalysisTest("test/Test.java", 5.50 - "package test;" + 5.51 - "public class Test {" + 5.52 - " private void test () {" + 5.53 - " java.util.List<String> l = null;" + 5.54 - " l.rem|ove(Integer.valueOf(1));" + 5.55 - " }" + 5.56 - "}", 5.57 - "0:106-0:134:warning:SCIT: java.util.Collection.remove, Integer, String"); 5.58 - } 5.59 - 5.60 - public void testSimple4() throws Exception { 5.61 - performAnalysisTest("test/Test.java", 5.62 - "package test;" + 5.63 - "public class Test {" + 5.64 - " private void test () {" + 5.65 - " java.util.List<String> l = null;" + 5.66 - " l.con|tains(Integer.valueOf(1));" + 5.67 - " }" + 5.68 - "}", 5.69 - "0:106-0:136:warning:SCIT: java.util.Collection.contains, Integer, String"); 5.70 - } 5.71 - 5.72 - public void testSimple5() throws Exception { 5.73 - performAnalysisTest("test/Test.java", 5.74 - "package test;" + 5.75 - "public class Test {" + 5.76 - " private void test () {" + 5.77 - " java.util.Map<String, Number> l = null;" + 5.78 - " l.con|tainsKey(Integer.valueOf(1));" + 5.79 - " }" + 5.80 - "}", 5.81 - "0:113-0:146:warning:SCIT: java.util.Map.containsKey, Integer, String"); 5.82 - } 5.83 - 5.84 - public void testSimple6() throws Exception { 5.85 - performAnalysisTest("test/Test.java", 5.86 - "package test;" + 5.87 - "public class Test {" + 5.88 - " private void test () {" + 5.89 - " java.util.Map<String, Number> l = null;" + 5.90 - " l.con|tainsValue(\"\");" + 5.91 - " }" + 5.92 - "}", 5.93 - "0:113-0:132:warning:SCIT: java.util.Map.containsValue, String, Number"); 5.94 - } 5.95 - 5.96 - public void testExtends1() throws Exception { 5.97 - performAnalysisTest("test/Test.java", 5.98 - "package test;" + 5.99 - "public class Test extends java.util.LinkedList<String> {" + 5.100 - " private void test () {" + 5.101 - " re|move(new Object());" + 5.102 - " }" + 5.103 - "}", 5.104 - "0:103-0:123:warning:SC: java.util.Collection.remove, Object, String"); 5.105 - } 5.106 - 5.107 - public void testExtends2() throws Exception { 5.108 - performAnalysisTest("test/Test.java", 5.109 - "package test;" + 5.110 - "public class Test extends java.util.LinkedList<String> {" + 5.111 - " private void test () {" + 5.112 - " new Runnable() {" + 5.113 - " public void run() {" + 5.114 - " re|move(new Object());" + 5.115 - " }" + 5.116 - " }" + 5.117 - " }" + 5.118 - "}", 5.119 - "0:166-0:186:warning:SC: java.util.Collection.remove, Object, String"); 5.120 - } 5.121 - 5.122 - @Override 5.123 - protected List<ErrorDescription> computeErrors(CompilationInfo info, TreePath tp) { 5.124 - while (tp != null) { 5.125 - if (tp.getLeaf().getKind() != Kind.METHOD_INVOCATION) { 5.126 - tp = tp.getParentPath(); 5.127 - continue; 5.128 - } 5.129 - return new CollectionRemove().run(info, tp); 5.130 - } 5.131 - 5.132 - return null; 5.133 - } 5.134 - 5.135 - static { 5.136 - NbBundle.setBranding("test"); 5.137 - } 5.138 - 5.139 -}