> ancestorMap;
+ if (userDefinedAncestorTokensMapValue == null) {
+ ancestorMap = Collections.emptyMap();
}
- if (supportContextStrategyChecksValue != null) {
- this.supportContextStrategyChecks.addAll(
- supportContextStrategyChecksValue);
+ else {
+ ancestorMap = Collections.unmodifiableMap(
+ new HashMap<>(userDefinedAncestorTokensMapValue));
}
- this.neverSuppressedChecks = neverSuppressedChecksValue;
+ this.userDefinedAncestorTokensMap = ancestorMap;
}
@Override
@@ -233,24 +221,18 @@ private boolean isNeverSuppressCheck(TreeWalkerAuditEvent event) {
}
/**
- * Apply stricter causality matching for checks configured under neverSuppressedChecks.
+ * Handles checks in {@code neverSuppressedChecks} with tighter scope logic.
*
- * Most checks report violations on the same line as the change, so the existing
- * line matching logic works for them. However, some checks have violations whose
- * causality is at a broader scope than the violation line itself:
+ *
Most checks just need a line match. A few are special:
+ * {@code CovariantEquals} fires on a method but the real cause is the enclosing
+ * type missing an {@code equals(Object)} override, so we check whether any changed
+ * line falls inside that type instead.
*
- *
- * - {@code CovariantEquals} - violation is reported on method AST, but causality
- * is type-level (missing {@code equals(Object)} in that class), so we attribute by
- * enclosing type scope
- *
- *
- * For all other checks, the fallback still applies and shows violations in touched
- * files, so nothing is missed. As more checks with similar behavior are discovered,
- * they can be added here with appropriate causality attribution logic.
+ *
For anything not in {@link #CLASS_SCOPE_NEVER_SUPPRESSED_CHECKS} the
+ * fallback is to show the violation whenever the file is touched.
*
* @param event audit event
- * @return true when violation can be attributed to changed lines in touched file
+ * @return true if the violation should be shown
*/
private boolean isMatchingByNeverSuppressedCheck(TreeWalkerAuditEvent event) {
boolean result = false;
@@ -271,10 +253,11 @@ private boolean isMatchingByNeverSuppressedCheck(TreeWalkerAuditEvent event) {
}
/**
- * Checks whether any changed line falls within the enclosing type of the event.
+ * Returns true if any changed line falls inside the enclosing class/interface/enum/record
+ * that contains the violation.
*
* @param event audit event
- * @return true if a changed line is inside the enclosing type scope
+ * @return true if a changed line is inside the enclosing type
*/
private boolean isChangedLineInsideEnclosingType(TreeWalkerAuditEvent event) {
final DetailAST eventAst = getEventAst(event);
@@ -289,10 +272,11 @@ private boolean isChangedLineInsideEnclosingType(TreeWalkerAuditEvent event) {
}
/**
- * Checks whether any changed line falls within the provided AST scope.
+ * Returns true if any changed line falls within the line range covered by {@code ast}
+ * and all its descendants.
*
- * @param ast enclosing AST node to evaluate
- * @return true if a changed line is inside the AST scope
+ * @param ast the enclosing node
+ * @return true if a changed line is inside this node's range
*/
private boolean isChangedLineInAstScope(DetailAST ast) {
boolean result = false;
@@ -376,19 +360,18 @@ private boolean lineMatching(int childAstStartLine, int childAstEndLine) {
}
/**
- * Is matching by context strategy.
+ * Returns true if the violation's resolved AST scope overlaps with a changed line.
+ * Looks the check up in {@link #contextStrategyMap} by short or full name,
+ * resolves the node via {@link #getAncestorAst}, then checks line overlap.
*
- * @param event {@code TreeWalkerAuditEvent} object
- * @return true if it is matching or not set.
+ * @param event the audit event
+ * @return true if the context scope overlaps a changed line
*/
private boolean isMatchingByContextStrategy(TreeWalkerAuditEvent event) {
boolean result = false;
- if (containsShortName(supportContextStrategyChecks, event)
- || containsShortName(
- checkNamesForContextStrategyByTokenOrParentSet, event)
- || containsShortName(
- checkNamesForContextStrategyByTokenOrAncestorSet, event)) {
- final DetailAST eventAst = getAncestorAst(event);
+ final ContextScope scope = resolveContextScope(event);
+ if (scope != null) {
+ final DetailAST eventAst = getAncestorAst(event, scope);
if (eventAst != null) {
final Map childAstLineNoMap =
@@ -402,35 +385,85 @@ private boolean isMatchingByContextStrategy(TreeWalkerAuditEvent event) {
}
/**
- * Gets the ancestor AST node for the event based on the check's context strategy.
+ * Returns the scope for the check that raised {@code event},
+ * or {@code null} if the check is not in {@link #contextStrategyMap}.
+ * Tries the full class name first, then the short name.
*
- * @param event the TreeWalkerAuditEvent
- * @return the ancestor AST node, or null if not found
+ * @param event the audit event
+ * @return the assigned {@link ContextScope}, or null
*/
- private DetailAST getAncestorAst(TreeWalkerAuditEvent event) {
+ private ContextScope resolveContextScope(TreeWalkerAuditEvent event) {
+ final String checkName = getCheckName(event);
+ final String checkShortName = getCheckShortName(event);
+ ContextScope scope = contextStrategyMap.get(checkName);
+ if (scope == null) {
+ scope = contextStrategyMap.get(checkShortName);
+ }
+ return scope;
+ }
+
+ /**
+ * Returns the AST node to use as the context window for line matching.
+ *
+ *
+ * - {@link ContextScope#SELF} — the violation node itself
+ * - {@link ContextScope#PARENT} — the immediate parent
+ * - {@link ContextScope#ANCESTOR} — walks up until hitting a node whose token type
+ * is in {@link #userDefinedAncestorTokensMap} (user-supplied) or
+ * {@link #CHECK_TO_ANCESTOR_NODES_MAP} (built-in fallback)
+ *
+ *
+ * @param event the audit event
+ * @param scope the scope to apply (never null)
+ * @return the resolved node, or null if not found
+ */
+ private DetailAST getAncestorAst(TreeWalkerAuditEvent event, ContextScope scope) {
DetailAST eventAst = getEventAst(event);
- if (containsShortName(
- checkNamesForContextStrategyByTokenOrAncestorSet, event)) {
- if (eventAst != null) {
- eventAst = eventAst.getParent();
- final List checkAncestorNodesList =
- CHECK_TO_ANCESTOR_NODES_MAP.get(getCheckShortName(event));
- while (eventAst != null && checkAncestorNodesList != null
- && !checkAncestorNodesList.contains(
- eventAst.getType())) {
+ switch (scope) {
+ case SELF -> {
+ // Use the violation's own AST node as the context window.
+ }
+ case PARENT -> {
+ if (eventAst != null) {
eventAst = eventAst.getParent();
}
}
- }
- else if (containsShortName(
- checkNamesForContextStrategyByTokenOrParentSet, event)) {
- if (eventAst != null) {
- eventAst = eventAst.getParent();
+ case ANCESTOR -> {
+ if (eventAst != null) {
+ eventAst = eventAst.getParent();
+ List ancestorTokens =
+ resolveAncestorTokens(getCheckName(event));
+ if (ancestorTokens == null) {
+ ancestorTokens = resolveAncestorTokens(getCheckShortName(event));
+ }
+ if (ancestorTokens == null) {
+ ancestorTokens =
+ CHECK_TO_ANCESTOR_NODES_MAP.get(getCheckShortName(event));
+ }
+ while (eventAst != null && ancestorTokens != null
+ && !ancestorTokens.contains(eventAst.getType())) {
+ eventAst = eventAst.getParent();
+ }
+ }
+ }
+ default -> {
+ // No other values exist in ContextScope; this case is unreachable.
}
}
return eventAst;
}
+ /**
+ * Returns the user-supplied ancestor token list for the given check name,
+ * or {@code null} if none was set.
+ *
+ * @param checkName short or full check name
+ * @return token-type list, or null
+ */
+ private List resolveAncestorTokens(String checkName) {
+ return userDefinedAncestorTokensMap.get(checkName);
+ }
+
/**
* Check whether checkNameSet contains event's check.
*
@@ -448,10 +481,10 @@ private static boolean containsShortName(Set checkNameSet,
}
/**
- * Gets the check name from the event.
+ * Returns the simple class name of the check that raised the event.
*
- * @param event the TreeWalkerAuditEvent
- * @return the check name
+ * @param event the audit event
+ * @return simple class name (e.g. {@code FallThroughCheck})
*/
private static String getCheckName(TreeWalkerAuditEvent event) {
final String[] checkNames = event.violation()
@@ -460,20 +493,22 @@ private static String getCheckName(TreeWalkerAuditEvent event) {
}
/**
- * Gets the short check name (without "Check" suffix) from the event.
+ * Returns the check name with the {@code Check} suffix stripped.
+ * For example, {@code FallThroughCheck} becomes {@code FallThrough}.
*
- * @param event the TreeWalkerAuditEvent
- * @return the short check name
+ * @param event the audit event
+ * @return short check name
*/
private static String getCheckShortName(TreeWalkerAuditEvent event) {
return getCheckName(event).replaceAll("Check", "");
}
/**
- * Return event's corresponding ast node using iterative algorithm.
+ * Walks the AST to find the node that matches the event's token type,
+ * line, and column. Returns null if no match is found.
*
- * @param event {@code TreeWalkerAuditEvent} object
- * @return DetailAST event's corresponding ast node
+ * @param event the audit event
+ * @return the matching AST node, or null
*/
private static DetailAST getEventAst(TreeWalkerAuditEvent event) {
DetailAST curNode = event.rootAst();
@@ -494,11 +529,11 @@ private static DetailAST getEventAst(TreeWalkerAuditEvent event) {
}
/**
- * Find min and max line numbers from AST node and its children.
+ * Scans {@code ast} and all its descendants to find the min and max line numbers.
+ * The result map has keys {@link #MIN} and {@link #MAX}.
*
- * @param ast DetailAST event's corresponding ast node
- * @return Map contains ast node's all child nodes' max and min line
- * number
+ * @param ast the root node to scan
+ * @return map with min and max line numbers across the subtree
*/
private static Map getChildAstLineNo(DetailAST ast) {
final Map childAstLineNoMap = new HashMap<>();
@@ -523,12 +558,11 @@ private static Map getChildAstLineNo(DetailAST ast) {
}
/**
- * Update childAstLineNoMap if line number of ast is smaller
- * or larger than current min and max value.
+ * Updates the min/max map if {@code ast}'s line number is outside the current range.
+ * Does nothing if {@code ast} is null.
*
- * @param childAstLineNoMap Map contains ast node's all child nodes' max
- * and min line number
- * @param ast DetailAST event's ast node's child node
+ * @param childAstLineNoMap running min/max map
+ * @param ast node to check; may be null
*/
private static void setChildAstLineNo(
Map childAstLineNoMap, DetailAST ast) {
@@ -544,11 +578,11 @@ else if (lineNo > childAstLineNoMap.get(MAX)) {
}
/**
- * Check whether AST node matches event's node.
+ * Returns true if {@code ast} matches the token type, line, and column of the event.
*
- * @param ast DetailAST
- * @param event {@code TreeWalkerAuditEvent} object
- * @return true if it is matching.
+ * @param ast node to test
+ * @param event the audit event
+ * @return true if the node is the one the event points at
*/
private static boolean isMatchingAst(DetailAST ast,
TreeWalkerAuditEvent event) {
diff --git a/src/main/java/com/puppycrawl/tools/checkstyle/filters/SuppressionJavaPatchFilter.java b/src/main/java/com/puppycrawl/tools/checkstyle/filters/SuppressionJavaPatchFilter.java
index 53f5c5c4..43c77cb8 100644
--- a/src/main/java/com/puppycrawl/tools/checkstyle/filters/SuppressionJavaPatchFilter.java
+++ b/src/main/java/com/puppycrawl/tools/checkstyle/filters/SuppressionJavaPatchFilter.java
@@ -23,9 +23,12 @@
import java.io.InputStream;
import java.util.Arrays;
import java.util.Collections;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
+import java.util.logging.Logger;
import org.eclipse.jgit.patch.FileHeader;
import org.eclipse.jgit.patch.Patch;
@@ -35,6 +38,7 @@
import com.puppycrawl.tools.checkstyle.api.AutomaticBean;
import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
import com.puppycrawl.tools.checkstyle.api.ExternalResourceHolder;
+import com.puppycrawl.tools.checkstyle.api.TokenTypes;
import com.puppycrawl.tools.checkstyle.utils.FilterUtil;
/**
@@ -45,14 +49,15 @@
*/
public final class SuppressionJavaPatchFilter extends AutomaticBean implements
TreeWalkerFilter, ExternalResourceHolder {
- /**
- * To split never Suppressed Checks and Ids's string.
- */
+
+ /** Logger for deprecation warnings. */
+ private static final Logger LOG =
+ Logger.getLogger(SuppressionJavaPatchFilter.class.getName());
+
+ /** Delimiter for splitting comma-separated property values. */
private static final String COMMA = ",";
- /**
- * List of checks that support context strategy.
- */
+ /** Hardcoded list of checks that always use SELF scope. Currently empty. */
private static final List SUPPORT_CONTEXT_STRATEGY_CHECKS = Arrays.asList();
/**
@@ -74,21 +79,17 @@ public final class SuppressionJavaPatchFilter extends AutomaticBean implements
private Strategy strategy = Strategy.NEWLINE;
/**
- * Set has user defined Checks that need modify violation nodes
- * to their parent abstract nodes to get their child nodes.
+ * Maps each check name to its context scope.
+ * Built from the {@code contextStrategy} property or from the deprecated setters.
*/
- private Set checkNamesForContextStrategyByTokenOrParentSet;
+ private Map contextStrategyMap = new HashMap<>();
/**
- * Set has user defined Checks that need modify violation nodes
- * to their ancestor abstract nodes to get their child nodes.
+ * Ancestor token types explicitly set via {@code contextStrategy} (e.g.
+ * {@code FallThrough:LITERAL_SWITCH}). These take priority over the
+ * built-in lookup table in {@code JavaPatchFilterElement}.
*/
- private Set checkNamesForContextStrategyByTokenOrAncestorSet;
-
- /**
- * Set has user defined Checks that support context strategy.
- */
- private Set supportContextStrategyChecks;
+ private Map> userDefinedAncestorTokensMap = new HashMap<>();
/**
* Set has user defined Checks to never suppress if files are referenced
@@ -123,47 +124,135 @@ public void setStrategy(String strategyName) {
this.strategy = Strategy.valueOf(strategyName.toUpperCase());
}
+ /**
+ * Configures the unified context strategy for checks running under the
+ * {@code context} strategy.
+ *
+ * Accepts a comma-separated list of {@code CheckName:SCOPE} or
+ * {@code CheckName:TOKEN_TYPE} pairs. {@code SCOPE} is one of:
+ *
+ * - {@code SELF} — match against the violation's own AST node
+ * - {@code PARENT} — match against the immediate parent node
+ * - {@code ANCESTOR} — walk up using the built-in token map
+ * (for checks already listed there)
+ * - Any valid {@code TokenTypes} constant name (e.g. {@code LITERAL_SWITCH},
+ * {@code CLASS_DEF}) — walk up to the first ancestor of that token type;
+ * works for any check, including custom ones
+ *
+ *
+ * Whitespace around names is trimmed. Examples:
+ *
+ * <property name="contextStrategy"
+ * value="MethodLength:SELF, RightCurly:PARENT, FallThrough:LITERAL_SWITCH"/>
+ *
+ *
+ * @param contextStrategyValue comma-separated entries
+ * @throws IllegalArgumentException if an entry is malformed or the scope/token name
+ * is not recognised
+ * @since 10.x
+ */
+ public void setContextStrategy(String contextStrategyValue) {
+ final String[] entries = contextStrategyValue.split(COMMA);
+ for (String entry : entries) {
+ final String trimmed = entry.trim();
+ final int colonIndex = trimmed.lastIndexOf(':');
+ if (colonIndex < 1 || colonIndex == trimmed.length() - 1) {
+ throw new IllegalArgumentException(
+ "contextStrategy entry must be 'CheckName:SCOPE' or "
+ + "'CheckName:TOKEN_TYPE', got: '" + trimmed + "'");
+ }
+ final String checkName = trimmed.substring(0, colonIndex).trim();
+ final String scopeName = trimmed.substring(colonIndex + 1).trim().toUpperCase();
+
+ ContextScope scope = null;
+ try {
+ scope = ContextScope.valueOf(scopeName);
+ }
+ catch (IllegalArgumentException ignored) {
+ scope = null;
+ }
+
+ if (scope != null) {
+ contextStrategyMap.put(checkName, scope);
+ }
+ else {
+ try {
+ final int tokenType =
+ TokenTypes.class.getField(scopeName).getInt(null);
+ contextStrategyMap.put(checkName, ContextScope.ANCESTOR);
+ userDefinedAncestorTokensMap.put(
+ checkName, Collections.singletonList(tokenType));
+ }
+ catch (NoSuchFieldException | IllegalAccessException exc) {
+ throw new IllegalArgumentException(
+ "Unknown scope or token type '" + scopeName
+ + "' in contextStrategy entry '" + trimmed
+ + "'. Use SELF, PARENT, ANCESTOR, or a valid "
+ + "TokenTypes constant name (e.g. LITERAL_SWITCH,"
+ + " CLASS_DEF, SLIST).", exc);
+ }
+ }
+ }
+ }
+
/**
* Setter to set has user defined list of Checks need modify violation
* nodes to their parent abstract nodes to get their child nodes.
*
* @param checkNamesForContextStrategyByTokenOrParentSetValue
- * string which is user defined Checks
+ * string which is user defined Checks
* that need modify violation nodes
* to their parent abstract nodes
* to get their child nodes,
* split by comma
- *
* @since 8.34
+ * @deprecated Since 10.x. Use {@code contextStrategy} with {@code CheckName:PARENT} instead.
+ * Example: {@code <property name="contextStrategy"
+ * value="RightCurly:PARENT"/>}.
*/
+ @Deprecated(since = "10.x", forRemoval = true)
public void setCheckNamesForContextStrategyByTokenOrParentSet(
String checkNamesForContextStrategyByTokenOrParentSetValue) {
- final String[] checksArray =
- checkNamesForContextStrategyByTokenOrParentSetValue
- .split(COMMA);
- this.checkNamesForContextStrategyByTokenOrParentSet =
- new HashSet<>(Arrays.asList(checksArray));
+ LOG.warning("Deprecated property 'checkNamesForContextStrategyByTokenOrParentSet'. "
+ + "Use contextStrategy with :PARENT scope instead. "
+ + "Example: ");
+ for (String check : checkNamesForContextStrategyByTokenOrParentSetValue.split(COMMA)) {
+ contextStrategyMap.put(check.trim(), ContextScope.PARENT);
+ }
}
/**
* Setter to set has user defined list of Checks need modify violation
* nodes to their ancestor abstract nodes to get their child nodes.
*
+ * Note: The ancestor token type used for each check was previously hardcoded
+ * inside {@code JavaPatchFilterElement}. With the new {@code contextStrategy}
+ * property you can now specify the exact token type explicitly, e.g.
+ * {@code FallThrough:LITERAL_SWITCH}, which eliminates the need for that
+ * hardcoded map.
+ *
* @param checkNamesForContextStrategyByTokenOrAncestorSetValue
- * string which is user defined Checks
+ * string which is user defined Checks
* that need modify violation nodes
* to their ancestor abstract nodes
* to get their child nodes,
* split by comma
* @since 8.34
+ * @deprecated Since 10.x. Use {@code contextStrategy} with an explicit token type instead.
+ * Example: {@code <property name="contextStrategy"
+ * value="FallThrough:LITERAL_SWITCH"/>}.
*/
+ @Deprecated(since = "10.x", forRemoval = true)
public void setCheckNamesForContextStrategyByTokenOrAncestorSet(
String checkNamesForContextStrategyByTokenOrAncestorSetValue) {
- final String[] checksArray =
- checkNamesForContextStrategyByTokenOrAncestorSetValue
- .split(COMMA);
- this.checkNamesForContextStrategyByTokenOrAncestorSet =
- new HashSet<>(Arrays.asList(checksArray));
+ LOG.warning("Deprecated property 'checkNamesForContextStrategyByTokenOrAncestorSet'. "
+ + "Use contextStrategy with an explicit token type instead. "
+ + "Example: ");
+ for (String check
+ : checkNamesForContextStrategyByTokenOrAncestorSetValue.split(COMMA)) {
+ contextStrategyMap.put(check.trim(), ContextScope.ANCESTOR);
+ }
}
/**
@@ -172,13 +261,22 @@ public void setCheckNamesForContextStrategyByTokenOrAncestorSet(
* @param supportContextStrategyChecksValue string has user defined checks that
* support context strategy
* @since 8.34
+ * @deprecated Since 10.x. Use {@code contextStrategy} with {@code CheckName:SELF} instead.
+ * Example: {@code <property name="contextStrategy"
+ * value="MethodLength:SELF"/>}.
*/
+ @Deprecated(since = "10.x", forRemoval = true)
public void setSupportContextStrategyChecks(
String supportContextStrategyChecksValue) {
- final String[] checksArray =
- supportContextStrategyChecksValue.split(COMMA);
- this.supportContextStrategyChecks = new HashSet<>(Arrays.asList(checksArray));
- this.supportContextStrategyChecks.addAll(SUPPORT_CONTEXT_STRATEGY_CHECKS);
+ LOG.warning("Deprecated property 'supportContextStrategyChecks'. "
+ + "Use contextStrategy with :SELF scope instead. "
+ + "Example: ");
+ final String[] checksArray = supportContextStrategyChecksValue.split(COMMA);
+ for (String check : checksArray) {
+ contextStrategyMap.put(check.trim(), ContextScope.SELF);
+ }
+ contextStrategyMap.putAll(
+ buildSelfMapFromList(SUPPORT_CONTEXT_STRATEGY_CHECKS));
}
/**
@@ -258,9 +356,8 @@ private void loadPatchFile() throws CheckstyleException {
final JavaPatchFilterElement element =
new JavaPatchFilterElement(fileName, lineRangeList,
strategy,
- checkNamesForContextStrategyByTokenOrParentSet,
- checkNamesForContextStrategyByTokenOrAncestorSet,
- supportContextStrategyChecks,
+ Collections.unmodifiableMap(contextStrategyMap),
+ Collections.unmodifiableMap(userDefinedAncestorTokensMap),
neverSuppressedChecks);
filters.add(element);
}
@@ -278,4 +375,19 @@ private void loadPatchFile() throws CheckstyleException {
public Set getExternalResourceLocations() {
return Collections.singleton(file);
}
+
+ /**
+ * Builds a SELF-scope entry map from a list of check names.
+ * Used internally to convert the default {@link #SUPPORT_CONTEXT_STRATEGY_CHECKS} list.
+ *
+ * @param checks list of check names
+ * @return map of check name to {@link ContextScope#SELF}
+ */
+ private static Map buildSelfMapFromList(List checks) {
+ final Map map = new HashMap<>();
+ for (String check : checks) {
+ map.put(check.trim(), ContextScope.SELF);
+ }
+ return map;
+ }
}
diff --git a/src/test/java/com/puppycrawl/tools/checkstyle/filters/SuppressionJavaPatchFilterTest.java b/src/test/java/com/puppycrawl/tools/checkstyle/filters/SuppressionJavaPatchFilterTest.java
index b8faff4e..b071bb91 100644
--- a/src/test/java/com/puppycrawl/tools/checkstyle/filters/SuppressionJavaPatchFilterTest.java
+++ b/src/test/java/com/puppycrawl/tools/checkstyle/filters/SuppressionJavaPatchFilterTest.java
@@ -51,6 +51,20 @@ public void testShortName() throws Exception {
testByConfig("ShortName/neverSuppressedChecks/defaultContextConfig.xml");
}
+ /**
+ * Tests the unified {@code contextStrategy} property.
+ * Covers SELF, PARENT, ANCESTOR, and explicit token type ({@code FallThrough:LITERAL_SWITCH}).
+ *
+ * @throws Exception if config or check processing fails
+ */
+ @Test
+ public void testContextStrategy() throws Exception {
+ testByConfig("ContextStrategy/self/defaultContextConfig.xml");
+ testByConfig("ContextStrategy/parent/defaultContextConfig.xml");
+ testByConfig("ContextStrategy/ancestor/defaultContextConfig.xml");
+ testByConfig("ContextStrategy/ancestorTokenType/defaultContextConfig.xml");
+ }
+
@Test
public void testNonExistentPatchFileWithFalseOptional() throws Exception {
try {
diff --git a/src/test/resources/com/puppycrawl/tools/checkstyle/filters/suppressionjavapatchfilter/ContextStrategy/ancestor/Test.java b/src/test/resources/com/puppycrawl/tools/checkstyle/filters/suppressionjavapatchfilter/ContextStrategy/ancestor/Test.java
new file mode 100644
index 00000000..0dfb536e
--- /dev/null
+++ b/src/test/resources/com/puppycrawl/tools/checkstyle/filters/suppressionjavapatchfilter/ContextStrategy/ancestor/Test.java
@@ -0,0 +1,40 @@
+package TreeWalker.blocks.RightCurly;
+
+public class Test {
+ public void test() {
+ int x = 0;
+ while (true) {
+ try {
+ if (x > 0)
+ {
+ break;
+ } // violation without filter
+ else if (x < 0)
+ {
+ ;
+ } // violation without filter
+ else
+ {
+ break;
+ }
+
+ } // violation without filter
+ catch (Exception e)
+ {
+ break; //changed
+ } // violation without filter
+ finally
+ {
+ break;
+ }
+ }
+
+ try {
+
+ } // violation without filter
+ catch (Exception e)
+ {
+
+ }
+ }
+}
diff --git a/src/test/resources/com/puppycrawl/tools/checkstyle/filters/suppressionjavapatchfilter/ContextStrategy/ancestor/defaultContext.patch b/src/test/resources/com/puppycrawl/tools/checkstyle/filters/suppressionjavapatchfilter/ContextStrategy/ancestor/defaultContext.patch
new file mode 100644
index 00000000..9bfbadb5
--- /dev/null
+++ b/src/test/resources/com/puppycrawl/tools/checkstyle/filters/suppressionjavapatchfilter/ContextStrategy/ancestor/defaultContext.patch
@@ -0,0 +1,21 @@
+diff --git a/Test.java b/Test.java
+index c07979d..35e1c6f 100644
+--- a/Test.java
++++ b/Test.java
+@@ -13,11 +13,15 @@ public class Test {
+ {
+ ;
+ } // violation without filter
++ else
++ {
++ break;
++ }
+
+ } // violation without filter
+ catch (Exception e)
+ {
+- break;
++ break; //changed
+ } // violation without filter
+ finally
+ {
diff --git a/src/test/resources/com/puppycrawl/tools/checkstyle/filters/suppressionjavapatchfilter/ContextStrategy/ancestor/defaultContextConfig.xml b/src/test/resources/com/puppycrawl/tools/checkstyle/filters/suppressionjavapatchfilter/ContextStrategy/ancestor/defaultContextConfig.xml
new file mode 100644
index 00000000..44985878
--- /dev/null
+++ b/src/test/resources/com/puppycrawl/tools/checkstyle/filters/suppressionjavapatchfilter/ContextStrategy/ancestor/defaultContextConfig.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/test/resources/com/puppycrawl/tools/checkstyle/filters/suppressionjavapatchfilter/ContextStrategy/ancestor/expected.txt b/src/test/resources/com/puppycrawl/tools/checkstyle/filters/suppressionjavapatchfilter/ContextStrategy/ancestor/expected.txt
new file mode 100644
index 00000000..968cef5e
--- /dev/null
+++ b/src/test/resources/com/puppycrawl/tools/checkstyle/filters/suppressionjavapatchfilter/ContextStrategy/ancestor/expected.txt
@@ -0,0 +1,4 @@
+Test.java:11:17: '}' at column 17 should be on the same line as the next part of a multi-block statement (one that directly contains multiple blocks: if/else-if/else, do/while or try/catch/finally).
+Test.java:15:17: '}' at column 17 should be on the same line as the next part of a multi-block statement (one that directly contains multiple blocks: if/else-if/else, do/while or try/catch/finally).
+Test.java:21:13: '}' at column 13 should be on the same line as the next part of a multi-block statement (one that directly contains multiple blocks: if/else-if/else, do/while or try/catch/finally).
+Test.java:25:13: '}' at column 13 should be on the same line as the next part of a multi-block statement (one that directly contains multiple blocks: if/else-if/else, do/while or try/catch/finally).
diff --git a/src/test/resources/com/puppycrawl/tools/checkstyle/filters/suppressionjavapatchfilter/ContextStrategy/ancestorTokenType/Test.java b/src/test/resources/com/puppycrawl/tools/checkstyle/filters/suppressionjavapatchfilter/ContextStrategy/ancestorTokenType/Test.java
new file mode 100644
index 00000000..92011fc0
--- /dev/null
+++ b/src/test/resources/com/puppycrawl/tools/checkstyle/filters/suppressionjavapatchfilter/ContextStrategy/ancestorTokenType/Test.java
@@ -0,0 +1,33 @@
+package TreeWalker.coding.FallThrough;
+
+public class Test {
+ public void test(int i) {
+ switch (i) {
+ case 0:
+ i++;
+ break;
+ case 1:
+ i++;
+ case 2: // violation without filter
+ case 3:
+ case 4: {
+ i++;
+ }
+ case 5: // violation without filter
+ i++;
+ case 6: // violation without filter
+ i++;
+ case 7: // violation without filter
+ i++;
+ }
+
+ switch (i) {
+ case 0:
+ i++;
+ break;
+ case 1:
+ i++;
+ case 2: // violation without filter
+ }
+ }
+}
diff --git a/src/test/resources/com/puppycrawl/tools/checkstyle/filters/suppressionjavapatchfilter/ContextStrategy/ancestorTokenType/defaultContext.patch b/src/test/resources/com/puppycrawl/tools/checkstyle/filters/suppressionjavapatchfilter/ContextStrategy/ancestorTokenType/defaultContext.patch
new file mode 100644
index 00000000..0bd6690d
--- /dev/null
+++ b/src/test/resources/com/puppycrawl/tools/checkstyle/filters/suppressionjavapatchfilter/ContextStrategy/ancestorTokenType/defaultContext.patch
@@ -0,0 +1,23 @@
+diff --git a/Test.java b/Test.java
+index 397f121..cf434a7 100644
+--- a/Test.java
++++ b/Test.java
+@@ -5,9 +5,9 @@ public class Test {
+ switch (i) {
+ case 0:
+ i++;
++ break;
+ case 1:
+ i++;
+- // falls through
+ case 2: // violation without filter
+ case 3:
+ case 4: {
+@@ -19,7 +19,6 @@ public class Test {
+ i++;
+ case 7: // violation without filter
+ i++;
+- break;
+ }
+ }
+ }
diff --git a/src/test/resources/com/puppycrawl/tools/checkstyle/filters/suppressionjavapatchfilter/ContextStrategy/ancestorTokenType/defaultContextConfig.xml b/src/test/resources/com/puppycrawl/tools/checkstyle/filters/suppressionjavapatchfilter/ContextStrategy/ancestorTokenType/defaultContextConfig.xml
new file mode 100644
index 00000000..8f89deb3
--- /dev/null
+++ b/src/test/resources/com/puppycrawl/tools/checkstyle/filters/suppressionjavapatchfilter/ContextStrategy/ancestorTokenType/defaultContextConfig.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/test/resources/com/puppycrawl/tools/checkstyle/filters/suppressionjavapatchfilter/ContextStrategy/ancestorTokenType/expected.txt b/src/test/resources/com/puppycrawl/tools/checkstyle/filters/suppressionjavapatchfilter/ContextStrategy/ancestorTokenType/expected.txt
new file mode 100644
index 00000000..41427d18
--- /dev/null
+++ b/src/test/resources/com/puppycrawl/tools/checkstyle/filters/suppressionjavapatchfilter/ContextStrategy/ancestorTokenType/expected.txt
@@ -0,0 +1,4 @@
+Test.java:11:13: Fall through from previous branch of the switch statement.
+Test.java:16:13: Fall through from previous branch of the switch statement.
+Test.java:18:13: Fall through from previous branch of the switch statement.
+Test.java:20:13: Fall through from previous branch of the switch statement.
diff --git a/src/test/resources/com/puppycrawl/tools/checkstyle/filters/suppressionjavapatchfilter/ContextStrategy/parent/Test.java b/src/test/resources/com/puppycrawl/tools/checkstyle/filters/suppressionjavapatchfilter/ContextStrategy/parent/Test.java
new file mode 100644
index 00000000..faab5513
--- /dev/null
+++ b/src/test/resources/com/puppycrawl/tools/checkstyle/filters/suppressionjavapatchfilter/ContextStrategy/parent/Test.java
@@ -0,0 +1,20 @@
+package TreeWalker.coding.SuperClone;
+
+public class Test {
+ public Object clone() throws CloneNotSupportedException { // violation without filter
+ return new Object();
+ }
+}
+
+class Test1 {
+ public Object clone() throws CloneNotSupportedException { // violation without filter
+ return new Object();
+ }
+}
+
+class Test2 {
+ public Integer clone() throws CloneNotSupportedException {
+ return (Integer) super.clone();
+ }
+}
+
diff --git a/src/test/resources/com/puppycrawl/tools/checkstyle/filters/suppressionjavapatchfilter/ContextStrategy/parent/defaultContext.patch b/src/test/resources/com/puppycrawl/tools/checkstyle/filters/suppressionjavapatchfilter/ContextStrategy/parent/defaultContext.patch
new file mode 100644
index 00000000..ab063920
--- /dev/null
+++ b/src/test/resources/com/puppycrawl/tools/checkstyle/filters/suppressionjavapatchfilter/ContextStrategy/parent/defaultContext.patch
@@ -0,0 +1,19 @@
+diff --git a/Test.java b/Test.java
+index 6d5e669..ecb7271 100644
+--- a/Test.java
++++ b/Test.java
+@@ -2,13 +2,12 @@ package TreeWalker.coding.SuperClone;
+
+ public class Test {
+ public Object clone() throws CloneNotSupportedException { // violation without filter
+- return super.clone();
++ return new Object();
+ }
+ }
+
+ class Test1 {
+ public Object clone() throws CloneNotSupportedException { // violation without filter
+- System.out.println();
+ return new Object();
+ }
+ }
diff --git a/src/test/resources/com/puppycrawl/tools/checkstyle/filters/suppressionjavapatchfilter/ContextStrategy/parent/defaultContextConfig.xml b/src/test/resources/com/puppycrawl/tools/checkstyle/filters/suppressionjavapatchfilter/ContextStrategy/parent/defaultContextConfig.xml
new file mode 100644
index 00000000..036a4030
--- /dev/null
+++ b/src/test/resources/com/puppycrawl/tools/checkstyle/filters/suppressionjavapatchfilter/ContextStrategy/parent/defaultContextConfig.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/test/resources/com/puppycrawl/tools/checkstyle/filters/suppressionjavapatchfilter/ContextStrategy/parent/expected.txt b/src/test/resources/com/puppycrawl/tools/checkstyle/filters/suppressionjavapatchfilter/ContextStrategy/parent/expected.txt
new file mode 100644
index 00000000..6052363e
--- /dev/null
+++ b/src/test/resources/com/puppycrawl/tools/checkstyle/filters/suppressionjavapatchfilter/ContextStrategy/parent/expected.txt
@@ -0,0 +1,2 @@
+Test.java:4:19: Method 'clone' should call 'super.clone'.
+Test.java:10:19: Method 'clone' should call 'super.clone'.
diff --git a/src/test/resources/com/puppycrawl/tools/checkstyle/filters/suppressionjavapatchfilter/ContextStrategy/self/Test.java b/src/test/resources/com/puppycrawl/tools/checkstyle/filters/suppressionjavapatchfilter/ContextStrategy/self/Test.java
new file mode 100644
index 00000000..2b29d754
--- /dev/null
+++ b/src/test/resources/com/puppycrawl/tools/checkstyle/filters/suppressionjavapatchfilter/ContextStrategy/self/Test.java
@@ -0,0 +1,20 @@
+package TreeWalker.FinalClass;
+
+public class Test { // violation without filter
+ private Test() {}
+}
+
+class Test1 { // violation without filter
+ private Test1() {}
+}
+
+class Test2 { // violation without filter
+ private Test2() {
+ System.out.println();
+ }
+}
+
+class Test3 { // violation without filter
+ private Test3() {}
+ private int a;
+}
diff --git a/src/test/resources/com/puppycrawl/tools/checkstyle/filters/suppressionjavapatchfilter/ContextStrategy/self/defaultContext.patch b/src/test/resources/com/puppycrawl/tools/checkstyle/filters/suppressionjavapatchfilter/ContextStrategy/self/defaultContext.patch
new file mode 100644
index 00000000..62eeba5e
--- /dev/null
+++ b/src/test/resources/com/puppycrawl/tools/checkstyle/filters/suppressionjavapatchfilter/ContextStrategy/self/defaultContext.patch
@@ -0,0 +1,28 @@
+diff --git a/Test.java b/Test.java
+index 2fc8329..d3dd3f2 100644
+--- a/Test.java
++++ b/Test.java
+@@ -1,18 +1,20 @@
+ package TreeWalker.FinalClass;
+
+-public final class Test {
++public class Test { // violation without filter
+ private Test() {}
+ }
+
+ class Test1 { // violation without filter
+ private Test1() {}
+-
+ }
+
+ class Test2 { // violation without filter
+- private Test2() {}
++ private Test2() {
++ System.out.println();
++ }
+ }
+
+ class Test3 { // violation without filter
+ private Test3() {}
++ private int a;
+ }
diff --git a/src/test/resources/com/puppycrawl/tools/checkstyle/filters/suppressionjavapatchfilter/ContextStrategy/self/defaultContextConfig.xml b/src/test/resources/com/puppycrawl/tools/checkstyle/filters/suppressionjavapatchfilter/ContextStrategy/self/defaultContextConfig.xml
new file mode 100644
index 00000000..eb2f4eff
--- /dev/null
+++ b/src/test/resources/com/puppycrawl/tools/checkstyle/filters/suppressionjavapatchfilter/ContextStrategy/self/defaultContextConfig.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/test/resources/com/puppycrawl/tools/checkstyle/filters/suppressionjavapatchfilter/ContextStrategy/self/expected.txt b/src/test/resources/com/puppycrawl/tools/checkstyle/filters/suppressionjavapatchfilter/ContextStrategy/self/expected.txt
new file mode 100644
index 00000000..c54fde6e
--- /dev/null
+++ b/src/test/resources/com/puppycrawl/tools/checkstyle/filters/suppressionjavapatchfilter/ContextStrategy/self/expected.txt
@@ -0,0 +1,4 @@
+Test.java:3:1: Class Test should be declared as final.
+Test.java:7:1: Class Test1 should be declared as final.
+Test.java:11:1: Class Test2 should be declared as final.
+Test.java:17:1: Class Test3 should be declared as final.