diff --git a/build.xml b/build.xml
index b1d780e0e..f81981839 100644
--- a/build.xml
+++ b/build.xml
@@ -149,12 +149,13 @@
+ * A filter that uses nearby comments to suppress audit events.
+ *
+ * This check is philosophically similar to {@link SuppressionCommentFilter}.
+ * Unlike {@link SuppressionCommentFilter}, this filter does not require
+ * pairs of comments. This check may be used to suppress warnings in the
+ * current line:
+ *
+ * offendingLine(for, whatever, reason); // SUPPRESS ParameterNumberCheck
+ *
+ * or it may be configured to span multiple lines, either forward:
+ *
+ * // PERMIT MultipleVariableDeclarations NEXT 3 LINES
+ * double x1 = 1.0, y1 = 0.0, z1 = 0.0;
+ * double x2 = 0.0, y2 = 1.0, z2 = 0.0;
+ * double x3 = 0.0, y3 = 0.0, z3 = 1.0;
+ *
+ * or reverse:
+ *
+ * try {
+ * thirdPartyLibrary.method();
+ * } catch (RuntimeException e) {
+ * // ALLOW ILLEGAL CATCH BECAUSE third party API wraps everything
+ * // in RuntimeExceptions.
+ * ...
+ * }
+ *
+ *
+ * See {@link SuppressionCommentFilter} for usage notes. + *
+ * + * @author Mick Killianey + */ +public class SuppressWithNearbyCommentFilter + extends AutomaticBean + implements Filter +{ + /** + * A Tag holds a suppression comment and its location. + */ + public class Tag implements Comparable + { + /** The text of the tag. */ + private String mText; + + /** The first line where warnings may be suppressed. */ + private int mFirstLine; + + /** The last line where warnings may be suppressed. */ + private int mLastLine; + + /** The parsed check regexp, expanded for the text of this tag. */ + private Pattern mTagCheckRegexp; + + /** The parsed message regexp, expanded for the text of this tag. */ + private Pattern mTagMessageRegexp; + + /** + * Constructs a tag. + * @param aText the text of the suppression. + * @param aLine the line number. + * @throws ConversionException if unable to parse expanded aText. + * on. + */ + public Tag(String aText, int aLine) + throws ConversionException + { + mText = aText; + + mTagCheckRegexp = mCheckRegexp; + //Expand regexp for check and message + //Does not intern Patterns with Utils.getPattern() + String format = ""; + try { + format = expandFromComment(aText, mCheckFormat, mCommentRegexp); + mTagCheckRegexp = Pattern.compile(format); + if (mMessageFormat != null) { + format = expandFromComment( + aText, mMessageFormat, mCommentRegexp); + mTagMessageRegexp = Pattern.compile(format); + } + int influence = 0; + if (mInfluenceFormat != null) { + format = expandFromComment( + aText, mInfluenceFormat, mCommentRegexp); + try { + if (format.startsWith("+")) { + format = format.substring(1); + } + influence = Integer.parseInt(format); + } + catch (NumberFormatException e) { + throw new ConversionException( + "unable to parse influence from '" + aText + + "' using " + mInfluenceFormat, e); + } + } + if (influence >= 0) { + mFirstLine = aLine; + mLastLine = aLine + influence; + } + else { + mFirstLine = aLine + influence; + mLastLine = aLine; + } + } + catch (final PatternSyntaxException e) { + throw new ConversionException( + "unable to parse expanded comment " + format, + e); + } + } + + /** @return the text of the tag. */ + public String getText() + { + return mText; + } + + /** @return the line number of the first suppressed line. */ + public int getFirstLine() + { + return mFirstLine; + } + + /** @return the line number of the last suppressed line. */ + public int getLastLine() + { + return mLastLine; + } + + /** + * Compares the position of this tag in the file + * with the position of another tag. + * @param aObject the tag to compare with this one. + * @return a negative number if this tag is before the other tag, + * 0 if they are at the same position, and a positive number if this + * tag is after the other tag. + * @see java.lang.Comparable#compareTo(java.lang.Object) + */ + public int compareTo(Object aObject) + { + final Tag other = (Tag) aObject; + if (mFirstLine == other.mFirstLine) { + return mLastLine - other.mLastLine; + } + + return (mFirstLine - other.mFirstLine); + } + + /** + * Determines whether the source of an audit event + * matches the text of this tag. + * @param aEvent theAuditEvent to check.
+ * @return true if the source of aEvent matches the text of this tag.
+ */
+ public boolean isMatch(AuditEvent aEvent)
+ {
+ final int line = aEvent.getLine();
+ if (line < mFirstLine) {
+ return false;
+ }
+ if (line > mLastLine) {
+ return false;
+ }
+ final Matcher tagMatcher =
+ mTagCheckRegexp.matcher(aEvent.getSourceName());
+ if (tagMatcher.find()) {
+ return true;
+ }
+ if (mTagMessageRegexp != null) {
+ final Matcher messageMatcher =
+ mTagMessageRegexp.matcher(aEvent.getMessage());
+ return messageMatcher.find();
+ }
+ return false;
+ }
+
+ /**
+ * Expand based on a matching comment.
+ * @param aComment the comment.
+ * @param aString the string to expand.
+ * @param aRegexp the parsed expander.
+ * @return the expanded string
+ */
+ private String expandFromComment(
+ String aComment,
+ String aString,
+ Pattern aRegexp)
+ {
+ final Matcher matcher = aRegexp.matcher(aComment);
+ // Match primarily for effect.
+ if (!matcher.find()) {
+ ///CLOVER:OFF
+ return aString;
+ ///CLOVER:ON
+ }
+ String result = aString;
+ for (int i = 0; i <= matcher.groupCount(); i++) {
+ // $n expands comment match like in Pattern.subst().
+ result = result.replaceAll("\\$" + i, matcher.group(i));
+ }
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ public final String toString()
+ {
+ return "Tag[lines=[" + getFirstLine() + " to " + getLastLine()
+ + "]; text='" + getText() + "']";
+ }
+ }
+
+ /** Format to turns checkstyle reporting off. */
+ private static final String DEFAULT_COMMENT_FORMAT =
+ "SUPPRESS CHECKSTYLE (\\w+)";
+
+ /** Default regex for checks that should be suppressed. */
+ private static final String DEFAULT_CHECK_FORMAT = ".*";
+
+ /** Default regex for messages that should be suppressed. */
+ private static final String DEFAULT_MESSAGE_FORMAT = null;
+
+ /** Default regex for lines that should be suppressed. */
+ private static final String DEFAULT_INFLUENCE_FORMAT = "0";
+
+ /** Whether to look for trigger in C-style comments. */
+ private boolean mCheckC = true;
+
+ /** Whether to look for trigger in C++-style comments. */
+ private boolean mCheckCPP = true;
+
+ /** Parsed comment regexp that marks checkstyle suppression region. */
+ private Pattern mCommentRegexp;
+
+ /** The comment pattern that triggers suppression. */
+ private String mCheckFormat;
+
+ /** The parsed check regexp. */
+ private Pattern mCheckRegexp;
+
+ /** The message format to suppress. */
+ private String mMessageFormat;
+
+ /** The influence of the suppression comment. */
+ private String mInfluenceFormat;
+
+
+ //TODO: Investigate performance improvement with array
+ /** Tagged comments */
+ private final List mTags = new ArrayList();
+
+ /**
+ * References the current FileContents for this filter.
+ * Since this is a weak reference to the FileContents, the FileContents
+ * can be reclaimed as soon as the strong references in TreeWalker
+ * and FileContentsHolder are reassigned to the next FileContents,
+ * at which time filtering for the current FileContents is finished.
+ */
+ private WeakReference mFileContentsReference = new WeakReference(null);
+
+ /**
+ * Constructs a SuppressionCommentFilter.
+ * Initializes comment on, comment off, and check formats
+ * to defaults.
+ */
+ public SuppressWithNearbyCommentFilter()
+ {
+ if (DEFAULT_COMMENT_FORMAT != null) {
+ setCommentFormat(DEFAULT_COMMENT_FORMAT);
+ }
+ if (DEFAULT_CHECK_FORMAT != null) {
+ setCheckFormat(DEFAULT_CHECK_FORMAT);
+ }
+ if (DEFAULT_MESSAGE_FORMAT != null) {
+ setMessageFormat(DEFAULT_MESSAGE_FORMAT);
+ }
+ if (DEFAULT_INFLUENCE_FORMAT != null) {
+ setInfluenceFormat(DEFAULT_INFLUENCE_FORMAT);
+ }
+ }
+
+ /**
+ * Set the format for a comment that turns off reporting.
+ * @param aFormat a String value.
+ * @throws ConversionException unable to parse aFormat.
+ */
+ public void setCommentFormat(String aFormat)
+ throws ConversionException
+ {
+ try {
+ mCommentRegexp = Utils.getPattern(aFormat);
+ }
+ catch (final PatternSyntaxException e) {
+ throw new ConversionException("unable to parse " + aFormat, e);
+ }
+ }
+
+ /** @return the FileContents for this filter. */
+ public FileContents getFileContents()
+ {
+ return (FileContents) mFileContentsReference.get();
+ }
+
+ /**
+ * Set the FileContents for this filter.
+ * @param aFileContents the FileContents for this filter.
+ */
+ public void setFileContents(FileContents aFileContents)
+ {
+ mFileContentsReference = new WeakReference(aFileContents);
+ }
+
+ /**
+ * Set the format for a check.
+ * @param aFormat a String value
+ * @throws ConversionException unable to parse aFormat
+ */
+ public void setCheckFormat(String aFormat)
+ throws ConversionException
+ {
+ try {
+ mCheckRegexp = Utils.getPattern(aFormat);
+ mCheckFormat = aFormat;
+ }
+ catch (final PatternSyntaxException e) {
+ throw new ConversionException("unable to parse " + aFormat, e);
+ }
+ }
+
+ /**
+ * Set the format for a message.
+ * @param aFormat a String value
+ * @throws ConversionException unable to parse aFormat
+ */
+ public void setMessageFormat(String aFormat)
+ throws ConversionException
+ {
+ // check that aFormat parses
+ try {
+ Utils.getPattern(aFormat);
+ }
+ catch (final PatternSyntaxException e) {
+ throw new ConversionException("unable to parse " + aFormat, e);
+ }
+ mMessageFormat = aFormat;
+ }
+
+ /**
+ * Set the format for the influence of this check.
+ * @param aFormat a String value
+ * @throws ConversionException unable to parse aFormat
+ */
+ public void setInfluenceFormat(String aFormat)
+ throws ConversionException
+ {
+ // check that aFormat parses
+ try {
+ Utils.getPattern(aFormat);
+ }
+ catch (final PatternSyntaxException e) {
+ throw new ConversionException("unable to parse " + aFormat, e);
+ }
+ mInfluenceFormat = aFormat;
+ }
+
+
+ /**
+ * Set whether to look in C++ comments.
+ * @param aCheckCPP true if C++ comments are checked.
+ */
+ public void setCheckCPP(boolean aCheckCPP)
+ {
+ mCheckCPP = aCheckCPP;
+ }
+
+ /**
+ * Set whether to look in C comments.
+ * @param aCheckC true if C comments are checked.
+ */
+ public void setCheckC(boolean aCheckC)
+ {
+ mCheckC = aCheckC;
+ }
+
+ /** {@inheritDoc} */
+ public boolean accept(AuditEvent aEvent)
+ {
+ if (aEvent.getLocalizedMessage() == null) {
+ return true; // A special event.
+ }
+
+ // Lazy update. If the first event for the current file, update file
+ // contents and tag suppressions
+ final FileContents currentContents = FileContentsHolder.getContents();
+ if (currentContents == null) {
+ // we have no contents, so we can not filter.
+ // TODO: perhaps we should notify user somehow?
+ return true;
+ }
+ if (getFileContents() != currentContents) {
+ setFileContents(currentContents);
+ tagSuppressions();
+ }
+ for (final Iterator iter = mTags.iterator(); iter.hasNext();) {
+ final Tag tag = (Tag) iter.next();
+ if (tag.isMatch(aEvent)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Collects all the suppression tags for all comments into a list and
+ * sorts the list.
+ */
+ private void tagSuppressions()
+ {
+ mTags.clear();
+ final FileContents contents = getFileContents();
+ if (mCheckCPP) {
+ tagSuppressions(contents.getCppComments().values());
+ }
+ if (mCheckC) {
+ final Collection cComments = contents.getCComments().values();
+ final Iterator iter = cComments.iterator();
+ while (iter.hasNext()) {
+ final Collection element = (Collection) iter.next();
+ tagSuppressions(element);
+ }
+ }
+ Collections.sort(mTags);
+ }
+
+ /**
+ * Appends the suppressions in a collection of comments to the full
+ * set of suppression tags.
+ * @param aComments the set of comments.
+ */
+ private void tagSuppressions(Collection aComments)
+ {
+ for (final Iterator iter = aComments.iterator(); iter.hasNext();) {
+ final TextBlock comment = (TextBlock) iter.next();
+ final int startLineNo = comment.getStartLineNo();
+ final String[] text = comment.getText();
+ tagCommentLine(text[0], startLineNo, comment.getStartColNo());
+ for (int i = 1; i < text.length; i++) {
+ tagCommentLine(text[i], startLineNo + i, 0);
+ }
+ }
+ }
+
+ /**
+ * Tags a string if it matches the format for turning
+ * checkstyle reporting on or the format for turning reporting off.
+ * @param aText the string to tag.
+ * @param aLine the line number of aText.
+ * @param aColumn the column number of aText.
+ */
+ private void tagCommentLine(String aText, int aLine, int aColumn)
+ {
+ final Matcher matcher = mCommentRegexp.matcher(aText);
+ if (matcher.find()) {
+ addTag(matcher.group(0), aLine);
+ }
+ }
+
+ /**
+ * Adds a comment suppression Tag to the list of all tags.
+ * @param aText the text of the tag.
+ * @param aLine the line number of the tag.
+ */
+ private void addTag(String aText, int aLine)
+ {
+ final Tag tag = new Tag(aText, aLine);
+ mTags.add(tag);
+ }
+}
diff --git a/src/testinputs/com/puppycrawl/tools/checkstyle/filters/InputSuppressWithNearbyCommentFilter.java b/src/testinputs/com/puppycrawl/tools/checkstyle/filters/InputSuppressWithNearbyCommentFilter.java
new file mode 100644
index 000000000..42fcf6ffe
--- /dev/null
+++ b/src/testinputs/com/puppycrawl/tools/checkstyle/filters/InputSuppressWithNearbyCommentFilter.java
@@ -0,0 +1,77 @@
+////////////////////////////////////////////////////////////////////////////////
+// Test case file for checkstyle.
+////////////////////////////////////////////////////////////////////////////////
+
+package com.puppycrawl.tools.checkstyle.filters;
+
+/**
+ * Test input for using comments to suppress errors.
+ *
+ * @author Mick Killianey
+ */
+class InputSuppressWithNearbyCommentFilter
+{
+ private int A1; // SUPPRESS CHECKSTYLE MemberNameCheck
+ private int A2; /* SUPPRESS CHECKSTYLE MemberNameCheck */
+ /* SUPPRESS CHECKSTYLE MemberNameCheck */ private int A3;
+
+ private int B1; // SUPPRESS CHECKSTYLE MemberNameCheck
+ private int B2; /* SUPPRESS CHECKSTYLE MemberNameCheck */
+ /* SUPPRESS CHECKSTYLE MemberNameCheck */ private int B3;
+
+ private int C1;
+ // ALLOW MemberName ON NEXT LINE
+ private int C2;
+ private int C3;
+
+ private int D1;
+ private int D2;
+ // ALLOW MemberName ON PREVIOUS LINE
+ private int D3;
+
+ private static final int e1 = 0;
+ private int E2;
+ private int E3; // ALLOW ConstantName UNTIL THIS LINE+2
+ private static final int e4 = 0;
+ private int E5;
+ private static final int e6 = 0;
+ private int E7;
+ private int E8; /* ALLOW MemberName UNTIL THIS LINE-3 */
+ private static final int e9 = 0;
+
+ // ALLOW Unused UNTIL THIS LINE+5
+ public static void doit1(int aInt) // this is +1
+ {
+ }
+
+ public static void doit2(int aInt) // this is +5
+ {
+ }
+
+ public static void doit3(int aInt) // this is +9
+ {
+ }
+
+ public void doit4()
+ {
+ try {
+ // blah blah blah
+ for(int i = 0; i < 10; i++) {
+ // blah blah blah
+ while(true) {
+ try {
+ // blah blah blah
+ } catch(Exception e) {
+ // bad bad bad
+ } catch (Throwable t) {
+ // ALLOW CATCH Throwable BECAUSE I threw this together.
+ }
+ }
+ // blah blah blah
+ }
+ // blah blah blah
+ } catch(Exception ex) {
+ // ALLOW CATCH Exception BECAUSE I am an exceptional person.
+ }
+ }
+}
diff --git a/src/tests/com/puppycrawl/tools/checkstyle/filters/SuppressWithNearbyCommentFilterTest.java b/src/tests/com/puppycrawl/tools/checkstyle/filters/SuppressWithNearbyCommentFilterTest.java
new file mode 100644
index 000000000..7c756a5ec
--- /dev/null
+++ b/src/tests/com/puppycrawl/tools/checkstyle/filters/SuppressWithNearbyCommentFilterTest.java
@@ -0,0 +1,222 @@
+////////////////////////////////////////////////////////////////////////////////
+// Test case for checkstyle.
+////////////////////////////////////////////////////////////////////////////////
+
+package com.puppycrawl.tools.checkstyle.filters;
+
+import com.google.common.collect.Lists;
+import com.puppycrawl.tools.checkstyle.BaseCheckTestSupport;
+import com.puppycrawl.tools.checkstyle.Checker;
+import com.puppycrawl.tools.checkstyle.DefaultConfiguration;
+import com.puppycrawl.tools.checkstyle.TreeWalker;
+import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
+import com.puppycrawl.tools.checkstyle.api.Configuration;
+import com.puppycrawl.tools.checkstyle.checks.FileContentsHolder;
+import com.puppycrawl.tools.checkstyle.checks.coding.IllegalCatchCheck;
+import com.puppycrawl.tools.checkstyle.checks.naming.ConstantNameCheck;
+import com.puppycrawl.tools.checkstyle.checks.naming.MemberNameCheck;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Locale;
+import org.junit.Test;
+
+public class SuppressWithNearbyCommentFilterTest
+ extends BaseCheckTestSupport
+{
+ static String[] sAllMessages = {
+ "14:17: Name 'A1' must match pattern '^[a-z][a-zA-Z0-9]*$'.",
+ "15:17: Name 'A2' must match pattern '^[a-z][a-zA-Z0-9]*$'.",
+ "16:59: Name 'A3' must match pattern '^[a-z][a-zA-Z0-9]*$'.",
+
+ "18:17: Name 'B1' must match pattern '^[a-z][a-zA-Z0-9]*$'.",
+ "19:17: Name 'B2' must match pattern '^[a-z][a-zA-Z0-9]*$'.",
+ "20:59: Name 'B3' must match pattern '^[a-z][a-zA-Z0-9]*$'.",
+
+ "22:17: Name 'C1' must match pattern '^[a-z][a-zA-Z0-9]*$'.",
+ "24:17: Name 'C2' must match pattern '^[a-z][a-zA-Z0-9]*$'.",
+ "25:17: Name 'C3' must match pattern '^[a-z][a-zA-Z0-9]*$'.",
+
+ "27:17: Name 'D1' must match pattern '^[a-z][a-zA-Z0-9]*$'.",
+ "28:17: Name 'D2' must match pattern '^[a-z][a-zA-Z0-9]*$'.",
+ "30:17: Name 'D3' must match pattern '^[a-z][a-zA-Z0-9]*$'.",
+
+ "32:30: Name 'e1' must match pattern '^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$'.",
+ "33:17: Name 'E2' must match pattern '^[a-z][a-zA-Z0-9]*$'.",
+ "34:17: Name 'E3' must match pattern '^[a-z][a-zA-Z0-9]*$'.",
+ "35:30: Name 'e4' must match pattern '^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$'.",
+ "36:17: Name 'E5' must match pattern '^[a-z][a-zA-Z0-9]*$'.",
+ "37:30: Name 'e6' must match pattern '^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$'.",
+ "38:17: Name 'E7' must match pattern '^[a-z][a-zA-Z0-9]*$'.",
+ "39:17: Name 'E8' must match pattern '^[a-z][a-zA-Z0-9]*$'.",
+ "40:30: Name 'e9' must match pattern '^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$'.",
+
+ "64:23: Catching 'Exception' is not allowed.",
+ "66:23: Catching 'Throwable' is not allowed.",
+ "73:11: Catching 'Exception' is not allowed.",
+ };
+
+ @Test
+ public void testNone()
+ throws Exception
+ {
+ final DefaultConfiguration filterConfig = null;
+ final String[] suppressed = {
+ };
+ verifySuppressed(filterConfig, suppressed);
+ }
+
+ @Test
+ public void testDefault() throws Exception
+ {
+ final DefaultConfiguration filterConfig =
+ createFilterConfig(SuppressWithNearbyCommentFilter.class);
+ final String[] suppressed = {
+ "14:17: Name 'A1' must match pattern '^[a-z][a-zA-Z0-9]*$'.",
+ "15:17: Name 'A2' must match pattern '^[a-z][a-zA-Z0-9]*$'.",
+ "16:59: Name 'A3' must match pattern '^[a-z][a-zA-Z0-9]*$'.",
+
+ "18:17: Name 'B1' must match pattern '^[a-z][a-zA-Z0-9]*$'.",
+ "19:17: Name 'B2' must match pattern '^[a-z][a-zA-Z0-9]*$'.",
+ "20:59: Name 'B3' must match pattern '^[a-z][a-zA-Z0-9]*$'.",
+ };
+ verifySuppressed(filterConfig, suppressed);
+ }
+
+ @Test
+ public void testCheckC() throws Exception
+ {
+ final DefaultConfiguration filterConfig =
+ createFilterConfig(SuppressWithNearbyCommentFilter.class);
+ filterConfig.addAttribute("checkC", "false");
+ final String[] suppressed = {
+ "14:17: Name 'A1' must match pattern '^[a-z][a-zA-Z0-9]*$'.",
+ "18:17: Name 'B1' must match pattern '^[a-z][a-zA-Z0-9]*$'.",
+ };
+ verifySuppressed(filterConfig, suppressed);
+ }
+
+ @Test
+ public void testCheckCPP() throws Exception
+ {
+ final DefaultConfiguration filterConfig =
+ createFilterConfig(SuppressWithNearbyCommentFilter.class);
+ filterConfig.addAttribute("checkCPP", "false");
+ final String[] suppressed = {
+ "15:17: Name 'A2' must match pattern '^[a-z][a-zA-Z0-9]*$'.",
+ "16:59: Name 'A3' must match pattern '^[a-z][a-zA-Z0-9]*$'.",
+
+ "19:17: Name 'B2' must match pattern '^[a-z][a-zA-Z0-9]*$'.",
+ "20:59: Name 'B3' must match pattern '^[a-z][a-zA-Z0-9]*$'.",
+ };
+ verifySuppressed(filterConfig, suppressed);
+ }
+
+ @Test
+ public void testUsingAVariableMessage() throws Exception
+ {
+ final DefaultConfiguration filterConfig =
+ createFilterConfig(SuppressWithNearbyCommentFilter.class);
+ filterConfig.addAttribute("commentFormat", "ALLOW CATCH (\\w+) BECAUSE");
+ filterConfig.addAttribute("checkFormat", "IllegalCatchCheck");
+ filterConfig.addAttribute("messageFormat", "$1");
+ filterConfig.addAttribute("influenceFormat", "-1");
+ final String[] suppressed = {
+ "66:23: Catching 'Throwable' is not allowed.",
+ "73:11: Catching 'Exception' is not allowed.",
+ };
+ verifySuppressed(filterConfig, suppressed);
+ }
+
+ @Test
+ public void testUsingAVariableCheckOnNextLine() throws Exception
+ {
+ final DefaultConfiguration filterConfig =
+ createFilterConfig(SuppressWithNearbyCommentFilter.class);
+ filterConfig.addAttribute("commentFormat", "ALLOW (\\w+) ON NEXT LINE");
+ filterConfig.addAttribute("checkFormat", "$1");
+ filterConfig.addAttribute("influenceFormat", "1");
+ final String[] suppressed = {
+ "24:17: Name 'C2' must match pattern '^[a-z][a-zA-Z0-9]*$'.",
+ };
+ verifySuppressed(filterConfig, suppressed);
+ }
+
+ @Test
+ public void testUsingAVariableCheckOnPreviousLine() throws Exception
+ {
+ final DefaultConfiguration filterConfig =
+ createFilterConfig(SuppressWithNearbyCommentFilter.class);
+ filterConfig.addAttribute("commentFormat", "ALLOW (\\w+) ON PREVIOUS LINE");
+ filterConfig.addAttribute("checkFormat", "$1");
+ filterConfig.addAttribute("influenceFormat", "-1");
+ final String[] suppressed = {
+ "28:17: Name 'D2' must match pattern '^[a-z][a-zA-Z0-9]*$'.",
+ };
+ verifySuppressed(filterConfig, suppressed);
+ }
+
+ @Test
+ public void testVariableCheckOnVariableNumberOfLines() throws Exception
+ {
+ final DefaultConfiguration filterConfig =
+ createFilterConfig(SuppressWithNearbyCommentFilter.class);
+ filterConfig.addAttribute("commentFormat", "ALLOW (\\w+) UNTIL THIS LINE([+-]\\d+)");
+ filterConfig.addAttribute("checkFormat", "$1");
+ filterConfig.addAttribute("influenceFormat", "$2");
+ final String[] suppressed = {
+ "35:30: Name 'e4' must match pattern '^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$'.",
+ "36:17: Name 'E5' must match pattern '^[a-z][a-zA-Z0-9]*$'.",
+ "38:17: Name 'E7' must match pattern '^[a-z][a-zA-Z0-9]*$'.",
+ "39:17: Name 'E8' must match pattern '^[a-z][a-zA-Z0-9]*$'.",
+ };
+ verifySuppressed(filterConfig, suppressed);
+ }
+
+
+ public static DefaultConfiguration createFilterConfig(Class aClass)
+ {
+ return new DefaultConfiguration(aClass.getName());
+ }
+
+ protected void verifySuppressed(Configuration aFilterConfig,
+ String[] aSuppressed)
+ throws Exception
+ {
+ verify(createChecker(aFilterConfig),
+ getPath("filters/InputSuppressWithNearbyCommentFilter.java"),
+ removeSuppressed(sAllMessages, aSuppressed));
+ }
+
+ @Override
+ protected Checker createChecker(Configuration aFilterConfig)
+ throws CheckstyleException
+ {
+ final DefaultConfiguration checkerConfig =
+ new DefaultConfiguration("configuration");
+ final DefaultConfiguration checksConfig = createCheckConfig(TreeWalker.class);
+ checksConfig.addChild(createCheckConfig(FileContentsHolder.class));
+ checksConfig.addChild(createCheckConfig(MemberNameCheck.class));
+ checksConfig.addChild(createCheckConfig(ConstantNameCheck.class));
+ checksConfig.addChild(createCheckConfig(IllegalCatchCheck.class));
+ checkerConfig.addChild(checksConfig);
+ if (aFilterConfig != null) {
+ checkerConfig.addChild(aFilterConfig);
+ }
+ final Checker checker = new Checker();
+ final Locale locale = Locale.ENGLISH;
+ checker.setLocaleCountry(locale.getCountry());
+ checker.setLocaleLanguage(locale.getLanguage());
+ checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader());
+ checker.configure(checkerConfig);
+ checker.addListener(new BriefLogger(mStream));
+ return checker;
+ }
+
+ private String[] removeSuppressed(String[] aFrom, String[] aRemove)
+ {
+ final Collection@@ -963,7 +964,148 @@ <property name="checkFormat" value="$1"/> </module> - +
+ Filter SuppressWithNearbyCommentFilter uses + individual comments to suppress audit events. +
+ ++ Rationale: Same as SuppressionCommentFilter. + Whereas the SuppressionCommentFilter uses matched pairs of + filters to turn on/off comment matching, + SuppressWithNearbyCommentFilter uses + single comments. This requires fewer lines to mark a region, and + may be aesthetically preferable in some contexts. +
+ ++ Usage: This filter only works in conjunction with a FileContentsHolder, since that check makes + the suppression comments in the .java files available sub + rosa. A configuration that includes this filter must + configure FileContentsHolder as a + child module of TreeWalker. +
+ +| name | +description | +type | +default value | +
|---|---|---|---|
| commentFormat | +comment pattern to trigger filter to begin suppression | +regular expression | +SUPPRESS CHECKSTYLE (\w+) | +
| checkFormat | +check pattern to suppress | +regular expression | +$1 | +
| messageFormat | +message pattern to suppress | +regular expression | +none | +
| influenceFormat | +a negative/zero/positive value that defines the number of + lines preceding/at/following the suppression comment | +regular expression | +0 (the line containing the comment) | +
| checkCPP | +whether to check C++ style comments (//) |
+ boolean | +true | +
| checkC | +whether to check C style comments (/* ... */) |
+ boolean | +true | +
+ To configure the check that makes comments available to the filter: +
+ +
+ To configure a filter to suppress audit events for check
+ on any line with a comment SUPPRESS CHECKSTYLE check:
+
+ To configure a filter to suppress all audit events on any line
+ containing the comment CHECKSTYLE IGNORE THIS LINE:
+
+ To configure a filter so that
+ // Ok to catch (Throwable|Exception|RuntimeException) here
+ permits the current and previous line to avoid generating an IllegalCatch
+ audit event:
+
+ To configure a filter so that CHECKSTYLE IGNORE check FOR NEXT var LINES
+ avoids triggering any audits for the given check for the current line and the next var lines
+ (for a total of var+1 lines):
+
Fixed Bugs:
diff --git a/suppressions.xml b/suppressions.xml index f468289d0..8dac962b9 100755 --- a/suppressions.xml +++ b/suppressions.xml @@ -20,9 +20,12 @@