Skip to content

Commit 222ef9b

Browse files
authored
More fixes to type checking (#801)
1 parent 05cc3b8 commit 222ef9b

File tree

147 files changed

+568
-2197
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

147 files changed

+568
-2197
lines changed

internal/ast/ast.go

+9
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ func (f *NodeFactory) TextCount() int {
113113

114114
func updateNode(updated *Node, original *Node, hooks NodeFactoryHooks) *Node {
115115
if updated != original {
116+
updated.Flags = original.Flags
116117
updated.Loc = original.Loc
117118
if hooks.OnUpdate != nil {
118119
hooks.OnUpdate(updated, original)
@@ -8705,6 +8706,10 @@ func (node *JSDocNonNullableType) Clone(f *NodeFactory) *Node {
87058706
return cloneNode(f.NewJSDocNonNullableType(node.Type), node.AsNode(), f.hooks)
87068707
}
87078708

8709+
func IsJSDocNonNullableType(node *Node) bool {
8710+
return node.Kind == KindJSDocNonNullableType
8711+
}
8712+
87088713
// JSDocNullableType
87098714

87108715
type JSDocNullableType struct {
@@ -8737,6 +8742,10 @@ func (node *JSDocNullableType) Clone(f *NodeFactory) *Node {
87378742
return cloneNode(f.NewJSDocNullableType(node.Type), node.AsNode(), f.hooks)
87388743
}
87398744

8745+
func IsJSDocNullableType(node *Node) bool {
8746+
return node.Kind == KindJSDocNullableType
8747+
}
8748+
87408749
// JSDocAllType
87418750

87428751
type JSDocAllType struct {

internal/ast/utilities.go

+10-9
Original file line numberDiff line numberDiff line change
@@ -2602,17 +2602,17 @@ func IsUnterminatedLiteral(node *Node) bool {
26022602
}
26032603

26042604
func GetJSXImplicitImportBase(compilerOptions *core.CompilerOptions, file *SourceFile) string {
2605-
jsxImportSourcePragma := getPragmaFromSourceFile(file, "jsximportsource")
2606-
jsxRuntimePragma := getPragmaFromSourceFile(file, "jsxruntime")
2607-
if getPragmaArgument(jsxRuntimePragma, "factory") == "classic" {
2605+
jsxImportSourcePragma := GetPragmaFromSourceFile(file, "jsximportsource")
2606+
jsxRuntimePragma := GetPragmaFromSourceFile(file, "jsxruntime")
2607+
if GetPragmaArgument(jsxRuntimePragma, "factory") == "classic" {
26082608
return ""
26092609
}
26102610
if compilerOptions.Jsx == core.JsxEmitReactJSX ||
26112611
compilerOptions.Jsx == core.JsxEmitReactJSXDev ||
26122612
compilerOptions.JsxImportSource != "" ||
26132613
jsxImportSourcePragma != nil ||
2614-
getPragmaArgument(jsxRuntimePragma, "factory") == "automatic" {
2615-
result := getPragmaArgument(jsxImportSourcePragma, "factory")
2614+
GetPragmaArgument(jsxRuntimePragma, "factory") == "automatic" {
2615+
result := GetPragmaArgument(jsxImportSourcePragma, "factory")
26162616
if result == "" {
26172617
result = compilerOptions.JsxImportSource
26182618
}
@@ -2631,18 +2631,19 @@ func GetJSXRuntimeImport(base string, options *core.CompilerOptions) string {
26312631
return base + "/" + core.IfElse(options.Jsx == core.JsxEmitReactJSXDev, "jsx-dev-runtime", "jsx-runtime")
26322632
}
26332633

2634-
func getPragmaFromSourceFile(file *SourceFile, name string) *Pragma {
2634+
func GetPragmaFromSourceFile(file *SourceFile, name string) *Pragma {
2635+
var result *Pragma
26352636
if file != nil {
26362637
for i := range file.Pragmas {
26372638
if file.Pragmas[i].Name == name {
2638-
return &file.Pragmas[i]
2639+
result = &file.Pragmas[i] // Last one wins
26392640
}
26402641
}
26412642
}
2642-
return nil
2643+
return result
26432644
}
26442645

2645-
func getPragmaArgument(pragma *Pragma, name string) string {
2646+
func GetPragmaArgument(pragma *Pragma, name string) string {
26462647
if pragma != nil {
26472648
if arg, ok := pragma.Args[name]; ok {
26482649
return arg.Value

internal/binder/binder.go

+1-4
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ func (b *Binder) getDeclarationName(node *ast.Node) string {
324324
}
325325
return GetSymbolNameForPrivateIdentifier(containingClass.Symbol(), name.Text())
326326
}
327-
if ast.IsPropertyNameLiteral(name) {
327+
if ast.IsPropertyNameLiteral(name) || ast.IsJsxNamespacedName(name) {
328328
return name.Text()
329329
}
330330
if ast.IsComputedPropertyName(name) {
@@ -339,9 +339,6 @@ func (b *Binder) getDeclarationName(node *ast.Node) string {
339339
}
340340
panic("Only computed properties with literal names have declaration names")
341341
}
342-
// if isJsxNamespacedName(name) {
343-
// return getEscapedTextOfJsxNamespacedName(name)
344-
// }
345342
return ast.InternalSymbolNameMissing
346343
}
347344
switch node.Kind {

internal/checker/checker.go

+27-1
Original file line numberDiff line numberDiff line change
@@ -2208,6 +2208,8 @@ func (c *Checker) checkSourceElementWorker(node *ast.Node) {
22082208
c.checkGrammarStatementInAmbientContext(node)
22092209
case ast.KindMissingDeclaration:
22102210
c.checkMissingDeclaration(node)
2211+
case ast.KindJSDocNonNullableType, ast.KindJSDocNullableType, ast.KindJSDocAllType, ast.KindJSDocTypeLiteral:
2212+
c.checkJSDocType(node)
22112213
}
22122214
}
22132215

@@ -2326,6 +2328,30 @@ func (c *Checker) resolveJSDocMemberName(name *ast.Node, location *ast.Node) *as
23262328
return nil
23272329
}
23282330

2331+
func (c *Checker) checkJSDocType(node *ast.Node) {
2332+
c.checkJSDocTypeIsInJsFile(node)
2333+
node.ForEachChild(c.checkSourceElement)
2334+
}
2335+
2336+
func (c *Checker) checkJSDocTypeIsInJsFile(node *ast.Node) {
2337+
if !ast.IsInJSFile(node) {
2338+
if ast.IsJSDocNonNullableType(node) || ast.IsJSDocNullableType(node) {
2339+
token := core.IfElse(ast.IsJSDocNonNullableType(node), "!", "?")
2340+
postfix := node.Pos() == node.Type().Pos()
2341+
message := core.IfElse(postfix,
2342+
diagnostics.X_0_at_the_end_of_a_type_is_not_valid_TypeScript_syntax_Did_you_mean_to_write_1,
2343+
diagnostics.X_0_at_the_start_of_a_type_is_not_valid_TypeScript_syntax_Did_you_mean_to_write_1)
2344+
t := c.getTypeFromTypeNode(node.Type())
2345+
if ast.IsJSDocNullableType(node) && t != c.neverType && t != c.voidType {
2346+
t = c.getNullableType(t, core.IfElse(postfix, TypeFlagsUndefined, TypeFlagsNullable))
2347+
}
2348+
c.grammarErrorOnNode(node, message, token, c.TypeToString(t))
2349+
} else {
2350+
c.grammarErrorOnNode(node, diagnostics.JSDoc_types_can_only_be_used_inside_documentation_comments)
2351+
}
2352+
}
2353+
}
2354+
23292355
func (c *Checker) checkTypeParameter(node *ast.Node) {
23302356
// Grammar Checking
23312357
c.checkGrammarModifiers(node)
@@ -6598,7 +6624,7 @@ func (c *Checker) checkUnusedLocalsAndParameters(node *ast.Node) {
65986624
importClauses[importClause] = append(importClauses[importClause], declaration)
65996625
}
66006626
default:
6601-
if !ast.IsAmbientModule(declaration) {
6627+
if !ast.IsTypeParameterDeclaration(declaration) && !ast.IsAmbientModule(declaration) {
66026628
c.reportUnusedLocal(declaration, ast.SymbolName(local))
66036629
}
66046630
}

internal/checker/jsx.go

+8-29
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ func (c *Checker) checkJsxFragment(node *ast.Node) *Type {
103103
// by default, jsx:'react' will use jsxFactory = React.createElement and jsxFragmentFactory = React.Fragment
104104
// if jsxFactory compiler option is provided, ensure jsxFragmentFactory compiler option or @jsxFrag pragma is provided too
105105
nodeSourceFile := ast.GetSourceFileOfNode(node)
106-
if c.compilerOptions.GetJSXTransformEnabled() && (c.compilerOptions.JsxFactory != "" || getPragmaFromSourceFile(nodeSourceFile, "jsx") != nil) && c.compilerOptions.JsxFragmentFactory == "" && getPragmaFromSourceFile(nodeSourceFile, "jsxfrag") == nil {
106+
if c.compilerOptions.GetJSXTransformEnabled() && (c.compilerOptions.JsxFactory != "" || ast.GetPragmaFromSourceFile(nodeSourceFile, "jsx") != nil) && c.compilerOptions.JsxFragmentFactory == "" && ast.GetPragmaFromSourceFile(nodeSourceFile, "jsxfrag") == nil {
107107
message := core.IfElse(c.compilerOptions.JsxFactory != "",
108108
diagnostics.The_jsxFragmentFactory_compiler_option_must_be_provided_to_use_JSX_fragments_with_the_jsxFactory_compiler_option,
109109
diagnostics.An_jsxFrag_pragma_is_required_when_using_an_jsx_pragma_with_JSX_fragments)
@@ -377,10 +377,10 @@ func (c *Checker) resolveJsxOpeningLikeElement(node *ast.Node, candidatesOutArra
377377
result := c.getIntrinsicAttributesTypeFromJsxOpeningLikeElement(node)
378378
fakeSignature := c.createSignatureForJSXIntrinsic(node, result)
379379
c.checkTypeAssignableToAndOptionallyElaborate(c.checkExpressionWithContextualType(node.Attributes(), c.getEffectiveFirstArgumentForJsxSignature(fakeSignature, node), nil /*inferenceContext*/, CheckModeNormal), result, node.TagName(), node.Attributes(), nil, nil)
380-
typeArgumentList := node.TypeArgumentList()
381-
if typeArgumentList != nil {
382-
c.checkSourceElements(typeArgumentList.Nodes)
383-
c.diagnostics.Add(ast.NewDiagnostic(ast.GetSourceFileOfNode(node), node.TypeArgumentList().Loc, diagnostics.Expected_0_type_arguments_but_got_1, 0, len(typeArgumentList.Nodes)))
380+
typeArguments := node.TypeArguments()
381+
if len(typeArguments) != 0 {
382+
c.checkSourceElements(typeArguments)
383+
c.diagnostics.Add(ast.NewDiagnostic(ast.GetSourceFileOfNode(node), node.TypeArgumentList().Loc, diagnostics.Expected_0_type_arguments_but_got_1, 0, len(typeArguments)))
384384
}
385385
return fakeSignature
386386
}
@@ -1134,7 +1134,7 @@ func (c *Checker) getJsxNamespace(location *ast.Node) string {
11341134
if links.localJsxFragmentNamespace != "" {
11351135
return links.localJsxFragmentNamespace
11361136
}
1137-
jsxFragmentPragma := getPragmaFromSourceFile(file, "jsxfrag")
1137+
jsxFragmentPragma := ast.GetPragmaFromSourceFile(file, "jsxfrag")
11381138
if jsxFragmentPragma != nil {
11391139
links.localJsxFragmentFactory = c.parseIsolatedEntityName(jsxFragmentPragma.Args["factory"].Value)
11401140
if links.localJsxFragmentFactory != nil {
@@ -1179,7 +1179,7 @@ func (c *Checker) getLocalJsxNamespace(file *ast.SourceFile) string {
11791179
if links.localJsxNamespace != "" {
11801180
return links.localJsxNamespace
11811181
}
1182-
jsxPragma := getPragmaFromSourceFile(file, "jsx")
1182+
jsxPragma := ast.GetPragmaFromSourceFile(file, "jsx")
11831183
if jsxPragma != nil {
11841184
links.localJsxFactory = c.parseIsolatedEntityName(jsxPragma.Args["factory"].Value)
11851185
if links.localJsxFactory != nil {
@@ -1208,7 +1208,7 @@ func (c *Checker) getJsxFragmentFactoryEntity(location *ast.Node) *ast.EntityNam
12081208
if links.localJsxFragmentFactory != nil {
12091209
return links.localJsxFragmentFactory
12101210
}
1211-
jsxFragPragma := getPragmaFromSourceFile(file, "jsxfrag")
1211+
jsxFragPragma := ast.GetPragmaFromSourceFile(file, "jsxfrag")
12121212
if jsxFragPragma != nil {
12131213
links.localJsxFragmentFactory = c.parseIsolatedEntityName(jsxFragPragma.Args["factory"].Value)
12141214
return links.localJsxFragmentFactory
@@ -1246,7 +1246,6 @@ func (c *Checker) getJsxNamespaceContainerForImplicitImport(location *ast.Node)
12461246
if links != nil && links.jsxImplicitImportContainer != nil {
12471247
return core.IfElse(links.jsxImplicitImportContainer == c.unknownSymbol, nil, links.jsxImplicitImportContainer)
12481248
}
1249-
12501249
moduleReference, specifier := c.getJSXRuntimeImportSpecifier(file)
12511250
if moduleReference == "" {
12521251
return nil
@@ -1266,23 +1265,3 @@ func (c *Checker) getJsxNamespaceContainerForImplicitImport(location *ast.Node)
12661265
func (c *Checker) getJSXRuntimeImportSpecifier(file *ast.SourceFile) (moduleReference string, specifier *ast.Node) {
12671266
return c.program.GetJSXRuntimeImportSpecifier(file.Path())
12681267
}
1269-
1270-
func getPragmaFromSourceFile(file *ast.SourceFile, name string) *ast.Pragma {
1271-
if file != nil {
1272-
for i := range file.Pragmas {
1273-
if file.Pragmas[i].Name == name {
1274-
return &file.Pragmas[i]
1275-
}
1276-
}
1277-
}
1278-
return nil
1279-
}
1280-
1281-
func getPragmaArgument(pragma *ast.Pragma, name string) string {
1282-
if pragma != nil {
1283-
if arg, ok := pragma.Args[name]; ok {
1284-
return arg.Value
1285-
}
1286-
}
1287-
return ""
1288-
}

internal/parser/parser.go

+24-13
Original file line numberDiff line numberDiff line change
@@ -2588,6 +2588,18 @@ func (p *Parser) parsePostfixTypeOrHigher() *ast.Node {
25882588
typeNode := p.parseNonArrayType()
25892589
for !p.hasPrecedingLineBreak() {
25902590
switch p.token {
2591+
case ast.KindExclamationToken:
2592+
p.nextToken()
2593+
typeNode = p.factory.NewJSDocNonNullableType(typeNode)
2594+
p.finishNode(typeNode, pos)
2595+
case ast.KindQuestionToken:
2596+
// If next token is start of a type we have a conditional type
2597+
if p.lookAhead((*Parser).nextIsStartOfType) {
2598+
return typeNode
2599+
}
2600+
p.nextToken()
2601+
typeNode = p.factory.NewJSDocNullableType(typeNode)
2602+
p.finishNode(typeNode, pos)
25912603
case ast.KindOpenBracketToken:
25922604
p.parseExpected(ast.KindOpenBracketToken)
25932605
if p.isStartOfType(false /*isStartOfParameter*/) {
@@ -3553,11 +3565,11 @@ func (p *Parser) parseTupleElementType() *ast.TypeNode {
35533565
return result
35543566
}
35553567
typeNode := p.parseType()
3556-
// If next token is start of a type we have a conditional type and not an optional type
3557-
if p.token == ast.KindQuestionToken && !p.lookAhead((*Parser).nextIsStartOfType) {
3558-
p.nextToken()
3559-
typeNode = p.factory.NewOptionalTypeNode(typeNode)
3560-
p.finishNode(typeNode, pos)
3568+
if ast.IsJSDocNullableType(typeNode) && typeNode.Pos() == typeNode.Type().Pos() {
3569+
node := p.factory.NewOptionalTypeNode(typeNode.Type())
3570+
node.Flags = typeNode.Flags
3571+
node.Loc = typeNode.Loc
3572+
return node
35613573
}
35623574
return typeNode
35633575
}
@@ -5001,12 +5013,11 @@ func (p *Parser) parseSimpleUnaryExpression() *ast.Expression {
50015013
case ast.KindVoidKeyword:
50025014
return p.parseVoidExpression()
50035015
case ast.KindLessThanToken:
5004-
// !!!
5005-
// // Just like in parseUpdateExpression, we need to avoid parsing type assertions when
5006-
// // in JSX and we see an expression like "+ <foo> bar".
5007-
// if (languageVariant == core.LanguageVariant.JSX) {
5008-
// return parseJsxElementOrSelfClosingElementOrFragment(/*inExpressionContext*/ true, /*topInvalidNodePosition*/ undefined, /*openingTag*/ undefined, /*mustBeUnary*/ true);
5009-
// }
5016+
// Just like in parseUpdateExpression, we need to avoid parsing type assertions when
5017+
// in JSX and we see an expression like "+ <foo> bar".
5018+
if p.languageVariant == core.LanguageVariantJSX {
5019+
return p.parseJsxElementOrSelfClosingElementOrFragment(true /*inExpressionContext*/, -1 /*topInvalidNodePosition*/, nil /*openingTag*/, true /*mustBeUnary*/)
5020+
}
50105021
// // This is modified UnaryExpression grammar in TypeScript
50115022
// // UnaryExpression (modified):
50125023
// // < type > UnaryExpression
@@ -6415,7 +6426,7 @@ func getCommentPragmas(f *ast.NodeFactory, sourceText string) (pragmas []ast.Pra
64156426
}
64166427

64176428
func extractPragmas(commentRange ast.CommentRange, text string) []ast.Pragma {
6418-
if commentRange.Kind == ast.KindSingleLineCommentTrivia && match(text, 0, "//") {
6429+
if commentRange.Kind == ast.KindSingleLineCommentTrivia {
64196430
pos := 2
64206431
tripleSlash := match(text, pos, "/")
64216432
if tripleSlash {
@@ -6473,6 +6484,7 @@ func extractPragmas(commentRange ast.CommentRange, text string) []ast.Pragma {
64736484
}
64746485
}
64756486
if commentRange.Kind == ast.KindMultiLineCommentTrivia {
6487+
text = text[:len(text)-2]
64766488
pos := 2
64776489
var pragmas []ast.Pragma
64786490
for {
@@ -6613,7 +6625,6 @@ func (p *Parser) processPragmasIntoFields(context *ast.SourceFile) {
66136625
}
66146626
}
66156627
}
6616-
66176628
case "jsx", "jsxfrag", "jsximportsource", "jsxruntime":
66186629
// Nothing to do here
66196630
default:

internal/scanner/scanner.go

-1
Original file line numberDiff line numberDiff line change
@@ -1012,7 +1012,6 @@ func (s *Scanner) ReScanSlashToken() ast.Kind {
10121012
switch {
10131013
case size == 0 || stringutil.IsLineBreak(ch):
10141014
s.tokenFlags |= ast.TokenFlagsUnterminated
1015-
s.error(diagnostics.Unterminated_regular_expression_literal)
10161015
break loop
10171016
case inEscape:
10181017
// Parsing an escape character;

0 commit comments

Comments
 (0)