Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix up always-truthy check for 0n and support negative numbers #60324

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

kirkwaiblinger
Copy link

Fixes #60320

@typescript-bot typescript-bot added the For Uncommitted Bug PR for untriaged, rejected, closed or missing bug label Oct 22, 2024
@kirkwaiblinger
Copy link
Author

@microsoft-github-policy-service agree

@jakebailey
Copy link
Member

@typescript-bot test it

@typescript-bot
Copy link
Collaborator

typescript-bot commented Oct 22, 2024

Starting jobs; this comment will be updated as builds start and complete.

Command Status Results
test top400 ✅ Started ✅ Results
user test this ✅ Started ✅ Results
run dt ✅ Started ✅ Results
perf test this faster ✅ Started 👀 Results

@typescript-bot
Copy link
Collaborator

@jakebailey Here are the results of running the user tests with tsc comparing main and refs/pull/60324/merge:

Everything looks good!

@typescript-bot
Copy link
Collaborator

Hey @jakebailey, the results of running the DT tests are ready.

Everything looks the same!

You can check the log here.

@typescript-bot
Copy link
Collaborator

@jakebailey
The results of the perf run you requested are in!

Here they are:

tsc

Comparison Report - baseline..pr
Metric baseline pr Delta Best Worst p-value
Compiler-Unions - node (v18.15.0, x64)
Errors 31 31 ~ ~ ~ p=1.000 n=6
Symbols 62,340 62,340 ~ ~ ~ p=1.000 n=6
Types 50,379 50,379 ~ ~ ~ p=1.000 n=6
Memory used 196,487k (± 0.02%) 195,794k (± 0.79%) ~ 192,647k 196,506k p=0.065 n=6
Parse Time 1.59s (± 2.15%) 1.61s (± 1.20%) ~ 1.59s 1.64s p=0.413 n=6
Bind Time 0.87s (± 0.73%) 0.87s (± 1.45%) ~ 0.85s 0.88s p=0.799 n=6
Check Time 11.74s (± 0.31%) 11.73s (± 0.34%) ~ 11.69s 11.79s p=0.461 n=6
Emit Time 3.33s (± 1.37%) 3.33s (± 3.34%) ~ 3.27s 3.56s p=0.290 n=6
Total Time 17.53s (± 0.26%) 17.55s (± 0.65%) ~ 17.44s 17.77s p=0.809 n=6
angular-1 - node (v18.15.0, x64)
Errors 33 33 ~ ~ ~ p=1.000 n=6
Symbols 947,886 947,886 ~ ~ ~ p=1.000 n=6
Types 410,840 410,840 ~ ~ ~ p=1.000 n=6
Memory used 1,224,593k (± 0.00%) 1,224,615k (± 0.00%) ~ 1,224,586k 1,224,626k p=0.574 n=6
Parse Time 6.63s (± 0.70%) 6.63s (± 0.77%) ~ 6.58s 6.70s p=0.935 n=6
Bind Time 1.89s (± 0.43%) 1.89s (± 0.40%) ~ 1.88s 1.90s p=0.729 n=6
Check Time 31.76s (± 0.13%) 31.83s (± 0.49%) ~ 31.58s 32.04s p=0.199 n=6
Emit Time 15.15s (± 0.55%) 15.18s (± 0.30%) ~ 15.11s 15.23s p=0.573 n=6
Total Time 55.42s (± 0.21%) 55.52s (± 0.41%) ~ 55.21s 55.84s p=0.688 n=6
mui-docs - node (v18.15.0, x64)
Errors 0 0 ~ ~ ~ p=1.000 n=6
Symbols 2,494,498 2,494,498 ~ ~ ~ p=1.000 n=6
Types 908,298 908,298 ~ ~ ~ p=1.000 n=6
Memory used 2,307,084k (± 0.00%) 2,307,079k (± 0.00%) ~ 2,307,047k 2,307,138k p=0.521 n=6
Parse Time 9.33s (± 0.19%) 9.32s (± 0.10%) ~ 9.31s 9.33s p=0.222 n=6
Bind Time 2.15s (± 0.64%) 2.14s (± 0.24%) ~ 2.14s 2.15s p=0.794 n=6
Check Time 75.00s (± 0.56%) 74.85s (± 0.36%) ~ 74.45s 75.20s p=0.471 n=6
Emit Time 0.29s (± 9.52%) 0.29s (± 3.77%) ~ 0.27s 0.30s p=0.668 n=6
Total Time 86.77s (± 0.53%) 86.60s (± 0.31%) ~ 86.21s 86.96s p=0.471 n=6
self-build-src - node (v18.15.0, x64)
Errors 0 0 ~ ~ ~ p=1.000 n=6
Symbols 1,258,033 1,258,036 +3 (+ 0.00%) ~ ~ p=0.001 n=6
Types 266,263 266,266 +3 (+ 0.00%) ~ ~ p=0.001 n=6
Memory used 2,422,650k (± 0.02%) 2,422,476k (± 0.01%) ~ 2,421,915k 2,422,652k p=0.810 n=6
Parse Time 5.20s (± 0.55%) 5.21s (± 0.86%) ~ 5.13s 5.26s p=0.423 n=6
Bind Time 1.94s (± 0.62%) 1.94s (± 0.27%) ~ 1.94s 1.95s p=0.107 n=6
Check Time 35.50s (± 0.48%) 35.46s (± 0.07%) ~ 35.42s 35.49s p=0.471 n=6
Emit Time 3.02s (± 2.54%) 3.02s (± 1.35%) ~ 2.97s 3.06s p=1.000 n=6
Total Time 45.67s (± 0.43%) 45.63s (± 0.14%) ~ 45.55s 45.73s p=0.471 n=6
self-build-src-public-api - node (v18.15.0, x64)
Errors 0 0 ~ ~ ~ p=1.000 n=6
Symbols 1,258,033 1,258,036 +3 (+ 0.00%) ~ ~ p=0.001 n=6
Types 266,263 266,266 +3 (+ 0.00%) ~ ~ p=0.001 n=6
Memory used 2,885,229k (±12.80%) 2,499,715k (± 0.03%) ~ 2,498,788k 2,500,388k p=0.093 n=6
Parse Time 6.72s (± 2.60%) 6.51s (± 0.60%) 🟩-0.21s (- 3.15%) 6.43s 6.54s p=0.031 n=6
Bind Time 2.14s (± 1.81%) 2.14s (± 1.60%) ~ 2.10s 2.19s p=0.936 n=6
Check Time 43.03s (± 0.44%) 43.18s (± 0.72%) ~ 42.75s 43.66s p=0.230 n=6
Emit Time 3.58s (± 2.57%) 3.51s (± 3.06%) ~ 3.37s 3.69s p=0.172 n=6
Total Time 55.46s (± 0.63%) 55.36s (± 0.70%) ~ 54.91s 56.01s p=0.689 n=6
self-compiler - node (v18.15.0, x64)
Errors 0 0 ~ ~ ~ p=1.000 n=6
Symbols 261,789 261,792 +3 (+ 0.00%) ~ ~ p=0.001 n=6
Types 106,511 106,514 +3 (+ 0.00%) ~ ~ p=0.001 n=6
Memory used 438,565k (± 0.01%) 438,618k (± 0.01%) ~ 438,555k 438,693k p=0.066 n=6
Parse Time 2.89s (± 0.47%) 2.88s (± 0.57%) ~ 2.86s 2.90s p=0.871 n=6
Bind Time 1.10s (± 0.47%) 1.10s ~ ~ ~ p=0.174 n=6
Check Time 15.70s (± 0.28%) 15.75s (± 0.43%) ~ 15.66s 15.84s p=0.221 n=6
Emit Time 1.33s (± 1.33%) 1.30s (± 1.26%) ~ 1.29s 1.33s p=0.070 n=6
Total Time 21.01s (± 0.23%) 21.04s (± 0.32%) ~ 20.94s 21.12s p=0.375 n=6
ts-pre-modules - node (v18.15.0, x64)
Errors 68 68 ~ ~ ~ p=1.000 n=6
Symbols 225,919 225,919 ~ ~ ~ p=1.000 n=6
Types 94,415 94,415 ~ ~ ~ p=1.000 n=6
Memory used 371,056k (± 0.02%) 371,105k (± 0.02%) ~ 371,030k 371,189k p=0.230 n=6
Parse Time 2.89s (± 1.13%) 2.89s (± 0.90%) ~ 2.85s 2.92s p=0.687 n=6
Bind Time 1.59s (± 1.10%) 1.60s (± 1.60%) ~ 1.58s 1.65s p=1.000 n=6
Check Time 16.34s (± 0.58%) 16.38s (± 0.23%) ~ 16.35s 16.43s p=0.375 n=6
Emit Time 0.00s 0.00s ~ ~ ~ p=1.000 n=6
Total Time 20.83s (± 0.63%) 20.87s (± 0.28%) ~ 20.78s 20.93s p=0.747 n=6
vscode - node (v18.15.0, x64)
Errors 3 3 ~ ~ ~ p=1.000 n=6
Symbols 3,126,375 3,126,375 ~ ~ ~ p=1.000 n=6
Types 1,077,686 1,077,686 ~ ~ ~ p=1.000 n=6
Memory used 3,218,555k (± 0.01%) 3,218,230k (± 0.01%) ~ 3,217,943k 3,218,790k p=0.230 n=6
Parse Time 14.07s (± 0.75%) 14.09s (± 0.62%) ~ 14.00s 14.23s p=0.688 n=6
Bind Time 4.48s (± 0.62%) 4.51s (± 2.33%) ~ 4.45s 4.72s p=0.684 n=6
Check Time 87.29s (± 2.94%) 85.51s (± 1.20%) ~ 84.85s 87.58s p=0.173 n=6
Emit Time 26.36s (± 7.23%) 26.20s (± 3.83%) ~ 25.02s 27.25s p=0.575 n=6
Total Time 132.21s (± 1.20%) 130.31s (± 1.36%) ~ 128.50s 133.37s p=0.128 n=6
webpack - node (v18.15.0, x64)
Errors 0 0 ~ ~ ~ p=1.000 n=6
Symbols 286,375 286,375 ~ ~ ~ p=1.000 n=6
Types 116,078 116,078 ~ ~ ~ p=1.000 n=6
Memory used 437,145k (± 0.01%) 437,107k (± 0.04%) ~ 436,990k 437,439k p=0.230 n=6
Parse Time 5.06s (± 0.57%) 5.09s (± 0.71%) ~ 5.04s 5.14s p=0.257 n=6
Bind Time 2.15s (± 1.15%) 2.16s (± 1.25%) ~ 2.13s 2.19s p=0.367 n=6
Check Time 22.89s (± 0.42%) 22.85s (± 0.47%) ~ 22.75s 22.99s p=0.377 n=6
Emit Time 0.00s 0.00s ~ ~ ~ p=1.000 n=6
Total Time 30.10s (± 0.41%) 30.11s (± 0.34%) ~ 30.02s 30.26s p=0.936 n=6
xstate-main - node (v18.15.0, x64)
Errors 3 3 ~ ~ ~ p=1.000 n=6
Symbols 543,130 543,130 ~ ~ ~ p=1.000 n=6
Types 181,889 181,889 ~ ~ ~ p=1.000 n=6
Memory used 485,513k (± 0.01%) 485,534k (± 0.01%) ~ 485,491k 485,574k p=0.128 n=6
Parse Time 4.17s (± 0.81%) 4.18s (± 0.54%) ~ 4.15s 4.21s p=0.746 n=6
Bind Time 1.46s (± 0.28%) 1.46s (± 0.56%) ~ 1.45s 1.47s p=0.206 n=6
Check Time 23.74s (± 0.33%) 23.77s (± 0.34%) ~ 23.65s 23.86s p=0.575 n=6
Emit Time 0.00s 0.00s ~ ~ ~ p=1.000 n=6
Total Time 29.37s (± 0.27%) 29.42s (± 0.29%) ~ 29.31s 29.52s p=0.520 n=6
System info unknown
Hosts
  • node (v18.15.0, x64)
