KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > puppycrawl > tools > checkstyle > checks > coding > UnnecessaryParenthesesCheck


1 ////////////////////////////////////////////////////////////////////////////////
2
// checkstyle: Checks Java source code for adherence to a set of rules.
3
// Copyright (C) 2001-2005 Oliver Burn
4
//
5
// This library is free software; you can redistribute it and/or
6
// modify it under the terms of the GNU Lesser General Public
7
// License as published by the Free Software Foundation; either
8
// version 2.1 of the License, or (at your option) any later version.
9
//
10
// This library is distributed in the hope that it will be useful,
11
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
// Lesser General Public License for more details.
14
//
15
// You should have received a copy of the GNU Lesser General Public
16
// License along with this library; if not, write to the Free Software
17
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
////////////////////////////////////////////////////////////////////////////////
19

20 package com.puppycrawl.tools.checkstyle.checks.coding;
21
22 import antlr.collections.AST;
23 import com.puppycrawl.tools.checkstyle.api.Check;
24 import com.puppycrawl.tools.checkstyle.api.DetailAST;
25 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
26
27 /**
28  * <p>
29  * Checks if unnecessary parentheses are used in a statement or expression.
30  * The check will flag the following with warnings:
31  * </p>
32  * <pre>
33  * return (x); // parens around identifier
34  * return (x + 1); // parens around return value
35  * int x = (y / 2 + 1); // parens around assignment rhs
36  * for (int i = (0); i &lt; 10; i++) { // parens around literal
37  * t -= (z + 1); // parens around assignment rhs</pre>
38  * <p>
39  * The check is not "type aware", that is to say, it can't tell if parentheses
40  * are unnecessary based on the types in an expression. It also doesn't know
41  * about operator precedence and associatvity; therefore it won't catch
42  * something like
43  * </p>
44  * <pre>
45  * int x = (a + b) + c;</pre>
46  * <p>
47  * In the above case, given that <em>a</em>, <em>b</em>, and <em>c</em> are
48  * all <code>int</code> variables, the parentheses around <code>a + b</code>
49  * are not needed.
50  * </p>
51  *
52  * @author Eric Roe
53  */

