One more option for JavadocMethodCheck

(allowThrowsTagsForSubclasses, request 540383)
This commit is contained in:
Oleg Sukhodolsky 2003-05-09 18:14:25 +00:00
parent 205cbab8af
commit 4e1f8ef98e
6 changed files with 186 additions and 20 deletions

View File

@ -37,6 +37,7 @@
</module>
<module name="JavadocMethod">
<property name="allowUndeclaredRTE" value="true"/>
<property name="allowThrowsTagsForSubclasses" value="true"/>
</module>
<module name="JavadocVariable"/>

View File

@ -226,6 +226,13 @@
<td><a href="property_types.html#boolean">boolean</a></td>
<td><span class="default">false</span></td>
</tr>
<tr>
<td>allowThrowsTagsForSubclasses</td>
<td>whether to allow documented exceptions that
are subclass of one of declared exception.</td>
<td><a href="property_types.html#scope">boolean</a></td>
<td><span class="default">false</span></td>
</tr>
<tr>
<td>allowMissingParamTags</td>
<td>whether to ignore errors when a method has parameters

View File

@ -115,10 +115,13 @@
<li class="body">Added check to find empty statements
(module EmptyStatement, request 724573).</li>
<li class="body">Added check to find magic numbers
<li class="body">Added check to find magic numbers
(module MagicNumber, request 564206).</li>
<li class="body">One more option for JavadocMethodCheck
(allowThrowsTagsForSubclasses, request 540383)</li>
</ul>
<p class="body">

View File

