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 com.puppycrawl.tools.checkstyle.StatelessCheck;
23 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
24 import com.puppycrawl.tools.checkstyle.api.DetailAST;
25 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
26 import com.puppycrawl.tools.checkstyle.utils.CheckUtil;
27 import com.puppycrawl.tools.checkstyle.utils.ScopeUtil;
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48 @StatelessCheck
49 public class ExplicitInitializationCheck extends AbstractCheck {
50
51
52
53
54
55 public static final String MSG_KEY = "explicit.init";
56
57
58
59
60 private boolean onlyObjectReferences;
61
62 @Override
63 public final int[] getDefaultTokens() {
64 return getRequiredTokens();
65 }
66
67 @Override
68 public final int[] getRequiredTokens() {
69 return new int[] {TokenTypes.VARIABLE_DEF};
70 }
71
72 @Override
73 public final int[] getAcceptableTokens() {
74 return getRequiredTokens();
75 }
76
77
78
79
80
81
82
83
84
85 public void setOnlyObjectReferences(boolean onlyObjectReferences) {
86 this.onlyObjectReferences = onlyObjectReferences;
87 }
88
89 @Override
90 public void visitToken(DetailAST ast) {
91 if (!isSkipCase(ast)) {
92 final DetailAST assign = ast.findFirstToken(TokenTypes.ASSIGN);
93 final DetailAST exprStart =
94 assign.getFirstChild().getFirstChild();
95 if (exprStart.getType() == TokenTypes.LITERAL_NULL) {
96 final DetailAST ident = ast.findFirstToken(TokenTypes.IDENT);
97 log(ident, MSG_KEY, ident.getText(), "null");
98 }
99 if (!onlyObjectReferences) {
100 validateNonObjects(ast);
101 }
102 }
103 }
104
105
106
107
108
109
110 private void validateNonObjects(DetailAST ast) {
111 final DetailAST ident = ast.findFirstToken(TokenTypes.IDENT);
112 final DetailAST assign = ast.findFirstToken(TokenTypes.ASSIGN);
113 final DetailAST exprStart =
114 assign.getFirstChild().getFirstChild();
115 final DetailAST type = ast.findFirstToken(TokenTypes.TYPE);
116 final int primitiveType = type.getFirstChild().getType();
117 if (primitiveType == TokenTypes.LITERAL_BOOLEAN
118 && exprStart.getType() == TokenTypes.LITERAL_FALSE) {
119 log(ident, MSG_KEY, ident.getText(), "false");
120 }
121 if (isNumericType(primitiveType) && isZero(exprStart)) {
122 log(ident, MSG_KEY, ident.getText(), "0");
123 }
124 if (primitiveType == TokenTypes.LITERAL_CHAR
125 && isZeroChar(exprStart)) {
126 log(ident, MSG_KEY, ident.getText(), "\\0");
127 }
128 }
129
130
131
132
133
134
135
136 private static boolean isZeroChar(DetailAST exprStart) {
137 return isZero(exprStart)
138 || "'\\0'".equals(exprStart.getText());
139 }
140
141
142
143
144
145
146
147 private static boolean isSkipCase(DetailAST ast) {
148 boolean skipCase = true;
149
150
151
152 if (!ScopeUtil.isLocalVariableDef(ast)
153 && !ScopeUtil.isInInterfaceOrAnnotationBlock(ast)) {
154 final DetailAST assign = ast.findFirstToken(TokenTypes.ASSIGN);
155
156 if (assign != null) {
157 final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS);
158 skipCase = modifiers.findFirstToken(TokenTypes.FINAL) != null;
159 }
160 }
161 return skipCase;
162 }
163
164
165
166
167
168
169
170
171 private static boolean isNumericType(int type) {
172 return type == TokenTypes.LITERAL_BYTE
173 || type == TokenTypes.LITERAL_SHORT
174 || type == TokenTypes.LITERAL_INT
175 || type == TokenTypes.LITERAL_FLOAT
176 || type == TokenTypes.LITERAL_LONG
177 || type == TokenTypes.LITERAL_DOUBLE;
178 }
179
180
181
182
183
184
185
186 private static boolean isZero(DetailAST expr) {
187 final int type = expr.getType();
188 return switch (type) {
189 case TokenTypes.NUM_FLOAT, TokenTypes.NUM_DOUBLE, TokenTypes.NUM_INT,
190 TokenTypes.NUM_LONG -> {
191 final String text = expr.getText();
192 yield Double.compare(CheckUtil.parseDouble(text, type), 0.0) == 0;
193 }
194 default -> false;
195 };
196 }
197
198 }