54 public class UnnecessaryParenthesesCheck extends Check
55 {
56     /** The minimum number of child nodes to consider for a match. */
57     private static final int MIN_CHILDREN_FOR_MATCH = 3;
58     /** The maximum string length before we chop the string. */
59     private static final int MAX_QUOTED_LENGTH = 25;
60
61     /** Token types for literals. */
62     private static final int [] LITERALS = {
63         TokenTypes.NUM_DOUBLE,
64         TokenTypes.NUM_FLOAT,
65         TokenTypes.NUM_INT,
66         TokenTypes.NUM_LONG,
67         TokenTypes.STRING_LITERAL,
68         TokenTypes.LITERAL_NULL,
69         TokenTypes.LITERAL_FALSE,
70         TokenTypes.LITERAL_TRUE,
71     };
72
73     /** Token types for assignment operations. */
74     private static final int [] ASSIGNMENTS = {
75         TokenTypes.ASSIGN,
76         TokenTypes.BAND_ASSIGN,
77         TokenTypes.BOR_ASSIGN,
78         TokenTypes.BSR_ASSIGN,
79         TokenTypes.BXOR_ASSIGN,
80         TokenTypes.DIV_ASSIGN,
81         TokenTypes.MINUS_ASSIGN,
82         TokenTypes.MOD_ASSIGN,
83         TokenTypes.PLUS_ASSIGN,
84         TokenTypes.SL_ASSIGN,
85         TokenTypes.SR_ASSIGN,
86         TokenTypes.STAR_ASSIGN,
87     };
88
89     /**
90      * Used to test if logging a warning in a parent node may be skipped
91      * because a warning was already logged on an immediate child node.
92      */

93     private DetailAST mParentToSkip;
94     /** Depth of nested assignments. Normally this will be 0 or 1. */
95     private int mAssignDepth;
96
97     /** {@inheritDoc} */
98     public int[] getDefaultTokens()
99     {
100         return new int [] {
101             TokenTypes.EXPR,
102             TokenTypes.IDENT,
103             TokenTypes.NUM_DOUBLE,
104             TokenTypes.NUM_FLOAT,
105             TokenTypes.NUM_INT,
106             TokenTypes.NUM_LONG,
107             TokenTypes.STRING_LITERAL,
108             TokenTypes.LITERAL_NULL,
109             TokenTypes.LITERAL_FALSE,
110             TokenTypes.LITERAL_TRUE,
111             TokenTypes.ASSIGN,
112             TokenTypes.BAND_ASSIGN,
113             TokenTypes.BOR_ASSIGN,
114             TokenTypes.BSR_ASSIGN,
115             TokenTypes.BXOR_ASSIGN,
116             TokenTypes.DIV_ASSIGN,
117             TokenTypes.MINUS_ASSIGN,
118             TokenTypes.MOD_ASSIGN,
119             TokenTypes.PLUS_ASSIGN,
120             TokenTypes.SL_ASSIGN,
121             TokenTypes.SR_ASSIGN,
122             TokenTypes.STAR_ASSIGN,
123         };
124     }
125
126     /** {@inheritDoc} */
127     public void visitToken(DetailAST aAST)
128     {
129         final int type = aAST.getType();
130         final boolean surrounded = isSurrounded(aAST);
131         final DetailAST parent = aAST.getParent();
132
133         if ((type == TokenTypes.ASSIGN)
134             && (parent.getType() == TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR))
135         {
136             // shouldn't process assign in annotation pairs
137
return;
138         }
139
140         // An identifier surrounded by parentheses.
141
if (surrounded && (type == TokenTypes.IDENT)) {
142             mParentToSkip = aAST.getParent();
143             log(aAST, "unnecessary.paren.ident", aAST.getText());
144             return;
145         }
146
147         // A literal (numeric or string) surrounded by parentheses.
148
if (surrounded && inTokenList(type, LITERALS)) {
149             mParentToSkip = aAST.getParent();
150             if (type == TokenTypes.STRING_LITERAL) {
151                 log(aAST, "unnecessary.paren.string",
152                     chopString(aAST.getText()));
153             }
154             else {
155                 log(aAST, "unnecessary.paren.literal", aAST.getText());
156             }
157             return;
158         }
159
160         // The rhs of an assignment surrounded by parentheses.
161
if (inTokenList(type, ASSIGNMENTS)) {
162             mAssignDepth++;
163             final DetailAST last = aAST.getLastChild();
164             if (last.getType() == TokenTypes.RPAREN) {
165                 log(aAST, "unnecessary.paren.assign");
166             }
167         }
168     }
169
170     /** {@inheritDoc} */
171     public void leaveToken(DetailAST aAST)
172     {
173         final int type = aAST.getType();
174         final DetailAST parent = aAST.getParent();
175
176         if ((type == TokenTypes.ASSIGN)
177             && (parent.getType() == TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR))
178         {
179             // shouldn't process assign in annotation pairs
180
return;
181         }
182
183         // An expression is surrounded by parentheses.
184
if (type == TokenTypes.EXPR) {
185
186             // If 'mParentToSkip' == 'aAST', then we've already logged a
187
// warning about an immediate child node in visitToken, so we don't
188
// need to log another one here.
189

190             if ((mParentToSkip != aAST) && exprSurrounded(aAST)) {
191                 if (mAssignDepth >= 1) {
192                     log(aAST, "unnecessary.paren.assign");
193                 }
194                 else if (aAST.getParent().getType()
195                     == TokenTypes.LITERAL_RETURN)
196                 {
197                     log(aAST, "unnecessary.paren.return");
198                 }
199                 else {
200                     log(aAST, "unnecessary.paren.expr");
201                 }
202             }
203
204             mParentToSkip = null;
205         }
206         else if (inTokenList(type, ASSIGNMENTS)) {
207             mAssignDepth--;
208         }
209
210         super.leaveToken(aAST);
211     }
212
213     /**
214      * Tests if the given <code>DetailAST</code> is surrounded by parentheses.
215      * In short, does <code>aAST</code> have a previous sibling whose type is
216      * <code>TokenTypes.LPAREN</code> and a next sibling whose type is <code>
217      * TokenTypes.RPAREN</code>.
218      * @param aAST the <code>DetailAST</code> to check if it is surrounded by
219      * parentheses.
220      * @return <code>true</code> if <code>aAST</code> is surrounded by
221      * parentheses.
222      */

223     private boolean isSurrounded(DetailAST aAST)
224     {
225         final DetailAST prev = aAST.getPreviousSibling();
226         final DetailAST next = (DetailAST) aAST.getNextSibling();
227
228         return (prev != null) && (prev.getType() == TokenTypes.LPAREN)
229             && (next != null) && (next.getType() == TokenTypes.RPAREN);
230     }
231
232     /**
233      * Tests if the given expression node is surrounded by parentheses.
234      * @param aAST a <code>DetailAST</code> whose type is
235      * <code>TokenTypes.EXPR</code>.
236      * @return <code>true</code> if the expression is surrounded by
237      * parentheses.
238      * @throws IllegalArgumentException if <code>aAST.getType()</code> is not
239      * equal to <code>TokenTypes.EXPR</code>.
240      */

241     private boolean exprSurrounded(DetailAST aAST)
242     {
243         if (aAST.getType() != TokenTypes.EXPR) {
244             throw new IllegalArgumentException JavaDoc("Not an expression node.");
245         }
246         boolean surrounded = false;
247         if (aAST.getChildCount() >= MIN_CHILDREN_FOR_MATCH) {
248             final AST n1 = aAST.getFirstChild();
249             final AST nn = aAST.getLastChild();
250
251             surrounded = (n1.getType() == TokenTypes.LPAREN)
252                 && (nn.getType() == TokenTypes.RPAREN);
253         }
254         return surrounded;
255     }
256
257     /**
258      * Check if the given token type can be found in an array of token types.
259      * @param aType the token type.
260      * @param aTokens an array of token types to search.
261      * @return <code>true</code> if <code>aType</code> was found in <code>
262      * aTokens</code>.
263      */

264     private boolean inTokenList(int aType, int [] aTokens)
265     {
266         // NOTE: Given the small size of the two arrays searched, I'm not sure
267
// it's worth bothering with doing a binary search or using a
268
// HashMap to do the searches.
269

270         boolean found = false;
271         for (int i = 0; (i < aTokens.length) && !found; i++) {
272             found = aTokens[i] == aType;
273         }
274         return found;
275     }
276
277     /**
278      * Returns the specified string chopped to <code>MAX_QUOTED_LENGTH</code>
279      * plus an ellipsis (...) if the length of the string exceeds <code>
280      * MAX_QUOTED_LENGTH</code>.
281      * @param aString the string to potentially chop.
282      * @return the chopped string if <code>aString</code> is longer than
283      * <code>MAX_QUOTED_LENGTH</code>; otherwise <code>aString</code>.
284      */

285     private String JavaDoc chopString(String JavaDoc aString)
286     {
287         if (aString.length() > MAX_QUOTED_LENGTH) {
288             return aString.substring(0, MAX_QUOTED_LENGTH) + "...\"";
289         }
290         return aString;
291     }
292 }
293
Popular Tags