19
19
*/
20
20
package org .sonar .java .checks ;
21
21
22
- import java .util .Arrays ;
23
22
import java .util .List ;
24
23
import javax .annotation .Nullable ;
25
24
import org .sonar .check .Rule ;
26
25
import org .sonarsource .analyzer .commons .collections .ListUtils ;
27
26
import org .sonar .plugins .java .api .IssuableSubscriptionVisitor ;
28
27
import org .sonar .plugins .java .api .semantic .MethodMatchers ;
29
28
import org .sonar .plugins .java .api .semantic .Symbol ;
29
+ import org .sonar .plugins .java .api .semantic .Symbol .TypeSymbol ;
30
30
import org .sonar .plugins .java .api .semantic .Type ;
31
+ import org .sonar .plugins .java .api .tree .BaseTreeVisitor ;
31
32
import org .sonar .plugins .java .api .tree .BlockTree ;
33
+ import org .sonar .plugins .java .api .tree .ClassTree ;
32
34
import org .sonar .plugins .java .api .tree .ExpressionStatementTree ;
33
35
import org .sonar .plugins .java .api .tree .IdentifierTree ;
36
+ import org .sonar .plugins .java .api .tree .LambdaExpressionTree ;
34
37
import org .sonar .plugins .java .api .tree .MemberSelectExpressionTree ;
35
38
import org .sonar .plugins .java .api .tree .MethodInvocationTree ;
36
39
import org .sonar .plugins .java .api .tree .MethodTree ;
@@ -47,76 +50,95 @@ public class ObjectFinalizeOverridenCallsSuperFinalizeCheck extends IssuableSubs
47
50
private static final MethodMatchers FINALIZE_MATCHER = MethodMatchers .create ()
48
51
.ofAnyType ().names (FINALIZE ).addWithoutParametersMatcher ().build ();
49
52
50
- private MethodInvocationTree lastStatementTree ;
51
-
52
53
@ Override
53
54
public List <Tree .Kind > nodesToVisit () {
54
- return Arrays . asList (Tree .Kind .METHOD , Tree . Kind . METHOD_INVOCATION );
55
+ return List . of (Tree .Kind .METHOD );
55
56
}
56
57
57
58
@ Override
58
59
public void visitNode (Tree tree ) {
59
- if (tree .is (Tree .Kind .METHOD_INVOCATION )) {
60
- MethodInvocationTree methodInvocationTree = (MethodInvocationTree ) tree ;
61
- if (methodInvocationTree .methodSelect ().is (Tree .Kind .MEMBER_SELECT )) {
62
- MemberSelectExpressionTree mset = (MemberSelectExpressionTree ) methodInvocationTree .methodSelect ();
63
- if (FINALIZE .equals (mset .identifier ().name ()) && mset .expression ().is (Tree .Kind .IDENTIFIER ) && "super" .equals (((IdentifierTree ) mset .expression ()).name ())) {
64
- lastStatementTree = methodInvocationTree ;
60
+
61
+ MethodTree methodTree = (MethodTree ) tree ;
62
+ if (isFinalizeOverriddenMethod (methodTree )) {
63
+ BlockTree blockTree = methodTree .block ();
64
+ if (blockTree != null ) {
65
+ MethodInvocationTree lastSuperFinalizeInvocation = findLastSuperFinalizeInvocation (blockTree );
66
+ if (lastSuperFinalizeInvocation == null ) {
67
+ reportIssue (methodTree .simpleName (), "Add a call to super.finalize() at the end of this Object.finalize() implementation." );
68
+ } else if (!isLastStatement (blockTree , lastSuperFinalizeInvocation )) {
69
+ reportIssue (lastSuperFinalizeInvocation , "Move this super.finalize() call to the end of this Object.finalize() implementation." );
65
70
}
66
71
}
67
- } else if (FINALIZE_MATCHER .matches ((MethodTree ) tree )) {
68
- lastStatementTree = null ;
69
72
}
70
73
}
71
74
72
- @ Override
73
- public void leaveNode ( Tree tree ) {
74
- if ( tree . is ( Tree . Kind . METHOD )) {
75
- MethodTree methodTree = ( MethodTree ) tree ;
76
- if ( FINALIZE_MATCHER . matches ( methodTree ) && doesOverrideFinalize (methodTree . symbol (). owner ()) ) {
77
- if ( lastStatementTree == null ) {
78
- reportIssue ( methodTree . simpleName (), "Add a call to super.finalize() at the end of this Object.finalize() implementation." );
79
- } else if (! isLastStatement ( methodTree , lastStatementTree )) {
80
- reportIssue ( lastStatementTree , "Move this super.finalize() call to the end of this Object.finalize() implementation." );
81
- }
75
+ private static boolean isFinalizeOverriddenMethod ( MethodTree methodTree ) {
76
+ return FINALIZE_MATCHER . matches ( methodTree ) && doesOverrideFinalize (( TypeSymbol ) methodTree . symbol (). owner ());
77
+ }
78
+
79
+ private static boolean doesOverrideFinalize (TypeSymbol typeSymbol ) {
80
+ Type superClassType = typeSymbol . superClass ();
81
+ while ( superClassType != null && ! superClassType . is ( "java.lang.Object" )) {
82
+ Symbol . TypeSymbol currentClass = superClassType . symbol ();
83
+ if ( currentClass . lookupSymbols ( FINALIZE ). stream (). anyMatch ( FINALIZE_MATCHER :: matches )) {
84
+ return true ;
82
85
}
86
+ superClassType = currentClass .superClass ();
83
87
}
88
+ return false ;
84
89
}
85
90
86
- private static boolean isLastStatement (MethodTree methodTree , MethodInvocationTree lastStatementTree ) {
87
- BlockTree blockTree = methodTree .block ();
88
- if (blockTree != null
89
- && blockTree .body ().stream ().anyMatch (statement ->
90
- statement .is (Tree .Kind .TRY_STATEMENT )&& isLastStatement (((TryStatementTree ) statement ).finallyBlock (), lastStatementTree ))) {
91
+ @ Nullable
92
+ private static MethodInvocationTree findLastSuperFinalizeInvocation (BlockTree blockTree ) {
93
+ FindLastSuperFinalizeInvocationVisitor visitor = new FindLastSuperFinalizeInvocationVisitor ();
94
+ blockTree .accept (visitor );
95
+ return visitor .lastSuperFinalizeInvocation ;
96
+ }
97
+
98
+ private static boolean isLastStatement (BlockTree blockTree , MethodInvocationTree lastStatementTree ) {
99
+ if (blockTree .body ().stream ().anyMatch (statement ->
100
+ statement .is (Tree .Kind .TRY_STATEMENT ) && isLastStatementInner (((TryStatementTree ) statement ).finallyBlock (), lastStatementTree )
101
+ )) {
91
102
return true ;
92
103
}
93
- return isLastStatement (blockTree , lastStatementTree );
104
+ return isLastStatementInner (blockTree , lastStatementTree );
94
105
}
95
106
96
- private static boolean isLastStatement (@ Nullable BlockTree blockTree , MethodInvocationTree lastStatementTree ) {
107
+ private static boolean isLastStatementInner (@ Nullable BlockTree blockTree , MethodInvocationTree lastStatementTree ) {
97
108
if (blockTree != null ) {
98
109
StatementTree last = ListUtils .getLast (blockTree .body ());
99
110
if (last .is (Tree .Kind .EXPRESSION_STATEMENT )) {
100
111
return lastStatementTree .equals (((ExpressionStatementTree ) last ).expression ());
101
112
} else if (last .is (Tree .Kind .TRY_STATEMENT )) {
102
- return isLastStatement (((TryStatementTree ) last ).finallyBlock (), lastStatementTree );
113
+ return isLastStatementInner (((TryStatementTree ) last ).finallyBlock (), lastStatementTree );
103
114
}
104
115
}
105
116
return false ;
106
117
}
107
118
108
- private static boolean doesOverrideFinalize (Symbol classSymbol ) {
109
- if (classSymbol .isTypeSymbol ()) {
110
- Type superClassType = ((Symbol .TypeSymbol ) classSymbol ).superClass ();
111
- while (superClassType != null && !superClassType .is ("java.lang.Object" )) {
112
- Symbol .TypeSymbol currentClass = superClassType .symbol ();
113
- if (currentClass .lookupSymbols (FINALIZE ).stream ().anyMatch (FINALIZE_MATCHER ::matches )) {
114
- return true ;
119
+ private static class FindLastSuperFinalizeInvocationVisitor extends BaseTreeVisitor {
120
+
121
+ @ Nullable
122
+ public MethodInvocationTree lastSuperFinalizeInvocation ;
123
+
124
+ @ Override
125
+ public void visitMethodInvocation (MethodInvocationTree tree ) {
126
+ if (tree .methodSelect ().is (Tree .Kind .MEMBER_SELECT )) {
127
+ MemberSelectExpressionTree mset = (MemberSelectExpressionTree ) tree .methodSelect ();
128
+ if (FINALIZE .equals (mset .identifier ().name ()) && mset .expression ().is (Tree .Kind .IDENTIFIER ) && "super" .equals (((IdentifierTree ) mset .expression ()).name ())) {
129
+ lastSuperFinalizeInvocation = tree ;
115
130
}
116
- superClassType = currentClass .superClass ();
117
131
}
118
132
}
119
- return false ;
120
- }
121
133
134
+ @ Override
135
+ public void visitClass (ClassTree tree ) {
136
+ // cut analysis on inner and anonymous classes, because we want to analyze actual control flow of finalize method only
137
+ }
138
+
139
+ @ Override
140
+ public void visitLambdaExpression (LambdaExpressionTree lambdaExpressionTree ) {
141
+ // cut analysis on lambda function bodies, because we want to analyze actual control flow of finalize method only
142
+ }
143
+ }
122
144
}
0 commit comments