1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package com.puppycrawl.tools.checkstyle.checks.coding;
21
22 import java.util.ArrayDeque;
23 import java.util.Deque;
24 import java.util.regex.Pattern;
25
26 import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
27 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
28 import com.puppycrawl.tools.checkstyle.api.DetailAST;
29 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54 @FileStatefulCheck
55 public final class ReturnCountCheck extends AbstractCheck {
56
57
58
59
60
61 public static final String MSG_KEY = "return.count";
62
63
64
65
66 public static final String MSG_KEY_VOID = "return.countVoid";
67
68
69 private final Deque<Context> contextStack = new ArrayDeque<>();
70
71
72 private Pattern format = Pattern.compile("^equals$");
73
74
75 private int max = 2;
76
77 private int maxForVoid = 1;
78
79 private Context context;
80
81 @Override
82 public int[] getDefaultTokens() {
83 return new int[] {
84 TokenTypes.CTOR_DEF,
85 TokenTypes.METHOD_DEF,
86 TokenTypes.LAMBDA,
87 TokenTypes.LITERAL_RETURN,
88 };
89 }
90
91 @Override
92 public int[] getRequiredTokens() {
93 return new int[] {TokenTypes.LITERAL_RETURN};
94 }
95
96 @Override
97 public int[] getAcceptableTokens() {
98 return new int[] {
99 TokenTypes.CTOR_DEF,
100 TokenTypes.METHOD_DEF,
101 TokenTypes.LAMBDA,
102 TokenTypes.LITERAL_RETURN,
103 };
104 }
105
106
107
108
109
110 public void setFormat(Pattern pattern) {
111 format = pattern;
112 }
113
114
115
116
117
118 public void setMax(int max) {
119 this.max = max;
120 }
121
122
123
124
125
126 public void setMaxForVoid(int maxForVoid) {
127 this.maxForVoid = maxForVoid;
128 }
129
130 @Override
131 public void beginTree(DetailAST rootAST) {
132 context = new Context(false);
133 contextStack.clear();
134 }
135
136 @Override
137 public void visitToken(DetailAST ast) {
138 switch (ast.getType()) {
139 case TokenTypes.CTOR_DEF:
140 case TokenTypes.METHOD_DEF:
141 visitMethodDef(ast);
142 break;
143 case TokenTypes.LAMBDA:
144 visitLambda();
145 break;
146 case TokenTypes.LITERAL_RETURN:
147 visitReturn(ast);
148 break;
149 default:
150 throw new IllegalStateException(ast.toString());
151 }
152 }
153
154 @Override
155 public void leaveToken(DetailAST ast) {
156 switch (ast.getType()) {
157 case TokenTypes.CTOR_DEF:
158 case TokenTypes.METHOD_DEF:
159 case TokenTypes.LAMBDA:
160 leave(ast);
161 break;
162 case TokenTypes.LITERAL_RETURN:
163
164 break;
165 default:
166 throw new IllegalStateException(ast.toString());
167 }
168 }
169
170
171
172
173
174 private void visitMethodDef(DetailAST ast) {
175 contextStack.push(context);
176 final DetailAST methodNameAST = ast.findFirstToken(TokenTypes.IDENT);
177 final boolean check = !format.matcher(methodNameAST.getText()).find();
178 context = new Context(check);
179 }
180
181
182
183
184
185 private void leave(DetailAST ast) {
186 context.checkCount(ast);
187 context = contextStack.pop();
188 }
189
190
191
192
193 private void visitLambda() {
194 contextStack.push(context);
195 context = new Context(true);
196 }
197
198
199
200
201
202 private void visitReturn(DetailAST ast) {
203
204
205 if (ast.getFirstChild().getType() == TokenTypes.SEMI) {
206 context.visitLiteralReturn(maxForVoid, true);
207 }
208 else {
209 context.visitLiteralReturn(max, false);
210 }
211 }
212
213
214
215
216 private class Context {
217
218
219 private final boolean checking;
220
221 private int count;
222
223 private Integer maxAllowed;
224
225 private boolean isVoidContext;
226
227
228
229
230
231 Context(boolean checking) {
232 this.checking = checking;
233 }
234
235
236
237
238
239
240 public void visitLiteralReturn(int maxAssigned, Boolean voidReturn) {
241 isVoidContext = voidReturn;
242 if (maxAllowed == null) {
243 maxAllowed = maxAssigned;
244 }
245
246 ++count;
247 }
248
249
250
251
252
253
254 public void checkCount(DetailAST ast) {
255 if (checking && maxAllowed != null && count > maxAllowed) {
256 if (isVoidContext) {
257 log(ast, MSG_KEY_VOID, count, maxAllowed);
258 }
259 else {
260 log(ast, MSG_KEY, count, maxAllowed);
261 }
262 }
263 }
264
265 }
266
267 }