@ -77,7 +77,8 @@ import org.apache.regexp.RE;
*
* @author <a href="mailto:checkstyle@puppycrawl.com">Oliver Burn</a>
* @author Rick Giles
* @version 1.0
* @author o_sukhodoslky
* @version 1.1
*/
public class JavadocMethodCheck
extends AbstractImportCheck
@ -151,6 +152,13 @@ public class JavadocMethodCheck
**/
private boolean mAllowUndeclaredRTE = false;
/**
* controls whether to allow documented exceptions that
* are subclass of one of declared exception.
* Defaults to false (backward compatibility).
**/
private boolean mAllowThrowsTagsForSubclasses = false;
/**
* controls whether to ignore errors when a method has parameters
* but does not have matching param tags in the javadoc.
@ -192,6 +200,16 @@ public class JavadocMethodCheck
mAllowUndeclaredRTE = aFlag;
}
/**
* controls whether to allow documented exception that
* are subclass of one of declared exceptions.
* @param aFlag a <code>Boolean</code> value
*/
public void setAllowThrowsTagsForSubclasses(boolean aFlag)
{
mAllowThrowsTagsForSubclasses = aFlag;
}
/**
* controls whether to allow a method which has parameters
* to omit matching param tags in the javadoc.
@ -234,7 +252,7 @@ public class JavadocMethodCheck
TokenTypes.METHOD_DEF,
TokenTypes.CTOR_DEF};
}
/** @see com.puppycrawl.tools.checkstyle.api.Check */
public int[] getAcceptableTokens()
{
@ -242,7 +260,7 @@ public class JavadocMethodCheck
TokenTypes.METHOD_DEF,
TokenTypes.CTOR_DEF};
}
/** @see com.puppycrawl.tools.checkstyle.api.Check */
public int[] getRequiredTokens()
{
@ -256,6 +274,7 @@ public class JavadocMethodCheck
{
mPackageFullIdent = FullIdent.createFullIdent(null);
mImports.clear();
mClassResolver = null;
}
/** @see com.puppycrawl.tools.checkstyle.api.Check */
@ -473,8 +492,9 @@ public class JavadocMethodCheck
if ((child.getType() == TokenTypes.IDENT)
|| (child.getType() == TokenTypes.DOT))
{
final FullIdent fi = FullIdent.createFullIdent(child);
retVal.add(fi);
final ExceptionInfo ei =
new ExceptionInfo(FullIdent.createFullIdent(child));
retVal.add(ei);
}
child = (DetailAST) child.getNextSibling();
}
@ -641,7 +661,7 @@ public class JavadocMethodCheck
private void checkThrowsTags(List aTags, List aThrows)
{
// Loop over the tags, checking to see they exist in the throws.
final Set foundThrows = new HashSet();
final Set foundThrows = new HashSet(); //used for performance only
final ListIterator tagIt = aTags.listIterator();
while (tagIt.hasNext()) {
final JavadocTag tag = (JavadocTag) tagIt.next();
@ -657,24 +677,43 @@ public class JavadocMethodCheck
boolean found = foundThrows.contains(documentedEx);
final ListIterator throwIt = aThrows.listIterator();
while (!found && throwIt.hasNext()) {
final FullIdent fi = (FullIdent) throwIt.next();
final ExceptionInfo ei = (ExceptionInfo) throwIt.next();
final FullIdent fi = ei.getFullIdent();
final String declaredEx = fi.getText();
if (isSameType(declaredEx, documentedEx)) {
found = true;
throwIt.remove();
ei.setFound();
foundThrows.add(documentedEx);
}
else if (mAllowThrowsTagsForSubclasses) {
final ClassResolver cr = getClassResolver();
try {
final Class documentedClass = cr.resolve(documentedEx);
try {
final Class declaredClass = cr.resolve(declaredEx);
found =
declaredClass.isAssignableFrom(documentedClass);
if (found) {
ei.setFound();
}
}
catch (ClassNotFoundException e) {
log(tag.getLineNo(), "javadoc.classInfo",
"@throws", declaredEx);
}
}
catch (ClassNotFoundException e) {
log(tag.getLineNo(), "javadoc.classInfo",
"@throws", documentedEx);
}
}
}
// Handle extra JavadocTag.
if (!found) {
boolean reqd = true;
if (mAllowUndeclaredRTE) {
final ClassResolver cr =
new ClassResolver(
getClassLoader(),
mPackageFullIdent.getText(),
mImports);
final ClassResolver cr = getClassResolver();
try {
final Class clazz = cr.resolve(tag.getArg1());
reqd =
@ -699,10 +738,68 @@ public class JavadocMethodCheck
if (!mAllowMissingThrowsTags) {
final ListIterator throwIt = aThrows.listIterator();
while (throwIt.hasNext()) {
final FullIdent fi = (FullIdent) throwIt.next();
log(fi.getLineNo(), fi.getColumnNo(),
"javadoc.expectedTag", "@throws", fi.getText());
final ExceptionInfo ei = (ExceptionInfo) throwIt.next();
if (!ei.isFound()) {
final FullIdent fi = ei.getFullIdent();
log(fi.getLineNo(), fi.getColumnNo(),
"javadoc.expectedTag", "@throws", fi.getText());
}
}
}
}
/** @return <code>ClassResolver</code> for current tree. */
final ClassResolver getClassResolver()
{
if (mClassResolver == null) {
mClassResolver = new ClassResolver(getClassLoader(),
mPackageFullIdent.getText(),
mImports);
}
return mClassResolver;
}
/** <code>ClassResolver</code> instance for current tree. */
private ClassResolver mClassResolver;
}
/**
* Stores useful information about declared exception.
* @author o_sukhodoslky
*/
final class ExceptionInfo
{
/** <code>FullIdent</code> of the exception. */
private final FullIdent mIdent;
/** does the exception have throws tag associated with. */
private boolean mFound;
/**
* Creates new instance for <code>FullIdent</code>.
* @param aIdent <code>FullIdent</code> of the exception
*/
ExceptionInfo(FullIdent aIdent)
{
mIdent = aIdent;
}
/** @return <code>FullIdent</code> of the exception. */
final FullIdent getFullIdent()
{
return mIdent;
}
/** Mark that the exception has associated throws tag */
final void setFound()
{
mFound = true;
}
/** @return whether the exception has throws tag associated with */
final boolean isFound()
{
return mFound;
}
}

View File

@ -217,4 +217,26 @@ class InputTags
{
return 579190;
}
/**
* Bug XXX, "two tags for the same exception"
*
* @exception java.io.IOException for some reasons
* @exception IOException for another reason
*/
void method21()
throws IOException
{
}
/**
* RFE 540383, "Unused throws tag for exception subclass"
*
* @exception IOException for some reasons
* @exception java.io.FileNotFoundException for another reasons
*/
void method22()
throws IOException
{
}
}

View File

@ -36,6 +36,7 @@ public class JavadocMethodCheckTest
"109:66: Expected @param tag for 'aFive'.",
"178: Unused @throws tag for 'ThreadDeath'.",
"179: Unused @throws tag for 'ArrayStoreException'.",
"236: Unused @throws tag for 'java.io.FileNotFoundException'.",
};
verify(checkConfig, getPath("InputTags.java"), expected);
@ -69,6 +70,7 @@ public class JavadocMethodCheckTest
"109:23: Expected @param tag for 'aOne'.",
"109:55: Expected @param tag for 'aFour'.",
"109:66: Expected @param tag for 'aFive'.",
"236: Unused @throws tag for 'java.io.FileNotFoundException'.",
};
verify(checkConfig, getPath("InputTags.java"), expected);
}
@ -163,7 +165,7 @@ public class JavadocMethodCheckTest
verify(checkConfig, getPath("InputScopeAnonInner.java"), expected);
}
public void testScopeAnonInnerWithResolver()
public void testScopeAnonInnerWithResolver()
throws Exception
{
final DefaultConfiguration checkConfig =
@ -173,4 +175,38 @@ public class JavadocMethodCheckTest
};
verify(checkConfig, getPath("InputScopeAnonInner.java"), expected);
}
public void testTagsWithSubclassesAllowed()
throws Exception
{
final DefaultConfiguration checkConfig =
createCheckConfig(JavadocMethodCheck.class);
checkConfig.addAttribute("allowThrowsTagsForSubclasses", "true");
final String[] expected = {
"14:5: Missing a Javadoc comment.",
"18: Unused @param tag for 'unused'.",
"24: Expected an @return tag.",
"33: Expected an @return tag.",
"40:16: Expected @throws tag for 'Exception'.",
"49:16: Expected @throws tag for 'Exception'.",
"53: Unable to get class information for @throws tag 'WrongException'.",
"53: Unused @throws tag for 'WrongException'.",
"55:16: Expected @throws tag for 'Exception'.",
"55:27: Expected @throws tag for 'NullPointerException'.",
"60:22: Expected @param tag for 'aOne'.",
"68:22: Expected @param tag for 'aOne'.",
"72: Unused @param tag for 'WrongParam'.",
"73:23: Expected @param tag for 'aOne'.",
"73:33: Expected @param tag for 'aTwo'.",
"78: Unused @param tag for 'Unneeded'.",
"79: Unused Javadoc tag.",
"87: Duplicate @return tag.",
"109:23: Expected @param tag for 'aOne'.",
"109:55: Expected @param tag for 'aFour'.",
"109:66: Expected @param tag for 'aFive'.",
"178: Unused @throws tag for 'ThreadDeath'.",
"179: Unused @throws tag for 'ArrayStoreException'.",
};
verify(checkConfig, getPath("InputTags.java"), expected);
}
}