diff --git a/internal/ast/ast.go b/internal/ast/ast.go index 4939307b77..3d6dbcffcc 100644 --- a/internal/ast/ast.go +++ b/internal/ast/ast.go @@ -11001,7 +11001,6 @@ func (node *SourceFile) GetOrCreateToken( ) *TokenNode { node.tokenCacheMu.Lock() defer node.tokenCacheMu.Unlock() - loc := core.NewTextRange(pos, end) if node.tokenCache == nil { node.tokenCache = make(map[core.TextRange]*Node) @@ -11014,7 +11013,9 @@ func (node *SourceFile) GetOrCreateToken( } return token } - + if parent.Flags&NodeFlagsReparsed != 0 { + panic(fmt.Sprintf("Cannot create token from reparsed node of kind %v", parent.Kind)) + } token := createToken(kind, node, pos, end, flags) token.Loc = loc token.Parent = parent diff --git a/internal/astnav/tokens.go b/internal/astnav/tokens.go index c4fb9ee350..c68bf9ec68 100644 --- a/internal/astnav/tokens.go +++ b/internal/astnav/tokens.go @@ -65,10 +65,6 @@ func getTokenAtPosition( // `left` tracks the lower boundary of the node/token that could be returned, // and is eventually the scanner's start position, if the scanner is used. left := 0 - // `allowReparsed` is set when we're navigating inside an AsExpression or - // SatisfiesExpression, which allows visiting their reparsed children to reach - // the actual identifier from JSDoc type assertions. - allowReparsed := false testNode := func(node *ast.Node) int { if node.Kind != ast.KindEndOfFile && node.End() == position && includePrecedingTokenAtEndPosition != nil { @@ -92,15 +88,8 @@ func getTokenAtPosition( // We can't abort visiting children, so once a match is found, we set `next` // and do nothing on subsequent visits. if node != nil && next == nil { - // Skip reparsed nodes unless: - // 1. The node itself is AsExpression or SatisfiesExpression, OR - // 2. We're already inside an AsExpression or SatisfiesExpression (allowReparsed=true) - // These are special cases where reparsed nodes from JSDoc type assertions - // should still be navigable to reach identifiers. - isSpecialReparsed := node.Flags&ast.NodeFlagsReparsed != 0 && - (node.Kind == ast.KindAsExpression || node.Kind == ast.KindSatisfiesExpression) - - if node.Flags&ast.NodeFlagsReparsed == 0 || isSpecialReparsed || allowReparsed { + // Skip reparsed nodes + if node.Flags&ast.NodeFlagsReparsed == 0 { result := testNode(node) switch result { case -1: @@ -211,11 +200,6 @@ func getTokenAtPosition( current = next left = current.Pos() next = nil - // When navigating into AsExpression or SatisfiesExpression, allow visiting - // their reparsed children to reach identifiers from JSDoc type assertions. - if current.Kind == ast.KindAsExpression || current.Kind == ast.KindSatisfiesExpression { - allowReparsed = true - } } } diff --git a/internal/checker/checker.go b/internal/checker/checker.go index 6d8953871b..58981001be 100644 --- a/internal/checker/checker.go +++ b/internal/checker/checker.go @@ -10408,18 +10408,15 @@ func (c *Checker) getInstantiationExpressionType(exprType *Type, node *ast.Node) } func (c *Checker) checkSatisfiesExpression(node *ast.Node) *Type { - c.checkSourceElement(node.Type()) - return c.checkSatisfiesExpressionWorker(node.Expression(), node.Type(), CheckModeNormal) -} - -func (c *Checker) checkSatisfiesExpressionWorker(expression *ast.Node, target *ast.Node, checkMode CheckMode) *Type { - exprType := c.checkExpressionEx(expression, checkMode) - targetType := c.getTypeFromTypeNode(target) + typeNode := node.Type() + c.checkSourceElement(typeNode) + exprType := c.checkExpression(node.Expression()) + targetType := c.getTypeFromTypeNode(typeNode) if c.isErrorType(targetType) { return targetType } - errorNode := ast.FindAncestor(target.Parent, func(n *ast.Node) bool { return ast.IsSatisfiesExpression(n) }) - c.checkTypeAssignableToAndOptionallyElaborate(exprType, targetType, errorNode, expression, diagnostics.Type_0_does_not_satisfy_the_expected_type_1, nil) + errorNode := core.IfElse(typeNode.Flags&ast.NodeFlagsReparsed != 0, typeNode, node) + c.checkTypeAssignableToAndOptionallyElaborate(exprType, targetType, errorNode, node.Expression(), diagnostics.Type_0_does_not_satisfy_the_expected_type_1, nil) return exprType } @@ -11935,14 +11932,15 @@ func (c *Checker) checkAssertion(node *ast.Node, checkMode CheckMode) *Type { } func (c *Checker) checkAssertionDeferred(node *ast.Node) { + typeNode := node.Type() exprType := c.getRegularTypeOfObjectLiteral(c.getBaseTypeOfLiteralType(c.assertionLinks.Get(node).exprType)) - targetType := c.getTypeFromTypeNode(node.Type()) + targetType := c.getTypeFromTypeNode(typeNode) if !c.isErrorType(targetType) { widenedType := c.getWidenedType(exprType) if !c.isTypeComparableTo(targetType, widenedType) { errNode := node - if node.Flags&ast.NodeFlagsReparsed != 0 { - errNode = node.Type() + if typeNode.Flags&ast.NodeFlagsReparsed != 0 { + errNode = typeNode } c.checkTypeComparableTo(exprType, targetType, errNode, diagnostics.Conversion_of_type_0_to_type_1_may_be_a_mistake_because_neither_type_sufficiently_overlaps_with_the_other_If_this_was_intentional_convert_the_expression_to_unknown_first) } diff --git a/internal/ls/folding.go b/internal/ls/folding.go index ecd819d635..863891469a 100644 --- a/internal/ls/folding.go +++ b/internal/ls/folding.go @@ -109,10 +109,7 @@ func (l *LanguageService) addRegionOutliningSpans(sourceFile *ast.SourceFile) [] } func visitNode(ctx context.Context, n *ast.Node, depthRemaining int, sourceFile *ast.SourceFile, l *LanguageService) []*lsproto.FoldingRange { - if depthRemaining == 0 { - return nil - } - if ctx.Err() != nil { + if n.Flags&ast.NodeFlagsReparsed != 0 || depthRemaining == 0 || ctx.Err() != nil { return nil } foldingRange := make([]*lsproto.FoldingRange, 0, 40) diff --git a/internal/parser/reparser.go b/internal/parser/reparser.go index 2b8c04d6d6..f31c8a0ae6 100644 --- a/internal/parser/reparser.go +++ b/internal/parser/reparser.go @@ -314,7 +314,7 @@ func (p *Parser) reparseHosted(tag *ast.Node, parent *ast.Node, jsDoc *ast.Node) if parent.Expression() != nil && tag.TypeExpression() != nil { parent.AsMutable().SetExpression(p.makeNewCast( p.factory.DeepCloneReparse(tag.TypeExpression().Type()), - p.factory.DeepCloneReparse(parent.Expression()), + parent.Expression(), true /*isAssertion*/)) p.finishMutatedNode(parent) return @@ -335,7 +335,7 @@ func (p *Parser) reparseHosted(tag *ast.Node, parent *ast.Node, jsDoc *ast.Node) if declaration.Initializer() != nil && tag.TypeExpression() != nil { declaration.AsMutable().SetInitializer(p.makeNewCast( p.factory.DeepCloneReparse(tag.TypeExpression().Type()), - p.factory.DeepCloneReparse(declaration.Initializer()), + declaration.Initializer(), false /*isAssertion*/)) p.finishMutatedNode(declaration) break @@ -348,7 +348,7 @@ func (p *Parser) reparseHosted(tag *ast.Node, parent *ast.Node, jsDoc *ast.Node) if parent.Initializer() != nil && tag.TypeExpression() != nil { parent.AsMutable().SetInitializer(p.makeNewCast( p.factory.DeepCloneReparse(tag.TypeExpression().Type()), - p.factory.DeepCloneReparse(parent.Initializer()), + parent.Initializer(), false /*isAssertion*/)) p.finishMutatedNode(parent) } @@ -357,7 +357,7 @@ func (p *Parser) reparseHosted(tag *ast.Node, parent *ast.Node, jsDoc *ast.Node) if shorthand.ObjectAssignmentInitializer != nil && tag.AsJSDocSatisfiesTag().TypeExpression != nil { shorthand.ObjectAssignmentInitializer = p.makeNewCast( p.factory.DeepCloneReparse(tag.AsJSDocSatisfiesTag().TypeExpression.Type()), - p.factory.DeepCloneReparse(shorthand.ObjectAssignmentInitializer), + shorthand.ObjectAssignmentInitializer, false /*isAssertion*/) p.finishMutatedNode(parent) } @@ -366,7 +366,7 @@ func (p *Parser) reparseHosted(tag *ast.Node, parent *ast.Node, jsDoc *ast.Node) if parent.Expression() != nil && tag.TypeExpression() != nil { parent.AsMutable().SetExpression(p.makeNewCast( p.factory.DeepCloneReparse(tag.TypeExpression().Type()), - p.factory.DeepCloneReparse(parent.Expression()), + parent.Expression(), false /*isAssertion*/)) p.finishMutatedNode(parent) } @@ -376,7 +376,7 @@ func (p *Parser) reparseHosted(tag *ast.Node, parent *ast.Node, jsDoc *ast.Node) if kind := ast.GetAssignmentDeclarationKind(bin); kind != ast.JSDeclarationKindNone && tag.TypeExpression() != nil { bin.Right = p.makeNewCast( p.factory.DeepCloneReparse(tag.TypeExpression().Type()), - p.factory.DeepCloneReparse(bin.Right), + bin.Right, false /*isAssertion*/) p.finishMutatedNode(bin.AsNode()) } @@ -602,7 +602,7 @@ func (p *Parser) makeNewCast(t *ast.TypeNode, e *ast.Node, isAssertion bool) *as } else { assert = p.factory.NewSatisfiesExpression(e, t) } - p.finishReparsedNode(assert, e) + p.finishNodeWithEnd(assert, e.Pos(), e.End()) return assert } diff --git a/internal/testutil/tsbaseline/type_symbol_baseline.go b/internal/testutil/tsbaseline/type_symbol_baseline.go index 6a7e08878b..39133f4b13 100644 --- a/internal/testutil/tsbaseline/type_symbol_baseline.go +++ b/internal/testutil/tsbaseline/type_symbol_baseline.go @@ -354,6 +354,7 @@ func (walker *typeWriterWalker) writeTypeOrSymbol(node *ast.Node, isSymbolWalk b // Don't try to get the type of something that's already a type. // Exception for `T` in `type T = something` because that may evaluate to some interesting type. if ast.IsPartOfTypeNode(node) || + (node.Kind == ast.KindAsExpression || node.Kind == ast.KindSatisfiesExpression) && node.Type().Flags&ast.NodeFlagsReparsed != 0 || ast.IsIdentifier(node) && (ast.GetMeaningFromDeclaration(node.Parent)&ast.SemanticMeaningValue) == 0 && !(ast.IsTypeOrJSTypeAliasDeclaration(node.Parent) && node == node.Parent.Name()) { diff --git a/testdata/baselines/reference/submodule/conformance/checkJsdocSatisfiesTag1.errors.txt b/testdata/baselines/reference/submodule/conformance/checkJsdocSatisfiesTag1.errors.txt index 501d8cad97..d9dbd2c6e5 100644 --- a/testdata/baselines/reference/submodule/conformance/checkJsdocSatisfiesTag1.errors.txt +++ b/testdata/baselines/reference/submodule/conformance/checkJsdocSatisfiesTag1.errors.txt @@ -1,5 +1,5 @@ /a.js(21,44): error TS2353: Object literal may only specify known properties, and 'b' does not exist in type 'T1'. -/a.js(22,38): error TS2741: Property 'a' is missing in type '{}' but required in type 'T1'. +/a.js(22,28): error TS2741: Property 'a' is missing in type '{}' but required in type 'T1'. /a.js(31,49): error TS2353: Object literal may only specify known properties, and 'b' does not exist in type 'T4'. @@ -28,7 +28,7 @@ ~ !!! error TS2353: Object literal may only specify known properties, and 'b' does not exist in type 'T1'. const t3 = /** @satisfies {T1} */ ({}); - ~ + ~~ !!! error TS2741: Property 'a' is missing in type '{}' but required in type 'T1'. !!! related TS2728 /a.js:3:23: 'a' is declared here. diff --git a/testdata/baselines/reference/submodule/conformance/checkJsdocSatisfiesTag1.errors.txt.diff b/testdata/baselines/reference/submodule/conformance/checkJsdocSatisfiesTag1.errors.txt.diff index ade10090ef..30a35d0db4 100644 --- a/testdata/baselines/reference/submodule/conformance/checkJsdocSatisfiesTag1.errors.txt.diff +++ b/testdata/baselines/reference/submodule/conformance/checkJsdocSatisfiesTag1.errors.txt.diff @@ -4,7 +4,7 @@ /a.js(21,44): error TS2353: Object literal may only specify known properties, and 'b' does not exist in type 'T1'. -/a.js(22,17): error TS1360: Type '{}' does not satisfy the expected type 'T1'. - Property 'a' is missing in type '{}' but required in type 'T1'. -+/a.js(22,38): error TS2741: Property 'a' is missing in type '{}' but required in type 'T1'. ++/a.js(22,28): error TS2741: Property 'a' is missing in type '{}' but required in type 'T1'. /a.js(31,49): error TS2353: Object literal may only specify known properties, and 'b' does not exist in type 'T4'. @@ -16,7 +16,7 @@ -!!! error TS1360: Type '{}' does not satisfy the expected type 'T1'. -!!! error TS1360: Property 'a' is missing in type '{}' but required in type 'T1'. -!!! related TS2728 /a.js:3:4: 'a' is declared here. -+ ~ ++ ~~ +!!! error TS2741: Property 'a' is missing in type '{}' but required in type 'T1'. +!!! related TS2728 /a.js:3:23: 'a' is declared here. diff --git a/testdata/baselines/reference/submodule/conformance/checkJsdocSatisfiesTag11.types b/testdata/baselines/reference/submodule/conformance/checkJsdocSatisfiesTag11.types index 14b38c0fff..a9dfcaf190 100644 --- a/testdata/baselines/reference/submodule/conformance/checkJsdocSatisfiesTag11.types +++ b/testdata/baselines/reference/submodule/conformance/checkJsdocSatisfiesTag11.types @@ -18,7 +18,6 @@ const t1 = { a: 1 }; >t1 : { a: number; } >{ a: 1 } : { a: number; } ->{ a: 1 } : { a: number; } >a : number >1 : 1 diff --git a/testdata/baselines/reference/submodule/conformance/checkJsdocSatisfiesTag11.types.diff b/testdata/baselines/reference/submodule/conformance/checkJsdocSatisfiesTag11.types.diff deleted file mode 100644 index 0f3a111a10..0000000000 --- a/testdata/baselines/reference/submodule/conformance/checkJsdocSatisfiesTag11.types.diff +++ /dev/null @@ -1,9 +0,0 @@ ---- old.checkJsdocSatisfiesTag11.types -+++ new.checkJsdocSatisfiesTag11.types -@@= skipped -17, +17 lines =@@ - const t1 = { a: 1 }; - >t1 : { a: number; } - >{ a: 1 } : { a: number; } -+>{ a: 1 } : { a: number; } - >a : number - >1 : 1 diff --git a/testdata/baselines/reference/submodule/conformance/checkJsdocSatisfiesTag12.errors.txt b/testdata/baselines/reference/submodule/conformance/checkJsdocSatisfiesTag12.errors.txt index b9d1132592..23f2335a68 100644 --- a/testdata/baselines/reference/submodule/conformance/checkJsdocSatisfiesTag12.errors.txt +++ b/testdata/baselines/reference/submodule/conformance/checkJsdocSatisfiesTag12.errors.txt @@ -1,7 +1,7 @@ /a.js(24,20): error TS2353: Object literal may only specify known properties, and 'b' does not exist in type 'T1'. -/a.js(29,14): error TS2741: Property 'a' is missing in type '{}' but required in type 'T1'. +/a.js(27,16): error TS2741: Property 'a' is missing in type '{}' but required in type 'T1'. /a.js(44,25): error TS2353: Object literal may only specify known properties, and 'b' does not exist in type 'T2'. -/a.js(51,42): error TS1360: Type 'number' does not satisfy the expected type 'string'. +/a.js(51,17): error TS1360: Type 'number' does not satisfy the expected type 'string'. ==== /a.js (4 errors) ==== @@ -34,11 +34,11 @@ /** * @satisfies {T1} - */ - const t3 = {}; - ~ + ~~ !!! error TS2741: Property 'a' is missing in type '{}' but required in type 'T1'. !!! related TS2728 /a.js:3:23: 'a' is declared here. + */ + const t3 = {}; /** * @satisfies {Array.} @@ -63,6 +63,6 @@ const t7 = { a: "a" }; /** @satisfies {string} */ const t8 = (1); - ~ + ~~~~~~ !!! error TS1360: Type 'number' does not satisfy the expected type 'string'. \ No newline at end of file diff --git a/testdata/baselines/reference/submodule/conformance/checkJsdocSatisfiesTag12.errors.txt.diff b/testdata/baselines/reference/submodule/conformance/checkJsdocSatisfiesTag12.errors.txt.diff index 0edead04b9..9fb50fa561 100644 --- a/testdata/baselines/reference/submodule/conformance/checkJsdocSatisfiesTag12.errors.txt.diff +++ b/testdata/baselines/reference/submodule/conformance/checkJsdocSatisfiesTag12.errors.txt.diff @@ -2,34 +2,34 @@ +++ new.checkJsdocSatisfiesTag12.errors.txt @@= skipped -0, +0 lines =@@ /a.js(24,20): error TS2353: Object literal may only specify known properties, and 'b' does not exist in type 'T1'. -+/a.js(29,14): error TS2741: Property 'a' is missing in type '{}' but required in type 'T1'. ++/a.js(27,16): error TS2741: Property 'a' is missing in type '{}' but required in type 'T1'. /a.js(44,25): error TS2353: Object literal may only specify known properties, and 'b' does not exist in type 'T2'. -/a.js(51,6): error TS1360: Type 'number' does not satisfy the expected type 'string'. - - -==== /a.js (3 errors) ==== -+/a.js(51,42): error TS1360: Type 'number' does not satisfy the expected type 'string'. ++/a.js(51,17): error TS1360: Type 'number' does not satisfy the expected type 'string'. + + +==== /a.js (4 errors) ==== /** * @typedef {Object} T1 * @property {number} a -@@= skipped -34, +35 lines =@@ +@@= skipped -32, +33 lines =@@ + + /** * @satisfies {T1} - */ - const t3 = {}; -+ ~ ++ ~~ +!!! error TS2741: Property 'a' is missing in type '{}' but required in type 'T1'. +!!! related TS2728 /a.js:3:23: 'a' is declared here. + */ + const t3 = {}; - /** - * @satisfies {Array.} -@@= skipped -24, +27 lines =@@ +@@= skipped -26, +29 lines =@@ const t7 = { a: "a" }; /** @satisfies {string} */ const t8 = (1); - ~~~~~~~~~ -+ ~ ++ ~~~~~~ !!! error TS1360: Type 'number' does not satisfy the expected type 'string'. \ No newline at end of file diff --git a/testdata/baselines/reference/submodule/conformance/checkJsdocSatisfiesTag4.errors.txt b/testdata/baselines/reference/submodule/conformance/checkJsdocSatisfiesTag4.errors.txt index 17573f218b..c5445a8150 100644 --- a/testdata/baselines/reference/submodule/conformance/checkJsdocSatisfiesTag4.errors.txt +++ b/testdata/baselines/reference/submodule/conformance/checkJsdocSatisfiesTag4.errors.txt @@ -1,4 +1,4 @@ -/a.js(5,43): error TS2741: Property 'a' is missing in type '{}' but required in type 'Foo'. +/a.js(5,32): error TS2741: Property 'a' is missing in type '{}' but required in type 'Foo'. ==== /a.js (1 errors) ==== @@ -7,7 +7,7 @@ * @property {number} a */ export default /** @satisfies {Foo} */ ({}); - ~ + ~~~ !!! error TS2741: Property 'a' is missing in type '{}' but required in type 'Foo'. !!! related TS2728 /a.js:3:23: 'a' is declared here. diff --git a/testdata/baselines/reference/submodule/conformance/checkJsdocSatisfiesTag4.errors.txt.diff b/testdata/baselines/reference/submodule/conformance/checkJsdocSatisfiesTag4.errors.txt.diff index 50185c3ada..3d8e1e84b3 100644 --- a/testdata/baselines/reference/submodule/conformance/checkJsdocSatisfiesTag4.errors.txt.diff +++ b/testdata/baselines/reference/submodule/conformance/checkJsdocSatisfiesTag4.errors.txt.diff @@ -3,7 +3,7 @@ @@= skipped -0, +0 lines =@@ -/a.js(5,21): error TS1360: Type '{}' does not satisfy the expected type 'Foo'. - Property 'a' is missing in type '{}' but required in type 'Foo'. -+/a.js(5,43): error TS2741: Property 'a' is missing in type '{}' but required in type 'Foo'. ++/a.js(5,32): error TS2741: Property 'a' is missing in type '{}' but required in type 'Foo'. ==== /a.js (1 errors) ==== @@ -15,7 +15,7 @@ -!!! error TS1360: Type '{}' does not satisfy the expected type 'Foo'. -!!! error TS1360: Property 'a' is missing in type '{}' but required in type 'Foo'. -!!! related TS2728 /a.js:3:4: 'a' is declared here. -+ ~ ++ ~~~ +!!! error TS2741: Property 'a' is missing in type '{}' but required in type 'Foo'. +!!! related TS2728 /a.js:3:23: 'a' is declared here.