vendor tsgo
This commit is contained in:
1355
tools/tsgo/internal/parser/jsdoc.go
Normal file
1355
tools/tsgo/internal/parser/jsdoc.go
Normal file
File diff suppressed because it is too large
Load Diff
6827
tools/tsgo/internal/parser/parser.go
Normal file
6827
tools/tsgo/internal/parser/parser.go
Normal file
File diff suppressed because it is too large
Load Diff
233
tools/tsgo/internal/parser/parser_test.go
Normal file
233
tools/tsgo/internal/parser/parser_test.go
Normal file
@@ -0,0 +1,233 @@
|
||||
package parser_test
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"iter"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/microsoft/typescript-go/internal/ast"
|
||||
"github.com/microsoft/typescript-go/internal/collections"
|
||||
"github.com/microsoft/typescript-go/internal/core"
|
||||
"github.com/microsoft/typescript-go/internal/parser"
|
||||
"github.com/microsoft/typescript-go/internal/repo"
|
||||
"github.com/microsoft/typescript-go/internal/testrunner"
|
||||
"github.com/microsoft/typescript-go/internal/testutil/fixtures"
|
||||
"github.com/microsoft/typescript-go/internal/tspath"
|
||||
"github.com/microsoft/typescript-go/internal/vfs/osvfs"
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
func BenchmarkParse(b *testing.B) {
|
||||
for _, f := range fixtures.BenchFixtures {
|
||||
b.Run(f.Name(), func(b *testing.B) {
|
||||
f.SkipIfNotExist(b)
|
||||
|
||||
fileName := tspath.GetNormalizedAbsolutePath(f.Path(), "/")
|
||||
path := tspath.ToPath(fileName, "/", osvfs.FS().UseCaseSensitiveFileNames())
|
||||
sourceText := f.ReadFile(b)
|
||||
scriptKind := core.GetScriptKindFromFileName(fileName)
|
||||
|
||||
opts := ast.SourceFileParseOptions{
|
||||
FileName: fileName,
|
||||
Path: path,
|
||||
}
|
||||
|
||||
for b.Loop() {
|
||||
parser.ParseSourceFile(opts, sourceText, scriptKind)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type parsableFile struct {
|
||||
path string
|
||||
name string
|
||||
}
|
||||
|
||||
func allParsableFiles(tb testing.TB, root string) iter.Seq[parsableFile] {
|
||||
tb.Helper()
|
||||
return func(yield func(parsableFile) bool) {
|
||||
tb.Helper()
|
||||
err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if d.IsDir() || tspath.TryGetExtensionFromPath(path) == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
testName, err := filepath.Rel(root, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
testName = filepath.ToSlash(testName)
|
||||
|
||||
if !yield(parsableFile{path, testName}) {
|
||||
return filepath.SkipAll
|
||||
}
|
||||
return nil
|
||||
})
|
||||
assert.NilError(tb, err)
|
||||
}
|
||||
}
|
||||
|
||||
func FuzzParser(f *testing.F) {
|
||||
repo.SkipIfNoTypeScriptSubmodule(f)
|
||||
|
||||
tests := []string{
|
||||
"src",
|
||||
"scripts",
|
||||
"Herebyfile.mjs",
|
||||
}
|
||||
|
||||
var extensions collections.Set[string]
|
||||
for _, es := range tspath.AllSupportedExtensionsWithJson {
|
||||
for _, e := range es {
|
||||
extensions.Add(e)
|
||||
}
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
root := filepath.Join(repo.TypeScriptSubmodulePath(), test)
|
||||
|
||||
for file := range allParsableFiles(f, root) {
|
||||
sourceText, err := os.ReadFile(file.path)
|
||||
assert.NilError(f, err)
|
||||
extension := tspath.TryGetExtensionFromPath(file.path)
|
||||
f.Add(extension, string(sourceText), false, false)
|
||||
}
|
||||
}
|
||||
|
||||
testDirs := []string{
|
||||
filepath.Join(repo.TypeScriptSubmodulePath(), "tests/cases/compiler"),
|
||||
filepath.Join(repo.TypeScriptSubmodulePath(), "tests/cases/conformance"),
|
||||
filepath.Join(repo.TestDataPath(), "tests/cases/compiler"),
|
||||
}
|
||||
|
||||
for _, testDir := range testDirs {
|
||||
if _, err := os.Stat(testDir); os.IsNotExist(err) {
|
||||
continue
|
||||
}
|
||||
|
||||
for file := range allParsableFiles(f, testDir) {
|
||||
sourceText, err := os.ReadFile(file.path)
|
||||
assert.NilError(f, err)
|
||||
|
||||
type testFile struct {
|
||||
content string
|
||||
name string
|
||||
}
|
||||
|
||||
testUnits, _, _, _, err := testrunner.ParseTestFilesAndSymlinks(
|
||||
string(sourceText),
|
||||
file.path,
|
||||
func(filename string, content string, fileOptions map[string]string) (testFile, error) {
|
||||
return testFile{content: content, name: filename}, nil
|
||||
},
|
||||
)
|
||||
assert.NilError(f, err)
|
||||
|
||||
for _, unit := range testUnits {
|
||||
extension := tspath.TryGetExtensionFromPath(unit.name)
|
||||
if extension == "" {
|
||||
continue
|
||||
}
|
||||
f.Add(extension, unit.content, false, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
f.Fuzz(func(t *testing.T, extension string, sourceText string, externalModuleIndicatorOptionsJSX bool, externalModuleIndicatorOptionsForce bool) {
|
||||
if !extensions.Has(extension) {
|
||||
t.Skip()
|
||||
}
|
||||
|
||||
fileName := "/index" + extension
|
||||
path := tspath.Path(fileName)
|
||||
|
||||
opts := ast.SourceFileParseOptions{
|
||||
FileName: fileName,
|
||||
Path: path,
|
||||
ExternalModuleIndicatorOptions: ast.ExternalModuleIndicatorOptions{
|
||||
JSX: externalModuleIndicatorOptionsJSX,
|
||||
Force: externalModuleIndicatorOptionsForce,
|
||||
},
|
||||
}
|
||||
|
||||
parser.ParseSourceFile(opts, sourceText, core.GetScriptKindFromFileName(fileName))
|
||||
})
|
||||
}
|
||||
|
||||
func TestJSDocImportTypeParentChain(t *testing.T) {
|
||||
t.Parallel()
|
||||
sourceText := `test("", async function () {
|
||||
;(/** @type {typeof import("a")} */ ({}))
|
||||
})
|
||||
|
||||
test("", async function () {
|
||||
;(/** @type {typeof import("a")} */ a)
|
||||
})
|
||||
|
||||
test("", async function () {
|
||||
(/** @type {typeof import("a")} */ ({}))
|
||||
;(/** @type {typeof import("a")} */ ({}))
|
||||
})
|
||||
|
||||
test("", async function () {
|
||||
(/** @type {typeof import("a")} */ a)
|
||||
;(/** @type {typeof import("a")} */ a)
|
||||
})
|
||||
|
||||
test("", async function () {
|
||||
(/** @type {typeof import("a")} */ ({}))
|
||||
;(/** @type {typeof import("a")} */ ({}))
|
||||
})
|
||||
`
|
||||
opts := ast.SourceFileParseOptions{
|
||||
FileName: "/index.js",
|
||||
Path: "/index.js",
|
||||
}
|
||||
|
||||
file := parser.ParseSourceFile(opts, sourceText, core.ScriptKindJS)
|
||||
|
||||
for i := 1; i < len(file.ReparsedClones); i++ {
|
||||
a, b := file.ReparsedClones[i-1], file.ReparsedClones[i]
|
||||
if a.Pos() == b.Pos() && a.End() == b.End() && a.Kind == b.Kind {
|
||||
t.Errorf("duplicate ReparsedClones at [%d] and [%d]: %s pos=%d end=%d", i-1, i, a.Kind.String(), a.Pos(), a.End())
|
||||
}
|
||||
}
|
||||
|
||||
for _, imp := range file.Imports() {
|
||||
reparsed := ast.GetReparsedNodeForNode(imp)
|
||||
if ast.GetSourceFileOfNode(reparsed) == nil {
|
||||
t.Errorf("reparsed import at pos=%d has broken parent chain", imp.Pos())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSourceFileContainsNonASCIIInStringLiteralFastPath(t *testing.T) {
|
||||
t.Parallel()
|
||||
sourceText := `const x = "─";
|
||||
|
||||
namespace N {
|
||||
export const y = x;
|
||||
}
|
||||
`
|
||||
opts := ast.SourceFileParseOptions{
|
||||
FileName: "/index.ts",
|
||||
Path: "/index.ts",
|
||||
}
|
||||
|
||||
file := parser.ParseSourceFile(opts, sourceText, core.ScriptKindTS)
|
||||
|
||||
assert.Assert(t, file.ContainsNonASCII)
|
||||
positionMap := file.GetPositionMap()
|
||||
assert.Assert(t, !positionMap.IsAsciiOnly())
|
||||
afterBoxDrawingCharacter := strings.Index(sourceText, "─") + len("─")
|
||||
assert.Equal(t, positionMap.UTF8ToUTF16(afterBoxDrawingCharacter), afterBoxDrawingCharacter-2)
|
||||
assert.Equal(t, positionMap.UTF8ToUTF16(len(sourceText)), len(sourceText)-2)
|
||||
}
|
||||
71
tools/tsgo/internal/parser/references.go
Normal file
71
tools/tsgo/internal/parser/references.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/microsoft/typescript-go/internal/ast"
|
||||
"github.com/microsoft/typescript-go/internal/core"
|
||||
"github.com/microsoft/typescript-go/internal/tspath"
|
||||
)
|
||||
|
||||
func collectExternalModuleReferences(file *ast.SourceFile) {
|
||||
for _, node := range file.Statements.Nodes {
|
||||
collectModuleReferences(file, node, false /*inAmbientModule*/)
|
||||
}
|
||||
|
||||
if file.Flags&ast.NodeFlagsPossiblyContainsDynamicImport != 0 || ast.IsInJSFile(file.AsNode()) {
|
||||
ast.ForEachDynamicImportOrRequireCall(file /*includeTypeSpaceImports*/, true /*requireStringLiteralLikeArgument*/, true, func(node *ast.Node, moduleSpecifier *ast.Expression) bool {
|
||||
ast.SetImportsOfSourceFile(file, append(file.Imports(), moduleSpecifier))
|
||||
return false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func collectModuleReferences(file *ast.SourceFile, node *ast.Statement, inAmbientModule bool) {
|
||||
if ast.IsAnyImportOrReExport(node) {
|
||||
moduleNameExpr := ast.GetExternalModuleName(node)
|
||||
// TypeScript 1.0 spec (April 2014): 12.1.6
|
||||
// An ExternalImportDeclaration in an AmbientExternalModuleDeclaration may reference other external modules
|
||||
// only through top - level external module names. Relative external module names are not permitted.
|
||||
if moduleNameExpr != nil && ast.IsStringLiteral(moduleNameExpr) {
|
||||
moduleName := moduleNameExpr.Text()
|
||||
if moduleName != "" && (!inAmbientModule || !tspath.IsExternalModuleNameRelative(moduleName)) {
|
||||
ast.SetImportsOfSourceFile(file, append(file.Imports(), moduleNameExpr))
|
||||
// !!! removed `&& p.currentNodeModulesDepth == 0`
|
||||
if file.UsesUriStyleNodeCoreModules != core.TSTrue && !file.IsDeclarationFile {
|
||||
if strings.HasPrefix(moduleName, "node:") && !core.ExclusivelyPrefixedNodeCoreModules[moduleName] {
|
||||
// Presence of `node:` prefix takes precedence over unprefixed node core modules
|
||||
file.UsesUriStyleNodeCoreModules = core.TSTrue
|
||||
} else if file.UsesUriStyleNodeCoreModules == core.TSUnknown && core.UnprefixedNodeCoreModules[moduleName] {
|
||||
// Avoid `unprefixedNodeCoreModules.has` for every import
|
||||
file.UsesUriStyleNodeCoreModules = core.TSFalse
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
if ast.IsModuleDeclaration(node) && ast.IsAmbientModule(node) && (inAmbientModule || ast.HasSyntacticModifier(node, ast.ModifierFlagsAmbient) || file.IsDeclarationFile) {
|
||||
nameText := node.AsModuleDeclaration().Name().Text()
|
||||
// Ambient module declarations can be interpreted as augmentations for some existing external modules.
|
||||
// This will happen in two cases:
|
||||
// - if current file is external module then module augmentation is a ambient module declaration defined in the top level scope
|
||||
// - if current file is not external module then module augmentation is an ambient module declaration with non-relative module name
|
||||
// immediately nested in top level ambient module declaration .
|
||||
if ast.IsExternalModule(file) || (inAmbientModule && !tspath.IsExternalModuleNameRelative(nameText)) {
|
||||
file.ModuleAugmentations = append(file.ModuleAugmentations, node.AsModuleDeclaration().Name())
|
||||
} else if !inAmbientModule {
|
||||
file.AmbientModuleNames = append(file.AmbientModuleNames, nameText)
|
||||
// An AmbientExternalModuleDeclaration declares an external module.
|
||||
// This type of declaration is permitted only in the global module.
|
||||
// The StringLiteral must specify a top - level external module name.
|
||||
// Relative external module names are not permitted
|
||||
// NOTE: body of ambient module is always a module block, if it exists
|
||||
if node.Body() != nil {
|
||||
for _, statement := range node.Body().Statements() {
|
||||
collectModuleReferences(file, statement, true /*inAmbientModule*/)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
748
tools/tsgo/internal/parser/reparser.go
Normal file
748
tools/tsgo/internal/parser/reparser.go
Normal file
@@ -0,0 +1,748 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/microsoft/typescript-go/internal/ast"
|
||||
"github.com/microsoft/typescript-go/internal/core"
|
||||
"github.com/microsoft/typescript-go/internal/diagnostics"
|
||||
"github.com/microsoft/typescript-go/internal/scanner"
|
||||
)
|
||||
|
||||
func (p *Parser) finishReparsedNode(node *ast.Node, locationNode *ast.Node) {
|
||||
node.Flags = p.contextFlags | ast.NodeFlagsReparsed
|
||||
node.Loc = locationNode.Loc
|
||||
p.overrideParentInImmediateChildren(node)
|
||||
}
|
||||
|
||||
func (p *Parser) finishMutatedNode(node *ast.Node) {
|
||||
p.overrideParentInImmediateChildren(node)
|
||||
}
|
||||
|
||||
// Deep-clone the given node and add the clone to the reparsed clone list. The list is used by ast.GetReparsedNodeForNode
|
||||
// to locate reparsed clones of JSDoc nodes. Since the binder attaches symbols to reparsed nodes and not to JSDoc nodes, we
|
||||
// need the mapping when obtaining symbols and types from JSDoc nodes.
|
||||
func (p *Parser) addDeepCloneReparse(node *ast.Node) *ast.Node {
|
||||
clone := p.factory.DeepCloneReparse(node)
|
||||
if clone != nil {
|
||||
p.reparsedClones = append(p.reparsedClones, clone)
|
||||
}
|
||||
return clone
|
||||
}
|
||||
|
||||
func (p *Parser) addTransformedReparse(newNode *ast.Node, old *ast.Node) *ast.Node {
|
||||
p.finishReparsedNode(newNode, old)
|
||||
newNode.Flags |= ast.NodeFlagsReparserTransformedLiteral
|
||||
p.reparsedClones = append(p.reparsedClones, newNode)
|
||||
return newNode
|
||||
}
|
||||
|
||||
func (p *Parser) checkNonIdentifierName(name *ast.Node) *ast.Node {
|
||||
if ast.IsIdentifier(name) && !scanner.IsValidIdentifier(name.AsIdentifier().Text) {
|
||||
errLoc := name.Loc
|
||||
if errLoc.Len() == 0 { // missing name, emit error on the character before the missing name node
|
||||
errLoc = core.NewTextRange(name.Loc.Pos()-1, name.Loc.Pos())
|
||||
}
|
||||
p.parseErrorAtRange(errLoc, diagnostics.Identifier_expected)
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
// Hosted tags find a host and add their children to the correct location under the host.
|
||||
// Unhosted tags add synthetic nodes to the reparse list.
|
||||
func (p *Parser) reparseTags(parent *ast.Node, jsDoc []*ast.Node) {
|
||||
for _, j := range jsDoc {
|
||||
isLast := j == jsDoc[len(jsDoc)-1]
|
||||
tags := j.AsJSDoc().Tags
|
||||
if tags == nil {
|
||||
continue
|
||||
}
|
||||
for _, tag := range tags.Nodes {
|
||||
p.reparseUnhosted(tag, parent, j)
|
||||
if isLast {
|
||||
p.reparseHosted(tag, parent, j)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) reparseUnhosted(tag *ast.Node, parent *ast.Node, jsDoc *ast.Node) {
|
||||
switch tag.Kind {
|
||||
case ast.KindJSDocTypedefTag:
|
||||
typeExpression := tag.TypeExpression()
|
||||
if typeExpression == nil {
|
||||
break
|
||||
}
|
||||
fullName := tag.Name()
|
||||
isNamespace := fullName != nil && ast.IsModuleDeclaration(fullName)
|
||||
var modifiers *ast.ModifierList
|
||||
if isNamespace {
|
||||
modifiers = p.createExportModifier(tag)
|
||||
}
|
||||
typeAlias := p.factory.NewJSTypeAliasDeclaration(modifiers, p.addDeepCloneReparse(p.checkNonIdentifierName(p.getInnermostNameOfJSDocNamespace(fullName))), nil, nil)
|
||||
typeAlias.AsTypeAliasDeclaration().TypeParameters = p.gatherTypeParameters(jsDoc, true /*typedefOrCallback*/)
|
||||
var t *ast.Node
|
||||
switch typeExpression.Kind {
|
||||
case ast.KindJSDocTypeExpression:
|
||||
t = p.addDeepCloneReparse(typeExpression.Type())
|
||||
case ast.KindJSDocTypeLiteral:
|
||||
t = p.reparseJSDocTypeLiteral(typeExpression)
|
||||
default:
|
||||
panic("typedef tag type expression should be a name reference or a type expression" + typeExpression.Kind.String())
|
||||
}
|
||||
typeAlias.AsTypeAliasDeclaration().Type = t
|
||||
p.finishReparsedNode(typeAlias, tag)
|
||||
p.jsdocInfos = append(p.jsdocInfos, JSDocInfo{parent: typeAlias, jsDocs: []*ast.Node{jsDoc}})
|
||||
typeAlias.Flags |= ast.NodeFlagsHasJSDoc
|
||||
result := p.wrapInJSDocNamespace(fullName, typeAlias, false /*nested*/)
|
||||
p.reparseList = append(p.reparseList, result)
|
||||
case ast.KindJSDocCallbackTag:
|
||||
typeExpression := tag.TypeExpression()
|
||||
if typeExpression == nil {
|
||||
break
|
||||
}
|
||||
fullName := tag.Name()
|
||||
isNamespace := fullName != nil && ast.IsModuleDeclaration(fullName)
|
||||
var modifiers *ast.ModifierList
|
||||
if isNamespace {
|
||||
modifiers = p.createExportModifier(tag)
|
||||
}
|
||||
functionType := p.reparseJSDocSignature(typeExpression, tag, jsDoc, tag, nil)
|
||||
typeAlias := p.factory.NewJSTypeAliasDeclaration(modifiers, p.addDeepCloneReparse(p.getInnermostNameOfJSDocNamespace(fullName)), nil, functionType)
|
||||
typeAlias.AsTypeAliasDeclaration().TypeParameters = p.gatherTypeParameters(jsDoc, true /*typedefOrCallback*/)
|
||||
p.finishReparsedNode(typeAlias, tag)
|
||||
p.jsdocInfos = append(p.jsdocInfos, JSDocInfo{parent: typeAlias, jsDocs: []*ast.Node{jsDoc}})
|
||||
typeAlias.Flags |= ast.NodeFlagsHasJSDoc
|
||||
result := p.wrapInJSDocNamespace(fullName, typeAlias, false /*nested*/)
|
||||
p.reparseList = append(p.reparseList, result)
|
||||
case ast.KindJSDocImportTag:
|
||||
importTag := tag.AsJSDocImportTag()
|
||||
if importTag.ImportClause == nil {
|
||||
break
|
||||
}
|
||||
importClause := p.addDeepCloneReparse(importTag.ImportClause)
|
||||
importClause.AsImportClause().PhaseModifier = ast.KindTypeKeyword
|
||||
importDeclaration := p.factory.NewJSImportDeclaration(
|
||||
p.factory.DeepCloneReparseModifiers(importTag.Modifiers()),
|
||||
importClause,
|
||||
p.addDeepCloneReparse(importTag.ModuleSpecifier),
|
||||
p.addDeepCloneReparse(importTag.Attributes),
|
||||
)
|
||||
p.finishReparsedNode(importDeclaration, tag)
|
||||
p.reparseList = append(p.reparseList, importDeclaration)
|
||||
case ast.KindJSDocOverloadTag:
|
||||
// Create overload signatures only for function, method, and constructor declarations outside object literals
|
||||
if (ast.IsFunctionDeclaration(parent) || ast.IsMethodDeclaration(parent) || ast.IsConstructorDeclaration(parent)) && p.parsingContexts&(1<<PCObjectLiteralMembers) == 0 {
|
||||
p.reparseList = append(p.reparseList, p.reparseJSDocSignature(tag.AsJSDocOverloadTag().TypeExpression, parent, jsDoc, tag, parent.Modifiers()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) reparseJSDocSignature(jsSignature *ast.Node, fun *ast.Node, jsDoc *ast.Node, tag *ast.Node, modifiers *ast.ModifierList) *ast.Node {
|
||||
var signature *ast.Node
|
||||
clonedModifiers := p.factory.DeepCloneReparseModifiers(modifiers)
|
||||
switch fun.Kind {
|
||||
case ast.KindFunctionDeclaration:
|
||||
signature = p.factory.NewFunctionDeclaration(clonedModifiers, nil, p.factory.DeepCloneReparse(p.checkNonIdentifierName(fun.Name())), nil, nil, nil, nil, nil)
|
||||
case ast.KindMethodDeclaration:
|
||||
signature = p.factory.NewMethodDeclaration(clonedModifiers, nil, p.factory.DeepCloneReparse(p.checkNonIdentifierName(fun.Name())), nil, nil, nil, nil, nil, nil)
|
||||
case ast.KindConstructor:
|
||||
signature = p.factory.NewConstructorDeclaration(clonedModifiers, nil, nil, nil, nil, nil)
|
||||
case ast.KindJSDocCallbackTag:
|
||||
signature = p.factory.NewFunctionTypeNode(nil, nil, p.factory.NewKeywordTypeNode(ast.KindAnyKeyword))
|
||||
default:
|
||||
panic("Unexpected kind " + fun.Kind.String())
|
||||
}
|
||||
|
||||
if tag.Kind != ast.KindJSDocCallbackTag {
|
||||
signature.FunctionLikeData().TypeParameters = p.gatherTypeParameters(jsDoc, false /*typedefOrCallback*/)
|
||||
}
|
||||
parameters := p.nodeSliceArena.NewSlice(0)
|
||||
for pi, param := range jsSignature.Parameters() {
|
||||
var parameter *ast.Node
|
||||
if param.Kind == ast.KindJSDocThisTag {
|
||||
thisTag := param.AsJSDocThisTag()
|
||||
thisIdent := p.factory.NewIdentifier("this")
|
||||
thisIdent.Loc = thisTag.Loc
|
||||
thisIdent.Flags = p.contextFlags | ast.NodeFlagsReparsed
|
||||
parameter = p.factory.NewParameterDeclaration(nil, nil, thisIdent, nil, nil, nil)
|
||||
if thisTag.TypeExpression != nil {
|
||||
parameter.AsParameterDeclaration().Type = p.addDeepCloneReparse(thisTag.TypeExpression.Type())
|
||||
}
|
||||
} else if param.Kind == ast.KindJSDocParameterTag || param.Kind == ast.KindJSDocPropertyTag {
|
||||
jsparam := param.AsJSDocParameterOrPropertyTag()
|
||||
// Skip sub-property parameters (e.g., @param x.y) - these have QualifiedNames
|
||||
// and describe properties of a parent parameter, not standalone parameters.
|
||||
if ast.IsQualifiedName(jsparam.Name()) {
|
||||
continue
|
||||
}
|
||||
var dotDotDotToken *ast.Node
|
||||
var paramType *ast.TypeNode
|
||||
|
||||
if jsparam.TypeExpression != nil {
|
||||
if jsparam.TypeExpression.Type().Kind == ast.KindJSDocVariadicType {
|
||||
dotDotDotToken = p.factory.NewToken(ast.KindDotDotDotToken)
|
||||
dotDotDotToken.Loc = jsparam.Loc
|
||||
dotDotDotToken.Flags = p.contextFlags | ast.NodeFlagsReparsed
|
||||
|
||||
variadicType := jsparam.TypeExpression.Type().AsJSDocVariadicType()
|
||||
paramType = p.reparseJSDocTypeLiteral(variadicType.Type)
|
||||
} else {
|
||||
paramType = p.reparseJSDocTypeLiteral(jsparam.TypeExpression.Type())
|
||||
}
|
||||
}
|
||||
name := jsparam.Name()
|
||||
if ast.IsIdentifier(name) && !scanner.IsValidIdentifier(name.AsIdentifier().Text) {
|
||||
// drop invalid chars for _, if empty, write _0, etc., so we have a valid param name to emit later
|
||||
result := strings.Builder{}
|
||||
for i, ch := range name.AsIdentifier().Text {
|
||||
if i == 0 {
|
||||
if !scanner.IsIdentifierStart(ch) {
|
||||
result.WriteRune('_')
|
||||
} else {
|
||||
result.WriteRune(ch)
|
||||
}
|
||||
continue
|
||||
} else if !scanner.IsIdentifierPart(ch) {
|
||||
result.WriteRune('_')
|
||||
} else {
|
||||
result.WriteRune(ch)
|
||||
}
|
||||
}
|
||||
if result.Len() == 0 {
|
||||
result.WriteRune('_')
|
||||
result.WriteString(strconv.Itoa(pi))
|
||||
}
|
||||
name = p.addTransformedReparse(p.factory.NewIdentifier(result.String()), name)
|
||||
} else {
|
||||
name = p.addDeepCloneReparse(name)
|
||||
}
|
||||
parameter = p.factory.NewParameterDeclaration(nil, dotDotDotToken, name, p.makeQuestionIfOptional(jsparam), paramType, nil)
|
||||
}
|
||||
p.finishReparsedNode(parameter, param)
|
||||
parameters = append(parameters, parameter)
|
||||
p.reparseJSDocComment(parameter, param)
|
||||
}
|
||||
signature.FunctionLikeData().Parameters = p.newNodeList(jsSignature.AsJSDocSignature().Parameters.Loc, parameters)
|
||||
|
||||
if jsSignature.Type() != nil && jsSignature.Type().TypeExpression() != nil {
|
||||
signature.FunctionLikeData().Type = p.addDeepCloneReparse(jsSignature.Type().TypeExpression().Type())
|
||||
}
|
||||
loc := jsSignature
|
||||
if tag.Kind == ast.KindJSDocOverloadTag {
|
||||
loc = tag.TagName()
|
||||
}
|
||||
p.finishReparsedNode(signature, loc)
|
||||
return signature
|
||||
}
|
||||
|
||||
func (p *Parser) reparseJSDocTypeLiteral(t *ast.TypeNode) *ast.Node {
|
||||
if t == nil {
|
||||
return nil
|
||||
}
|
||||
if t.Kind == ast.KindJSDocTypeLiteral {
|
||||
jstypeliteral := t.AsJSDocTypeLiteral()
|
||||
isArrayType := jstypeliteral.IsArrayType
|
||||
properties := p.nodeSliceArena.NewSlice(0)
|
||||
for _, prop := range jstypeliteral.JSDocPropertyTags {
|
||||
if prop.Kind != ast.KindJSDocPropertyTag && prop.Kind != ast.KindJSDocParameterTag {
|
||||
continue
|
||||
}
|
||||
jsprop := prop.AsJSDocParameterOrPropertyTag()
|
||||
name := prop.Name()
|
||||
if name.Kind == ast.KindQualifiedName {
|
||||
name = name.AsQualifiedName().Right
|
||||
}
|
||||
if ast.IsIdentifier(name) && !scanner.IsValidIdentifier(name.AsIdentifier().Text) {
|
||||
name = p.addTransformedReparse(p.factory.NewStringLiteral(name.AsIdentifier().Text, ast.TokenFlagsNone), name)
|
||||
} else {
|
||||
name = p.addDeepCloneReparse(name)
|
||||
}
|
||||
property := p.factory.NewPropertySignatureDeclaration(nil, name, p.makeQuestionIfOptional(jsprop), nil, nil)
|
||||
if jsprop.TypeExpression != nil {
|
||||
property.AsPropertySignatureDeclaration().Type = p.reparseJSDocTypeLiteral(jsprop.TypeExpression.Type())
|
||||
}
|
||||
p.finishReparsedNode(property, prop)
|
||||
properties = append(properties, property)
|
||||
p.reparseJSDocComment(property, prop)
|
||||
}
|
||||
t = p.factory.NewTypeLiteralNode(p.newNodeList(jstypeliteral.Loc, properties))
|
||||
if isArrayType {
|
||||
p.finishReparsedNode(t, jstypeliteral.AsNode())
|
||||
t = p.factory.NewArrayTypeNode(t)
|
||||
}
|
||||
p.finishReparsedNode(t, jstypeliteral.AsNode())
|
||||
return t
|
||||
}
|
||||
return p.addDeepCloneReparse(t)
|
||||
}
|
||||
|
||||
func (p *Parser) reparseJSDocComment(node *ast.Node, tag *ast.Node) {
|
||||
if comment := tag.CommentList(); comment != nil {
|
||||
newComment := p.factory.NewNodeList(core.Map(comment.Nodes, p.factory.DeepCloneReparse))
|
||||
newComment.Loc = comment.Loc
|
||||
propJSDoc := p.factory.NewJSDoc(newComment, nil)
|
||||
p.finishReparsedNode(propJSDoc, tag)
|
||||
propJSDoc.Parent = node
|
||||
p.jsdocInfos = append(p.jsdocInfos, JSDocInfo{parent: node, jsDocs: []*ast.Node{propJSDoc}})
|
||||
node.Flags |= ast.NodeFlagsHasJSDoc
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) gatherTypeParameters(j *ast.Node, typedefOrCallback bool) *ast.NodeList {
|
||||
var typeParameters []*ast.Node
|
||||
pos := -1
|
||||
endPos := -1
|
||||
firstTemplate := true
|
||||
for _, tag := range j.AsJSDoc().Tags.Nodes {
|
||||
// When a JSDoc comment contains an `@typedef` or `@callback` tag, `@template` type parameter
|
||||
// declarations apply to the type being defined.
|
||||
if !typedefOrCallback && (ast.IsJSDocTypedefTag(tag) || ast.IsJSDocCallbackTag(tag)) {
|
||||
return nil
|
||||
}
|
||||
if !ast.IsJSDocTemplateTag(tag) {
|
||||
continue
|
||||
}
|
||||
if firstTemplate {
|
||||
pos = tag.Pos()
|
||||
firstTemplate = false
|
||||
}
|
||||
endPos = tag.End()
|
||||
constraint := tag.AsJSDocTemplateTag().Constraint
|
||||
firstTypeParameter := true
|
||||
for _, tp := range tag.TypeParameters() {
|
||||
var reparse *ast.Node
|
||||
if constraint != nil && firstTypeParameter {
|
||||
reparse = p.factory.NewTypeParameterDeclaration(
|
||||
p.factory.DeepCloneReparseModifiers(tp.Modifiers()),
|
||||
p.addDeepCloneReparse(p.checkNonIdentifierName(tp.Name())),
|
||||
p.addDeepCloneReparse(constraint.Type()),
|
||||
nil, // expression
|
||||
p.addDeepCloneReparse(tp.AsTypeParameterDeclaration().DefaultType),
|
||||
)
|
||||
p.finishReparsedNode(reparse, tp)
|
||||
} else {
|
||||
reparse = p.addDeepCloneReparse(tp)
|
||||
}
|
||||
if typeParameters == nil {
|
||||
typeParameters = p.nodeSliceArena.NewSlice(0)
|
||||
}
|
||||
typeParameters = append(typeParameters, reparse)
|
||||
firstTypeParameter = false
|
||||
}
|
||||
}
|
||||
if len(typeParameters) == 0 {
|
||||
return nil
|
||||
} else {
|
||||
return p.newNodeList(core.NewTextRange(pos, endPos), typeParameters)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) reparseHosted(tag *ast.Node, parent *ast.Node, jsDoc *ast.Node) {
|
||||
switch tag.Kind {
|
||||
case ast.KindJSDocTypeTag:
|
||||
switch parent.Kind {
|
||||
case ast.KindVariableStatement:
|
||||
if parent.AsVariableStatement().DeclarationList != nil {
|
||||
for _, declaration := range parent.AsVariableStatement().DeclarationList.AsVariableDeclarationList().Declarations.Nodes {
|
||||
if declaration.Type() == nil && tag.TypeExpression() != nil {
|
||||
declaration.AsMutable().SetType(p.addDeepCloneReparse(tag.TypeExpression().Type()))
|
||||
p.finishMutatedNode(declaration)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
case ast.KindVariableDeclaration, ast.KindExportAssignment, ast.KindPropertyDeclaration, ast.KindPropertyAssignment,
|
||||
ast.KindShorthandPropertyAssignment, ast.KindGetAccessor:
|
||||
if parent.Type() == nil && tag.TypeExpression() != nil {
|
||||
parent.AsMutable().SetType(p.addDeepCloneReparse(tag.TypeExpression().Type()))
|
||||
p.finishMutatedNode(parent)
|
||||
return
|
||||
}
|
||||
case ast.KindParameter:
|
||||
if parent.Type() == nil && tag.TypeExpression() != nil {
|
||||
parent.AsMutable().SetType(p.reparseJSDocTypeLiteral(tag.TypeExpression().Type()))
|
||||
p.finishMutatedNode(parent)
|
||||
return
|
||||
}
|
||||
case ast.KindExpressionStatement:
|
||||
if parent.Expression().Kind == ast.KindBinaryExpression {
|
||||
bin := parent.Expression().AsBinaryExpression()
|
||||
if kind := ast.GetAssignmentDeclarationKind(bin.AsNode()); kind != ast.JSDeclarationKindNone && tag.TypeExpression() != nil {
|
||||
bin.AsMutable().SetType(p.addDeepCloneReparse(tag.TypeExpression().Type()))
|
||||
p.finishMutatedNode(bin.AsNode())
|
||||
return
|
||||
}
|
||||
}
|
||||
case ast.KindReturnStatement, ast.KindParenthesizedExpression:
|
||||
if parent.Expression() != nil && tag.TypeExpression() != nil {
|
||||
parent.AsMutable().SetExpression(p.makeNewCast(
|
||||
p.addDeepCloneReparse(tag.TypeExpression().Type()),
|
||||
parent.Expression(),
|
||||
true, /*isAssertion*/
|
||||
))
|
||||
p.finishMutatedNode(parent)
|
||||
return
|
||||
}
|
||||
}
|
||||
if fun := getFunctionLikeHost(parent); fun != nil {
|
||||
noTypedParams := core.Every(fun.Parameters(), func(param *ast.Node) bool { return param.Type() == nil })
|
||||
if fun.TypeParameterList() == nil && fun.Type() == nil && noTypedParams && tag.TypeExpression() != nil {
|
||||
fun.FunctionLikeData().FullSignature = p.addDeepCloneReparse(tag.TypeExpression().Type())
|
||||
p.finishMutatedNode(fun)
|
||||
}
|
||||
}
|
||||
case ast.KindJSDocSatisfiesTag:
|
||||
switch parent.Kind {
|
||||
case ast.KindVariableStatement:
|
||||
if parent.AsVariableStatement().DeclarationList != nil {
|
||||
for _, declaration := range parent.AsVariableStatement().DeclarationList.AsVariableDeclarationList().Declarations.Nodes {
|
||||
if declaration.Initializer() != nil && tag.TypeExpression() != nil {
|
||||
declaration.AsMutable().SetInitializer(p.makeNewCast(
|
||||
p.addDeepCloneReparse(tag.TypeExpression().Type()),
|
||||
declaration.Initializer(),
|
||||
false, /*isAssertion*/
|
||||
))
|
||||
p.finishMutatedNode(declaration)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
case ast.KindVariableDeclaration, ast.KindPropertyDeclaration, ast.KindPropertyAssignment:
|
||||
if parent.Initializer() != nil && tag.TypeExpression() != nil {
|
||||
parent.AsMutable().SetInitializer(p.makeNewCast(
|
||||
p.addDeepCloneReparse(tag.TypeExpression().Type()),
|
||||
parent.Initializer(),
|
||||
false, /*isAssertion*/
|
||||
))
|
||||
p.finishMutatedNode(parent)
|
||||
}
|
||||
case ast.KindShorthandPropertyAssignment:
|
||||
shorthand := parent.AsShorthandPropertyAssignment()
|
||||
if shorthand.ObjectAssignmentInitializer != nil && tag.AsJSDocSatisfiesTag().TypeExpression != nil {
|
||||
shorthand.ObjectAssignmentInitializer = p.makeNewCast(
|
||||
p.addDeepCloneReparse(tag.AsJSDocSatisfiesTag().TypeExpression.Type()),
|
||||
shorthand.ObjectAssignmentInitializer,
|
||||
false, /*isAssertion*/
|
||||
)
|
||||
p.finishMutatedNode(parent)
|
||||
}
|
||||
case ast.KindReturnStatement, ast.KindParenthesizedExpression, ast.KindExportAssignment:
|
||||
if parent.Expression() != nil && tag.TypeExpression() != nil {
|
||||
parent.AsMutable().SetExpression(p.makeNewCast(
|
||||
p.addDeepCloneReparse(tag.TypeExpression().Type()),
|
||||
parent.Expression(),
|
||||
false, /*isAssertion*/
|
||||
))
|
||||
p.finishMutatedNode(parent)
|
||||
}
|
||||
case ast.KindExpressionStatement:
|
||||
if parent.Expression().Kind == ast.KindBinaryExpression {
|
||||
bin := parent.Expression().AsBinaryExpression()
|
||||
if kind := ast.GetAssignmentDeclarationKind(bin.AsNode()); kind != ast.JSDeclarationKindNone && tag.TypeExpression() != nil {
|
||||
bin.Right = p.makeNewCast(
|
||||
p.addDeepCloneReparse(tag.TypeExpression().Type()),
|
||||
bin.Right,
|
||||
false, /*isAssertion*/
|
||||
)
|
||||
p.finishMutatedNode(bin.AsNode())
|
||||
}
|
||||
}
|
||||
}
|
||||
case ast.KindJSDocTemplateTag:
|
||||
if fun := getFunctionLikeHost(parent); fun != nil {
|
||||
if fun.TypeParameters() == nil && fun.FunctionLikeData().FullSignature == nil {
|
||||
fun.FunctionLikeData().TypeParameters = p.gatherTypeParameters(jsDoc, false /*typedefOrCallback*/)
|
||||
p.finishMutatedNode(fun)
|
||||
}
|
||||
} else if parent.Kind == ast.KindClassDeclaration {
|
||||
class := parent.AsClassDeclaration()
|
||||
if class.TypeParameters == nil {
|
||||
class.TypeParameters = p.gatherTypeParameters(jsDoc, false /*typedefOrCallback*/)
|
||||
p.finishMutatedNode(parent)
|
||||
}
|
||||
} else if parent.Kind == ast.KindClassExpression {
|
||||
class := parent.AsClassExpression()
|
||||
if class.TypeParameters == nil {
|
||||
class.TypeParameters = p.gatherTypeParameters(jsDoc, false /*typedefOrCallback*/)
|
||||
p.finishMutatedNode(parent)
|
||||
}
|
||||
}
|
||||
case ast.KindJSDocParameterTag:
|
||||
if fun := getFunctionLikeHost(parent); fun != nil && fun.FunctionLikeData().FullSignature == nil {
|
||||
parameterTag := tag.AsJSDocParameterOrPropertyTag()
|
||||
if param, ok := findMatchingParameter(fun, parameterTag, jsDoc); ok {
|
||||
if param.Type == nil && parameterTag.TypeExpression != nil {
|
||||
param.AsParameterDeclaration().Type = p.reparseJSDocTypeLiteral(parameterTag.TypeExpression.Type())
|
||||
}
|
||||
if param.QuestionToken == nil {
|
||||
if question := p.makeQuestionIfOptional(parameterTag); question != nil {
|
||||
param.QuestionToken = question
|
||||
}
|
||||
}
|
||||
p.finishMutatedNode(param.AsNode())
|
||||
}
|
||||
}
|
||||
case ast.KindJSDocThisTag:
|
||||
if fun := getFunctionLikeHost(parent); fun != nil {
|
||||
params := fun.Parameters()
|
||||
if len(params) == 0 || (params[0].Name().Kind != ast.KindThisKeyword && !ast.IsThisIdentifier(params[0].Name())) {
|
||||
thisParam := p.factory.NewParameterDeclaration(
|
||||
nil, /* decorators */
|
||||
nil, /* modifiers */
|
||||
p.factory.NewIdentifier("this"),
|
||||
nil, /* questionToken */
|
||||
nil, /* type */
|
||||
nil, /* initializer */
|
||||
)
|
||||
if tag.AsJSDocThisTag().TypeExpression != nil {
|
||||
thisParam.AsParameterDeclaration().Type = p.addDeepCloneReparse(tag.AsJSDocThisTag().TypeExpression.Type())
|
||||
}
|
||||
p.finishReparsedNode(thisParam, tag.TagName())
|
||||
|
||||
newParams := p.nodeSliceArena.NewSlice(len(params) + 1)
|
||||
newParams[0] = thisParam
|
||||
for i, param := range params {
|
||||
newParams[i+1] = param
|
||||
}
|
||||
|
||||
fun.FunctionLikeData().Parameters = p.newNodeList(fun.ParameterList().Loc, newParams)
|
||||
p.finishMutatedNode(fun)
|
||||
}
|
||||
}
|
||||
case ast.KindJSDocReturnTag:
|
||||
if fun := getFunctionLikeHost(parent); fun != nil && fun.FunctionLikeData().FullSignature == nil {
|
||||
if fun.Type() == nil && tag.TypeExpression() != nil {
|
||||
fun.FunctionLikeData().Type = p.addDeepCloneReparse(tag.TypeExpression().Type())
|
||||
p.finishMutatedNode(fun)
|
||||
}
|
||||
}
|
||||
case ast.KindJSDocReadonlyTag, ast.KindJSDocPrivateTag, ast.KindJSDocPublicTag, ast.KindJSDocProtectedTag, ast.KindJSDocOverrideTag:
|
||||
if parent.Kind == ast.KindExpressionStatement {
|
||||
parent = parent.Expression()
|
||||
}
|
||||
switch parent.Kind {
|
||||
case ast.KindMethodDeclaration, ast.KindGetAccessor, ast.KindSetAccessor:
|
||||
// In object literals these aren't class-like members, so JSDoc modifiers like @override
|
||||
// or @readonly aren't real modifiers there; reparsing them produces spurious grammar errors (#4437).
|
||||
if p.parsingContexts&(1<<PCObjectLiteralMembers) != 0 {
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
case ast.KindPropertyDeclaration, ast.KindConstructor, ast.KindBinaryExpression:
|
||||
var keyword ast.Kind
|
||||
switch tag.Kind {
|
||||
case ast.KindJSDocReadonlyTag:
|
||||
keyword = ast.KindReadonlyKeyword
|
||||
case ast.KindJSDocPrivateTag:
|
||||
keyword = ast.KindPrivateKeyword
|
||||
case ast.KindJSDocPublicTag:
|
||||
keyword = ast.KindPublicKeyword
|
||||
case ast.KindJSDocProtectedTag:
|
||||
keyword = ast.KindProtectedKeyword
|
||||
case ast.KindJSDocOverrideTag:
|
||||
keyword = ast.KindOverrideKeyword
|
||||
}
|
||||
modifier := p.factory.NewModifier(keyword)
|
||||
modifier.Loc = tag.Loc
|
||||
modifier.Flags = p.contextFlags | ast.NodeFlagsReparsed
|
||||
var nodes []*ast.Node
|
||||
var loc core.TextRange
|
||||
if parent.Modifiers() == nil {
|
||||
nodes = p.nodeSliceArena.NewSlice(1)
|
||||
nodes[0] = modifier
|
||||
loc = tag.Loc
|
||||
} else {
|
||||
nodes = append(parent.ModifierNodes(), modifier)
|
||||
loc = parent.Modifiers().Loc
|
||||
}
|
||||
parent.AsMutable().SetModifiers(p.newModifierList(loc, nodes))
|
||||
p.finishMutatedNode(parent)
|
||||
}
|
||||
case ast.KindJSDocImplementsTag:
|
||||
if class := getClassLikeData(parent); class != nil {
|
||||
implementsTag := tag.AsJSDocImplementsTag()
|
||||
|
||||
if class.HeritageClauses != nil {
|
||||
if implementsClause := core.Find(class.HeritageClauses.Nodes, func(node *ast.Node) bool {
|
||||
return node.AsHeritageClause().Token == ast.KindImplementsKeyword
|
||||
}); implementsClause != nil {
|
||||
implementsClause.AsHeritageClause().Types.Nodes = append(implementsClause.AsHeritageClause().Types.Nodes, p.addDeepCloneReparse(implementsTag.ClassName))
|
||||
p.finishMutatedNode(implementsClause)
|
||||
return
|
||||
}
|
||||
}
|
||||
typesList := p.newNodeList(implementsTag.ClassName.Loc, p.nodeSliceArena.NewSlice1(p.addDeepCloneReparse(implementsTag.ClassName)))
|
||||
|
||||
heritageClause := p.factory.NewHeritageClause(ast.KindImplementsKeyword, typesList)
|
||||
p.finishReparsedNode(heritageClause, implementsTag.ClassName)
|
||||
|
||||
if class.HeritageClauses == nil {
|
||||
heritageClauses := p.newNodeList(implementsTag.ClassName.Loc, p.nodeSliceArena.NewSlice1(heritageClause))
|
||||
class.HeritageClauses = heritageClauses
|
||||
} else {
|
||||
class.HeritageClauses.Nodes = append(class.HeritageClauses.Nodes, heritageClause)
|
||||
}
|
||||
p.finishMutatedNode(parent)
|
||||
}
|
||||
case ast.KindJSDocAugmentsTag:
|
||||
if class := getClassLikeData(parent); class != nil && class.HeritageClauses != nil {
|
||||
if extendsClause := core.Find(class.HeritageClauses.Nodes, func(node *ast.Node) bool {
|
||||
return node.AsHeritageClause().Token == ast.KindExtendsKeyword
|
||||
}); extendsClause != nil && len(extendsClause.AsHeritageClause().Types.Nodes) == 1 {
|
||||
target := extendsClause.AsHeritageClause().Types.Nodes[0].AsExpressionWithTypeArguments()
|
||||
source := tag.ClassName().AsExpressionWithTypeArguments()
|
||||
if ast.HasSamePropertyAccessName(target.Expression, source.Expression) {
|
||||
if target.TypeArguments == nil && source.TypeArguments != nil {
|
||||
newArguments := p.nodeSliceArena.NewSlice(len(source.TypeArguments.Nodes))
|
||||
for i, arg := range source.TypeArguments.Nodes {
|
||||
newArguments[i] = p.addDeepCloneReparse(arg)
|
||||
}
|
||||
target.TypeArguments = p.newNodeList(source.TypeArguments.Loc, newArguments)
|
||||
p.finishMutatedNode(target.AsNode())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) makeQuestionIfOptional(parameter *ast.JSDocParameterOrPropertyTag) *ast.Node {
|
||||
var questionToken *ast.Node
|
||||
if parameter.IsBracketed || parameter.TypeExpression != nil && parameter.TypeExpression.Type().Kind == ast.KindJSDocOptionalType {
|
||||
questionToken = p.factory.NewToken(ast.KindQuestionToken)
|
||||
questionToken.Loc = parameter.Loc
|
||||
questionToken.Flags = p.contextFlags | ast.NodeFlagsReparsed
|
||||
}
|
||||
return questionToken
|
||||
}
|
||||
|
||||
func findMatchingParameter(fun *ast.Node, parameterTag *ast.JSDocParameterOrPropertyTag, jsDoc *ast.Node) (*ast.ParameterDeclaration, bool) {
|
||||
tagIndex := -1
|
||||
paramCount := -1
|
||||
for _, tag := range jsDoc.AsJSDoc().Tags.Nodes {
|
||||
if tag.Kind == ast.KindJSDocParameterTag {
|
||||
paramCount++
|
||||
if tag.AsJSDocParameterOrPropertyTag() == parameterTag {
|
||||
tagIndex = paramCount
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
for parameterIndex, parameter := range fun.Parameters() {
|
||||
if parameter.Name().Kind == ast.KindIdentifier {
|
||||
if parameterTag.Name().Kind == ast.KindIdentifier &&
|
||||
((parameter.Name().Text() == parameterTag.Name().Text()) || (parameterIndex == tagIndex && len(parameterTag.Name().Text()) == 0)) {
|
||||
return parameter.AsParameterDeclaration(), true
|
||||
}
|
||||
} else if parameterIndex == tagIndex {
|
||||
return parameter.AsParameterDeclaration(), true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func skipSatisfiesExpressions(node *ast.Node) *ast.Node {
|
||||
for node != nil && node.Kind == ast.KindSatisfiesExpression {
|
||||
node = node.Expression()
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
func getFunctionLikeHost(host *ast.Node) *ast.Node {
|
||||
fun := host
|
||||
switch host.Kind {
|
||||
case ast.KindVariableStatement:
|
||||
if nodes := host.AsVariableStatement().DeclarationList.AsVariableDeclarationList().Declarations.Nodes; len(nodes) != 0 {
|
||||
fun = nodes[0].Initializer()
|
||||
}
|
||||
case ast.KindPropertyAssignment, ast.KindPropertyDeclaration:
|
||||
fun = host.Initializer()
|
||||
case ast.KindExportAssignment, ast.KindReturnStatement:
|
||||
fun = host.Expression()
|
||||
case ast.KindExpressionStatement:
|
||||
fun = ast.GetRightMostAssignedExpression(host.Expression())
|
||||
}
|
||||
fun = skipSatisfiesExpressions(fun)
|
||||
if ast.IsFunctionLike(fun) {
|
||||
return fun
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Parser) makeNewCast(t *ast.TypeNode, e *ast.Node, isAssertion bool) *ast.Node {
|
||||
var assert *ast.Node
|
||||
if isAssertion {
|
||||
assert = p.factory.NewAsExpression(e, t)
|
||||
} else {
|
||||
assert = p.factory.NewSatisfiesExpression(e, t)
|
||||
}
|
||||
p.finishNodeWithEnd(assert, e.Pos(), e.End())
|
||||
return assert
|
||||
}
|
||||
|
||||
func getClassLikeData(parent *ast.Node) *ast.ClassLikeBase {
|
||||
var class *ast.ClassLikeBase
|
||||
switch parent.Kind {
|
||||
case ast.KindClassDeclaration:
|
||||
class = parent.AsClassDeclaration().ClassLikeData()
|
||||
case ast.KindClassExpression:
|
||||
class = parent.AsClassExpression().ClassLikeData()
|
||||
}
|
||||
return class
|
||||
}
|
||||
|
||||
func (p *Parser) createExportModifier(locationNode *ast.Node) *ast.ModifierList {
|
||||
exportModifier := p.factory.NewModifier(ast.KindExportKeyword)
|
||||
exportModifier.Loc = locationNode.Loc
|
||||
exportModifier.Flags = p.contextFlags | ast.NodeFlagsReparsed
|
||||
nodes := p.nodeSliceArena.NewSlice1(exportModifier)
|
||||
return p.newModifierList(locationNode.Loc, nodes)
|
||||
}
|
||||
|
||||
// getInnermostNameOfJSDocNamespace returns the innermost identifier from a
|
||||
// JSDoc namespace chain (ModuleDeclaration). For a simple identifier, it returns
|
||||
// the identifier itself. For "A.B.C", it returns the identifier "C".
|
||||
func (p *Parser) getInnermostNameOfJSDocNamespace(fullName *ast.Node) *ast.Node {
|
||||
if fullName == nil {
|
||||
return nil
|
||||
}
|
||||
for fullName.Kind == ast.KindModuleDeclaration {
|
||||
body := fullName.AsModuleDeclaration().Body
|
||||
if body == nil {
|
||||
return fullName.Name()
|
||||
}
|
||||
fullName = body
|
||||
}
|
||||
return fullName
|
||||
}
|
||||
|
||||
// wrapInJSDocNamespace wraps a statement (typically a type alias) in namespace
|
||||
// declarations corresponding to a JSDoc dotted name. For example, given name
|
||||
// "A.B.C" and a type alias for C, this produces:
|
||||
//
|
||||
// namespace A { namespace B { type C = ... } }
|
||||
//
|
||||
// If the name is a simple identifier (not a ModuleDeclaration), it returns the
|
||||
// statement as-is.
|
||||
func (p *Parser) wrapInJSDocNamespace(fullName *ast.Node, statement *ast.Node, nested bool) *ast.Node {
|
||||
if fullName == nil || !ast.IsModuleDeclaration(fullName) {
|
||||
return statement
|
||||
}
|
||||
// Recursively wrap from outermost to innermost. Inner namespaces always get an export modifier
|
||||
// so members are accessible via dotted access from outside. The outermost namespace is treated as
|
||||
// exported only in module files via IsImplicitlyExportedJSDocDeclaration (in the binder), so it
|
||||
// does not get an explicit export modifier here.
|
||||
wrapped := p.wrapInJSDocNamespace(fullName.Body(), statement, true /*nested*/)
|
||||
block := p.factory.NewModuleBlock(p.newNodeList(fullName.Loc, p.nodeSliceArena.NewSlice1(wrapped)))
|
||||
p.finishReparsedNode(block, fullName)
|
||||
var modifiers *ast.ModifierList
|
||||
if nested {
|
||||
modifiers = p.createExportModifier(fullName)
|
||||
}
|
||||
result := p.factory.NewModuleDeclaration(modifiers, ast.KindNamespaceKeyword, p.addDeepCloneReparse(fullName.Name()), block)
|
||||
p.finishReparsedNode(result, fullName)
|
||||
p.reparsedClones = append(p.reparsedClones, result)
|
||||
return result
|
||||
}
|
||||
14
tools/tsgo/internal/parser/types.go
Normal file
14
tools/tsgo/internal/parser/types.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package parser
|
||||
|
||||
// ParseFlags
|
||||
|
||||
type ParseFlags uint32
|
||||
|
||||
const (
|
||||
ParseFlagsNone ParseFlags = 0
|
||||
ParseFlagsYield ParseFlags = 1 << 0
|
||||
ParseFlagsAwait ParseFlags = 1 << 1
|
||||
ParseFlagsType ParseFlags = 1 << 2
|
||||
ParseFlagsIgnoreMissingOpenBrace ParseFlags = 1 << 4
|
||||
ParseFlagsJSDoc ParseFlags = 1 << 5
|
||||
)
|
||||
56
tools/tsgo/internal/parser/utilities.go
Normal file
56
tools/tsgo/internal/parser/utilities.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"slices"
|
||||
|
||||
"github.com/microsoft/typescript-go/internal/ast"
|
||||
"github.com/microsoft/typescript-go/internal/core"
|
||||
"github.com/microsoft/typescript-go/internal/scanner"
|
||||
)
|
||||
|
||||
func getLanguageVariant(scriptKind core.ScriptKind) core.LanguageVariant {
|
||||
switch scriptKind {
|
||||
case core.ScriptKindTSX, core.ScriptKindJSX, core.ScriptKindJS, core.ScriptKindJSON:
|
||||
// .tsx and .jsx files are treated as jsx language variant.
|
||||
return core.LanguageVariantJSX
|
||||
}
|
||||
return core.LanguageVariantStandard
|
||||
}
|
||||
|
||||
func tokenIsIdentifierOrKeyword(token ast.Kind) bool {
|
||||
return token >= ast.KindIdentifier
|
||||
}
|
||||
|
||||
func tokenIsIdentifierOrKeywordOrGreaterThan(token ast.Kind) bool {
|
||||
return token == ast.KindGreaterThanToken || tokenIsIdentifierOrKeyword(token)
|
||||
}
|
||||
|
||||
func GetJSDocCommentRanges(f *ast.NodeFactory, commentRanges []ast.CommentRange, node *ast.Node, text string) []ast.CommentRange {
|
||||
switch node.Kind {
|
||||
case ast.KindParameter, ast.KindTypeParameter, ast.KindFunctionExpression, ast.KindArrowFunction, ast.KindParenthesizedExpression, ast.KindVariableDeclaration, ast.KindExportSpecifier:
|
||||
for commentRange := range scanner.GetTrailingCommentRanges(f, text, node.Pos()) {
|
||||
commentRanges = append(commentRanges, commentRange)
|
||||
}
|
||||
for commentRange := range scanner.GetLeadingCommentRanges(f, text, node.Pos()) {
|
||||
commentRanges = append(commentRanges, commentRange)
|
||||
}
|
||||
default:
|
||||
for commentRange := range scanner.GetLeadingCommentRanges(f, text, node.Pos()) {
|
||||
commentRanges = append(commentRanges, commentRange)
|
||||
}
|
||||
}
|
||||
// Keep if the comment starts with '/**' but not if it is '/**/'
|
||||
return slices.DeleteFunc(commentRanges, func(comment ast.CommentRange) bool {
|
||||
commentStart := comment.Pos()
|
||||
commentLen := comment.End() - commentStart
|
||||
return comment.End() > node.End() || commentLen < 4 || text[commentStart+1] != '*' || text[commentStart+2] != '*' || text[commentStart+3] == '/'
|
||||
})
|
||||
}
|
||||
|
||||
func isKeywordOrPunctuation(token ast.Kind) bool {
|
||||
return ast.IsKeywordKind(token) || ast.IsPunctuationKind(token)
|
||||
}
|
||||
|
||||
func isJSDocLikeText(text string) bool {
|
||||
return len(text) >= 4 && text[1] == '*' && text[2] == '*' && text[3] != '/'
|
||||
}
|
||||
Reference in New Issue
Block a user