Scenarios
  • Compiler-Unions - node (v18.15.0, x64)
  • angular-1 - node (v18.15.0, x64)
  • mui-docs - node (v18.15.0, x64)
  • self-build-src - node (v18.15.0, x64)
  • self-build-src-public-api - node (v18.15.0, x64)
  • self-compiler - node (v18.15.0, x64)
  • ts-pre-modules - node (v18.15.0, x64)
  • vscode - node (v18.15.0, x64)
  • webpack - node (v18.15.0, x64)
  • xstate-main - node (v18.15.0, x64)
Benchmark Name Iterations
Current pr 6
Baseline baseline 6

Developer Information:

Download Benchmarks

@typescript-bot
Copy link
Collaborator

@jakebailey Here are the results of running the top 400 repos with tsc comparing main and refs/pull/60324/merge:

Everything looks good!

@kirkwaiblinger
Copy link
Author

@jakebailey should I undraft? I'm unclear since the issue hasn't been accepted

@jakebailey
Copy link
Member

The issue is unlabeled so it's actually just un-triaged; feel free to undraft.

@kirkwaiblinger kirkwaiblinger marked this pull request as ready for review October 24, 2024 18:33

// Not OK; always falsy.
if (0n) { }
Copy link
Member

Choose a reason for hiding this comment

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

