Java tuning tutorial 4 - Tuning NPE.RET to detect additional issues (Advanced)
Java tuning tutorial 4 - Tuning NPE.RET to detect additional issues (Advanced)Note: Tuning existing checkers to detect additional issues (handling "false negatives") is considered an advanced tuning function.
Assertion methods from the junit library throw runtime exceptions if the assertion was wrong. Getting a runtime exception in the Java code is undesirable, and here we will use the case to demonstrate the introduction of the @Sink annotation into a Java knowledge base. Let's use the following snippet: import static junit.framework.Assert.assertNotNull; public class SinkSample { public static void main(final String[] args) { final SinkSample s = new SinkSample(); final Object o = s.get(); assertNotNull(o); } private Object get() { if (hashCode() < 0) { return new Object(); } return null; } } Method get() can return null, that will cause a runtime exception thrown by assertNotNull(o) call. We want to detect and prevent such situations using the NPE.RET checker. Create a .jkb file and add assertNotNull as the sinkIn the .jkb file, describe the assertNotNull as the sink: package junit.framework; class Assert { public static void assertNotNull(@Sink Object object); } Bind the Sink record to the checkerAfter you add @Sink, bind the data to the issue, using @Bind("NPE.RET"): package junit.framework;
Test the knowledge baseTo test your knowledge base:
When the code is analyzed using this knowledge base, Klocwork now detects the NPE.RET issue. Add assertNull as the sourceWhile assertNotNull can work as a null check for NPE.RET, assertNull can be treated as a null source, since the method will continue its execution only if the parameter of the call is null. See below: package com.klocwork.jdefects.checkers.dfa.binding_walkthrough; import static junit.framework.Assert.assertNull; public class SourceSample { private Object field; public void setField(Object field) { this.field = field; } public String toString() { StringBuilder sb = new StringBuilder(); assertNull(field); sb.append('['); sb.append(field.hashCode()); sb.append(']'); return sb.toString(); } } In the sample above, assertNull guarantees that field is null, however there is an attempt to dereference this null value at sb.append(field.hashCode()); In order to detect this situation we have to add @Source to the knowledge base and bind it to the checker: package junit.framework; @Bind("NPE.RET") class Assert { public static void assertNull(@Source Object object); } To test your knowledge base:
When you run kwcheck you will see that NPE.RET is detected. Add a Prop record to the knowledge baseThe junit framework has a set of methods for verifying the equality of two given objects. This means that if we know that if one object is null, then the other should be null after the method was executed. That means that NPE.RET should be reported for the following snippet: import static junit.framework.Assert.assertEquals; public class PropSample { private Object field; public void setField(Object field) { this.field = field; } public String toString() { final Object o = get(); assertEquals(field, o); return field.toString(); } private Object get() { if (hashCode() > 0) { return new Object(); } return null; } } NPE.RET is reported because get() can return null, and there is an assertion that the returned value equals to field, which is dereferenced at return field.toString();) Note: assertEquals is reflective, meaning that if any parameter is null, then another one is null as well. So we need to introduce two @Prop methods in the .jkb file with different with the @In and @Out annotations.
To the Java knowledge base file add: package junit.framework; class Assert { @Prop public static void assertEquals(@In Object objA, @Out Object objB); @Prop public static void assertEquals(@Out Object objA, @In Object objB); }
Test the knowledge baseTo test your knowledge base:
|