Skip to content

New Linter rule: MisleadingCondition#2184

Open
tapple wants to merge 14 commits intoluau-lang:masterfrom
tapple:if-validator
Open

New Linter rule: MisleadingCondition#2184
tapple wants to merge 14 commits intoluau-lang:masterfrom
tapple:if-validator

Conversation

@tapple
Copy link

@tapple tapple commented Jan 11, 2026

The linter now complains about condition expressions that are not one of:

  1. a boolean
  2. a supertype of nil

The linter considers a condition expression to be one of:

  1. The condition expression of an if statement
  2. The condition expression of an if expression
  3. The left expression of an and operator
  4. The left expression of an or operator

See the unit test for error messages

@tapple tapple changed the title If validator Linter now warns about non-booleans used in condition expressions Jan 11, 2026
@tapple tapple changed the title Linter now warns about non-booleans used in condition expressions New Linter rule: MisleadingConditions Jan 11, 2026
@jLn0n
Copy link

jLn0n commented Jan 11, 2026

this should be documented on a rfc and approved first before creating a new linter rule i think

@WolfGangS
Copy link

this should be documented on a rfc and approved first before creating a new linter rule i think

Contribute suggests rfcs only for language changes, this wouldn't do that, and an issue has been created, though maybe some more time should have passed between issue and pr.

On the rule in general, I think it's extremely useful for devs unfamiliar with lua. Nearly every other language has at least 0 as false, and many have a concept of falsey for things like empty strings or tables.

As lua and by extension luau doesn't have a broad falsey concept, this lint would catch a very common footgun for newer users.

@@ -19,7 +19,7 @@ TEST_CASE_FIXTURE(Fixture, "CleanCode")
{
Copy link
Author

@tapple tapple Jan 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the 2 remaining unit test failures are due to a typechecker bug:

@JohnnyMorganz
Copy link
Contributor

Can't comment on all the cases, but I like the idea. I think this is also valuable for a case where you have a function foo that returns a boolean, and you accidentally write if foo then instead of if foo() then, which will always be true. This has caused prod incidents before

@tapple tapple changed the title New Linter rule: MisleadingConditions New Linter rule: MisleadingCondition Jan 13, 2026
@aatxe
Copy link
Member

aatxe commented Jan 13, 2026

this should be documented on a rfc and approved first before creating a new linter rule i think

The linter is not part of the language itself, and frankly will probably be sunset in favor of the linter we're writing in lute since that allows programmers to author their own lints in luau instead of writing C++ to lint luau. Even if it continues to exist as well, it's not something we've treated as requiring an RFC.

{
const char * msg;
bool negated;
if (!checkCondition(node->condition, &msg, &negated))
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: also check the first argument to assert()

Copy link
Author

@tapple tapple Jan 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or maybe don't. An assert evaluating false is usually understood to be a bug. And the typechecker should often be able to catch the same bugs (at leasts the asserts that assert not nil). So nearly every time this linter rule flagged an assert, it would be a false warning.

also, asserts are pretty specialized. this linter rule is meant to catch common, stupid errors. And I don't think the linter even could distinguish between "asserts that meant to check a boolean but forgot a comparison operator" and "asserts that are checking for non-nil and the type-checker agrees". Maybe checking for intersection types, but either way, it's different enough that it should be a different linter rule

Copy link
Member

@aatxe aatxe Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An assert evaluating false is usually understood to be a bug.

I don't think this is true at all. We use things like assert(false) or assert("this message to read about an unreachable assumption" && false) all the time to mark branches that we expect to be unreachable.

if (isBoolean(*type))
return true; // boolean is a valid condition
if (isOptional(*type))
return true; // anything that can be nil is a valid condition
Copy link
Author

@tapple tapple Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

using this on my own code, I've only found one pattern so far where I can't rewrite the code to shut this linter rule up and the linter rule seems wrong:

local list: {number} = {1, 2, 3}
local n5 = list[5] or 5 -- Misleading Condition

direct indexing can return nil if the list is too short, and the typechecker can't detect that

I think I should handle this case specifically

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Linter should check for non-booleans in condition expressions

5 participants