Please add // @target: esnext to the top of this file to remove the unnecessary errors in the baseline

Copy link
Author

Choose a reason for hiding this comment

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

changed 👍

return PredicateSemantics.Always;
// handle +123, -123, -123n, etc.
case SyntaxKind.PrefixUnaryExpression:
const prefixUnaryExpression = node as PrefixUnaryExpression;
Copy link
Member

Choose a reason for hiding this comment

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

Seems like this could just be something like

if (operand.kind === SyntaxKind.NumericLiteral && (operator === SyntaxKind.MinusToken || operator === SyntaxKind.PlusToken)) {
  return getTruthySemantics(operand);
}

? We shouldn't be duplicating all the logic of the numeric case.

Copy link
Author

@kirkwaiblinger kirkwaiblinger Oct 31, 2024

Choose a reason for hiding this comment

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

Fair question. I wrote it this way to avoid a few bugs....

if (1) {} // this is intentionally exempted from the check, BUT
if (-1) {} // should report. Would NOT with the recursion.
if (+1) {} // IMO should also report, since this is clearly intended as a number, not as a shorthand for `if (true)`.

// (ditto for the following)
if (0) {} // intentionally exempted from reporting
if (+0) {} // should report IMO. This is clearly intended as a number, and not as a shorthand for `if (false)`.
if (-0) {} // see previous.

Note that the branches are therefore different:

            case SyntaxKind.NumericLiteral:
                // Allow `while(0)` or `while(1)`
                if ((node as NumericLiteral).text === "0" || (node as NumericLiteral).text === "1") {
                    return PredicateSemantics.Sometimes; // <-- special case; while(0) or while(1).
                }
                return PredicateSemantics.Always;

vs

                if (operand.kind === SyntaxKind.NumericLiteral && (operator === SyntaxKind.MinusToken || operator === SyntaxKind.PlusToken)) {
                    if ((operand as NumericLiteral).text === "0") {
                        return PredicateSemantics.Never; // <-- just a numeric zero
                    }
                    else {
                        return PredicateSemantics.Always;
                    }
                }

I think the bigint case is ok to recurse on the unary - operator. However, we just need to be sure it doesn't get hit by recursion from the unary + operator, since if (+0n) isn't falsy. It's an exception!

FWIW, note that ESLint does recurse in the bigint unary + case, and therefore does incur this bug.

So between

a) recurse in these cases, and add context to the recursion to be able to handle the edge cases.
b) recurse in these cases, and accept some IMO faulty edge cases
c) Not recurse at all, get the edge cases right, but ignore things like if (-(-(-3))) {}

I chose c) for simplicity, and added test cases around the while (+0), etc. cases.

But LMK if you'd like a different approach!

Note that within c) there's one opportunity that I see for deduping, which is to extract the identical bigint checks to a function.

        function getBigIntLiteralTruthySemantics (node: BigIntLiteral): PredicateSemantics {
            return node.text === "0n" ? PredicateSemantics.Never : PredicateSemantics.Always;

        }

        switch (node.kind) {
            case SyntaxKind.BigIntLiteral:
                    // unlike `while(0)`, `while (0n)` is not idiomatic.
                return getBigIntLiteralTruthySemantics(node as BigIntLiteral);
            // handle +123, -123, -123n, etc.
            case SyntaxKind.PrefixUnaryExpression:
                const prefixUnaryExpression = node as PrefixUnaryExpression;
                const { operator, operand } = prefixUnaryExpression;
                if (operand.kind === SyntaxKind.BigIntLiteral && operator === SyntaxKind.MinusToken) {
                    return getBigIntLiteralTruthySemantics(operand as BigIntLiteral);
                } else 
                // etc

@typescript-bot typescript-bot added For Backlog Bug PRs that fix a backlog bug and removed For Uncommitted Bug PR for untriaged, rejected, closed or missing bug labels Oct 31, 2024
@kirkwaiblinger
Copy link
Author

@microsoft-github-policy-service agree

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
For Backlog Bug PRs that fix a backlog bug
Projects
Status: Not started
Development

Successfully merging this pull request may close these issues.

bug: error message says 0n is truthy
4 participants