Added check for uncommented main methods (debugging leftovers). (module UncommentedMain,request 732257)
This commit is contained in:
parent
e8ad93aea6
commit
1878e21451
|
|
@ -34,6 +34,9 @@
|
|||
<li>
|
||||
<a href="#Translation">Translation</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#UncommentedMain">UncommentedMain</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#UpperEll">UpperEll</a>
|
||||
</li>
|
||||
|
|
@ -283,6 +286,57 @@
|
|||
<p class="body">
|
||||
<a href="config.html#checker">Checker</a>
|
||||
</p>
|
||||
<!-- --> <a name="UncommentedMain"></a> <h2>UncommentedMain</h2> <h4>Description</h4>
|
||||
<p class="body">
|
||||
Checks for uncommented main() methods (debugging leftovers).
|
||||
</p>
|
||||
<p class="body"> Rationale: main() method can be often used for
|
||||
debug puposes. Thus most of main() method should be
|
||||
removed/commented out of the sources.
|
||||
</p>
|
||||
<h4>Properties</h4>
|
||||
<table width="100%" border="1" cellpadding="5" class="body">
|
||||
<tr class="header">
|
||||
<th>name</th>
|
||||
<th>description</th>
|
||||
<th>type</th>
|
||||
<th>default value</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>excludedClasses</td>
|
||||
<td>pattern for qualified names of classes which ar allowed
|
||||
to have main method.</td>
|
||||
<td><a href="property_types.html#regexp">regular expression</a></td>
|
||||
<td><span class="default">^$</span></td>
|
||||
</tr>
|
||||
</table>
|
||||
<h4>Examples</h4>
|
||||
<p class="body">
|
||||
To configure the check:
|
||||
</p>
|
||||
<pre class="body">
|
||||
<module name="UncommentedMain"/>
|
||||
</pre>
|
||||
<p class="body">
|
||||
To configure the check to allow main method for all classes with
|
||||
"Main" name:
|
||||
</p>
|
||||
<pre class="body">
|
||||
<module name="UncommentedMain">
|
||||
<property name="excludedClasses" value="\.Main$"/>
|
||||
</module>
|
||||
</pre>
|
||||
<h4>Package</h4>
|
||||
<p class="body">
|
||||
com.puppycrawl.tools.checkstyle.checks
|
||||
|
||||
</p>
|
||||
<h4>Parent Module</h4>
|
||||
<p class="body">
|
||||
<a href="config.html#treewalker">TreeWalker</a>
|
||||
|
||||
</p>
|
||||
|
||||
<!-- --> <a name="UpperEll"></a> <h2>UpperEll</h2> <h4>Description</h4>
|
||||
<p class="body">
|
||||
Checks that long constants are defined with an upper ell. That is <span class="code">'
|
||||
|
|
|
|||
|
|
@ -65,6 +65,10 @@
|
|||
<li class="body">MagicNumberCheck now checks array initializers. (request
|
||||
745949)</li>
|
||||
|
||||
<li class="body">Added check for uncommented main
|
||||
methods (debugging leftovers). (module UncommentedMain,
|
||||
request 732257)</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<p class="body">
|
||||
|
|
|
|||
|
|
@ -0,0 +1,253 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
// checkstyle: Checks Java source code for adherence to a set of rules.
|
||||
// Copyright (C) 2001-2002 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 com.puppycrawl.tools.checkstyle.api.Check;
|
||||
import com.puppycrawl.tools.checkstyle.api.DetailAST;
|
||||
import com.puppycrawl.tools.checkstyle.api.FullIdent;
|
||||
import com.puppycrawl.tools.checkstyle.api.TokenTypes;
|
||||
import com.puppycrawl.tools.checkstyle.api.Utils;
|
||||
|
||||
import org.apache.commons.beanutils.ConversionException;
|
||||
import org.apache.regexp.RE;
|
||||
import org.apache.regexp.RESyntaxException;
|
||||
|
||||
/**
|
||||
* Detects uncommented main methods. Basically detects
|
||||
* any main method, since if it is detectable
|
||||
* that means it is uncommented.
|
||||
*
|
||||
* <pre class="body">
|
||||
* <module name="UncommentedMain"/>
|
||||
* </pre>
|
||||
*
|
||||
* @author Michael Yui
|
||||
* @author o_sukhodolsky
|
||||
*/
|
||||
public class UncommentedMainCheck
|
||||
extends Check
|
||||
{
|
||||
/** the pattern to exclude classes from the check */
|
||||
private String mExcludedClasses = "^$";
|
||||
/** compiled regexp to exclude classes from check */
|
||||
private RE mExcludedClassesRE = Utils.createRE(mExcludedClasses);
|
||||
/** current class name */
|
||||
private String mCurrentClass;
|
||||
/** current package */
|
||||
private FullIdent mPackage;
|
||||
/** class definition depth */
|
||||
private int mClassDepth;
|
||||
|
||||
/**
|
||||
* Set the excluded classes pattern.
|
||||
* @param aExcludedClasses a <code>String</code> value
|
||||
* @throws ConversionException unable to parse aExcludedClasses
|
||||
*/
|
||||
public void setExcludedClasses(String aExcludedClasses)
|
||||
throws ConversionException
|
||||
{
|
||||
try {
|
||||
mExcludedClasses = aExcludedClasses;
|
||||
mExcludedClassesRE = Utils.getRE(mExcludedClasses);
|
||||
}
|
||||
catch (RESyntaxException e) {
|
||||
throw new ConversionException("unable to parse "
|
||||
+ mExcludedClasses,
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
/** @see Check */
|
||||
public int[] getDefaultTokens()
|
||||
{
|
||||
return new int[] {
|
||||
TokenTypes.METHOD_DEF,
|
||||
TokenTypes.CLASS_DEF,
|
||||
TokenTypes.PACKAGE_DEF,
|
||||
};
|
||||
}
|
||||
|
||||
/** @see Check */
|
||||
public int[] getRequiredTokens()
|
||||
{
|
||||
return getDefaultTokens();
|
||||
}
|
||||
|
||||
/** @see Check */
|
||||
public void beginTree(DetailAST aRootAST)
|
||||
{
|
||||
mPackage = FullIdent.createFullIdent(null);
|
||||
mCurrentClass = null;
|
||||
mClassDepth = 0;
|
||||
}
|
||||
|
||||
/** @see Check */
|
||||
public void leaveToken(DetailAST aAst)
|
||||
{
|
||||
if (aAst.getType() == TokenTypes.CLASS_DEF) {
|
||||
if (mClassDepth == 1) {
|
||||
mCurrentClass = null;
|
||||
}
|
||||
mClassDepth--;
|
||||
}
|
||||
}
|
||||
|
||||
/** @see Check */
|
||||
public void visitToken(DetailAST aAst)
|
||||
{
|
||||
switch (aAst.getType()) {
|
||||
case TokenTypes.PACKAGE_DEF:
|
||||
visitPackageDef(aAst);
|
||||
break;
|
||||
case TokenTypes.CLASS_DEF:
|
||||
visitClassDef(aAst);
|
||||
break;
|
||||
case TokenTypes.METHOD_DEF:
|
||||
visitMethodDef(aAst);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException(aAst.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets current package.
|
||||
* @param aPackage node for package definition
|
||||
*/
|
||||
private void visitPackageDef(DetailAST aPackage)
|
||||
{
|
||||
mPackage =
|
||||
FullIdent.createFullIdent((DetailAST) aPackage.getFirstChild());
|
||||
}
|
||||
|
||||
/**
|
||||
* If not inner class then change current class name.
|
||||
* @param aClass node for class definition
|
||||
*/
|
||||
private void visitClassDef(DetailAST aClass)
|
||||
{
|
||||
// we are not use inner classes because they can not
|
||||
// have static methods
|
||||
if (mClassDepth == 0) {
|
||||
DetailAST ident = aClass.findFirstToken(TokenTypes.IDENT);
|
||||
mCurrentClass = mPackage.getText() + "." + ident.getText();
|
||||
mClassDepth++;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks method definition if this is
|
||||
* <code>public static void main(String[])</code>.
|
||||
* @param aMethod method definition node
|
||||
*/
|
||||
private void visitMethodDef(DetailAST aMethod)
|
||||
{
|
||||
if (mClassDepth != 1) {
|
||||
// method in inner class or in interface definition
|
||||
return;
|
||||
}
|
||||
|
||||
if (checkClassName()
|
||||
&& checkName(aMethod)
|
||||
&& checkModifiers(aMethod)
|
||||
&& checkType(aMethod)
|
||||
&& checkParams(aMethod))
|
||||
{
|
||||
log(aMethod.getLineNo(), "uncommented.main");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that current class is not excluded
|
||||
* @return true if check passed, false otherwise
|
||||
*/
|
||||
private boolean checkClassName()
|
||||
{
|
||||
return !mExcludedClassesRE.match(mCurrentClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that method name is @quot;main@quot;.
|
||||
* @param aMethod the METHOD_DEF node
|
||||
* @return true if check passed, false otherwise
|
||||
*/
|
||||
private boolean checkName(DetailAST aMethod)
|
||||
{
|
||||
DetailAST ident = aMethod.findFirstToken(TokenTypes.IDENT);
|
||||
return "main".equals(ident.getText());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that method has final and static modifiers.
|
||||
* @param aMethod the METHOD_DEF node
|
||||
* @return true if check passed, false otherwise
|
||||
*/
|
||||
private boolean checkModifiers(DetailAST aMethod)
|
||||
{
|
||||
DetailAST modifiers = aMethod.findFirstToken(TokenTypes.MODIFIERS);
|
||||
|
||||
return modifiers.branchContains(TokenTypes.LITERAL_PUBLIC)
|
||||
&& modifiers.branchContains(TokenTypes.LITERAL_STATIC);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that return type is <code>void</code>.
|
||||
* @param aMethod the METHOD_DEF node
|
||||
* @return true if check passed, false otherwise
|
||||
*/
|
||||
private boolean checkType(DetailAST aMethod)
|
||||
{
|
||||
DetailAST type =
|
||||
(DetailAST) aMethod.findFirstToken(TokenTypes.TYPE).getFirstChild();
|
||||
return type.getType() == TokenTypes.LITERAL_VOID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that method has only <code>String[]</code> param
|
||||
* @param aMethod the METHOD_DEF node
|
||||
* @return true if check passed, false otherwise
|
||||
*/
|
||||
private boolean checkParams(DetailAST aMethod)
|
||||
{
|
||||
DetailAST params = aMethod.findFirstToken(TokenTypes.PARAMETERS);
|
||||
if (params.getChildCount() != 1) {
|
||||
return false;
|
||||
}
|
||||
DetailAST paramType = ((DetailAST) params.getFirstChild())
|
||||
.findFirstToken(TokenTypes.TYPE);
|
||||
DetailAST arrayDecl =
|
||||
paramType.findFirstToken(TokenTypes.ARRAY_DECLARATOR);
|
||||
if (arrayDecl == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DetailAST arrayType = (DetailAST) arrayDecl.getFirstChild();
|
||||
|
||||
if (arrayType.getType() == TokenTypes.IDENT
|
||||
|| arrayType.getType() == TokenTypes.DOT)
|
||||
{
|
||||
FullIdent type = FullIdent.createFullIdent(arrayType);
|
||||
return ("String".equals(type.getText())
|
||||
|| "java.lang.String".equals(type.getText()));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -97,3 +97,5 @@ indentation.child.error={0} child at indentation level {1} not at correct indent
|
|||
interface.type=interfaces should describe a type and hence have methods.
|
||||
|
||||
array.trailing.comma=Array should contain trailing comma.
|
||||
|
||||
uncommented.main=Uncommented main method found.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,91 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Test case file for checkstyle.
|
||||
// Created: 2003
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
package com.puppycrawl.tools.checkstyle;
|
||||
|
||||
/**
|
||||
* Test case for UncommentedMainCheck
|
||||
* @author o_sukhodolsky
|
||||
*/
|
||||
public class InputUncommentedMain
|
||||
{
|
||||
// uncommented main
|
||||
public static void main(String[] args)
|
||||
{
|
||||
System.out.println("InputUncommentedMain.main()");
|
||||
}
|
||||
}
|
||||
|
||||
class Main
|
||||
{
|
||||
// uncommented main in class Main
|
||||
public static void main(String[] args)
|
||||
{
|
||||
System.out.println("Main.main()");
|
||||
}
|
||||
}
|
||||
|
||||
class test1
|
||||
{
|
||||
// one more uncommented main
|
||||
public static void main(java.lang.String[] args)
|
||||
{
|
||||
System.out.println("test1.main()");
|
||||
}
|
||||
}
|
||||
|
||||
class test2
|
||||
{
|
||||
// wrong arg type
|
||||
public static void main(int args)
|
||||
{
|
||||
System.out.println("test2.main()");
|
||||
}
|
||||
}
|
||||
|
||||
class test3
|
||||
{
|
||||
// no-public main
|
||||
static void main(String[] args)
|
||||
{
|
||||
System.out.println("test3.main()");
|
||||
}
|
||||
}
|
||||
|
||||
class test4
|
||||
{
|
||||
// non-static main
|
||||
public void main(String[] args)
|
||||
{
|
||||
System.out.println("test4.main()");
|
||||
}
|
||||
}
|
||||
|
||||
class test5
|
||||
{
|
||||
// wrong return type
|
||||
public static int main(String[] args)
|
||||
{
|
||||
System.out.println("test5.main()");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
class test6
|
||||
{
|
||||
// too many params
|
||||
public static void main(String[] args, int param)
|
||||
{
|
||||
System.out.println("test6.main()");
|
||||
}
|
||||
}
|
||||
|
||||
class test7
|
||||
{
|
||||
// main w/o params
|
||||
public static void main()
|
||||
{
|
||||
System.out.println("test7.main()");
|
||||
}
|
||||
}
|
||||
|
|
@ -56,6 +56,7 @@ import com.puppycrawl.tools.checkstyle.checks.TabCharacterCheckTest;
|
|||
import com.puppycrawl.tools.checkstyle.checks.TodoCommentCheckTest;
|
||||
import com.puppycrawl.tools.checkstyle.checks.TranslationCheckTest;
|
||||
import com.puppycrawl.tools.checkstyle.checks.TypeNameCheckTest;
|
||||
import com.puppycrawl.tools.checkstyle.checks.UncommentedMainCheckTest;
|
||||
import com.puppycrawl.tools.checkstyle.checks.UnusedImportsCheckTest;
|
||||
import com.puppycrawl.tools.checkstyle.checks.UpperEllCheckTest;
|
||||
import com.puppycrawl.tools.checkstyle.checks.VisibilityModifierCheckTest;
|
||||
|
|
@ -148,6 +149,7 @@ public class AllTests {
|
|||
suite.addTest(new TestSuite(TranslationCheckTest.class));
|
||||
suite.addTest(new TestSuite(LeftCurlyCheckTest.class));
|
||||
suite.addTest(new TestSuite(TypeNameCheckTest.class));
|
||||
suite.addTest(new TestSuite(UncommentedMainCheckTest.class));
|
||||
suite.addTest(new TestSuite(UnusedImportsCheckTest.class));
|
||||
suite.addTest(new TestSuite(UpperEllCheckTest.class));
|
||||
suite.addTest(new TestSuite(UtilsTest.class));
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
package com.puppycrawl.tools.checkstyle.checks;
|
||||
|
||||
import com.puppycrawl.tools.checkstyle.BaseCheckTestCase;
|
||||
import com.puppycrawl.tools.checkstyle.DefaultConfiguration;
|
||||
|
||||
public class UncommentedMainCheckTest
|
||||
extends BaseCheckTestCase
|
||||
{
|
||||
public void testDefaults()
|
||||
throws Exception
|
||||
{
|
||||
final DefaultConfiguration checkConfig =
|
||||
createCheckConfig(UncommentedMainCheck.class);
|
||||
final String[] expected = {
|
||||
"14: Uncommented main method found.",
|
||||
"23: Uncommented main method found.",
|
||||
"32: Uncommented main method found.",
|
||||
};
|
||||
verify(checkConfig, getPath("InputUncommentedMain.java"), expected);
|
||||
}
|
||||
|
||||
public void testExcludedClasses()
|
||||
throws Exception
|
||||
{
|
||||
final DefaultConfiguration checkConfig =
|
||||
createCheckConfig(UncommentedMainCheck.class);
|
||||
checkConfig.addAttribute("excludedClasses", "\\.Main$");
|
||||
final String[] expected = {
|
||||
"14: Uncommented main method found.",
|
||||
"32: Uncommented main method found.",
|
||||
};
|
||||
verify(checkConfig, getPath("InputUncommentedMain.java"), expected);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue