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

feat(typescript-estree): throw on invalid update expressions #7202

Merged
merged 12 commits into from Jan 4, 2024

Conversation

Josh-Cena
Copy link
Member

@Josh-Cena Josh-Cena commented Jul 12, 2023

PR Checklist

Overview

We should be able to report on things like a() = 1 and 1 = 1 as well, but I didn't do this because things like { a } = 1 makes it a little more complicated.

@typescript-eslint
Copy link
Contributor

Thanks for the PR, @Josh-Cena!

typescript-eslint is a 100% community driven project, and we are incredibly grateful that you are contributing to that community.

The core maintainers work on this in their personal time, so please understand that it may not be possible for them to review your work immediately.

Thanks again!


🙏 Please, if you or your company is finding typescript-eslint valuable, help us sustain the project by sponsoring it transparently on https://opencollective.com/typescript-eslint.

@netlify
Copy link

netlify bot commented Jul 12, 2023

Deploy Preview for typescript-eslint ready!

Name Link
🔨 Latest commit d6d6d70
🔍 Latest deploy log https://app.netlify.com/sites/typescript-eslint/deploys/657a67374fea510008a9a791
😎 Deploy Preview https://deploy-preview-7202--typescript-eslint.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
1 paths audited
Performance: 96 (🔴 down 1 from production)
Accessibility: 100 (no change from production)
Best Practices: 92 (no change from production)
SEO: 98 (no change from production)
PWA: 80 (no change from production)
View the detailed breakdown and full score reports

To edit notification comments on pull requests, go to your Netlify site configuration.

@bradzacher bradzacher added the enhancement New feature or request label Jul 17, 2023
@bradzacher
Copy link
Member

bradzacher commented Jul 17, 2023

I had a look at the TS codebase and it's a bit messy with regard to this class of errors.

Here's a few snippets I can find:

I can't link the checker.ts file in GH cos it's too big but here are the relevant lines:

    function checkReferenceExpression(expr: Expression, invalidReferenceMessage: DiagnosticMessage, invalidOptionalChainMessage: DiagnosticMessage): boolean {
        // References are combinations of identifiers, parentheses, and property accesses.
        const node = skipOuterExpressions(expr, OuterExpressionKinds.Assertions | OuterExpressionKinds.Parentheses);
        if (node.kind !== SyntaxKind.Identifier && !isAccessExpression(node)) {
            error(expr, invalidReferenceMessage);
            return false;
        }
        if (node.flags & NodeFlags.OptionalChain) {
            error(expr, invalidOptionalChainMessage);
            return false;
        }
        return true;
    }
export function isAccessExpression(node: Node): node is AccessExpression {
    return node.kind === SyntaxKind.PropertyAccessExpression || node.kind === SyntaxKind.ElementAccessExpression;
}

and

    function checkReferenceAssignment(target: Expression, sourceType: Type, checkMode?: CheckMode): Type {
        const targetType = checkExpression(target, checkMode);
        const error = target.parent.kind === SyntaxKind.SpreadAssignment ?
            Diagnostics.The_target_of_an_object_rest_assignment_must_be_a_variable_or_a_property_access :
            Diagnostics.The_left_hand_side_of_an_assignment_expression_must_be_a_variable_or_a_property_access;
        const optionalError = target.parent.kind === SyntaxKind.SpreadAssignment ?
            Diagnostics.The_target_of_an_object_rest_assignment_may_not_be_an_optional_property_access :
            Diagnostics.The_left_hand_side_of_an_assignment_expression_may_not_be_an_optional_property_access;
        if (checkReferenceExpression(target, error, optionalError)) {
            checkTypeAssignableToAndOptionallyElaborate(sourceType, targetType, target, target);
        }
        if (isPrivateIdentifierPropertyAccessExpression(target)) {
            checkExternalEmitHelpers(target.parent, ExternalEmitHelpers.ClassPrivateFieldSet);
        }
        return sourceType;
    }
    function checkReferenceExpression(expr: Expression, invalidReferenceMessage: DiagnosticMessage, invalidOptionalChainMessage: DiagnosticMessage): boolean {
        // References are combinations of identifiers, parentheses, and property accesses.
        const node = skipOuterExpressions(expr, OuterExpressionKinds.Assertions | OuterExpressionKinds.Parentheses);
        if (node.kind !== SyntaxKind.Identifier && !isAccessExpression(node)) {
            error(expr, invalidReferenceMessage);
            return false;
        }
        if (node.flags & NodeFlags.OptionalChain) {
            error(expr, invalidOptionalChainMessage);
            return false;
        }
        return true;
    }
export function isPrivateIdentifierPropertyAccessExpression(node: Node): node is PrivateIdentifierPropertyAccessExpression {
    return isPropertyAccessExpression(node) && isPrivateIdentifier(node.name);
}

Copy link
Member

@JoshuaKGoldberg JoshuaKGoldberg left a comment

Choose a reason for hiding this comment

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

LGTM - glad you're diving into this @Josh-Cena!

@bradzacher - since you commented in the issue I'll wait for your review for a bit.

@JoshuaKGoldberg JoshuaKGoldberg added the 1 approval PR that a maintainer has LGTM'd - any maintainer can merge this when ready label Jul 17, 2023
Copy link
Member

@bradzacher bradzacher left a comment

Choose a reason for hiding this comment

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

These are also semantically valid assignment:

declare let x: 1;

(x as 1) = 1;
(<1>x) = 1;
(x satisfies 1) = 1;

Which is covered by the skipOuterExpressions(expr, OuterExpressionKinds.Assertions | OuterExpressionKinds.Parentheses); line in the TS codebase.

Also currently this is invalid (for now):

declare const y: { z: number };

y?.z = 1;

I don't know if we have handling for cases like this (if we don't - do we want to add them in this PR?):

declare const z: string[];

let [ ...a = [] ] = z;
let { ...b = {} } = z;

Other than that looking good!

@bradzacher bradzacher added awaiting response Issues waiting for a reply from the OP or another party and removed 1 approval PR that a maintainer has LGTM'd - any maintainer can merge this when ready labels Jul 18, 2023
@Josh-Cena
Copy link
Member Author

let [ ...a = [] ] = z;
let { ...b = {} } = z;

This PR only deals with update expressions, not assignment or declaration, so not those atm.

I'll add the assertion ones.

@fisker
Copy link
Contributor

fisker commented Jul 23, 2023

@Josh-Cena

Side question: when I work on invalid AST checks, I feel it's hard to debug, maybe we need find a better way to run test on single fixture, do you feel the same?

@bradzacher
Copy link
Member

@fisker I did build this into the infra to make it possible to filter the tests:

https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/ast-spec/tests/fixtures.test.ts#L21-L25

But I agree that it's not a very good DevX and pretty hacky. Open to suggestions to improve it!

@fisker
Copy link
Contributor

fisker commented Jul 28, 2023

I hope we run test from each directory, so we can
jest packages/ast-spec/src/expression/UpdateExpression/fixtures/_error_/literal/.

Similar to Prettier's approach https://github.com/prettier/prettier/blob/6ed37ad85731e4e62d81481d40dfd98c6a675a10/tests/format/js/array-spread/jsfmt.spec.js#L1

@JoshuaKGoldberg JoshuaKGoldberg removed the awaiting response Issues waiting for a reply from the OP or another party label Oct 18, 2023
Copy link
Member

@bradzacher bradzacher left a comment

Choose a reason for hiding this comment

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

just one comment otherwise
youhavemystamp

}
return true;
case SyntaxKind.ParenthesizedExpression:
case SyntaxKind.TypeAssertionExpression:
Copy link
Member

Choose a reason for hiding this comment

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

I might have missed it but I think we're missing a case for this

Copy link
Member Author

Choose a reason for hiding this comment

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

@bradzacher Wow, thanks for catching that! In doing so I realized TypeAssertionExpression is only for <number>foo and foo as number is a separate syntax kind.

@bradzacher bradzacher added awaiting response Issues waiting for a reply from the OP or another party 1 approval PR that a maintainer has LGTM'd - any maintainer can merge this when ready labels Nov 10, 2023
@Josh-Cena Josh-Cena removed the awaiting response Issues waiting for a reply from the OP or another party label Nov 23, 2023
@bradzacher bradzacher added the awaiting response Issues waiting for a reply from the OP or another party label Dec 6, 2023
bradzacher
bradzacher previously approved these changes Dec 6, 2023
Copy link
Member

@bradzacher bradzacher left a comment

Choose a reason for hiding this comment

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

just need to get CI passing and we can merge this

@Josh-Cena

This comment was marked as outdated.

@github-actions github-actions bot removed the awaiting response Issues waiting for a reply from the OP or another party label Dec 13, 2023
@JoshuaKGoldberg JoshuaKGoldberg merged commit 1a8e0dc into typescript-eslint:main Jan 4, 2024
58 of 59 checks passed
@Josh-Cena Josh-Cena deleted the invalid-update branch January 4, 2024 16:52
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jan 12, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
1 approval PR that a maintainer has LGTM'd - any maintainer can merge this when ready enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Enhancement: Throw errors for invalid UpdateExpressions
4 participants