Skip to content

Commit c9f441e

Browse files
committed
fix: implement precise filtering for bracketed sub-tags
1 parent efe2751 commit c9f441e

File tree

2 files changed

+84
-14
lines changed

2 files changed

+84
-14
lines changed

src/ansiblelint/rules/__init__.py

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -504,27 +504,52 @@ def run(
504504
for rule in self.rules:
505505
if rule.id == "syntax-check":
506506
continue
507+
508+
is_targeted = any(t.startswith(f"{rule.id}[") for t in tags)
509+
is_skipped = any(t.startswith(f"{rule.id}[") for t in skip_list)
510+
511+
# rule selection logic
507512
if (
508513
not tags
509514
or rule.has_dynamic_tags
510515
or not set(rule.tags).union([rule.id]).isdisjoint(tags)
516+
or is_targeted
511517
):
512-
if tags and set(rule.tags).union(list(rule.ids().keys())).isdisjoint(
513-
tags,
518+
# specific tag targeting override
519+
if (
520+
tags
521+
and not is_targeted
522+
and set(rule.tags).union(list(rule.ids().keys())).isdisjoint(tags)
514523
):
515-
_logger.debug("Skipping rule %s", rule.id)
516-
else:
517-
_logger.debug("Running rule %s", rule.id)
518-
rule_definition = set(rule.tags)
519-
rule_definition.add(rule.id)
520-
if set(rule_definition).isdisjoint(skip_list):
521-
matches.extend(rule.getmatches(file))
522-
else:
523-
_logger.debug("Skipping rule %s", rule.id)
524+
continue
524525

525-
# some rules can produce matches with tags that are inside our
526-
# skip_list, so we need to cleanse the matches
527-
matches = [m for m in matches if m.tag not in skip_list]
526+
# rule-level skip check
527+
rule_definition = set(rule.tags) | {rule.id}
528+
if rule_definition.isdisjoint(skip_list) and not is_skipped:
529+
matches.extend(rule.getmatches(file))
530+
531+
if tags or skip_list:
532+
filtered_matches = []
533+
for m in matches:
534+
# skip if exact tag is in skip_list or broad rule id is skipped
535+
if m.tag in skip_list or m.rule.id in skip_list:
536+
continue
537+
538+
# inclusion logic (if tags are provided)
539+
if tags:
540+
if (
541+
m.tag in tags
542+
or m.rule.id in tags
543+
or (
544+
not set(m.rule.tags).isdisjoint(tags)
545+
and not any(t.startswith(f"{m.rule.id}[") for t in tags)
546+
)
547+
):
548+
filtered_matches.append(m)
549+
else:
550+
# no tags requested, so keep everything that wasn't skipped
551+
filtered_matches.append(m)
552+
matches = filtered_matches
528553

529554
return matches
530555

test/test_rules_collection.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,3 +179,48 @@ def test_rules_id_format(config_options: Options, app: App) -> None:
179179
assert "yaml" in keys, "yaml rule is missing"
180180
assert len(rules) == 51 # update this number when adding new rules!
181181
assert len(keys) == len(rules), "Duplicate rule ids?"
182+
183+
184+
def test_tag_inclusion(
185+
test_rules_collection: RulesCollection,
186+
ematchtestfile: Lintable,
187+
) -> None:
188+
"""Test that bracketed sub-tags are treated surgically for inclusion."""
189+
all_matches = test_rules_collection.run(ematchtestfile)
190+
191+
if not all_matches:
192+
pytest.fail("No matches found in ematchtestfile!")
193+
194+
target_tag = all_matches[0].tag
195+
matches = test_rules_collection.run(ematchtestfile, tags={target_tag})
196+
197+
assert len(matches) > 0
198+
for m in matches:
199+
assert m.tag == target_tag
200+
201+
202+
def test_tag_exclusion(
203+
test_rules_collection: RulesCollection,
204+
ematchtestfile: Lintable,
205+
) -> None:
206+
"""Test that bracketed sub-tags are treated surgically for exclusion."""
207+
target_tag = "TEST0001[BANNED]"
208+
209+
matches = test_rules_collection.run(ematchtestfile, skip_list=[target_tag])
210+
211+
tag_results = [m.tag for m in matches]
212+
assert target_tag not in tag_results
213+
214+
215+
def test_category_tag_override(
216+
test_rules_collection: RulesCollection,
217+
ematchtestfile: Lintable,
218+
) -> None:
219+
"""Test that specific sub-tag requests override broad category inclusion."""
220+
matches = test_rules_collection.run(
221+
ematchtestfile, tags={"test1", "TEST0001[BANNED]"}
222+
)
223+
224+
for m in matches:
225+
if m.rule.id == "TEST0001":
226+
assert m.tag == "TEST0001[BANNED]"

0 commit comments

Comments
 (0)