diff --git a/README.md b/README.md index b784dc54..375e5e03 100644 --- a/README.md +++ b/README.md @@ -43,58 +43,61 @@ checks `Translation` and `UniqueProperties`: ``` - ### SuppressionJavaPatchFilter -Filter SuppressionJavaPatchFilter(TreeWalker level) has three different strategies that control suppression -scope. if property `strategy` is set to `newline`, then it only accepts TreeWalker audit events for TreeWalker -Check violations whose line number belong to added lines in patch file and will suppress all TreeWalker Checks’ -violation messages which are not from added lines. if property `strategy` is set to `patchedline`, it will accept -all violations whose line number belong to added/changed lines in patch file. if property `strategy` is set to `context`, -for checks listed in `supportContextStrategyChecks`, it will not only accept violations whose line number belong to added/changed/deleted lines, -but also consider a wider context that new code introduces violations outside of added/changed lines, but its child nodes in added/changed lines, -for checks not listed in `supportContextStrategyChecks`, it will accept violations whose line number belong to added/changed/deleted lines in patch file. -If there is no configured patch file or the optional is set to true and patch file was not found the Filter suppresses all audit events. +Filter SuppressionJavaPatchFilter (TreeWalker level) supports three strategies for controlling which +violations get suppressed: -Note that it's ok to use all four properties (`supportContextStrategyChecks`, `checkNameForContextStrategyByTokenOrParentSet`, `checkNameForContextStrategyByTokenOrAncestorSet`, -`neverSuppressedChecks`), and the context scope of the three is also growing, for TreeWalker checks only in one of the property, its context scope is the same as that property, -if in two or all properties, then The context scope is the maximum scope. +- `newline` — only accepts violations on lines that were added in the patch +- `patchedline` — accepts violations on added or changed lines +- `context` — for checks registered in `contextStrategy`, it widens the window: it accepts violations + whose AST scope (own node, parent, or ancestor) overlaps with any changed line, not just the exact + violation line. For all other checks it falls back to line-by-line matching. -Attention: `supportContextStrategyChecks` and `checkNameForContextStrategyByTokenOrParentSet`, `checkNameForContextStrategyByTokenOrAncestorSet` -only have effect when the `strategy` property is set to `context`, `neverSuppressedChecks` is no such requirement. +If no patch file is configured, or `optional` is `true` and the file is missing, the filter suppresses +all audit events. #### Properties - | name | description | type | default value | - |--------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------|---------------| - | file | Specify the location of the patch file. | [String](https://checkstyle.sourceforge.io/property_types.html#String) | null | - | optional | Control what to do when the file is not existing. If `optional` is set to `false` the file must exist, or else it ends with error. On the other hand if optional is `true` and file is not found, the filter suppresses all audit events. | [boolean](https://checkstyle.sourceforge.io/property_types.html#boolean) | false | - | strategy | Control suppression scope that you need. If `startegy` is set to `newline`, it only accepts TreeWalker audit events for TreeWalker Check violations whose line number belong to added lines in patch file. `patchedline` will accept added/changed lines. if `strategy` is set to `context` , for checks listed in `supportContextStrategyChecks`, it will accept violations whose line number belong to added/changed/deleted lines and new code introduces violations outside of added/changed lines, but its child nodes in added/changed lines, for checks not listed in `supportContextStrategyChecks`, it will accept violations whose line number belong to added/changed/deleted lines in patch file. | [String](https://checkstyle.sourceforge.io/property_types.html#String) | newline | - | supportContextStrategyChecks | String has user defined Checks that support context strategy | [String](https://checkstyle.sourceforge.io/property_types.html#String) | null | - | checkNamesForContextStrategyByTokenOrParentSet | String has user defined TreeWalker Checks that need modify violation nodes to their parent node to expand the context scope, split by comma | [String](https://checkstyle.sourceforge.io/property_types.html#String) | null | - | checkNamesForContextStrategyByTokenOrAncestorSet | String has user defined TreeWalker Checks that need modify violation nodes to their ancestor node to expand the context scope, split by comma | [String](https://checkstyle.sourceforge.io/property_types.html#String) | null | - | neverSuppressedChecks | String has user defined TreeWalker Checks to never suppress if files are touched, split by comma. This property is useful for Checks that place violation on whole file or on not all (first/last) occurrence of cause/violated code. | [String](https://checkstyle.sourceforge.io/property_types.html#String) | null | + | name | description | type | default value | + |--------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------|---------------| + | file | Path to the patch file. | [String](https://checkstyle.sourceforge.io/property_types.html#String) | null | + | optional | If `false`, the patch file must exist or the run fails. If `true` and the file is missing, all audit events are suppressed. | [boolean](https://checkstyle.sourceforge.io/property_types.html#boolean) | false | + | strategy | Suppression scope. `newline` matches added lines only, `patchedline` matches added or changed lines, `context` uses AST-scope matching for registered checks and line matching for the rest. | [String](https://checkstyle.sourceforge.io/property_types.html#String) | newline | + | contextStrategy | **Preferred.** Comma-separated list of `CheckName:SCOPE` or `CheckName:TOKEN_TYPE` entries. `SCOPE` is one of `SELF` (violation's own node), `PARENT` (immediate parent node), or `ANCESTOR` (walk up using the built-in token map). `TOKEN_TYPE` is any Checkstyle `TokenTypes` constant (e.g. `LITERAL_SWITCH`, `CLASS_DEF`) — walks up to the first ancestor of that type, which works for any check including custom ones. Only applies when `strategy` is `context`. | [String](https://checkstyle.sourceforge.io/property_types.html#String) | null | + | neverSuppressedChecks | Comma-separated list of checks that should never be suppressed when the file is touched. Useful for checks that report at file level or on the first/last occurrence of a pattern. | [String](https://checkstyle.sourceforge.io/property_types.html#String) | null | + | supportContextStrategyChecks | **Deprecated** since 10.x — use `contextStrategy` with `CheckName:SELF` instead. Comma-separated checks that use their own AST node as the context window. | [String](https://checkstyle.sourceforge.io/property_types.html#String) | null | + | checkNamesForContextStrategyByTokenOrParentSet | **Deprecated** since 10.x — use `contextStrategy` with `CheckName:PARENT` instead. Comma-separated checks that walk up to the immediate parent node. | [String](https://checkstyle.sourceforge.io/property_types.html#String) | null | + | checkNamesForContextStrategyByTokenOrAncestorSet | **Deprecated** since 10.x — use `contextStrategy` with an explicit token type (e.g. `FallThrough:LITERAL_SWITCH`) instead. Comma-separated checks that walk up to a specific ancestor; the token type was previously hardcoded internally. | [String](https://checkstyle.sourceforge.io/property_types.html#String) | null | + +#### contextStrategy scope guide + +| Use | When | +|-----|------| +| `CheckName:SELF` | The check reports the violation on the exact node it cares about | +| `CheckName:PARENT` | The violation is on a child token but context is one level up | +| `CheckName:LITERAL_SWITCH` (or any TokenTypes name) | The violation is deeply nested; you know the enclosing token type | +| `CheckName:ANCESTOR` | The violation is deeply nested and the check is in the built-in list below (legacy support) | #### Notes -Currently, the following checks support `checkNamesForContextStrategyByTokenOrAncestorSet` property, -other checks using this property will get the same effect like `checkNamesForContextStrategyByTokenOrParentSet`: - -* ArrayTrailingComma -* AvoidNestedBlocks -* CommentsIndentation -* DefaultComesLast -* DeclarationOrder -* EqualsHashCode -* FinalLocalVariable -* FallThrough -* RightCurly +The following checks have built-in ancestor token mappings and work with both `ANCESTOR` and their +explicit token type via `contextStrategy`: -#### Notes +* ArrayTrailingComma — walks to `ARRAY_INIT` +* AvoidNestedBlocks — walks to `SLIST` +* CommentsIndentation — walks to `METHOD_DEF`, `SLIST`, `CLASS_DEF`, `INTERFACE_DEF`, `RECORD_DEF`, `ENUM_DEF` +* DefaultComesLast — walks to `LITERAL_SWITCH` +* DeclarationOrder — walks to `CLASS_DEF`, `INTERFACE_DEF`, `ENUM_DEF` +* EqualsHashCode — walks to `CLASS_DEF` +* FinalLocalVariable — walks to `METHOD_DEF`, `VARIABLE_DEF`, `CTOR_DEF` +* FallThrough — walks to `LITERAL_SWITCH` +* InnerTypeLast — walks to `CLASS_DEF`, `INTERFACE_DEF`, `RECORD_DEF`, `ENUM_DEF` +* RightCurly — walks to `LITERAL_TRY`, `LITERAL_IF` +* VariableDeclarationUsageDistance — walks to `SLIST` -Currently, the following checks will suppress some violations that should not be suppressed when using `supportContextStrategyChecks`. -And you can use `checkNamesForContextStrategyByTokenOrParentSet`, `checkNamesForContextStrategyByTokenOrAncestorSet` -or `neverSuppressedChecks` to get the larger context scope to solve this problem: +The following checks suppress more than expected when using `SELF` scope and need `PARENT`, +an explicit token type, or `neverSuppressedChecks` instead: * ParameterNumberCheck * SuperClone @@ -103,8 +106,8 @@ or `neverSuppressedChecks` to get the larger context scope to solve this problem * OverloadMethodsDeclarationOrder * UnnecessarySemicolonInEnumeration -Also, the following checks will suppress some violations that should not be suppressed when using `supportContextStrategyChecks` or `checkNamesForContextStrategyByTokenOrParentSet`. -So you need use `checkNamesForContextStrategyByTokenOrAncestorSet` or `neverSuppressedChecks` to solve this problem: +The following checks suppress more than expected even with `PARENT` and need an explicit token type +or `neverSuppressedChecks`: * ArrayTrailingComma * AvoidNestedBlocks @@ -117,8 +120,7 @@ So you need use `checkNamesForContextStrategyByTokenOrAncestorSet` or `neverSupp * RightCurly * VariableDeclarationUsageDistance -Then the following checks will suppress some violations that should not be suppressed unless you use -`neverSuppressedChecks`: +The following checks need `neverSuppressedChecks` regardless of scope: * CovariantEquals * EmptyLineSeparator @@ -140,8 +142,7 @@ Then the following checks will suppress some violations that should not be suppr #### Examples -For example, the following configuration fragment directs the Checker to use a SuppressionJavaPatchFilter -with patch file config/file.patch and strategy is `newline`: +Basic setup with `newline` strategy: ```xml @@ -149,8 +150,7 @@ with patch file config/file.patch and strategy is `newline`: ``` -the following configuration fragment directs the Checker to use a SuppressionJavaPatchFilter -with patch file config/file.patch and strategy is `patchedline`: +Using `patchedline` to cover added and changed lines: ```xml @@ -158,29 +158,43 @@ with patch file config/file.patch and strategy is `patchedline`: ``` -the following configuration fragment directs the Checker to use a SuppressionJavaPatchFilter -with patch file config/file.patch, whose strategy is `context`, -support context strategy check `MethodLength` and never suppress checks `EmptyBlock` and `HiddenField`: +Using `context` with the new unified `contextStrategy` property (recommended): ```xml - + + + + ``` -the following configuration fragment directs the Checker to use a SuppressionJavaPatchFilter -with patch file config/file.patch, whose strategy is `context`, -expand `SuperFinalize`'s context scope to parents' node -and expand `EqualsHashCode`, `FinalLocalVariable`'s context scope to ancestors' node : +Migrating from the old three properties to `contextStrategy`: ```xml + - + + + + + + + + + +``` ``` ## PatchFilter Report Setup diff --git a/src/main/java/com/puppycrawl/tools/checkstyle/filters/ContextScope.java b/src/main/java/com/puppycrawl/tools/checkstyle/filters/ContextScope.java new file mode 100644 index 00000000..cff148bd --- /dev/null +++ b/src/main/java/com/puppycrawl/tools/checkstyle/filters/ContextScope.java @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////////////////// +// checkstyle: Checks Java source code and other text files for adherence to a set of rules. +// Copyright (C) 2001-2026 the original author or authors. +// +// 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.filters; + +/** + * AST walk scope used by the {@code contextStrategy} property of + * {@code SuppressionJavaPatchFilter}. + * + * @see SuppressionJavaPatchFilter + * @since 10.x + */ +public enum ContextScope { + + /** + * Match against the violation's own node. No walking needed. + * Replaces {@code supportContextStrategyChecks}. + */ + SELF, + + /** + * Walk up to the immediate parent node. + * Replaces {@code checkNamesForContextStrategyByTokenOrParentSet}. + */ + PARENT, + + /** + * Walk up to the first ancestor matching the built-in token map for that check. + * For custom checks, use an explicit token type instead + * (e.g. {@code FallThrough:LITERAL_SWITCH}). + * Replaces {@code checkNamesForContextStrategyByTokenOrAncestorSet}. + */ + ANCESTOR +} diff --git a/src/main/java/com/puppycrawl/tools/checkstyle/filters/JavaPatchFilterElement.java b/src/main/java/com/puppycrawl/tools/checkstyle/filters/JavaPatchFilterElement.java index ae519c00..e42f77b7 100644 --- a/src/main/java/com/puppycrawl/tools/checkstyle/filters/JavaPatchFilterElement.java +++ b/src/main/java/com/puppycrawl/tools/checkstyle/filters/JavaPatchFilterElement.java @@ -21,6 +21,7 @@ import java.io.File; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -33,7 +34,8 @@ import com.puppycrawl.tools.checkstyle.api.TokenTypes; /** - * This filter element is immutable and processes. + * Immutable filter element that decides whether to suppress a single violation + * for one file in the patch. Used internally by {@code SuppressionJavaPatchFilter}. */ public final class JavaPatchFilterElement implements TreeWalkerFilter { /** @@ -47,7 +49,10 @@ public final class JavaPatchFilterElement implements TreeWalkerFilter { private static final String MAX = "max"; /** - * Mapping between a check and its ancestor token types. + * Default ancestor token types for known checks, used when a check is + * registered with {@link ContextScope#ANCESTOR} but the user has not + * supplied an explicit token type via {@code contextStrategy}. + * For example, {@code FallThrough} maps to {@code LITERAL_SWITCH}. */ private static final Map> CHECK_TO_ANCESTOR_NODES_MAP = new HashMap<>(); @@ -87,23 +92,18 @@ public final class JavaPatchFilterElement implements TreeWalkerFilter { } /** - * Set of checks that support context strategy but need modify violation - * nodes to their parent abstract nodes to get their child nodes. + * Maps each check name (short or full class name) to its context scope. + * Built from the {@code contextStrategy} property, or from the old deprecated + * setters for backward compatibility. */ - private final Set checkNamesForContextStrategyByTokenOrParentSet = - new HashSet<>(); + private final Map contextStrategyMap; /** - * Set of checks that support context strategy but need modify violation - * nodes to their ancestor abstract nodes to get their child nodes. + * Explicit ancestor token types supplied by the user via {@code contextStrategy}, + * for example {@code FallThrough:LITERAL_SWITCH}. Takes priority over + * {@link #CHECK_TO_ANCESTOR_NODES_MAP} when present. */ - private final Set checkNamesForContextStrategyByTokenOrAncestorSet = - new HashSet<>(); - - /** - * Set has user defined Checks that support context strategy. - */ - private final Set supportContextStrategyChecks = new HashSet<>(); + private final Map> userDefinedAncestorTokensMap; /** The String of file names. */ private final String fileName; @@ -122,56 +122,44 @@ public final class JavaPatchFilterElement implements TreeWalkerFilter { private final Strategy strategy; /** - * Constructs a {@code SuppressPatchFilterElement} for a - * file name pattern. + * Creates a filter element for one file in the patch. * - * @param fileNameValue names of filtered files - * @param lineRangeListValue list of line range for line number - * filtering - * @param strategyValue strategy that used - * @param checkNamesForContextStrategyByTokenOrParentSetValue - * user defined Checks that need modify - * violation nodes to their parent - * abstract nodes to get their child - * nodes - * @param checkNamesForContextStrategyByTokenOrAncestorSetValue - * user defined Checks that need modify - * violation nodes to their ancestor - * abstract nodes to get their child - * nodes - * @param supportContextStrategyChecksValue - * user defined Checks that support - * context strategy - * @param neverSuppressedChecksValue set has user defined Checks to never - * suppress if files are touched + * @param fileNameValue file name suffix from the patch header + * @param lineRangeListValue changed line ranges for this file + * @param strategyValue suppression strategy to apply + * @param contextStrategyMapValue check name to scope map; may be {@code null} + * @param userDefinedAncestorTokensMapValue explicit ancestor token types from the + * {@code contextStrategy} property; may be + * {@code null} + * @param neverSuppressedChecksValue checks that should never be suppressed */ public JavaPatchFilterElement(String fileNameValue, List> lineRangeListValue, Strategy strategyValue, - Set - checkNamesForContextStrategyByTokenOrParentSetValue, - Set - checkNamesForContextStrategyByTokenOrAncestorSetValue, - Set - supportContextStrategyChecksValue, - Set - neverSuppressedChecksValue) { + Map contextStrategyMapValue, + Map> userDefinedAncestorTokensMapValue, + Set neverSuppressedChecksValue) { this.fileName = fileNameValue; this.lineRangeList = lineRangeListValue; this.strategy = strategyValue; - if (checkNamesForContextStrategyByTokenOrParentSetValue != null) { - this.checkNamesForContextStrategyByTokenOrParentSet.addAll( - checkNamesForContextStrategyByTokenOrParentSetValue); + this.neverSuppressedChecks = neverSuppressedChecksValue; + final Map contextMap; + if (contextStrategyMapValue == null) { + contextMap = Collections.emptyMap(); + } + else { + contextMap = Collections.unmodifiableMap(new HashMap<>(contextStrategyMapValue)); } - if (checkNamesForContextStrategyByTokenOrAncestorSetValue != null) { - this.checkNamesForContextStrategyByTokenOrAncestorSet.addAll( - checkNamesForContextStrategyByTokenOrAncestorSetValue); + this.contextStrategyMap = contextMap; + final Map> 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.