Moving DescendantToken check to main distribution.
This commit is contained in:
parent
b3f2169626
commit
1d1ff53c20
|
|
@ -0,0 +1,371 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
// checkstyle: Checks Java source code for adherence to a set of rules.
|
||||
// Copyright (C) 2001-2003 Oliver Burn
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
package com.puppycrawl.tools.checkstyle.checks;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
import antlr.collections.AST;
|
||||
|
||||
import com.puppycrawl.tools.checkstyle.api.Check;
|
||||
import com.puppycrawl.tools.checkstyle.api.DetailAST;
|
||||
import com.puppycrawl.tools.checkstyle.api.TokenTypes;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Checks for restricted tokens beneath other tokens.
|
||||
* <p>
|
||||
* Examples of how to configure the check:
|
||||
* </p>
|
||||
* <pre>
|
||||
* <!-- String literal equality check -->
|
||||
* <module name="DescendantToken">
|
||||
* <property name="tokens" value="EQUAL,NOT_EQUAL"/>
|
||||
* <property name="limitedTokens" value="STRING_LITERAL"/>
|
||||
* <property name="maximumNumber" value="0"/>
|
||||
* </module>
|
||||
*
|
||||
* <!-- Switch with no default -->
|
||||
* <module name="DescendantToken">
|
||||
* <property name="tokens" value="LITERAL_SWITCH"/>
|
||||
* <property name="maximumDepth" value="2"/>
|
||||
* <property name="limitedTokens" value="LITERAL_DEFAULT"/>
|
||||
* <property name="minimumNumber" value="1"/>
|
||||
* </module>
|
||||
*
|
||||
* <!-- Assert statement may have side effects -->
|
||||
* <module name="DescendantToken">
|
||||
* <property name="tokens" value="LITERAL_ASSERT"/>
|
||||
* <property name="limitedTokens" value="ASSIGN,DEC,INC,POST_DEC,
|
||||
* POST_INC,PLUS_ASSIGN,MINUS_ASSIGN,STAR_ASSIGN,DIV_ASSIGN,MOD_ASSIGN,
|
||||
* BSR_ASSIGN,SR_ASSIGN,SL_ASSIGN,BAND_ASSIGN,BXOR_ASSIGN,BOR_ASSIGN,
|
||||
* METHOD_CALL"/>
|
||||
* <property name="maximumNumber" value="0"/>
|
||||
* </module>
|
||||
*
|
||||
* <!-- Initialiser in for performs no setup - use while instead? -->
|
||||
* <module name="DescendantToken">
|
||||
* <property name="tokens" value="FOR_INIT"/>
|
||||
* <property name="limitedTokens" value="EXPR"/>
|
||||
* <property name="minimumNumber" value="1"/>
|
||||
* </module>
|
||||
*
|
||||
* <!-- Condition in for performs no check -->
|
||||
* <module name="DescendantToken">
|
||||
* <property name="tokens" value="FOR_CONDITION"/>
|
||||
* <property name="limitedTokens" value="EXPR"/>
|
||||
* <property name="minimumNumber" value="1"/>
|
||||
* </module>
|
||||
*
|
||||
* <!-- Switch within switch -->
|
||||
* <module name="DescendantToken">
|
||||
* <property name="tokens" value="LITERAL_SWITCH"/>
|
||||
* <property name="limitedTokens" value="LITERAL_SWITCH"/>
|
||||
* <property name="maximumNumber" value="0"/>
|
||||
* <property name="minimumDepth" value="1"/>
|
||||
* </module>
|
||||
*
|
||||
* <!-- Return from within a catch or finally block -->
|
||||
* <module name="DescendantToken">
|
||||
* <property name="tokens" value="LITERAL_FINALLY,LITERAL_CATCH"/>
|
||||
* <property name="limitedTokens" value="LITERAL_RETURN"/>
|
||||
* <property name="maximumNumber" value="0"/>
|
||||
* </module>
|
||||
*
|
||||
* <!-- Try within catch or finally block -->
|
||||
* <module name="DescendantToken">
|
||||
* <property name="tokens" value="LITERAL_CATCH,LITERAL_FINALLY"/>
|
||||
* <property name="limitedTokens" value="LITERAL_TRY"/>
|
||||
* <property name="maximumNumber" value="0"/>
|
||||
* </module>
|
||||
*
|
||||
* <!-- Too many cases within a switch -->
|
||||
* <module name="DescendantToken">
|
||||
* <property name="tokens" value="LITERAL_SWITCH"/>
|
||||
* <property name="limitedTokens" value="LITERAL_CASE"/>
|
||||
* <property name="maximumDepth" value="2"/>
|
||||
* <property name="maximumNumber" value="10"/>
|
||||
* </module>
|
||||
*
|
||||
* <!-- Too many local variables within a method -->
|
||||
* <module name="DescendantToken">
|
||||
* <property name="tokens" value="METHOD_DEF"/>
|
||||
* <property name="limitedTokens" value="VARIABLE_DEF"/>
|
||||
* <property name="maximumDepth" value="2"/>
|
||||
* <property name="maximumNumber" value="10"/>
|
||||
* </module>
|
||||
*
|
||||
* <!-- Too many returns from within a method -->
|
||||
* <module name="DescendantToken">
|
||||
* <property name="tokens" value="METHOD_DEF"/>
|
||||
* <property name="limitedTokens" value="LITERAL_RETURN"/>
|
||||
* <property name="maximumNumber" value="3"/>
|
||||
* </module>
|
||||
*
|
||||
* <!-- Too many fields within an interface -->
|
||||
* <module name="DescendantToken">
|
||||
* <property name="tokens" value="INTERFACE_DEF"/>
|
||||
* <property name="limitedTokens" value="VARIABLE_DEF"/>
|
||||
* <property name="maximumDepth" value="2"/>
|
||||
* <property name="maximumNumber" value="0"/>
|
||||
* </module>
|
||||
*
|
||||
* <!-- Limit the number of exceptions a method can throw -->
|
||||
* <module name="DescendantToken">
|
||||
* <property name="tokens" value="LITERAL_THROWS"/>
|
||||
* <property name="limitedTokens" value="IDENT"/>
|
||||
* <property name="maximumNumber" value="1"/>
|
||||
* </module>
|
||||
*
|
||||
* <!-- Limit the number of expressions in a method -->
|
||||
* <module name="DescendantToken">
|
||||
* <property name="tokens" value="METHOD_DEF"/>
|
||||
* <property name="limitedTokens" value="EXPR"/>
|
||||
* <property name="maximumNumber" value="200"/>
|
||||
* </module>
|
||||
*
|
||||
* <!-- Disallow empty statements -->
|
||||
* <module name="DescendantToken">
|
||||
* <property name="tokens" value="EMPTY_STAT"/>
|
||||
* <property name="limitedTokens" value="EMPTY_STAT"/>
|
||||
* <property name="maximumNumber" value="0"/>
|
||||
* <property name="maximumDepth" value="0"/>
|
||||
* <property name="maximumMessage"
|
||||
* value="Empty statement is not allowed."/>
|
||||
* </module>
|
||||
*
|
||||
* <!-- Too many fields within a class -->
|
||||
* <module name="DescendantToken">
|
||||
* <property name="tokens" value="CLASS_DEF"/>
|
||||
* <property name="limitedTokens" value="VARIABLE_DEF"/>
|
||||
* <property name="maximumDepth" value="2"/>
|
||||
* <property name="maximumNumber" value="10"/>
|
||||
* </module>
|
||||
* </pre>
|
||||
* <p>
|
||||
* @author Tim Tyler <tim@tt1.org>
|
||||
* @author Rick Giles
|
||||
*/
|
||||
public class DescendantTokenCheck extends Check
|
||||
{
|
||||
/** minimum depth */
|
||||
private int mMinimumDepth = 0;
|
||||
|
||||
/** maximum depth */
|
||||
private int mMaximumDepth = Integer.MAX_VALUE;
|
||||
|
||||
/** minimum number */
|
||||
private int mMinimumNumber = 0;
|
||||
|
||||
/** maximum number */
|
||||
private int mMaximumNumber = Integer.MAX_VALUE;
|
||||
|
||||
/** limited tokens */
|
||||
private int[] mLimitedTokens = new int[0];
|
||||
|
||||
/** error message when minimum count not reached */
|
||||
private String mMinimumMessage = "descendant.token.min";
|
||||
|
||||
/** error message when maximum count exceeded */
|
||||
private String mMaximumMessage = "descendant.token.max";
|
||||
|
||||
/**
|
||||
* Counts of descendant tokens.
|
||||
* Indexed by (token ID - 1) for performance.
|
||||
*/
|
||||
private int[] mCounts = new int[0];
|
||||
|
||||
/** @see com.puppycrawl.tools.checkstyle.api.Check#getDefaultTokens() */
|
||||
public int[] getDefaultTokens()
|
||||
{
|
||||
return new int[0];
|
||||
}
|
||||
|
||||
/** @see com.puppycrawl.tools.checkstyle.api.Check */
|
||||
public void visitToken(DetailAST aAST)
|
||||
{
|
||||
//reset counts
|
||||
Arrays.fill(mCounts, 0);
|
||||
|
||||
countTokens(aAST, 0);
|
||||
|
||||
// name of this token
|
||||
final String name = TokenTypes.getTokenName(aAST.getType());
|
||||
|
||||
for (int i = 0; i < mLimitedTokens.length; i++) {
|
||||
final int tokenCount = mCounts[mLimitedTokens[i] - 1];
|
||||
if (tokenCount < mMinimumNumber) {
|
||||
final String descendantName =
|
||||
TokenTypes.getTokenName(mLimitedTokens[i]);
|
||||
log(
|
||||
aAST.getLineNo(),
|
||||
aAST.getColumnNo(),
|
||||
mMinimumMessage,
|
||||
new String[] {
|
||||
"" + tokenCount,
|
||||
"" + mMinimumNumber,
|
||||
name,
|
||||
descendantName,
|
||||
});
|
||||
}
|
||||
if (tokenCount > mMaximumNumber) {
|
||||
final String descendantName =
|
||||
TokenTypes.getTokenName(mLimitedTokens[i]);
|
||||
log(
|
||||
aAST.getLineNo(),
|
||||
aAST.getColumnNo(),
|
||||
mMaximumMessage,
|
||||
new String[] {
|
||||
"" + tokenCount,
|
||||
"" + mMaximumNumber,
|
||||
name,
|
||||
descendantName,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the number of occurrences of descendant tokens.
|
||||
* @param aAST the root token for descendants.
|
||||
* @param aDepth the maximum depth of the counted descendants.
|
||||
*/
|
||||
private void countTokens(AST aAST, int aDepth)
|
||||
{
|
||||
if (aDepth <= mMaximumDepth) {
|
||||
//update count
|
||||
if (aDepth >= mMinimumDepth) {
|
||||
final int type = aAST.getType();
|
||||
if (type <= mCounts.length) {
|
||||
mCounts[type - 1]++;
|
||||
}
|
||||
}
|
||||
AST child = aAST.getFirstChild();
|
||||
final int nextDepth = aDepth + 1;
|
||||
while (child != null) {
|
||||
countTokens(child, nextDepth);
|
||||
child = child.getNextSibling();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @see com.puppycrawl.tools.checkstyle.api.Check */
|
||||
public int[] getAcceptableTokens()
|
||||
{
|
||||
// Any tokens set by property 'tokens' are acceptable
|
||||
final Set tokenNames = getTokenNames();
|
||||
final int[] result = new int[tokenNames.size()];
|
||||
int i = 0;
|
||||
final Iterator it = tokenNames.iterator();
|
||||
while (it.hasNext()) {
|
||||
final String name = (String) it.next();
|
||||
result[i] = TokenTypes.getTokenId(name);
|
||||
i++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the tokens to ignore in the check.
|
||||
* @param aLimitedTokens - list of tokens to ignore.
|
||||
*/
|
||||
public void setLimitedTokens(String[] aLimitedTokens)
|
||||
{
|
||||
mLimitedTokens = new int[aLimitedTokens.length];
|
||||
|
||||
int maxToken = 0;
|
||||
for (int i = 0; i < aLimitedTokens.length; i++) {
|
||||
mLimitedTokens[i] = TokenTypes.getTokenId(aLimitedTokens[i]);
|
||||
if (mLimitedTokens[i] > maxToken) {
|
||||
maxToken = mLimitedTokens[i];
|
||||
}
|
||||
}
|
||||
mCounts = new int[maxToken];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the mimimum depth for descendant counts.
|
||||
* @param aMinimumDepth the mimimum depth for descendant counts.
|
||||
*/
|
||||
public void setMinimumDepth(int aMinimumDepth)
|
||||
{
|
||||
mMinimumDepth = aMinimumDepth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum depth for descendant counts.
|
||||
* @param aMaximumDepth the maximum depth for descendant counts.
|
||||
*/
|
||||
public void setMaximumDepth(int aMaximumDepth)
|
||||
{
|
||||
mMaximumDepth = aMaximumDepth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a minimum count for descendants.
|
||||
* @param aMinimumNumber the minimum count for descendants.
|
||||
*/
|
||||
public void setMinimumNumber(int aMinimumNumber)
|
||||
{
|
||||
mMinimumNumber = aMinimumNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a maximum count for descendants.
|
||||
* @param aMaximumNumber the maximum count for descendants.
|
||||
*/
|
||||
public void setMaximumNumber(int aMaximumNumber)
|
||||
{
|
||||
mMaximumNumber = aMaximumNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the error message for minimum count not reached.
|
||||
* @param aMessage the error message for minimum count not reached.
|
||||
* Used as a <code>MessageFormat</code> pattern with arguments
|
||||
* <ul>
|
||||
* <li>{0} - token count</li>
|
||||
* <li>{1} - minimum number</li>
|
||||
* <li>{2} - name of token</li>
|
||||
* <li>{3} - name of limited token</li>
|
||||
* </ul>
|
||||
*/
|
||||
public void setMinimumMessage(String aMessage)
|
||||
{
|
||||
mMinimumMessage = aMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the error message for maximum count exceeded.
|
||||
* @param aMessage the error message for maximum count exceeded.
|
||||
* Used as a <code>MessageFormat</code> pattern with arguments
|
||||
* <ul>
|
||||
* <li>{0} - token count</li>
|
||||
* <li>{1} - maximum number</li>
|
||||
* <li>{2} - name of token</li>
|
||||
* <li>{3} - name of limited token</li>
|
||||
* </ul>
|
||||
*/
|
||||
|
||||
public void setMaximumMessage(String aMessage)
|
||||
{
|
||||
mMaximumMessage = aMessage;
|
||||
}
|
||||
}
|
||||
|
|
@ -20,3 +20,6 @@ translation.missingKey=Key ''{0}'' missing.
|
|||
missing.switch.default=switch without \"default\" clause.
|
||||
|
||||
uncommented.main=Uncommented main method found.
|
||||
|
||||
descendant.token.min=Count of {0} for ''{2}'' descendant ''{3}'' is less than minimum count {1}.
|
||||
descendant.token.max=Count of {0} for ''{2}'' descendant ''{3}'' exceeds maximum count {1}.
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.puppycrawl.tools.checkstyle;
|
|||
import com.puppycrawl.tools.checkstyle.api.AbstractViolationReporterTest;
|
||||
import com.puppycrawl.tools.checkstyle.api.DetailASTTest;
|
||||
import com.puppycrawl.tools.checkstyle.checks.ArrayTypeStyleCheckTest;
|
||||
import com.puppycrawl.tools.checkstyle.checks.DescendantTokenCheckTest;
|
||||
import com.puppycrawl.tools.checkstyle.checks.FileSetCheckLifecycleTest;
|
||||
import com.puppycrawl.tools.checkstyle.checks.FinalParametersCheckTest;
|
||||
import com.puppycrawl.tools.checkstyle.checks.GenericIllegalRegexpCheckTest;
|
||||
|
|
@ -125,6 +126,7 @@ public class AllTests {
|
|||
suite.addTest(new TestSuite(ConstantNameCheckTest.class));
|
||||
suite.addTest(new TestSuite(CovariantEqualsCheckTest.class));
|
||||
suite.addTest(new TestSuite(CyclomaticComplexityCheckTest.class));
|
||||
suite.addTest(new TestSuite(DescendantTokenCheckTest.class));
|
||||
suite.addTest(new TestSuite(DesignForExtensionCheckTest.class));
|
||||
suite.addTest(new TestSuite(DoubleCheckedLockingCheckTest.class));
|
||||
suite.addTest(new TestSuite(EmptyBlockCheckTest.class));
|
||||
|
|
|
|||
|
|
@ -0,0 +1,86 @@
|
|||
package com.puppycrawl.tools.checkstyle.checks;
|
||||
|
||||
import com.puppycrawl.tools.checkstyle.BaseCheckTestCase;
|
||||
import com.puppycrawl.tools.checkstyle.DefaultConfiguration;
|
||||
|
||||
public class DescendantTokenCheckTest
|
||||
extends BaseCheckTestCase
|
||||
{
|
||||
public void testDefault()
|
||||
throws Exception
|
||||
{
|
||||
final DefaultConfiguration checkConfig =
|
||||
createCheckConfig(DescendantTokenCheck.class);
|
||||
final String[] expected = {};
|
||||
verify(checkConfig, getPath("InputIllegalTokens.java"), expected);
|
||||
}
|
||||
|
||||
public void testMaximumNumber()
|
||||
throws Exception
|
||||
{
|
||||
final DefaultConfiguration checkConfig =
|
||||
createCheckConfig(DescendantTokenCheck.class);
|
||||
checkConfig.addAttribute("tokens", "LITERAL_NATIVE");
|
||||
checkConfig.addAttribute("limitedTokens", "LITERAL_NATIVE");
|
||||
checkConfig.addAttribute("maximumNumber", "0");
|
||||
final String[] expected = {
|
||||
"20:12: Count of 1 for 'LITERAL_NATIVE' descendant 'LITERAL_NATIVE' exceeds maximum count 0.",
|
||||
};
|
||||
verify(checkConfig, getPath("InputIllegalTokens.java"), expected);
|
||||
}
|
||||
|
||||
public void testMessage()
|
||||
throws Exception
|
||||
{
|
||||
final DefaultConfiguration checkConfig =
|
||||
createCheckConfig(DescendantTokenCheck.class);
|
||||
checkConfig.addAttribute("tokens", "LITERAL_NATIVE");
|
||||
checkConfig.addAttribute("limitedTokens", "LITERAL_NATIVE");
|
||||
checkConfig.addAttribute("maximumNumber", "0");
|
||||
checkConfig.addAttribute("maximumMessage", "Using ''native'' is not allowed.");
|
||||
final String[] expected = {
|
||||
"20:12: Using 'native' is not allowed.",
|
||||
};
|
||||
verify(checkConfig, getPath("InputIllegalTokens.java"), expected);
|
||||
}
|
||||
|
||||
public void testMinimumNumber()
|
||||
throws Exception
|
||||
{
|
||||
final DefaultConfiguration checkConfig =
|
||||
createCheckConfig(DescendantTokenCheck.class);
|
||||
checkConfig.addAttribute("tokens", "LITERAL_SWITCH");
|
||||
checkConfig.addAttribute("limitedTokens", "LITERAL_DEFAULT");
|
||||
checkConfig.addAttribute("minimumNumber", "2");
|
||||
final String[] expected = {
|
||||
"11:9: Count of 1 for 'LITERAL_SWITCH' descendant 'LITERAL_DEFAULT' is less than minimum count 2.",
|
||||
};
|
||||
verify(checkConfig, getPath("InputIllegalTokens.java"), expected);
|
||||
}
|
||||
|
||||
public void testMinimumDepth()
|
||||
throws Exception
|
||||
{
|
||||
final DefaultConfiguration checkConfig =
|
||||
createCheckConfig(DescendantTokenCheck.class);
|
||||
checkConfig.addAttribute("tokens", "LITERAL_SWITCH");
|
||||
checkConfig.addAttribute("limitedTokens", "LITERAL_DEFAULT");
|
||||
checkConfig.addAttribute("maximumNumber", "0");
|
||||
checkConfig.addAttribute("minimumDepth", "3");
|
||||
final String[] expected = {};
|
||||
verify(checkConfig, getPath("InputIllegalTokens.java"), expected);
|
||||
}
|
||||
|
||||
public void testMaximumDepth()
|
||||
throws Exception
|
||||
{
|
||||
final DefaultConfiguration checkConfig =
|
||||
createCheckConfig(DescendantTokenCheck.class);
|
||||
checkConfig.addAttribute("tokens", "LITERAL_SWITCH");
|
||||
checkConfig.addAttribute("limitedTokens", "LITERAL_DEFAULT");
|
||||
checkConfig.addAttribute("maximumNumber", "0");
|
||||
checkConfig.addAttribute("maximumDepth", "1");
|
||||
final String[] expected = {};
|
||||
verify(checkConfig, getPath("InputIllegalTokens.java"), expected);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue