Added check for uncommented main methods (debugging leftovers). (module UncommentedMain,request 732257)

This commit is contained in:
Oleg Sukhodolsky 2003-05-30 19:35:55 +00:00
parent e8ad93aea6
commit 1878e21451
7 changed files with 440 additions and 0 deletions

View File

@ -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">
&lt;module name=&quot;UncommentedMain&quot;/&gt;
</pre>
<p class="body">
To configure the check to allow main method for all classes with
&quot;Main&quot; name:
</p>
<pre class="body">
&lt;module name=&quot;UncommentedMain&quot;&gt;
&lt;property name=&quot;excludedClasses&quot; value=&quot;\.Main$&quot;/&gt;
&lt;/module&gt;
</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">'

View File

@ -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">

View File

@ -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">
* &lt;module name=&quot;UncommentedMain&quot;/&gt;
* </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;
}
}

View File

@ -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.

View File

@ -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()");
}
}

View File

@ -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));

View File

@ -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);
}
}