Skip to content

Commit d250d62

Browse files
Fix not keyword silently ignored when used without parentheses in conditions (#4421)
* Initial plan * Fix: not keyword now works without parentheses in guard conditions Previously, `boolean(not false)` silently ignored the `not` keyword while `boolean(not (false))` worked correctly. The `negatedCondition` parser function consumed the `not` keyword but only tried `parenthesisCondition`, which requires `(`. With no parens, it returned undefined with `not` already consumed, causing silent skip. Fix: fall back to `atomicCondition` when `parenthesisCondition` fails, allowing both `not false` and `not (false)` to work consistently. Co-authored-by: matthew-dean <[email protected]> * Restrict not-without-parens to simple values only (keywords/variables) Complex conditions like `not 2 < 1` still require parentheses, keeping alignment with CSS media query syntax. Only simple bare values (keywords, variables, quoted strings) are allowed after `not` without parens: `not false`, `not @var`. Remove the `boolean(not 2 < 1)` test case that relied on the broader atomicCondition fallback. Co-authored-by: matthew-dean <[email protected]> --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: matthew-dean <[email protected]> Co-authored-by: Matthew Dean <[email protected]>
1 parent 2958e9d commit d250d62

5 files changed

Lines changed: 38 additions & 1 deletion

File tree

packages/less/lib/less/parser/parser.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2438,8 +2438,18 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
24382438
const result = this.parenthesisCondition(needsParens);
24392439
if (result) {
24402440
result.negate = !result.negate;
2441+
return result;
2442+
}
2443+
2444+
// Allow simple bare values (keyword/variable) without parens,
2445+
// e.g., `not false` or `not @var`.
2446+
// Complex conditions (comparisons, function calls) require parentheses.
2447+
const entities = this.entities;
2448+
const index = parserInput.i;
2449+
const a = entities.keyword() || entities.variable() || entities.quoted() || entities.mixinLookup();
2450+
if (a) {
2451+
return new(tree.Condition)('=', a, new(tree.Keyword)('true'), index + currentIndex, true);
24412452
}
2442-
return result;
24432453
}
24442454
},
24452455
parenthesisCondition: function (needsParens) {

packages/test-data/tests-unit/functions/functions.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,8 @@ html {
224224
a: true;
225225
b: false;
226226
c: false;
227+
d: true;
228+
e: false;
227229
}
228230
#if {
229231
a: 1;
@@ -236,6 +238,8 @@ html {
236238
i: 6;
237239
j: 8;
238240
k: 1;
241+
m: 1;
242+
n: 2;
239243
l: black;
240244
/* results in void */
241245
color: green;

packages/test-data/tests-unit/functions/functions.less

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,9 @@ html {
256256
a: boolean(not(2 < 1));
257257
b: boolean(not(2 > 1) and (true));
258258
c: boolean(not(boolean(true)));
259+
// not without parentheses (should behave the same as with parentheses)
260+
d: boolean(not false);
261+
e: boolean(not true);
259262
}
260263

261264
#if {
@@ -271,6 +274,9 @@ html {
271274
i: if(true and isnumber(6), 6, 8);
272275
j: if(not(true) and true, 6, 8);
273276
k: if(true or true, 1);
277+
// not without parentheses
278+
m: if(not false, 1, 2);
279+
n: if(not true, 1, 2);
274280

275281
// see: https://github.com/less/less.js/issues/3371
276282
@some: foo;

packages/test-data/tests-unit/mixins-guards/mixins-guards.css

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,3 +209,9 @@
209209
no-parenthesis: evaluated true 4;
210210
with-parenthesis: evaluated true;
211211
}
212+
.test-not-noparens1 {
213+
content: "not without parens true.";
214+
}
215+
.test-not-noparens2 {
216+
content: "not without parens false.";
217+
}

packages/test-data/tests-unit/mixins-guards/mixins-guards.less

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,3 +356,14 @@
356356
.orderOfEvaluation(true, true, false);
357357
}
358358

359+
// not without parentheses should work the same as not with parentheses
360+
.test-not-noparens (@a) when not @a {
361+
content: "not without parens false.";
362+
}
363+
.test-not-noparens (@a) when (@a) {
364+
content: "not without parens true.";
365+
}
366+
367+
.test-not-noparens1 { .test-not-noparens(true) }
368+
.test-not-noparens2 { .test-not-noparens(false) }
369+

0 commit comments

Comments
 (0)