Fix selector-no-qualifying-type false positives for :has()#9106
Fix selector-no-qualifying-type false positives for :has()#9106ragini-pandey wants to merge 9 commits into
selector-no-qualifying-type false positives for :has()#9106Conversation
Type selectors inside :has() do not qualify the current element — they apply conditions on descendant nodes. Exclude :has() from the SELECTOR_CONTAINING_PSEUDO_CLASSES set so inner type selectors are not merged into the outer compound selector for qualifying-type checks. Fixes stylelint#9103
🦋 Changeset detectedLatest commit: f0249f8 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
This PR is packaged and the instant preview is available (f0249f8). View the demo website. Install it locally: npm i -D https://pkg.pr.new/stylelint@d3547e9 |
jeddy3
left a comment
There was a problem hiding this comment.
@ragini-pandey Thanks for making a start on this.
I've requested a change to the tests and posed a question we'll want to discuss, giving people time to chime in.
…evaluate :has() inner selectors independently
| if (isHasPseudoClass(node)) { | ||
| const hasPseudo = /** @type {selectorParser.Pseudo} */ (node); | ||
|
|
||
| // Add :has() to the outer compound without merging inner selectors | ||
| currentCompoundSelectors.forEach((compoundSelector) => { | ||
| compoundSelector.push(hasPseudo); | ||
| }); | ||
|
|
||
| // Evaluate inner selectors independently | ||
| hasPseudo.each((childSelector) => { | ||
| compoundSelectors.push(...groupByCompoundSelectors(childSelector)); | ||
| }); | ||
|
|
||
| return; | ||
| } |
There was a problem hiding this comment.
This is incorrect even if it is very hard to observe.
I think it also exposes an existing issue in the code I wrote for groupByCompoundSelectors.
@jeddy3 Can we hold of on merging this one?
I will try to make time to investigate.
There was a problem hiding this comment.
I will try to make time to investigate.
That would be fantastic. I don't believe it's a popular rule or use case, so in no way pressing.
There was a problem hiding this comment.
I think I have a good working fix for this issue but splitting out this code into a helper and adding some unit tests will help to verify.
|
(I've removed the blocked label as we reserve that for external factors or dependencies. A maintainer trying to find time is just normal in OSS.) |
|
Thank you for working on this @ragini-pandey. |
Description
Fixes #9103
What's the problem?
The
selector-no-qualifying-typerule incorrectly flagged type selectors inside:has()as qualifying type selectors. For example:Why is it a false positive?
:has()is not a grouping pseudo-class (unlike:is()or:where()). Its argument selects descendant (or relational) elements, not the current element. Soinputinside.form-field:has(input:disabled)is not qualifying.form-fieldIt expresses a condition on child nodes.What's the fix?
In
SELECTOR_CONTAINING_PSEUDO_CLASSES,:haswas included (vialogicalCombinationsPseudoClasses), which causedgroupByCompoundSelectorsto merge the inner selectors of:has()with the outer compound selector. This made inner type selectors appear to be at the same compound level as the outer class/id/attribute — triggering a false positive.The fix excludes
hasfromSELECTOR_CONTAINING_PSEUDO_CLASSESso its inner selectors are not merged into the outer compound. Type selectors outside:has()(e.g.,a.foo:has(.bar)) are still correctly flagged.Tests
Added four
accepttest cases covering the patterns from the issue report.