2828import org .sonar .java .se .ConstraintManager ;
2929import org .sonar .java .se .ObjectConstraint ;
3030import org .sonar .java .se .ProgramState ;
31- import org .sonar .java .se .ProgramState .ConstrainedValue ;
3231import org .sonar .java .se .SymbolicValue ;
3332import org .sonar .java .se .SymbolicValueFactory ;
3433import org .sonar .plugins .java .api .JavaFileScanner ;
6766public class UnclosedResourcesCheck extends SECheck implements JavaFileScanner {
6867
6968 private enum Status {
70- OPENED , CLOSED ;
69+ OPENED , CLOSED
7170 }
7271
7372 private static final String JAVA_IO_AUTO_CLOSEABLE = "java.lang.AutoCloseable" ;
7473 private static final String JAVA_IO_CLOSEABLE = "java.io.Closeable" ;
74+ private static final String JAVA_SQL_STATEMENT = "java.sql.Statement" ;
75+ private static final String [] JDBC_RESOURCE_CREATIONS = {"java.sql.Connection" , JAVA_SQL_STATEMENT };
7576 private static final String [] IGNORED_CLOSEABLE_SUBTYPES = {
7677 "java.io.ByteArrayOutputStream" ,
7778 "java.io.ByteArrayInputStream" ,
@@ -81,10 +82,47 @@ private enum Status {
8182 "java.io.StringWriter" ,
8283 "com.sun.org.apache.xml.internal.security.utils.UnsyncByteArrayOutputStream"
8384 };
84- private static final String JAVA_SQL_STATEMENT = "java.sql.Statement" ;
85- private static final String [] JDBC_RESOURCE_CREATIONS = {"java.sql.Connection" , JAVA_SQL_STATEMENT };
8685
87- static boolean needsClosing (Type type ) {
86+ @ Override
87+ public void scanFile (JavaFileScannerContext context ) {
88+ Multimap <Tree , String > issues = ((DefaultJavaFileScannerContext ) context ).getSEIssues (UnclosedResourcesCheck .class );
89+ for (Map .Entry <Tree , String > issue : issues .entries ()) {
90+ context .reportIssue (this , issue .getKey (), issue .getValue ());
91+ }
92+ }
93+
94+ @ Override
95+ public ProgramState checkPreStatement (CheckerContext context , Tree syntaxNode ) {
96+ final PreStatementVisitor visitor = new PreStatementVisitor (context );
97+ syntaxNode .accept (visitor );
98+ return visitor .programState ;
99+ }
100+
101+ @ Override
102+ public ProgramState checkPostStatement (CheckerContext context , Tree syntaxNode ) {
103+ final PostStatementVisitor visitor = new PostStatementVisitor (context );
104+ syntaxNode .accept (visitor );
105+ return visitor .programState ;
106+ }
107+
108+ @ Override
109+ public void checkEndOfExecutionPath (CheckerContext context , ConstraintManager constraintManager ) {
110+ final List <ObjectConstraint > constraints = context .getState ().getFieldConstraints (Status .OPENED );
111+ for (ObjectConstraint constraint : constraints ) {
112+ Tree syntaxNode = constraint .syntaxNode ();
113+ String name = null ;
114+ if (syntaxNode .is (Tree .Kind .NEW_CLASS )) {
115+ name = ((NewClassTree ) syntaxNode ).identifier ().symbolType ().name ();
116+ } else if (syntaxNode .is (Tree .Kind .METHOD_INVOCATION )) {
117+ name = ((MethodInvocationTree ) syntaxNode ).symbolType ().name ();
118+ }
119+ if (name != null ) {
120+ context .reportIssue (syntaxNode , this , "Close this \" " + name + "\" ." );
121+ }
122+ }
123+ }
124+
125+ private static boolean needsClosing (Type type ) {
88126 for (String ignoredTypes : IGNORED_CLOSEABLE_SUBTYPES ) {
89127 if (type .is (ignoredTypes )) {
90128 return false ;
@@ -93,32 +131,28 @@ static boolean needsClosing(Type type) {
93131 return isCloseable (type );
94132 }
95133
96- static boolean isCloseable (Type type ) {
134+ private static boolean isCloseable (Type type ) {
97135 return type .isSubtypeOf (JAVA_IO_AUTO_CLOSEABLE ) || type .isSubtypeOf (JAVA_IO_CLOSEABLE );
98136 }
99137
100- static boolean isOpeningResource (NewClassTree syntaxNode ) {
138+ private static boolean isOpeningResource (NewClassTree syntaxNode ) {
101139 if (isWithinTryHeader (syntaxNode )) {
102140 return false ;
103141 }
104- final Type type = syntaxNode .symbolType ();
105- return needsClosing (type );
142+ return needsClosing (syntaxNode .symbolType ());
106143 }
107144
108- static boolean isWithinTryHeader (Tree syntaxNode ) {
145+ private static boolean isWithinTryHeader (Tree syntaxNode ) {
109146 final Tree parent = syntaxNode .parent ();
110147 if (parent .is (Tree .Kind .VARIABLE )) {
111148 return isTryStatementResource ((VariableTree ) parent );
112149 }
113150 return false ;
114151 }
115152
116- static boolean isTryStatementResource (VariableTree variable ) {
153+ private static boolean isTryStatementResource (VariableTree variable ) {
117154 final TryStatementTree tryStatement = getEnclosingTryStatement (variable );
118- if (tryStatement != null ) {
119- return tryStatement .resources ().contains (variable );
120- }
121- return false ;
155+ return tryStatement != null && tryStatement .resources ().contains (variable );
122156 }
123157
124158 private static TryStatementTree getEnclosingTryStatement (Tree syntaxNode ) {
@@ -132,48 +166,11 @@ private static TryStatementTree getEnclosingTryStatement(Tree syntaxNode) {
132166 return null ;
133167 }
134168
135- private static class PostStatementVisitor extends CheckerTreeNodeVisitor {
136-
137- public PostStatementVisitor (CheckerContext context ) {
138- super (context .getState ());
139- }
140-
141- @ Override
142- public void visitNewClass (NewClassTree syntaxNode ) {
143- if (isOpeningResource (syntaxNode )) {
144- final SymbolicValue instanceValue = programState .peekValue ();
145- if (!(instanceValue instanceof ResourceWrapperSymbolicValue )) {
146- programState = programState .addConstraint (instanceValue , new ObjectConstraint (false , false , syntaxNode , Status .OPENED ));
147- }
148- }
149- }
150-
151- @ Override
152- public void visitMethodInvocation (MethodInvocationTree syntaxNode ) {
153- if (syntaxNode .methodSelect ().is (Tree .Kind .MEMBER_SELECT ) && needsClosing (syntaxNode .symbolType ())) {
154- final ExpressionTree targetExpression = ((MemberSelectExpressionTree ) syntaxNode .methodSelect ()).expression ();
155- if (targetExpression .is (Tree .Kind .IDENTIFIER ) && !isWithinTryHeader (syntaxNode )
156- && (syntaxNode .symbol ().isStatic () || isJdbcResourceCreation (targetExpression ))) {
157- programState = programState .addConstraint (programState .peekValue (), new ObjectConstraint (false , false , syntaxNode , Status .OPENED ));
158- }
159- }
160- }
161-
162- private static boolean isJdbcResourceCreation (ExpressionTree targetExpression ) {
163- for (String creator : JDBC_RESOURCE_CREATIONS ) {
164- if (targetExpression .symbolType ().is (creator )) {
165- return true ;
166- }
167- }
168- return false ;
169- }
170- }
171-
172- public static class ResourceWrapperSymbolicValue extends SymbolicValue {
169+ private static class ResourceWrapperSymbolicValue extends SymbolicValue {
173170
174171 private final SymbolicValue dependent ;
175172
176- public ResourceWrapperSymbolicValue (int id , SymbolicValue dependent ) {
173+ ResourceWrapperSymbolicValue (int id , SymbolicValue dependent ) {
177174 super (id );
178175 this .dependent = dependent ;
179176 }
@@ -182,9 +179,10 @@ public ResourceWrapperSymbolicValue(int id, SymbolicValue dependent) {
182179 public SymbolicValue wrappedValue () {
183180 return dependent .wrappedValue ();
184181 }
182+
185183 }
186184
187- static class WrappedValueFactory implements SymbolicValueFactory {
185+ private static class WrappedValueFactory implements SymbolicValueFactory {
188186
189187 private final SymbolicValue value ;
190188
@@ -196,12 +194,13 @@ static class WrappedValueFactory implements SymbolicValueFactory {
196194 public SymbolicValue createSymbolicValue (int counter , Tree syntaxNode ) {
197195 return new ResourceWrapperSymbolicValue (counter , value );
198196 }
199- }
200197
201- static class PreStatementVisitor extends CheckerTreeNodeVisitor {
198+ }
202199
200+ private static class PreStatementVisitor extends CheckerTreeNodeVisitor {
203201 private static final String CLOSE_METHOD_NAME = "close" ;
204202 private static final String GET_MORE_RESULTS_METHOD_NAME = "getMoreResults" ;
203+
205204 private static final String GET_RESULT_SET = "getResultSet" ;
206205
207206 private final ConstraintManager constraintManager ;
@@ -298,12 +297,11 @@ private SymbolicValue getTargetValue(MethodInvocationTree syntaxNode) {
298297 }
299298
300299 private void closeResultSetsRelatedTo (SymbolicValue value ) {
301- final List <ConstrainedValue > constrainedValues = programState .getValuesWithConstraints (Status .OPENED );
302- for (ConstrainedValue constrainedValue : constrainedValues ) {
303- if (constrainedValue .value instanceof ResourceWrapperSymbolicValue ) {
304- ResourceWrapperSymbolicValue rValue = (ResourceWrapperSymbolicValue ) constrainedValue .value ;
300+ for (Map .Entry <SymbolicValue , ObjectConstraint > constrainedValue : programState .getValuesWithConstraints (Status .OPENED ).entrySet ()) {
301+ if (constrainedValue .getKey () instanceof ResourceWrapperSymbolicValue ) {
302+ ResourceWrapperSymbolicValue rValue = (ResourceWrapperSymbolicValue ) constrainedValue .getKey ();
305303 if (value .equals (rValue .dependent )) {
306- programState = programState .addConstraint (rValue , constrainedValue .constraint .withStatus (Status .CLOSED ));
304+ programState = programState .addConstraint (rValue , constrainedValue .getValue () .withStatus (Status .CLOSED ));
307305 }
308306 }
309307 }
@@ -338,55 +336,59 @@ private static ObjectConstraint openedConstraint(ProgramState programState, Symb
338336 return null ;
339337 }
340338
341- private static boolean isClosingResource (Symbol methodInvocationSymbol ) {
342- return methodInvocationSymbol . isMethodSymbol () && CLOSE_METHOD_NAME . equals ( methodInvocationSymbol . name () );
339+ private static boolean isClosingResource (Symbol symbol ) {
340+ return isMethodMatchingName ( symbol , CLOSE_METHOD_NAME );
343341 }
344342
345- private static boolean isClosingResultSets (Symbol methodInvocationSymbol ) {
346- return methodInvocationSymbol . isMethodSymbol () && GET_MORE_RESULTS_METHOD_NAME . equals ( methodInvocationSymbol . name () );
343+ private static boolean isClosingResultSets (Symbol symbol ) {
344+ return isMethodMatchingName ( symbol , GET_MORE_RESULTS_METHOD_NAME );
347345 }
348346
349- private static boolean isOpeningResultSet (Symbol methodInvocationSymbol ) {
350- return methodInvocationSymbol . isMethodSymbol () && GET_RESULT_SET . equals ( methodInvocationSymbol . name () );
347+ private static boolean isOpeningResultSet (Symbol symbol ) {
348+ return isMethodMatchingName ( symbol , GET_RESULT_SET );
351349 }
352- }
353350
354- @ Override
355- public void scanFile (JavaFileScannerContext context ) {
356- Multimap <Tree , String > issues = ((DefaultJavaFileScannerContext ) context ).getSEIssues (UnclosedResourcesCheck .class );
357- for (Map .Entry <Tree , String > issue : issues .entries ()) {
358- context .reportIssue (this , issue .getKey (), issue .getValue ());
351+ private static boolean isMethodMatchingName (Symbol symbol , String matchName ) {
352+ return symbol .isMethodSymbol () && matchName .equals (symbol .name ());
359353 }
360- }
361354
362- @ Override
363- public ProgramState checkPreStatement (CheckerContext context , Tree syntaxNode ) {
364- final PreStatementVisitor visitor = new PreStatementVisitor (context );
365- syntaxNode .accept (visitor );
366- return visitor .programState ;
367355 }
368356
369- @ Override
370- public ProgramState checkPostStatement (CheckerContext context , Tree syntaxNode ) {
371- final PostStatementVisitor visitor = new PostStatementVisitor (context );
372- syntaxNode .accept (visitor );
373- return visitor .programState ;
374- }
357+ private static class PostStatementVisitor extends CheckerTreeNodeVisitor {
375358
376- @ Override
377- public void checkEndOfExecutionPath (CheckerContext context , ConstraintManager constraintManager ) {
378- final List <ObjectConstraint > constraints = context .getState ().getFieldConstraints (Status .OPENED );
379- for (ObjectConstraint constraint : constraints ) {
380- Tree syntaxNode = constraint .syntaxNode ();
381- if (syntaxNode .is (Tree .Kind .NEW_CLASS )) {
382- context .reportIssue (syntaxNode , this , "Close this \" " + ((NewClassTree ) syntaxNode ).identifier () + "\" ." );
383- } else if (syntaxNode .is (Tree .Kind .METHOD_INVOCATION )) {
384- context .reportIssue (syntaxNode , this , "Close this \" " + toString ((MethodInvocationTree ) syntaxNode ) + "\" ." );
359+ PostStatementVisitor (CheckerContext context ) {
360+ super (context .getState ());
361+ }
362+
363+ @ Override
364+ public void visitNewClass (NewClassTree syntaxNode ) {
365+ if (isOpeningResource (syntaxNode )) {
366+ final SymbolicValue instanceValue = programState .peekValue ();
367+ if (!(instanceValue instanceof ResourceWrapperSymbolicValue )) {
368+ programState = programState .addConstraint (instanceValue , new ObjectConstraint (false , false , syntaxNode , Status .OPENED ));
369+ }
370+ }
371+ }
372+
373+ @ Override
374+ public void visitMethodInvocation (MethodInvocationTree syntaxNode ) {
375+ if (syntaxNode .methodSelect ().is (Tree .Kind .MEMBER_SELECT ) && needsClosing (syntaxNode .symbolType ())) {
376+ final ExpressionTree targetExpression = ((MemberSelectExpressionTree ) syntaxNode .methodSelect ()).expression ();
377+ if (targetExpression .is (Tree .Kind .IDENTIFIER ) && !isWithinTryHeader (syntaxNode )
378+ && (syntaxNode .symbol ().isStatic () || isJdbcResourceCreation (targetExpression ))) {
379+ programState = programState .addConstraint (programState .peekValue (), new ObjectConstraint (false , false , syntaxNode , Status .OPENED ));
380+ }
385381 }
386382 }
387- }
388383
389- private static String toString (MethodInvocationTree syntaxNode ) {
390- return syntaxNode .symbolType ().name ();
384+ private static boolean isJdbcResourceCreation (ExpressionTree targetExpression ) {
385+ for (String creator : JDBC_RESOURCE_CREATIONS ) {
386+ if (targetExpression .symbolType ().is (creator )) {
387+ return true ;
388+ }
389+ }
390+ return false ;
391+ }
391392 }
393+
392394}
0 commit comments