diff --git a/go/cmd/typecheck/main.go b/go/cmd/typecheck/main.go index 5e4700ad..b13d141b 100644 --- a/go/cmd/typecheck/main.go +++ b/go/cmd/typecheck/main.go @@ -1,54 +1,54 @@ package main -// Frontend TypeScript checker. Runs tsc in noEmit mode using -// tsconfig.json. Requires node on PATH; downloads the pinned TypeScript -// release on first run (no npm). +// Frontend TypeScript checker using TypeScript 7 (tsgo) - Microsoft's native Go +// port of tsc. No npm, no Node: tsgo is built from the vendored source under +// kjol/tools/tsgo and executed directly. // -// go run ./cmd/typecheck -// go run ./cmd/typecheck -p tsconfig.json +// go run kjol/cmd/typecheck +// go run kjol/cmd/typecheck -p tsconfig.json +// go run kjol/cmd/typecheck -tsgo path/to/kjol/tools/tsgo import ( - "archive/tar" - "compress/gzip" "flag" "fmt" - "io" - "net/http" "os" "os/exec" "path/filepath" + "runtime" "strings" ) -const typescriptVersion = "5.8.3" +// tsgoVersion pins the vendored typescript-go source (kjol/tools/tsgo). Bump when +// the vendored copy is updated; it also keys the built-binary cache. +const tsgoVersion = "v0.0.0-20260709155237-487baf0cc74a" // TypeScript 7.1.0-dev + +// goToolchain is the Go toolchain used to build tsgo. Its go.mod needs >= go 1.26; +// this exact patch is known-good. Go downloads it once if absent. +const goToolchain = "go1.26.5" func main() { tsconfig := flag.String("p", "tsconfig.json", "path to tsconfig.json") + tsgoDir := flag.String("tsgo", "kjol/tools/tsgo", "path to the vendored typescript-go source") flag.Parse() - if err := runTypecheck(*tsconfig); err != nil { + if err := runTypecheck(*tsconfig, *tsgoDir); err != nil { fmt.Fprintf(os.Stderr, "Typecheck failed: %v\n", err) os.Exit(1) } } -func runTypecheck(tsconfig string) error { - node, err := exec.LookPath("node") - if err != nil { - return fmt.Errorf("node not found on PATH (required to run tsc): %w", err) - } - - tsc, err := ensureTypeScript() - if err != nil { - return err - } - +func runTypecheck(tsconfig, tsgoDir string) error { if _, err := os.Stat(tsconfig); err != nil { return fmt.Errorf("tsconfig not found: %s", tsconfig) } - fmt.Printf("Typechecking with TypeScript %s...\n", typescriptVersion) - cmd := exec.Command(node, tsc, "--noEmit", "-p", tsconfig) + tsgo, err := ensureTsgo(tsgoDir) + if err != nil { + return err + } + + fmt.Printf("Typechecking with TypeScript 7 (tsgo %s)...\n", tsgoVersion) + cmd := exec.Command(tsgo, "--noEmit", "-p", tsconfig) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { @@ -59,92 +59,56 @@ func runTypecheck(tsconfig string) error { return nil } -func ensureTypeScript() (string, error) { - root, err := os.Getwd() +// ensureTsgo builds tsgo from the vendored source once (cached under tmp/tsgo) and +// returns the binary path. +func ensureTsgo(tsgoDir string) (string, error) { + exeName := "tsgo" + if runtime.GOOS == "windows" { + exeName += ".exe" + } + + cwd, err := os.Getwd() if err != nil { return "", err } - - cacheDir := filepath.Join(root, "tools", ".cache", "typescript", typescriptVersion) - tscPath := filepath.Join(cacheDir, "package", "lib", "tsc.js") - if _, err := os.Stat(tscPath); err == nil { - return tscPath, nil + binPath := filepath.Join(cwd, "tmp", "tsgo", tsgoVersion, exeName) + if _, err := os.Stat(binPath); err == nil { + return binPath, nil } - fmt.Printf("Downloading TypeScript %s...\n", typescriptVersion) - if err := downloadTypeScript(cacheDir); err != nil { + absTsgo, err := filepath.Abs(tsgoDir) + if err != nil { return "", err } - - if _, err := os.Stat(tscPath); err != nil { - return "", fmt.Errorf("tsc not found after download: %s", tscPath) + if _, err := os.Stat(filepath.Join(absTsgo, "go.mod")); err != nil { + return "", fmt.Errorf("vendored tsgo source not found at %s (pass -tsgo or check kjol/tools/tsgo)", absTsgo) } - return tscPath, nil + + if err := os.MkdirAll(filepath.Dir(binPath), 0o755); err != nil { + return "", err + } + fmt.Println("Building tsgo from vendored source (first run; cached afterwards)...") + build := exec.Command("go", "build", "-C", absTsgo, "-mod=vendor", "-o", binPath, "./cmd/tsgo") + build.Env = buildEnv() + build.Stdout = os.Stdout + build.Stderr = os.Stderr + if err := build.Run(); err != nil { + return "", fmt.Errorf("building tsgo: %w", err) + } + if _, err := os.Stat(binPath); err != nil { + return "", fmt.Errorf("tsgo not found after build: %s", binPath) + } + return binPath, nil } -func downloadTypeScript(destDir string) error { - url := fmt.Sprintf("https://registry.npmjs.org/typescript/-/typescript-%s.tgz", typescriptVersion) - resp, err := http.Get(url) - if err != nil { - return fmt.Errorf("download typescript: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("download typescript: HTTP %s", resp.Status) - } - - if err := os.MkdirAll(destDir, 0o755); err != nil { - return err - } - return extractTGZ(resp.Body, destDir) -} - -func extractTGZ(r io.Reader, destDir string) error { - gz, err := gzip.NewReader(r) - if err != nil { - return fmt.Errorf("read typescript archive: %w", err) - } - defer gz.Close() - - tr := tar.NewReader(gz) - cleanDest := filepath.Clean(destDir) - - for { - hdr, err := tr.Next() - if err == io.EOF { - return nil - } - if err != nil { - return fmt.Errorf("read typescript archive: %w", err) - } - - target := filepath.Join(destDir, filepath.FromSlash(hdr.Name)) - cleanTarget := filepath.Clean(target) - if cleanTarget != cleanDest && !strings.HasPrefix(cleanTarget, cleanDest+string(os.PathSeparator)) { - return fmt.Errorf("invalid archive path: %s", hdr.Name) - } - - switch hdr.Typeflag { - case tar.TypeDir: - if err := os.MkdirAll(target, 0o755); err != nil { - return err - } - case tar.TypeReg: - if err := os.MkdirAll(filepath.Dir(target), 0o755); err != nil { - return err - } - f, err := os.OpenFile(target, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.FileMode(hdr.Mode)&0o777|0o600) - if err != nil { - return err - } - if _, err := io.Copy(f, tr); err != nil { - f.Close() - return err - } - if err := f.Close(); err != nil { - return err - } +// buildEnv builds tsgo standalone: outside any go.work, with a pinned toolchain. +func buildEnv() []string { + var env []string + for _, e := range os.Environ() { + if strings.HasPrefix(e, "GOWORK=") || strings.HasPrefix(e, "GOTOOLCHAIN=") { + continue } + env = append(env, e) } -} + return append(env, "GOWORK=off", "GOTOOLCHAIN="+goToolchain) +} \ No newline at end of file diff --git a/tools/tsgo/LICENSE b/tools/tsgo/LICENSE new file mode 100644 index 00000000..8746124b --- /dev/null +++ b/tools/tsgo/LICENSE @@ -0,0 +1,55 @@ +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of this License; and + +You must cause any modified files to carry prominent notices stating that You changed the files; and + +You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + +If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/tools/tsgo/NOTICE.txt b/tools/tsgo/NOTICE.txt new file mode 100644 index 00000000..0c40b1e6 --- /dev/null +++ b/tools/tsgo/NOTICE.txt @@ -0,0 +1,436 @@ +NOTICES AND INFORMATION +Do Not Translate or Localize + +This software incorporates material from third parties. +Microsoft makes certain open source code available at https://3rdpartysource.microsoft.com, +or you may send a check or money order for US $5.00, including the product name, +the open source component name, platform, and version number, to: + +Source Code Compliance Team +Microsoft Corporation +One Microsoft Way +Redmond, WA 98052 +USA + +Notwithstanding any other terms, you may reverse engineer this software to the extent +required to debug changes to any libraries licensed under the GNU Lesser General Public License. + + +------------------- DefinitelyTyped -------------------- +This file is based on or incorporates material from the projects listed below (collectively "Third Party Code"). Microsoft is not the original author of the Third Party Code. The original copyright notice and the license, under which Microsoft received such Third Party Code, are set forth below. Such licenses and notices are provided for informational purposes only. Microsoft, not the third party, licenses the Third Party Code to you under the terms set forth in the EULA for the Microsoft Product. Microsoft reserves all other rights not expressly granted under this agreement, whether by implication, estoppel or otherwise. +DefinitelyTyped +This project is licensed under the MIT license. Copyrights are respective of each contributor listed at the beginning of each definition file. Provided for Informational Purposes Only + +MIT License +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ""Software""), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +-------------------------------------------------------------------------------------- + +------------------- Unicode -------------------- +UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE + +Unicode Data Files include all data files under the directories +http://www.unicode.org/Public/, http://www.unicode.org/reports/, +http://www.unicode.org/cldr/data/, http://source.icu-project.org/repos/icu/, and +http://www.unicode.org/utility/trac/browser/. + +Unicode Data Files do not include PDF online code charts under the +directory http://www.unicode.org/Public/. + +Software includes any source code published in the Unicode Standard +or under the directories +http://www.unicode.org/Public/, http://www.unicode.org/reports/, +http://www.unicode.org/cldr/data/, http://source.icu-project.org/repos/icu/, and +http://www.unicode.org/utility/trac/browser/. + +NOTICE TO USER: Carefully read the following legal agreement. +BY DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S +DATA FILES ("DATA FILES"), AND/OR SOFTWARE ("SOFTWARE"), +YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE +TERMS AND CONDITIONS OF THIS AGREEMENT. +IF YOU DO NOT AGREE, DO NOT DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE +THE DATA FILES OR SOFTWARE. + +COPYRIGHT AND PERMISSION NOTICE + +Copyright (c) 1991-2017 Unicode, Inc. All rights reserved. +Distributed under the Terms of Use in http://www.unicode.org/copyright.html. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Unicode data files and any associated documentation +(the "Data Files") or Unicode software and any associated documentation +(the "Software") to deal in the Data Files or Software +without restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, and/or sell copies of +the Data Files or Software, and to permit persons to whom the Data Files +or Software are furnished to do so, provided that either +(a) this copyright and permission notice appear with all copies +of the Data Files or Software, or +(b) this copyright and permission notice appear in associated +Documentation. + +THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT OF THIRD PARTY RIGHTS. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS +NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL +DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, +DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THE DATA FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder +shall not be used in advertising or otherwise to promote the sale, +use or other dealings in these Data Files or Software without prior +written authorization of the copyright holder. +------------------------------------------------------------------------------------- + +-------------------Document Object Model----------------------------- +DOM + +W3C License +This work is being provided by the copyright holders under the following license. +By obtaining and/or copying this work, you (the licensee) agree that you have read, understood, and will comply with the following terms and conditions. +Permission to copy, modify, and distribute this work, with or without modification, for any purpose and without fee or royalty is hereby granted, provided that you include the following +on ALL copies of the work or portions thereof, including modifications: +* The full text of this NOTICE in a location viewable to users of the redistributed or derivative work. +* Any pre-existing intellectual property disclaimers, notices, or terms and conditions. If none exist, the W3C Software and Document Short Notice should be included. +* Notice of any changes or modifications, through a copyright statement on the new code or document such as "This software or document includes material copied from or derived +from [title and URI of the W3C document]. Copyright © [YEAR] W3C® (MIT, ERCIM, Keio, Beihang)." +Disclaimers +THIS WORK IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY OR +FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE OR DOCUMENT WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. +COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENT. +The name and trademarks of copyright holders may NOT be used in advertising or publicity pertaining to the work without specific, written prior permission. +Title to copyright in this work will at all times remain with copyright holders. + +--------- + +DOM +Copyright © 2018 WHATWG (Apple, Google, Mozilla, Microsoft). This work is licensed under a Creative Commons Attribution 4.0 International License: Attribution 4.0 International +======================================================================= +Creative Commons Corporation ("Creative Commons") is not a law firm and does not provide legal services or legal advice. Distribution of Creative Commons public licenses does not create a lawyer-client or other relationship. Creative Commons makes its licenses and related information available on an "as-is" basis. Creative Commons gives no warranties regarding its licenses, any material licensed under their terms and conditions, or any related information. Creative Commons disclaims all liability for damages resulting from their use to the fullest extent possible. Using Creative Commons Public Licenses Creative Commons public licenses provide a standard set of terms and conditions that creators and other rights holders may use to share original works of authorship and other material subject to copyright and certain other rights specified in the public license below. The following considerations are for informational purposes only, are not exhaustive, and do not form part of our licenses. Considerations for licensors: Our public licenses are intended for use by those authorized to give the public permission to use material in ways otherwise restricted by copyright and certain other rights. Our licenses are irrevocable. Licensors should read and understand the terms and conditions of the license they choose before applying it. Licensors should also secure all rights necessary before applying our licenses so that the public can reuse the material as expected. Licensors should clearly mark any material not subject to the license. This includes other CC- licensed material, or material used under an exception or limitation to copyright. More considerations for licensors: + +wiki.creativecommons.org/Considerations_for_licensors Considerations for the public: By using one of our public licenses, a licensor grants the public permission to use the licensed material under specified terms and conditions. If the licensor's permission is not necessary for any reason--for example, because of any applicable exception or limitation to copyright--then that use is not regulated by the license. Our licenses grant only permissions under copyright and certain other rights that a licensor has authority to grant. Use of the licensed material may still be restricted for other reasons, including because others have copyright or other rights in the material. A licensor may make special requests, such as asking that all changes be marked or described. Although not required by our licenses, you are encouraged to respect those requests where reasonable. More_considerations for the public: wiki.creativecommons.org/Considerations_for_licensees ======================================================================= +Creative Commons Attribution 4.0 International Public License By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions. Section 1 -- Definitions. a. Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image. b. Adapter's License means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License. c. Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. d. Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements. e. Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material. f. Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License. g. Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license. h. Licensor means the individual(s) or entity(ies) granting rights under this Public License. i. Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them. j. Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world. k. You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning. Section 2 -- Scope. a. License grant. 1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to: a. reproduce and Share the Licensed Material, in whole or in part; and b. produce, reproduce, and Share Adapted Material. 2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions. 3. Term. The term of this Public License is specified in Section 6(a). 4. Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a) (4) never produces Adapted Material. 5. Downstream recipients. a. Offer from the Licensor -- Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License. b. No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material. 6. No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i). b. Other rights. 1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise. 2. Patent and trademark rights are not licensed under this Public License. 3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties. Section 3 -- License Conditions. Your exercise of the Licensed Rights is expressly made subject to the following conditions. a. Attribution. 1. If You Share the Licensed Material (including in modified form), You must: a. retain the following if it is supplied by the Licensor with the Licensed Material: i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated); ii. a copyright notice; iii. a notice that refers to this Public License; iv. a notice that refers to the disclaimer of warranties; v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable; b. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and c. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License. 2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information. 3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable. 4. If You Share Adapted Material You produce, the Adapter's License You apply must not prevent recipients of the Adapted Material from complying with this Public License. Section 4 -- Sui Generis Database Rights. Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material: a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database; b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material; and c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database. For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights. Section 5 -- Disclaimer of Warranties and Limitation of Liability. a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability. Section 6 -- Term and Termination. a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically. b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates: 1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or 2. upon express reinstatement by the Licensor. For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License. c. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License. d. Sections 1, 5, 6, 7, and 8 survive termination of this Public License. Section 7 -- Other Terms and Conditions. a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed. b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License. Section 8 -- Interpretation. a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License. b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions. c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor. d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority. ======================================================================= Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the "Licensor." Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at creativecommons.org/policies, Creative Commons does not authorize the use of the trademark "Creative Commons" or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses. Creative Commons may be contacted at creativecommons.org. + +-------------------------------------------------------------------------------- + +----------------------Web Background Synchronization------------------------------ + +Web Background Synchronization Specification +Portions of spec © by W3C + +W3C Community Final Specification Agreement +To secure commitments from participants for the full text of a Community or Business Group Report, the group may call for voluntary commitments to the following terms; a "summary" is +available. See also the related "W3C Community Contributor License Agreement". +1. The Purpose of this Agreement. +This Agreement sets forth the terms under which I make certain copyright and patent rights available to you for your implementation of the Specification. +Any other capitalized terms not specifically defined herein have the same meaning as those terms have in the "W3C Patent Policy", and if not defined there, in the "W3C Process Document". +2. Copyrights. +2.1. Copyright Grant. I grant to you a perpetual (for the duration of the applicable copyright), worldwide, non-exclusive, no-charge, royalty-free, copyright license, without any obligation for accounting to me, to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, distribute, and implement the Specification to the full extent of my copyright interest in the Specification. +2.2. Attribution. As a condition of the copyright grant, you must include an attribution to the Specification in any derivative work you make based on the Specification. That attribution must include, at minimum, the Specification name and version number. +3. Patents. +3.1. Patent Licensing Commitment. I agree to license my Essential Claims under the W3C Community RF Licensing Requirements. This requirement includes Essential Claims that I own and any that I have the right to license without obligation of payment or other consideration to an unrelated third party. W3C Community RF Licensing Requirements obligations made concerning the Specification and described in this policy are binding on me for the life of the patents in question and encumber the patents containing Essential Claims, regardless of changes in participation status or W3C Membership. I also agree to license my Essential Claims under the W3C Community RF Licensing Requirements in derivative works of the Specification so long as all normative portions of the Specification are maintained and that this licensing commitment does not extend to any portion of the derivative work that was not included in the Specification. +3.2. Optional, Additional Patent Grant. In addition to the provisions of Section 3.1, I may also, at my option, make certain intellectual property rights infringed by implementations of the Specification, including Essential Claims, available by providing those terms via the W3C Web site. +4. No Other Rights. Except as specifically set forth in this Agreement, no other express or implied patent, trademark, copyright, or other property rights are granted under this Agreement, including by implication, waiver, or estoppel. +5. Antitrust Compliance. I acknowledge that I may compete with other participants, that I am under no obligation to implement the Specification, that each participant is free to develop competing technologies and standards, and that each party is free to license its patent rights to third parties, including for the purpose of enabling competing technologies and standards. +6. Non-Circumvention. I agree that I will not intentionally take or willfully assist any third party to take any action for the purpose of circumventing my obligations under this Agreement. +7. Transition to W3C Recommendation Track. The Specification developed by the Project may transition to the W3C Recommendation Track. The W3C Team is responsible for notifying me that a Corresponding Working Group has been chartered. I have no obligation to join the Corresponding Working Group. If the Specification developed by the Project transitions to the W3C Recommendation Track, the following terms apply: +7.1. If I join the Corresponding Working Group. If I join the Corresponding Working Group, I will be subject to all W3C rules, obligations, licensing commitments, and policies that govern that Corresponding Working Group. +7.2. If I Do Not Join the Corresponding Working Group. +7.2.1. Licensing Obligations to Resulting Specification. If I do not join the Corresponding Working Group, I agree to offer patent licenses according to the W3C Royalty-Free licensing requirements described in Section 5 of the W3C Patent Policy for the portions of the Specification included in the resulting Recommendation. This licensing commitment does not extend to any portion of an implementation of the Recommendation that was not included in the Specification. This licensing commitment may not be revoked but may be modified through the exclusion process defined in Section 4 of the W3C Patent Policy. I am not required to join the Corresponding Working Group to exclude patents from the W3C Royalty-Free licensing commitment, but must otherwise follow the normal exclusion procedures defined by the W3C Patent Policy. The W3C Team will notify me of any Call for Exclusion in the Corresponding Working Group as set forth in Section 4.5 of the W3C Patent Policy. +7.2.2. No Disclosure Obligation. If I do not join the Corresponding Working Group, I have no patent disclosure obligations outside of those set forth in Section 6 of the W3C Patent Policy. +8. Conflict of Interest. I will disclose significant relationships when those relationships might reasonably be perceived as creating a conflict of interest with my role. I will notify W3C of any change in my affiliation using W3C-provided mechanisms. +9. Representations, Warranties and Disclaimers. I represent and warrant that I am legally entitled to grant the rights and promises set forth in this Agreement. IN ALL OTHER RESPECTS THE SPECIFICATION IS PROVIDED “AS IS.” The entire risk as to implementing or otherwise using the Specification is assumed by the implementer and user. Except as stated herein, I expressly disclaim any warranties (express, implied, or otherwise), including implied warranties of merchantability, non-infringement, fitness for a particular purpose, or title, related to the Specification. IN NO EVENT WILL ANY PARTY BE LIABLE TO ANY OTHER PARTY FOR LOST PROFITS OR ANY FORM OF INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER FROM ANY CAUSES OF ACTION OF ANY KIND WITH RESPECT TO THIS AGREEMENT, WHETHER BASED ON BREACH OF CONTRACT, TORT (INCLUDING NEGLIGENCE), OR OTHERWISE, AND WHETHER OR NOT THE OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. All of my obligations under Section 3 regarding the transfer, successors in interest, or assignment of Granted Claims will be satisfied if I notify the transferee or assignee of any patent that I know contains Granted Claims of the obligations under Section 3. Nothing in this Agreement requires me to undertake a patent search. +10. Definitions. +10.1. Agreement. “Agreement” means this W3C Community Final Specification Agreement. +10.2. Corresponding Working Group. “Corresponding Working Group” is a W3C Working Group that is chartered to develop a Recommendation, as defined in the W3C Process Document, that takes the Specification as an input. +10.3. Essential Claims. “Essential Claims” shall mean all claims in any patent or patent application in any jurisdiction in the world that would necessarily be infringed by implementation of the Specification. A claim is necessarily infringed hereunder only when it is not possible to avoid infringing it because there is no non-infringing alternative for implementing the normative portions of the Specification. Existence of a non-infringing alternative shall be judged based on the state of the art at the time of the publication of the Specification. The following are expressly excluded from and shall not be deemed to constitute Essential Claims: +10.3.1. any claims other than as set forth above even if contained in the same patent as Essential Claims; and +10.3.2. claims which would be infringed only by: +portions of an implementation that are not specified in the normative portions of the Specification, or +enabling technologies that may be necessary to make or use any product or portion thereof that complies with the Specification and are not themselves expressly set forth in the Specification (e.g., semiconductor manufacturing technology, compiler technology, object-oriented technology, basic operating system technology, and the like); or +the implementation of technology developed elsewhere and merely incorporated by reference in the body of the Specification. +10.3.3. design patents and design registrations. +For purposes of this definition, the normative portions of the Specification shall be deemed to include only architectural and interoperability requirements. Optional features in the RFC 2119 sense are considered normative unless they are specifically identified as informative. Implementation examples or any other material that merely illustrate the requirements of the Specification are informative, rather than normative. +10.4. I, Me, or My. “I,” “me,” or “my” refers to the signatory. +10.5 Project. “Project” means the W3C Community Group or Business Group for which I executed this Agreement. +10.6. Specification. “Specification” means the Specification identified by the Project as the target of this agreement in a call for Final Specification Commitments. W3C shall provide the authoritative mechanisms for the identification of this Specification. +10.7. W3C Community RF Licensing Requirements. “W3C Community RF Licensing Requirements” license shall mean a non-assignable, non-sublicensable license to make, have made, use, sell, have sold, offer to sell, import, and distribute and dispose of implementations of the Specification that: +10.7.1. shall be available to all, worldwide, whether or not they are W3C Members; +10.7.2. shall extend to all Essential Claims owned or controlled by me; +10.7.3. may be limited to implementations of the Specification, and to what is required by the Specification; +10.7.4. may be conditioned on a grant of a reciprocal RF license (as defined in this policy) to all Essential Claims owned or controlled by the licensee. A reciprocal license may be required to be available to all, and a reciprocal license may itself be conditioned on a further reciprocal license from all. +10.7.5. may not be conditioned on payment of royalties, fees or other consideration; +10.7.6. may be suspended with respect to any licensee when licensor issued by licensee for infringement of claims essential to implement the Specification or any W3C Recommendation; +10.7.7. may not impose any further conditions or restrictions on the use of any technology, intellectual property rights, or other restrictions on behavior of the licensee, but may include reasonable, customary terms relating to operation or maintenance of the license relationship such as the following: choice of law and dispute resolution; +10.7.8. shall not be considered accepted by an implementer who manifests an intent not to accept the terms of the W3C Community RF Licensing Requirements license as offered by the licensor. +10.7.9. The RF license conforming to the requirements in this policy shall be made available by the licensor as long as the Specification is in effect. The term of such license shall be for the life of the patents in question. +I am encouraged to provide a contact from which licensing information can be obtained and other relevant licensing information. Any such information will be made publicly available. +10.8. You or Your. “You,” “you,” or “your” means any person or entity who exercises copyright or patent rights granted under this Agreement, and any person that person or entity controls. + +------------------------------------------------------------------------------------- + +------------------- WebGL ----------------------------- +Copyright (c) 2018 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Materials. + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +------------------------------------------------------ + +--------------------------------------------------------- + +github.com/zeebo/xxh3 v1.1.0 - BSD-2-Clause + + +Copyright (c) 2019, Jeff Wendling +Copyright (c) 2012-2014, Yann Collet + +Copyright (c) . All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +github.com/go-json-experiment/json v0.0.0-20260623181947-01eb4420fa68 - BSD-3-Clause + + +Copyright 2010 The Go Authors +Copyright 2011 The Go Authors +Copyright 2016 The Go Authors +Copyright 2018 The Go Authors +Copyright 2020 The Go Authors +Copyright 2021 The Go Authors +Copyright 2022 The Go Authors +Copyright 2023 The Go Authors +Copyright 2024 The Go Authors +Copyright (c) 2020 The Go Authors + +Copyright (c) . All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +golang.org/x/sync v0.21.0 - BSD-3-Clause AND LicenseRef-scancode-google-patent-license-golang + + +Copyright 2009 The Go Authors +Copyright 2013 The Go Authors +Copyright 2016 The Go Authors +Copyright 2017 The Go Authors +Copyright 2019 The Go Authors + +BSD-3-Clause AND LicenseRef-scancode-google-patent-license-golang + +--------------------------------------------------------- + +--------------------------------------------------------- + +golang.org/x/sys v0.46.0 - BSD-3-Clause AND LicenseRef-scancode-google-patent-license-golang + + +Copyright 2009 The Go Authors +Copyright 2010 The Go Authors +Copyright 2011 The Go Authors +Copyright 2012 The Go Authors +Copyright 2013 The Go Authors +Copyright 2014 The Go Authors +Copyright 2015 The Go Authors +Copyright 2016 The Go Authors +Copyright 2017 The Go Authors +Copyright 2018 The Go Authors +Copyright 2019 The Go Authors +Copyright 2020 The Go Authors +Copyright 2021 The Go Authors +Copyright 2022 The Go Authors +Copyright 2023 The Go Authors +Copyright 2024 The Go Authors +Copyright 2025 The Go Authors +Copyright 2009,2010 The Go Authors + +BSD-3-Clause AND LicenseRef-scancode-google-patent-license-golang + +--------------------------------------------------------- + +--------------------------------------------------------- + +golang.org/x/term v0.44.0 - BSD-3-Clause AND LicenseRef-scancode-google-patent-license-golang + + +Copyright 2009 The Go Authors +Copyright 2011 The Go Authors +Copyright 2013 The Go Authors +Copyright 2019 The Go Authors +Copyright 2021 The Go Authors + +BSD-3-Clause AND LicenseRef-scancode-google-patent-license-golang + +--------------------------------------------------------- + +--------------------------------------------------------- + +golang.org/x/text v0.38.0 - BSD-3-Clause AND LicenseRef-scancode-google-patent-license-golang + + +(c) AeHa (c) +(c) EeAoq (c) +(c) EAiE (c) A +(c) lEEe (c) AE +(c) (c) AAEE (c) +(c) oav!A (c) AY +(c) aA"AE (c) 1AE (c) +Copyright 2009 The Go Authors +Copyright 2011 The Go Authors +Copyright 2012 The Go Authors +Copyright 2013 The Go Authors +Copyright 2014 The Go Authors +Copyright 2015 The Go Authors +Copyright 2016 The Go Authors +Copyright 2017 The Go Authors +Copyright 2018 The Go Authors +Copyright 2019 The Go Authors +Copyright 2021 The Go Authors +Copyright 2025 The Go Authors + +BSD-3-Clause AND LicenseRef-scancode-google-patent-license-golang + +--------------------------------------------------------- + +--------------------------------------------------------- + +github.com/klauspost/cpuid/v2 v2.2.10 - MIT + + +Copyright (c) 2015 Klaus Post +Copyright (c) 2020 Klaus Post +Copyright (c) 2021 Klaus Post +Copyright 2018 The Go Authors +Copyright (c) 2015- Klaus Post & Contributors + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +github.com/Microsoft/go-winio v0.6.2 - MIT + + +Copyright (c) 2015 Microsoft +Copyright 2013 The Go Authors + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +github.com/mackerelio/go-osstat v0.2.7 - Apache-2.0 + + +Copyright 2017-2019 Hatena Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +--------------------------------------------------------- + +--------------------------------------------------------- + +parcel-bundler/watcher 8926bb8b281733bbfcaf69bb4e62ab7a1431c42a - MIT + + +Copyright Node.js contributors +Copyright 2012-2020 Facebook, Inc. +Copyright (c) 2017-present Devon Govett + +MIT License + +Copyright (c) 2017-present Devon Govett + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + diff --git a/tools/tsgo/cmd/tsgo/api.go b/tools/tsgo/cmd/tsgo/api.go new file mode 100644 index 00000000..fa47eb9b --- /dev/null +++ b/tools/tsgo/cmd/tsgo/api.go @@ -0,0 +1,61 @@ +package main + +import ( + "context" + "flag" + "fmt" + "os" + "os/signal" + "strings" + "syscall" + + "github.com/microsoft/typescript-go/internal/api" + "github.com/microsoft/typescript-go/internal/bundled" + "github.com/microsoft/typescript-go/internal/core" +) + +func runAPI(args []string) int { + flag := flag.NewFlagSet("api", flag.ContinueOnError) + cwd := flag.String("cwd", core.Must(os.Getwd()), "current working directory") + pipePath := flag.String("pipe", "", "use named pipe or Unix domain socket for communication instead of stdio") + callbacks := flag.String("callbacks", "", "comma-separated list of FS callbacks to enable (readFile,fileExists,directoryExists,getAccessibleEntries,realpath)") + async := flag.Bool("async", false, "use JSON-RPC protocol instead of MessagePack (for async API)") + timing := flag.Bool("timing", false, "collect per-request server processing time, folded into the client's timing snapshot") + if err := flag.Parse(args); err != nil { + return 2 + } + + defaultLibraryPath := bundled.LibPath() + + // Parse callbacks list + var callbacksList []string + if *callbacks != "" { + callbacksList = strings.Split(*callbacks, ",") + } + + options := &api.StdioServerOptions{ + Err: os.Stderr, + Cwd: *cwd, + DefaultLibraryPath: defaultLibraryPath, + Callbacks: callbacksList, + Async: *async, + CollectTiming: *timing, + } + if *pipePath != "" { + options.PipePath = *pipePath + } else { + options.In = os.Stdin + options.Out = os.Stdout + } + + s := api.NewStdioServer(options) + + ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) + defer stop() + + if err := s.Run(ctx); err != nil { + fmt.Fprintln(os.Stderr, err) + return 1 + } + return 0 +} diff --git a/tools/tsgo/cmd/tsgo/enablevtprocessing_windows.go b/tools/tsgo/cmd/tsgo/enablevtprocessing_windows.go new file mode 100644 index 00000000..784d0186 --- /dev/null +++ b/tools/tsgo/cmd/tsgo/enablevtprocessing_windows.go @@ -0,0 +1,22 @@ +package main + +import ( + "golang.org/x/sys/windows" +) + +func init() { + h, err := windows.GetStdHandle(windows.STD_OUTPUT_HANDLE) + if err != nil || h == windows.InvalidHandle { + return + } + fileType, err := windows.GetFileType(h) + if err != nil || fileType == windows.FILE_TYPE_CHAR { + var mode uint32 + if err := windows.GetConsoleMode(h, &mode); err != nil { + return + } + if mode&windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING == 0 { + _ = windows.SetConsoleMode(h, mode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING) + } + } +} diff --git a/tools/tsgo/cmd/tsgo/isprocessalive_other.go b/tools/tsgo/cmd/tsgo/isprocessalive_other.go new file mode 100644 index 00000000..e847d9a4 --- /dev/null +++ b/tools/tsgo/cmd/tsgo/isprocessalive_other.go @@ -0,0 +1,9 @@ +//go:build !unix && !windows + +package main + +const processAliveSupported = false + +func isProcessAlive(pid int) bool { + panic("isProcessAlive is not supported on this platform") +} diff --git a/tools/tsgo/cmd/tsgo/isprocessalive_unix.go b/tools/tsgo/cmd/tsgo/isprocessalive_unix.go new file mode 100644 index 00000000..0517d37c --- /dev/null +++ b/tools/tsgo/cmd/tsgo/isprocessalive_unix.go @@ -0,0 +1,25 @@ +//go:build unix + +package main + +import ( + "errors" + "os" + "syscall" +) + +const processAliveSupported = true + +// isProcessAlive checks if a process with the given PID is still running. +// On Unix, FindProcess always succeeds, so we send signal 0 to probe the +// process. If the signal returns nil or EPERM, the process exists (EPERM +// means it exists but we lack permission to signal it). ESRCH or any +// other error indicates the process is gone. +func isProcessAlive(pid int) bool { + proc, err := os.FindProcess(pid) + if err != nil { + return false + } + err = proc.Signal(syscall.Signal(0)) + return err == nil || errors.Is(err, syscall.EPERM) +} diff --git a/tools/tsgo/cmd/tsgo/isprocessalive_windows.go b/tools/tsgo/cmd/tsgo/isprocessalive_windows.go new file mode 100644 index 00000000..3f0a81c6 --- /dev/null +++ b/tools/tsgo/cmd/tsgo/isprocessalive_windows.go @@ -0,0 +1,26 @@ +//go:build windows + +package main + +import "syscall" + +const processAliveSupported = true + +// isProcessAlive checks if a process with the given PID is still running. +// On Windows, we open the process with SYNCHRONIZE access and use +// WaitForSingleObject with a zero timeout. If the wait times out, the +// process is still running. If the object is signaled, it has exited. +func isProcessAlive(pid int) bool { + const SYNCHRONIZE = 0x00100000 + handle, err := syscall.OpenProcess(SYNCHRONIZE, false, uint32(pid)) + if err != nil { + return false + } + defer func() { _ = syscall.CloseHandle(handle) }() + ret, err := syscall.WaitForSingleObject(handle, 0) + if err != nil { + return false + } + const WAIT_TIMEOUT = 258 + return ret == WAIT_TIMEOUT +} diff --git a/tools/tsgo/cmd/tsgo/lsp.go b/tools/tsgo/cmd/tsgo/lsp.go new file mode 100644 index 00000000..bc1a63b6 --- /dev/null +++ b/tools/tsgo/cmd/tsgo/lsp.go @@ -0,0 +1,108 @@ +package main + +import ( + "context" + "flag" + "fmt" + "os" + "os/exec" + "os/signal" + "syscall" + "time" + + "github.com/microsoft/typescript-go/internal/bundled" + "github.com/microsoft/typescript-go/internal/core" + "github.com/microsoft/typescript-go/internal/lsp" + "github.com/microsoft/typescript-go/internal/pprof" + "github.com/microsoft/typescript-go/internal/vfs/osvfs" +) + +func runLSP(args []string) int { + flag := flag.NewFlagSet("lsp", flag.ContinueOnError) + stdio := flag.Bool("stdio", false, "use stdio for communication") + pprofDir := flag.String("pprofDir", "", "Generate pprof CPU/memory profiles to the given directory.") + pipe := flag.String("pipe", "", "use named pipe for communication") + _ = pipe + socket := flag.String("socket", "", "use socket for communication") + _ = socket + if err := flag.Parse(args); err != nil { + return 2 + } + + if !*stdio { + fmt.Fprintln(os.Stderr, "only stdio is supported") + return 1 + } + + if *pprofDir != "" { + fmt.Fprintf(os.Stderr, "pprof profiles will be written to: %v\n", *pprofDir) + profileSession := pprof.BeginProfiling(*pprofDir, os.Stderr) + defer profileSession.Stop() + } + + fs := bundled.WrapFS(osvfs.FS()) + defaultLibraryPath := bundled.LibPath() + typingsLocation := osvfs.GetGlobalTypingsCacheLocation() + + ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) + defer stop() + + s := lsp.NewServer(&lsp.ServerOptions{ + In: lsp.ToReader(os.Stdin), + Out: lsp.ToWriter(os.Stdout), + Err: os.Stderr, + Cwd: core.Must(os.Getwd()), + FS: fs, + DefaultLibraryPath: defaultLibraryPath, + TypingsLocation: typingsLocation, + NpmInstall: func(cwd string, args []string) ([]byte, error) { + cmd := exec.Command("npm", args...) + cmd.Dir = cwd + return cmd.Output() + }, + ProgressDelay: 250 * time.Millisecond, + SetParentProcessID: newParentProcessWatchdog(ctx, stop), + }) + + if err := s.Run(ctx); err != nil { + fmt.Fprintln(os.Stderr, err) + return 1 + } + return 0 +} + +// newParentProcessWatchdog returns a SetParentProcessID callback if the platform +// supports process-alive checking, or nil otherwise. +func newParentProcessWatchdog(ctx context.Context, stop context.CancelFunc) func(int) { + if !processAliveSupported { + return nil + } + return func(parentPID int) { + startParentProcessWatchdog(ctx, stop, parentPID) + } +} + +// startParentProcessWatchdog starts a goroutine that monitors the parent process +// and cancels the context if the parent dies. This prevents orphaned language +// server processes when the editor crashes or is killed. +func startParentProcessWatchdog(ctx context.Context, stop context.CancelFunc, parentPID int) { + if parentPID <= 0 { + return + } + go func() { + ticker := time.NewTicker(5 * time.Second) + defer ticker.Stop() + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + if !isProcessAlive(parentPID) { + fmt.Fprintf(os.Stderr, "Parent process %d has exited, shutting down.\n", parentPID) + stop() + return + } + } + } + }() +} diff --git a/tools/tsgo/cmd/tsgo/main.go b/tools/tsgo/cmd/tsgo/main.go new file mode 100644 index 00000000..8d6816fa --- /dev/null +++ b/tools/tsgo/cmd/tsgo/main.go @@ -0,0 +1,32 @@ +package main + +import ( + "context" + "os" + "os/signal" + "syscall" + + "github.com/microsoft/typescript-go/internal/core" + "github.com/microsoft/typescript-go/internal/execute" +) + +func main() { + os.Exit(runMain()) +} + +func runMain() int { + core.ApplyDebugStackLimit() + args := os.Args[1:] + if len(args) > 0 { + switch args[0] { + case "--lsp": + return runLSP(args[1:]) + case "--api": + return runAPI(args[1:]) + } + } + ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) + defer stop() + result := execute.CommandLine(ctx, newSystem(), args, nil) + return int(result.Status) +} diff --git a/tools/tsgo/cmd/tsgo/sys.go b/tools/tsgo/cmd/tsgo/sys.go new file mode 100644 index 00000000..e5b2b8d5 --- /dev/null +++ b/tools/tsgo/cmd/tsgo/sys.go @@ -0,0 +1,76 @@ +package main + +import ( + "fmt" + "io" + "os" + "time" + + "github.com/microsoft/typescript-go/internal/bundled" + "github.com/microsoft/typescript-go/internal/execute/tsc" + "github.com/microsoft/typescript-go/internal/tspath" + "github.com/microsoft/typescript-go/internal/vfs" + "github.com/microsoft/typescript-go/internal/vfs/osvfs" + "golang.org/x/term" +) + +type osSys struct { + writer io.Writer + fs vfs.FS + defaultLibraryPath string + cwd string + start time.Time +} + +func (s *osSys) SinceStart() time.Duration { + return time.Since(s.start) +} + +func (s *osSys) Now() time.Time { + return time.Now() +} + +func (s *osSys) FS() vfs.FS { + return s.fs +} + +func (s *osSys) DefaultLibraryPath() string { + return s.defaultLibraryPath +} + +func (s *osSys) GetCurrentDirectory() string { + return s.cwd +} + +func (s *osSys) Writer() io.Writer { + return s.writer +} + +func (s *osSys) WriteOutputIsTTY() bool { + return term.IsTerminal(int(os.Stdout.Fd())) +} + +func (s *osSys) GetWidthOfTerminal() int { + width, _, _ := term.GetSize(int(os.Stdout.Fd())) + return width +} + +func (s *osSys) GetEnvironmentVariable(name string) string { + return os.Getenv(name) +} + +func newSystem() *osSys { + cwd, err := os.Getwd() + if err != nil { + fmt.Fprintf(os.Stderr, "Error getting current directory: %v\n", err) + os.Exit(int(tsc.ExitStatusInvalidProject_OutputsSkipped)) + } + + return &osSys{ + cwd: tspath.NormalizePath(cwd), + fs: bundled.WrapFS(osvfs.FS()), + defaultLibraryPath: bundled.LibPath(), + writer: os.Stdout, + start: time.Now(), + } +} diff --git a/tools/tsgo/go.mod b/tools/tsgo/go.mod new file mode 100644 index 00000000..5f8ca54d --- /dev/null +++ b/tools/tsgo/go.mod @@ -0,0 +1,38 @@ +module github.com/microsoft/typescript-go + +go 1.26 + +require ( + github.com/Microsoft/go-winio v0.6.2 + github.com/go-json-experiment/json v0.0.0-20260623181947-01eb4420fa68 + github.com/google/go-cmp v0.7.0 + github.com/mackerelio/go-osstat v0.2.7 + github.com/peter-evans/patience v0.3.0 + github.com/zeebo/xxh3 v1.1.0 + golang.org/x/sync v0.21.0 + golang.org/x/sys v0.46.0 + golang.org/x/term v0.44.0 + golang.org/x/text v0.38.0 + gotest.tools/v3 v3.5.2 +) + +require ( + github.com/klauspost/cpuid/v2 v2.2.10 // indirect + github.com/matryer/moq v0.7.1 // indirect + golang.org/x/mod v0.37.0 // indirect + golang.org/x/tools v0.47.0 // indirect +) + +tool ( + github.com/matryer/moq + golang.org/x/tools/cmd/stringer +) + +ignore ( + ./_extension + ./_packages + ./_submodules + ./built + ./coverage + node_modules +) diff --git a/tools/tsgo/go.sum b/tools/tsgo/go.sum new file mode 100644 index 00000000..7c8a1974 --- /dev/null +++ b/tools/tsgo/go.sum @@ -0,0 +1,34 @@ +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/go-json-experiment/json v0.0.0-20260623181947-01eb4420fa68 h1:KZaTBSyshWX3MP5jukJcNSuXDQTO+rNpt0J564dX/eg= +github.com/go-json-experiment/json v0.0.0-20260623181947-01eb4420fa68/go.mod h1:tphK2c80bpPhMOI4v6bIc2xWywPfbqi1Z06+RcrMkDg= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= +github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +github.com/mackerelio/go-osstat v0.2.7 h1:TCavZi10wF49bT6iQZ9eT2keGZQpC69MTDfdJej5e94= +github.com/mackerelio/go-osstat v0.2.7/go.mod h1:dwpYh5pIPmvk+IEwBKNIWRFMB92mrC08CmXOhDC7nQk= +github.com/matryer/moq v0.7.1 h1:/QaXqMAdOrLqlshW2z7SMS21jDi7aVrbW0wJrR+hhJk= +github.com/matryer/moq v0.7.1/go.mod h1:IabIiFkaKCyHxej25INgFR+fnOxSZFMv2LYrU+ioyDs= +github.com/peter-evans/patience v0.3.0 h1:rX0JdJeepqdQl1Sk9c9uvorjYYzL2TfgLX1adqYm9cA= +github.com/peter-evans/patience v0.3.0/go.mod h1:Kmxu5sY1NmBLFSStvXjX1wS9mIv7wMcP/ubucyMOAu0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= +github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= +github.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs= +github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s= +golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ= +golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0= +golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM= +golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw= +golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc= +golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y= +golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE= +golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4= +golang.org/x/tools v0.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q= +golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA= +gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= +gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= diff --git a/tools/tsgo/internal/api/callbackfs.go b/tools/tsgo/internal/api/callbackfs.go new file mode 100644 index 00000000..daf170e7 --- /dev/null +++ b/tools/tsgo/internal/api/callbackfs.go @@ -0,0 +1,226 @@ +package api + +import ( + "context" + "fmt" + "time" + + "github.com/microsoft/typescript-go/internal/json" + "github.com/microsoft/typescript-go/internal/vfs" +) + +// callbackFS wraps a base filesystem and delegates certain operations +// to the client via RPC callbacks. This allows the API client to provide +// a virtual filesystem (e.g., in-memory files for testing). +// +// The callbacks to enable are specified at construction time via the +// --callbacks CLI flag. The connection is set via SetConnection after +// the transport connection is established. +type callbackFS struct { + base vfs.FS + enabledCallbacks map[string]bool + + // conn and ctx are set after connection is established + conn Conn + ctx context.Context +} + +// Callback names that can be enabled +const ( + callbackReadFile = "readFile" + callbackFileExists = "fileExists" + callbackDirectoryExists = "directoryExists" + callbackGetAccessibleEntries = "getAccessibleEntries" + callbackRealpath = "realpath" +) + +func isCallbackName(name string) bool { + switch name { + case callbackReadFile, + callbackFileExists, + callbackDirectoryExists, + callbackGetAccessibleEntries, + callbackRealpath: + return true + default: + return false + } +} + +// newCallbackFS creates a new callbackFS wrapping the given base filesystem. +// The callbacks slice specifies which filesystem operations should be delegated +// to the client (e.g., "readFile", "fileExists"). +func newCallbackFS(base vfs.FS, callbacks []string) *callbackFS { + enabled := make(map[string]bool, len(callbacks)) + for _, cb := range callbacks { + if !isCallbackName(cb) { + panic("unknown callback name: " + cb) + } + enabled[cb] = true + } + return &callbackFS{ + base: base, + enabledCallbacks: enabled, + } +} + +// SetConnection sets the RPC connection for callbacks. +// This must be called after the transport connection is established +// but before any filesystem operations that need callbacks. +func (fs *callbackFS) SetConnection(ctx context.Context, conn Conn) { + fs.ctx = ctx + fs.conn = conn +} + +// isEnabled returns true if the named callback is enabled. +func (fs *callbackFS) isEnabled(name string) bool { + return fs.enabledCallbacks[name] +} + +// call invokes a callback on the client and returns the result. +func (fs *callbackFS) call(name string, arg any) ([]byte, error) { + if fs.conn == nil { + return nil, fmt.Errorf("CallbackFS: %s called before connection set", name) + } + + result, err := fs.conn.Call(fs.ctx, name, arg) + if err != nil { + return nil, err + } + return result, nil +} + +// UseCaseSensitiveFileNames implements vfs.FS. +func (fs *callbackFS) UseCaseSensitiveFileNames() bool { + return fs.base.UseCaseSensitiveFileNames() +} + +// ReadFile implements vfs.FS. +// +// The readFile callback uses a wrapped response format to distinguish three states: +// - undefined (fall back to real FS): null or empty on wire +// - null (not found, no fallback): {"content": null} +// - string content: {"content": "..."} +func (fs *callbackFS) ReadFile(path string) (contents string, ok bool) { + if fs.isEnabled(callbackReadFile) { + result, err := fs.call(callbackReadFile, path) + if err != nil { + panic(err) + } + if len(result) > 0 && string(result) != "null" { + var wrapper struct { + Content *string `json:"content"` + } + if err := json.Unmarshal(result, &wrapper); err != nil { + panic(err) + } + if wrapper.Content == nil { + return "", false + } + return *wrapper.Content, true + } + } + return fs.base.ReadFile(path) +} + +// FileExists implements vfs.FS. +func (fs *callbackFS) FileExists(path string) bool { + if fs.isEnabled(callbackFileExists) { + result, err := fs.call(callbackFileExists, path) + if err != nil { + panic(err) + } + if len(result) > 0 && string(result) != "null" { + return string(result) == "true" + } + } + return fs.base.FileExists(path) +} + +// DirectoryExists implements vfs.FS. +func (fs *callbackFS) DirectoryExists(path string) bool { + if fs.isEnabled(callbackDirectoryExists) { + result, err := fs.call(callbackDirectoryExists, path) + if err != nil { + panic(err) + } + if len(result) > 0 && string(result) != "null" { + return string(result) == "true" + } + } + return fs.base.DirectoryExists(path) +} + +// GetAccessibleEntries implements vfs.FS. +func (fs *callbackFS) GetAccessibleEntries(path string) vfs.Entries { + if fs.isEnabled(callbackGetAccessibleEntries) { + result, err := fs.call(callbackGetAccessibleEntries, path) + if err != nil { + panic(err) + } + if len(result) > 0 { + var rawEntries *struct { + Files []string `json:"files"` + Directories []string `json:"directories"` + } + if err := json.Unmarshal(result, &rawEntries); err != nil { + panic(err) + } + if rawEntries != nil { + return vfs.Entries{ + Files: rawEntries.Files, + Directories: rawEntries.Directories, + } + } + } + } + return fs.base.GetAccessibleEntries(path) +} + +// Realpath implements vfs.FS. +func (fs *callbackFS) Realpath(path string) string { + if fs.isEnabled(callbackRealpath) { + result, err := fs.call(callbackRealpath, path) + if err != nil { + panic(err) + } + if len(result) > 0 && string(result) != "null" { + var realpath string + if err := json.Unmarshal(result, &realpath); err != nil { + panic(err) + } + return realpath + } + } + return fs.base.Realpath(path) +} + +// WriteFile implements vfs.FS - always delegates to base (no callback support). +func (fs *callbackFS) WriteFile(path string, data string) error { + return fs.base.WriteFile(path, data) +} + +// AppendFile implements vfs.FS - always delegates to base (no callback support). +func (fs *callbackFS) AppendFile(path string, data string) error { + return fs.base.AppendFile(path, data) +} + +// Remove implements vfs.FS - always delegates to base (no callback support). +func (fs *callbackFS) Remove(path string) error { + return fs.base.Remove(path) +} + +// Chtimes implements vfs.FS - always delegates to base (no callback support). +func (fs *callbackFS) Chtimes(path string, aTime time.Time, mTime time.Time) error { + return fs.base.Chtimes(path, aTime, mTime) +} + +// Stat implements vfs.FS - always delegates to base (no callback support). +func (fs *callbackFS) Stat(path string) vfs.FileInfo { + return fs.base.Stat(path) +} + +// WalkDir implements vfs.FS - always delegates to base (no callback support). +func (fs *callbackFS) WalkDir(root string, walkFn vfs.WalkDirFunc) error { + return fs.base.WalkDir(root, walkFn) +} diff --git a/tools/tsgo/internal/api/conn.go b/tools/tsgo/internal/api/conn.go new file mode 100644 index 00000000..091a169b --- /dev/null +++ b/tools/tsgo/internal/api/conn.go @@ -0,0 +1,46 @@ +package api + +import ( + "context" + "errors" + + "github.com/microsoft/typescript-go/internal/json" +) + +var ( + ErrConnClosed = errors.New("api: connection closed") + ErrRequestTimeout = errors.New("api: request timeout") +) + +// Handler processes incoming API requests and notifications. +type Handler interface { + // HandleRequest handles an incoming request and returns a result or error. + HandleRequest(ctx context.Context, method string, params json.Value) (any, error) + // HandleNotification handles an incoming notification. + HandleNotification(ctx context.Context, method string, params json.Value) error +} + +// Conn represents a bidirectional connection for API communication. +type Conn interface { + // Run starts processing messages on the connection. + // It blocks until the context is cancelled or an error occurs. + Run(ctx context.Context) error + + // Call sends a request to the client and waits for a response. + Call(ctx context.Context, method string, params any) (json.Value, error) + + // Notify sends a notification to the client (no response expected). + Notify(ctx context.Context, method string, params any) error +} + +// UnmarshalParams is a helper to unmarshal params into a typed struct. +func UnmarshalParams[T any](params json.Value) (*T, error) { + if len(params) == 0 { + return nil, nil + } + var v T + if err := json.Unmarshal(params, &v); err != nil { + return nil, err + } + return &v, nil +} diff --git a/tools/tsgo/internal/api/conn_async.go b/tools/tsgo/internal/api/conn_async.go new file mode 100644 index 00000000..a55965b4 --- /dev/null +++ b/tools/tsgo/internal/api/conn_async.go @@ -0,0 +1,231 @@ +package api + +import ( + "context" + "errors" + "fmt" + "io" + "runtime/debug" + "sync" + "sync/atomic" + "time" + + "github.com/microsoft/typescript-go/internal/json" + "github.com/microsoft/typescript-go/internal/jsonrpc" +) + +// AsyncConn manages bidirectional JSON-RPC communication with async request handling. +// Each incoming request is handled in its own goroutine, allowing concurrent processing. +// This is the standard implementation for LSP-style JSON-RPC protocols. +type AsyncConn struct { + rwc io.ReadWriteCloser + protocol Protocol + handler Handler + + // timing, when non-nil, accumulates the wall-clock time spent handling each + // request. Clients retrieve the collected data via a getServerTiming request. + timing *timingCollector + + // For server→client requests + seq atomic.Int64 + pending map[jsonrpc.ID]chan *Message + pendingMu sync.Mutex + writeMu sync.Mutex +} + +// NewAsyncConn creates a new async connection with the given transport and handler. +// It uses JSONRPCProtocol (LSP-style Content-Length framing) by default. +func NewAsyncConn(rwc io.ReadWriteCloser, handler Handler) *AsyncConn { + return NewAsyncConnWithProtocol(rwc, NewJSONRPCProtocol(rwc), handler) +} + +// NewAsyncConnWithProtocol creates a new async connection with a custom protocol. +func NewAsyncConnWithProtocol(rwc io.ReadWriteCloser, protocol Protocol, handler Handler) *AsyncConn { + return &AsyncConn{ + rwc: rwc, + protocol: protocol, + handler: handler, + pending: make(map[jsonrpc.ID]chan *Message), + } +} + +// SetCollectTiming enables or disables per-request server processing-time +// measurement. When enabled, the connection accumulates timing that clients can +// retrieve via a getServerTiming request. +func (c *AsyncConn) SetCollectTiming(enabled bool) { + if enabled { + c.timing = newTimingCollector() + } else { + c.timing = nil + } +} + +// Run starts processing messages on the connection. +// It blocks until the context is cancelled or an error occurs. +func (c *AsyncConn) Run(ctx context.Context) error { + for { + if ctx.Err() != nil { + return ctx.Err() + } + + msg, err := c.protocol.ReadMessage() + if err != nil { + if errors.Is(err, io.EOF) { + return nil + } + return err + } + + if msg.IsResponse() { + c.handleResponse(msg) + } else if msg.IsRequest() { + go c.handleRequest(ctx, msg) + } else if msg.IsNotification() { + go c.handleNotification(ctx, msg) + } + } +} + +// handleResponse matches a response to a pending request. +func (c *AsyncConn) handleResponse(msg *Message) { + c.pendingMu.Lock() + ch, ok := c.pending[*msg.ID] + if ok { + delete(c.pending, *msg.ID) + } + c.pendingMu.Unlock() + + if ok { + ch <- msg + close(ch) + } +} + +// handleRequest processes an incoming request. +func (c *AsyncConn) handleRequest(ctx context.Context, msg *Message) { + // Intercept the meta-requests for collected server timing before dispatching + // to the handler, so they are answered directly and not themselves recorded. + switch msg.Method { + case string(MethodGetServerTiming): + c.writeMu.Lock() + writeErr := c.protocol.WriteResponse(msg.ID, serverTimingSnapshot(c.timing)) + c.writeMu.Unlock() + if writeErr != nil { + panic(fmt.Sprintf("api: failed to write server timing response: %v", writeErr)) + } + return + case string(MethodResetServerTiming): + if c.timing != nil { + c.timing.reset() + } + c.writeMu.Lock() + writeErr := c.protocol.WriteResponse(msg.ID, nil) + c.writeMu.Unlock() + if writeErr != nil { + panic(fmt.Sprintf("api: failed to write reset server timing response: %v", writeErr)) + } + return + } + + var result any + var err error + + start := time.Time{} + if c.timing != nil { + start = time.Now() + } + + // Recover from panics and convert to error response with stack trace + defer func() { + if r := recover(); r != nil { + stack := string(debug.Stack()) + err = fmt.Errorf("panic: %v\n%s", r, stack) + + c.writeMu.Lock() + writeErr := c.protocol.WriteError(msg.ID, &jsonrpc.ResponseError{ + Code: jsonrpc.CodeInternalError, + Message: err.Error(), + }) + c.writeMu.Unlock() + + if writeErr != nil { + panic(fmt.Sprintf("api: failed to write panic error response: %v (original panic: %v)", writeErr, r)) + } + } + }() + + result, err = c.handler.HandleRequest(ctx, msg.Method, msg.Params) + + if c.timing != nil { + c.timing.record(msg.Method, time.Since(start)) + } + + c.writeMu.Lock() + defer c.writeMu.Unlock() + + var writeErr error + if err != nil { + writeErr = c.protocol.WriteError(msg.ID, &jsonrpc.ResponseError{ + Code: jsonrpc.CodeInternalError, + Message: err.Error(), + }) + } else { + writeErr = c.protocol.WriteResponse(msg.ID, result) + } + + if writeErr != nil { + panic(fmt.Sprintf("api: failed to write response: %v", writeErr)) + } +} + +// handleNotification processes an incoming notification. +func (c *AsyncConn) handleNotification(ctx context.Context, msg *Message) { + _ = c.handler.HandleNotification(ctx, msg.Method, msg.Params) +} + +// Call sends a request to the client and waits for a response. +func (c *AsyncConn) Call(ctx context.Context, method string, params any) (json.Value, error) { + // Create unique request ID + id := jsonrpc.NewIDString(fmt.Sprintf("api%d", c.seq.Add(1))) + + // Register response channel BEFORE sending request to avoid race + responseChan := make(chan *Message, 1) + c.pendingMu.Lock() + c.pending[*id] = responseChan + c.pendingMu.Unlock() + + defer func() { + c.pendingMu.Lock() + defer c.pendingMu.Unlock() + if ch, ok := c.pending[*id]; ok { + close(ch) + delete(c.pending, *id) + } + }() + + // Send the request + c.writeMu.Lock() + err := c.protocol.WriteRequest(id, method, params) + c.writeMu.Unlock() + + if err != nil { + return nil, err + } + + select { + case <-ctx.Done(): + return nil, ctx.Err() + case resp := <-responseChan: + if resp.Error != nil { + return nil, fmt.Errorf("api: remote error [%d]: %s", resp.Error.Code, resp.Error.Message) + } + return resp.Result, nil + } +} + +// Notify sends a notification to the client (no response expected). +func (c *AsyncConn) Notify(ctx context.Context, method string, params any) error { + c.writeMu.Lock() + defer c.writeMu.Unlock() + return c.protocol.WriteNotification(method, params) +} diff --git a/tools/tsgo/internal/api/conn_sync.go b/tools/tsgo/internal/api/conn_sync.go new file mode 100644 index 00000000..30209603 --- /dev/null +++ b/tools/tsgo/internal/api/conn_sync.go @@ -0,0 +1,208 @@ +package api + +import ( + "context" + "errors" + "fmt" + "io" + "runtime/debug" + "sync" + "time" + + "github.com/microsoft/typescript-go/internal/json" + "github.com/microsoft/typescript-go/internal/jsonrpc" +) + +// SyncConn manages bidirectional communication with synchronous request handling. +// Requests are handled one at a time inline, and outgoing calls are serialized. +type SyncConn struct { + rwc io.ReadWriteCloser + protocol Protocol + handler Handler + + // timing, when non-nil, accumulates the wall-clock time spent handling each + // request. Clients retrieve the collected data via a getServerTiming request. + timing *timingCollector + + // mu serializes all protocol operations (reads and writes). + // This ensures that concurrent calls from handler goroutines (e.g., project code + // spawning goroutines that invoke filesystem callbacks) don't corrupt the stream. + mu sync.Mutex +} + +// NewSyncConn creates a new sync connection with the given transport and handler. +func NewSyncConn(rwc io.ReadWriteCloser, protocol Protocol, handler Handler) *SyncConn { + return &SyncConn{ + rwc: rwc, + protocol: protocol, + handler: handler, + } +} + +// SetCollectTiming enables or disables per-request server processing-time +// measurement. When enabled, the connection accumulates timing that clients can +// retrieve via a getServerTiming request. +func (c *SyncConn) SetCollectTiming(enabled bool) { + if enabled { + c.timing = newTimingCollector() + } else { + c.timing = nil + } +} + +// Run starts processing messages on the connection. +// It blocks until the context is cancelled or an error occurs. +func (c *SyncConn) Run(ctx context.Context) error { + for { + if ctx.Err() != nil { + return ctx.Err() + } + + c.mu.Lock() + msg, err := c.protocol.ReadMessage() + c.mu.Unlock() + + if err != nil { + if errors.Is(err, io.EOF) { + return nil + } + return err + } + + if msg.IsRequest() { + c.handleRequest(ctx, msg) + } else if msg.IsNotification() { + c.handleNotification(ctx, msg) + } else { + // Responses are not expected in the main loop - they are read inline by Call(). + return errors.New("api: unexpected response message in sync connection") + } + } +} + +// handleRequest processes an incoming request. +func (c *SyncConn) handleRequest(ctx context.Context, msg *Message) { + // Intercept the meta-requests for collected server timing before dispatching + // to the handler, so they are answered directly and not themselves recorded. + switch msg.Method { + case string(MethodGetServerTiming): + c.mu.Lock() + writeErr := c.protocol.WriteResponse(msg.ID, serverTimingSnapshot(c.timing)) + c.mu.Unlock() + if writeErr != nil { + panic(fmt.Sprintf("api: failed to write server timing response: %v", writeErr)) + } + return + case string(MethodResetServerTiming): + if c.timing != nil { + c.timing.reset() + } + c.mu.Lock() + writeErr := c.protocol.WriteResponse(msg.ID, nil) + c.mu.Unlock() + if writeErr != nil { + panic(fmt.Sprintf("api: failed to write reset server timing response: %v", writeErr)) + } + return + } + + var result any + var err error + + start := time.Time{} + if c.timing != nil { + start = time.Now() + } + + // Recover from panics and convert to error response with stack trace + defer func() { + if r := recover(); r != nil { + stack := string(debug.Stack()) + err = fmt.Errorf("panic: %v\n%s", r, stack) + + c.mu.Lock() + writeErr := c.protocol.WriteError(msg.ID, &jsonrpc.ResponseError{ + Code: jsonrpc.CodeInternalError, + Message: err.Error(), + }) + c.mu.Unlock() + + if writeErr != nil { + panic(fmt.Sprintf("api: failed to write panic error response: %v (original panic: %v)", writeErr, r)) + } + } + }() + + result, err = c.handler.HandleRequest(ctx, msg.Method, msg.Params) + + if c.timing != nil { + c.timing.record(msg.Method, time.Since(start)) + } + + c.mu.Lock() + defer c.mu.Unlock() + + var writeErr error + if err != nil { + writeErr = c.protocol.WriteError(msg.ID, &jsonrpc.ResponseError{ + Code: jsonrpc.CodeInternalError, + Message: err.Error(), + }) + } else { + writeErr = c.protocol.WriteResponse(msg.ID, result) + } + + if writeErr != nil { + panic(fmt.Sprintf("api: failed to write response: %v", writeErr)) + } +} + +// handleNotification processes an incoming notification. +func (c *SyncConn) handleNotification(ctx context.Context, msg *Message) { + _ = c.handler.HandleNotification(ctx, msg.Method, msg.Params) +} + +// Call sends a request to the client and waits for a response. +// This method is safe to call from multiple goroutines - calls are serialized. +func (c *SyncConn) Call(ctx context.Context, method string, params any) (json.Value, error) { + // Serialize all Call operations. This is critical because: + // 1. The msgpack protocol uses method names as response IDs + // 2. The handler code (project internals) may spawn goroutines that call + // filesystem callbacks concurrently + // 3. We need to ensure write/read pairs are atomic + c.mu.Lock() + defer c.mu.Unlock() + + id := jsonrpc.NewIDString(method) + + if err := c.protocol.WriteRequest(id, method, params); err != nil { + return nil, err + } + + if ctx.Err() != nil { + return nil, ctx.Err() + } + + // Read the response inline. + msg, err := c.protocol.ReadMessage() + if err != nil { + return nil, err + } + + if msg.IsResponse() && msg.ID != nil && msg.ID.String() == method { + if msg.Error != nil { + return nil, fmt.Errorf("api: remote error [%d]: %s", msg.Error.Code, msg.Error.Message) + } + return msg.Result, nil + } + + // Unexpected message while waiting for response + return nil, fmt.Errorf("api: unexpected message while waiting for %q response", method) +} + +// Notify sends a notification to the client (no response expected). +func (c *SyncConn) Notify(ctx context.Context, method string, params any) error { + c.mu.Lock() + defer c.mu.Unlock() + return c.protocol.WriteNotification(method, params) +} diff --git a/tools/tsgo/internal/api/encoder/decoder.go b/tools/tsgo/internal/api/encoder/decoder.go new file mode 100644 index 00000000..e22f59f1 --- /dev/null +++ b/tools/tsgo/internal/api/encoder/decoder.go @@ -0,0 +1,381 @@ +package encoder + +import ( + "encoding/binary" + "errors" + "fmt" + + "github.com/microsoft/typescript-go/internal/ast" + "github.com/microsoft/typescript-go/internal/core" + "github.com/microsoft/typescript-go/internal/tspath" +) + +// astDecoder reconstructs real *ast.Node objects from binary-encoded data. +type astDecoder struct { + raw []byte + strTable uint32 + strData uint32 + extData uint32 + nodeOff uint32 + nodeCount int + factory *ast.NodeFactory + childBuf []int + // Single Go string covering all string data; substrings are zero-alloc slices. + allStringData string + // Arena for batch-allocating []*ast.Node slices used by NodeLists. + nodeArena []*ast.Node + // Results + nodes []*ast.Node + nodeLists []*ast.NodeList +} + +// DecodeSourceFile decodes binary-encoded data into an *ast.SourceFile. +func DecodeSourceFile(data []byte) (*ast.SourceFile, error) { + node, err := DecodeNodes(data) + if err != nil { + return nil, err + } + if node.Kind != ast.KindSourceFile { + return nil, fmt.Errorf("expected SourceFile root, got %v", node.Kind) + } + return node.AsSourceFile(), nil +} + +// DecodeNodes decodes binary-encoded AST data into a tree of *ast.Node objects. +func DecodeNodes(data []byte) (*ast.Node, error) { + d, err := newASTDecoder(data) + if err != nil { + return nil, err + } + return d.decode() +} + +func newASTDecoder(data []byte) (*astDecoder, error) { + if len(data) < HeaderSize { + return nil, fmt.Errorf("data too short for header: %d bytes", len(data)) + } + version := data[HeaderOffsetMetadata+3] + if version != ProtocolVersion { + return nil, fmt.Errorf("unsupported protocol version %d (expected %d)", version, ProtocolVersion) + } + + strTable := readLE32(data, HeaderOffsetStringOffsets) + strData := readLE32(data, HeaderOffsetStringData) + extData := readLE32(data, HeaderOffsetExtendedData) + nodeOff := readLE32(data, HeaderOffsetNodes) + + dataLen := uint32(len(data)) + + // Validate that all offsets are within the buffer. + if strTable > dataLen || strData > dataLen || extData > dataLen || nodeOff > dataLen { + return nil, fmt.Errorf("invalid AST header offsets: offsets exceed data length (%d)", dataLen) + } + + // Validate monotonic non-decreasing order of regions. + if !(strTable <= strData && strData <= extData && extData <= nodeOff) { + return nil, fmt.Errorf("invalid AST header offsets: expected strTable <= strData <= extData <= nodeOff (got %d, %d, %d, %d)", strTable, strData, extData, nodeOff) + } + + d := &astDecoder{ + raw: data, + strTable: strTable, + strData: strData, + extData: extData, + nodeOff: nodeOff, + factory: ast.NewNodeFactory(ast.NodeFactoryHooks{}), + } + + d.nodeCount = (len(data) - int(d.nodeOff)) / NodeSize + + // Convert entire string data region to a single Go string upfront. + // Substringing a Go string shares the backing array, so subsequent + // getString calls produce substrings with zero allocations. + d.allStringData = string(data[d.strData:]) + + return d, nil +} + +// allocNodeSlice returns a zero-length slice with the given capacity, backed by +// the pre-allocated nodeArena. This avoids a heap allocation per NodeList. +func (d *astDecoder) allocNodeSlice(capacity int) []*ast.Node { + start := len(d.nodeArena) + d.nodeArena = d.nodeArena[:start+capacity] + return d.nodeArena[start : start : start+capacity] +} + +// nodeField reads a uint32 field from node i at the given field offset. +func (d *astDecoder) nodeField(i int, field int) uint32 { + return readLE32(d.raw, int(d.nodeOff)+i*NodeSize+field) +} + +func (d *astDecoder) getString(idx uint32) string { + offBase := int(d.strTable) + int(idx)*4 + start := readLE32(d.raw, offBase) + end := readLE32(d.raw, offBase+4) + return d.allStringData[start:end] +} + +// collectChildren returns indices of direct children of node i. +// The returned slice is reused across calls; callers must not retain it. +func (d *astDecoder) collectChildren(i int) []int { + d.childBuf = d.childBuf[:0] + if i+1 >= d.nodeCount { + return d.childBuf + } + firstChild := i + 1 + if d.nodeField(firstChild, NodeOffsetParent) != uint32(i) { + return d.childBuf + } + d.childBuf = append(d.childBuf, firstChild) + next := int(d.nodeField(firstChild, NodeOffsetNext)) + for next != 0 { + d.childBuf = append(d.childBuf, next) + next = int(d.nodeField(next, NodeOffsetNext)) + } + return d.childBuf +} + +func (d *astDecoder) decode() (*ast.Node, error) { + if d.nodeCount < 2 { + return nil, errors.New("no nodes to decode") + } + + d.nodes = make([]*ast.Node, d.nodeCount) + d.nodeLists = make([]*ast.NodeList, d.nodeCount) + // Pre-allocate arena for NodeList child slices. Each node can appear as a + // child at most once, so nodeCount is an upper bound on total child pointers. + d.nodeArena = make([]*ast.Node, 0, d.nodeCount) + + // Process bottom-up so children exist before parents. + for i := d.nodeCount - 1; i >= 1; i-- { + kind := d.nodeField(i, NodeOffsetKind) + pos := d.nodeField(i, NodeOffsetPos) + end := d.nodeField(i, NodeOffsetEnd) + data := d.nodeField(i, NodeOffsetData) + childIndices := d.collectChildren(i) + + if kind == SyntaxKindNodeList { + childNodes := d.allocNodeSlice(len(childIndices)) + for _, ci := range childIndices { + if d.nodes[ci] != nil { + childNodes = append(childNodes, d.nodes[ci]) + } + } + nl := d.factory.NewNodeList(childNodes) + nl.Loc = core.NewTextRange(int(pos), int(end)) + d.nodeLists[i] = nl + continue + } + + node, err := d.createNode(ast.Kind(kind), data, childIndices) + if err != nil { + return nil, fmt.Errorf("at node %d (kind %v): %w", i, ast.Kind(kind), err) + } + node.Loc = core.NewTextRange(int(pos), int(end)) + node.Flags = ast.NodeFlags(d.nodeField(i, NodeOffsetFlags)) + d.nodes[i] = node + } + + return d.nodes[1], nil +} + +// getModifierList creates a *ast.ModifierList from a child index that is a NodeList. +func (d *astDecoder) getModifierList(ci int) *ast.ModifierList { + nl := d.nodeLists[ci] + if nl == nil { + return nil + } + ml := d.factory.NewModifierList(nl.Nodes) + ml.Loc = nl.Loc + return ml +} + +// childIterator helps walk through children based on a bitmask. +type childIterator struct { + indices []int + pos int +} + +func newChildIter(indices []int) childIterator { + return childIterator{indices: indices} +} + +// next returns the index of the next child, advancing the position. +func (it *childIterator) next() int { + if it.pos >= len(it.indices) { + return 0 + } + ci := it.indices[it.pos] + it.pos++ + return ci +} + +// nextIf returns the index of the next child if the corresponding mask bit is set. +func (it *childIterator) nextIf(mask uint8, bit uint8) int { + if mask&(1<> 24) & 0x3f) + + switch dataType { + case NodeDataTypeString: + return d.createStringNode(kind, data, commonData) + case NodeDataTypeExtendedData: + return d.createExtendedNode(kind, data, childIndices, commonData) + default: + return d.createChildrenNode(kind, data, childIndices, commonData) + } +} + +func (d *astDecoder) decodeExtendedData_SourceFile(data uint32, childIndices []int, commonData uint8) (*ast.Node, error) { + extOff := int(d.extData) + int(data&NodeDataStringIndexMask) + + textIdx := readLE32(d.raw, extOff) + fileNameIdx := readLE32(d.raw, extOff+4) + pathIdx := readLE32(d.raw, extOff+8) + text := d.getString(textIdx) + fileName := d.getString(fileNameIdx) + path := d.getString(pathIdx) + + // Recover parse options from header. + parseOpts := readLE32(d.raw, HeaderOffsetParseOptions) + opts := ast.SourceFileParseOptions{ + FileName: fileName, + Path: tspath.Path(path), + ExternalModuleIndicatorOptions: ast.ExternalModuleIndicatorOptions{ + JSX: parseOpts&1 != 0, + Force: parseOpts&2 != 0, + }, + } + + // Collect children: first is statements NodeList, second is EndOfFile. + var stmts *ast.NodeList + var endOfFile *ast.Node + for _, ci := range childIndices { + if d.nodeField(ci, NodeOffsetKind) == SyntaxKindNodeList { + stmts = d.nodeListAt(ci) + } else if d.nodes[ci] != nil && d.nodes[ci].Kind == ast.KindEndOfFile { + endOfFile = d.nodes[ci] + } + } + if endOfFile == nil { + endOfFile = d.factory.NewToken(ast.KindEndOfFile) + } + return d.factory.NewSourceFile(opts, text, stmts, endOfFile), nil +} + +func (d *astDecoder) decodeExtendedData_TemplateHead(data uint32, childIndices []int, commonData uint8) (*ast.Node, error) { + extOff := int(d.extData) + int(data&NodeDataStringIndexMask) + textIdx := readLE32(d.raw, extOff) + rawTextIdx := readLE32(d.raw, extOff+4) + flags := readLE32(d.raw, extOff+8) + return d.factory.NewTemplateHead(d.getString(textIdx), d.getString(rawTextIdx), ast.TokenFlags(flags)), nil +} + +func (d *astDecoder) decodeExtendedData_TemplateMiddle(data uint32, childIndices []int, commonData uint8) (*ast.Node, error) { + extOff := int(d.extData) + int(data&NodeDataStringIndexMask) + textIdx := readLE32(d.raw, extOff) + rawTextIdx := readLE32(d.raw, extOff+4) + flags := readLE32(d.raw, extOff+8) + return d.factory.NewTemplateMiddle(d.getString(textIdx), d.getString(rawTextIdx), ast.TokenFlags(flags)), nil +} + +func (d *astDecoder) decodeExtendedData_TemplateTail(data uint32, childIndices []int, commonData uint8) (*ast.Node, error) { + extOff := int(d.extData) + int(data&NodeDataStringIndexMask) + textIdx := readLE32(d.raw, extOff) + rawTextIdx := readLE32(d.raw, extOff+4) + flags := readLE32(d.raw, extOff+8) + return d.factory.NewTemplateTail(d.getString(textIdx), d.getString(rawTextIdx), ast.TokenFlags(flags)), nil +} + +func (d *astDecoder) singleChild(childIndices []int) *ast.Node { + if len(childIndices) == 0 { + return nil + } + return d.nodes[childIndices[0]] +} + +func (d *astDecoder) singleNodeListChild(childIndices []int) *ast.NodeList { + if len(childIndices) == 0 { + return nil + } + return d.nodeLists[childIndices[0]] +} + +func readLE32(data []byte, offset int) uint32 { + if offset < 0 || offset+4 > len(data) { + return 0 + } + return binary.LittleEndian.Uint32(data[offset : offset+4]) +} + +// Hand-written commonData decoding functions. Each extracts the original values +// from the 6-bit commonData that were packed by the corresponding +// getNodeCommonData_* function. + +func decodeNodeCommonData_SyntheticExpression(_ uint8) (any, bool) { + panic("SyntheticExpression should never be decoded") +} + +// Hand-written extended data decoding functions for literal nodes. + +func (d *astDecoder) decodeExtendedData_StringLiteral(data uint32, _ []int, _ uint8) (*ast.Node, error) { + extOff := int(d.extData) + int(data&NodeDataStringIndexMask) + textIdx := readLE32(d.raw, extOff) + flags := readLE32(d.raw, extOff+4) + return d.factory.NewStringLiteral(d.getString(textIdx), ast.TokenFlags(flags)), nil +} + +func (d *astDecoder) decodeExtendedData_NumericLiteral(data uint32, _ []int, _ uint8) (*ast.Node, error) { + extOff := int(d.extData) + int(data&NodeDataStringIndexMask) + textIdx := readLE32(d.raw, extOff) + flags := readLE32(d.raw, extOff+4) + return d.factory.NewNumericLiteral(d.getString(textIdx), ast.TokenFlags(flags)), nil +} + +func (d *astDecoder) decodeExtendedData_BigIntLiteral(data uint32, _ []int, _ uint8) (*ast.Node, error) { + extOff := int(d.extData) + int(data&NodeDataStringIndexMask) + textIdx := readLE32(d.raw, extOff) + flags := readLE32(d.raw, extOff+4) + return d.factory.NewBigIntLiteral(d.getString(textIdx), ast.TokenFlags(flags)), nil +} + +func (d *astDecoder) decodeExtendedData_RegularExpressionLiteral(data uint32, _ []int, _ uint8) (*ast.Node, error) { + extOff := int(d.extData) + int(data&NodeDataStringIndexMask) + textIdx := readLE32(d.raw, extOff) + flags := readLE32(d.raw, extOff+4) + return d.factory.NewRegularExpressionLiteral(d.getString(textIdx), ast.TokenFlags(flags)), nil +} + +func (d *astDecoder) decodeExtendedData_NoSubstitutionTemplateLiteral(data uint32, _ []int, _ uint8) (*ast.Node, error) { + extOff := int(d.extData) + int(data&NodeDataStringIndexMask) + textIdx := readLE32(d.raw, extOff) + flags := readLE32(d.raw, extOff+4) + return d.factory.NewNoSubstitutionTemplateLiteral(d.getString(textIdx), ast.TokenFlags(flags)), nil +} diff --git a/tools/tsgo/internal/api/encoder/decoder_generated.go b/tools/tsgo/internal/api/encoder/decoder_generated.go new file mode 100644 index 00000000..29dff5c5 --- /dev/null +++ b/tools/tsgo/internal/api/encoder/decoder_generated.go @@ -0,0 +1,1140 @@ +// Code generated by _scripts/generate-encoder.ts. DO NOT EDIT. + +package encoder + +import ( + "fmt" + + "github.com/microsoft/typescript-go/internal/ast" +) + +func (d *astDecoder) createStringNode(kind ast.Kind, data uint32, commonData uint8) (*ast.Node, error) { + strIdx := data & NodeDataStringIndexMask + text := d.getString(strIdx) + + switch kind { + case ast.KindIdentifier: + return d.factory.NewIdentifier(text), nil + case ast.KindPrivateIdentifier: + return d.factory.NewPrivateIdentifier(text), nil + case ast.KindJsxText: + containsOnlyTriviaWhiteSpaces := commonData&1 != 0 + return d.factory.NewJsxText(text, containsOnlyTriviaWhiteSpaces), nil + case ast.KindJSDocText: + return d.factory.NewJSDocText([]string{text}), nil + case ast.KindJSDocLink: + return d.factory.NewJSDocLink(nil, []string{text}), nil + case ast.KindJSDocLinkPlain: + return d.factory.NewJSDocLinkPlain(nil, []string{text}), nil + case ast.KindJSDocLinkCode: + return d.factory.NewJSDocLinkCode(nil, []string{text}), nil + default: + return nil, fmt.Errorf("unknown string node kind %v", kind) + } +} + +func (d *astDecoder) createExtendedNode(kind ast.Kind, data uint32, childIndices []int, commonData uint8) (*ast.Node, error) { + switch kind { + case ast.KindStringLiteral: + return d.decodeExtendedData_StringLiteral(data, childIndices, commonData) + case ast.KindNumericLiteral: + return d.decodeExtendedData_NumericLiteral(data, childIndices, commonData) + case ast.KindBigIntLiteral: + return d.decodeExtendedData_BigIntLiteral(data, childIndices, commonData) + case ast.KindRegularExpressionLiteral: + return d.decodeExtendedData_RegularExpressionLiteral(data, childIndices, commonData) + case ast.KindNoSubstitutionTemplateLiteral: + return d.decodeExtendedData_NoSubstitutionTemplateLiteral(data, childIndices, commonData) + case ast.KindTemplateHead: + return d.decodeExtendedData_TemplateHead(data, childIndices, commonData) + case ast.KindTemplateMiddle: + return d.decodeExtendedData_TemplateMiddle(data, childIndices, commonData) + case ast.KindTemplateTail: + return d.decodeExtendedData_TemplateTail(data, childIndices, commonData) + case ast.KindSourceFile: + return d.decodeExtendedData_SourceFile(data, childIndices, commonData) + default: + return nil, fmt.Errorf("unknown extended data node kind %v", kind) + } +} + +func (d *astDecoder) createChildrenNode(kind ast.Kind, data uint32, childIndices []int, commonData uint8) (*ast.Node, error) { + mask := uint8(data & NodeDataChildMask) + + switch kind { + case ast.KindUnknown, + ast.KindEndOfFile, + ast.KindSingleLineCommentTrivia, + ast.KindMultiLineCommentTrivia, + ast.KindNewLineTrivia, + ast.KindWhitespaceTrivia, + ast.KindConflictMarkerTrivia, + ast.KindNonTextFileMarkerTrivia, + ast.KindNumericLiteral, + ast.KindBigIntLiteral, + ast.KindStringLiteral, + ast.KindJsxText, + ast.KindJsxTextAllWhiteSpaces, + ast.KindRegularExpressionLiteral, + ast.KindNoSubstitutionTemplateLiteral, + ast.KindTemplateHead, + ast.KindTemplateMiddle, + ast.KindTemplateTail, + ast.KindOpenBraceToken, + ast.KindCloseBraceToken, + ast.KindOpenParenToken, + ast.KindCloseParenToken, + ast.KindOpenBracketToken, + ast.KindCloseBracketToken, + ast.KindDotToken, + ast.KindDotDotDotToken, + ast.KindSemicolonToken, + ast.KindCommaToken, + ast.KindQuestionDotToken, + ast.KindLessThanToken, + ast.KindLessThanSlashToken, + ast.KindGreaterThanToken, + ast.KindLessThanEqualsToken, + ast.KindGreaterThanEqualsToken, + ast.KindEqualsEqualsToken, + ast.KindExclamationEqualsToken, + ast.KindEqualsEqualsEqualsToken, + ast.KindExclamationEqualsEqualsToken, + ast.KindEqualsGreaterThanToken, + ast.KindPlusToken, + ast.KindMinusToken, + ast.KindAsteriskToken, + ast.KindAsteriskAsteriskToken, + ast.KindSlashToken, + ast.KindPercentToken, + ast.KindPlusPlusToken, + ast.KindMinusMinusToken, + ast.KindLessThanLessThanToken, + ast.KindGreaterThanGreaterThanToken, + ast.KindGreaterThanGreaterThanGreaterThanToken, + ast.KindAmpersandToken, + ast.KindBarToken, + ast.KindCaretToken, + ast.KindExclamationToken, + ast.KindTildeToken, + ast.KindAmpersandAmpersandToken, + ast.KindBarBarToken, + ast.KindQuestionToken, + ast.KindColonToken, + ast.KindAtToken, + ast.KindQuestionQuestionToken, + ast.KindBacktickToken, + ast.KindHashToken, + ast.KindEqualsToken, + ast.KindPlusEqualsToken, + ast.KindMinusEqualsToken, + ast.KindAsteriskEqualsToken, + ast.KindAsteriskAsteriskEqualsToken, + ast.KindSlashEqualsToken, + ast.KindPercentEqualsToken, + ast.KindLessThanLessThanEqualsToken, + ast.KindGreaterThanGreaterThanEqualsToken, + ast.KindGreaterThanGreaterThanGreaterThanEqualsToken, + ast.KindAmpersandEqualsToken, + ast.KindBarEqualsToken, + ast.KindBarBarEqualsToken, + ast.KindAmpersandAmpersandEqualsToken, + ast.KindQuestionQuestionEqualsToken, + ast.KindCaretEqualsToken, + ast.KindIdentifier, + ast.KindPrivateIdentifier, + ast.KindJSDocCommentTextToken, + ast.KindBreakKeyword, + ast.KindCaseKeyword, + ast.KindCatchKeyword, + ast.KindClassKeyword, + ast.KindConstKeyword, + ast.KindContinueKeyword, + ast.KindDebuggerKeyword, + ast.KindDefaultKeyword, + ast.KindDeleteKeyword, + ast.KindDoKeyword, + ast.KindElseKeyword, + ast.KindEnumKeyword, + ast.KindExportKeyword, + ast.KindExtendsKeyword, + ast.KindFinallyKeyword, + ast.KindForKeyword, + ast.KindFunctionKeyword, + ast.KindIfKeyword, + ast.KindInKeyword, + ast.KindInstanceOfKeyword, + ast.KindNewKeyword, + ast.KindReturnKeyword, + ast.KindSwitchKeyword, + ast.KindThrowKeyword, + ast.KindTryKeyword, + ast.KindTypeOfKeyword, + ast.KindVarKeyword, + ast.KindWhileKeyword, + ast.KindWithKeyword, + ast.KindImplementsKeyword, + ast.KindInterfaceKeyword, + ast.KindLetKeyword, + ast.KindPackageKeyword, + ast.KindPrivateKeyword, + ast.KindProtectedKeyword, + ast.KindPublicKeyword, + ast.KindStaticKeyword, + ast.KindYieldKeyword, + ast.KindAbstractKeyword, + ast.KindAccessorKeyword, + ast.KindAsKeyword, + ast.KindAssertsKeyword, + ast.KindAssertKeyword, + ast.KindAsyncKeyword, + ast.KindAwaitKeyword, + ast.KindConstructorKeyword, + ast.KindDeclareKeyword, + ast.KindGetKeyword, + ast.KindImmediateKeyword, + ast.KindInferKeyword, + ast.KindIsKeyword, + ast.KindKeyOfKeyword, + ast.KindModuleKeyword, + ast.KindNamespaceKeyword, + ast.KindOutKeyword, + ast.KindReadonlyKeyword, + ast.KindRequireKeyword, + ast.KindSatisfiesKeyword, + ast.KindSetKeyword, + ast.KindTypeKeyword, + ast.KindUniqueKeyword, + ast.KindUsingKeyword, + ast.KindFromKeyword, + ast.KindGlobalKeyword, + ast.KindOverrideKeyword, + ast.KindOfKeyword, + ast.KindDeferKeyword: + return d.factory.NewToken(kind), nil + case ast.KindQualifiedName: + it := newChildIter(childIndices) + left := d.nodeAt(it.nextIf(mask, 0)) + right := d.nodeAt(it.nextIf(mask, 1)) + return d.factory.NewQualifiedName(left, right), nil + case ast.KindComputedPropertyName: + return d.factory.NewComputedPropertyName(d.singleChild(childIndices)), nil + case ast.KindDecorator: + return d.factory.NewDecorator(d.singleChild(childIndices)), nil + case ast.KindEmptyStatement: + return d.factory.NewEmptyStatement(), nil + case ast.KindIfStatement: + it := newChildIter(childIndices) + expression := d.nodeAt(it.nextIf(mask, 0)) + thenStatement := d.nodeAt(it.nextIf(mask, 1)) + elseStatement := d.nodeAt(it.nextIf(mask, 2)) + return d.factory.NewIfStatement(expression, thenStatement, elseStatement), nil + case ast.KindDoStatement: + it := newChildIter(childIndices) + statement := d.nodeAt(it.nextIf(mask, 0)) + expression := d.nodeAt(it.nextIf(mask, 1)) + return d.factory.NewDoStatement(statement, expression), nil + case ast.KindWhileStatement: + it := newChildIter(childIndices) + expression := d.nodeAt(it.nextIf(mask, 0)) + statement := d.nodeAt(it.nextIf(mask, 1)) + return d.factory.NewWhileStatement(expression, statement), nil + case ast.KindForStatement: + it := newChildIter(childIndices) + initializer := d.nodeAt(it.nextIf(mask, 0)) + condition := d.nodeAt(it.nextIf(mask, 1)) + incrementor := d.nodeAt(it.nextIf(mask, 2)) + statement := d.nodeAt(it.nextIf(mask, 3)) + return d.factory.NewForStatement(initializer, condition, incrementor, statement), nil + case ast.KindForInStatement, ast.KindForOfStatement: + it := newChildIter(childIndices) + awaitModifier := d.nodeAt(it.nextIf(mask, 0)) + initializer := d.nodeAt(it.nextIf(mask, 1)) + expression := d.nodeAt(it.nextIf(mask, 2)) + statement := d.nodeAt(it.nextIf(mask, 3)) + return d.factory.NewForInOrOfStatement(kind, awaitModifier, initializer, expression, statement), nil + case ast.KindBreakStatement: + return d.factory.NewBreakStatement(d.singleChild(childIndices)), nil + case ast.KindContinueStatement: + return d.factory.NewContinueStatement(d.singleChild(childIndices)), nil + case ast.KindReturnStatement: + return d.factory.NewReturnStatement(d.singleChild(childIndices)), nil + case ast.KindWithStatement: + it := newChildIter(childIndices) + expression := d.nodeAt(it.nextIf(mask, 0)) + statement := d.nodeAt(it.nextIf(mask, 1)) + return d.factory.NewWithStatement(expression, statement), nil + case ast.KindSwitchStatement: + it := newChildIter(childIndices) + expression := d.nodeAt(it.nextIf(mask, 0)) + caseBlock := d.nodeAt(it.nextIf(mask, 1)) + return d.factory.NewSwitchStatement(expression, caseBlock), nil + case ast.KindCaseBlock: + return d.factory.NewCaseBlock(d.singleNodeListChild(childIndices)), nil + case ast.KindCaseClause, ast.KindDefaultClause: + it := newChildIter(childIndices) + expression := d.nodeAt(it.nextIf(mask, 0)) + statements := d.nodeListAt(it.nextIf(mask, 1)) + return d.factory.NewCaseOrDefaultClause(kind, expression, statements), nil + case ast.KindThrowStatement: + return d.factory.NewThrowStatement(d.singleChild(childIndices)), nil + case ast.KindTryStatement: + it := newChildIter(childIndices) + tryBlock := d.nodeAt(it.nextIf(mask, 0)) + catchClause := d.nodeAt(it.nextIf(mask, 1)) + finallyBlock := d.nodeAt(it.nextIf(mask, 2)) + return d.factory.NewTryStatement(tryBlock, catchClause, finallyBlock), nil + case ast.KindCatchClause: + it := newChildIter(childIndices) + variableDeclaration := d.nodeAt(it.nextIf(mask, 0)) + block := d.nodeAt(it.nextIf(mask, 1)) + return d.factory.NewCatchClause(variableDeclaration, block), nil + case ast.KindDebuggerStatement: + return d.factory.NewDebuggerStatement(), nil + case ast.KindLabeledStatement: + it := newChildIter(childIndices) + label := d.nodeAt(it.nextIf(mask, 0)) + statement := d.nodeAt(it.nextIf(mask, 1)) + return d.factory.NewLabeledStatement(label, statement), nil + case ast.KindExpressionStatement: + return d.factory.NewExpressionStatement(d.singleChild(childIndices)), nil + case ast.KindBlock: + multiLine := commonData&1 != 0 + var list *ast.NodeList + if len(childIndices) > 0 { + list = d.nodeListAt(childIndices[0]) + } + return d.factory.NewBlock(list, multiLine), nil + case ast.KindVariableStatement: + it := newChildIter(childIndices) + modifiers := d.modifierListAt(it.nextIf(mask, 0)) + declarationList := d.nodeAt(it.nextIf(mask, 1)) + return d.factory.NewVariableStatement(modifiers, declarationList), nil + case ast.KindVariableDeclaration: + it := newChildIter(childIndices) + name := d.nodeAt(it.nextIf(mask, 0)) + exclamationToken := d.nodeAt(it.nextIf(mask, 1)) + typeNode := d.nodeAt(it.nextIf(mask, 2)) + initializer := d.nodeAt(it.nextIf(mask, 3)) + return d.factory.NewVariableDeclaration(name, exclamationToken, typeNode, initializer), nil + case ast.KindVariableDeclarationList: + return d.factory.NewVariableDeclarationList(d.singleNodeListChild(childIndices), 0), nil + case ast.KindObjectBindingPattern, ast.KindArrayBindingPattern: + return d.factory.NewBindingPattern(kind, d.singleNodeListChild(childIndices)), nil + case ast.KindParameter: + it := newChildIter(childIndices) + modifiers := d.modifierListAt(it.nextIf(mask, 0)) + dotDotDotToken := d.nodeAt(it.nextIf(mask, 1)) + name := d.nodeAt(it.nextIf(mask, 2)) + questionToken := d.nodeAt(it.nextIf(mask, 3)) + typeNode := d.nodeAt(it.nextIf(mask, 4)) + initializer := d.nodeAt(it.nextIf(mask, 5)) + return d.factory.NewParameterDeclaration(modifiers, dotDotDotToken, name, questionToken, typeNode, initializer), nil + case ast.KindBindingElement: + it := newChildIter(childIndices) + dotDotDotToken := d.nodeAt(it.nextIf(mask, 0)) + propertyName := d.nodeAt(it.nextIf(mask, 1)) + name := d.nodeAt(it.nextIf(mask, 2)) + initializer := d.nodeAt(it.nextIf(mask, 3)) + return d.factory.NewBindingElement(dotDotDotToken, propertyName, name, initializer), nil + case ast.KindMissingDeclaration: + var mods *ast.ModifierList + if len(childIndices) > 0 { + mods = d.modifierListAt(childIndices[0]) + } + return d.factory.NewMissingDeclaration(mods), nil + case ast.KindFunctionDeclaration: + it := newChildIter(childIndices) + modifiers := d.modifierListAt(it.nextIf(mask, 0)) + asteriskToken := d.nodeAt(it.nextIf(mask, 1)) + name := d.nodeAt(it.nextIf(mask, 2)) + typeParameters := d.nodeListAt(it.nextIf(mask, 3)) + parameters := d.nodeListAt(it.nextIf(mask, 4)) + typeNode := d.nodeAt(it.nextIf(mask, 5)) + body := d.nodeAt(it.nextIf(mask, 6)) + return d.factory.NewFunctionDeclaration(modifiers, asteriskToken, name, typeParameters, parameters, typeNode, nil, body), nil + case ast.KindClassDeclaration: + it := newChildIter(childIndices) + modifiers := d.modifierListAt(it.nextIf(mask, 0)) + name := d.nodeAt(it.nextIf(mask, 1)) + typeParameters := d.nodeListAt(it.nextIf(mask, 2)) + heritageClauses := d.nodeListAt(it.nextIf(mask, 3)) + members := d.nodeListAt(it.nextIf(mask, 4)) + return d.factory.NewClassDeclaration(modifiers, name, typeParameters, heritageClauses, members), nil + case ast.KindClassExpression: + it := newChildIter(childIndices) + modifiers := d.modifierListAt(it.nextIf(mask, 0)) + name := d.nodeAt(it.nextIf(mask, 1)) + typeParameters := d.nodeListAt(it.nextIf(mask, 2)) + heritageClauses := d.nodeListAt(it.nextIf(mask, 3)) + members := d.nodeListAt(it.nextIf(mask, 4)) + return d.factory.NewClassExpression(modifiers, name, typeParameters, heritageClauses, members), nil + case ast.KindHeritageClause: + token := ast.KindExtendsKeyword + if commonData&1 != 0 { + token = ast.KindImplementsKeyword + } + return d.factory.NewHeritageClause(token, d.singleNodeListChild(childIndices)), nil + case ast.KindInterfaceDeclaration: + it := newChildIter(childIndices) + modifiers := d.modifierListAt(it.nextIf(mask, 0)) + name := d.nodeAt(it.nextIf(mask, 1)) + typeParameters := d.nodeListAt(it.nextIf(mask, 2)) + heritageClauses := d.nodeListAt(it.nextIf(mask, 3)) + members := d.nodeListAt(it.nextIf(mask, 4)) + return d.factory.NewInterfaceDeclaration(modifiers, name, typeParameters, heritageClauses, members), nil + case ast.KindTypeAliasDeclaration, ast.KindJSTypeAliasDeclaration: + it := newChildIter(childIndices) + modifiers := d.modifierListAt(it.nextIf(mask, 0)) + name := d.nodeAt(it.nextIf(mask, 1)) + typeParameters := d.nodeListAt(it.nextIf(mask, 2)) + typeNode := d.nodeAt(it.nextIf(mask, 3)) + if kind == ast.KindJSTypeAliasDeclaration { + return d.factory.NewJSTypeAliasDeclaration(modifiers, name, typeParameters, typeNode), nil + } + return d.factory.NewTypeAliasDeclaration(modifiers, name, typeParameters, typeNode), nil + case ast.KindEnumMember: + it := newChildIter(childIndices) + name := d.nodeAt(it.nextIf(mask, 0)) + initializer := d.nodeAt(it.nextIf(mask, 1)) + return d.factory.NewEnumMember(name, initializer), nil + case ast.KindEnumDeclaration: + it := newChildIter(childIndices) + modifiers := d.modifierListAt(it.nextIf(mask, 0)) + name := d.nodeAt(it.nextIf(mask, 1)) + members := d.nodeListAt(it.nextIf(mask, 2)) + return d.factory.NewEnumDeclaration(modifiers, name, members), nil + case ast.KindModuleBlock: + return d.factory.NewModuleBlock(d.singleNodeListChild(childIndices)), nil + case ast.KindNotEmittedStatement: + return d.factory.NewNotEmittedStatement(), nil + case ast.KindNotEmittedTypeElement: + return d.factory.NewNotEmittedTypeElement(), nil + case ast.KindImportDeclaration, ast.KindJSImportDeclaration: + it := newChildIter(childIndices) + modifiers := d.modifierListAt(it.nextIf(mask, 0)) + importClause := d.nodeAt(it.nextIf(mask, 1)) + moduleSpecifier := d.nodeAt(it.nextIf(mask, 2)) + attributes := d.nodeAt(it.nextIf(mask, 3)) + if kind == ast.KindJSImportDeclaration { + return d.factory.NewJSImportDeclaration(modifiers, importClause, moduleSpecifier, attributes), nil + } + return d.factory.NewImportDeclaration(modifiers, importClause, moduleSpecifier, attributes), nil + case ast.KindExternalModuleReference: + return d.factory.NewExternalModuleReference(d.singleChild(childIndices)), nil + case ast.KindNamespaceImport: + return d.factory.NewNamespaceImport(d.singleChild(childIndices)), nil + case ast.KindNamedImports: + return d.factory.NewNamedImports(d.singleNodeListChild(childIndices)), nil + case ast.KindExportAssignment: + isExportEquals := commonData&1 != 0 + it := newChildIter(childIndices) + modifiers := d.modifierListAt(it.nextIf(mask, 0)) + typeNode := d.nodeAt(it.nextIf(mask, 1)) + expression := d.nodeAt(it.nextIf(mask, 2)) + return d.factory.NewExportAssignment(modifiers, isExportEquals, typeNode, expression), nil + case ast.KindNamespaceExportDeclaration: + it := newChildIter(childIndices) + modifiers := d.modifierListAt(it.nextIf(mask, 0)) + name := d.nodeAt(it.nextIf(mask, 1)) + return d.factory.NewNamespaceExportDeclaration(modifiers, name), nil + case ast.KindNamespaceExport: + return d.factory.NewNamespaceExport(d.singleChild(childIndices)), nil + case ast.KindNamedExports: + return d.factory.NewNamedExports(d.singleNodeListChild(childIndices)), nil + case ast.KindExportSpecifier: + isTypeOnly := commonData&1 != 0 + it := newChildIter(childIndices) + propertyName := d.nodeAt(it.nextIf(mask, 0)) + name := d.nodeAt(it.nextIf(mask, 1)) + return d.factory.NewExportSpecifier(isTypeOnly, propertyName, name), nil + case ast.KindCallSignature: + it := newChildIter(childIndices) + typeParameters := d.nodeListAt(it.nextIf(mask, 0)) + parameters := d.nodeListAt(it.nextIf(mask, 1)) + typeNode := d.nodeAt(it.nextIf(mask, 2)) + return d.factory.NewCallSignatureDeclaration(typeParameters, parameters, typeNode), nil + case ast.KindConstructSignature: + it := newChildIter(childIndices) + typeParameters := d.nodeListAt(it.nextIf(mask, 0)) + parameters := d.nodeListAt(it.nextIf(mask, 1)) + typeNode := d.nodeAt(it.nextIf(mask, 2)) + return d.factory.NewConstructSignatureDeclaration(typeParameters, parameters, typeNode), nil + case ast.KindConstructor: + it := newChildIter(childIndices) + modifiers := d.modifierListAt(it.nextIf(mask, 0)) + typeParameters := d.nodeListAt(it.nextIf(mask, 1)) + parameters := d.nodeListAt(it.nextIf(mask, 2)) + typeNode := d.nodeAt(it.nextIf(mask, 3)) + body := d.nodeAt(it.nextIf(mask, 4)) + return d.factory.NewConstructorDeclaration(modifiers, typeParameters, parameters, typeNode, nil, body), nil + case ast.KindGetAccessor: + it := newChildIter(childIndices) + modifiers := d.modifierListAt(it.nextIf(mask, 0)) + name := d.nodeAt(it.nextIf(mask, 1)) + typeParameters := d.nodeListAt(it.nextIf(mask, 2)) + parameters := d.nodeListAt(it.nextIf(mask, 3)) + typeNode := d.nodeAt(it.nextIf(mask, 4)) + body := d.nodeAt(it.nextIf(mask, 5)) + return d.factory.NewGetAccessorDeclaration(modifiers, name, typeParameters, parameters, typeNode, nil, body), nil + case ast.KindSetAccessor: + it := newChildIter(childIndices) + modifiers := d.modifierListAt(it.nextIf(mask, 0)) + name := d.nodeAt(it.nextIf(mask, 1)) + typeParameters := d.nodeListAt(it.nextIf(mask, 2)) + parameters := d.nodeListAt(it.nextIf(mask, 3)) + typeNode := d.nodeAt(it.nextIf(mask, 4)) + body := d.nodeAt(it.nextIf(mask, 5)) + return d.factory.NewSetAccessorDeclaration(modifiers, name, typeParameters, parameters, typeNode, nil, body), nil + case ast.KindIndexSignature: + it := newChildIter(childIndices) + modifiers := d.modifierListAt(it.nextIf(mask, 0)) + parameters := d.nodeListAt(it.nextIf(mask, 1)) + typeNode := d.nodeAt(it.nextIf(mask, 2)) + return d.factory.NewIndexSignatureDeclaration(modifiers, parameters, typeNode), nil + case ast.KindMethodSignature: + it := newChildIter(childIndices) + modifiers := d.modifierListAt(it.nextIf(mask, 0)) + name := d.nodeAt(it.nextIf(mask, 1)) + postfixToken := d.nodeAt(it.nextIf(mask, 2)) + typeParameters := d.nodeListAt(it.nextIf(mask, 3)) + parameters := d.nodeListAt(it.nextIf(mask, 4)) + typeNode := d.nodeAt(it.nextIf(mask, 5)) + return d.factory.NewMethodSignatureDeclaration(modifiers, name, postfixToken, typeParameters, parameters, typeNode), nil + case ast.KindMethodDeclaration: + it := newChildIter(childIndices) + modifiers := d.modifierListAt(it.nextIf(mask, 0)) + asteriskToken := d.nodeAt(it.nextIf(mask, 1)) + name := d.nodeAt(it.nextIf(mask, 2)) + postfixToken := d.nodeAt(it.nextIf(mask, 3)) + typeParameters := d.nodeListAt(it.nextIf(mask, 4)) + parameters := d.nodeListAt(it.nextIf(mask, 5)) + typeNode := d.nodeAt(it.nextIf(mask, 6)) + body := d.nodeAt(it.nextIf(mask, 7)) + return d.factory.NewMethodDeclaration(modifiers, asteriskToken, name, postfixToken, typeParameters, parameters, typeNode, nil, body), nil + case ast.KindPropertySignature: + it := newChildIter(childIndices) + modifiers := d.modifierListAt(it.nextIf(mask, 0)) + name := d.nodeAt(it.nextIf(mask, 1)) + postfixToken := d.nodeAt(it.nextIf(mask, 2)) + typeNode := d.nodeAt(it.nextIf(mask, 3)) + initializer := d.nodeAt(it.nextIf(mask, 4)) + return d.factory.NewPropertySignatureDeclaration(modifiers, name, postfixToken, typeNode, initializer), nil + case ast.KindPropertyDeclaration: + it := newChildIter(childIndices) + modifiers := d.modifierListAt(it.nextIf(mask, 0)) + name := d.nodeAt(it.nextIf(mask, 1)) + postfixToken := d.nodeAt(it.nextIf(mask, 2)) + typeNode := d.nodeAt(it.nextIf(mask, 3)) + initializer := d.nodeAt(it.nextIf(mask, 4)) + return d.factory.NewPropertyDeclaration(modifiers, name, postfixToken, typeNode, initializer), nil + case ast.KindSemicolonClassElement: + return d.factory.NewSemicolonClassElement(), nil + case ast.KindClassStaticBlockDeclaration: + it := newChildIter(childIndices) + modifiers := d.modifierListAt(it.nextIf(mask, 0)) + body := d.nodeAt(it.nextIf(mask, 1)) + return d.factory.NewClassStaticBlockDeclaration(modifiers, body), nil + case ast.KindOmittedExpression: + return d.factory.NewOmittedExpression(), nil + case ast.KindFalseKeyword, + ast.KindImportKeyword, + ast.KindNullKeyword, + ast.KindSuperKeyword, + ast.KindThisKeyword, + ast.KindTrueKeyword: + return d.factory.NewKeywordExpression(kind), nil + case ast.KindBinaryExpression: + it := newChildIter(childIndices) + modifiers := d.modifierListAt(it.nextIf(mask, 0)) + left := d.nodeAt(it.nextIf(mask, 1)) + typeNode := d.nodeAt(it.nextIf(mask, 2)) + operatorToken := d.nodeAt(it.nextIf(mask, 3)) + right := d.nodeAt(it.nextIf(mask, 4)) + return d.factory.NewBinaryExpression(modifiers, left, typeNode, operatorToken, right), nil + case ast.KindPrefixUnaryExpression: + var operator ast.Kind + switch commonData & 7 { + case 0: + operator = ast.KindPlusToken + case 1: + operator = ast.KindMinusToken + case 2: + operator = ast.KindTildeToken + case 3: + operator = ast.KindExclamationToken + case 4: + operator = ast.KindPlusPlusToken + case 5: + operator = ast.KindMinusMinusToken + } + return d.factory.NewPrefixUnaryExpression(operator, d.singleChild(childIndices)), nil + case ast.KindPostfixUnaryExpression: + operator := ast.KindPlusPlusToken + if commonData&1 != 0 { + operator = ast.KindMinusMinusToken + } + return d.factory.NewPostfixUnaryExpression(d.singleChild(childIndices), operator), nil + case ast.KindYieldExpression: + it := newChildIter(childIndices) + asteriskToken := d.nodeAt(it.nextIf(mask, 0)) + expression := d.nodeAt(it.nextIf(mask, 1)) + return d.factory.NewYieldExpression(asteriskToken, expression), nil + case ast.KindArrowFunction: + it := newChildIter(childIndices) + modifiers := d.modifierListAt(it.nextIf(mask, 0)) + typeParameters := d.nodeListAt(it.nextIf(mask, 1)) + parameters := d.nodeListAt(it.nextIf(mask, 2)) + typeNode := d.nodeAt(it.nextIf(mask, 3)) + equalsGreaterThanToken := d.nodeAt(it.nextIf(mask, 4)) + body := d.nodeAt(it.nextIf(mask, 5)) + return d.factory.NewArrowFunction(modifiers, typeParameters, parameters, typeNode, nil, equalsGreaterThanToken, body), nil + case ast.KindFunctionExpression: + it := newChildIter(childIndices) + modifiers := d.modifierListAt(it.nextIf(mask, 0)) + asteriskToken := d.nodeAt(it.nextIf(mask, 1)) + name := d.nodeAt(it.nextIf(mask, 2)) + typeParameters := d.nodeListAt(it.nextIf(mask, 3)) + parameters := d.nodeListAt(it.nextIf(mask, 4)) + typeNode := d.nodeAt(it.nextIf(mask, 5)) + body := d.nodeAt(it.nextIf(mask, 6)) + return d.factory.NewFunctionExpression(modifiers, asteriskToken, name, typeParameters, parameters, typeNode, nil, body), nil + case ast.KindAsExpression: + it := newChildIter(childIndices) + expression := d.nodeAt(it.nextIf(mask, 0)) + typeNode := d.nodeAt(it.nextIf(mask, 1)) + return d.factory.NewAsExpression(expression, typeNode), nil + case ast.KindSatisfiesExpression: + it := newChildIter(childIndices) + expression := d.nodeAt(it.nextIf(mask, 0)) + typeNode := d.nodeAt(it.nextIf(mask, 1)) + return d.factory.NewSatisfiesExpression(expression, typeNode), nil + case ast.KindConditionalExpression: + it := newChildIter(childIndices) + condition := d.nodeAt(it.nextIf(mask, 0)) + questionToken := d.nodeAt(it.nextIf(mask, 1)) + whenTrue := d.nodeAt(it.nextIf(mask, 2)) + colonToken := d.nodeAt(it.nextIf(mask, 3)) + whenFalse := d.nodeAt(it.nextIf(mask, 4)) + return d.factory.NewConditionalExpression(condition, questionToken, whenTrue, colonToken, whenFalse), nil + case ast.KindPropertyAccessExpression: + it := newChildIter(childIndices) + expression := d.nodeAt(it.nextIf(mask, 0)) + questionDotToken := d.nodeAt(it.nextIf(mask, 1)) + name := d.nodeAt(it.nextIf(mask, 2)) + return d.factory.NewPropertyAccessExpression(expression, questionDotToken, name, 0), nil + case ast.KindElementAccessExpression: + it := newChildIter(childIndices) + expression := d.nodeAt(it.nextIf(mask, 0)) + questionDotToken := d.nodeAt(it.nextIf(mask, 1)) + argumentExpression := d.nodeAt(it.nextIf(mask, 2)) + return d.factory.NewElementAccessExpression(expression, questionDotToken, argumentExpression, 0), nil + case ast.KindCallExpression: + it := newChildIter(childIndices) + expression := d.nodeAt(it.nextIf(mask, 0)) + questionDotToken := d.nodeAt(it.nextIf(mask, 1)) + typeArguments := d.nodeListAt(it.nextIf(mask, 2)) + arguments := d.nodeListAt(it.nextIf(mask, 3)) + return d.factory.NewCallExpression(expression, questionDotToken, typeArguments, arguments, 0), nil + case ast.KindNewExpression: + it := newChildIter(childIndices) + expression := d.nodeAt(it.nextIf(mask, 0)) + typeArguments := d.nodeListAt(it.nextIf(mask, 1)) + arguments := d.nodeListAt(it.nextIf(mask, 2)) + return d.factory.NewNewExpression(expression, typeArguments, arguments), nil + case ast.KindMetaProperty: + keywordToken := ast.KindImportKeyword + if commonData&1 != 0 { + keywordToken = ast.KindNewKeyword + } + return d.factory.NewMetaProperty(keywordToken, d.singleChild(childIndices)), nil + case ast.KindNonNullExpression: + return d.factory.NewNonNullExpression(d.singleChild(childIndices), 0), nil + case ast.KindSpreadElement: + return d.factory.NewSpreadElement(d.singleChild(childIndices)), nil + case ast.KindTemplateExpression: + it := newChildIter(childIndices) + head := d.nodeAt(it.nextIf(mask, 0)) + templateSpans := d.nodeListAt(it.nextIf(mask, 1)) + return d.factory.NewTemplateExpression(head, templateSpans), nil + case ast.KindTemplateSpan: + it := newChildIter(childIndices) + expression := d.nodeAt(it.nextIf(mask, 0)) + literal := d.nodeAt(it.nextIf(mask, 1)) + return d.factory.NewTemplateSpan(expression, literal), nil + case ast.KindTaggedTemplateExpression: + it := newChildIter(childIndices) + tag := d.nodeAt(it.nextIf(mask, 0)) + questionDotToken := d.nodeAt(it.nextIf(mask, 1)) + typeArguments := d.nodeListAt(it.nextIf(mask, 2)) + template := d.nodeAt(it.nextIf(mask, 3)) + return d.factory.NewTaggedTemplateExpression(tag, questionDotToken, typeArguments, template, 0), nil + case ast.KindParenthesizedExpression: + return d.factory.NewParenthesizedExpression(d.singleChild(childIndices)), nil + case ast.KindArrayLiteralExpression: + multiLine := commonData&1 != 0 + var list *ast.NodeList + if len(childIndices) > 0 { + list = d.nodeListAt(childIndices[0]) + } + return d.factory.NewArrayLiteralExpression(list, multiLine), nil + case ast.KindObjectLiteralExpression: + multiLine := commonData&1 != 0 + var list *ast.NodeList + if len(childIndices) > 0 { + list = d.nodeListAt(childIndices[0]) + } + return d.factory.NewObjectLiteralExpression(list, multiLine), nil + case ast.KindSpreadAssignment: + return d.factory.NewSpreadAssignment(d.singleChild(childIndices)), nil + case ast.KindPropertyAssignment: + it := newChildIter(childIndices) + modifiers := d.modifierListAt(it.nextIf(mask, 0)) + name := d.nodeAt(it.nextIf(mask, 1)) + postfixToken := d.nodeAt(it.nextIf(mask, 2)) + typeNode := d.nodeAt(it.nextIf(mask, 3)) + initializer := d.nodeAt(it.nextIf(mask, 4)) + return d.factory.NewPropertyAssignment(modifiers, name, postfixToken, typeNode, initializer), nil + case ast.KindShorthandPropertyAssignment: + it := newChildIter(childIndices) + modifiers := d.modifierListAt(it.nextIf(mask, 0)) + name := d.nodeAt(it.nextIf(mask, 1)) + postfixToken := d.nodeAt(it.nextIf(mask, 2)) + typeNode := d.nodeAt(it.nextIf(mask, 3)) + equalsToken := d.nodeAt(it.nextIf(mask, 4)) + objectAssignmentInitializer := d.nodeAt(it.nextIf(mask, 5)) + return d.factory.NewShorthandPropertyAssignment(modifiers, name, postfixToken, typeNode, equalsToken, objectAssignmentInitializer), nil + case ast.KindDeleteExpression: + return d.factory.NewDeleteExpression(d.singleChild(childIndices)), nil + case ast.KindTypeOfExpression: + return d.factory.NewTypeOfExpression(d.singleChild(childIndices)), nil + case ast.KindVoidExpression: + return d.factory.NewVoidExpression(d.singleChild(childIndices)), nil + case ast.KindAwaitExpression: + return d.factory.NewAwaitExpression(d.singleChild(childIndices)), nil + case ast.KindTypeAssertionExpression: + it := newChildIter(childIndices) + typeNode := d.nodeAt(it.nextIf(mask, 0)) + expression := d.nodeAt(it.nextIf(mask, 1)) + return d.factory.NewTypeAssertion(typeNode, expression), nil + case ast.KindVoidKeyword, + ast.KindAnyKeyword, + ast.KindBooleanKeyword, + ast.KindIntrinsicKeyword, + ast.KindNeverKeyword, + ast.KindNumberKeyword, + ast.KindObjectKeyword, + ast.KindStringKeyword, + ast.KindSymbolKeyword, + ast.KindUndefinedKeyword, + ast.KindUnknownKeyword, + ast.KindBigIntKeyword: + return d.factory.NewKeywordTypeNode(kind), nil + case ast.KindUnionType: + return d.factory.NewUnionTypeNode(d.singleNodeListChild(childIndices)), nil + case ast.KindIntersectionType: + return d.factory.NewIntersectionTypeNode(d.singleNodeListChild(childIndices)), nil + case ast.KindConditionalType: + it := newChildIter(childIndices) + checkType := d.nodeAt(it.nextIf(mask, 0)) + extendsType := d.nodeAt(it.nextIf(mask, 1)) + trueType := d.nodeAt(it.nextIf(mask, 2)) + falseType := d.nodeAt(it.nextIf(mask, 3)) + return d.factory.NewConditionalTypeNode(checkType, extendsType, trueType, falseType), nil + case ast.KindTypeOperator: + var operator ast.Kind + switch commonData & 3 { + case 0: + operator = ast.KindKeyOfKeyword + case 1: + operator = ast.KindReadonlyKeyword + case 2: + operator = ast.KindUniqueKeyword + } + return d.factory.NewTypeOperatorNode(operator, d.singleChild(childIndices)), nil + case ast.KindInferType: + return d.factory.NewInferTypeNode(d.singleChild(childIndices)), nil + case ast.KindArrayType: + return d.factory.NewArrayTypeNode(d.singleChild(childIndices)), nil + case ast.KindIndexedAccessType: + it := newChildIter(childIndices) + objectType := d.nodeAt(it.nextIf(mask, 0)) + indexType := d.nodeAt(it.nextIf(mask, 1)) + return d.factory.NewIndexedAccessTypeNode(objectType, indexType), nil + case ast.KindTypeReference: + it := newChildIter(childIndices) + typeName := d.nodeAt(it.nextIf(mask, 0)) + typeArguments := d.nodeListAt(it.nextIf(mask, 1)) + return d.factory.NewTypeReferenceNode(typeName, typeArguments), nil + case ast.KindExpressionWithTypeArguments: + it := newChildIter(childIndices) + expression := d.nodeAt(it.nextIf(mask, 0)) + typeArguments := d.nodeListAt(it.nextIf(mask, 1)) + return d.factory.NewExpressionWithTypeArguments(expression, typeArguments), nil + case ast.KindLiteralType: + return d.factory.NewLiteralTypeNode(d.singleChild(childIndices)), nil + case ast.KindThisType: + return d.factory.NewThisTypeNode(), nil + case ast.KindTypePredicate: + it := newChildIter(childIndices) + assertsModifier := d.nodeAt(it.nextIf(mask, 0)) + parameterName := d.nodeAt(it.nextIf(mask, 1)) + typeNode := d.nodeAt(it.nextIf(mask, 2)) + return d.factory.NewTypePredicateNode(assertsModifier, parameterName, typeNode), nil + case ast.KindImportAttribute: + it := newChildIter(childIndices) + name := d.nodeAt(it.nextIf(mask, 0)) + value := d.nodeAt(it.nextIf(mask, 1)) + return d.factory.NewImportAttribute(name, value), nil + case ast.KindImportAttributes: + multiLine := commonData&1 != 0 + token := ast.KindWithKeyword + if (commonData>>1)&1 != 0 { + token = ast.KindAssertKeyword + } + var list *ast.NodeList + if len(childIndices) > 0 { + list = d.nodeListAt(childIndices[0]) + } + return d.factory.NewImportAttributes(token, list, multiLine), nil + case ast.KindTypeQuery: + it := newChildIter(childIndices) + exprName := d.nodeAt(it.nextIf(mask, 0)) + typeArguments := d.nodeListAt(it.nextIf(mask, 1)) + return d.factory.NewTypeQueryNode(exprName, typeArguments), nil + case ast.KindMappedType: + it := newChildIter(childIndices) + readonlyToken := d.nodeAt(it.nextIf(mask, 0)) + typeParameter := d.nodeAt(it.nextIf(mask, 1)) + nameType := d.nodeAt(it.nextIf(mask, 2)) + questionToken := d.nodeAt(it.nextIf(mask, 3)) + typeNode := d.nodeAt(it.nextIf(mask, 4)) + members := d.nodeListAt(it.nextIf(mask, 5)) + return d.factory.NewMappedTypeNode(readonlyToken, typeParameter, nameType, questionToken, typeNode, members), nil + case ast.KindTypeLiteral: + return d.factory.NewTypeLiteralNode(d.singleNodeListChild(childIndices)), nil + case ast.KindTupleType: + return d.factory.NewTupleTypeNode(d.singleNodeListChild(childIndices)), nil + case ast.KindNamedTupleMember: + it := newChildIter(childIndices) + dotDotDotToken := d.nodeAt(it.nextIf(mask, 0)) + name := d.nodeAt(it.nextIf(mask, 1)) + questionToken := d.nodeAt(it.nextIf(mask, 2)) + typeNode := d.nodeAt(it.nextIf(mask, 3)) + return d.factory.NewNamedTupleMember(dotDotDotToken, name, questionToken, typeNode), nil + case ast.KindOptionalType: + return d.factory.NewOptionalTypeNode(d.singleChild(childIndices)), nil + case ast.KindRestType: + return d.factory.NewRestTypeNode(d.singleChild(childIndices)), nil + case ast.KindParenthesizedType: + return d.factory.NewParenthesizedTypeNode(d.singleChild(childIndices)), nil + case ast.KindFunctionType: + it := newChildIter(childIndices) + typeParameters := d.nodeListAt(it.nextIf(mask, 0)) + parameters := d.nodeListAt(it.nextIf(mask, 1)) + typeNode := d.nodeAt(it.nextIf(mask, 2)) + return d.factory.NewFunctionTypeNode(typeParameters, parameters, typeNode), nil + case ast.KindConstructorType: + it := newChildIter(childIndices) + modifiers := d.modifierListAt(it.nextIf(mask, 0)) + typeParameters := d.nodeListAt(it.nextIf(mask, 1)) + parameters := d.nodeListAt(it.nextIf(mask, 2)) + typeNode := d.nodeAt(it.nextIf(mask, 3)) + return d.factory.NewConstructorTypeNode(modifiers, typeParameters, parameters, typeNode), nil + case ast.KindTemplateLiteralType: + it := newChildIter(childIndices) + head := d.nodeAt(it.nextIf(mask, 0)) + templateSpans := d.nodeListAt(it.nextIf(mask, 1)) + return d.factory.NewTemplateLiteralTypeNode(head, templateSpans), nil + case ast.KindTemplateLiteralTypeSpan: + it := newChildIter(childIndices) + typeNode := d.nodeAt(it.nextIf(mask, 0)) + literal := d.nodeAt(it.nextIf(mask, 1)) + return d.factory.NewTemplateLiteralTypeSpan(typeNode, literal), nil + case ast.KindSyntheticExpression: + typeNode, isSpread := decodeNodeCommonData_SyntheticExpression(commonData) + return d.factory.NewSyntheticExpression(typeNode, isSpread, d.singleChild(childIndices)), nil + case ast.KindPartiallyEmittedExpression: + return d.factory.NewPartiallyEmittedExpression(d.singleChild(childIndices)), nil + case ast.KindJsxElement: + it := newChildIter(childIndices) + openingElement := d.nodeAt(it.nextIf(mask, 0)) + children := d.nodeListAt(it.nextIf(mask, 1)) + closingElement := d.nodeAt(it.nextIf(mask, 2)) + return d.factory.NewJsxElement(openingElement, children, closingElement), nil + case ast.KindJsxAttributes: + return d.factory.NewJsxAttributes(d.singleNodeListChild(childIndices)), nil + case ast.KindJsxNamespacedName: + it := newChildIter(childIndices) + namespace := d.nodeAt(it.nextIf(mask, 0)) + name := d.nodeAt(it.nextIf(mask, 1)) + return d.factory.NewJsxNamespacedName(namespace, name), nil + case ast.KindJsxOpeningElement: + it := newChildIter(childIndices) + tagName := d.nodeAt(it.nextIf(mask, 0)) + typeArguments := d.nodeListAt(it.nextIf(mask, 1)) + attributes := d.nodeAt(it.nextIf(mask, 2)) + return d.factory.NewJsxOpeningElement(tagName, typeArguments, attributes), nil + case ast.KindJsxSelfClosingElement: + it := newChildIter(childIndices) + tagName := d.nodeAt(it.nextIf(mask, 0)) + typeArguments := d.nodeListAt(it.nextIf(mask, 1)) + attributes := d.nodeAt(it.nextIf(mask, 2)) + return d.factory.NewJsxSelfClosingElement(tagName, typeArguments, attributes), nil + case ast.KindJsxFragment: + it := newChildIter(childIndices) + openingFragment := d.nodeAt(it.nextIf(mask, 0)) + children := d.nodeListAt(it.nextIf(mask, 1)) + closingFragment := d.nodeAt(it.nextIf(mask, 2)) + return d.factory.NewJsxFragment(openingFragment, children, closingFragment), nil + case ast.KindJsxOpeningFragment: + return d.factory.NewJsxOpeningFragment(), nil + case ast.KindJsxClosingFragment: + return d.factory.NewJsxClosingFragment(), nil + case ast.KindJsxAttribute: + it := newChildIter(childIndices) + name := d.nodeAt(it.nextIf(mask, 0)) + initializer := d.nodeAt(it.nextIf(mask, 1)) + return d.factory.NewJsxAttribute(name, initializer), nil + case ast.KindJsxSpreadAttribute: + return d.factory.NewJsxSpreadAttribute(d.singleChild(childIndices)), nil + case ast.KindJsxClosingElement: + return d.factory.NewJsxClosingElement(d.singleChild(childIndices)), nil + case ast.KindJsxExpression: + it := newChildIter(childIndices) + dotDotDotToken := d.nodeAt(it.nextIf(mask, 0)) + expression := d.nodeAt(it.nextIf(mask, 1)) + return d.factory.NewJsxExpression(dotDotDotToken, expression), nil + case ast.KindSyntaxList: + nodes := d.allocNodeSlice(len(childIndices)) + for i, ci := range childIndices { + nodes[i] = d.nodes[ci] + } + return d.factory.NewSyntaxList(nodes), nil + case ast.KindJSDoc: + it := newChildIter(childIndices) + comment := d.nodeListAt(it.nextIf(mask, 0)) + tags := d.nodeListAt(it.nextIf(mask, 1)) + return d.factory.NewJSDoc(comment, tags), nil + case ast.KindJSDocTypeExpression: + return d.factory.NewJSDocTypeExpression(d.singleChild(childIndices)), nil + case ast.KindJSDocNonNullableType: + return d.factory.NewJSDocNonNullableType(d.singleChild(childIndices)), nil + case ast.KindJSDocNullableType: + return d.factory.NewJSDocNullableType(d.singleChild(childIndices)), nil + case ast.KindJSDocAllType: + return d.factory.NewJSDocAllType(), nil + case ast.KindJSDocVariadicType: + return d.factory.NewJSDocVariadicType(d.singleChild(childIndices)), nil + case ast.KindJSDocOptionalType: + return d.factory.NewJSDocOptionalType(d.singleChild(childIndices)), nil + case ast.KindJSDocTypeTag: + it := newChildIter(childIndices) + tagName := d.nodeAt(it.nextIf(mask, 0)) + typeExpression := d.nodeAt(it.nextIf(mask, 1)) + comment := d.nodeListAt(it.nextIf(mask, 2)) + return d.factory.NewJSDocTypeTag(tagName, typeExpression, comment), nil + case ast.KindJSDocUnknownTag: + it := newChildIter(childIndices) + tagName := d.nodeAt(it.nextIf(mask, 0)) + comment := d.nodeListAt(it.nextIf(mask, 1)) + return d.factory.NewJSDocUnknownTag(tagName, comment), nil + case ast.KindJSDocTemplateTag: + it := newChildIter(childIndices) + tagName := d.nodeAt(it.nextIf(mask, 0)) + constraint := d.nodeAt(it.nextIf(mask, 1)) + typeParameters := d.nodeListAt(it.nextIf(mask, 2)) + comment := d.nodeListAt(it.nextIf(mask, 3)) + return d.factory.NewJSDocTemplateTag(tagName, constraint, typeParameters, comment), nil + case ast.KindJSDocReturnTag: + it := newChildIter(childIndices) + tagName := d.nodeAt(it.nextIf(mask, 0)) + typeExpression := d.nodeAt(it.nextIf(mask, 1)) + comment := d.nodeListAt(it.nextIf(mask, 2)) + return d.factory.NewJSDocReturnTag(tagName, typeExpression, comment), nil + case ast.KindJSDocPublicTag: + it := newChildIter(childIndices) + tagName := d.nodeAt(it.nextIf(mask, 0)) + comment := d.nodeListAt(it.nextIf(mask, 1)) + return d.factory.NewJSDocPublicTag(tagName, comment), nil + case ast.KindJSDocPrivateTag: + it := newChildIter(childIndices) + tagName := d.nodeAt(it.nextIf(mask, 0)) + comment := d.nodeListAt(it.nextIf(mask, 1)) + return d.factory.NewJSDocPrivateTag(tagName, comment), nil + case ast.KindJSDocProtectedTag: + it := newChildIter(childIndices) + tagName := d.nodeAt(it.nextIf(mask, 0)) + comment := d.nodeListAt(it.nextIf(mask, 1)) + return d.factory.NewJSDocProtectedTag(tagName, comment), nil + case ast.KindJSDocReadonlyTag: + it := newChildIter(childIndices) + tagName := d.nodeAt(it.nextIf(mask, 0)) + comment := d.nodeListAt(it.nextIf(mask, 1)) + return d.factory.NewJSDocReadonlyTag(tagName, comment), nil + case ast.KindJSDocOverrideTag: + it := newChildIter(childIndices) + tagName := d.nodeAt(it.nextIf(mask, 0)) + comment := d.nodeListAt(it.nextIf(mask, 1)) + return d.factory.NewJSDocOverrideTag(tagName, comment), nil + case ast.KindJSDocDeprecatedTag: + it := newChildIter(childIndices) + tagName := d.nodeAt(it.nextIf(mask, 0)) + comment := d.nodeListAt(it.nextIf(mask, 1)) + return d.factory.NewJSDocDeprecatedTag(tagName, comment), nil + case ast.KindJSDocSeeTag: + it := newChildIter(childIndices) + tagName := d.nodeAt(it.nextIf(mask, 0)) + nameExpression := d.nodeAt(it.nextIf(mask, 1)) + comment := d.nodeListAt(it.nextIf(mask, 2)) + return d.factory.NewJSDocSeeTag(tagName, nameExpression, comment), nil + case ast.KindJSDocImplementsTag: + it := newChildIter(childIndices) + tagName := d.nodeAt(it.nextIf(mask, 0)) + className := d.nodeAt(it.nextIf(mask, 1)) + comment := d.nodeListAt(it.nextIf(mask, 2)) + return d.factory.NewJSDocImplementsTag(tagName, className, comment), nil + case ast.KindJSDocAugmentsTag: + it := newChildIter(childIndices) + tagName := d.nodeAt(it.nextIf(mask, 0)) + className := d.nodeAt(it.nextIf(mask, 1)) + comment := d.nodeListAt(it.nextIf(mask, 2)) + return d.factory.NewJSDocAugmentsTag(tagName, className, comment), nil + case ast.KindJSDocSatisfiesTag: + it := newChildIter(childIndices) + tagName := d.nodeAt(it.nextIf(mask, 0)) + typeExpression := d.nodeAt(it.nextIf(mask, 1)) + comment := d.nodeListAt(it.nextIf(mask, 2)) + return d.factory.NewJSDocSatisfiesTag(tagName, typeExpression, comment), nil + case ast.KindJSDocThrowsTag: + it := newChildIter(childIndices) + tagName := d.nodeAt(it.nextIf(mask, 0)) + typeExpression := d.nodeAt(it.nextIf(mask, 1)) + comment := d.nodeListAt(it.nextIf(mask, 2)) + return d.factory.NewJSDocThrowsTag(tagName, typeExpression, comment), nil + case ast.KindJSDocThisTag: + it := newChildIter(childIndices) + tagName := d.nodeAt(it.nextIf(mask, 0)) + typeExpression := d.nodeAt(it.nextIf(mask, 1)) + comment := d.nodeListAt(it.nextIf(mask, 2)) + return d.factory.NewJSDocThisTag(tagName, typeExpression, comment), nil + case ast.KindJSDocImportTag: + it := newChildIter(childIndices) + tagName := d.nodeAt(it.nextIf(mask, 0)) + importClause := d.nodeAt(it.nextIf(mask, 1)) + moduleSpecifier := d.nodeAt(it.nextIf(mask, 2)) + attributes := d.nodeAt(it.nextIf(mask, 3)) + comment := d.nodeListAt(it.nextIf(mask, 4)) + return d.factory.NewJSDocImportTag(tagName, importClause, moduleSpecifier, attributes, comment), nil + case ast.KindJSDocCallbackTag: + it := newChildIter(childIndices) + tagName := d.nodeAt(it.nextIf(mask, 0)) + typeExpression := d.nodeAt(it.nextIf(mask, 1)) + name := d.nodeAt(it.nextIf(mask, 2)) + comment := d.nodeListAt(it.nextIf(mask, 3)) + return d.factory.NewJSDocCallbackTag(tagName, typeExpression, name, comment), nil + case ast.KindJSDocOverloadTag: + it := newChildIter(childIndices) + tagName := d.nodeAt(it.nextIf(mask, 0)) + typeExpression := d.nodeAt(it.nextIf(mask, 1)) + comment := d.nodeListAt(it.nextIf(mask, 2)) + return d.factory.NewJSDocOverloadTag(tagName, typeExpression, comment), nil + case ast.KindJSDocTypedefTag: + it := newChildIter(childIndices) + tagName := d.nodeAt(it.nextIf(mask, 0)) + typeExpression := d.nodeAt(it.nextIf(mask, 1)) + name := d.nodeAt(it.nextIf(mask, 2)) + comment := d.nodeListAt(it.nextIf(mask, 3)) + return d.factory.NewJSDocTypedefTag(tagName, typeExpression, name, comment), nil + case ast.KindJSDocSignature: + it := newChildIter(childIndices) + typeParameters := d.nodeListAt(it.nextIf(mask, 0)) + parameters := d.nodeListAt(it.nextIf(mask, 1)) + typeNode := d.nodeAt(it.nextIf(mask, 2)) + return d.factory.NewJSDocSignature(typeParameters, parameters, typeNode), nil + case ast.KindJSDocNameReference: + return d.factory.NewJSDocNameReference(d.singleChild(childIndices)), nil + case ast.KindModuleDeclaration: + keyword := ast.KindModuleKeyword + if commonData&1 != 0 { + keyword = ast.KindNamespaceKeyword + } + it := newChildIter(childIndices) + modifiers := d.modifierListAt(it.nextIf(mask, 0)) + name := d.nodeAt(it.nextIf(mask, 1)) + body := d.nodeAt(it.nextIf(mask, 2)) + return d.factory.NewModuleDeclaration(modifiers, keyword, name, body), nil + case ast.KindImportEqualsDeclaration: + isTypeOnly := commonData&1 != 0 + it := newChildIter(childIndices) + modifiers := d.modifierListAt(it.nextIf(mask, 0)) + name := d.nodeAt(it.nextIf(mask, 1)) + moduleReference := d.nodeAt(it.nextIf(mask, 2)) + return d.factory.NewImportEqualsDeclaration(modifiers, isTypeOnly, name, moduleReference), nil + case ast.KindExportDeclaration: + isTypeOnly := commonData&1 != 0 + it := newChildIter(childIndices) + modifiers := d.modifierListAt(it.nextIf(mask, 0)) + exportClause := d.nodeAt(it.nextIf(mask, 1)) + moduleSpecifier := d.nodeAt(it.nextIf(mask, 2)) + attributes := d.nodeAt(it.nextIf(mask, 3)) + return d.factory.NewExportDeclaration(modifiers, isTypeOnly, exportClause, moduleSpecifier, attributes), nil + case ast.KindImportType: + isTypeOf := commonData&1 != 0 + it := newChildIter(childIndices) + argument := d.nodeAt(it.nextIf(mask, 0)) + attributes := d.nodeAt(it.nextIf(mask, 1)) + qualifier := d.nodeAt(it.nextIf(mask, 2)) + typeArguments := d.nodeListAt(it.nextIf(mask, 3)) + return d.factory.NewImportTypeNode(isTypeOf, argument, attributes, qualifier, typeArguments), nil + case ast.KindImportClause: + var phaseModifier ast.Kind + switch commonData & 3 { + case 1: + phaseModifier = ast.KindTypeKeyword + case 2: + phaseModifier = ast.KindDeferKeyword + } + it := newChildIter(childIndices) + name := d.nodeAt(it.nextIf(mask, 0)) + namedBindings := d.nodeAt(it.nextIf(mask, 1)) + return d.factory.NewImportClause(phaseModifier, name, namedBindings), nil + case ast.KindImportSpecifier: + isTypeOnly := commonData&1 != 0 + it := newChildIter(childIndices) + propertyName := d.nodeAt(it.nextIf(mask, 0)) + name := d.nodeAt(it.nextIf(mask, 1)) + return d.factory.NewImportSpecifier(isTypeOnly, propertyName, name), nil + case ast.KindTypeParameter: + it := newChildIter(childIndices) + modifiers := d.modifierListAt(it.nextIf(mask, 0)) + name := d.nodeAt(it.nextIf(mask, 1)) + constraint := d.nodeAt(it.nextIf(mask, 2)) + expression := d.nodeAt(it.nextIf(mask, 3)) + defaultType := d.nodeAt(it.nextIf(mask, 4)) + return d.factory.NewTypeParameterDeclaration(modifiers, name, constraint, expression, defaultType), nil + case ast.KindSyntheticReferenceExpression: + it := newChildIter(childIndices) + expression := d.nodeAt(it.nextIf(mask, 0)) + thisArg := d.nodeAt(it.nextIf(mask, 1)) + return d.factory.NewSyntheticReferenceExpression(expression, thisArg), nil + case ast.KindJSDocTypeLiteral: + isArrayType := commonData&1 != 0 + nodes := d.allocNodeSlice(len(childIndices)) + for i, ci := range childIndices { + nodes[i] = d.nodes[ci] + } + return d.factory.NewJSDocTypeLiteral(nodes, isArrayType), nil + case ast.KindJSDocParameterTag, ast.KindJSDocPropertyTag: + isBracketed := commonData&1 != 0 + isNameFirst := commonData&2 != 0 + it := newChildIter(childIndices) + tagName := d.nodeAt(it.nextIf(mask, 0)) + name := d.nodeAt(it.nextIf(mask, 1)) + typeExpression := d.nodeAt(it.nextIf(mask, 2)) + comment := d.nodeListAt(it.nextIf(mask, 3)) + return d.factory.NewJSDocParameterOrPropertyTag(kind, tagName, name, isBracketed, typeExpression, isNameFirst, comment), nil + default: + return nil, fmt.Errorf("unhandled node kind %v with %d children", kind, len(childIndices)) + } +} diff --git a/tools/tsgo/internal/api/encoder/decoder_test.go b/tools/tsgo/internal/api/encoder/decoder_test.go new file mode 100644 index 00000000..184d3aca --- /dev/null +++ b/tools/tsgo/internal/api/encoder/decoder_test.go @@ -0,0 +1,450 @@ +package encoder_test + +import ( + "os" + "path/filepath" + "testing" + + "github.com/microsoft/typescript-go/internal/api/encoder" + "github.com/microsoft/typescript-go/internal/ast" + "github.com/microsoft/typescript-go/internal/core" + "github.com/microsoft/typescript-go/internal/parser" + "github.com/microsoft/typescript-go/internal/repo" + "gotest.tools/v3/assert" +) + +func parseSourceFile(code string) *ast.SourceFile { + return parser.ParseSourceFile(ast.SourceFileParseOptions{ + FileName: "/test.ts", + Path: "/test.ts", + }, code, core.ScriptKindTS) +} + +func TestDecodeSourceFile_Basic(t *testing.T) { + t.Parallel() + sf := parseSourceFile("let x = 1;") + buf, _, err := encoder.EncodeSourceFile(sf) + assert.NilError(t, err) + + decoded, err := encoder.DecodeSourceFile(buf) + assert.NilError(t, err) + assert.Equal(t, decoded.AsNode().Kind, ast.KindSourceFile) + assert.Equal(t, decoded.FileName(), "/test.ts") + assert.Equal(t, decoded.Text(), "let x = 1;") + assert.Assert(t, decoded.Statements != nil) + assert.Assert(t, decoded.EndOfFileToken != nil) +} + +func TestDecodeSourceFile_Statements(t *testing.T) { + t.Parallel() + sf := parseSourceFile("let a = 1;\nlet b = 2;\nlet c = 3;") + buf, _, err := encoder.EncodeSourceFile(sf) + assert.NilError(t, err) + + decoded, err := encoder.DecodeSourceFile(buf) + assert.NilError(t, err) + assert.Equal(t, len(decoded.Statements.Nodes), 3) + for i, stmt := range decoded.Statements.Nodes { + assert.Equal(t, stmt.Kind, ast.KindVariableStatement, "statement %d", i) + } +} + +func TestDecodeSourceFile_VariableDeclaration(t *testing.T) { + t.Parallel() + sf := parseSourceFile("let x = 1;") + buf, _, err := encoder.EncodeSourceFile(sf) + assert.NilError(t, err) + + decoded, err := encoder.DecodeSourceFile(buf) + assert.NilError(t, err) + + varStmt := decoded.Statements.Nodes[0].AsVariableStatement() + assert.Assert(t, varStmt.DeclarationList != nil) + declList := varStmt.DeclarationList.AsVariableDeclarationList() + assert.Assert(t, declList.Declarations != nil) + assert.Equal(t, len(declList.Declarations.Nodes), 1) + + decl := declList.Declarations.Nodes[0].AsVariableDeclaration() + assert.Equal(t, decl.Name().Kind, ast.KindIdentifier) + assert.Equal(t, decl.Name().AsIdentifier().Text, "x") + assert.Assert(t, decl.Initializer != nil) + assert.Equal(t, decl.Initializer.Kind, ast.KindNumericLiteral) + assert.Equal(t, decl.Initializer.AsNumericLiteral().Text, "1") +} + +func TestDecodeSourceFile_VariableDeclarationListFlags(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + code string + expected ast.NodeFlags + }{ + {"const", "const x = 1;", ast.NodeFlagsConst}, + {"let", "let x = 1;", ast.NodeFlagsLet}, + {"var", "var x = 1;", ast.NodeFlagsNone}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + sf := parseSourceFile(tt.code) + buf, _, err := encoder.EncodeSourceFile(sf) + assert.NilError(t, err) + + decoded, err := encoder.DecodeSourceFile(buf) + assert.NilError(t, err) + + declList := decoded.Statements.Nodes[0].AsVariableStatement().DeclarationList.AsVariableDeclarationList() + got := declList.Flags & (ast.NodeFlagsLet | ast.NodeFlagsConst) + assert.Equal(t, got, tt.expected, "flags for %q: got %d, want %d", tt.code, got, tt.expected) + }) + } +} + +func TestDecodeSourceFile_FunctionDeclaration(t *testing.T) { + t.Parallel() + sf := parseSourceFile("function add(a: number, b: number): number { return a + b; }") + buf, _, err := encoder.EncodeSourceFile(sf) + assert.NilError(t, err) + + decoded, err := encoder.DecodeSourceFile(buf) + assert.NilError(t, err) + + funcDecl := decoded.Statements.Nodes[0].AsFunctionDeclaration() + assert.Assert(t, funcDecl.Name() != nil) + assert.Equal(t, funcDecl.Name().AsIdentifier().Text, "add") + assert.Assert(t, funcDecl.Parameters != nil) + assert.Equal(t, len(funcDecl.Parameters.Nodes), 2) + assert.Assert(t, funcDecl.Type != nil) + assert.Assert(t, funcDecl.Body != nil) + + param0 := funcDecl.Parameters.Nodes[0].AsParameterDeclaration() + assert.Equal(t, param0.Name().AsIdentifier().Text, "a") + assert.Assert(t, param0.Type != nil) +} + +func TestDecodeSourceFile_ImportDeclaration(t *testing.T) { + t.Parallel() + sf := parseSourceFile(`import { bar } from "bar";`) + buf, _, err := encoder.EncodeSourceFile(sf) + assert.NilError(t, err) + + decoded, err := encoder.DecodeSourceFile(buf) + assert.NilError(t, err) + + imp := decoded.Statements.Nodes[0].AsImportDeclaration() + assert.Assert(t, imp.ImportClause != nil) + assert.Assert(t, imp.ModuleSpecifier != nil) + assert.Equal(t, imp.ModuleSpecifier.AsStringLiteral().Text, "bar") + + clause := imp.ImportClause.AsImportClause() + assert.Assert(t, clause.NamedBindings != nil) + namedImports := clause.NamedBindings.AsNamedImports() + assert.Assert(t, namedImports.Elements != nil) + assert.Equal(t, len(namedImports.Elements.Nodes), 1) + spec := namedImports.Elements.Nodes[0].AsImportSpecifier() + assert.Equal(t, spec.Name().AsIdentifier().Text, "bar") +} + +func TestDecodeSourceFile_IfStatement(t *testing.T) { + t.Parallel() + sf := parseSourceFile("if (true) { } else { }") + buf, _, err := encoder.EncodeSourceFile(sf) + assert.NilError(t, err) + + decoded, err := encoder.DecodeSourceFile(buf) + assert.NilError(t, err) + + ifStmt := decoded.Statements.Nodes[0].AsIfStatement() + assert.Assert(t, ifStmt.Expression != nil) + assert.Assert(t, ifStmt.ThenStatement != nil) + assert.Assert(t, ifStmt.ElseStatement != nil) + assert.Equal(t, ifStmt.ThenStatement.Kind, ast.KindBlock) + assert.Equal(t, ifStmt.ElseStatement.Kind, ast.KindBlock) +} + +func TestDecodeSourceFile_TemplateExpression(t *testing.T) { + t.Parallel() + sf := parseSourceFile("let x = `hello ${name} world`;") + buf, _, err := encoder.EncodeSourceFile(sf) + assert.NilError(t, err) + + decoded, err := encoder.DecodeSourceFile(buf) + assert.NilError(t, err) + + varDecl := decoded.Statements.Nodes[0].AsVariableStatement().DeclarationList.AsVariableDeclarationList().Declarations.Nodes[0].AsVariableDeclaration() + tmplExpr := varDecl.Initializer.AsTemplateExpression() + assert.Assert(t, tmplExpr.Head != nil) + assert.Equal(t, tmplExpr.Head.AsTemplateHead().Text, "hello ") + assert.Assert(t, tmplExpr.TemplateSpans != nil) + assert.Equal(t, len(tmplExpr.TemplateSpans.Nodes), 1) + + span := tmplExpr.TemplateSpans.Nodes[0].AsTemplateSpan() + assert.Assert(t, span.Expression != nil) + assert.Equal(t, span.Expression.Kind, ast.KindIdentifier) + assert.Assert(t, span.Literal != nil) + assert.Equal(t, span.Literal.AsTemplateTail().Text, " world") +} + +func TestDecodeSourceFile_ExportModifier(t *testing.T) { + t.Parallel() + sf := parseSourceFile("export function foo() {}") + buf, _, err := encoder.EncodeSourceFile(sf) + assert.NilError(t, err) + + decoded, err := encoder.DecodeSourceFile(buf) + assert.NilError(t, err) + + funcDecl := decoded.Statements.Nodes[0].AsFunctionDeclaration() + assert.Assert(t, funcDecl.Modifiers() != nil) + assert.Equal(t, len(funcDecl.Modifiers().Nodes), 1) + assert.Equal(t, funcDecl.Modifiers().Nodes[0].Kind, ast.KindExportKeyword) +} + +func TestDecodeSourceFile_Positions(t *testing.T) { + t.Parallel() + code := "let x = 1;" + sf := parseSourceFile(code) + buf, _, err := encoder.EncodeSourceFile(sf) + assert.NilError(t, err) + + decoded, err := encoder.DecodeSourceFile(buf) + assert.NilError(t, err) + + assert.Equal(t, decoded.AsNode().Pos(), 0) + assert.Equal(t, decoded.AsNode().End(), len(code)) +} + +func TestDecodeSourceFile_ClassDeclaration(t *testing.T) { + t.Parallel() + sf := parseSourceFile("class Foo { bar(): void {} }") + buf, _, err := encoder.EncodeSourceFile(sf) + assert.NilError(t, err) + + decoded, err := encoder.DecodeSourceFile(buf) + assert.NilError(t, err) + + classDecl := decoded.Statements.Nodes[0].AsClassDeclaration() + assert.Assert(t, classDecl.Name() != nil) + assert.Equal(t, classDecl.Name().AsIdentifier().Text, "Foo") + assert.Assert(t, classDecl.Members != nil) + assert.Equal(t, len(classDecl.Members.Nodes), 1) + assert.Equal(t, classDecl.Members.Nodes[0].Kind, ast.KindMethodDeclaration) +} + +func TestDecodeNodes_SubtreeRoundTrip(t *testing.T) { + t.Parallel() + sf := parseSourceFile("function greet(name: string) { return `Hello, ${name}!`; }") + + var funcNode *ast.Node + visitor := &ast.NodeVisitor{} + visitor.Visit = func(node *ast.Node) *ast.Node { + if node.Kind == ast.KindFunctionDeclaration && funcNode == nil { + funcNode = node + } + return node + } + visitor.VisitEachChild(sf.AsNode()) + assert.Assert(t, funcNode != nil) + + buf, _, err := encoder.EncodeNode(funcNode, sf) + assert.NilError(t, err) + + decoded, err := encoder.DecodeNodes(buf) + assert.NilError(t, err) + + assert.Equal(t, decoded.Kind, ast.KindFunctionDeclaration) + funcDecl := decoded.AsFunctionDeclaration() + assert.Assert(t, funcDecl.Name() != nil) + assert.Equal(t, funcDecl.Name().AsIdentifier().Text, "greet") + assert.Assert(t, funcDecl.Parameters != nil) + assert.Equal(t, len(funcDecl.Parameters.Nodes), 1) + assert.Assert(t, funcDecl.Body != nil) +} + +func TestDecodeSourceFile_BinaryExpression(t *testing.T) { + t.Parallel() + sf := parseSourceFile("let x = 1 + 2;") + buf, _, err := encoder.EncodeSourceFile(sf) + assert.NilError(t, err) + + decoded, err := encoder.DecodeSourceFile(buf) + assert.NilError(t, err) + + decl := decoded.Statements.Nodes[0].AsVariableStatement().DeclarationList.AsVariableDeclarationList().Declarations.Nodes[0].AsVariableDeclaration() + binExpr := decl.Initializer.AsBinaryExpression() + assert.Assert(t, binExpr.Left != nil) + assert.Assert(t, binExpr.Right != nil) + assert.Assert(t, binExpr.OperatorToken != nil) + assert.Equal(t, binExpr.Left.Kind, ast.KindNumericLiteral) + assert.Equal(t, binExpr.Right.Kind, ast.KindNumericLiteral) +} + +func TestDecodeSourceFile_KeywordExpressions(t *testing.T) { + t.Parallel() + // "this" must decode as KeywordExpression, not Token, or the printer panics + sf := parseSourceFile("const x = this;") + buf, _, err := encoder.EncodeSourceFile(sf) + assert.NilError(t, err) + + decoded, err := encoder.DecodeSourceFile(buf) + assert.NilError(t, err) + + // Navigate: const x = this -> VariableStatement -> declaration -> initializer + decl := decoded.Statements.Nodes[0].AsVariableStatement().DeclarationList.AsVariableDeclarationList().Declarations.Nodes[0].AsVariableDeclaration() + thisExpr := decl.Initializer + assert.Equal(t, thisExpr.Kind, ast.KindThisKeyword) + // This would panic if decoded as Token instead of KeywordExpression + assert.Assert(t, thisExpr.AsKeywordExpression() != nil) +} + +func TestDecodeSourceFile_EmptyModuleBlock(t *testing.T) { + t.Parallel() + sf := parseSourceFile("namespace N { }") + buf, _, err := encoder.EncodeSourceFile(sf) + assert.NilError(t, err) + + decoded, err := encoder.DecodeSourceFile(buf) + assert.NilError(t, err) + + // Navigate: namespace N { } -> ModuleDeclaration -> ModuleBlock + mod := decoded.Statements.Nodes[0].AsModuleDeclaration() + assert.Assert(t, mod.Body != nil) + block := mod.Body.AsModuleBlock() + // Statements must be non-nil even when empty, otherwise the printer panics + assert.Assert(t, block.Statements != nil) + assert.Equal(t, len(block.Statements.Nodes), 0) +} + +func TestDecodeSourceFile_EmptyBlockAndParams(t *testing.T) { + t.Parallel() + // Empty blocks and parameter lists must decode with non-nil NodeLists (not nil), + // matching parser behavior. Previously the decoder left them nil, crashing the printer. + sf := parseSourceFile("function foo() {}") + buf, _, err := encoder.EncodeSourceFile(sf) + assert.NilError(t, err) + + decoded, err := encoder.DecodeSourceFile(buf) + assert.NilError(t, err) + + funcDecl := decoded.Statements.Nodes[0].AsFunctionDeclaration() + assert.Assert(t, funcDecl.Parameters != nil, "FunctionDeclaration.Parameters must be non-nil for foo()") + assert.Equal(t, len(funcDecl.Parameters.Nodes), 0) + assert.Assert(t, funcDecl.Body != nil) + block := funcDecl.Body.AsBlock() + assert.Assert(t, block.Statements != nil, "Block.Statements must be non-nil for empty blocks") + assert.Equal(t, len(block.Statements.Nodes), 0) +} + +func TestDecodeSourceFile_ArrowFunctionEmptyParams(t *testing.T) { + t.Parallel() + // `() => {}` must decode with non-nil Parameters (empty NodeList), + // matching parser behavior. Previously the decoder left it nil, crashing the printer. + sf := parseSourceFile("const f = () => {};") + buf, _, err := encoder.EncodeSourceFile(sf) + assert.NilError(t, err) + + decoded, err := encoder.DecodeSourceFile(buf) + assert.NilError(t, err) + + decl := decoded.Statements.Nodes[0].AsVariableStatement().DeclarationList.AsVariableDeclarationList().Declarations.Nodes[0].AsVariableDeclaration() + arrow := decl.Initializer.AsArrowFunction() + assert.Assert(t, arrow.Parameters != nil, "ArrowFunction.Parameters must be non-nil for () => {}") + assert.Equal(t, len(arrow.Parameters.Nodes), 0) + assert.Assert(t, arrow.Body != nil) + block := arrow.Body.AsBlock() + assert.Assert(t, block.Statements != nil, "Block.Statements must be non-nil for empty body") + assert.Equal(t, len(block.Statements.Nodes), 0) +} + +func TestDecodeSourceFile_FunctionExpressionEmptyParams(t *testing.T) { + t.Parallel() + // `function() {}` must decode with non-nil Parameters (empty NodeList). + sf := parseSourceFile("const f = function() {};") + buf, _, err := encoder.EncodeSourceFile(sf) + assert.NilError(t, err) + + decoded, err := encoder.DecodeSourceFile(buf) + assert.NilError(t, err) + + decl := decoded.Statements.Nodes[0].AsVariableStatement().DeclarationList.AsVariableDeclarationList().Declarations.Nodes[0].AsVariableDeclaration() + funcExpr := decl.Initializer.AsFunctionExpression() + assert.Assert(t, funcExpr.Parameters != nil, "FunctionExpression.Parameters must be non-nil for function() {}") + assert.Equal(t, len(funcExpr.Parameters.Nodes), 0) +} + +func TestDecodeSourceFile_PostfixUnaryOperator(t *testing.T) { + t.Parallel() + sf := parseSourceFile("let i = 0; i++;") + buf, _, err := encoder.EncodeSourceFile(sf) + assert.NilError(t, err) + + decoded, err := encoder.DecodeSourceFile(buf) + assert.NilError(t, err) + + exprStmt := decoded.Statements.Nodes[1].AsExpressionStatement() + postfix := exprStmt.Expression.AsPostfixUnaryExpression() + assert.Equal(t, postfix.Operator, ast.KindPlusPlusToken) + assert.Equal(t, postfix.Operand.Kind, ast.KindIdentifier) +} + +func TestDecodeSourceFile_PrefixUnaryOperator(t *testing.T) { + t.Parallel() + sf := parseSourceFile("let x = true; !x;") + buf, _, err := encoder.EncodeSourceFile(sf) + assert.NilError(t, err) + + decoded, err := encoder.DecodeSourceFile(buf) + assert.NilError(t, err) + + exprStmt := decoded.Statements.Nodes[1].AsExpressionStatement() + prefix := exprStmt.Expression.AsPrefixUnaryExpression() + assert.Equal(t, prefix.Operator, ast.KindExclamationToken) + assert.Equal(t, prefix.Operand.Kind, ast.KindIdentifier) +} + +func TestDecodeSourceFile_PostfixDecrement(t *testing.T) { + t.Parallel() + sf := parseSourceFile("let n = 5; n--;") + buf, _, err := encoder.EncodeSourceFile(sf) + assert.NilError(t, err) + + decoded, err := encoder.DecodeSourceFile(buf) + assert.NilError(t, err) + + exprStmt := decoded.Statements.Nodes[1].AsExpressionStatement() + postfix := exprStmt.Expression.AsPostfixUnaryExpression() + assert.Equal(t, postfix.Operator, ast.KindMinusMinusToken) +} + +func BenchmarkDecodeSourceFile(b *testing.B) { + repo.SkipIfNoTypeScriptSubmodule(b) + filePath := filepath.Join(repo.TypeScriptSubmodulePath(), "src/compiler/checker.ts") + fileContent, err := os.ReadFile(filePath) + assert.NilError(b, err) + code := string(fileContent) + sourceFile := parser.ParseSourceFile(ast.SourceFileParseOptions{ + FileName: "/checker.ts", + Path: "/checker.ts", + }, code, core.ScriptKindTS) + + buf, _, err := encoder.EncodeSourceFile(sourceFile) + assert.NilError(b, err) + + b.Run("parse", func(b *testing.B) { + for b.Loop() { + parser.ParseSourceFile(ast.SourceFileParseOptions{ + FileName: "/checker.ts", + Path: "/checker.ts", + }, code, core.ScriptKindTS) + } + }) + + b.Run("decode", func(b *testing.B) { + for b.Loop() { + _, decodeErr := encoder.DecodeSourceFile(buf) + assert.NilError(b, decodeErr) + } + }) +} diff --git a/tools/tsgo/internal/api/encoder/encoder.go b/tools/tsgo/internal/api/encoder/encoder.go new file mode 100644 index 00000000..dd93a2ea --- /dev/null +++ b/tools/tsgo/internal/api/encoder/encoder.go @@ -0,0 +1,844 @@ +package encoder + +import ( + "cmp" + "encoding/binary" + "fmt" + "slices" + "sync" + + "github.com/microsoft/typescript-go/internal/ast" + "github.com/microsoft/typescript-go/internal/core" + "github.com/zeebo/xxh3" +) + +func init() { + if ast.KindLastUnaryOperator > 0x3f { + panic(fmt.Sprintf("KindLastUnaryOperator (%d) exceeds the 6-bit commonData capacity (max 63)", ast.KindLastUnaryOperator)) + } +} + +const ( + NodeOffsetKind = iota * 4 + NodeOffsetPos + NodeOffsetEnd + NodeOffsetNext + NodeOffsetParent + NodeOffsetData + NodeOffsetFlags + // NodeSize is the number of bytes that represents a single node in the encoded format. + NodeSize +) + +const ( + NodeDataTypeChildren uint32 = iota << 30 + NodeDataTypeString + NodeDataTypeExtendedData +) + +const ( + NodeDataTypeMask uint32 = 0xc0_00_00_00 + NodeDataChildMask uint32 = 0x00_00_00_ff + NodeDataStringIndexMask uint32 = 0x00_ff_ff_ff +) + +const ( + SyntaxKindNodeList uint32 = 1<<32 - 1 +) + +const ( + HeaderOffsetMetadata = iota * 4 + HeaderOffsetHashLo0 + HeaderOffsetHashLo1 + HeaderOffsetHashHi0 + HeaderOffsetHashHi1 + HeaderOffsetParseOptions + HeaderOffsetStringOffsets + HeaderOffsetStringData + HeaderOffsetExtendedData + HeaderOffsetStructuredData + HeaderOffsetNodes + HeaderSize +) + +const ( + ProtocolVersion uint8 = 5 +) + +// Source File Binary Format +// ========================= +// +// The following defines a protocol for serializing TypeScript SourceFile objects to a compact binary format. All integer +// values are little-endian. +// +// Overview +// -------- +// +// The format comprises seven sections: +// +// | Section | Length | Description | +// | ------------------ | ------------------ | ----------------------------------------------------------------------------------------------- | +// | Header | 44 bytes | Contains the content hash, parse options, flags, and byte offsets to the start of each section. | +// | String offsets | 8 bytes per string | Pairs of starting byte offsets and ending byte offsets into the **string data** section. | +// | String data | variable | UTF-8 encoded string data. | +// | Extended node data | variable | Extra data for some kinds of nodes. | +// | Structured data | variable | Msgpack-encoded metadata blobs (e.g. file references). | +// | Nodes | 28 bytes per node | Defines the AST structure of the file, with references to strings and extended data. | +// +// Header (44 bytes) +// ----------------- +// +// The header contains the following fields: +// +// | Byte offset | Type | Field | +// | ----------- | --------- | ------------------------------------------------- | +// | 0 | uint8 | Protocol version | +// | 1-3 | | Reserved | +// | 4-19 | uint128 | Source file content hash (xxh3, LE) | +// | 20-23 | uint32 | Parse options (bitmask; bit 0: JSX, bit 1: Force) | +// | 24-27 | uint32 | Byte offset to string offsets section | +// | 28-31 | uint32 | Byte offset to string data section | +// | 32-35 | uint32 | Byte offset to extended node data section | +// | 36-39 | uint32 | Byte offset to structured data section | +// | 40-43 | uint32 | Byte offset to nodes section | +// +// String offsets (8 bytes per string) +// ----------------------------------- +// +// Each string offset entry consists of two 4-byte unsigned integers, representing the start and end byte offsets into the +// **string data** section. +// +// String data (variable) +// ---------------------- +// +// The string data section contains UTF-8 encoded string data, with WTF-8 used for JS strings containing lone UTF-16 +// surrogates. In typical cases, the entirety of the string data is the source file text, and individual nodes with +// string properties reference their positional slice of the file text. In cases where a node's string property is not +// equal to the slice of file text at its position, the unique string is appended to the string data section after the +// file text. +// +// Extended node data (variable) +// ----------------------------- +// +// The extended node data section contains additional data for specific node types. The length and meaning of each entry +// is defined by the node type. +// +// Currently, the only node types that use this section are `TemplateHead`, `TemplateMiddle`, `TemplateTail`, and +// `SourceFile`. The extended data format for the first three is: +// +// | Byte offset | Type | Field | +// | ----------- | ------ | ------------------------------------------------ | +// | 0-4 | uint32 | Index of `text` in the string offsets section | +// | 4-8 | uint32 | Index of `rawText` in the string offsets section | +// | 8-12 | uint32 | Value of `templateFlags` | +// +// and for `SourceFile` is: +// +// | Byte offset | Type | Field | +// | ----------- | ------ | -------------------------------------------------------------- | +// | 0-4 | uint32 | Index of `text` in the string offsets section | +// | 4-8 | uint32 | Index of `fileName` in the string offsets section | +// | 8-12 | uint32 | Index of `path` in the string offsets section | +// | 12-16 | uint32 | Value of `languageVariant` | +// | 16-20 | uint32 | Value of `scriptKind` | +// | 20-24 | uint32 | Byte offset of `referencedFiles` in structured data section | +// | 24-28 | uint32 | Byte offset of `typeReferenceDirectives` in structured data | +// | 28-32 | uint32 | Byte offset of `libReferenceDirectives` in structured data | +// | 32-36 | uint32 | Byte offset of `imports` node index array in structured data | +// | 36-40 | uint32 | Byte offset of `moduleAugmentations` node index array | +// | 40-44 | uint32 | Byte offset of `ambientModuleNames` string array | +// | 44-48 | uint32 | Node index of `externalModuleIndicator` (0 = nil) | +// +// Structured data (variable) +// -------------------------- +// +// The structured data section contains msgpack-encoded metadata blobs. Each blob is a self-contained +// msgpack value. File reference arrays use the following tuple format: +// +// [pos: uint, end: uint, fileName: string, resolutionMode: uint, preserve: bool] +// +// Node index arrays (imports, moduleAugmentations) are msgpack arrays of uint values, where each +// value is a node index into the nodes section. String arrays (ambientModuleNames) are msgpack +// arrays of string values. +// +// An offset of 0xFFFFFFFF indicates no data (empty array). +// +// Nodes (28 bytes per node) +// ------------------------- +// +// The nodes section contains the AST structure of the file. Nodes are represented in a flat array in source order, +// heavily inspired by https://marvinh.dev/blog/speeding-up-javascript-ecosystem-part-11/. Each node has the following +// structure: +// +// | Byte offset | Type | Field | +// | ----------- | ------ | -------------------------- | +// | 0-4 | uint32 | Kind | +// | 4-8 | uint32 | Pos | +// | 8-12 | uint32 | End | +// | 12-16 | uint32 | Node index of next sibling | +// | 16-20 | uint32 | Node index of parent | +// | 20-24 | | Node data | +// | 24-28 | uint32 | Node flags | +// +// The first 28 bytes of the nodes section are zeros representing a nil node, such that nodes without a parent or next +// sibling can unambiuously use `0` for those indices. +// +// NodeLists are represented as normal nodes with the special `kind` value `0xff_ff_ff_ff`. They are considered the parent +// of their contents in the encoded format. A client reconstructing an AST similar to TypeScript's internal representation +// should instead set the `parent` pointers of a NodeList's children to the NodeList's parent. A NodeList's `data` field +// is the uint32 length of the list, and does not use one of the data types described below. +// +// For node types other than NodeList, the node data field encodes one of the following, determined by the first 2 bits of +// the field: +// +// | Value | Data type | Description | +// | ----- | --------- | ------------------------------------------------------------------------------------ | +// | 0b00 | Children | Disambiguates which named properties of the node its children should be assigned to. | +// | 0b01 | String | The index of the node's string property in the **string offsets** section. | +// | 0b10 | Extended | The byte offset of the node's extended data into the **extended node data** section. | +// | 0b11 | Reserved | Reserved for future use. | +// +// In all node data types, the remaining 6 bits of the first byte are used to encode small values specific to the node +// type. For most node types, these are individual boolean flags. For unary expressions, all 6 bits encode the operator's +// SyntaxKind value (e.g., PlusPlusToken=45, TildeToken=54), which fits because KindLastUnaryOperator (54) <= 0x3f (63). +// +// | Node type | Bits 0-5 | Notes | +// | ---------------------------- | ------------------------------------- | ------------------------------ | +// | `ImportSpecifier` | Bit 0: `isTypeOnly` | | +// | `ImportClause` | Bit 0: `isTypeOnly`, Bit 1: `isDefer` | | +// | `ExportSpecifier` | Bit 0: `isTypeOnly` | | +// | `ImportEqualsDeclaration` | Bit 0: `isTypeOnly` | | +// | `ExportDeclaration` | Bit 0: `isTypeOnly` | | +// | `ImportTypeNode` | Bit 0: `isTypeOf` | | +// | `ExportAssignment` | Bit 0: `isExportEquals` | | +// | `Block` | Bit 0: `multiline` | | +// | `ArrayLiteralExpression` | Bit 0: `multiline` | | +// | `ObjectLiteralExpression` | Bit 0: `multiline` | | +// | `JsxText` | Bit 0: `containsOnlyTriviaWhiteSpaces`| | +// | `JSDocTypeLiteral` | Bit 0: `isArrayType` | | +// | `JsDocPropertyTag` | Bit 0: `isBracketed`, Bit 1: `isNameFirst` | | +// | `JsDocParameterTag` | Bit 0: `isBracketed`, Bit 1: `isNameFirst` | | +// | `VariableDeclarationList` | Bit 0: is `let`, Bit 1: is `const` | | +// | `ImportAttributes` | Bit 0: `multiline`, Bit 1: is `assert`| | +// | `PrefixUnaryExpression` | Bits 0-5: operator SyntaxKind | e.g., `!`, `~`, `++`, `--` | +// | `PostfixUnaryExpression` | Bits 0-5: operator SyntaxKind | e.g., `++`, `--` | +// +// The remaining 3 bytes of the node data field vary by data type: +// +// ### Children (0b00) +// +// If a node has fewer children than its type allows, additional data is needed to determine which properties the children +// correspond to. The last byte of the 4-byte data field is a bitmask representing the child properties of the node type, +// in visitor order, where `1` indicates that the child at that property is present and `0` indicates that the property is +// nil. For example, a `MethodDeclaration` has the following child properties: +// +// | Property name | Bit position | +// | -------------- | ------------ | +// | modifiers | 0 | +// | asteriskToken | 1 | +// | name | 2 | +// | postfixToken | 3 | +// | typeParameters | 4 | +// | parameters | 5 | +// | returnType | 6 | +// | body | 7 | +// +// A bitmask with value `0b01100101` would indicate that the next four direct descendants (i.e., node records that have a +// `parent` set to the node index of the `MethodDeclaration`) of the node are its `modifiers`, `name`, `parameters`, and +// `body` properties, in that order. The remaining properties are nil. (To reconstruct the node with named properties, the +// client must consult a static table of each node type's child property names.) +// +// The bitmask may be zero for node types that can only have a single child, since no disambiguation is needed. +// Additionally, the children data type may be used for nodes that can never have children, but do not require other +// data types. +// +// ### String (0b01) +// +// The string data type is used for nodes with a single string property. (Currently, the name of that property is always +// `text`.) The last three bytes of the 4-byte data field form a single 24-bit unsigned integer (i.e., +// `uint32(0x00_ff_ff_ff & node.data)`) _N_ that is an index into the **string offsets** section. The *N*th 32-bit +// unsigned integer in the **string offsets** section is the byte offset of the start of the string in the **string data** +// section, and the *N+1*th 32-bit unsigned integer is the byte offset of the end of the string in the +// **string data** section. +// +// ### Extended (0b10) +// +// The extended data type is used for nodes with properties that don't fit into either the children or string data types. +// The last three bytes of the 4-byte data field form a single 24-bit unsigned integer (i.e., +// `uint32(0x00_ff_ff_ff & node.data)`) _N_ that is a byte offset into the **extended node data** section. The length and +// meaning of the data at that offset is defined by the node type. See the **Extended node data** section for details on +// the format of the extended data for specific node types. +// +// Encoding Arbitrary Nodes +// ------------------------ +// +// The same binary format can be used to encode an arbitrary subtree of a SourceFile, not just a whole SourceFile. When +// encoding a non-SourceFile node, the format is identical with the following differences: +// +// - The content hash fields in the header (bytes 4-19) are zero. +// - The parse options field in the header (bytes 20-23) is zero. +// - The root node in the nodes section uses its actual node kind and data encoding (via getNodeData) rather than the +// SourceFile-specific extended data format. +// +// The string data section contains only the strings referenced by nodes in the subtree, rather than the full source +// file text. The EncodeNode function provides this entrypoint. + +// SourceFileHash returns the 128-bit content hash for a source file as a hex string. +func SourceFileHash(sourceFile *ast.SourceFile) string { + h := sourceFile.Hash + return fmt.Sprintf("%016x%016x", h.Hi, h.Lo) +} + +// encodeParseOptions encodes the per-file ExternalModuleIndicatorOptions as a uint32 bitmask. +func encodeParseOptions(opts ast.ExternalModuleIndicatorOptions) uint32 { + var bits uint32 + if opts.JSX { + bits |= 1 + } + if opts.Force { + bits |= 2 + } + return bits +} + +// NodeIndexTable maps between AST nodes and their encoder indices for O(1) node handle resolution. +type NodeIndexTable struct { + Nodes []*ast.Node // index → node (for resolution) + sortedOnce sync.Once + sortedIdx []uint32 // indices into Nodes, sorted by node ID; built lazily +} + +var nodeIndexTableKey = ast.NewSourceFileDataKey[*NodeIndexTable]() + +// GetIndex returns the encoder index for the given node. +// On the first call the sortedIdx array is built (O(n log n) sort on a flat []uint32), +// then subsequent calls use binary search (O(log n)). This turns out to be much faster than +// building a map[*ast.Node]uint32 and not significantly slower for lookups. +func (t *NodeIndexTable) GetIndex(node *ast.Node) uint32 { + t.sortedOnce.Do(func() { + idx := make([]uint32, 0, len(t.Nodes)) + for i, n := range t.Nodes { + if n != nil { + idx = append(idx, uint32(i)) + } + } + nodes := t.Nodes + slices.SortFunc(idx, func(a, b uint32) int { + return cmp.Compare(ast.GetNodeId(nodes[a]), ast.GetNodeId(nodes[b])) + }) + t.sortedIdx = idx + }) + target := ast.GetNodeId(node) + i, found := core.BinarySearchUniqueFunc(t.sortedIdx, func(_ int, el uint32) int { + return cmp.Compare(ast.GetNodeId(t.Nodes[el]), target) + }) + if found { + return t.sortedIdx[i] + } + return 0 +} + +// BuildNodeIndexTable walks the AST in the same order as encodeTree and builds +// a NodeIndexTable without performing the full binary encoding. This is used to +// eagerly create index tables for files that need node handles before getSourceFile +// is called. The indices produced are guaranteed to match those from EncodeSourceFile. +func BuildNodeIndexTable(sourceFile *ast.SourceFile) *NodeIndexTable { + var nodeCount uint32 + nodeTable := make([]*ast.Node, 1, sourceFile.NodeCount+1) // index 0 = nil sentinel + + visitor := &ast.NodeVisitor{ + Hooks: ast.NodeVisitorHooks{ + VisitNodes: func(nodeList *ast.NodeList, visitor *ast.NodeVisitor) *ast.NodeList { + if nodeList == nil { + return nodeList + } + nodeCount++ + nodeTable = append(nodeTable, nil) // NodeLists are not *ast.Node + visitor.VisitSlice(nodeList.Nodes) + return nodeList + }, + VisitModifiers: func(modifiers *ast.ModifierList, visitor *ast.NodeVisitor) *ast.ModifierList { + if modifiers != nil && len(modifiers.Nodes) > 0 { + visitor.Hooks.VisitNodes(&modifiers.NodeList, visitor) + } + return modifiers + }, + }, + } + visitor.Visit = func(node *ast.Node) *ast.Node { + nodeCount++ + nodeTable = append(nodeTable, node) + visitor.VisitEachChild(node) + for _, jsdoc := range node.JSDoc(sourceFile) { + visitor.Visit(jsdoc) + } + return node + } + + rootNode := sourceFile.AsNode() + // Index 1 = root node (matches encodeTree) + nodeCount++ + nodeTable = append(nodeTable, rootNode) + + visitor.VisitEachChild(rootNode) + for _, jsdoc := range rootNode.JSDoc(sourceFile) { + visitor.Visit(jsdoc) + } + + return &NodeIndexTable{Nodes: nodeTable} +} + +func GetNodeIndexTable(sourceFile *ast.SourceFile) *NodeIndexTable { + return ast.GetOrComputeSourceFileData(sourceFile, nodeIndexTableKey, BuildNodeIndexTable) +} + +// EncodeSourceFile encodes an entire source file AST into the binary format. +// Returns the encoded bytes and a NodeIndexTable mapping encoder indices to AST nodes. +func EncodeSourceFile(sourceFile *ast.SourceFile) ([]byte, *NodeIndexTable, error) { + data, nodeTable, err := encodeTree(sourceFile.AsNode(), sourceFile) + if err != nil { + return nil, nil, err + } + nodeTable = ast.GetOrComputeSourceFileData(sourceFile, nodeIndexTableKey, func(*ast.SourceFile) *NodeIndexTable { + return nodeTable + }) + return data, nodeTable, nil +} + +// EncodeNode encodes an arbitrary AST node and its descendants into the binary format. +// The sourceFile is needed to provide the source text for efficient string encoding. +// When encoding a non-SourceFile node, the header hash and parse options fields will be zero. +// Returns the encoded bytes and a NodeIndexTable mapping encoder indices to AST nodes. +func EncodeNode(node *ast.Node, sourceFile *ast.SourceFile) ([]byte, *NodeIndexTable, error) { + return encodeTree(node, sourceFile) +} + +func encodeTree(rootNode *ast.Node, sourceFile *ast.SourceFile) ([]byte, *NodeIndexTable, error) { + var parentIndex, nodeCount, prevIndex uint32 + var extendedData []byte + var structuredData []byte + var strs *stringTable + var positionMap *ast.PositionMap + if rootNode.Kind == ast.KindSourceFile { + strs = newStringTable(sourceFile.Text(), sourceFile.TextCount) + positionMap = sourceFile.GetPositionMap() + } else { + strs = newStringTable("", 0) + if sourceFile != nil { + positionMap = sourceFile.GetPositionMap() + } + } + if positionMap == nil { + positionMap = ast.ComputePositionMap("") + } + utf16 := func(pos int) uint32 { + return uint32(positionMap.UTF8ToUTF16(pos)) + } + var initialNodeCount int + if sourceFile != nil { + initialNodeCount = sourceFile.NodeCount + } + nodes := make([]byte, 0, (initialNodeCount+1)*NodeSize) + + // Build node index table for O(1) handle resolution. + // Index 0 is a nil sentinel; real nodes start at index 1. + nodeTable := make([]*ast.Node, 1, initialNodeCount+1) // index 0 = nil sentinel + + // Build a small map of nodes we need to track indices for (imports + moduleAugmentations). + // Values start at 0 and are filled in during the walk. + var nodeIndexMap map[*ast.Node]uint32 + var sfExtendedDataOffset int // byte offset in extendedData where SourceFile fields start + if rootNode.Kind == ast.KindSourceFile { + sf := rootNode.AsSourceFile() + total := len(sf.Imports()) + len(sf.ModuleAugmentations) + if sf.ExternalModuleIndicator != nil && sf.ExternalModuleIndicator != rootNode { + total++ + } + if total > 0 { + nodeIndexMap = make(map[*ast.Node]uint32, total) + for _, imp := range sf.Imports() { + nodeIndexMap[imp.AsNode()] = 0 + } + for _, aug := range sf.ModuleAugmentations { + nodeIndexMap[aug.AsNode()] = 0 + } + if sf.ExternalModuleIndicator != nil && sf.ExternalModuleIndicator != rootNode { + nodeIndexMap[sf.ExternalModuleIndicator] = 0 + } + } + } + + visitor := &ast.NodeVisitor{ + Hooks: ast.NodeVisitorHooks{ + VisitNodes: func(nodeList *ast.NodeList, visitor *ast.NodeVisitor) *ast.NodeList { + if nodeList == nil { + return nodeList + } + + nodeCount++ + nodeTable = append(nodeTable, nil) // NodeLists are not *ast.Node + if prevIndex != 0 { + // this is the next sibling of `prevNode` + b0, b1, b2, b3 := uint8(nodeCount), uint8(nodeCount>>8), uint8(nodeCount>>16), uint8(nodeCount>>24) + nodes[prevIndex*NodeSize+NodeOffsetNext+0] = b0 + nodes[prevIndex*NodeSize+NodeOffsetNext+1] = b1 + nodes[prevIndex*NodeSize+NodeOffsetNext+2] = b2 + nodes[prevIndex*NodeSize+NodeOffsetNext+3] = b3 + } + + nodes = appendUint32s(nodes, SyntaxKindNodeList, utf16(nodeList.Pos()), utf16(nodeList.End()), 0, parentIndex, uint32(len(nodeList.Nodes)), 0) + + saveParentIndex := parentIndex + + currentIndex := nodeCount + prevIndex = 0 + parentIndex = currentIndex + visitor.VisitSlice(nodeList.Nodes) + prevIndex = currentIndex + parentIndex = saveParentIndex + + return nodeList + }, + VisitModifiers: func(modifiers *ast.ModifierList, visitor *ast.NodeVisitor) *ast.ModifierList { + if modifiers != nil && len(modifiers.Nodes) > 0 { + visitor.Hooks.VisitNodes(&modifiers.NodeList, visitor) + } + return modifiers + }, + }, + } + visitor.Visit = func(node *ast.Node) *ast.Node { + nodeCount++ + nodeTable = append(nodeTable, node) + if prevIndex != 0 { + // this is the next sibling of `prevNode` + b0, b1, b2, b3 := uint8(nodeCount), uint8(nodeCount>>8), uint8(nodeCount>>16), uint8(nodeCount>>24) + nodes[prevIndex*NodeSize+NodeOffsetNext+0] = b0 + nodes[prevIndex*NodeSize+NodeOffsetNext+1] = b1 + nodes[prevIndex*NodeSize+NodeOffsetNext+2] = b2 + nodes[prevIndex*NodeSize+NodeOffsetNext+3] = b3 + } + + nodes = appendUint32s(nodes, uint32(node.Kind), utf16(node.Pos()), utf16(node.End()), 0, parentIndex, getNodeData(node, strs, positionMap, &extendedData, &structuredData), uint32(node.Flags)) + + if nodeIndexMap != nil { + if _, ok := nodeIndexMap[node]; ok { + nodeIndexMap[node] = nodeCount + } + } + + saveParentIndex := parentIndex + + currentIndex := nodeCount + prevIndex = 0 + parentIndex = currentIndex + visitor.VisitEachChild(node) + if sourceFile != nil { + for _, jsdoc := range node.JSDoc(sourceFile) { + visitor.Visit(jsdoc) + } + } + prevIndex = currentIndex + parentIndex = saveParentIndex + return node + } + + nodes = appendUint32s(nodes, 0, 0, 0, 0, 0, 0, 0) + + nodeCount++ + parentIndex++ + nodeTable = append(nodeTable, rootNode) // index 1 = root node + + sfExtendedDataOffset = len(extendedData) + nodes = appendUint32s(nodes, uint32(rootNode.Kind), utf16(rootNode.Pos()), utf16(rootNode.End()), 0, 0, getNodeData(rootNode, strs, positionMap, &extendedData, &structuredData), uint32(rootNode.Flags)) + + visitor.VisitEachChild(rootNode) + if sourceFile != nil { + for _, jsdoc := range rootNode.JSDoc(sourceFile) { + visitor.Visit(jsdoc) + } + } + + var hash xxh3.Uint128 + var parseOpts uint32 + if rootNode.Kind == ast.KindSourceFile { + hash = sourceFile.Hash + parseOpts = encodeParseOptions(sourceFile.ParseOptions().ExternalModuleIndicatorOptions) + + // Encode imports, moduleAugmentations, and ambientModuleNames into structured data, + // and patch the placeholder offsets in the SourceFile extended data. + sf := rootNode.AsSourceFile() + importsOffset := encodeNodeIndexArray(sf.Imports(), nodeIndexMap, &structuredData) + moduleAugmentationsOffset := encodeModuleAugmentations(sf.ModuleAugmentations, nodeIndexMap, &structuredData) + ambientModuleNamesOffset := encodeStringArray(sf.AmbientModuleNames, &structuredData) + // Patch the 3 placeholder uint32s at sfExtendedDataOffset + 32, 36, 40 + binary.LittleEndian.PutUint32(extendedData[sfExtendedDataOffset+32:], importsOffset) + binary.LittleEndian.PutUint32(extendedData[sfExtendedDataOffset+36:], moduleAugmentationsOffset) + binary.LittleEndian.PutUint32(extendedData[sfExtendedDataOffset+40:], ambientModuleNamesOffset) + // Patch externalModuleIndicator node index at offset 44 + var externalModuleIndicatorIndex uint32 + if sf.ExternalModuleIndicator != nil { + if sf.ExternalModuleIndicator == rootNode { + externalModuleIndicatorIndex = 1 // root node index + } else { + externalModuleIndicatorIndex = nodeIndexMap[sf.ExternalModuleIndicator] + } + } + binary.LittleEndian.PutUint32(extendedData[sfExtendedDataOffset+44:], externalModuleIndicatorIndex) + } + + metadata := uint32(ProtocolVersion) << 24 + offsetStringTableOffsets := HeaderSize + offsetStringTableData := HeaderSize + len(strs.offsets)*4 + offsetExtendedData := offsetStringTableData + strs.stringLength() + offsetStructuredData := offsetExtendedData + len(extendedData) + offsetNodes := offsetStructuredData + len(structuredData) + + header := []uint32{ + metadata, + uint32(hash.Lo), uint32(hash.Lo >> 32), + uint32(hash.Hi), uint32(hash.Hi >> 32), + parseOpts, + uint32(offsetStringTableOffsets), + uint32(offsetStringTableData), + uint32(offsetExtendedData), + uint32(offsetStructuredData), + uint32(offsetNodes), + } + + var headerBytes, strsBytes []byte + headerBytes = appendUint32s(nil, header...) + strsBytes = strs.encode() + + return slices.Concat( + headerBytes, + strsBytes, + extendedData, + structuredData, + nodes, + ), &NodeIndexTable{Nodes: nodeTable}, nil +} + +func appendUint32s(buf []byte, values ...uint32) []byte { + for _, value := range values { + buf = binary.LittleEndian.AppendUint32(buf, value) + } + return buf +} + +func getNodeData(node *ast.Node, strs *stringTable, positionMap *ast.PositionMap, extendedData *[]byte, structuredData *[]byte) uint32 { + t := getNodeDataType(node) + switch t { + case NodeDataTypeChildren: + return t | getNodeCommonData(node) | uint32(getChildrenPropertyMask(node)) + case NodeDataTypeString: + return t | getNodeCommonData(node) | recordNodeStrings(node, strs) + case NodeDataTypeExtendedData: + return t | getNodeCommonData(node) | recordExtendedData(node, strs, positionMap, extendedData, structuredData) + default: + panic("unreachable") + } +} + +const noStructuredData = 0xFFFFFFFF + +func recordExtendedData_SourceFile(node *ast.Node, strs *stringTable, positionMap *ast.PositionMap, extendedData *[]byte, structuredData *[]byte) { + sf := node.AsSourceFile() + textIndex := strs.add(sf.Text(), sf.Kind, sf.Pos(), sf.End()) + fileNameIndex := strs.add(sf.FileName(), 0, 0, 0) + pathIndex := strs.add(string(sf.Path()), 0, 0, 0) + referencedFilesOffset := encodeFileReferences(sf.ReferencedFiles, positionMap, structuredData) + typeRefDirectivesOffset := encodeFileReferences(sf.TypeReferenceDirectives, positionMap, structuredData) + libRefDirectivesOffset := encodeFileReferences(sf.LibReferenceDirectives, positionMap, structuredData) + // imports, moduleAugmentations, ambientModuleNames offsets are placeholders; + // they will be patched after the tree walk when node indices are known. + *extendedData = appendUint32s(*extendedData, textIndex, fileNameIndex, pathIndex, uint32(sf.LanguageVariant), uint32(sf.ScriptKind), referencedFilesOffset, typeRefDirectivesOffset, libRefDirectivesOffset, noStructuredData, noStructuredData, noStructuredData, 0) +} + +func recordExtendedData_TemplateHead(node *ast.Node, strs *stringTable, positionMap *ast.PositionMap, extendedData *[]byte, structuredData *[]byte) { + n := node.AsTemplateHead() + textIndex := strs.add(n.Text, node.Kind, node.Pos(), node.End()) + rawTextIndex := strs.add(n.RawText, node.Kind, node.Pos(), node.End()) + *extendedData = appendUint32s(*extendedData, textIndex, rawTextIndex, uint32(n.TemplateFlags)) +} + +func recordExtendedData_TemplateMiddle(node *ast.Node, strs *stringTable, positionMap *ast.PositionMap, extendedData *[]byte, structuredData *[]byte) { + n := node.AsTemplateMiddle() + textIndex := strs.add(n.Text, node.Kind, node.Pos(), node.End()) + rawTextIndex := strs.add(n.RawText, node.Kind, node.Pos(), node.End()) + *extendedData = appendUint32s(*extendedData, textIndex, rawTextIndex, uint32(n.TemplateFlags)) +} + +func recordExtendedData_TemplateTail(node *ast.Node, strs *stringTable, positionMap *ast.PositionMap, extendedData *[]byte, structuredData *[]byte) { + n := node.AsTemplateTail() + textIndex := strs.add(n.Text, node.Kind, node.Pos(), node.End()) + rawTextIndex := strs.add(n.RawText, node.Kind, node.Pos(), node.End()) + *extendedData = appendUint32s(*extendedData, textIndex, rawTextIndex, uint32(n.TemplateFlags)) +} + +func boolToByte(b bool) byte { + if b { + return 1 + } + return 0 +} + +// hasModifiers returns true if the modifier list is non-nil and has at least one modifier. +func hasModifiers(modifiers *ast.ModifierList) bool { + return modifiers != nil && len(modifiers.Nodes) > 0 +} + +// encodeFileReferences encodes a slice of FileReferences as a msgpack array of tuples +// into the structured data buffer. Returns the byte offset into the buffer, or +// noStructuredData (0xFFFFFFFF) if the slice is empty. +func encodeFileReferences(refs []*ast.FileReference, positionMap *ast.PositionMap, buf *[]byte) uint32 { + if len(refs) == 0 { + return noStructuredData + } + offset := uint32(len(*buf)) + *buf = msgpackWriteArrayHeader(*buf, len(refs)) + for _, ref := range refs { + // Each entry is a 5-element tuple: [pos, end, fileName, resolutionMode, preserve] + *buf = msgpackWriteArrayHeader(*buf, 5) + *buf = msgpackWriteUint(*buf, uint32(positionMap.UTF8ToUTF16(ref.Pos()))) + *buf = msgpackWriteUint(*buf, uint32(positionMap.UTF8ToUTF16(ref.End()))) + *buf = msgpackWriteString(*buf, ref.FileName) + *buf = msgpackWriteUint(*buf, uint32(ref.ResolutionMode)) + *buf = msgpackWriteBool(*buf, ref.Preserve) + } + return offset +} + +// encodeNodeIndexArray encodes a slice of LiteralLikeNodes as a msgpack array of +// uint node indices. Returns the byte offset into the buffer, or noStructuredData +// if the slice is empty. +func encodeNodeIndexArray(nodes []*ast.LiteralLikeNode, indexMap map[*ast.Node]uint32, buf *[]byte) uint32 { + if len(nodes) == 0 { + return noStructuredData + } + offset := uint32(len(*buf)) + *buf = msgpackWriteArrayHeader(*buf, len(nodes)) + for _, node := range nodes { + *buf = msgpackWriteUint(*buf, indexMap[node.AsNode()]) + } + return offset +} + +// encodeModuleAugmentations encodes a slice of ModuleName nodes as a msgpack array +// of uint node indices. Returns the byte offset into the buffer, or noStructuredData +// if the slice is empty. +func encodeModuleAugmentations(nodes []*ast.ModuleName, indexMap map[*ast.Node]uint32, buf *[]byte) uint32 { + if len(nodes) == 0 { + return noStructuredData + } + offset := uint32(len(*buf)) + *buf = msgpackWriteArrayHeader(*buf, len(nodes)) + for _, node := range nodes { + *buf = msgpackWriteUint(*buf, indexMap[node.AsNode()]) + } + return offset +} + +// encodeStringArray encodes a slice of strings as a msgpack array of strings. +// Returns the byte offset into the buffer, or noStructuredData if the slice is empty. +func encodeStringArray(strs []string, buf *[]byte) uint32 { + if len(strs) == 0 { + return noStructuredData + } + offset := uint32(len(*buf)) + *buf = msgpackWriteArrayHeader(*buf, len(strs)) + for _, s := range strs { + *buf = msgpackWriteString(*buf, s) + } + return offset +} + +// Minimal msgpack writers for the structured data section. + +func msgpackWriteArrayHeader(buf []byte, length int) []byte { + if length <= 0x0f { + return append(buf, byte(0x90|length)) + } + if length <= 0xffff { + return append(buf, 0xdc, byte(length>>8), byte(length)) + } + return append(buf, 0xdd, byte(length>>24), byte(length>>16), byte(length>>8), byte(length)) +} + +func msgpackWriteUint(buf []byte, value uint32) []byte { + if value <= 0x7f { + return append(buf, byte(value)) + } + if value <= 0xff { + return append(buf, 0xcc, byte(value)) + } + if value <= 0xffff { + return append(buf, 0xcd, byte(value>>8), byte(value)) + } + return append(buf, 0xce, byte(value>>24), byte(value>>16), byte(value>>8), byte(value)) +} + +func msgpackWriteString(buf []byte, s string) []byte { + n := len(s) + if n <= 0x1f { + buf = append(buf, byte(0xa0|n)) + } else if n <= 0xff { + buf = append(buf, 0xd9, byte(n)) + } else if n <= 0xffff { + buf = append(buf, 0xda, byte(n>>8), byte(n)) + } else { + buf = append(buf, 0xdb, byte(n>>24), byte(n>>16), byte(n>>8), byte(n)) + } + return append(buf, s...) +} + +func msgpackWriteBool(buf []byte, value bool) []byte { + if value { + return append(buf, 0xc3) + } + return append(buf, 0xc2) +} + +// Hand-written commonData encoding functions for nodes whose non-bool data +// members cannot be automatically encoded by the generator. Each function +// packs relevant fields into the 6-bit commonData area (bits 24-29) of the +// 32-bit node data word. + +func getNodeCommonData_SyntheticExpression(_ *ast.Node) uint32 { + // SyntheticExpression is an internal compiler node that is never part of a parsed AST. + // It should never be encoded. + panic("SyntheticExpression should never be encoded") +} + +// Hand-written extended data encoding functions for literal nodes that were +// previously string-type but whose TokenFlags/TemplateFlags cannot fit in 6 bits. + +func recordExtendedData_StringLiteral(node *ast.Node, strs *stringTable, _ *ast.PositionMap, extendedData *[]byte, _ *[]byte) { + n := node.AsStringLiteral() + textIndex := strs.add(n.Text, node.Kind, node.Pos(), node.End()) + *extendedData = appendUint32s(*extendedData, textIndex, uint32(n.TokenFlags)) +} + +func recordExtendedData_NumericLiteral(node *ast.Node, strs *stringTable, _ *ast.PositionMap, extendedData *[]byte, _ *[]byte) { + n := node.AsNumericLiteral() + textIndex := strs.add(n.Text, node.Kind, node.Pos(), node.End()) + *extendedData = appendUint32s(*extendedData, textIndex, uint32(n.TokenFlags)) +} + +func recordExtendedData_BigIntLiteral(node *ast.Node, strs *stringTable, _ *ast.PositionMap, extendedData *[]byte, _ *[]byte) { + n := node.AsBigIntLiteral() + textIndex := strs.add(n.Text, node.Kind, node.Pos(), node.End()) + *extendedData = appendUint32s(*extendedData, textIndex, uint32(n.TokenFlags)) +} + +func recordExtendedData_RegularExpressionLiteral(node *ast.Node, strs *stringTable, _ *ast.PositionMap, extendedData *[]byte, _ *[]byte) { + n := node.AsRegularExpressionLiteral() + textIndex := strs.add(n.Text, node.Kind, node.Pos(), node.End()) + *extendedData = appendUint32s(*extendedData, textIndex, uint32(n.TokenFlags)) +} + +func recordExtendedData_NoSubstitutionTemplateLiteral(node *ast.Node, strs *stringTable, _ *ast.PositionMap, extendedData *[]byte, _ *[]byte) { + n := node.AsNoSubstitutionTemplateLiteral() + textIndex := strs.add(n.Text, node.Kind, node.Pos(), node.End()) + *extendedData = appendUint32s(*extendedData, textIndex, uint32(n.TemplateFlags)) +} diff --git a/tools/tsgo/internal/api/encoder/encoder_generated.go b/tools/tsgo/internal/api/encoder/encoder_generated.go new file mode 100644 index 00000000..c77e287e --- /dev/null +++ b/tools/tsgo/internal/api/encoder/encoder_generated.go @@ -0,0 +1,707 @@ +// Code generated by _scripts/generate-encoder.ts. DO NOT EDIT. + +package encoder + +import ( + "fmt" + + "github.com/microsoft/typescript-go/internal/ast" +) + +func getNodeDataType(node *ast.Node) uint32 { + switch node.Kind { + case ast.KindIdentifier, + ast.KindPrivateIdentifier, + ast.KindJsxText, + ast.KindJSDocText, + ast.KindJSDocLink, + ast.KindJSDocLinkPlain, + ast.KindJSDocLinkCode: + return NodeDataTypeString + case ast.KindStringLiteral, + ast.KindNumericLiteral, + ast.KindBigIntLiteral, + ast.KindRegularExpressionLiteral, + ast.KindNoSubstitutionTemplateLiteral, + ast.KindTemplateHead, + ast.KindTemplateMiddle, + ast.KindTemplateTail, + ast.KindSourceFile: + return NodeDataTypeExtendedData + default: + return NodeDataTypeChildren + } +} + +func getChildrenPropertyMask(node *ast.Node) uint8 { + switch node.Kind { + case ast.KindQualifiedName: + n := node.AsQualifiedName() + return (boolToByte(n.Left != nil) << 0) | (boolToByte(n.Right != nil) << 1) + case ast.KindComputedPropertyName: + n := node.AsComputedPropertyName() + return (boolToByte(n.Expression != nil) << 0) + case ast.KindDecorator: + n := node.AsDecorator() + return (boolToByte(n.Expression != nil) << 0) + case ast.KindIfStatement: + n := node.AsIfStatement() + return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.ThenStatement != nil) << 1) | (boolToByte(n.ElseStatement != nil) << 2) + case ast.KindDoStatement: + n := node.AsDoStatement() + return (boolToByte(n.Statement != nil) << 0) | (boolToByte(n.Expression != nil) << 1) + case ast.KindWhileStatement: + n := node.AsWhileStatement() + return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.Statement != nil) << 1) + case ast.KindForStatement: + n := node.AsForStatement() + return (boolToByte(n.Initializer != nil) << 0) | (boolToByte(n.Condition != nil) << 1) | (boolToByte(n.Incrementor != nil) << 2) | (boolToByte(n.Statement != nil) << 3) + case ast.KindForInStatement, ast.KindForOfStatement: + n := node.AsForInOrOfStatement() + return (boolToByte(n.AwaitModifier != nil) << 0) | (boolToByte(n.Initializer != nil) << 1) | (boolToByte(n.Expression != nil) << 2) | (boolToByte(n.Statement != nil) << 3) + case ast.KindBreakStatement: + n := node.AsBreakStatement() + return (boolToByte(n.Label != nil) << 0) + case ast.KindContinueStatement: + n := node.AsContinueStatement() + return (boolToByte(n.Label != nil) << 0) + case ast.KindReturnStatement: + n := node.AsReturnStatement() + return (boolToByte(n.Expression != nil) << 0) + case ast.KindWithStatement: + n := node.AsWithStatement() + return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.Statement != nil) << 1) + case ast.KindSwitchStatement: + n := node.AsSwitchStatement() + return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.CaseBlock != nil) << 1) + case ast.KindCaseBlock: + n := node.AsCaseBlock() + return (boolToByte(n.Clauses != nil) << 0) + case ast.KindCaseClause, ast.KindDefaultClause: + n := node.AsCaseOrDefaultClause() + return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.Statements != nil) << 1) + case ast.KindThrowStatement: + n := node.AsThrowStatement() + return (boolToByte(n.Expression != nil) << 0) + case ast.KindTryStatement: + n := node.AsTryStatement() + return (boolToByte(n.TryBlock != nil) << 0) | (boolToByte(n.CatchClause != nil) << 1) | (boolToByte(n.FinallyBlock != nil) << 2) + case ast.KindCatchClause: + n := node.AsCatchClause() + return (boolToByte(n.VariableDeclaration != nil) << 0) | (boolToByte(n.Block != nil) << 1) + case ast.KindLabeledStatement: + n := node.AsLabeledStatement() + return (boolToByte(n.Label != nil) << 0) | (boolToByte(n.Statement != nil) << 1) + case ast.KindExpressionStatement: + n := node.AsExpressionStatement() + return (boolToByte(n.Expression != nil) << 0) + case ast.KindBlock: + n := node.AsBlock() + return (boolToByte(n.Statements != nil) << 0) + case ast.KindVariableStatement: + n := node.AsVariableStatement() + return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.DeclarationList != nil) << 1) + case ast.KindVariableDeclaration: + n := node.AsVariableDeclaration() + return (boolToByte(n.Name() != nil) << 0) | (boolToByte(n.ExclamationToken != nil) << 1) | (boolToByte(n.Type != nil) << 2) | (boolToByte(n.Initializer != nil) << 3) + case ast.KindVariableDeclarationList: + n := node.AsVariableDeclarationList() + return (boolToByte(n.Declarations != nil) << 0) + case ast.KindObjectBindingPattern, ast.KindArrayBindingPattern: + n := node.AsBindingPattern() + return (boolToByte(n.Elements != nil) << 0) + case ast.KindParameter: + n := node.AsParameterDeclaration() + return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.DotDotDotToken != nil) << 1) | (boolToByte(n.Name() != nil) << 2) | (boolToByte(n.QuestionToken != nil) << 3) | (boolToByte(n.Type != nil) << 4) | (boolToByte(n.Initializer != nil) << 5) + case ast.KindBindingElement: + n := node.AsBindingElement() + return (boolToByte(n.DotDotDotToken != nil) << 0) | (boolToByte(n.PropertyName != nil) << 1) | (boolToByte(n.Name() != nil) << 2) | (boolToByte(n.Initializer != nil) << 3) + case ast.KindMissingDeclaration: + n := node.AsMissingDeclaration() + return (boolToByte(hasModifiers(n.Modifiers())) << 0) + case ast.KindFunctionDeclaration: + n := node.AsFunctionDeclaration() + return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.AsteriskToken != nil) << 1) | (boolToByte(n.Name() != nil) << 2) | (boolToByte(n.TypeParameters != nil) << 3) | (boolToByte(n.Parameters != nil) << 4) | (boolToByte(n.Type != nil) << 5) | (boolToByte(n.Body != nil) << 6) + case ast.KindClassDeclaration: + n := node.AsClassDeclaration() + return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.TypeParameters != nil) << 2) | (boolToByte(n.HeritageClauses != nil) << 3) | (boolToByte(n.Members != nil) << 4) + case ast.KindClassExpression: + n := node.AsClassExpression() + return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.TypeParameters != nil) << 2) | (boolToByte(n.HeritageClauses != nil) << 3) | (boolToByte(n.Members != nil) << 4) + case ast.KindHeritageClause: + n := node.AsHeritageClause() + return (boolToByte(n.Types != nil) << 0) + case ast.KindInterfaceDeclaration: + n := node.AsInterfaceDeclaration() + return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.TypeParameters != nil) << 2) | (boolToByte(n.HeritageClauses != nil) << 3) | (boolToByte(n.Members != nil) << 4) + case ast.KindTypeAliasDeclaration, ast.KindJSTypeAliasDeclaration: + n := node.AsTypeAliasDeclaration() + return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.TypeParameters != nil) << 2) | (boolToByte(n.Type != nil) << 3) + case ast.KindEnumMember: + n := node.AsEnumMember() + return (boolToByte(n.Name() != nil) << 0) | (boolToByte(n.Initializer != nil) << 1) + case ast.KindEnumDeclaration: + n := node.AsEnumDeclaration() + return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.Members != nil) << 2) + case ast.KindModuleBlock: + n := node.AsModuleBlock() + return (boolToByte(n.Statements != nil) << 0) + case ast.KindImportDeclaration, ast.KindJSImportDeclaration: + n := node.AsImportDeclaration() + return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.ImportClause != nil) << 1) | (boolToByte(n.ModuleSpecifier != nil) << 2) | (boolToByte(n.Attributes != nil) << 3) + case ast.KindExternalModuleReference: + n := node.AsExternalModuleReference() + return (boolToByte(n.Expression != nil) << 0) + case ast.KindNamespaceImport: + n := node.AsNamespaceImport() + return (boolToByte(n.Name() != nil) << 0) + case ast.KindNamedImports: + n := node.AsNamedImports() + return (boolToByte(n.Elements != nil) << 0) + case ast.KindExportAssignment: + n := node.AsExportAssignment() + return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Type != nil) << 1) | (boolToByte(n.Expression != nil) << 2) + case ast.KindNamespaceExportDeclaration: + n := node.AsNamespaceExportDeclaration() + return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Name() != nil) << 1) + case ast.KindNamespaceExport: + n := node.AsNamespaceExport() + return (boolToByte(n.Name() != nil) << 0) + case ast.KindNamedExports: + n := node.AsNamedExports() + return (boolToByte(n.Elements != nil) << 0) + case ast.KindExportSpecifier: + n := node.AsExportSpecifier() + return (boolToByte(n.PropertyName != nil) << 0) | (boolToByte(n.Name() != nil) << 1) + case ast.KindCallSignature: + n := node.AsCallSignatureDeclaration() + return (boolToByte(n.TypeParameters != nil) << 0) | (boolToByte(n.Parameters != nil) << 1) | (boolToByte(n.Type != nil) << 2) + case ast.KindConstructSignature: + n := node.AsConstructSignatureDeclaration() + return (boolToByte(n.TypeParameters != nil) << 0) | (boolToByte(n.Parameters != nil) << 1) | (boolToByte(n.Type != nil) << 2) + case ast.KindConstructor: + n := node.AsConstructorDeclaration() + return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.TypeParameters != nil) << 1) | (boolToByte(n.Parameters != nil) << 2) | (boolToByte(n.Type != nil) << 3) | (boolToByte(n.Body != nil) << 4) + case ast.KindGetAccessor: + n := node.AsGetAccessorDeclaration() + return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.TypeParameters != nil) << 2) | (boolToByte(n.Parameters != nil) << 3) | (boolToByte(n.Type != nil) << 4) | (boolToByte(n.Body != nil) << 5) + case ast.KindSetAccessor: + n := node.AsSetAccessorDeclaration() + return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.TypeParameters != nil) << 2) | (boolToByte(n.Parameters != nil) << 3) | (boolToByte(n.Type != nil) << 4) | (boolToByte(n.Body != nil) << 5) + case ast.KindIndexSignature: + n := node.AsIndexSignatureDeclaration() + return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Parameters != nil) << 1) | (boolToByte(n.Type != nil) << 2) + case ast.KindMethodSignature: + n := node.AsMethodSignatureDeclaration() + return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.PostfixToken != nil) << 2) | (boolToByte(n.TypeParameters != nil) << 3) | (boolToByte(n.Parameters != nil) << 4) | (boolToByte(n.Type != nil) << 5) + case ast.KindMethodDeclaration: + n := node.AsMethodDeclaration() + return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.AsteriskToken != nil) << 1) | (boolToByte(n.Name() != nil) << 2) | (boolToByte(n.PostfixToken != nil) << 3) | (boolToByte(n.TypeParameters != nil) << 4) | (boolToByte(n.Parameters != nil) << 5) | (boolToByte(n.Type != nil) << 6) | (boolToByte(n.Body != nil) << 7) + case ast.KindPropertySignature: + n := node.AsPropertySignatureDeclaration() + return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.PostfixToken != nil) << 2) | (boolToByte(n.Type != nil) << 3) | (boolToByte(n.Initializer != nil) << 4) + case ast.KindPropertyDeclaration: + n := node.AsPropertyDeclaration() + return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.PostfixToken != nil) << 2) | (boolToByte(n.Type != nil) << 3) | (boolToByte(n.Initializer != nil) << 4) + case ast.KindClassStaticBlockDeclaration: + n := node.AsClassStaticBlockDeclaration() + return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Body != nil) << 1) + case ast.KindBinaryExpression: + n := node.AsBinaryExpression() + return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Left != nil) << 1) | (boolToByte(n.Type != nil) << 2) | (boolToByte(n.OperatorToken != nil) << 3) | (boolToByte(n.Right != nil) << 4) + case ast.KindPrefixUnaryExpression: + n := node.AsPrefixUnaryExpression() + return (boolToByte(n.Operand != nil) << 0) + case ast.KindPostfixUnaryExpression: + n := node.AsPostfixUnaryExpression() + return (boolToByte(n.Operand != nil) << 0) + case ast.KindYieldExpression: + n := node.AsYieldExpression() + return (boolToByte(n.AsteriskToken != nil) << 0) | (boolToByte(n.Expression != nil) << 1) + case ast.KindArrowFunction: + n := node.AsArrowFunction() + return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.TypeParameters != nil) << 1) | (boolToByte(n.Parameters != nil) << 2) | (boolToByte(n.Type != nil) << 3) | (boolToByte(n.EqualsGreaterThanToken != nil) << 4) | (boolToByte(n.Body != nil) << 5) + case ast.KindFunctionExpression: + n := node.AsFunctionExpression() + return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.AsteriskToken != nil) << 1) | (boolToByte(n.Name() != nil) << 2) | (boolToByte(n.TypeParameters != nil) << 3) | (boolToByte(n.Parameters != nil) << 4) | (boolToByte(n.Type != nil) << 5) | (boolToByte(n.Body != nil) << 6) + case ast.KindAsExpression: + n := node.AsAsExpression() + return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.Type != nil) << 1) + case ast.KindSatisfiesExpression: + n := node.AsSatisfiesExpression() + return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.Type != nil) << 1) + case ast.KindConditionalExpression: + n := node.AsConditionalExpression() + return (boolToByte(n.Condition != nil) << 0) | (boolToByte(n.QuestionToken != nil) << 1) | (boolToByte(n.WhenTrue != nil) << 2) | (boolToByte(n.ColonToken != nil) << 3) | (boolToByte(n.WhenFalse != nil) << 4) + case ast.KindPropertyAccessExpression: + n := node.AsPropertyAccessExpression() + return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.QuestionDotToken != nil) << 1) | (boolToByte(n.Name() != nil) << 2) + case ast.KindElementAccessExpression: + n := node.AsElementAccessExpression() + return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.QuestionDotToken != nil) << 1) | (boolToByte(n.ArgumentExpression != nil) << 2) + case ast.KindCallExpression: + n := node.AsCallExpression() + return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.QuestionDotToken != nil) << 1) | (boolToByte(n.TypeArguments != nil) << 2) | (boolToByte(n.Arguments != nil) << 3) + case ast.KindNewExpression: + n := node.AsNewExpression() + return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.TypeArguments != nil) << 1) | (boolToByte(n.Arguments != nil) << 2) + case ast.KindMetaProperty: + n := node.AsMetaProperty() + return (boolToByte(n.Name() != nil) << 0) + case ast.KindNonNullExpression: + n := node.AsNonNullExpression() + return (boolToByte(n.Expression != nil) << 0) + case ast.KindSpreadElement: + n := node.AsSpreadElement() + return (boolToByte(n.Expression != nil) << 0) + case ast.KindTemplateExpression: + n := node.AsTemplateExpression() + return (boolToByte(n.Head != nil) << 0) | (boolToByte(n.TemplateSpans != nil) << 1) + case ast.KindTemplateSpan: + n := node.AsTemplateSpan() + return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.Literal != nil) << 1) + case ast.KindTaggedTemplateExpression: + n := node.AsTaggedTemplateExpression() + return (boolToByte(n.Tag != nil) << 0) | (boolToByte(n.QuestionDotToken != nil) << 1) | (boolToByte(n.TypeArguments != nil) << 2) | (boolToByte(n.Template != nil) << 3) + case ast.KindParenthesizedExpression: + n := node.AsParenthesizedExpression() + return (boolToByte(n.Expression != nil) << 0) + case ast.KindArrayLiteralExpression: + n := node.AsArrayLiteralExpression() + return (boolToByte(n.Elements != nil) << 0) + case ast.KindObjectLiteralExpression: + n := node.AsObjectLiteralExpression() + return (boolToByte(n.Properties != nil) << 0) + case ast.KindSpreadAssignment: + n := node.AsSpreadAssignment() + return (boolToByte(n.Expression != nil) << 0) + case ast.KindPropertyAssignment: + n := node.AsPropertyAssignment() + return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.PostfixToken != nil) << 2) | (boolToByte(n.Type != nil) << 3) | (boolToByte(n.Initializer != nil) << 4) + case ast.KindShorthandPropertyAssignment: + n := node.AsShorthandPropertyAssignment() + return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.PostfixToken != nil) << 2) | (boolToByte(n.Type != nil) << 3) | (boolToByte(n.EqualsToken != nil) << 4) | (boolToByte(n.ObjectAssignmentInitializer != nil) << 5) + case ast.KindDeleteExpression: + n := node.AsDeleteExpression() + return (boolToByte(n.Expression != nil) << 0) + case ast.KindTypeOfExpression: + n := node.AsTypeOfExpression() + return (boolToByte(n.Expression != nil) << 0) + case ast.KindVoidExpression: + n := node.AsVoidExpression() + return (boolToByte(n.Expression != nil) << 0) + case ast.KindAwaitExpression: + n := node.AsAwaitExpression() + return (boolToByte(n.Expression != nil) << 0) + case ast.KindTypeAssertionExpression: + n := node.AsTypeAssertion() + return (boolToByte(n.Type != nil) << 0) | (boolToByte(n.Expression != nil) << 1) + case ast.KindUnionType: + n := node.AsUnionTypeNode() + return (boolToByte(n.Types != nil) << 0) + case ast.KindIntersectionType: + n := node.AsIntersectionTypeNode() + return (boolToByte(n.Types != nil) << 0) + case ast.KindConditionalType: + n := node.AsConditionalTypeNode() + return (boolToByte(n.CheckType != nil) << 0) | (boolToByte(n.ExtendsType != nil) << 1) | (boolToByte(n.TrueType != nil) << 2) | (boolToByte(n.FalseType != nil) << 3) + case ast.KindTypeOperator: + n := node.AsTypeOperatorNode() + return (boolToByte(n.Type != nil) << 0) + case ast.KindInferType: + n := node.AsInferTypeNode() + return (boolToByte(n.TypeParameter != nil) << 0) + case ast.KindArrayType: + n := node.AsArrayTypeNode() + return (boolToByte(n.ElementType != nil) << 0) + case ast.KindIndexedAccessType: + n := node.AsIndexedAccessTypeNode() + return (boolToByte(n.ObjectType != nil) << 0) | (boolToByte(n.IndexType != nil) << 1) + case ast.KindTypeReference: + n := node.AsTypeReferenceNode() + return (boolToByte(n.TypeName != nil) << 0) | (boolToByte(n.TypeArguments != nil) << 1) + case ast.KindExpressionWithTypeArguments: + n := node.AsExpressionWithTypeArguments() + return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.TypeArguments != nil) << 1) + case ast.KindLiteralType: + n := node.AsLiteralTypeNode() + return (boolToByte(n.Literal != nil) << 0) + case ast.KindTypePredicate: + n := node.AsTypePredicateNode() + return (boolToByte(n.AssertsModifier != nil) << 0) | (boolToByte(n.ParameterName != nil) << 1) | (boolToByte(n.Type != nil) << 2) + case ast.KindImportAttribute: + n := node.AsImportAttribute() + return (boolToByte(n.Name() != nil) << 0) | (boolToByte(n.Value != nil) << 1) + case ast.KindImportAttributes: + n := node.AsImportAttributes() + return (boolToByte(n.Attributes != nil) << 0) + case ast.KindTypeQuery: + n := node.AsTypeQueryNode() + return (boolToByte(n.ExprName != nil) << 0) | (boolToByte(n.TypeArguments != nil) << 1) + case ast.KindMappedType: + n := node.AsMappedTypeNode() + return (boolToByte(n.ReadonlyToken != nil) << 0) | (boolToByte(n.TypeParameter != nil) << 1) | (boolToByte(n.NameType != nil) << 2) | (boolToByte(n.QuestionToken != nil) << 3) | (boolToByte(n.Type != nil) << 4) | (boolToByte(n.Members != nil) << 5) + case ast.KindTypeLiteral: + n := node.AsTypeLiteralNode() + return (boolToByte(n.Members != nil) << 0) + case ast.KindTupleType: + n := node.AsTupleTypeNode() + return (boolToByte(n.Elements != nil) << 0) + case ast.KindNamedTupleMember: + n := node.AsNamedTupleMember() + return (boolToByte(n.DotDotDotToken != nil) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.QuestionToken != nil) << 2) | (boolToByte(n.Type != nil) << 3) + case ast.KindOptionalType: + n := node.AsOptionalTypeNode() + return (boolToByte(n.Type != nil) << 0) + case ast.KindRestType: + n := node.AsRestTypeNode() + return (boolToByte(n.Type != nil) << 0) + case ast.KindParenthesizedType: + n := node.AsParenthesizedTypeNode() + return (boolToByte(n.Type != nil) << 0) + case ast.KindFunctionType: + n := node.AsFunctionTypeNode() + return (boolToByte(n.TypeParameters != nil) << 0) | (boolToByte(n.Parameters != nil) << 1) | (boolToByte(n.Type != nil) << 2) + case ast.KindConstructorType: + n := node.AsConstructorTypeNode() + return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.TypeParameters != nil) << 1) | (boolToByte(n.Parameters != nil) << 2) | (boolToByte(n.Type != nil) << 3) + case ast.KindTemplateLiteralType: + n := node.AsTemplateLiteralTypeNode() + return (boolToByte(n.Head != nil) << 0) | (boolToByte(n.TemplateSpans != nil) << 1) + case ast.KindTemplateLiteralTypeSpan: + n := node.AsTemplateLiteralTypeSpan() + return (boolToByte(n.Type != nil) << 0) | (boolToByte(n.Literal != nil) << 1) + case ast.KindSyntheticExpression: + n := node.AsSyntheticExpression() + return (boolToByte(n.TupleNameSource != nil) << 0) + case ast.KindPartiallyEmittedExpression: + n := node.AsPartiallyEmittedExpression() + return (boolToByte(n.Expression != nil) << 0) + case ast.KindJsxElement: + n := node.AsJsxElement() + return (boolToByte(n.OpeningElement != nil) << 0) | (boolToByte(n.Children != nil) << 1) | (boolToByte(n.ClosingElement != nil) << 2) + case ast.KindJsxAttributes: + n := node.AsJsxAttributes() + return (boolToByte(n.Properties != nil) << 0) + case ast.KindJsxNamespacedName: + n := node.AsJsxNamespacedName() + return (boolToByte(n.Namespace != nil) << 0) | (boolToByte(n.Name() != nil) << 1) + case ast.KindJsxOpeningElement: + n := node.AsJsxOpeningElement() + return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.TypeArguments != nil) << 1) | (boolToByte(n.Attributes != nil) << 2) + case ast.KindJsxSelfClosingElement: + n := node.AsJsxSelfClosingElement() + return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.TypeArguments != nil) << 1) | (boolToByte(n.Attributes != nil) << 2) + case ast.KindJsxFragment: + n := node.AsJsxFragment() + return (boolToByte(n.OpeningFragment != nil) << 0) | (boolToByte(n.Children != nil) << 1) | (boolToByte(n.ClosingFragment != nil) << 2) + case ast.KindJsxAttribute: + n := node.AsJsxAttribute() + return (boolToByte(n.Name() != nil) << 0) | (boolToByte(n.Initializer != nil) << 1) + case ast.KindJsxSpreadAttribute: + n := node.AsJsxSpreadAttribute() + return (boolToByte(n.Expression != nil) << 0) + case ast.KindJsxClosingElement: + n := node.AsJsxClosingElement() + return (boolToByte(n.TagName != nil) << 0) + case ast.KindJsxExpression: + n := node.AsJsxExpression() + return (boolToByte(n.DotDotDotToken != nil) << 0) | (boolToByte(n.Expression != nil) << 1) + case ast.KindSyntaxList: + n := node.AsSyntaxList() + return (boolToByte(len(n.Children) > 0) << 0) + case ast.KindJSDoc: + n := node.AsJSDoc() + return (boolToByte(n.Comment != nil) << 0) | (boolToByte(n.Tags != nil) << 1) + case ast.KindJSDocTypeExpression: + n := node.AsJSDocTypeExpression() + return (boolToByte(n.Type != nil) << 0) + case ast.KindJSDocNonNullableType: + n := node.AsJSDocNonNullableType() + return (boolToByte(n.Type != nil) << 0) + case ast.KindJSDocNullableType: + n := node.AsJSDocNullableType() + return (boolToByte(n.Type != nil) << 0) + case ast.KindJSDocVariadicType: + n := node.AsJSDocVariadicType() + return (boolToByte(n.Type != nil) << 0) + case ast.KindJSDocOptionalType: + n := node.AsJSDocOptionalType() + return (boolToByte(n.Type != nil) << 0) + case ast.KindJSDocTypeTag: + n := node.AsJSDocTypeTag() + return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.TypeExpression != nil) << 1) | (boolToByte(n.Comment != nil) << 2) + case ast.KindJSDocUnknownTag: + n := node.AsJSDocUnknownTag() + return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.Comment != nil) << 1) + case ast.KindJSDocTemplateTag: + n := node.AsJSDocTemplateTag() + return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.Constraint != nil) << 1) | (boolToByte(n.TypeParameters != nil) << 2) | (boolToByte(n.Comment != nil) << 3) + case ast.KindJSDocReturnTag: + n := node.AsJSDocReturnTag() + return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.TypeExpression != nil) << 1) | (boolToByte(n.Comment != nil) << 2) + case ast.KindJSDocPublicTag: + n := node.AsJSDocPublicTag() + return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.Comment != nil) << 1) + case ast.KindJSDocPrivateTag: + n := node.AsJSDocPrivateTag() + return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.Comment != nil) << 1) + case ast.KindJSDocProtectedTag: + n := node.AsJSDocProtectedTag() + return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.Comment != nil) << 1) + case ast.KindJSDocReadonlyTag: + n := node.AsJSDocReadonlyTag() + return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.Comment != nil) << 1) + case ast.KindJSDocOverrideTag: + n := node.AsJSDocOverrideTag() + return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.Comment != nil) << 1) + case ast.KindJSDocDeprecatedTag: + n := node.AsJSDocDeprecatedTag() + return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.Comment != nil) << 1) + case ast.KindJSDocSeeTag: + n := node.AsJSDocSeeTag() + return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.NameExpression != nil) << 1) | (boolToByte(n.Comment != nil) << 2) + case ast.KindJSDocImplementsTag: + n := node.AsJSDocImplementsTag() + return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.ClassName != nil) << 1) | (boolToByte(n.Comment != nil) << 2) + case ast.KindJSDocAugmentsTag: + n := node.AsJSDocAugmentsTag() + return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.ClassName != nil) << 1) | (boolToByte(n.Comment != nil) << 2) + case ast.KindJSDocSatisfiesTag: + n := node.AsJSDocSatisfiesTag() + return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.TypeExpression != nil) << 1) | (boolToByte(n.Comment != nil) << 2) + case ast.KindJSDocThrowsTag: + n := node.AsJSDocThrowsTag() + return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.TypeExpression != nil) << 1) | (boolToByte(n.Comment != nil) << 2) + case ast.KindJSDocThisTag: + n := node.AsJSDocThisTag() + return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.TypeExpression != nil) << 1) | (boolToByte(n.Comment != nil) << 2) + case ast.KindJSDocImportTag: + n := node.AsJSDocImportTag() + return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.ImportClause != nil) << 1) | (boolToByte(n.ModuleSpecifier != nil) << 2) | (boolToByte(n.Attributes != nil) << 3) | (boolToByte(n.Comment != nil) << 4) + case ast.KindJSDocCallbackTag: + n := node.AsJSDocCallbackTag() + return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.TypeExpression != nil) << 1) | (boolToByte(n.Name() != nil) << 2) | (boolToByte(n.Comment != nil) << 3) + case ast.KindJSDocOverloadTag: + n := node.AsJSDocOverloadTag() + return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.TypeExpression != nil) << 1) | (boolToByte(n.Comment != nil) << 2) + case ast.KindJSDocTypedefTag: + n := node.AsJSDocTypedefTag() + return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.TypeExpression != nil) << 1) | (boolToByte(n.Name() != nil) << 2) | (boolToByte(n.Comment != nil) << 3) + case ast.KindJSDocSignature: + n := node.AsJSDocSignature() + return (boolToByte(n.TypeParameters != nil) << 0) | (boolToByte(n.Parameters != nil) << 1) | (boolToByte(n.Type != nil) << 2) + case ast.KindJSDocNameReference: + n := node.AsJSDocNameReference() + return (boolToByte(n.Name() != nil) << 0) + case ast.KindModuleDeclaration: + n := node.AsModuleDeclaration() + return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.Body != nil) << 2) + case ast.KindImportEqualsDeclaration: + n := node.AsImportEqualsDeclaration() + return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.ModuleReference != nil) << 2) + case ast.KindExportDeclaration: + n := node.AsExportDeclaration() + return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.ExportClause != nil) << 1) | (boolToByte(n.ModuleSpecifier != nil) << 2) | (boolToByte(n.Attributes != nil) << 3) + case ast.KindImportType: + n := node.AsImportTypeNode() + return (boolToByte(n.Argument != nil) << 0) | (boolToByte(n.Attributes != nil) << 1) | (boolToByte(n.Qualifier != nil) << 2) | (boolToByte(n.TypeArguments != nil) << 3) + case ast.KindImportClause: + n := node.AsImportClause() + return (boolToByte(n.Name() != nil) << 0) | (boolToByte(n.NamedBindings != nil) << 1) + case ast.KindImportSpecifier: + n := node.AsImportSpecifier() + return (boolToByte(n.PropertyName != nil) << 0) | (boolToByte(n.Name() != nil) << 1) + case ast.KindJSDocLink: + n := node.AsJSDocLink() + return (boolToByte(n.Name() != nil) << 0) + case ast.KindJSDocLinkPlain: + n := node.AsJSDocLinkPlain() + return (boolToByte(n.Name() != nil) << 0) + case ast.KindJSDocLinkCode: + n := node.AsJSDocLinkCode() + return (boolToByte(n.Name() != nil) << 0) + case ast.KindTypeParameter: + n := node.AsTypeParameterDeclaration() + return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.Constraint != nil) << 2) | (boolToByte(n.Expression != nil) << 3) | (boolToByte(n.DefaultType != nil) << 4) + case ast.KindSyntheticReferenceExpression: + n := node.AsSyntheticReferenceExpression() + return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.ThisArg != nil) << 1) + case ast.KindJSDocTypeLiteral: + n := node.AsJSDocTypeLiteral() + return (boolToByte(len(n.JSDocPropertyTags) > 0) << 0) + case ast.KindJSDocParameterTag, ast.KindJSDocPropertyTag: + n := node.AsJSDocParameterOrPropertyTag() + return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.TypeExpression != nil) << 2) | (boolToByte(n.Comment != nil) << 3) + default: + return 0 + } +} + +func getNodeCommonData(node *ast.Node) uint32 { + switch node.Kind { + case ast.KindBlock: + n := node.AsBlock() + return uint32(boolToByte(n.MultiLine)) << 24 + case ast.KindHeritageClause: + n := node.AsHeritageClause() + var tokenIdx uint32 + switch n.Token { + case ast.KindImplementsKeyword: + tokenIdx = 1 + } + return tokenIdx << 24 + case ast.KindExportAssignment: + n := node.AsExportAssignment() + return uint32(boolToByte(n.IsExportEquals)) << 24 + case ast.KindExportSpecifier: + n := node.AsExportSpecifier() + return uint32(boolToByte(n.IsTypeOnly)) << 24 + case ast.KindPrefixUnaryExpression: + n := node.AsPrefixUnaryExpression() + var operatorIdx uint32 + switch n.Operator { + case ast.KindMinusToken: + operatorIdx = 1 + case ast.KindTildeToken: + operatorIdx = 2 + case ast.KindExclamationToken: + operatorIdx = 3 + case ast.KindPlusPlusToken: + operatorIdx = 4 + case ast.KindMinusMinusToken: + operatorIdx = 5 + } + return operatorIdx << 24 + case ast.KindPostfixUnaryExpression: + n := node.AsPostfixUnaryExpression() + var operatorIdx uint32 + switch n.Operator { + case ast.KindMinusMinusToken: + operatorIdx = 1 + } + return operatorIdx << 24 + case ast.KindMetaProperty: + n := node.AsMetaProperty() + var keywordTokenIdx uint32 + switch n.KeywordToken { + case ast.KindNewKeyword: + keywordTokenIdx = 1 + } + return keywordTokenIdx << 24 + case ast.KindArrayLiteralExpression: + n := node.AsArrayLiteralExpression() + return uint32(boolToByte(n.MultiLine)) << 24 + case ast.KindObjectLiteralExpression: + n := node.AsObjectLiteralExpression() + return uint32(boolToByte(n.MultiLine)) << 24 + case ast.KindTypeOperator: + n := node.AsTypeOperatorNode() + var operatorIdx uint32 + switch n.Operator { + case ast.KindReadonlyKeyword: + operatorIdx = 1 + case ast.KindUniqueKeyword: + operatorIdx = 2 + } + return operatorIdx << 24 + case ast.KindImportAttributes: + n := node.AsImportAttributes() + var tokenIdx uint32 + switch n.Token { + case ast.KindAssertKeyword: + tokenIdx = 1 + } + return uint32(boolToByte(n.MultiLine))<<24 | tokenIdx<<25 + case ast.KindSyntheticExpression: + return getNodeCommonData_SyntheticExpression(node) + case ast.KindJsxText: + n := node.AsJsxText() + return uint32(boolToByte(n.ContainsOnlyTriviaWhiteSpaces)) << 24 + case ast.KindModuleDeclaration: + n := node.AsModuleDeclaration() + var keywordIdx uint32 + switch n.Keyword { + case ast.KindNamespaceKeyword: + keywordIdx = 1 + } + return keywordIdx << 24 + case ast.KindImportEqualsDeclaration: + n := node.AsImportEqualsDeclaration() + return uint32(boolToByte(n.IsTypeOnly)) << 24 + case ast.KindExportDeclaration: + n := node.AsExportDeclaration() + return uint32(boolToByte(n.IsTypeOnly)) << 24 + case ast.KindImportType: + n := node.AsImportTypeNode() + return uint32(boolToByte(n.IsTypeOf)) << 24 + case ast.KindImportClause: + n := node.AsImportClause() + var phaseModifierIdx uint32 + switch n.PhaseModifier { + case ast.KindTypeKeyword: + phaseModifierIdx = 1 + case ast.KindDeferKeyword: + phaseModifierIdx = 2 + } + return phaseModifierIdx << 24 + case ast.KindImportSpecifier: + n := node.AsImportSpecifier() + return uint32(boolToByte(n.IsTypeOnly)) << 24 + case ast.KindJSDocTypeLiteral: + n := node.AsJSDocTypeLiteral() + return uint32(boolToByte(n.IsArrayType)) << 24 + case ast.KindJSDocParameterTag, ast.KindJSDocPropertyTag: + n := node.AsJSDocParameterOrPropertyTag() + return uint32(boolToByte(n.IsBracketed))<<24 | uint32(boolToByte(n.IsNameFirst))<<25 + } + return 0 +} + +func recordNodeStrings(node *ast.Node, strs *stringTable) uint32 { + switch node.Kind { + case ast.KindIdentifier: + return strs.add(node.AsIdentifier().Text, node.Kind, node.Pos(), node.End()) + case ast.KindPrivateIdentifier: + return strs.add(node.AsPrivateIdentifier().Text, node.Kind, node.Pos(), node.End()) + case ast.KindJsxText: + return strs.add(node.AsJsxText().Text, node.Kind, node.Pos(), node.End()) + case ast.KindJSDocText: + return strs.add(node.AsJSDocText().Text(), node.Kind, node.Pos(), node.End()) + case ast.KindJSDocLink: + return strs.add(node.AsJSDocLink().Text(), node.Kind, node.Pos(), node.End()) + case ast.KindJSDocLinkPlain: + return strs.add(node.AsJSDocLinkPlain().Text(), node.Kind, node.Pos(), node.End()) + case ast.KindJSDocLinkCode: + return strs.add(node.AsJSDocLinkCode().Text(), node.Kind, node.Pos(), node.End()) + default: + panic(fmt.Sprintf("Unexpected node kind %v", node.Kind)) + } +} + +func recordExtendedData(node *ast.Node, strs *stringTable, positionMap *ast.PositionMap, extendedData *[]byte, structuredData *[]byte) uint32 { + offset := uint32(len(*extendedData)) + switch node.Kind { + case ast.KindStringLiteral: + recordExtendedData_StringLiteral(node, strs, positionMap, extendedData, structuredData) + case ast.KindNumericLiteral: + recordExtendedData_NumericLiteral(node, strs, positionMap, extendedData, structuredData) + case ast.KindBigIntLiteral: + recordExtendedData_BigIntLiteral(node, strs, positionMap, extendedData, structuredData) + case ast.KindRegularExpressionLiteral: + recordExtendedData_RegularExpressionLiteral(node, strs, positionMap, extendedData, structuredData) + case ast.KindNoSubstitutionTemplateLiteral: + recordExtendedData_NoSubstitutionTemplateLiteral(node, strs, positionMap, extendedData, structuredData) + case ast.KindTemplateHead: + recordExtendedData_TemplateHead(node, strs, positionMap, extendedData, structuredData) + case ast.KindTemplateMiddle: + recordExtendedData_TemplateMiddle(node, strs, positionMap, extendedData, structuredData) + case ast.KindTemplateTail: + recordExtendedData_TemplateTail(node, strs, positionMap, extendedData, structuredData) + case ast.KindSourceFile: + recordExtendedData_SourceFile(node, strs, positionMap, extendedData, structuredData) + default: + panic(fmt.Sprintf("unknown extended data node kind %v", node.Kind)) + } + return offset +} diff --git a/tools/tsgo/internal/api/encoder/encoder_test.go b/tools/tsgo/internal/api/encoder/encoder_test.go new file mode 100644 index 00000000..de774c74 --- /dev/null +++ b/tools/tsgo/internal/api/encoder/encoder_test.go @@ -0,0 +1,161 @@ +package encoder_test + +import ( + "encoding/binary" + "fmt" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/microsoft/typescript-go/internal/api/encoder" + "github.com/microsoft/typescript-go/internal/ast" + "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/testutil/baseline" + "gotest.tools/v3/assert" +) + +func TestEncodeSourceFile(t *testing.T) { + t.Parallel() + sourceFile := parser.ParseSourceFile(ast.SourceFileParseOptions{ + FileName: "/test.ts", + Path: "/test.ts", + }, "import { bar } from \"bar\";\nexport function foo(a: string, b: string): any {}\nfoo();", core.ScriptKindTS) + t.Run("baseline", func(t *testing.T) { + t.Parallel() + buf, _, err := encoder.EncodeSourceFile(sourceFile) + assert.NilError(t, err) + + str := formatEncodedSourceFile(buf) + baseline.Run(t, "encodeSourceFile.txt", str, baseline.Options{ + Subfolder: "api", + }) + }) +} + +func TestEncodeSourceFileWithUnicodeEscapes(t *testing.T) { + t.Parallel() + sourceFile := parser.ParseSourceFile(ast.SourceFileParseOptions{ + FileName: "/test.ts", + Path: "/test.ts", + }, `let a = "😃"; let b = "\ud83d\ude03"; let c = "\udc00\ud83d\ude03"; let d = "\ud83d\ud83d\ude03"`, core.ScriptKindTS) + t.Run("baseline", func(t *testing.T) { + t.Parallel() + buf, _, err := encoder.EncodeSourceFile(sourceFile) + assert.NilError(t, err) + + str := formatEncodedSourceFile(buf) + baseline.Run(t, "encodeSourceFileWithUnicodeEscapes.txt", str, baseline.Options{ + Subfolder: "api", + }) + }) +} + +func TestBuildNodeIndexTableMatchesEncode(t *testing.T) { + t.Parallel() + sourceFile := parser.ParseSourceFile(ast.SourceFileParseOptions{ + FileName: "/test.ts", + Path: "/test.ts", + }, "import { bar } from \"bar\";\nexport function foo(a: string, b: string): any {}\nfoo();", core.ScriptKindTS) + + _, encodeTable, err := encoder.EncodeSourceFile(sourceFile) + assert.NilError(t, err) + + buildTable := encoder.BuildNodeIndexTable(sourceFile) + + // Both tables should produce identical Nodes slices + assert.Equal(t, len(buildTable.Nodes), len(encodeTable.Nodes), "Nodes slice length mismatch") + + // Every index should map to the same node + for i := range encodeTable.Nodes { + assert.Equal(t, buildTable.Nodes[i], encodeTable.Nodes[i], "node mismatch at index %d", i) + } + + // GetIndex on both tables should agree for every non-nil node + for i, node := range encodeTable.Nodes { + if node == nil { + continue + } + encIdx := encodeTable.GetIndex(node) + buildIdx := buildTable.GetIndex(node) + assert.Equal(t, encIdx, uint32(i), "encodeTable.GetIndex mismatch at index %d, node kind=%s", i, node.Kind.String()) + assert.Equal(t, buildIdx, encIdx, "buildTable.GetIndex mismatch for node kind=%s", node.Kind.String()) + } +} + +func BenchmarkEncodeSourceFile(b *testing.B) { + repo.SkipIfNoTypeScriptSubmodule(b) + filePath := filepath.Join(repo.TypeScriptSubmodulePath(), "src/compiler/checker.ts") + fileContent, err := os.ReadFile(filePath) + assert.NilError(b, err) + sourceFile := parser.ParseSourceFile(ast.SourceFileParseOptions{ + FileName: "/checker.ts", + Path: "/checker.ts", + }, string(fileContent), core.ScriptKindTS) + + for b.Loop() { + _, _, err := encoder.EncodeSourceFile(sourceFile) + assert.NilError(b, err) + } +} + +func BenchmarkBuildNodeIndexTable(b *testing.B) { + repo.SkipIfNoTypeScriptSubmodule(b) + filePath := filepath.Join(repo.TypeScriptSubmodulePath(), "src/compiler/checker.ts") + fileContent, err := os.ReadFile(filePath) + assert.NilError(b, err) + sourceFile := parser.ParseSourceFile(ast.SourceFileParseOptions{ + FileName: "/checker.ts", + Path: "/checker.ts", + }, string(fileContent), core.ScriptKindTS) + + for b.Loop() { + encoder.BuildNodeIndexTable(sourceFile) + } +} + +func readUint32(buf []byte, offset int) uint32 { + return binary.LittleEndian.Uint32(buf[offset : offset+4]) +} + +func formatEncodedSourceFile(encoded []byte) string { + var result strings.Builder + var getIndent func(parentIndex uint32) string + offsetNodes := readUint32(encoded, encoder.HeaderOffsetNodes) + offsetStringOffsets := readUint32(encoded, encoder.HeaderOffsetStringOffsets) + offsetStrings := readUint32(encoded, encoder.HeaderOffsetStringData) + getIndent = func(parentIndex uint32) string { + if parentIndex == 0 { + return "" + } + return " " + getIndent(readUint32(encoded, int(offsetNodes)+int(parentIndex)*encoder.NodeSize+encoder.NodeOffsetParent)) + } + j := 1 + for i := int(offsetNodes) + encoder.NodeSize; i < len(encoded); i += encoder.NodeSize { + kind := readUint32(encoded, i+encoder.NodeOffsetKind) + pos := readUint32(encoded, i+encoder.NodeOffsetPos) + end := readUint32(encoded, i+encoder.NodeOffsetEnd) + parentIndex := readUint32(encoded, i+encoder.NodeOffsetParent) + result.WriteString(getIndent(parentIndex)) + if kind == encoder.SyntaxKindNodeList { + result.WriteString("NodeList") + } else { + result.WriteString(ast.Kind(kind).String()) + } + data := readUint32(encoded, i+encoder.NodeOffsetData) + dataType := data & encoder.NodeDataTypeMask + if ast.Kind(kind) == ast.KindIdentifier || (dataType == encoder.NodeDataTypeString) { + stringIndex := data & encoder.NodeDataStringIndexMask + strStart := readUint32(encoded, int(offsetStringOffsets+stringIndex*4)) + strEnd := readUint32(encoded, int(offsetStringOffsets+stringIndex*4)+4) + str := string(encoded[offsetStrings+strStart : offsetStrings+strEnd]) + result.WriteString(fmt.Sprintf(" \"%s\"", str)) + } + fmt.Fprintf(&result, " [%d, %d), i=%d, next=%d", pos, end, j, encoded[i+encoder.NodeOffsetNext]) + result.WriteString("\n") + j++ + } + return result.String() +} diff --git a/tools/tsgo/internal/api/encoder/stringtable.go b/tools/tsgo/internal/api/encoder/stringtable.go new file mode 100644 index 00000000..875ba906 --- /dev/null +++ b/tools/tsgo/internal/api/encoder/stringtable.go @@ -0,0 +1,68 @@ +package encoder + +import ( + "strings" + + "github.com/microsoft/typescript-go/internal/ast" +) + +type stringTable struct { + fileText string + otherStrings *strings.Builder + // offsets are pos/end pairs + offsets []uint32 +} + +func newStringTable(fileText string, stringCount int) *stringTable { + builder := &strings.Builder{} + return &stringTable{ + fileText: fileText, + otherStrings: builder, + offsets: make([]uint32, 0, stringCount*2), + } +} + +func (t *stringTable) add(text string, kind ast.Kind, pos int, end int) uint32 { + index := uint32(len(t.offsets)) + if kind == ast.KindSourceFile { + t.offsets = append(t.offsets, uint32(pos), uint32(end)) + return index + } + length := len(text) + if end-pos > 0 && end <= len(t.fileText) { + // pos includes leading trivia, but we can usually infer the actual start of the + // string from the kind and end + endOffset := 0 + if kind == ast.KindStringLiteral || kind == ast.KindTemplateTail || kind == ast.KindNoSubstitutionTemplateLiteral { + endOffset = 1 + } + end = end - endOffset + start := end - length + fileSlice := t.fileText[start:end] + if fileSlice == text { + t.offsets = append(t.offsets, uint32(start), uint32(end)) + return index + } + } + // no exact match, so we need to add it to the string table + offset := len(t.fileText) + t.otherStrings.Len() + t.otherStrings.WriteString(text) + t.offsets = append(t.offsets, uint32(offset), uint32(offset+length)) + return index +} + +func (t *stringTable) encode() []byte { + result := make([]byte, 0, t.encodedLength()) + result = appendUint32s(result, t.offsets...) + result = append(result, t.fileText...) + result = append(result, t.otherStrings.String()...) + return result +} + +func (t *stringTable) stringLength() int { + return len(t.fileText) + t.otherStrings.Len() +} + +func (t *stringTable) encodedLength() int { + return len(t.offsets)*4 + len(t.fileText) + t.otherStrings.Len() +} diff --git a/tools/tsgo/internal/api/encoder/testmain_test.go b/tools/tsgo/internal/api/encoder/testmain_test.go new file mode 100644 index 00000000..554853a7 --- /dev/null +++ b/tools/tsgo/internal/api/encoder/testmain_test.go @@ -0,0 +1,14 @@ +package encoder_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/core" + "github.com/microsoft/typescript-go/internal/testutil/baseline" +) + +func TestMain(m *testing.M) { + core.ApplyDebugStackLimit() + defer baseline.Track()() + m.Run() +} diff --git a/tools/tsgo/internal/api/proto.go b/tools/tsgo/internal/api/proto.go new file mode 100644 index 00000000..b1cd3335 --- /dev/null +++ b/tools/tsgo/internal/api/proto.go @@ -0,0 +1,1225 @@ +package api + +import ( + "errors" + "fmt" + + "github.com/microsoft/typescript-go/internal/ast" + "github.com/microsoft/typescript-go/internal/checker" + "github.com/microsoft/typescript-go/internal/core" + "github.com/microsoft/typescript-go/internal/diagnostics" + "github.com/microsoft/typescript-go/internal/jsnum" + "github.com/microsoft/typescript-go/internal/json" + "github.com/microsoft/typescript-go/internal/locale" + "github.com/microsoft/typescript-go/internal/ls/lsconv" + "github.com/microsoft/typescript-go/internal/lsp/lsproto" + "github.com/microsoft/typescript-go/internal/project" + "github.com/microsoft/typescript-go/internal/tspath" +) + +var ( + ErrInvalidRequest = errors.New("api: invalid request") + ErrClientError = errors.New("api: client error") +) + +type Method string + +type ( + SnapshotID uint64 + ProjectID string + SymbolID uint64 + TypeID uint32 + SignatureID uint64 + NodeHandle string +) + +func ProjectHandle(p *project.Project) ProjectID { + return ProjectID(p.ID()) +} + +func SymbolHandle(symbol *ast.Symbol) SymbolID { + return SymbolID(ast.GetSymbolId(symbol)) +} + +func TypeHandle(t *checker.Type) TypeID { + return TypeID(t.Id()) +} + +func SignatureHandle(sig *checker.Signature) SignatureID { + return SignatureID(sig.Id()) +} + +func parseProjectHandle(handle ProjectID) tspath.Path { + return tspath.Path(handle) +} + +const ( + MethodRelease Method = "release" + + // MethodGetServerTiming retrieves the server's collected per-request + // processing-time totals and recent-request ring buffer. It is handled by + // the connection itself (not the session) and is not recorded in the timing + // it reports. + MethodGetServerTiming Method = "getServerTiming" + + // MethodResetServerTiming clears the server's collected timing totals and + // recent-request ring buffer. Like MethodGetServerTiming, it is handled by + // the connection itself and is not recorded. + MethodResetServerTiming Method = "resetServerTiming" + + MethodInitialize Method = "initialize" + MethodUpdateSnapshot Method = "updateSnapshot" + MethodParseConfigFile Method = "parseConfigFile" + MethodGetDefaultProjectForFile Method = "getDefaultProjectForFile" + MethodGetSymbolAtPosition Method = "getSymbolAtPosition" + MethodGetSymbolsAtPositions Method = "getSymbolsAtPositions" + MethodGetSymbolAtLocation Method = "getSymbolAtLocation" + MethodGetSymbolsAtLocations Method = "getSymbolsAtLocations" + MethodGetTypeOfSymbol Method = "getTypeOfSymbol" + MethodGetTypesOfSymbols Method = "getTypesOfSymbols" + MethodGetDeclaredTypeOfSymbol Method = "getDeclaredTypeOfSymbol" + MethodGetSourceFile Method = "getSourceFile" + MethodGetSourceFileNames Method = "getSourceFileNames" + MethodGetSourceFileMetadata Method = "getSourceFileMetadata" + MethodResolveName Method = "resolveName" + MethodGetSignaturesOfType Method = "getSignaturesOfType" + MethodGetResolvedSignature Method = "getResolvedSignature" + MethodGetTypeAtLocation Method = "getTypeAtLocation" + MethodGetTypeAtLocations Method = "getTypeAtLocations" + MethodGetTypeAtPosition Method = "getTypeAtPosition" + MethodGetTypesAtPositions Method = "getTypesAtPositions" + + // Symbol sub-property methods + MethodGetParentOfSymbol Method = "getParentOfSymbol" + MethodGetMembersOfSymbol Method = "getMembersOfSymbol" + MethodGetExportsOfSymbol Method = "getExportsOfSymbol" + MethodGetExportSymbolOfSymbol Method = "getExportSymbolOfSymbol" + + // Type sub-property methods + MethodGetSymbolOfType Method = "getSymbolOfType" + MethodGetTargetOfType Method = "getTargetOfType" + MethodGetFreshTypeOfType Method = "getFreshTypeOfType" + MethodGetRegularTypeOfType Method = "getRegularTypeOfType" + MethodGetTypesOfType Method = "getTypesOfType" + MethodGetTypeParametersOfType Method = "getTypeParametersOfType" + MethodGetOuterTypeParametersOfType Method = "getOuterTypeParametersOfType" + MethodGetLocalTypeParametersOfType Method = "getLocalTypeParametersOfType" + MethodGetAliasTypeArgumentsOfType Method = "getAliasTypeArgumentsOfType" + MethodGetAliasSymbolOfType Method = "getAliasSymbolOfType" + MethodGetObjectTypeOfType Method = "getObjectTypeOfType" + MethodGetIndexTypeOfType Method = "getIndexTypeOfType" + MethodGetCheckTypeOfType Method = "getCheckTypeOfType" + MethodGetExtendsTypeOfType Method = "getExtendsTypeOfType" + MethodGetBaseTypeOfType Method = "getBaseTypeOfType" + MethodGetConstraintOfType Method = "getConstraintOfType" + + // Signature sub-property methods + MethodGetTypeParametersOfSignature Method = "getTypeParametersOfSignature" + MethodGetParametersOfSignature Method = "getParametersOfSignature" + MethodGetThisParameterOfSignature Method = "getThisParameterOfSignature" + MethodGetTargetOfSignature Method = "getTargetOfSignature" + + // Checker methods + MethodGetContextualType Method = "getContextualType" + MethodGetBaseTypeOfLiteralType Method = "getBaseTypeOfLiteralType" + MethodGetNonNullableType Method = "getNonNullableType" + MethodGetTypeFromTypeNode Method = "getTypeFromTypeNode" + MethodGetWidenedType Method = "getWidenedType" + MethodGetParameterType Method = "getParameterType" + MethodIsArrayLikeType Method = "isArrayLikeType" + MethodIsTypeAssignableTo Method = "isTypeAssignableTo" + MethodGetShorthandAssignmentValueSymbol Method = "getShorthandAssignmentValueSymbol" + MethodGetTypeOfSymbolAtLocation Method = "getTypeOfSymbolAtLocation" + MethodTypeToTypeNode Method = "typeToTypeNode" + MethodSignatureToSignatureDeclaration Method = "signatureToSignatureDeclaration" + MethodTypeToString Method = "typeToString" + MethodIsContextSensitive Method = "isContextSensitive" + MethodGetReturnTypeOfSignature Method = "getReturnTypeOfSignature" + MethodGetRestTypeOfSignature Method = "getRestTypeOfSignature" + MethodGetTypePredicateOfSignature Method = "getTypePredicateOfSignature" + MethodGetBaseTypes Method = "getBaseTypes" + MethodGetPropertiesOfType Method = "getPropertiesOfType" + MethodGetApparentType Method = "getApparentType" + MethodGetPropertyOfType Method = "getPropertyOfType" + MethodGetIndexInfosOfType Method = "getIndexInfosOfType" + MethodGetConstraintOfTypeParameter Method = "getConstraintOfTypeParameter" + MethodGetBaseConstraintOfType Method = "getBaseConstraintOfType" + MethodGetTypeArguments Method = "getTypeArguments" + MethodGetTrueTypeOfConditionalType Method = "getTrueTypeOfConditionalType" + MethodGetFalseTypeOfConditionalType Method = "getFalseTypeOfConditionalType" + MethodGetConstantValue Method = "getConstantValue" + MethodGetSignatureFromDeclaration Method = "getSignatureFromDeclaration" + MethodGetExportSpecifierLocalTarget Method = "getExportSpecifierLocalTargetSymbol" + MethodGetAliasedSymbol Method = "getAliasedSymbol" + MethodGetImmediateAliasedSymbol Method = "getImmediateAliasedSymbol" + MethodGetExportsOfModule Method = "getExportsOfModule" + MethodGetMemberInModuleExports Method = "getMemberInModuleExports" + MethodGetJSDocTags Method = "getJsDocTags" + MethodGetDocumentationComment Method = "getDocumentationComment" + MethodIsArrayType Method = "isArrayType" + MethodIsTupleType Method = "isTupleType" + + // Reference methods + MethodGetReferencesToSymbolInFile Method = "getReferencesToSymbolInFile" + MethodGetReferencedSymbolsForNode Method = "getReferencedSymbolsForNode" + MethodGetSignatureUsages Method = "getSignatureUsages" + + // Language service methods + MethodGetCompletionsAtPosition Method = "getCompletionsAtPosition" + + // Diagnostic methods + MethodGetSyntacticDiagnostics Method = "getSyntacticDiagnostics" + MethodGetBindDiagnostics Method = "getBindDiagnostics" + MethodGetSemanticDiagnostics Method = "getSemanticDiagnostics" + MethodGetSuggestionDiagnostics Method = "getSuggestionDiagnostics" + MethodGetDeclarationDiagnostics Method = "getDeclarationDiagnostics" + MethodGetProgramDiagnostics Method = "getProgramDiagnostics" + MethodGetGlobalDiagnostics Method = "getGlobalDiagnostics" + MethodGetConfigFileParsingDiagnostics Method = "getConfigFileParsingDiagnostics" + + // Emitter methods + MethodPrintNode Method = "printNode" + + // Intrinsic type getters + MethodGetAnyType Method = "getAnyType" + MethodGetStringType Method = "getStringType" + MethodGetNumberType Method = "getNumberType" + MethodGetBooleanType Method = "getBooleanType" + MethodGetVoidType Method = "getVoidType" + MethodGetUndefinedType Method = "getUndefinedType" + MethodGetNullType Method = "getNullType" + MethodGetNeverType Method = "getNeverType" + MethodGetUnknownType Method = "getUnknownType" + MethodGetBigIntType Method = "getBigIntType" + MethodGetESSymbolType Method = "getESSymbolType" + + // Well-known per-checker symbols + MethodGetWellKnownSymbols Method = "getWellKnownSymbols" + + // Well-known per-checker signatures + MethodGetWellKnownSignatures Method = "getWellKnownSignatures" + + // Profiling methods + MethodStartCPUProfile Method = "startCPUProfile" + MethodStopCPUProfile Method = "stopCPUProfile" + MethodSaveHeapProfile Method = "saveHeapProfile" +) + +// InitializeResponse is returned by the initialize method. +type InitializeResponse struct { + // UseCaseSensitiveFileNames indicates whether the host file system is case-sensitive. + UseCaseSensitiveFileNames bool `json:"useCaseSensitiveFileNames"` + // CurrentDirectory is the server's current working directory. + CurrentDirectory string `json:"currentDirectory"` +} + +// DocumentIdentifier identifies a document by either a file name (plain string) or a URI object. +// On the wire it is string | { uri: string }. +type DocumentIdentifier struct { + FileName string `json:"fileName,omitempty"` + URI lsproto.DocumentUri `json:"uri,omitempty"` +} + +var _ json.UnmarshalerFrom = (*DocumentIdentifier)(nil) + +func (d *DocumentIdentifier) UnmarshalJSONFrom(dec *json.Decoder) error { + // Try reading as a plain string first + tok, err := dec.ReadToken() + if err != nil { + return err + } + switch tok.Kind() { + case '"': + d.FileName = tok.String() + return nil + case '{': + // Read the object fields + for dec.PeekKind() != '}' { + key, err := dec.ReadToken() + if err != nil { + return err + } + isURI := key.String() == "uri" + val, err := dec.ReadToken() + if err != nil { + return err + } + if isURI { + d.URI = lsproto.DocumentUri(val.String()) + } + } + // Consume the closing brace + if _, err := dec.ReadToken(); err != nil { + return err + } + return nil + default: + return fmt.Errorf("DocumentIdentifier: expected string or object, got %v", tok.Kind()) + } +} + +func (d DocumentIdentifier) ToFileName() string { + if d.URI != "" { + return d.URI.FileName() + } + return d.FileName +} + +// ToURI returns the document URI for this identifier. An explicitly provided URI +// is returned as-is; a file name is first normalized to an absolute path against +// cwd before being converted to a URI. +func (d DocumentIdentifier) ToURI(cwd string) lsproto.DocumentUri { + if d.URI != "" { + return d.URI + } + return lsconv.FileNameToDocumentURI(tspath.GetNormalizedAbsolutePath(d.FileName, cwd)) +} + +func (d DocumentIdentifier) ToAbsoluteFileName(cwd string) string { + if d.URI != "" { + return d.URI.FileName() + } + return tspath.GetNormalizedAbsolutePath(d.FileName, cwd) +} + +func (d DocumentIdentifier) String() string { + if d.URI != "" { + return string(d.URI) + } + return d.FileName +} + +// APIFileChangeSummary lists documents that have been changed, created, or deleted. +type APIFileChangeSummary struct { + Changed []DocumentIdentifier `json:"changed,omitempty"` + Created []DocumentIdentifier `json:"created,omitempty"` + Deleted []DocumentIdentifier `json:"deleted,omitempty"` +} + +// APIFileChanges describes file changes to apply when updating a snapshot. +// Either InvalidateAll is true (discard all caches) or Changed/Created/Deleted +// list individual documents. +type APIFileChanges struct { + InvalidateAll bool `json:"invalidateAll,omitempty"` + Changed []DocumentIdentifier `json:"changed,omitempty"` + Created []DocumentIdentifier `json:"created,omitempty"` + Deleted []DocumentIdentifier `json:"deleted,omitempty"` +} + +// UpdateSnapshotParams are the parameters for creating a new snapshot. +// All fields are optional. With no fields set, the server adopts the latest LSP state. +type UpdateSnapshotParams struct { + // OpenProjects lists tsconfig.json files to open/load in the new snapshot. + // Opens are ref-counted and persist across snapshots until closed. + OpenProjects []DocumentIdentifier `json:"openProjects,omitempty"` + // CloseProjects lists tsconfig.json files to release in the new snapshot. + // A project is only unloaded once every API client that opened it closes it. + CloseProjects []DocumentIdentifier `json:"closeProjects,omitempty"` + // FileChanges describes file system changes since the last snapshot. + FileChanges *APIFileChanges `json:"fileChanges,omitempty"` + // OpenFiles lists files to keep open for the API client, mirroring LSP's + // textDocument/didOpen. For each file, ancestor directories are searched for a + // tsconfig that contains it; if found, that configured project is loaded and + // becomes the file's default project. Otherwise the file is loaded into the + // inferred project (e.g. a node_modules d.ts not in any project's import graph). + // Opens persist across snapshots until the file is closed. + OpenFiles []DocumentIdentifier `json:"openFiles,omitempty"` + // CloseFiles lists files to release in the new snapshot. A file is only fully + // closed once every API client that opened it closes it. + CloseFiles []DocumentIdentifier `json:"closeFiles,omitempty"` +} + +// ProjectFileChanges describes what source files changed within a single project. +type ProjectFileChanges struct { + // ChangedFiles lists source file paths whose content differs. + ChangedFiles []tspath.Path `json:"changedFiles,omitempty"` + // DeletedFiles lists source file paths removed from the project's program. + DeletedFiles []tspath.Path `json:"deletedFiles,omitempty"` +} + +// SnapshotChanges describes what changed between the previous latest snapshot +// and the newly created snapshot. Changes are reported per-project so clients +// can track cache refs at the (snapshot, project) level. +type SnapshotChanges struct { + // ChangedProjects maps project handles to the file changes within that project. + // Projects not listed here (and not in RemovedProjects) are unchanged. + ChangedProjects map[ProjectID]*ProjectFileChanges `json:"changedProjects,omitempty"` + // RemovedProjects lists project handles that were present in the previous + // snapshot but absent from the new one. + RemovedProjects []ProjectID `json:"removedProjects,omitempty"` +} + +// UpdateSnapshotResponse is returned by updateSnapshot. +type UpdateSnapshotResponse struct { + // Snapshot is the handle for the newly created snapshot. + Snapshot SnapshotID `json:"snapshot"` + // Projects is the list of projects in the snapshot. + Projects []*ProjectResponse `json:"projects"` + // Changes describes source file differences from the previous snapshot. + // Nil for the first snapshot in a session. + Changes *SnapshotChanges `json:"changes,omitempty"` +} + +var unmarshalers = map[Method]func([]byte) (any, error){ + MethodRelease: unmarshallerFor[ReleaseParams], + MethodInitialize: noParams, + MethodUpdateSnapshot: unmarshallerFor[UpdateSnapshotParams], + MethodParseConfigFile: unmarshallerFor[ParseConfigFileParams], + MethodGetDefaultProjectForFile: unmarshallerFor[GetDefaultProjectForFileParams], + MethodGetSourceFile: unmarshallerFor[GetSourceFileParams], + MethodGetSourceFileNames: unmarshallerFor[GetSourceFileNamesParams], + MethodGetSourceFileMetadata: unmarshallerFor[GetSourceFileParams], + MethodGetSymbolAtPosition: unmarshallerFor[GetSymbolAtPositionParams], + MethodGetSymbolsAtPositions: unmarshallerFor[GetSymbolsAtPositionsParams], + MethodGetSymbolAtLocation: unmarshallerFor[GetSymbolAtLocationParams], + MethodGetSymbolsAtLocations: unmarshallerFor[GetSymbolsAtLocationsParams], + MethodGetTypeOfSymbol: unmarshallerFor[GetTypeOfSymbolParams], + MethodGetTypesOfSymbols: unmarshallerFor[GetTypesOfSymbolsParams], + MethodGetDeclaredTypeOfSymbol: unmarshallerFor[GetTypeOfSymbolParams], + MethodResolveName: unmarshallerFor[ResolveNameParams], + MethodGetSignaturesOfType: unmarshallerFor[GetSignaturesOfTypeParams], + MethodGetResolvedSignature: unmarshallerFor[GetResolvedSignatureParams], + MethodGetTypeAtLocation: unmarshallerFor[GetTypeAtLocationParams], + MethodGetTypeAtLocations: unmarshallerFor[GetTypeAtLocationsParams], + MethodGetTypeAtPosition: unmarshallerFor[GetTypeAtPositionParams], + MethodGetTypesAtPositions: unmarshallerFor[GetTypesAtPositionsParams], + + MethodGetParentOfSymbol: unmarshallerFor[GetSymbolPropertyParams], + MethodGetMembersOfSymbol: unmarshallerFor[GetSymbolPropertyParams], + MethodGetExportsOfSymbol: unmarshallerFor[GetSymbolPropertyParams], + MethodGetExportSymbolOfSymbol: unmarshallerFor[GetSymbolPropertyParams], + + MethodGetSymbolOfType: unmarshallerFor[GetTypePropertyParams], + MethodGetTargetOfType: unmarshallerFor[GetTypePropertyParams], + MethodGetFreshTypeOfType: unmarshallerFor[GetTypePropertyParams], + MethodGetRegularTypeOfType: unmarshallerFor[GetTypePropertyParams], + MethodGetTypesOfType: unmarshallerFor[GetTypePropertyParams], + MethodGetTypeParametersOfType: unmarshallerFor[GetTypePropertyParams], + MethodGetOuterTypeParametersOfType: unmarshallerFor[GetTypePropertyParams], + MethodGetLocalTypeParametersOfType: unmarshallerFor[GetTypePropertyParams], + MethodGetAliasTypeArgumentsOfType: unmarshallerFor[GetTypePropertyParams], + MethodGetAliasSymbolOfType: unmarshallerFor[GetTypePropertyParams], + MethodGetObjectTypeOfType: unmarshallerFor[GetTypePropertyParams], + MethodGetIndexTypeOfType: unmarshallerFor[GetTypePropertyParams], + MethodGetCheckTypeOfType: unmarshallerFor[GetTypePropertyParams], + MethodGetExtendsTypeOfType: unmarshallerFor[GetTypePropertyParams], + MethodGetBaseTypeOfType: unmarshallerFor[GetTypePropertyParams], + MethodGetConstraintOfType: unmarshallerFor[GetTypePropertyParams], + MethodGetTrueTypeOfConditionalType: unmarshallerFor[GetTypePropertyParams], + MethodGetFalseTypeOfConditionalType: unmarshallerFor[GetTypePropertyParams], + + MethodGetTypeParametersOfSignature: unmarshallerFor[GetSignaturePropertyParams], + MethodGetParametersOfSignature: unmarshallerFor[GetSignaturePropertyParams], + MethodGetThisParameterOfSignature: unmarshallerFor[GetSignaturePropertyParams], + MethodGetTargetOfSignature: unmarshallerFor[GetSignaturePropertyParams], + + MethodGetContextualType: unmarshallerFor[GetContextualTypeParams], + MethodGetBaseTypeOfLiteralType: unmarshallerFor[GetBaseTypeOfLiteralTypeParams], + MethodGetNonNullableType: unmarshallerFor[GetNonNullableTypeParams], + MethodGetTypeFromTypeNode: unmarshallerFor[GetTypeFromTypeNodeParams], + MethodGetWidenedType: unmarshallerFor[GetWidenedTypeParams], + MethodGetParameterType: unmarshallerFor[GetParameterTypeParams], + MethodIsArrayLikeType: unmarshallerFor[IsArrayLikeTypeParams], + MethodIsTypeAssignableTo: unmarshallerFor[IsTypeAssignableToParams], + MethodGetShorthandAssignmentValueSymbol: unmarshallerFor[GetTypeAtLocationParams], + MethodGetTypeOfSymbolAtLocation: unmarshallerFor[GetTypeOfSymbolAtLocationParams], + MethodTypeToTypeNode: unmarshallerFor[TypeToTypeNodeParams], + MethodSignatureToSignatureDeclaration: unmarshallerFor[SignatureToSignatureDeclarationParams], + MethodTypeToString: unmarshallerFor[TypeToTypeNodeParams], + MethodIsContextSensitive: unmarshallerFor[GetContextualTypeParams], + MethodGetReturnTypeOfSignature: unmarshallerFor[CheckerSignatureParams], + MethodGetRestTypeOfSignature: unmarshallerFor[CheckerSignatureParams], + MethodGetTypePredicateOfSignature: unmarshallerFor[CheckerSignatureParams], + MethodGetBaseTypes: unmarshallerFor[CheckerTypeParams], + MethodGetPropertiesOfType: unmarshallerFor[CheckerTypeParams], + MethodGetApparentType: unmarshallerFor[CheckerTypeParams], + MethodGetPropertyOfType: unmarshallerFor[GetPropertyOfTypeParams], + MethodGetIndexInfosOfType: unmarshallerFor[CheckerTypeParams], + MethodGetConstraintOfTypeParameter: unmarshallerFor[CheckerTypeParams], + MethodGetBaseConstraintOfType: unmarshallerFor[CheckerTypeParams], + MethodGetTypeArguments: unmarshallerFor[CheckerTypeParams], + MethodGetConstantValue: unmarshallerFor[CheckerNodeParams], + MethodGetSignatureFromDeclaration: unmarshallerFor[CheckerNodeParams], + MethodGetExportSpecifierLocalTarget: unmarshallerFor[CheckerNodeParams], + MethodGetAliasedSymbol: unmarshallerFor[CheckerSymbolParams], + MethodGetImmediateAliasedSymbol: unmarshallerFor[CheckerSymbolParams], + MethodGetExportsOfModule: unmarshallerFor[CheckerSymbolParams], + MethodGetMemberInModuleExports: unmarshallerFor[GetMemberInModuleExportsParams], + MethodGetJSDocTags: unmarshallerFor[CheckerSymbolParams], + MethodGetDocumentationComment: unmarshallerFor[CheckerSymbolParams], + MethodIsArrayType: unmarshallerFor[CheckerTypeParams], + MethodIsTupleType: unmarshallerFor[CheckerTypeParams], + MethodGetReferencesToSymbolInFile: unmarshallerFor[GetReferencesToSymbolInFileParams], + MethodGetReferencedSymbolsForNode: unmarshallerFor[GetReferencedSymbolsForNodeParams], + MethodGetSignatureUsages: unmarshallerFor[GetSignatureUsagesParams], + MethodGetCompletionsAtPosition: unmarshallerFor[GetCompletionsAtPositionParams], + MethodPrintNode: unmarshallerFor[PrintNodeParams], + MethodGetAnyType: unmarshallerFor[GetIntrinsicTypeParams], + MethodGetStringType: unmarshallerFor[GetIntrinsicTypeParams], + MethodGetNumberType: unmarshallerFor[GetIntrinsicTypeParams], + MethodGetBooleanType: unmarshallerFor[GetIntrinsicTypeParams], + MethodGetVoidType: unmarshallerFor[GetIntrinsicTypeParams], + MethodGetUndefinedType: unmarshallerFor[GetIntrinsicTypeParams], + MethodGetNullType: unmarshallerFor[GetIntrinsicTypeParams], + MethodGetNeverType: unmarshallerFor[GetIntrinsicTypeParams], + MethodGetUnknownType: unmarshallerFor[GetIntrinsicTypeParams], + MethodGetBigIntType: unmarshallerFor[GetIntrinsicTypeParams], + MethodGetESSymbolType: unmarshallerFor[GetIntrinsicTypeParams], + MethodGetWellKnownSymbols: unmarshallerFor[GetIntrinsicTypeParams], + MethodGetWellKnownSignatures: unmarshallerFor[GetIntrinsicTypeParams], + MethodGetSyntacticDiagnostics: unmarshallerFor[GetDiagnosticsParams], + MethodGetBindDiagnostics: unmarshallerFor[GetDiagnosticsParams], + MethodGetSemanticDiagnostics: unmarshallerFor[GetDiagnosticsParams], + MethodGetSuggestionDiagnostics: unmarshallerFor[GetDiagnosticsParams], + MethodGetDeclarationDiagnostics: unmarshallerFor[GetDiagnosticsParams], + MethodGetProgramDiagnostics: unmarshallerFor[GetProjectDiagnosticsParams], + MethodGetGlobalDiagnostics: unmarshallerFor[GetProjectDiagnosticsParams], + MethodGetConfigFileParsingDiagnostics: unmarshallerFor[GetProjectDiagnosticsParams], + MethodStartCPUProfile: unmarshallerFor[ProfileParams], + MethodStopCPUProfile: noParams, + MethodSaveHeapProfile: unmarshallerFor[ProfileParams], +} + +type ParseConfigFileParams struct { + File DocumentIdentifier `json:"file"` +} + +// ReleaseParams are the parameters for the release method. +type ReleaseParams struct { + Snapshot SnapshotID `json:"snapshot"` +} + +type ProfileParams struct { + Dir string `json:"dir"` +} + +type ProfileResult struct { + File string `json:"file"` +} + +type ConfigFileResponse struct { + FileNames []string `json:"fileNames"` + Options *core.CompilerOptions `json:"options"` +} + +type GetDefaultProjectForFileParams struct { + Snapshot SnapshotID `json:"snapshot"` + File DocumentIdentifier `json:"file"` +} + +type ProjectResponse struct { + Id ProjectID `json:"id"` + ConfigFileName string `json:"configFileName"` + RootFiles []string `json:"rootFiles"` + CompilerOptions *core.CompilerOptions `json:"compilerOptions"` +} + +func NewProjectResponse(p *project.Project) *ProjectResponse { + if p == nil || p.CommandLine == nil { + panic("NewProjectResponse called with unloaded project") + } + return &ProjectResponse{ + Id: ProjectHandle(p), + ConfigFileName: p.Name(), + RootFiles: p.CommandLine.FileNames(), + CompilerOptions: p.CommandLine.CompilerOptions(), + } +} + +type GetSymbolAtPositionParams struct { + Snapshot SnapshotID `json:"snapshot"` + Project ProjectID `json:"project"` + File DocumentIdentifier `json:"file"` + Position uint32 `json:"position"` +} + +type GetSymbolsAtPositionsParams struct { + Snapshot SnapshotID `json:"snapshot"` + Project ProjectID `json:"project"` + File DocumentIdentifier `json:"file"` + Positions []uint32 `json:"positions"` +} + +type GetSymbolAtLocationParams struct { + Snapshot SnapshotID `json:"snapshot"` + Project ProjectID `json:"project"` + Location NodeHandle `json:"location"` +} + +type GetSymbolsAtLocationsParams struct { + Snapshot SnapshotID `json:"snapshot"` + Project ProjectID `json:"project"` + Locations []NodeHandle `json:"locations"` +} + +type SymbolResponse struct { + Id SymbolID `json:"id"` + Project ProjectID `json:"project"` + Name string `json:"name"` + Flags uint32 `json:"flags"` + CheckFlags uint32 `json:"checkFlags"` + Declarations []NodeHandle `json:"declarations,omitempty"` + ValueDeclaration NodeHandle `json:"valueDeclaration,omitempty"` + Parent SymbolID `json:"parent,omitzero"` + ExportSymbol SymbolID `json:"exportSymbol,omitzero"` +} + +func symbolHandles(symbols []*ast.Symbol) []SymbolID { + if len(symbols) == 0 { + return nil + } + handles := make([]SymbolID, len(symbols)) + for i, t := range symbols { + handles[i] = SymbolHandle(t) + } + return handles +} + +type GetTypeOfSymbolParams struct { + Snapshot SnapshotID `json:"snapshot"` + Project ProjectID `json:"project"` + Symbol SymbolID `json:"symbol"` +} + +type GetTypesOfSymbolsParams struct { + Snapshot SnapshotID `json:"snapshot"` + Project ProjectID `json:"project"` + Symbols []SymbolID `json:"symbols"` +} + +type TypeResponse struct { + Id TypeID `json:"id"` + Flags uint32 `json:"flags"` + ObjectFlags uint32 `json:"objectFlags,omitempty"` + + // LiteralType data + Value any `json:"value"` + + // ObjectType / TypeReference / StringMappingType / IndexType target + Target TypeID `json:"target,omitzero"` + + // InterfaceType type parameters + TypeParameters []TypeID `json:"typeParameters,omitempty"` + OuterTypeParameters []TypeID `json:"outerTypeParameters,omitempty"` + LocalTypeParameters []TypeID `json:"localTypeParameters,omitempty"` + + // TupleType data + ElementFlags []checker.ElementFlags `json:"elementFlags,omitempty"` + FixedLength *int `json:"fixedLength,omitempty"` + TupleReadonly *bool `json:"readonly,omitempty"` + + // IndexedAccessType data + ObjectType TypeID `json:"objectType,omitzero"` + IndexType TypeID `json:"indexType,omitzero"` + + // ConditionalType data + CheckType TypeID `json:"checkType,omitzero"` + ExtendsType TypeID `json:"extendsType,omitzero"` + + // SubstitutionType data + BaseType TypeID `json:"baseType,omitzero"` + SubstConstraint TypeID `json:"substConstraint,omitzero"` + + // TemplateLiteralType text segments + Texts []string `json:"texts,omitempty"` + + // FreshableType data (LiteralType and computed enum types) + FreshType TypeID `json:"freshType,omitzero"` + RegularType TypeID `json:"regularType,omitzero"` + + // TypeParameter data + IsThisType bool `json:"isThisType,omitempty"` + + // IntrinsicType data + IntrinsicName string `json:"intrinsicName,omitempty"` + + // TypeAlias data + AliasTypeArguments []TypeID `json:"aliasTypeArguments,omitempty"` + AliasSymbol SymbolID `json:"aliasSymbol,omitzero"` + + // Symbol associated with structured types + Symbol SymbolID `json:"symbol,omitzero"` +} + +func newTypeResponse(t *checker.Type, id TypeID) *TypeResponse { + resp := &TypeResponse{ + Id: id, + Flags: uint32(t.Flags()), + } + + if t.Symbol() != nil { + resp.Symbol = SymbolHandle(t.Symbol()) + } + + if t.Alias() != nil { + resp.AliasTypeArguments = typeHandles(t.Alias().TypeArguments()) + if t.Alias().Symbol() != nil { + resp.AliasSymbol = SymbolHandle(t.Alias().Symbol()) + } + } + + switch flags := t.Flags(); { + case flags&checker.TypeFlagsFreshable != 0: + lit := t.AsLiteralType() + if flags&checker.TypeFlagsLiteral != 0 { + resp.Value = literalValueToJSON(lit.Value()) + } + if lit.FreshType() != nil { + resp.FreshType = TypeHandle(lit.FreshType()) + } + if lit.RegularType() != nil { + resp.RegularType = TypeHandle(lit.RegularType()) + } + case flags&checker.TypeFlagsObject != 0: + resp.ObjectFlags = uint32(t.ObjectFlags()) + objectFlags := t.ObjectFlags() + if objectFlags&checker.ObjectFlagsReference != 0 { + var ref *checker.TypeReference + if objectFlags&checker.ObjectFlagsTuple != 0 { + tuple := t.AsTupleType() + ref = tuple.AsTypeReference() + resp.ElementFlags = tuple.ElementFlags() + fixedLen := tuple.FixedLength() + resp.FixedLength = &fixedLen + isReadonly := tuple.IsReadonly() + resp.TupleReadonly = &isReadonly + } else { + ref = t.AsTypeReference() + } + if ref.Target() != nil { + resp.Target = TypeHandle(ref.Target()) + } + } + if objectFlags&checker.ObjectFlagsClassOrInterface != 0 { + iface := t.AsInterfaceType() + resp.TypeParameters = typeHandles(iface.TypeParameters()) + resp.OuterTypeParameters = typeHandles(iface.OuterTypeParameters()) + resp.LocalTypeParameters = typeHandles(iface.LocalTypeParameters()) + } + case flags&checker.TypeFlagsUnionOrIntersection != 0: + // types omitted; fetched via separate request + case flags&checker.TypeFlagsIndex != 0: + resp.Target = TypeHandle(t.AsIndexType().Target()) + case flags&checker.TypeFlagsIndexedAccess != 0: + data := t.AsIndexedAccessType() + resp.ObjectType = TypeHandle(data.ObjectType()) + resp.IndexType = TypeHandle(data.IndexType()) + case flags&checker.TypeFlagsConditional != 0: + data := t.AsConditionalType() + resp.CheckType = TypeHandle(data.CheckType()) + resp.ExtendsType = TypeHandle(data.ExtendsType()) + case flags&checker.TypeFlagsSubstitution != 0: + data := t.AsSubstitutionType() + resp.BaseType = TypeHandle(data.BaseType()) + resp.SubstConstraint = TypeHandle(data.SubstConstraint()) + case flags&checker.TypeFlagsTemplateLiteral != 0: + tl := t.AsTemplateLiteralType() + resp.Texts = tl.Texts() + // types omitted; fetched via separate request + case flags&checker.TypeFlagsStringMapping != 0: + resp.Target = TypeHandle(t.AsStringMappingType().Target()) + case flags&checker.TypeFlagsTypeParameter != 0: + resp.IsThisType = t.AsTypeParameter().IsThisType() + case flags&checker.TypeFlagsIntrinsic != 0: + resp.IntrinsicName = t.AsIntrinsicType().IntrinsicName() + } + + return resp +} + +func typeHandles(types []*checker.Type) []TypeID { + if len(types) == 0 { + return nil + } + handles := make([]TypeID, len(types)) + for i, t := range types { + handles[i] = TypeHandle(t) + } + return handles +} + +func literalValueToJSON(value any) any { + switch v := value.(type) { + case string: + return v + case jsnum.Number: + return float64(v) + case bool: + return v + case jsnum.PseudoBigInt: + // Encode bigint literals as a signed decimal string (e.g. "-123"); the + // API client decodes this back into a real bigint. JSON has no bigint. + return v.String() + default: + return nil + } +} + +type SignatureResponse struct { + Id SignatureID `json:"id"` + Flags uint32 `json:"flags"` + Declaration NodeHandle `json:"declaration,omitempty"` + TypeParameters []TypeID `json:"typeParameters,omitempty"` + Parameters []SymbolID `json:"parameters,omitempty"` + ThisParameter SymbolID `json:"thisParameter,omitzero"` + Target SignatureID `json:"target,omitzero"` +} + +type GetSourceFileParams struct { + Snapshot SnapshotID `json:"snapshot"` + Project ProjectID `json:"project"` + File DocumentIdentifier `json:"file"` +} + +type GetSourceFileNamesParams struct { + Snapshot SnapshotID `json:"snapshot"` + Project ProjectID `json:"project"` +} + +// SourceFileMetadata carries program-stored metadata about a single source file. +type SourceFileMetadata struct { + IsDefaultLibrary bool `json:"isDefaultLibrary"` + IsFromExternalLibrary bool `json:"isFromExternalLibrary"` + PackageJsonType string `json:"packageJsonType"` + PackageJsonDirectory string `json:"packageJsonDirectory"` + ImpliedNodeFormat core.ResolutionMode `json:"impliedNodeFormat"` +} + +type ResolveNameParams struct { + Snapshot SnapshotID `json:"snapshot"` + Project ProjectID `json:"project"` + Name string `json:"name"` + Location NodeHandle `json:"location,omitempty"` // Optional: node handle for location context + File *DocumentIdentifier `json:"file,omitempty"` // Optional: file for location context (alternative to Location) + Position *uint32 `json:"position,omitempty"` // Optional: position in file for location context (with File) + Meaning uint32 `json:"meaning"` // SymbolFlags for what kind of symbol to find + ExcludeGlobals bool `json:"excludeGlobals,omitempty"` // Whether to exclude global symbols +} + +// GetTypePropertyParams is used for all type sub-property endpoints. +type GetTypePropertyParams struct { + Snapshot SnapshotID `json:"snapshot"` + Project ProjectID `json:"project"` + Type TypeID `json:"objectId"` +} + +// GetSymbolPropertyParams is used for all symbol sub-property endpoints. +type GetSymbolPropertyParams struct { + Snapshot SnapshotID `json:"snapshot"` + Project ProjectID `json:"project"` + Symbol SymbolID `json:"objectId"` +} + +// GetSignaturePropertyParams is used for all signature sub-property endpoints. +type GetSignaturePropertyParams struct { + Snapshot SnapshotID `json:"snapshot"` + Project ProjectID `json:"project"` + Signature SignatureID `json:"objectId"` +} + +// GetContextualTypeParams returns the contextual type for a node. +type GetContextualTypeParams struct { + Snapshot SnapshotID `json:"snapshot"` + Project ProjectID `json:"project"` + Location NodeHandle `json:"location"` +} + +// GetTypeOfSymbolAtLocationParams returns the narrowed type of a symbol at a specific location. +type GetTypeOfSymbolAtLocationParams struct { + Snapshot SnapshotID `json:"snapshot"` + Project ProjectID `json:"project"` + Symbol SymbolID `json:"symbol"` + Location NodeHandle `json:"location"` +} + +// GetReferencesToSymbolInFileParams are the parameters for the getReferencesToSymbolInFile method. +type GetReferencesToSymbolInFileParams struct { + Snapshot SnapshotID `json:"snapshot"` + Project ProjectID `json:"project"` + File DocumentIdentifier `json:"file"` + Symbol SymbolID `json:"symbol"` +} + +// GetReferencedSymbolsForNodeParams are the parameters for the getReferencedSymbolsForNode method. +type GetReferencedSymbolsForNodeParams struct { + Snapshot SnapshotID `json:"snapshot"` + Project ProjectID `json:"project"` + Node NodeHandle `json:"node"` + Position int `json:"position"` +} + +// ReferencedSymbolEntry represents a symbol definition and its references. +type ReferencedSymbolEntry struct { + Definition NodeHandle `json:"definition"` + Symbol *SymbolResponse `json:"symbol,omitempty"` + References []NodeHandle `json:"references"` +} + +// GetSignatureUsagesParams are the parameters for the getSignatureUsages method. +type GetSignatureUsagesParams struct { + Snapshot SnapshotID `json:"snapshot"` + Project ProjectID `json:"project"` + SignatureDecl NodeHandle `json:"signatureDecl"` +} + +// SignatureUsageResponse represents a single usage of a signature as a name-call pair. +type SignatureUsageResponse struct { + Name NodeHandle `json:"name"` + Call NodeHandle `json:"call,omitempty"` +} + +// GetCompletionsAtPositionParams are the parameters for the getCompletionsAtPosition method. +type GetCompletionsAtPositionParams struct { + Snapshot SnapshotID `json:"snapshot"` + Project ProjectID `json:"project"` + File DocumentIdentifier `json:"file"` + Position uint32 `json:"position"` + TriggerCharacter *string `json:"triggerCharacter,omitempty"` + IncludeSymbol bool `json:"includeSymbol,omitempty"` +} + +// CompletionEntryLabelDetailsResponse holds additional label display text for a completion entry. +type CompletionEntryLabelDetailsResponse struct { + Detail *string `json:"detail,omitempty"` + Description *string `json:"description,omitempty"` +} + +// CompletionEntryResponse represents a single completion item. +type CompletionEntryResponse struct { + Name string `json:"name"` + Kind uint32 `json:"kind,omitempty"` + SortText *string `json:"sortText,omitempty"` + InsertText *string `json:"insertText,omitempty"` + FilterText *string `json:"filterText,omitempty"` + Detail *string `json:"detail,omitempty"` + LabelDetails *CompletionEntryLabelDetailsResponse `json:"labelDetails,omitempty"` + Symbol *SymbolResponse `json:"symbol,omitempty"` +} + +// CompletionInfoResponse wraps a list of completion entries. +type CompletionInfoResponse struct { + IsIncomplete bool `json:"isIncomplete"` + Entries []*CompletionEntryResponse `json:"entries"` +} + +// GetIntrinsicTypeParams is used for intrinsic type getters (anyType, stringType, etc.). +type GetIntrinsicTypeParams struct { + Snapshot SnapshotID `json:"snapshot"` + Project ProjectID `json:"project"` +} + +// WellKnownSymbolsResponse carries the handle ids of the per-checker singleton +// symbols (unknown, undefined, arguments) so the client can identify them by id +// without a round-trip on every check. +type WellKnownSymbolsResponse struct { + Unknown SymbolID `json:"unknown"` + Undefined SymbolID `json:"undefined"` + Arguments SymbolID `json:"arguments"` +} + +// WellKnownSignaturesResponse carries the handle id of the per-checker singleton +// unknown signature (the signature the checker yields when a call cannot be +// resolved) so the client can identify it by id without a round-trip on every check. +type WellKnownSignaturesResponse struct { + Unknown SignatureID `json:"unknown"` +} + +// GetBaseTypeOfLiteralTypeParams returns the base type of a literal type. +type GetBaseTypeOfLiteralTypeParams struct { + Snapshot SnapshotID `json:"snapshot"` + Project ProjectID `json:"project"` + Type TypeID `json:"type"` +} + +// GetNonNullableTypeParams are the parameters for the getNonNullableType method. +type GetNonNullableTypeParams struct { + Snapshot SnapshotID `json:"snapshot"` + Project ProjectID `json:"project"` + Type TypeID `json:"type"` +} + +// GetTypeFromTypeNodeParams are the parameters for the getTypeFromTypeNode method. +type GetTypeFromTypeNodeParams struct { + Snapshot SnapshotID `json:"snapshot"` + Project ProjectID `json:"project"` + Location NodeHandle `json:"location"` +} + +// GetWidenedTypeParams are the parameters for the getWidenedType method. +type GetWidenedTypeParams struct { + Snapshot SnapshotID `json:"snapshot"` + Project ProjectID `json:"project"` + Type TypeID `json:"type"` +} + +// GetParameterTypeParams are the parameters for the getParameterType method. +type GetParameterTypeParams struct { + Snapshot SnapshotID `json:"snapshot"` + Project ProjectID `json:"project"` + Signature SignatureID `json:"signature"` + Index int32 `json:"index"` +} + +// IsArrayLikeTypeParams checks whether a type is array-like. +type IsArrayLikeTypeParams struct { + Snapshot SnapshotID `json:"snapshot"` + Project ProjectID `json:"project"` + Type TypeID `json:"type"` +} + +// IsTypeAssignableToParams checks assignability between two types. +type IsTypeAssignableToParams struct { + Snapshot SnapshotID `json:"snapshot"` + Project ProjectID `json:"project"` + Source TypeID `json:"source"` + Target TypeID `json:"target"` +} + +type GetSignaturesOfTypeParams struct { + Snapshot SnapshotID `json:"snapshot"` + Project ProjectID `json:"project"` + Type TypeID `json:"type"` + Kind int32 `json:"kind"` +} + +type GetResolvedSignatureParams struct { + Snapshot SnapshotID `json:"snapshot"` + Project ProjectID `json:"project"` + Location NodeHandle `json:"location"` +} + +type GetTypeAtLocationParams struct { + Snapshot SnapshotID `json:"snapshot"` + Project ProjectID `json:"project"` + Location NodeHandle `json:"location"` +} + +type GetTypeAtLocationsParams struct { + Snapshot SnapshotID `json:"snapshot"` + Project ProjectID `json:"project"` + Locations []NodeHandle `json:"locations"` +} + +type GetTypeAtPositionParams struct { + Snapshot SnapshotID `json:"snapshot"` + Project ProjectID `json:"project"` + File DocumentIdentifier `json:"file"` + Position uint32 `json:"position"` +} + +type GetTypesAtPositionsParams struct { + Snapshot SnapshotID `json:"snapshot"` + Project ProjectID `json:"project"` + File DocumentIdentifier `json:"file"` + Positions []uint32 `json:"positions"` +} + +// TypeToTypeNodeParams are the parameters for the typeToTypeNode method. +type TypeToTypeNodeParams struct { + Snapshot SnapshotID `json:"snapshot"` + Project ProjectID `json:"project"` + Type TypeID `json:"type"` + Location NodeHandle `json:"location,omitempty"` + Flags int32 `json:"flags,omitempty"` +} + +// SignatureToSignatureDeclarationParams are the parameters for the signatureToSignatureDeclaration method. +type SignatureToSignatureDeclarationParams struct { + Snapshot SnapshotID `json:"snapshot"` + Project ProjectID `json:"project"` + Signature SignatureID `json:"signature"` + Kind int32 `json:"kind"` + Location NodeHandle `json:"location,omitempty"` + Flags int32 `json:"flags,omitempty"` +} + +// PrintNodeParams are the parameters for the printNode method. +type PrintNodeParams struct { + Data string `json:"data"` // base64-encoded binary AST data + PreserveSourceNewlines bool `json:"preserveSourceNewlines,omitempty"` + NeverAsciiEscape bool `json:"neverAsciiEscape,omitempty"` + TerminateUnterminatedLiterals bool `json:"terminateUnterminatedLiterals,omitempty"` +} + +// CheckerTypeParams are parameters for checker methods that operate on a type. +type CheckerTypeParams struct { + Snapshot SnapshotID `json:"snapshot"` + Project ProjectID `json:"project"` + Type TypeID `json:"type"` +} + +// GetPropertyOfTypeParams are parameters for getPropertyOfType (a named property of a type). +type GetPropertyOfTypeParams struct { + Snapshot SnapshotID `json:"snapshot"` + Project ProjectID `json:"project"` + Type TypeID `json:"type"` + Name string `json:"name"` +} + +// GetMemberInModuleExportsParams are parameters for getMemberInModuleExports. +type GetMemberInModuleExportsParams struct { + Snapshot SnapshotID `json:"snapshot"` + Project ProjectID `json:"project"` + Symbol SymbolID `json:"symbol"` + Name string `json:"name"` +} + +// CheckerNodeParams are parameters for checker methods that operate on a node location. +type CheckerNodeParams struct { + Snapshot SnapshotID `json:"snapshot"` + Project ProjectID `json:"project"` + Location NodeHandle `json:"location"` +} + +// CheckerSymbolParams are parameters for checker methods that operate on a symbol. +type CheckerSymbolParams struct { + Snapshot SnapshotID `json:"snapshot"` + Project ProjectID `json:"project"` + Symbol SymbolID `json:"symbol"` +} + +// JSDocTagInfo is a single JSDoc tag, mirroring Strada's JSDocTagInfo but with the tag text +// rendered as a plain string rather than SymbolDisplayPart[]. +type JSDocTagInfo struct { + Name string `json:"name"` + Text string `json:"text,omitempty"` +} + +// CheckerSignatureParams are parameters for checker methods that operate on a signature. +type CheckerSignatureParams struct { + Snapshot SnapshotID `json:"snapshot"` + Project ProjectID `json:"project"` + Signature SignatureID `json:"signature"` +} + +// TypePredicateResponse is the response for getTypePredicateOfSignature. +type TypePredicateResponse struct { + Kind int32 `json:"kind"` + ParameterIndex int32 `json:"parameterIndex"` + ParameterName string `json:"parameterName,omitempty"` + Type *TypeResponse `json:"type,omitempty"` +} + +// IndexInfoResponse represents a single index signature. +type IndexInfoResponse struct { + KeyType TypeResponse `json:"keyType"` + ValueType TypeResponse `json:"valueType"` + IsReadonly bool `json:"isReadonly,omitempty"` + Declaration NodeHandle `json:"declaration,omitempty"` +} + +// SourceFileResponse contains the binary-encoded AST data for a source file. +// The Data field is base64-encoded binary data in the encoder's format. +type SourceFileResponse struct { + Data string `json:"data"` +} + +// GetDiagnosticsParams are parameters for per-file diagnostic methods. +type GetDiagnosticsParams struct { + Snapshot SnapshotID `json:"snapshot"` + Project ProjectID `json:"project"` + File *DocumentIdentifier `json:"file,omitempty"` +} + +// GetProjectDiagnosticsParams are parameters for project-wide diagnostic methods. +type GetProjectDiagnosticsParams struct { + Snapshot SnapshotID `json:"snapshot"` + Project ProjectID `json:"project"` +} + +// DiagnosticResponse is the API response for a single diagnostic. +type DiagnosticResponse struct { + // FileName is the path of the file this diagnostic belongs to, if any. + FileName string `json:"fileName,omitempty"` + // Pos is the start position of the diagnostic in the source file. + Pos int `json:"pos"` + // End is the end position of the diagnostic in the source file. + End int `json:"end"` + // Code is the diagnostic error code. + Code int32 `json:"code"` + // Category is the diagnostic category (error, warning, suggestion, message). + Category diagnostics.Category `json:"category"` + // Text is the localized diagnostic message text. + Text string `json:"text"` + // ReportsUnnecessary indicates this diagnostic highlights unnecessary code. + ReportsUnnecessary bool `json:"reportsUnnecessary,omitzero"` + // ReportsDeprecated indicates this diagnostic highlights deprecated code. + ReportsDeprecated bool `json:"reportsDeprecated,omitzero"` + // MessageChain contains chained diagnostic messages, if any. + MessageChain []*DiagnosticResponse `json:"messageChain,omitempty"` + // RelatedInformation contains related diagnostic information, if any. + RelatedInformation []*DiagnosticResponse `json:"relatedInformation,omitempty"` +} + +// NewDiagnosticResponse converts an ast.Diagnostic to a DiagnosticResponse. +func NewDiagnosticResponse(d *ast.Diagnostic) *DiagnosticResponse { + pos := d.Pos() + end := d.End() + file := d.File() + if file != nil { + positionMap := file.GetPositionMap() + pos = positionMap.UTF8ToUTF16(pos) + end = positionMap.UTF8ToUTF16(end) + } + resp := &DiagnosticResponse{ + Pos: pos, + End: end, + Code: d.Code(), + Category: d.Category(), + Text: d.Localize(locale.Default), + ReportsUnnecessary: d.ReportsUnnecessary(), + ReportsDeprecated: d.ReportsDeprecated(), + } + + if file != nil { + resp.FileName = file.FileName() + } + + if chain := d.MessageChain(); len(chain) > 0 { + resp.MessageChain = make([]*DiagnosticResponse, len(chain)) + for i, c := range chain { + resp.MessageChain[i] = NewDiagnosticResponse(c) + } + } + + if related := d.RelatedInformation(); len(related) > 0 { + resp.RelatedInformation = make([]*DiagnosticResponse, len(related)) + for i, r := range related { + resp.RelatedInformation[i] = NewDiagnosticResponse(r) + } + } + + return resp +} + +// NewDiagnosticResponses converts a slice of ast.Diagnostics to DiagnosticResponses. +func NewDiagnosticResponses(diags []*ast.Diagnostic) []*DiagnosticResponse { + if len(diags) == 0 { + return nil + } + result := make([]*DiagnosticResponse, len(diags)) + for i, d := range diags { + result[i] = NewDiagnosticResponse(d) + } + return result +} + +func unmarshalPayload(method string, payload json.Value) (any, error) { + unmarshaler, ok := unmarshalers[Method(method)] + if !ok { + return nil, fmt.Errorf("unknown API method %q", method) + } + return unmarshaler(payload) +} + +func unmarshallerFor[T any](data []byte) (any, error) { + var v T + if err := json.Unmarshal(data, &v); err != nil { + return nil, fmt.Errorf("failed to unmarshal %T: %w", (*T)(nil), err) + } + return &v, nil +} + +func noParams(data []byte) (any, error) { + return nil, nil +} diff --git a/tools/tsgo/internal/api/proto_test.go b/tools/tsgo/internal/api/proto_test.go new file mode 100644 index 00000000..6e17411e --- /dev/null +++ b/tools/tsgo/internal/api/proto_test.go @@ -0,0 +1,83 @@ +package api_test + +import ( + "strings" + "testing" + + "github.com/microsoft/typescript-go/internal/api" + "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/json" + "github.com/microsoft/typescript-go/internal/parser" + "gotest.tools/v3/assert" +) + +func TestDocumentIdentifierUnmarshalJSON(t *testing.T) { + t.Parallel() + tests := []struct { + name string + input string + fileName string + uri string + err string + }{ + { + name: "plain string", + input: `"foo.ts"`, + fileName: "foo.ts", + }, + { + name: "uri object", + input: `{"uri":"file:///foo.ts"}`, + uri: "file:///foo.ts", + }, + { + name: "uri object with unknown fields", + input: `{"uri":"file:///foo.ts","extra":true}`, + uri: "file:///foo.ts", + }, + { + name: "empty object", + input: `{}`, + }, + { + name: "invalid type", + input: `42`, + err: "expected string or object, got number", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + var d api.DocumentIdentifier + err := json.Unmarshal([]byte(tt.input), &d) + if tt.err != "" { + assert.ErrorContains(t, err, tt.err) + return + } + assert.NilError(t, err) + assert.Equal(t, d.FileName, tt.fileName) + assert.Equal(t, string(d.URI), tt.uri) + }) + } +} + +func TestNewDiagnosticResponseUsesUTF16Offsets(t *testing.T) { + t.Parallel() + + text := "const 💩 = 1;" + file := parser.ParseSourceFile(ast.SourceFileParseOptions{FileName: "/unicode.ts"}, text, core.ScriptKindTS) + pos := strings.Index(text, "=") + assert.Assert(t, pos > 0) + end := pos + len("=") + + diag := ast.NewDiagnostic(file, core.NewTextRange(pos, end), diagnostics.Expression_expected) + resp := api.NewDiagnosticResponse(diag) + + assert.Equal(t, resp.Pos, 9) + assert.Equal(t, resp.End, 10) + assert.Equal(t, resp.Pos, file.GetPositionMap().UTF8ToUTF16(pos)) + assert.Equal(t, resp.End, file.GetPositionMap().UTF8ToUTF16(end)) +} diff --git a/tools/tsgo/internal/api/protocol.go b/tools/tsgo/internal/api/protocol.go new file mode 100644 index 00000000..060b8a36 --- /dev/null +++ b/tools/tsgo/internal/api/protocol.go @@ -0,0 +1,22 @@ +package api + +import ( + "github.com/microsoft/typescript-go/internal/jsonrpc" +) + +// Message is an alias for jsonrpc.Message for convenience. +type Message = jsonrpc.Message + +// Protocol defines the interface for reading and writing API messages. +type Protocol interface { + // ReadMessage reads the next message from the connection. + ReadMessage() (*Message, error) + // WriteRequest writes a request message. + WriteRequest(id *jsonrpc.ID, method string, params any) error + // WriteNotification writes a notification message (no ID). + WriteNotification(method string, params any) error + // WriteResponse writes a successful response. + WriteResponse(id *jsonrpc.ID, result any) error + // WriteError writes an error response. + WriteError(id *jsonrpc.ID, err *jsonrpc.ResponseError) error +} diff --git a/tools/tsgo/internal/api/protocol_jsonrpc.go b/tools/tsgo/internal/api/protocol_jsonrpc.go new file mode 100644 index 00000000..ee0ded5d --- /dev/null +++ b/tools/tsgo/internal/api/protocol_jsonrpc.go @@ -0,0 +1,96 @@ +package api + +import ( + "io" + + "github.com/microsoft/typescript-go/internal/json" + "github.com/microsoft/typescript-go/internal/jsonrpc" +) + +// JSONRPCProtocol implements the Protocol interface using JSON-RPC 2.0 +// with the LSP base protocol framing (Content-Length headers). +type JSONRPCProtocol struct { + reader *jsonrpc.Reader + writer *jsonrpc.Writer +} + +var _ Protocol = (*JSONRPCProtocol)(nil) + +// NewJSONRPCProtocol creates a new JSON-RPC protocol handler. +func NewJSONRPCProtocol(rw io.ReadWriter) *JSONRPCProtocol { + return &JSONRPCProtocol{ + reader: jsonrpc.NewReader(rw), + writer: jsonrpc.NewWriter(rw), + } +} + +// ReadMessage implements Protocol. +func (p *JSONRPCProtocol) ReadMessage() (*Message, error) { + data, err := p.reader.Read() + if err != nil { + return nil, err + } + + var msg Message + if err := json.Unmarshal(data, &msg); err != nil { + return nil, err + } + + return &msg, nil +} + +// WriteRequest implements Protocol. +func (p *JSONRPCProtocol) WriteRequest(id *jsonrpc.ID, method string, params any) error { + msg := jsonrpc.RequestMessage{ + ID: id, + Method: method, + Params: params, + } + data, err := json.Marshal(msg) + if err != nil { + return err + } + return p.writer.Write(data) +} + +// WriteNotification implements Protocol. +func (p *JSONRPCProtocol) WriteNotification(method string, params any) error { + msg := jsonrpc.RequestMessage{ + Method: method, + Params: params, + } + data, err := json.Marshal(msg) + if err != nil { + return err + } + return p.writer.Write(data) +} + +// WriteResponse implements Protocol. +func (p *JSONRPCProtocol) WriteResponse(id *jsonrpc.ID, result any) error { + if result == nil { + result = json.Value("null") + } + msg := jsonrpc.ResponseMessage{ + ID: id, + Result: result, + } + data, err := json.Marshal(msg) + if err != nil { + return err + } + return p.writer.Write(data) +} + +// WriteError implements Protocol. +func (p *JSONRPCProtocol) WriteError(id *jsonrpc.ID, respErr *jsonrpc.ResponseError) error { + msg := jsonrpc.ResponseMessage{ + ID: id, + Error: respErr, + } + data, err := json.Marshal(msg) + if err != nil { + return err + } + return p.writer.Write(data) +} diff --git a/tools/tsgo/internal/api/protocol_msgpack.go b/tools/tsgo/internal/api/protocol_msgpack.go new file mode 100644 index 00000000..b8b9d87d --- /dev/null +++ b/tools/tsgo/internal/api/protocol_msgpack.go @@ -0,0 +1,280 @@ +package api + +import ( + "bufio" + "encoding/binary" + "fmt" + "io" + + "github.com/microsoft/typescript-go/internal/json" + "github.com/microsoft/typescript-go/internal/jsonrpc" +) + +// MessageType represents the type of message in the msgpack protocol. +type MessageType uint8 + +const ( + MessageTypeUnknown MessageType = iota + MessageTypeRequest + MessageTypeCallResponse + MessageTypeCallError + MessageTypeResponse + MessageTypeError + MessageTypeCall +) + +func (m MessageType) IsValid() bool { + return m >= MessageTypeRequest && m <= MessageTypeCall +} + +// MessagePack format constants +const ( + msgpackFixedArray3 byte = 0x93 + msgpackBin8 byte = 0xC4 + msgpackBin16 byte = 0xC5 + msgpackBin32 byte = 0xC6 + msgpackU8 byte = 0xCC +) + +// MessagePackProtocol implements the Protocol interface using a custom +// msgpack-based tuple format: [MessageType, method, payload]. +type MessagePackProtocol struct { + r *bufio.Reader + w *bufio.Writer +} + +var _ Protocol = (*MessagePackProtocol)(nil) + +// NewMessagePackProtocol creates a new msgpack protocol handler. +func NewMessagePackProtocol(rw io.ReadWriter) *MessagePackProtocol { + return &MessagePackProtocol{ + r: bufio.NewReader(rw), + w: bufio.NewWriter(rw), + } +} + +// ReadMessage implements Protocol. +func (p *MessagePackProtocol) ReadMessage() (*Message, error) { + msgType, method, payload, err := p.readTuple() + if err != nil { + return nil, err + } + + // Convert msgpack message type to JSON-RPC message + msg := &Message{} + + switch msgType { + case MessageTypeRequest: + // Client request - needs an ID for response + // We use the method as a pseudo-ID since this protocol doesn't have explicit IDs + id := jsonrpc.NewIDString(method) + msg.ID = id + msg.Method = method + msg.Params = payload + case MessageTypeCallResponse: + // Response to our Call - use method as ID + // Note: Method must be empty for IsResponse() to return true + id := jsonrpc.NewIDString(method) + msg.ID = id + msg.Result = payload + case MessageTypeCallError: + // Error response to our Call + // Note: Method must be empty for IsResponse() to return true + id := jsonrpc.NewIDString(method) + msg.ID = id + msg.Error = &jsonrpc.ResponseError{ + Code: jsonrpc.CodeInternalError, + Message: string(payload), + } + default: + return nil, fmt.Errorf("unexpected message type: %d", msgType) + } + + return msg, nil +} + +func (p *MessagePackProtocol) readTuple() (MessageType, string, []byte, error) { + // Read fixed array marker (0x93 = 3-element array) + t, err := p.r.ReadByte() + if err != nil { + return 0, "", nil, err + } + if t != msgpackFixedArray3 { + return 0, "", nil, fmt.Errorf("%w: expected fixed 3-element array (0x93), received: 0x%02x", ErrInvalidRequest, t) + } + + // Read message type - can be positive fixint (0x00-0x7F) or uint8 (0xCC + value) + t, err = p.r.ReadByte() + if err != nil { + return 0, "", nil, err + } + var rawType byte + if t <= 0x7F { + // Positive fixint - the byte IS the value + rawType = t + } else if t == msgpackU8 { + // uint8 marker - next byte is the value + rawType, err = p.r.ReadByte() + if err != nil { + return 0, "", nil, err + } + } else { + return 0, "", nil, fmt.Errorf("%w: expected positive fixint or uint8 marker, received: 0x%02x", ErrInvalidRequest, t) + } + msgType := MessageType(rawType) + if !msgType.IsValid() { + return 0, "", nil, fmt.Errorf("%w: unknown message type: %d", ErrInvalidRequest, msgType) + } + + // Read method (binary) + methodBytes, err := p.readBin() + if err != nil { + return 0, "", nil, err + } + method := string(methodBytes) + + // Read payload (binary) + payload, err := p.readBin() + if err != nil { + return 0, "", nil, err + } + + return msgType, method, payload, nil +} + +func (p *MessagePackProtocol) readBin() ([]byte, error) { + t, err := p.r.ReadByte() + if err != nil { + return nil, err + } + + var size uint + switch t { + case msgpackBin8: + var size8 uint8 + if err = binary.Read(p.r, binary.BigEndian, &size8); err != nil { + return nil, err + } + size = uint(size8) + case msgpackBin16: + var size16 uint16 + if err = binary.Read(p.r, binary.BigEndian, &size16); err != nil { + return nil, err + } + size = uint(size16) + case msgpackBin32: + var size32 uint32 + if err = binary.Read(p.r, binary.BigEndian, &size32); err != nil { + return nil, err + } + size = uint(size32) + default: + return nil, fmt.Errorf("%w: expected binary data (0xc4-0xc6), received: 0x%02x", ErrInvalidRequest, t) + } + + payload := make([]byte, size) + if _, err := io.ReadFull(p.r, payload); err != nil { + return nil, err + } + return payload, nil +} + +// WriteRequest implements Protocol. +func (p *MessagePackProtocol) WriteRequest(id *jsonrpc.ID, method string, params any) error { + // For msgpack protocol, requests from server are "Call" type + payload, err := json.Marshal(params) + if err != nil { + return err + } + return p.writeTuple(MessageTypeCall, method, payload) +} + +// WriteNotification implements Protocol. +func (p *MessagePackProtocol) WriteNotification(method string, params any) error { + // Msgpack protocol doesn't distinguish notifications from calls + return p.WriteRequest(nil, method, params) +} + +// WriteResponse implements Protocol. +func (p *MessagePackProtocol) WriteResponse(id *jsonrpc.ID, result any) error { + method := "" + if id != nil { + method = id.String() + } + + var payload []byte + var err error + + // Check if result is raw binary (for efficient binary transport) + if raw, ok := result.(RawBinary); ok { + payload = []byte(raw) + } else { + payload, err = json.Marshal(result) + if err != nil { + return err + } + } + + return p.writeTuple(MessageTypeResponse, method, payload) +} + +// WriteError implements Protocol. +func (p *MessagePackProtocol) WriteError(id *jsonrpc.ID, respErr *jsonrpc.ResponseError) error { + method := "" + if id != nil { + method = id.String() + } + return p.writeTuple(MessageTypeError, method, []byte(respErr.Message)) +} + +func (p *MessagePackProtocol) writeTuple(msgType MessageType, method string, payload []byte) error { + // Write fixed array marker + if err := p.w.WriteByte(msgpackFixedArray3); err != nil { + return err + } + // Write message type as positive fixint (values 0-127 are written directly) + if err := p.w.WriteByte(byte(msgType)); err != nil { + return err + } + // Write method + if err := p.writeBin([]byte(method)); err != nil { + return err + } + // Write payload + if err := p.writeBin(payload); err != nil { + return err + } + return p.w.Flush() +} + +func (p *MessagePackProtocol) writeBin(data []byte) error { + length := len(data) + if length < 256 { + if err := p.w.WriteByte(msgpackBin8); err != nil { + return err + } + if err := p.w.WriteByte(byte(length)); err != nil { + return err + } + } else if length < 1<<16 { + if err := p.w.WriteByte(msgpackBin16); err != nil { + return err + } + if err := binary.Write(p.w, binary.BigEndian, uint16(length)); err != nil { + return err + } + } else { + if err := p.w.WriteByte(msgpackBin32); err != nil { + return err + } + if err := binary.Write(p.w, binary.BigEndian, uint32(length)); err != nil { + return err + } + } + _, err := p.w.Write(data) + return err +} + +// RawBinary is a marker type for binary data that should be written +// directly by MessagePackProtocol instead of being JSON-encoded. +type RawBinary []byte diff --git a/tools/tsgo/internal/api/server.go b/tools/tsgo/internal/api/server.go new file mode 100644 index 00000000..ef5ca2a9 --- /dev/null +++ b/tools/tsgo/internal/api/server.go @@ -0,0 +1,124 @@ +package api + +import ( + "context" + "fmt" + "io" + + "github.com/microsoft/typescript-go/internal/bundled" + "github.com/microsoft/typescript-go/internal/lsp/lsproto" + "github.com/microsoft/typescript-go/internal/project" + "github.com/microsoft/typescript-go/internal/vfs/osvfs" +) + +// StdioServerOptions configures the STDIO-based API server. +type StdioServerOptions struct { + In io.ReadCloser + Out io.WriteCloser + Err io.Writer + Cwd string + DefaultLibraryPath string + // PipePath, if set, listens on a named pipe (Windows) or Unix domain + // socket instead of using In/Out for communication. + PipePath string + // Callbacks specifies which filesystem operations should be delegated + // to the client (e.g., "readFile", "fileExists"). Empty means no callbacks. + Callbacks []string + // Async enables JSON-RPC protocol with async connection handling. + // When false (default), uses MessagePack protocol with sync connection. + Async bool + // CollectTiming enables per-request server processing-time measurement. + // When enabled, the server accumulates each request's processing time into + // running totals and a recent-request ring buffer. Response messages are + // left unchanged; the client folds this data into its own timing snapshot + // on demand via getServerTiming / resetServerTiming requests. + CollectTiming bool +} + +// StdioServer runs an API session over STDIO using MessagePack protocol. +// This is the entry point for the synchronous STDIO-based API used by +// native TypeScript tooling integration. +type StdioServer struct { + options *StdioServerOptions +} + +// NewStdioServer creates a new STDIO-based API server. +func NewStdioServer(options *StdioServerOptions) *StdioServer { + if options.Cwd == "" { + panic("StdioServerOptions.Cwd is required") + } + + return &StdioServer{ + options: options, + } +} + +// Run starts the server and blocks until the connection closes. +func (s *StdioServer) Run(ctx context.Context) error { + var transport Transport + if s.options.PipePath != "" { + t, err := NewPipeTransport(s.options.PipePath) + if err != nil { + return fmt.Errorf("failed to create pipe transport: %w", err) + } + defer t.Close() + transport = t + } else { + t := NewStdioTransport(s.options.In, s.options.Out) + defer t.Close() + transport = t + } + + fs := bundled.WrapFS(osvfs.FS()) + + // Wrap the base FS with callbackFS if callbacks are requested + var callbackFS *callbackFS + if len(s.options.Callbacks) > 0 { + callbackFS = newCallbackFS(fs, s.options.Callbacks) + fs = callbackFS + } + + projectSession := project.NewSession(&project.SessionInit{ + BackgroundCtx: ctx, + Logger: nil, // TODO: Add logging support + FS: fs, + Options: &project.SessionOptions{ + CurrentDirectory: s.options.Cwd, + DefaultLibraryPath: s.options.DefaultLibraryPath, + PositionEncoding: lsproto.PositionEncodingKindUTF8, + LoggingEnabled: false, + }, + }) + + session := NewSession(projectSession, &SessionOptions{ + UseBinaryResponses: !s.options.Async, // Only msgpack uses binary responses + }) + defer session.Close() + + // Accept connection from transport + rwc, err := transport.Accept() + if err != nil { + return fmt.Errorf("failed to accept connection: %w", err) + } + + // Create protocol and connection based on async mode + var conn Conn + if s.options.Async { + protocol := NewJSONRPCProtocol(rwc) + asyncConn := NewAsyncConnWithProtocol(rwc, protocol, session) + asyncConn.SetCollectTiming(s.options.CollectTiming) + conn = asyncConn + } else { + protocol := NewMessagePackProtocol(rwc) + syncConn := NewSyncConn(rwc, protocol, session) + syncConn.SetCollectTiming(s.options.CollectTiming) + conn = syncConn + } + + // If callbacks are enabled, set the connection on the FS + if callbackFS != nil { + callbackFS.SetConnection(ctx, conn) + } + + return conn.Run(ctx) +} diff --git a/tools/tsgo/internal/api/session.go b/tools/tsgo/internal/api/session.go new file mode 100644 index 00000000..08f4293e --- /dev/null +++ b/tools/tsgo/internal/api/session.go @@ -0,0 +1,3210 @@ +package api + +import ( + "context" + "encoding/base64" + "fmt" + "slices" + "strconv" + "strings" + "sync" + "sync/atomic" + + "github.com/microsoft/typescript-go/internal/api/encoder" + "github.com/microsoft/typescript-go/internal/ast" + "github.com/microsoft/typescript-go/internal/astnav" + "github.com/microsoft/typescript-go/internal/checker" + "github.com/microsoft/typescript-go/internal/collections" + "github.com/microsoft/typescript-go/internal/compiler" + "github.com/microsoft/typescript-go/internal/core" + "github.com/microsoft/typescript-go/internal/json" + "github.com/microsoft/typescript-go/internal/ls" + "github.com/microsoft/typescript-go/internal/lsp/lsproto" + "github.com/microsoft/typescript-go/internal/nodebuilder" + "github.com/microsoft/typescript-go/internal/pprof" + "github.com/microsoft/typescript-go/internal/printer" + "github.com/microsoft/typescript-go/internal/project" + "github.com/microsoft/typescript-go/internal/tsoptions" + "github.com/microsoft/typescript-go/internal/tspath" +) + +var sessionIDCounter atomic.Uint64 + +// snapshotData holds the per-snapshot state including the snapshot itself +// and symbol/type registries scoped to this snapshot. +// Multiple clients may hold references to the same snapshot via ref counting; +// the registries are cleaned up when refCount reaches zero. +type snapshotData struct { + snapshot *project.Snapshot + refCount int + + // Symbol IDs come from ast.GetSymbolId, a global atomic counter, so the same + // *ast.Symbol pointer always has the same unique ID across all projects in the + // snapshot. Symbols are registered snapshot-wide to ensure identity semantics: + // querying the same symbol from two different projects returns the same handle. + symbolRegistry map[SymbolID]*ast.Symbol + symbolRegistryMu sync.RWMutex + + // symbolCanonicalProjects records, for each registered symbol, the project it was + // first observed in. Because symbols are shared snapshot-wide (binder symbols are + // attached to source files, which can be shared across projects), lookups that need + // a project context (e.g. member/export ordering, node handle resolution) but don't + // receive one from the caller default to this canonical project. First-writer wins so + // the choice is stable. Guarded by symbolRegistryMu. + symbolCanonicalProjects map[SymbolID]ProjectID + + projectRegistries map[ProjectID]*projectRegistryData + projectRegistriesMu sync.RWMutex +} + +// projectRegistryData holds per-project type and signature registries. +// Types and signatures use per-checker sequential IDs, so the same local ID +// can appear in multiple projects. Separate maps per project prevent collisions +// and allow clean teardown when a project is removed. +type projectRegistryData struct { + typeRegistry map[TypeID]*checker.Type + typeRegistryMu sync.RWMutex + + signatureRegistry map[SignatureID]*checker.Signature + signatureRegistryMu sync.RWMutex +} + +// getProgram looks up a program from a project handle within this snapshot. +func (sd *snapshotData) getProgram(projectHandle ProjectID) (*compiler.Program, error) { + proj, err := sd.getProject(projectHandle) + if err != nil { + return nil, err + } + + program := proj.GetProgram() + if program == nil { + return nil, fmt.Errorf("%w: project has no program", ErrClientError) + } + + return program, nil +} + +// getProject looks up a project from a project handle within this snapshot. +func (sd *snapshotData) getProject(projectHandle ProjectID) (*project.Project, error) { + projectName := parseProjectHandle(projectHandle) + proj := sd.snapshot.ProjectCollection.GetProjectByPath(projectName) + if proj == nil { + return nil, fmt.Errorf("%w: project %s not found", ErrClientError, projectName) + } + return proj, nil +} + +// nodeHandleFrom creates an index-based node handle (index.kind.path), building a node index table +// for the file on-demand if needed. +func (sd *snapshotData) nodeHandleFrom(node *ast.Node) NodeHandle { + sourceFile := ast.GetSourceFileOfNode(node) + path := sourceFile.Path() + table := encoder.GetNodeIndexTable(sourceFile) + idx := table.GetIndex(node) + return NodeHandle(fmt.Sprintf("%d.%d.%s", idx, node.Kind, path)) +} + +// getOrCreateProjectRegistry returns the registry for the given project, creating it if needed. +func (sd *snapshotData) getOrCreateProjectRegistry(projectID ProjectID) *projectRegistryData { + if projectID == "" { + panic("getOrCreateProjectRegistry: empty project ID") + } + // Fast path: registry already exists — read lock only. + sd.projectRegistriesMu.RLock() + reg := sd.projectRegistries[projectID] + sd.projectRegistriesMu.RUnlock() + if reg != nil { + return reg + } + // Slow path: create under write lock. + sd.projectRegistriesMu.Lock() + defer sd.projectRegistriesMu.Unlock() + if sd.projectRegistries[projectID] == nil { + sd.projectRegistries[projectID] = &projectRegistryData{ + typeRegistry: make(map[TypeID]*checker.Type), + signatureRegistry: make(map[SignatureID]*checker.Signature), + } + } + return sd.projectRegistries[projectID] +} + +// newSymbolResponse registers a symbol in the snapshot's registry and returns the response. +// canonicalProject is the project the symbol was observed in and must be non-empty; it is recorded +// as the symbol's canonical project (first writer wins) and returned to the client so it can default +// project-scoped follow-up lookups (members/exports, node resolution) to it. +func (sd *snapshotData) newSymbolResponse(symbol *ast.Symbol, canonicalProject ProjectID) *SymbolResponse { + if symbol == nil { + return nil + } + + id, project := sd.registerSymbol(symbol, canonicalProject) + resp := &SymbolResponse{ + Id: id, + Project: project, + Name: ast.EscapeSymbolName(symbol.Name), + Flags: uint32(symbol.Flags), + CheckFlags: uint32(symbol.CheckFlags), + } + + if len(symbol.Declarations) > 0 { + resp.Declarations = make([]NodeHandle, len(symbol.Declarations)) + for i, decl := range symbol.Declarations { + resp.Declarations[i] = sd.nodeHandleFrom(decl) + } + } + + if symbol.ValueDeclaration != nil { + resp.ValueDeclaration = sd.nodeHandleFrom(symbol.ValueDeclaration) + } + + if symbol.Parent != nil { + resp.Parent = SymbolHandle(symbol.Parent) + } + + if symbol.ExportSymbol != nil { + resp.ExportSymbol = SymbolHandle(symbol.ExportSymbol) + } + + return resp +} + +// registerSymbol registers a symbol in the snapshot's registry and returns its handle along with +// its canonical project. The canonical project is the project the symbol was first observed in +// (first writer wins for stability) and is always non-empty: every symbol handed to a client must +// carry a project so that project-scoped follow-up lookups (members/exports, parent, node +// resolution) have a default context. Callers must supply a non-empty project. +func (sd *snapshotData) registerSymbol(symbol *ast.Symbol, canonicalProject ProjectID) (SymbolID, ProjectID) { + if symbol == nil { + return 0, "" + } + if canonicalProject == "" { + panic("registerSymbol requires a non-empty canonical project") + } + id := SymbolHandle(symbol) + sd.symbolRegistryMu.Lock() + defer sd.symbolRegistryMu.Unlock() + existing := sd.symbolRegistry[id] + if existing != nil { + if existing != symbol { + panic("duplicate symbol") + } + } else { + sd.symbolRegistry[id] = symbol + } + project, ok := sd.symbolCanonicalProjects[id] + if !ok { + sd.symbolCanonicalProjects[id] = canonicalProject + project = canonicalProject + } + return id, project +} + +// newTypeResponse registers a type in the project's registry and returns the response. +func (sd *snapshotData) newTypeResponse(projectID ProjectID, t *checker.Type) *TypeResponse { + if t == nil { + return nil + } + return newTypeResponse(t, sd.registerType(projectID, t)) +} + +func (sd *snapshotData) registerType(projectID ProjectID, t *checker.Type) TypeID { + if t == nil { + return 0 + } + id := TypeHandle(t) + reg := sd.getOrCreateProjectRegistry(projectID) + reg.typeRegistryMu.Lock() + defer reg.typeRegistryMu.Unlock() + existing := reg.typeRegistry[id] + + if existing != nil { + if existing != t { + panic("duplicate type") + } + return id + } + reg.typeRegistry[id] = t + return id +} + +// resolveSymbolHandle resolves a symbol handle within the snapshot's registry. +func (sd *snapshotData) resolveSymbolHandle(handle SymbolID) (*ast.Symbol, error) { + if handle == 0 { + return nil, fmt.Errorf("%w: empty symbol handle", ErrClientError) + } + + sd.symbolRegistryMu.RLock() + symbol, ok := sd.symbolRegistry[handle] + sd.symbolRegistryMu.RUnlock() + + if !ok { + return nil, fmt.Errorf("%w: symbol handle %d not found in snapshot registry", ErrClientError, handle) + } + + return symbol, nil +} + +// resolveTypeHandle resolves a type handle within the project's registry. +func (sd *snapshotData) resolveTypeHandle(projectID ProjectID, handle TypeID) (*checker.Type, error) { + if handle == 0 { + return nil, fmt.Errorf("%w: empty type handle", ErrClientError) + } + if projectID == "" { + return nil, fmt.Errorf("%w: empty project ID for type handle %d", ErrClientError, handle) + } + + sd.projectRegistriesMu.RLock() + reg := sd.projectRegistries[projectID] + sd.projectRegistriesMu.RUnlock() + + if reg == nil { + return nil, fmt.Errorf("%w: type handle %d not found (no registry for project %s)", ErrClientError, handle, projectID) + } + + reg.typeRegistryMu.RLock() + t, ok := reg.typeRegistry[handle] + reg.typeRegistryMu.RUnlock() + + if !ok { + return nil, fmt.Errorf("%w: type handle %d not found in project registry", ErrClientError, handle) + } + + return t, nil +} + +// resolveSignatureHandle resolves a signature handle within the project's registry. +func (sd *snapshotData) resolveSignatureHandle(projectID ProjectID, handle SignatureID) (*checker.Signature, error) { + if handle == 0 { + return nil, fmt.Errorf("%w: empty signature handle", ErrClientError) + } + if projectID == "" { + return nil, fmt.Errorf("%w: empty project ID for signature handle %d", ErrClientError, handle) + } + + sd.projectRegistriesMu.RLock() + reg := sd.projectRegistries[projectID] + sd.projectRegistriesMu.RUnlock() + + if reg == nil { + return nil, fmt.Errorf("%w: signature handle %d not found (no registry for project %s)", ErrClientError, handle, projectID) + } + + reg.signatureRegistryMu.RLock() + sig, ok := reg.signatureRegistry[handle] + reg.signatureRegistryMu.RUnlock() + + if !ok { + return nil, fmt.Errorf("%w: signature handle %d not found in project registry", ErrClientError, handle) + } + + return sig, nil +} + +// newSignatureResponse registers a signature in the project's registry and returns the response. +func (sd *snapshotData) newSignatureResponse(projectID ProjectID, sig *checker.Signature) *SignatureResponse { + if sig == nil { + return nil + } + resp := &SignatureResponse{ + Id: sd.registerSignature(projectID, sig), + Flags: uint32(sig.Flags()), + } + + if sig.Declaration() != nil { + resp.Declaration = sd.nodeHandleFrom(sig.Declaration()) + } + + if len(sig.TypeParameters()) > 0 { + resp.TypeParameters = typeHandles(sig.TypeParameters()) + } + + if len(sig.Parameters()) > 0 { + resp.Parameters = symbolHandles(sig.Parameters()) + } + + if sig.ThisParameter() != nil { + resp.ThisParameter = SymbolHandle(sig.ThisParameter()) + } + + if sig.Target() != nil { + resp.Target = SignatureHandle(sig.Target()) + } + + return resp +} + +func (sd *snapshotData) registerSignature(projectID ProjectID, sig *checker.Signature) SignatureID { + if sig == nil { + return 0 + } + id := SignatureHandle(sig) + reg := sd.getOrCreateProjectRegistry(projectID) + reg.signatureRegistryMu.Lock() + defer reg.signatureRegistryMu.Unlock() + existing := reg.signatureRegistry[id] + + if existing != nil { + if existing != sig { + panic("duplicate signature") + } + return id + } + reg.signatureRegistry[id] = sig + return id +} + +// Session represents an API session that provides programmatic access +// to TypeScript language services through the LSP server. +// It implements the Handler interface to process incoming API requests. +// The session supports multiple active snapshots, each with their own +// symbol and type registries for maintaining object identity. +type Session struct { + id string + projectSession *project.Session + + // This is set to true when using MessagePackProtocol. + useBinaryResponses bool + + // snapshots maps snapshot handles to their data. Each snapshot has its own + // symbol/type registries. + // + // snapshotsMu guards the snapshots map and latestSnapshot. It is held only for + // short, map-bounded critical sections, never across slow work like a project + // snapshot update or checker queries. Read handlers (getSnapshotData and the + // language-service handlers built on it) take it for reading; handleRelease and + // the bookkeeping tail of handleUpdateSnapshot take it for writing. This is what + // lets queries against an existing snapshot run concurrently with the building of + // the next one. + snapshots map[SnapshotID]*snapshotData + snapshotsMu sync.RWMutex + + // latestSnapshot tracks the most recently created snapshot, used as the diff base + // for the next update. Guarded by snapshotsMu. + latestSnapshot SnapshotID + + // openProjects and openFiles track the projects and files this session + // currently holds open in the project session's API state. The session holds + // at most one ref per project/file (opens are idempotent), so it can release + // exactly those refs on Close and never send a close for a ref it doesn't hold. + // Guarded by updateMu. + openProjects collections.Set[tspath.Path] + openFiles collections.Set[tspath.Path] + + // updateMu serializes the whole of handleUpdateSnapshot (and releaseOpenRefs) + // against other updates. Unlike snapshotsMu it is held across the slow + // projectSession.APIUpdate call, because building the request from + // openProjects/openFiles, applying it, committing the ref tracking, and advancing + // latestSnapshot must be one atomic step; otherwise concurrent updates could + // double-count refs or diff against a non-adjacent snapshot. Read handlers do NOT + // take this lock, so an in-flight update never blocks queries against existing + // snapshots. Lock ordering is updateMu -> snapshotsMu (never the reverse). + updateMu sync.Mutex + + cpuProfiler pprof.CPUProfiler +} + +// Ensure Session implements Handler +var _ Handler = (*Session)(nil) + +// SessionOptions configures an API session. +type SessionOptions struct { + // UseBinaryResponses enables binary responses for msgpack protocol. + UseBinaryResponses bool +} + +// NewSession creates a new API session with the given project session. +func NewSession(projectSession *project.Session, options *SessionOptions) *Session { + id := sessionIDCounter.Add(1) + s := &Session{ + id: formatSessionID(id), + projectSession: projectSession, + snapshots: make(map[SnapshotID]*snapshotData), + } + if options != nil { + s.useBinaryResponses = options.UseBinaryResponses + } + return s +} + +// ID returns the unique identifier for this session. +func (s *Session) ID() string { + return s.id +} + +// ProjectSession returns the underlying project session. +func (s *Session) ProjectSession() *project.Session { + return s.projectSession +} + +// snapshotHandle creates a snapshot handle from a snapshot's ID. +func snapshotHandle(snapshot *project.Snapshot) SnapshotID { + return SnapshotID(snapshot.ID()) +} + +// getSnapshotData looks up snapshot data by handle. +func (s *Session) getSnapshotData(handle SnapshotID) (*snapshotData, error) { + s.snapshotsMu.RLock() + sd, ok := s.snapshots[handle] + s.snapshotsMu.RUnlock() + if !ok { + return nil, fmt.Errorf("%w: snapshot %d not found", ErrClientError, handle) + } + return sd, nil +} + +// checkerSetup holds the common context needed by handlers that require a type checker. +type checkerSetup struct { + sd *snapshotData + program *compiler.Program + checker *checker.Checker + done func() + projectID ProjectID +} + +func (setup checkerSetup) newTypeResponse(t *checker.Type) *TypeResponse { + return setup.sd.newTypeResponse(setup.projectID, t) +} + +func (setup checkerSetup) newSymbolResponse(sym *ast.Symbol) *SymbolResponse { + return setup.sd.newSymbolResponse(sym, setup.projectID) +} + +func (setup checkerSetup) newSignatureResponse(sig *checker.Signature) *SignatureResponse { + return setup.sd.newSignatureResponse(setup.projectID, sig) +} + +func (setup checkerSetup) resolveTypeHandle(id TypeID) (*checker.Type, error) { + return setup.sd.resolveTypeHandle(setup.projectID, id) +} + +func (setup checkerSetup) resolveSymbolHandle(id SymbolID) (*ast.Symbol, error) { + return setup.sd.resolveSymbolHandle(id) +} + +func (setup checkerSetup) resolveSignatureHandle(id SignatureID) (*checker.Signature, error) { + return setup.sd.resolveSignatureHandle(setup.projectID, id) +} + +// setupChecker resolves snapshot, program, and type checker for a project. +// Callers must defer setup.done() to release the checker. +func (s *Session) setupChecker(ctx context.Context, snapshot SnapshotID, projectHandle ProjectID) (checkerSetup, error) { + sd, err := s.getSnapshotData(snapshot) + if err != nil { + return checkerSetup{}, err + } + + program, err := sd.getProgram(projectHandle) + if err != nil { + return checkerSetup{}, err + } + + c, done := program.GetTypeChecker(core.WithCheckerLifetime(ctx, core.CheckerLifetimeAPI)) + return checkerSetup{ + sd: sd, + program: program, + checker: c, + done: done, + projectID: projectHandle, + }, nil +} + +// setupLanguageService creates a LanguageService for the given snapshot/project. +// Unlike setupChecker, this does NOT acquire a checker from the pool, so callers that +// only need an LS (and not a Checker) can avoid blocking on / holding a pooled checker. +// +// The LS acquires its own checker internally (keyed by the ctx's checker lifetime). +// If a handler returns symbol/type/signature handles the client may later re-query +// on the API checker (e.g. completion with IncludeSymbol -> GetTypeOfSymbol), wrap +// ctx with core.WithCheckerLifetime(ctx, core.CheckerLifetimeAPI) so those handles +// are produced on the persistent API checker and stay resolvable. Only safe when the +// LS operation acquires a checker exactly once; nested acquisitions (e.g. find-all- +// references) would deadlock on the single-slot persistent checker. +func (s *Session) setupLanguageService(sd *snapshotData, program *compiler.Program, projectHandle ProjectID, activeFile string) (*ls.LanguageService, error) { + projectName := parseProjectHandle(projectHandle) + proj := sd.snapshot.ProjectCollection.GetProjectByPath(projectName) + if proj == nil { + return nil, fmt.Errorf("%w: project %s not found", ErrClientError, projectName) + } + return ls.NewLanguageService(proj.ID(), program, sd.snapshot, activeFile), nil +} + +// HandleRequest implements Handler. +func (s *Session) HandleRequest(ctx context.Context, method string, params json.Value) (any, error) { + // Handle simple methods that don't need param parsing + switch method { + case "echo": + // Return raw binary for msgpack protocol compatibility + if s.useBinaryResponses { + return RawBinary(params), nil + } + return params, nil + case "ping": + return "pong", nil + } + + parsed, err := unmarshalPayload(method, params) + if err != nil { + return nil, fmt.Errorf("%w: %w", ErrInvalidRequest, err) + } + + switch method { + case string(MethodRelease): + return s.handleRelease(ctx, parsed.(*ReleaseParams)) + case string(MethodInitialize): + return s.handleInitialize(ctx) + case string(MethodUpdateSnapshot): + return s.handleUpdateSnapshot(ctx, parsed.(*UpdateSnapshotParams)) + case string(MethodParseConfigFile): + return s.handleParseConfigFile(ctx, parsed.(*ParseConfigFileParams)) + case string(MethodGetDefaultProjectForFile): + return s.handleGetDefaultProjectForFile(ctx, parsed.(*GetDefaultProjectForFileParams)) + case string(MethodGetSourceFile): + return s.handleGetSourceFile(ctx, parsed.(*GetSourceFileParams)) + case string(MethodGetSourceFileNames): + return s.handleGetSourceFileNames(ctx, parsed.(*GetSourceFileNamesParams)) + case string(MethodGetSourceFileMetadata): + return s.handleGetSourceFileMetadata(ctx, parsed.(*GetSourceFileParams)) + case string(MethodGetSymbolAtPosition): + return s.handleGetSymbolAtPosition(ctx, parsed.(*GetSymbolAtPositionParams)) + case string(MethodGetSymbolsAtPositions): + return s.handleGetSymbolsAtPositions(ctx, parsed.(*GetSymbolsAtPositionsParams)) + case string(MethodGetSymbolAtLocation): + return s.handleGetSymbolAtLocation(ctx, parsed.(*GetSymbolAtLocationParams)) + case string(MethodGetSymbolsAtLocations): + return s.handleGetSymbolsAtLocations(ctx, parsed.(*GetSymbolsAtLocationsParams)) + case string(MethodGetTypeOfSymbol): + return s.handleGetTypeOfSymbol(ctx, parsed.(*GetTypeOfSymbolParams)) + case string(MethodGetTypesOfSymbols): + return s.handleGetTypesOfSymbols(ctx, parsed.(*GetTypesOfSymbolsParams)) + case string(MethodGetDeclaredTypeOfSymbol): + return s.handleGetDeclaredTypeOfSymbol(ctx, parsed.(*GetTypeOfSymbolParams)) + case string(MethodResolveName): + return s.handleResolveName(ctx, parsed.(*ResolveNameParams)) + case string(MethodGetSignaturesOfType): + return s.handleGetSignaturesOfType(ctx, parsed.(*GetSignaturesOfTypeParams)) + case string(MethodGetResolvedSignature): + return s.handleGetResolvedSignature(ctx, parsed.(*GetResolvedSignatureParams)) + case string(MethodGetTypeAtLocation): + return s.handleGetTypeAtLocation(ctx, parsed.(*GetTypeAtLocationParams)) + case string(MethodGetTypeAtLocations): + return s.handleGetTypeAtLocations(ctx, parsed.(*GetTypeAtLocationsParams)) + case string(MethodGetTypeAtPosition): + return s.handleGetTypeAtPosition(ctx, parsed.(*GetTypeAtPositionParams)) + case string(MethodGetTypesAtPositions): + return s.handleGetTypesAtPositions(ctx, parsed.(*GetTypesAtPositionsParams)) + case string(MethodGetParentOfSymbol): + return s.handleGetParentOfSymbol(ctx, parsed.(*GetSymbolPropertyParams)) + case string(MethodGetMembersOfSymbol): + return s.handleGetMembersOfSymbol(ctx, parsed.(*GetSymbolPropertyParams)) + case string(MethodGetExportsOfSymbol): + return s.handleGetExportsOfSymbol(ctx, parsed.(*GetSymbolPropertyParams)) + case string(MethodGetExportSymbolOfSymbol): + return s.handleGetExportSymbolOfSymbol(ctx, parsed.(*GetSymbolPropertyParams)) + case string(MethodGetSymbolOfType): + return s.handleGetSymbolOfType(ctx, parsed.(*GetTypePropertyParams)) + case string(MethodGetTargetOfType): + return s.handleGetTargetOfType(ctx, parsed.(*GetTypePropertyParams)) + case string(MethodGetFreshTypeOfType): + return s.handleGetFreshTypeOfType(ctx, parsed.(*GetTypePropertyParams)) + case string(MethodGetRegularTypeOfType): + return s.handleGetRegularTypeOfType(ctx, parsed.(*GetTypePropertyParams)) + case string(MethodGetTypesOfType): + return s.handleGetTypesOfType(ctx, parsed.(*GetTypePropertyParams)) + case string(MethodGetTypeParametersOfType): + return s.handleGetTypeParametersOfType(ctx, parsed.(*GetTypePropertyParams)) + case string(MethodGetOuterTypeParametersOfType): + return s.handleGetOuterTypeParametersOfType(ctx, parsed.(*GetTypePropertyParams)) + case string(MethodGetLocalTypeParametersOfType): + return s.handleGetLocalTypeParametersOfType(ctx, parsed.(*GetTypePropertyParams)) + case string(MethodGetAliasTypeArgumentsOfType): + return s.handleGetAliasTypeArgumentsOfType(ctx, parsed.(*GetTypePropertyParams)) + case string(MethodGetAliasSymbolOfType): + return s.handleGetAliasSymbolOfType(ctx, parsed.(*GetTypePropertyParams)) + case string(MethodGetObjectTypeOfType): + return s.handleGetObjectTypeOfType(ctx, parsed.(*GetTypePropertyParams)) + case string(MethodGetIndexTypeOfType): + return s.handleGetIndexTypeOfType(ctx, parsed.(*GetTypePropertyParams)) + case string(MethodGetCheckTypeOfType): + return s.handleGetCheckTypeOfType(ctx, parsed.(*GetTypePropertyParams)) + case string(MethodGetExtendsTypeOfType): + return s.handleGetExtendsTypeOfType(ctx, parsed.(*GetTypePropertyParams)) + case string(MethodGetBaseTypeOfType): + return s.handleGetBaseTypeOfType(ctx, parsed.(*GetTypePropertyParams)) + case string(MethodGetConstraintOfType): + return s.handleGetConstraintOfType(ctx, parsed.(*GetTypePropertyParams)) + case string(MethodGetTrueTypeOfConditionalType): + return s.handleGetTrueTypeOfConditionalType(ctx, parsed.(*GetTypePropertyParams)) + case string(MethodGetFalseTypeOfConditionalType): + return s.handleGetFalseTypeOfConditionalType(ctx, parsed.(*GetTypePropertyParams)) + case string(MethodGetTypeParametersOfSignature): + return s.handleGetTypeParametersOfSignature(ctx, parsed.(*GetSignaturePropertyParams)) + case string(MethodGetParametersOfSignature): + return s.handleGetParametersOfSignature(ctx, parsed.(*GetSignaturePropertyParams)) + case string(MethodGetThisParameterOfSignature): + return s.handleGetThisParameterOfSignature(ctx, parsed.(*GetSignaturePropertyParams)) + case string(MethodGetTargetOfSignature): + return s.handleGetTargetOfSignature(ctx, parsed.(*GetSignaturePropertyParams)) + case string(MethodGetContextualType): + return s.handleGetContextualType(ctx, parsed.(*GetContextualTypeParams)) + case string(MethodGetBaseTypeOfLiteralType): + return s.handleGetBaseTypeOfLiteralType(ctx, parsed.(*GetBaseTypeOfLiteralTypeParams)) + case string(MethodGetNonNullableType): + return s.handleGetNonNullableType(ctx, parsed.(*GetNonNullableTypeParams)) + case string(MethodGetTypeFromTypeNode): + return s.handleGetTypeFromTypeNode(ctx, parsed.(*GetTypeFromTypeNodeParams)) + case string(MethodGetWidenedType): + return s.handleGetWidenedType(ctx, parsed.(*GetWidenedTypeParams)) + case string(MethodGetParameterType): + return s.handleGetParameterType(ctx, parsed.(*GetParameterTypeParams)) + case string(MethodIsArrayLikeType): + return s.handleIsArrayLikeType(ctx, parsed.(*IsArrayLikeTypeParams)) + case string(MethodIsTypeAssignableTo): + return s.handleIsTypeAssignableTo(ctx, parsed.(*IsTypeAssignableToParams)) + case string(MethodGetShorthandAssignmentValueSymbol): + return s.handleGetShorthandAssignmentValueSymbol(ctx, parsed.(*GetTypeAtLocationParams)) + case string(MethodGetTypeOfSymbolAtLocation): + return s.handleGetTypeOfSymbolAtLocation(ctx, parsed.(*GetTypeOfSymbolAtLocationParams)) + case string(MethodTypeToTypeNode): + return s.handleTypeToTypeNode(ctx, parsed.(*TypeToTypeNodeParams)) + case string(MethodSignatureToSignatureDeclaration): + return s.handleSignatureToSignatureDeclaration(ctx, parsed.(*SignatureToSignatureDeclarationParams)) + case string(MethodTypeToString): + return s.handleTypeToString(ctx, parsed.(*TypeToTypeNodeParams)) + case string(MethodPrintNode): + return s.handlePrintNode(ctx, parsed.(*PrintNodeParams)) + case string(MethodIsContextSensitive): + return s.handleIsContextSensitive(ctx, parsed.(*GetContextualTypeParams)) + case string(MethodGetReturnTypeOfSignature): + return s.handleGetReturnTypeOfSignature(ctx, parsed.(*CheckerSignatureParams)) + case string(MethodGetRestTypeOfSignature): + return s.handleGetRestTypeOfSignature(ctx, parsed.(*CheckerSignatureParams)) + case string(MethodGetTypePredicateOfSignature): + return s.handleGetTypePredicateOfSignature(ctx, parsed.(*CheckerSignatureParams)) + case string(MethodGetBaseTypes): + return s.handleGetBaseTypes(ctx, parsed.(*CheckerTypeParams)) + case string(MethodGetPropertiesOfType): + return s.handleGetPropertiesOfType(ctx, parsed.(*CheckerTypeParams)) + case string(MethodGetApparentType): + return s.handleGetApparentType(ctx, parsed.(*CheckerTypeParams)) + case string(MethodGetPropertyOfType): + return s.handleGetPropertyOfType(ctx, parsed.(*GetPropertyOfTypeParams)) + case string(MethodGetIndexInfosOfType): + return s.handleGetIndexInfosOfType(ctx, parsed.(*CheckerTypeParams)) + case string(MethodGetConstraintOfTypeParameter): + return s.handleGetConstraintOfTypeParameter(ctx, parsed.(*CheckerTypeParams)) + case string(MethodGetBaseConstraintOfType): + return s.handleGetBaseConstraintOfType(ctx, parsed.(*CheckerTypeParams)) + case string(MethodGetTypeArguments): + return s.handleGetTypeArguments(ctx, parsed.(*CheckerTypeParams)) + case string(MethodGetConstantValue): + return s.handleGetConstantValue(ctx, parsed.(*CheckerNodeParams)) + case string(MethodGetSignatureFromDeclaration): + return s.handleGetSignatureFromDeclaration(ctx, parsed.(*CheckerNodeParams)) + case string(MethodGetExportSpecifierLocalTarget): + return s.handleGetExportSpecifierLocalTargetSymbol(ctx, parsed.(*CheckerNodeParams)) + case string(MethodGetAliasedSymbol): + return s.handleGetAliasedSymbol(ctx, parsed.(*CheckerSymbolParams)) + case string(MethodGetImmediateAliasedSymbol): + return s.handleGetImmediateAliasedSymbol(ctx, parsed.(*CheckerSymbolParams)) + case string(MethodGetExportsOfModule): + return s.handleGetExportsOfModule(ctx, parsed.(*CheckerSymbolParams)) + case string(MethodGetMemberInModuleExports): + return s.handleGetMemberInModuleExports(ctx, parsed.(*GetMemberInModuleExportsParams)) + case string(MethodGetJSDocTags): + return s.handleGetJSDocTags(ctx, parsed.(*CheckerSymbolParams)) + case string(MethodGetDocumentationComment): + return s.handleGetDocumentationComment(ctx, parsed.(*CheckerSymbolParams)) + case string(MethodIsArrayType): + return s.handleIsArrayType(ctx, parsed.(*CheckerTypeParams)) + case string(MethodIsTupleType): + return s.handleIsTupleType(ctx, parsed.(*CheckerTypeParams)) + case string(MethodGetAnyType): + return s.handleGetIntrinsicType(ctx, parsed.(*GetIntrinsicTypeParams), (*checker.Checker).GetAnyType) + case string(MethodGetStringType): + return s.handleGetIntrinsicType(ctx, parsed.(*GetIntrinsicTypeParams), (*checker.Checker).GetStringType) + case string(MethodGetNumberType): + return s.handleGetIntrinsicType(ctx, parsed.(*GetIntrinsicTypeParams), (*checker.Checker).GetNumberType) + case string(MethodGetBooleanType): + return s.handleGetIntrinsicType(ctx, parsed.(*GetIntrinsicTypeParams), (*checker.Checker).GetBooleanType) + case string(MethodGetVoidType): + return s.handleGetIntrinsicType(ctx, parsed.(*GetIntrinsicTypeParams), (*checker.Checker).GetVoidType) + case string(MethodGetUndefinedType): + return s.handleGetIntrinsicType(ctx, parsed.(*GetIntrinsicTypeParams), (*checker.Checker).GetUndefinedType) + case string(MethodGetNullType): + return s.handleGetIntrinsicType(ctx, parsed.(*GetIntrinsicTypeParams), (*checker.Checker).GetNullType) + case string(MethodGetNeverType): + return s.handleGetIntrinsicType(ctx, parsed.(*GetIntrinsicTypeParams), (*checker.Checker).GetNeverType) + case string(MethodGetUnknownType): + return s.handleGetIntrinsicType(ctx, parsed.(*GetIntrinsicTypeParams), (*checker.Checker).GetUnknownType) + case string(MethodGetBigIntType): + return s.handleGetIntrinsicType(ctx, parsed.(*GetIntrinsicTypeParams), (*checker.Checker).GetBigIntType) + case string(MethodGetESSymbolType): + return s.handleGetIntrinsicType(ctx, parsed.(*GetIntrinsicTypeParams), (*checker.Checker).GetESSymbolType) + case string(MethodGetWellKnownSymbols): + return s.handleGetWellKnownSymbols(ctx, parsed.(*GetIntrinsicTypeParams)) + case string(MethodGetWellKnownSignatures): + return s.handleGetWellKnownSignatures(ctx, parsed.(*GetIntrinsicTypeParams)) + case string(MethodGetSyntacticDiagnostics): + return s.handleGetSyntacticDiagnostics(ctx, parsed.(*GetDiagnosticsParams)) + case string(MethodGetBindDiagnostics): + return s.handleGetBindDiagnostics(ctx, parsed.(*GetDiagnosticsParams)) + case string(MethodGetSemanticDiagnostics): + return s.handleGetSemanticDiagnostics(ctx, parsed.(*GetDiagnosticsParams)) + case string(MethodGetSuggestionDiagnostics): + return s.handleGetSuggestionDiagnostics(ctx, parsed.(*GetDiagnosticsParams)) + case string(MethodGetDeclarationDiagnostics): + return s.handleGetDeclarationDiagnostics(ctx, parsed.(*GetDiagnosticsParams)) + case string(MethodGetProgramDiagnostics): + return s.handleGetProgramDiagnostics(ctx, parsed.(*GetProjectDiagnosticsParams)) + case string(MethodGetGlobalDiagnostics): + return s.handleGetGlobalDiagnostics(ctx, parsed.(*GetProjectDiagnosticsParams)) + case string(MethodGetConfigFileParsingDiagnostics): + return s.handleGetConfigFileParsingDiagnostics(ctx, parsed.(*GetProjectDiagnosticsParams)) + case string(MethodStartCPUProfile): + return s.handleStartCPUProfile(ctx, parsed.(*ProfileParams)) + case string(MethodStopCPUProfile): + return s.handleStopCPUProfile(ctx) + case string(MethodSaveHeapProfile): + return s.handleSaveHeapProfile(ctx, parsed.(*ProfileParams)) + case string(MethodGetReferencesToSymbolInFile): + return s.handleGetReferencesToSymbolInFile(ctx, parsed.(*GetReferencesToSymbolInFileParams)) + case string(MethodGetReferencedSymbolsForNode): + return s.handleGetReferencedSymbolsForNode(ctx, parsed.(*GetReferencedSymbolsForNodeParams)) + case string(MethodGetSignatureUsages): + return s.handleGetSignatureUsages(ctx, parsed.(*GetSignatureUsagesParams)) + case string(MethodGetCompletionsAtPosition): + return s.handleGetCompletionsAtPosition(ctx, parsed.(*GetCompletionsAtPositionParams)) + default: + return nil, fmt.Errorf("unknown method: %s", method) + } +} + +func (s *Session) handleStartCPUProfile(_ context.Context, params *ProfileParams) (any, error) { + if params == nil || params.Dir == "" { + return nil, fmt.Errorf("%w: dir is required", ErrClientError) + } + if err := s.cpuProfiler.StartCPUProfile(params.Dir); err != nil { + return nil, fmt.Errorf("%w: failed to start CPU profile: %w", ErrClientError, err) + } + return nil, nil +} + +func (s *Session) handleStopCPUProfile(_ context.Context) (*ProfileResult, error) { + filePath, err := s.cpuProfiler.StopCPUProfile() + if err != nil { + return nil, fmt.Errorf("%w: failed to stop CPU profile: %w", ErrClientError, err) + } + return &ProfileResult{File: filePath}, nil +} + +func (s *Session) handleSaveHeapProfile(_ context.Context, params *ProfileParams) (*ProfileResult, error) { + if params == nil || params.Dir == "" { + return nil, fmt.Errorf("%w: dir is required", ErrClientError) + } + filePath, err := pprof.SaveHeapProfile(params.Dir) + if err != nil { + return nil, fmt.Errorf("%w: failed to save heap profile: %w", ErrClientError, err) + } + return &ProfileResult{File: filePath}, nil +} + +// HandleNotification implements Handler. +func (s *Session) HandleNotification(ctx context.Context, method string, params json.Value) error { + // TODO: Implement notification handling + return nil +} + +func (s *Session) handleInitialize(ctx context.Context) (*InitializeResponse, error) { + return &InitializeResponse{ + UseCaseSensitiveFileNames: s.projectSession.FS().UseCaseSensitiveFileNames(), + CurrentDirectory: s.projectSession.GetCurrentDirectory(), + }, nil +} + +// handleUpdateSnapshot creates a new snapshot, optionally opening or closing +// projects and files. With no args, it adopts the latest LSP state. Opens and +// closes are ref-counted per session: the session holds at most one ref per +// project/file, so repeated opens are idempotent and a close only releases a ref +// the session is actually holding. +func (s *Session) handleUpdateSnapshot(ctx context.Context, params *UpdateSnapshotParams) (*UpdateSnapshotResponse, error) { + // Fully serialize updates: snapshot creation, ref tracking, and the + // latestSnapshot/diff bookkeeping must be atomic with respect to other updates, + // otherwise concurrent updates could compute diffs against a non-adjacent + // snapshot or leave latestSnapshot pointing at a stale snapshot. + s.updateMu.Lock() + defer s.updateMu.Unlock() + + fileChanges := s.toFileChangeSummary(params.FileChanges) + + apiRequest := &project.APISnapshotRequest{} + + // Open projects: only take a new ref for projects we aren't already holding open. + var openedProjects []tspath.Path + for _, p := range params.OpenProjects { + configFileName := p.ToAbsoluteFileName(s.projectSession.GetCurrentDirectory()) + configPath := s.toPath(configFileName) + if s.openProjects.Has(configPath) { + continue + } + if apiRequest.OpenProjects == nil { + apiRequest.OpenProjects = collections.NewSetWithSizeHint[string](len(params.OpenProjects)) + } + apiRequest.OpenProjects.Add(configFileName) + openedProjects = append(openedProjects, configPath) + } + + // Close projects: only release a ref we currently hold. + var closedProjects []tspath.Path + for _, p := range params.CloseProjects { + configPath := s.toPath(p.ToAbsoluteFileName(s.projectSession.GetCurrentDirectory())) + if !s.openProjects.Has(configPath) { + continue + } + if apiRequest.CloseProjects == nil { + apiRequest.CloseProjects = collections.NewSetWithSizeHint[tspath.Path](len(params.CloseProjects)) + } + apiRequest.CloseProjects.Add(configPath) + closedProjects = append(closedProjects, configPath) + } + + // Open files: only open files we aren't already holding open, so each file is + // held by at most one API ref from this session. + var openedFiles []tspath.Path + for _, f := range params.OpenFiles { + uri := f.ToURI(s.projectSession.GetCurrentDirectory()) + path := s.toPath(uri.FileName()) + if s.openFiles.Has(path) { + continue + } + if apiRequest.OpenFiles == nil { + apiRequest.OpenFiles = collections.NewSetWithSizeHint[lsproto.DocumentUri](len(params.OpenFiles)) + } + apiRequest.OpenFiles.Add(uri) + openedFiles = append(openedFiles, path) + } + + // Close files: only release a ref we currently hold. + var closedFiles []tspath.Path + for _, f := range params.CloseFiles { + path := s.toPath(f.ToURI(s.projectSession.GetCurrentDirectory()).FileName()) + if !s.openFiles.Has(path) { + continue + } + if apiRequest.CloseFiles == nil { + apiRequest.CloseFiles = collections.NewSetWithSizeHint[tspath.Path](len(params.CloseFiles)) + } + apiRequest.CloseFiles.Add(path) + closedFiles = append(closedFiles, path) + } + + // Even when nothing is opened or closed, APIUpdate ensures all projects and + // files opened by the API are up to date. For an API connected to an LSP server, + // this brings the API state up to date with the LSP state and ensures projects + // the API cares about are ready to be queried. + snapshot, err := s.projectSession.APIUpdate(ctx, fileChanges, apiRequest) + if err != nil { + // APIUpdate returns a ref'd snapshot even on error; release it. + snapshot.Deref(s.projectSession) + return nil, fmt.Errorf("%w: failed to update snapshot: %w", ErrClientError, err) + } + + // Commit ref tracking now that the update succeeded. + for _, configPath := range openedProjects { + s.openProjects.Add(configPath) + } + for _, configPath := range closedProjects { + s.openProjects.Delete(configPath) + } + for _, path := range openedFiles { + s.openFiles.Add(path) + } + for _, path := range closedFiles { + s.openFiles.Delete(path) + } + + // Create or ref-count snapshot data, then atomically read the previous latest + // snapshot (the diff base) and advance latestSnapshot to the new handle. + // If the same snapshot ID is returned (no changes), we increment the ref count + // so each client-side Snapshot can be disposed independently. + handle := snapshotHandle(snapshot) + s.snapshotsMu.Lock() + sd, exists := s.snapshots[handle] + if exists { + // Same snapshot already stored — release the caller's ref since + // the stored snapshot already has one, and bump the API refcount. + snapshot.Deref(s.projectSession) + sd.refCount++ + } else { + sd = &snapshotData{ + snapshot: snapshot, + refCount: 1, + symbolRegistry: make(map[SymbolID]*ast.Symbol), + symbolCanonicalProjects: make(map[SymbolID]ProjectID), + projectRegistries: make(map[ProjectID]*projectRegistryData), + } + s.snapshots[handle] = sd + } + prevSD := s.snapshots[s.latestSnapshot] + s.latestSnapshot = handle + s.snapshotsMu.Unlock() + + // Build projects list + projects := snapshot.ProjectCollection.Projects() + projectResponses := make([]*ProjectResponse, 0, len(projects)) + for _, proj := range projects { + if proj.CommandLine == nil { + continue + } + projectResponses = append(projectResponses, NewProjectResponse(proj)) + } + + // Compute changes from the previous latest snapshot + var changes *SnapshotChanges + if prevSD != nil { + changes = computeSnapshotChanges(prevSD.snapshot, snapshot) + } + + return &UpdateSnapshotResponse{ + Snapshot: handle, + Projects: projectResponses, + Changes: changes, + }, nil +} + +// handleRelease decrements the ref count for a snapshot. +// The snapshot and its registries are only cleaned up when the ref count reaches zero. +func (s *Session) handleRelease(ctx context.Context, params *ReleaseParams) (any, error) { + if params == nil || params.Snapshot == 0 { + return nil, fmt.Errorf("%w: empty handle", ErrClientError) + } + + s.snapshotsMu.Lock() + sd := s.snapshots[params.Snapshot] + if sd == nil { + s.snapshotsMu.Unlock() + return nil, fmt.Errorf("%w: snapshot %d not found", ErrClientError, params.Snapshot) + } + sd.refCount-- + if sd.refCount <= 0 { + delete(s.snapshots, params.Snapshot) + // Release the API session's ref on the project snapshot. + sd.snapshot.Deref(s.projectSession) + } + s.snapshotsMu.Unlock() + return true, nil +} + +// handleGetDefaultProjectForFile returns the default project for a given file, +// or nil if no project currently contains the file. +func (s *Session) handleGetDefaultProjectForFile(ctx context.Context, params *GetDefaultProjectForFileParams) (*ProjectResponse, error) { + sd, err := s.getSnapshotData(params.Snapshot) + if err != nil { + return nil, err + } + + uri := params.File.ToURI(s.projectSession.GetCurrentDirectory()) + proj := sd.snapshot.GetDefaultProject(uri) + if proj == nil { + return nil, nil + } + + return NewProjectResponse(proj), nil +} + +// handleParseConfigFile parses a tsconfig.json file and returns its contents. +func (s *Session) handleParseConfigFile(ctx context.Context, params *ParseConfigFileParams) (*ConfigFileResponse, error) { + configFileName := params.File.ToAbsoluteFileName(s.projectSession.GetCurrentDirectory()) + configFileContent, ok := s.projectSession.FS().ReadFile(configFileName) + if !ok { + return nil, fmt.Errorf("%w: could not read file %q", ErrClientError, configFileName) + } + + configDir := tspath.GetDirectoryPath(configFileName) + tsConfigSourceFile := tsoptions.NewTsconfigSourceFileFromFilePath( + configFileName, + s.toPath(configFileName), + configFileContent, + ) + parsedCommandLine := tsoptions.ParseJsonSourceFileConfigFileContent( + tsConfigSourceFile, + s.projectSession, + configDir, + nil, /*existingOptions*/ + nil, /*existingOptionsRaw*/ + configFileName, + nil, /*resolutionStack*/ + nil, /*extraFileExtensions*/ + nil, /*extendedConfigCache*/ + ) + + return &ConfigFileResponse{ + FileNames: parsedCommandLine.FileNames(), + Options: parsedCommandLine.CompilerOptions(), + }, nil +} + +// handleGetSourceFile returns a source file from a project within a snapshot. +func (s *Session) handleGetSourceFile(ctx context.Context, params *GetSourceFileParams) (any, error) { + sd, err := s.getSnapshotData(params.Snapshot) + if err != nil { + return nil, err + } + + program, err := sd.getProgram(params.Project) + if err != nil { + return nil, err + } + + sourceFile := program.GetSourceFile(params.File.ToFileName()) + if sourceFile == nil { + if s.useBinaryResponses { + return RawBinary(nil), nil + } + return nil, nil + } + + // Encode the full source file. + data, _, err := encoder.EncodeSourceFile(sourceFile) + if err != nil { + return nil, fmt.Errorf("failed to encode source file: %w", err) + } + + // Return raw binary for msgpack protocol, or base64 for JSON + if s.useBinaryResponses { + return RawBinary(data), nil + } + return &SourceFileResponse{ + Data: base64.StdEncoding.EncodeToString(data), + }, nil +} + +// handleGetSourceFileNames returns file names of all source files in a project. +func (s *Session) handleGetSourceFileNames(ctx context.Context, params *GetSourceFileNamesParams) ([]string, error) { + sd, err := s.getSnapshotData(params.Snapshot) + if err != nil { + return nil, err + } + + program, err := sd.getProgram(params.Project) + if err != nil { + return nil, err + } + + sourceFiles := program.GetSourceFiles() + result := make([]string, len(sourceFiles)) + for i, sourceFile := range sourceFiles { + result[i] = sourceFile.FileName() + } + return result, nil +} + +// handleGetSourceFileMetadata returns program-stored metadata for a single source file. +// The client fetches this lazily per file and caches it. +func (s *Session) handleGetSourceFileMetadata(ctx context.Context, params *GetSourceFileParams) (*SourceFileMetadata, error) { + sd, err := s.getSnapshotData(params.Snapshot) + if err != nil { + return nil, err + } + + program, err := sd.getProgram(params.Project) + if err != nil { + return nil, err + } + + sourceFile := program.GetSourceFile(params.File.ToFileName()) + if sourceFile == nil { + return nil, nil + } + + metaData := program.GetSourceFileMetaData(sourceFile.Path()) + return &SourceFileMetadata{ + IsDefaultLibrary: program.IsSourceFileDefaultLibrary(sourceFile.Path()), + IsFromExternalLibrary: program.IsSourceFileFromExternalLibrary(sourceFile), + PackageJsonType: metaData.PackageJsonType, + PackageJsonDirectory: metaData.PackageJsonDirectory, + ImpliedNodeFormat: metaData.ImpliedNodeFormat, + }, nil +} + +// handleGetSymbolAtPosition returns the symbol at a position in a file. +func (s *Session) handleGetSymbolAtPosition(ctx context.Context, params *GetSymbolAtPositionParams) (*SymbolResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + sourceFile := setup.program.GetSourceFile(params.File.ToFileName()) + if sourceFile == nil { + return nil, fmt.Errorf("%w: source file not found: %v", ErrClientError, params.File) + } + + positionMap := sourceFile.GetPositionMap() + node := astnav.GetTouchingPropertyName(sourceFile, positionMap.UTF16ToUTF8(int(params.Position))) + if node == nil { + return nil, nil + } + + symbol := setup.checker.GetSymbolAtLocation(node) + if symbol == nil { + return nil, nil + } + + return setup.newSymbolResponse(symbol), nil +} + +// handleGetSymbolsAtPositions returns symbols at multiple positions in a file. +func (s *Session) handleGetSymbolsAtPositions(ctx context.Context, params *GetSymbolsAtPositionsParams) ([]*SymbolResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + sourceFile := setup.program.GetSourceFile(params.File.ToFileName()) + if sourceFile == nil { + return nil, fmt.Errorf("%w: source file not found: %v", ErrClientError, params.File) + } + + positionMap := sourceFile.GetPositionMap() + results := make([]*SymbolResponse, len(params.Positions)) + for i, pos := range params.Positions { + node := astnav.GetTouchingPropertyName(sourceFile, positionMap.UTF16ToUTF8(int(pos))) + if node == nil { + continue + } + symbol := setup.checker.GetSymbolAtLocation(node) + if symbol != nil { + results[i] = setup.newSymbolResponse(symbol) + } + } + + return results, nil +} + +// handleGetSymbolAtLocation returns the symbol at a node location. +func (s *Session) handleGetSymbolAtLocation(ctx context.Context, params *GetSymbolAtLocationParams) (*SymbolResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + node, err := setup.sd.resolveNodeHandle(setup.program, params.Location) + if err != nil { + return nil, err + } + if node == nil { + return nil, nil + } + + symbol := setup.checker.GetSymbolAtLocation(node) + if symbol == nil { + return nil, nil + } + + return setup.newSymbolResponse(symbol), nil +} + +// handleGetSymbolsAtLocations returns symbols at multiple node locations. +func (s *Session) handleGetSymbolsAtLocations(ctx context.Context, params *GetSymbolsAtLocationsParams) ([]*SymbolResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + results := make([]*SymbolResponse, len(params.Locations)) + for i, loc := range params.Locations { + node, err := setup.sd.resolveNodeHandle(setup.program, loc) + if err != nil { + return nil, err + } + if node == nil { + continue + } + symbol := setup.checker.GetSymbolAtLocation(node) + if symbol != nil { + results[i] = setup.newSymbolResponse(symbol) + } + } + + return results, nil +} + +// handleGetTypeOfSymbol returns the type of a symbol. +func (s *Session) handleGetTypeOfSymbol(ctx context.Context, params *GetTypeOfSymbolParams) (*TypeResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + symbol, err := setup.resolveSymbolHandle(params.Symbol) + if err != nil { + return nil, err + } + + return setup.newTypeResponse(setup.checker.GetTypeOfSymbol(symbol)), nil +} + +// handleGetTypesOfSymbols returns the types of multiple symbols. +func (s *Session) handleGetTypesOfSymbols(ctx context.Context, params *GetTypesOfSymbolsParams) ([]*TypeResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + results := make([]*TypeResponse, len(params.Symbols)) + for i, symHandle := range params.Symbols { + symbol, err := setup.resolveSymbolHandle(symHandle) + if err != nil { + return nil, err + } + // resolveSymbolHandle errors on an unresolvable handle and GetTypeOfSymbol + // never returns nil, so every element resolves to a type (error type at worst). + results[i] = setup.newTypeResponse(setup.checker.GetTypeOfSymbol(symbol)) + } + + return results, nil +} + +// handleGetDeclaredTypeOfSymbol returns the declared type of a symbol (e.g. the type alias body for type alias symbols). +func (s *Session) handleGetDeclaredTypeOfSymbol(ctx context.Context, params *GetTypeOfSymbolParams) (*TypeResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + symbol, err := setup.resolveSymbolHandle(params.Symbol) + if err != nil { + return nil, err + } + + return setup.newTypeResponse(setup.checker.GetDeclaredTypeOfSymbol(symbol)), nil +} + +// handleResolveName resolves a name to a symbol at a given location. +func (s *Session) handleResolveName(ctx context.Context, params *ResolveNameParams) (*SymbolResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + // Resolve location node - either from node handle or from fileName+position + var location *ast.Node + if params.Location != "" { + location, err = setup.sd.resolveNodeHandle(setup.program, params.Location) + if err != nil { + return nil, err + } + } else if params.File != nil && params.Position != nil { + sourceFile := setup.program.GetSourceFile(params.File.ToFileName()) + if sourceFile == nil { + return nil, fmt.Errorf("%w: source file not found: %v", ErrClientError, *params.File) + } + location = astnav.GetTouchingPropertyName(sourceFile, sourceFile.GetPositionMap().UTF16ToUTF8(int(*params.Position))) + } + + symbol := setup.checker.ResolveName(params.Name, location, ast.SymbolFlags(params.Meaning), params.ExcludeGlobals) + if symbol == nil { + return nil, nil + } + + return setup.newSymbolResponse(symbol), nil +} + +// handleGetSignaturesOfType returns the call or construct signatures of a type. +func (s *Session) handleGetSignaturesOfType(ctx context.Context, params *GetSignaturesOfTypeParams) ([]*SignatureResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + t, err := setup.resolveTypeHandle(params.Type) + if err != nil { + return nil, err + } + + sigs := setup.checker.GetSignaturesOfType(t, checker.SignatureKind(params.Kind)) + results := make([]*SignatureResponse, len(sigs)) + for i, sig := range sigs { + results[i] = setup.newSignatureResponse(sig) + } + + return results, nil +} + +// handleGetResolvedSignature returns the resolved signature of a call-like expression. +func (s *Session) handleGetResolvedSignature(ctx context.Context, params *GetResolvedSignatureParams) (*SignatureResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + node, err := setup.sd.resolveNodeHandle(setup.program, params.Location) + if err != nil { + return nil, err + } + + return setup.newSignatureResponse(setup.checker.GetResolvedSignature(node)), nil +} + +// handleGetTypeAtLocation returns the type at a node location. +func (s *Session) handleGetTypeAtLocation(ctx context.Context, params *GetTypeAtLocationParams) (*TypeResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + node, err := setup.sd.resolveNodeHandle(setup.program, params.Location) + if err != nil { + return nil, err + } + + return setup.newTypeResponse(setup.checker.GetTypeAtLocation(node)), nil +} + +// handleGetTypeAtLocations returns types at multiple node locations. +func (s *Session) handleGetTypeAtLocations(ctx context.Context, params *GetTypeAtLocationsParams) ([]*TypeResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + results := make([]*TypeResponse, len(params.Locations)) + for i, loc := range params.Locations { + node, err := setup.sd.resolveNodeHandle(setup.program, loc) + if err != nil { + return nil, err + } + // resolveNodeHandle errors on an unresolvable handle and GetTypeAtLocation + // never returns nil, so every element resolves to a type (error type at worst). + results[i] = setup.newTypeResponse(setup.checker.GetTypeAtLocation(node)) + } + + return results, nil +} + +// handleGetTypeAtPosition returns the type at a position in a file. +func (s *Session) handleGetTypeAtPosition(ctx context.Context, params *GetTypeAtPositionParams) (*TypeResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + sourceFile := setup.program.GetSourceFile(params.File.ToFileName()) + if sourceFile == nil { + return nil, fmt.Errorf("%w: source file not found: %v", ErrClientError, params.File) + } + + positionMap := sourceFile.GetPositionMap() + node := astnav.GetTouchingPropertyName(sourceFile, positionMap.UTF16ToUTF8(int(params.Position))) + if node == nil { + return nil, nil + } + + t := setup.checker.GetTypeAtLocation(node) + if t == nil { + return nil, nil + } + + return setup.newTypeResponse(t), nil +} + +// handleGetTypesAtPositions returns types at multiple positions in a file. +func (s *Session) handleGetTypesAtPositions(ctx context.Context, params *GetTypesAtPositionsParams) ([]*TypeResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + sourceFile := setup.program.GetSourceFile(params.File.ToFileName()) + if sourceFile == nil { + return nil, fmt.Errorf("%w: source file not found: %v", ErrClientError, params.File) + } + + positionMap := sourceFile.GetPositionMap() + results := make([]*TypeResponse, len(params.Positions)) + for i, pos := range params.Positions { + node := astnav.GetTouchingPropertyName(sourceFile, positionMap.UTF16ToUTF8(int(pos))) + if node == nil { + continue + } + t := setup.checker.GetTypeAtLocation(node) + if t != nil { + results[i] = setup.newTypeResponse(t) + } + } + + return results, nil +} + +func (s *Session) handleGetParentOfSymbol(_ context.Context, params *GetSymbolPropertyParams) (*SymbolResponse, error) { + return s.resolveSymbolPropertyOfSymbol(params, func(sym *ast.Symbol) *ast.Symbol { return sym.Parent }) +} + +func (s *Session) handleGetMembersOfSymbol(ctx context.Context, params *GetSymbolPropertyParams) ([]*SymbolResponse, error) { + return s.resolveSymbolTablePropertyOfSymbol(ctx, params, func(symbol *ast.Symbol) ast.SymbolTable { + return symbol.Members + }) +} + +func (s *Session) handleGetExportsOfSymbol(ctx context.Context, params *GetSymbolPropertyParams) ([]*SymbolResponse, error) { + return s.resolveSymbolTablePropertyOfSymbol(ctx, params, func(symbol *ast.Symbol) ast.SymbolTable { + return symbol.Exports + }) +} + +func (s *Session) handleGetExportSymbolOfSymbol(_ context.Context, params *GetSymbolPropertyParams) (*SymbolResponse, error) { + return s.resolveSymbolPropertyOfSymbol(params, func(sym *ast.Symbol) *ast.Symbol { return sym.ExportSymbol }) +} + +func (s *Session) handleGetSymbolOfType(_ context.Context, params *GetTypePropertyParams) (*SymbolResponse, error) { + return s.resolveSymbolPropertyOfType(params, (*checker.Type).Symbol) +} + +func (s *Session) handleGetTargetOfType(_ context.Context, params *GetTypePropertyParams) (*TypeResponse, error) { + return s.resolveTypePropertyOfType(params, (*checker.Type).Target) +} + +func (s *Session) handleGetFreshTypeOfType(_ context.Context, params *GetTypePropertyParams) (*TypeResponse, error) { + return s.resolveTypePropertyOfType(params, func(t *checker.Type) *checker.Type { return t.AsLiteralType().FreshType() }) +} + +func (s *Session) handleGetRegularTypeOfType(_ context.Context, params *GetTypePropertyParams) (*TypeResponse, error) { + return s.resolveTypePropertyOfType(params, func(t *checker.Type) *checker.Type { return t.AsLiteralType().RegularType() }) +} + +func (s *Session) handleGetTypesOfType(_ context.Context, params *GetTypePropertyParams) ([]*TypeResponse, error) { + return s.resolveTypeArrayPropertyOfType(params, (*checker.Type).Types) +} + +func (s *Session) handleGetTypeParametersOfType(_ context.Context, params *GetTypePropertyParams) ([]*TypeResponse, error) { + return s.resolveTypeArrayPropertyOfType(params, func(t *checker.Type) []*checker.Type { return t.AsInterfaceType().TypeParameters() }) +} + +func (s *Session) handleGetOuterTypeParametersOfType(_ context.Context, params *GetTypePropertyParams) ([]*TypeResponse, error) { + return s.resolveTypeArrayPropertyOfType(params, func(t *checker.Type) []*checker.Type { return t.AsInterfaceType().OuterTypeParameters() }) +} + +func (s *Session) handleGetLocalTypeParametersOfType(_ context.Context, params *GetTypePropertyParams) ([]*TypeResponse, error) { + return s.resolveTypeArrayPropertyOfType(params, func(t *checker.Type) []*checker.Type { return t.AsInterfaceType().LocalTypeParameters() }) +} + +func (s *Session) handleGetAliasTypeArgumentsOfType(_ context.Context, params *GetTypePropertyParams) ([]*TypeResponse, error) { + return s.resolveTypeArrayPropertyOfType(params, func(t *checker.Type) []*checker.Type { + if t.Alias() == nil { + return nil + } + return t.Alias().TypeArguments() + }) +} + +func (s *Session) handleGetAliasSymbolOfType(_ context.Context, params *GetTypePropertyParams) (*SymbolResponse, error) { + return s.resolveSymbolPropertyOfType(params, func(t *checker.Type) *ast.Symbol { + if t.Alias() == nil { + return nil + } + return t.Alias().Symbol() + }) +} + +func (s *Session) handleGetObjectTypeOfType(_ context.Context, params *GetTypePropertyParams) (*TypeResponse, error) { + return s.resolveTypePropertyOfType(params, func(t *checker.Type) *checker.Type { return t.AsIndexedAccessType().ObjectType() }) +} + +func (s *Session) handleGetIndexTypeOfType(_ context.Context, params *GetTypePropertyParams) (*TypeResponse, error) { + return s.resolveTypePropertyOfType(params, func(t *checker.Type) *checker.Type { return t.AsIndexedAccessType().IndexType() }) +} + +func (s *Session) handleGetCheckTypeOfType(_ context.Context, params *GetTypePropertyParams) (*TypeResponse, error) { + return s.resolveTypePropertyOfType(params, func(t *checker.Type) *checker.Type { return t.AsConditionalType().CheckType() }) +} + +func (s *Session) handleGetExtendsTypeOfType(_ context.Context, params *GetTypePropertyParams) (*TypeResponse, error) { + return s.resolveTypePropertyOfType(params, func(t *checker.Type) *checker.Type { return t.AsConditionalType().ExtendsType() }) +} + +func (s *Session) handleGetBaseTypeOfType(_ context.Context, params *GetTypePropertyParams) (*TypeResponse, error) { + return s.resolveTypePropertyOfType(params, func(t *checker.Type) *checker.Type { return t.AsSubstitutionType().BaseType() }) +} + +func (s *Session) handleGetConstraintOfType(_ context.Context, params *GetTypePropertyParams) (*TypeResponse, error) { + return s.resolveTypePropertyOfType(params, func(t *checker.Type) *checker.Type { return t.AsSubstitutionType().SubstConstraint() }) +} + +func (s *Session) handleGetTypeParametersOfSignature(_ context.Context, params *GetSignaturePropertyParams) ([]*TypeResponse, error) { + return s.resolveTypeArrayPropertyOfSignature(params, (*checker.Signature).TypeParameters) +} + +func (s *Session) handleGetParametersOfSignature(_ context.Context, params *GetSignaturePropertyParams) ([]*SymbolResponse, error) { + return s.resolveSymbolArrayPropertyOfSignature(params, (*checker.Signature).Parameters) +} + +func (s *Session) handleGetThisParameterOfSignature(_ context.Context, params *GetSignaturePropertyParams) (*SymbolResponse, error) { + return s.resolveSymbolPropertyOfSignature(params, (*checker.Signature).ThisParameter) +} + +func (s *Session) handleGetTargetOfSignature(_ context.Context, params *GetSignaturePropertyParams) (*SignatureResponse, error) { + return s.resolveSignaturePropertyOfSignature(params, (*checker.Signature).Target) +} + +// resolveTypePropertyOfType resolves a type property of type `Type` and returns a type response. +func (s *Session) resolveTypePropertyOfType(params *GetTypePropertyParams, getter func(*checker.Type) *checker.Type) (*TypeResponse, error) { + sd, err := s.getSnapshotData(params.Snapshot) + if err != nil { + return nil, err + } + + t, err := sd.resolveTypeHandle(params.Project, params.Type) + if err != nil { + return nil, err + } + + result := getter(t) + if result == nil { + return nil, nil + } + + return sd.newTypeResponse(params.Project, result), nil +} + +// resolveTypeArrayPropertyOfType resolves a type property of an array of types and returns an array of type responses. +func (s *Session) resolveTypeArrayPropertyOfType(params *GetTypePropertyParams, getter func(*checker.Type) []*checker.Type) ([]*TypeResponse, error) { + sd, err := s.getSnapshotData(params.Snapshot) + if err != nil { + return nil, err + } + + t, err := sd.resolveTypeHandle(params.Project, params.Type) + if err != nil { + return nil, err + } + + types := getter(t) + if len(types) == 0 { + return nil, nil + } + + results := make([]*TypeResponse, len(types)) + for i, sub := range types { + results[i] = sd.newTypeResponse(params.Project, sub) + } + return results, nil +} + +// resolveSymbolPropertyOfType resolves a type property of type `Symbol` and returns a symbol response. +func (s *Session) resolveSymbolPropertyOfType(params *GetTypePropertyParams, getter func(*checker.Type) *ast.Symbol) (*SymbolResponse, error) { + sd, err := s.getSnapshotData(params.Snapshot) + if err != nil { + return nil, err + } + + t, err := sd.resolveTypeHandle(params.Project, params.Type) + if err != nil { + return nil, err + } + + result := getter(t) + if result == nil { + return nil, nil + } + return sd.newSymbolResponse(result, params.Project), nil +} + +// resolveSymbolTablePropertyOfSymbol resolves a symbol property of type `Symbol` and returns a symbol response. +func (s *Session) resolveSymbolPropertyOfSymbol(params *GetSymbolPropertyParams, getter func(*ast.Symbol) *ast.Symbol) (*SymbolResponse, error) { + sd, err := s.getSnapshotData(params.Snapshot) + if err != nil { + return nil, err + } + + symbol, err := sd.resolveSymbolHandle(params.Symbol) + if err != nil { + return nil, err + } + + result := getter(symbol) + if result == nil { + return nil, nil + } + return sd.newSymbolResponse(result, params.Project), nil +} + +// resolveSymbolTablePropertyOfSymbol resolves a symbol property of type `SymbolTable` and returns an array of symbol responses. +// Results are sorted using the checker's canonical symbol ordering so that API consumers receive +// a stable, deterministic order instead of Go's randomized map iteration order. +func (s *Session) resolveSymbolTablePropertyOfSymbol(ctx context.Context, params *GetSymbolPropertyParams, getter func(*ast.Symbol) ast.SymbolTable) ([]*SymbolResponse, error) { + sd, err := s.getSnapshotData(params.Snapshot) + if err != nil { + return nil, err + } + + symbol, err := sd.resolveSymbolHandle(params.Symbol) + if err != nil { + return nil, err + } + + symbolTable := getter(symbol) + if len(symbolTable) == 0 { + return nil, nil + } + if len(symbolTable) == 1 { + for _, sub := range symbolTable { + return []*SymbolResponse{sd.newSymbolResponse(sub, params.Project)}, nil + } + } + + // More than one symbol, need a checker to sort + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + symbols := make([]*ast.Symbol, 0, len(symbolTable)) + for _, sub := range symbolTable { + symbols = append(symbols, sub) + } + slices.SortFunc(symbols, setup.checker.CompareSymbols) + + results := make([]*SymbolResponse, len(symbols)) + for i, sub := range symbols { + results[i] = setup.newSymbolResponse(sub) + } + return results, nil +} + +// resolveSymbolArrayPropertyOfSignature resolves a signature property of an array of symbols and returns an array of symbol responses. +func (s *Session) resolveSymbolArrayPropertyOfSignature(params *GetSignaturePropertyParams, getter func(*checker.Signature) []*ast.Symbol) ([]*SymbolResponse, error) { + sd, err := s.getSnapshotData(params.Snapshot) + if err != nil { + return nil, err + } + + sig, err := sd.resolveSignatureHandle(params.Project, params.Signature) + if err != nil { + return nil, err + } + + symbols := getter(sig) + if len(symbols) == 0 { + return nil, nil + } + + results := make([]*SymbolResponse, len(symbols)) + for i, sym := range symbols { + results[i] = sd.newSymbolResponse(sym, params.Project) + } + return results, nil +} + +// resolveSymbolPropertyOfSignature resolves a signature property of type `Symbol` and returns a symbol response. +func (s *Session) resolveSymbolPropertyOfSignature(params *GetSignaturePropertyParams, getter func(*checker.Signature) *ast.Symbol) (*SymbolResponse, error) { + sd, err := s.getSnapshotData(params.Snapshot) + if err != nil { + return nil, err + } + + sig, err := sd.resolveSignatureHandle(params.Project, params.Signature) + if err != nil { + return nil, err + } + + result := getter(sig) + if result == nil { + return nil, nil + } + return sd.newSymbolResponse(result, params.Project), nil +} + +func (s *Session) resolveTypeArrayPropertyOfSignature(params *GetSignaturePropertyParams, getter func(signature *checker.Signature) []*checker.Type) ([]*TypeResponse, error) { + sd, err := s.getSnapshotData(params.Snapshot) + if err != nil { + return nil, err + } + + sig, err := sd.resolveSignatureHandle(params.Project, params.Signature) + if err != nil { + return nil, err + } + + types := getter(sig) + if len(types) == 0 { + return nil, nil + } + + results := make([]*TypeResponse, len(types)) + for i, sub := range types { + results[i] = sd.newTypeResponse(params.Project, sub) + } + return results, nil +} + +func (s *Session) resolveSignaturePropertyOfSignature(params *GetSignaturePropertyParams, getter func(*checker.Signature) *checker.Signature) (*SignatureResponse, error) { + sd, err := s.getSnapshotData(params.Snapshot) + if err != nil { + return nil, err + } + + sig, err := sd.resolveSignatureHandle(params.Project, params.Signature) + if err != nil { + return nil, err + } + + result := getter(sig) + if result == nil { + return nil, nil + } + return sd.newSignatureResponse(params.Project, result), nil +} + +// handleGetContextualType returns the contextual type for a node. +func (s *Session) handleGetContextualType(ctx context.Context, params *GetContextualTypeParams) (*TypeResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + node, err := setup.sd.resolveNodeHandle(setup.program, params.Location) + if err != nil { + return nil, err + } + if node == nil { + return nil, nil + } + + t := setup.checker.GetContextualType(node, checker.ContextFlagsNone) + if t == nil { + return nil, nil + } + + return setup.newTypeResponse(t), nil +} + +// handleGetBaseTypeOfLiteralType returns the base type of a literal type (e.g. number for 42). +func (s *Session) handleGetBaseTypeOfLiteralType(ctx context.Context, params *GetBaseTypeOfLiteralTypeParams) (*TypeResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + t, err := setup.resolveTypeHandle(params.Type) + if err != nil { + return nil, err + } + + return setup.newTypeResponse(setup.checker.GetBaseTypeOfLiteralType(t)), nil +} + +// handleGetNonNullableType returns the type with null and undefined removed. +func (s *Session) handleGetNonNullableType(ctx context.Context, params *GetNonNullableTypeParams) (*TypeResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + t, err := setup.resolveTypeHandle(params.Type) + if err != nil { + return nil, err + } + + return setup.newTypeResponse(setup.checker.GetNonNullableType(t)), nil +} + +// handleGetTypeFromTypeNode returns the type for a type node. +func (s *Session) handleGetTypeFromTypeNode(ctx context.Context, params *GetTypeFromTypeNodeParams) (*TypeResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + node, err := setup.sd.resolveNodeHandle(setup.program, params.Location) + if err != nil { + return nil, err + } + + return setup.newTypeResponse(setup.checker.GetTypeFromTypeNode(node)), nil +} + +// handleGetWidenedType returns the widened type. +func (s *Session) handleGetWidenedType(ctx context.Context, params *GetWidenedTypeParams) (*TypeResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + t, err := setup.resolveTypeHandle(params.Type) + if err != nil { + return nil, err + } + + return setup.newTypeResponse(setup.checker.GetWidenedType(t)), nil +} + +// handleGetParameterType returns the type of a parameter at a given index in a signature. +func (s *Session) handleGetParameterType(ctx context.Context, params *GetParameterTypeParams) (*TypeResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + sig, err := setup.resolveSignatureHandle(params.Signature) + if err != nil { + return nil, err + } + + if params.Index < 0 { + return nil, fmt.Errorf("%w: invalid parameter index", ErrClientError) + } + + return setup.newTypeResponse(setup.checker.GetTypeAtPosition(sig, int(params.Index))), nil +} + +// handleIsArrayLikeType returns whether a type is array-like. +func (s *Session) handleIsArrayLikeType(ctx context.Context, params *IsArrayLikeTypeParams) (bool, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return false, err + } + defer setup.done() + + t, err := setup.resolveTypeHandle(params.Type) + if err != nil { + return false, err + } + + return setup.checker.IsArrayLikeType(t), nil +} + +// handleIsTypeAssignableTo returns whether source is assignable to target. +func (s *Session) handleIsTypeAssignableTo(ctx context.Context, params *IsTypeAssignableToParams) (bool, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return false, err + } + defer setup.done() + + source, err := setup.resolveTypeHandle(params.Source) + if err != nil { + return false, err + } + target, err := setup.resolveTypeHandle(params.Target) + if err != nil { + return false, err + } + + return setup.checker.IsTypeAssignableTo(source, target), nil +} + +// handleGetShorthandAssignmentValueSymbol returns the value symbol of a shorthand property assignment. +func (s *Session) handleGetShorthandAssignmentValueSymbol(ctx context.Context, params *GetTypeAtLocationParams) (*SymbolResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + node, err := setup.sd.resolveNodeHandle(setup.program, params.Location) + if err != nil { + return nil, err + } + if node == nil { + return nil, nil + } + + symbol := setup.checker.GetShorthandAssignmentValueSymbol(node) + if symbol == nil { + return nil, nil + } + + return setup.newSymbolResponse(symbol), nil +} + +// handleGetTypeOfSymbolAtLocation returns the narrowed type of a symbol at a specific location. +func (s *Session) handleGetTypeOfSymbolAtLocation(ctx context.Context, params *GetTypeOfSymbolAtLocationParams) (*TypeResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + symbol, err := setup.resolveSymbolHandle(params.Symbol) + if err != nil { + return nil, err + } + + node, err := setup.sd.resolveNodeHandle(setup.program, params.Location) + if err != nil { + return nil, err + } + + return setup.newTypeResponse(setup.checker.GetTypeOfSymbolAtLocation(symbol, node)), nil +} + +// handleTypeToTypeNode converts a Type to a TypeNode AST and returns it as binary-encoded data. +func (s *Session) handleTypeToTypeNode(ctx context.Context, params *TypeToTypeNodeParams) (any, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + t, err := setup.resolveTypeHandle(params.Type) + if err != nil { + return nil, err + } + + var enclosingDeclaration *ast.Node + if params.Location != "" { + enclosingDeclaration, err = setup.sd.resolveNodeHandle(setup.program, params.Location) + if err != nil { + return nil, err + } + } + + typeNode := setup.checker.TypeToTypeNode(t, enclosingDeclaration, nodebuilder.Flags(params.Flags), nil) + if typeNode == nil { + return nil, nil + } + + data, _, err := encoder.EncodeNode(typeNode.AsNode(), nil) + if err != nil { + return nil, fmt.Errorf("failed to encode type node: %w", err) + } + + if s.useBinaryResponses { + return RawBinary(data), nil + } + return &SourceFileResponse{ + Data: base64.StdEncoding.EncodeToString(data), + }, nil +} + +func (s *Session) handleSignatureToSignatureDeclaration(ctx context.Context, params *SignatureToSignatureDeclarationParams) (any, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + sig, err := setup.resolveSignatureHandle(params.Signature) + if err != nil { + return nil, err + } + + var enclosingDeclaration *ast.Node + if params.Location != "" { + enclosingDeclaration, err = setup.sd.resolveNodeHandle(setup.program, params.Location) + if err != nil { + return nil, err + } + } + + node := setup.checker.SignatureToSignatureDeclaration(sig, ast.Kind(params.Kind), enclosingDeclaration, nodebuilder.Flags(params.Flags)) + if node == nil { + return nil, nil + } + + data, _, err := encoder.EncodeNode(node.AsNode(), nil) + if err != nil { + return nil, fmt.Errorf("failed to encode signature declaration: %w", err) + } + + if s.useBinaryResponses { + return RawBinary(data), nil + } + return &SourceFileResponse{ + Data: base64.StdEncoding.EncodeToString(data), + }, nil +} + +// handleTypeToString converts a Type to its string representation. +func (s *Session) handleTypeToString(ctx context.Context, params *TypeToTypeNodeParams) (any, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + t, err := setup.resolveTypeHandle(params.Type) + if err != nil { + return nil, err + } + + var enclosingDeclaration *ast.Node + if params.Location != "" { + enclosingDeclaration, err = setup.sd.resolveNodeHandle(setup.program, params.Location) + if err != nil { + return nil, err + } + } + + if params.Flags != 0 { + return setup.checker.TypeToStringEx(t, enclosingDeclaration, checker.TypeFormatFlags(params.Flags), nil), nil + } + return setup.checker.TypeToStringEx(t, enclosingDeclaration, checker.TypeFormatFlagsAllowUniqueESSymbolType|checker.TypeFormatFlagsUseAliasDefinedOutsideCurrentScope, nil), nil +} + +// handlePrintNode decodes a binary-encoded AST node and prints it to text. +func (s *Session) handlePrintNode(_ context.Context, params *PrintNodeParams) (string, error) { + data, err := base64.StdEncoding.DecodeString(params.Data) + if err != nil { + return "", fmt.Errorf("%w: invalid base64 data: %w", ErrClientError, err) + } + + node, err := encoder.DecodeNodes(data) + if err != nil { + return "", fmt.Errorf("%w: failed to decode AST: %w", ErrClientError, err) + } + + p := printer.NewPrinter(printer.PrinterOptions{ + PreserveSourceNewlines: params.PreserveSourceNewlines, + NeverAsciiEscape: params.NeverAsciiEscape, + TerminateUnterminatedLiterals: params.TerminateUnterminatedLiterals, + }, printer.PrintHandlers{}, nil) + return p.Emit(node, nil), nil +} + +// handleGetIntrinsicType returns an intrinsic type (any, string, number, etc.). +func (s *Session) handleGetIntrinsicType(ctx context.Context, params *GetIntrinsicTypeParams, getter func(*checker.Checker) *checker.Type) (*TypeResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + t := getter(setup.checker) + if t == nil { + return nil, nil + } + + return setup.newTypeResponse(t), nil +} + +// handleGetWellKnownSymbols returns the handle ids of the per-checker singleton +// symbols (unknown, undefined, arguments) so the client can identify them by id. +func (s *Session) handleGetWellKnownSymbols(ctx context.Context, params *GetIntrinsicTypeParams) (*WellKnownSymbolsResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + unknown, _ := setup.sd.registerSymbol(setup.checker.GetUnknownSymbol(), setup.projectID) + undefined, _ := setup.sd.registerSymbol(setup.checker.GetUndefinedSymbol(), setup.projectID) + arguments, _ := setup.sd.registerSymbol(setup.checker.GetArgumentsSymbol(), setup.projectID) + return &WellKnownSymbolsResponse{ + Unknown: unknown, + Undefined: undefined, + Arguments: arguments, + }, nil +} + +// handleGetWellKnownSignatures returns the handle id of the per-checker unknown +// signature so the client can identify it by id. +func (s *Session) handleGetWellKnownSignatures(ctx context.Context, params *GetIntrinsicTypeParams) (*WellKnownSignaturesResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + return &WellKnownSignaturesResponse{ + Unknown: setup.sd.registerSignature(setup.projectID, setup.checker.GetUnknownSignature()), + }, nil +} + +// handleIsContextSensitive returns whether a node is context-sensitive. +func (s *Session) handleIsContextSensitive(ctx context.Context, params *GetContextualTypeParams) (bool, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return false, err + } + defer setup.done() + + node, err := setup.sd.resolveNodeHandle(setup.program, params.Location) + if err != nil { + return false, err + } + if node == nil { + return false, nil + } + + return setup.checker.IsContextSensitive(node), nil +} + +// handleGetReturnTypeOfSignature returns the return type of a signature. +func (s *Session) handleGetReturnTypeOfSignature(ctx context.Context, params *CheckerSignatureParams) (*TypeResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + sig, err := setup.resolveSignatureHandle(params.Signature) + if err != nil { + return nil, err + } + + return setup.newTypeResponse(setup.checker.GetReturnTypeOfSignature(sig)), nil +} + +// handleGetRestTypeOfSignature returns the rest type of a signature. +func (s *Session) handleGetRestTypeOfSignature(ctx context.Context, params *CheckerSignatureParams) (*TypeResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + sig, err := setup.resolveSignatureHandle(params.Signature) + if err != nil { + return nil, err + } + + return setup.newTypeResponse(setup.checker.GetRestTypeOfSignature(sig)), nil +} + +// handleGetTypePredicateOfSignature returns the type predicate of a signature. +func (s *Session) handleGetTypePredicateOfSignature(ctx context.Context, params *CheckerSignatureParams) (*TypePredicateResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + sig, err := setup.resolveSignatureHandle(params.Signature) + if err != nil { + return nil, err + } + + pred := setup.checker.GetTypePredicateOfSignature(sig) + if pred == nil { + return nil, nil + } + + resp := &TypePredicateResponse{ + Kind: int32(pred.Kind()), + ParameterIndex: pred.ParameterIndex(), + ParameterName: pred.ParameterName(), + } + if pred.Type() != nil { + resp.Type = setup.newTypeResponse(pred.Type()) + } + + return resp, nil +} + +// handleIsArrayType returns whether a type is Array or ReadonlyArray. +func (s *Session) handleIsArrayType(ctx context.Context, params *CheckerTypeParams) (bool, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return false, err + } + defer setup.done() + + t, err := setup.resolveTypeHandle(params.Type) + if err != nil { + return false, err + } + + return setup.checker.IsArrayType(t), nil +} + +// handleIsTupleType returns whether a type is a tuple type. +func (s *Session) handleIsTupleType(ctx context.Context, params *CheckerTypeParams) (bool, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return false, err + } + defer setup.done() + + t, err := setup.resolveTypeHandle(params.Type) + if err != nil { + return false, err + } + + return checker.IsTupleType(t), nil +} + +// handleGetBaseTypes returns the base types of an interface/class type. +func (s *Session) handleGetBaseTypes(ctx context.Context, params *CheckerTypeParams) ([]*TypeResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + t, err := setup.resolveTypeHandle(params.Type) + if err != nil { + return nil, err + } + + baseTypes := setup.checker.GetBaseTypes(t) + if len(baseTypes) == 0 { + return nil, nil + } + + results := make([]*TypeResponse, len(baseTypes)) + for i, bt := range baseTypes { + results[i] = setup.newTypeResponse(bt) + } + + return results, nil +} + +// handleGetPropertiesOfType returns the properties of a type. +func (s *Session) handleGetPropertiesOfType(ctx context.Context, params *CheckerTypeParams) ([]*SymbolResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + t, err := setup.resolveTypeHandle(params.Type) + if err != nil { + return nil, err + } + + props := setup.checker.GetPropertiesOfType(t) + if len(props) == 0 { + return nil, nil + } + + results := make([]*SymbolResponse, len(props)) + for i, prop := range props { + results[i] = setup.newSymbolResponse(prop) + } + + return results, nil +} + +// handleGetApparentType returns the apparent type of a type. +func (s *Session) handleGetApparentType(ctx context.Context, params *CheckerTypeParams) (*TypeResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + t, err := setup.resolveTypeHandle(params.Type) + if err != nil { + return nil, err + } + + return setup.newTypeResponse(setup.checker.GetApparentType(t)), nil +} + +// handleGetIndexInfosOfType returns the index infos of a type. +func (s *Session) handleGetIndexInfosOfType(ctx context.Context, params *CheckerTypeParams) ([]*IndexInfoResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + t, err := setup.resolveTypeHandle(params.Type) + if err != nil { + return nil, err + } + + infos := setup.checker.GetIndexInfosOfType(t) + if len(infos) == 0 { + return nil, nil + } + + results := make([]*IndexInfoResponse, len(infos)) + for i, info := range infos { + results[i] = &IndexInfoResponse{ + KeyType: *setup.newTypeResponse(info.KeyType()), + ValueType: *setup.newTypeResponse(info.ValueType()), + IsReadonly: info.IsReadonly(), + } + if info.Declaration() != nil { + results[i].Declaration = setup.sd.nodeHandleFrom(info.Declaration()) + } + } + + return results, nil +} + +// handleGetConstraintOfTypeParameter returns the constraint of a type parameter. +func (s *Session) handleGetConstraintOfTypeParameter(ctx context.Context, params *CheckerTypeParams) (*TypeResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + t, err := setup.resolveTypeHandle(params.Type) + if err != nil { + return nil, err + } + + constraint := setup.checker.GetConstraintOfTypeParameter(t) + if constraint == nil { + return nil, nil + } + + return setup.newTypeResponse(constraint), nil +} + +// handleGetBaseConstraintOfType returns the base constraint of an instantiable type. +func (s *Session) handleGetBaseConstraintOfType(ctx context.Context, params *CheckerTypeParams) (*TypeResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + t, err := setup.resolveTypeHandle(params.Type) + if err != nil { + return nil, err + } + + constraint := setup.checker.GetBaseConstraintOfType(t) + if constraint == nil { + return nil, nil + } + + return setup.newTypeResponse(constraint), nil +} + +// handleGetPropertyOfType returns a named property symbol of a type. +func (s *Session) handleGetPropertyOfType(ctx context.Context, params *GetPropertyOfTypeParams) (*SymbolResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + t, err := setup.resolveTypeHandle(params.Type) + if err != nil { + return nil, err + } + + prop := setup.checker.GetPropertyOfType(t, params.Name) + if prop == nil { + return nil, nil + } + + return setup.newSymbolResponse(prop), nil +} + +// handleGetConstantValue returns the constant value of an enum member or const enum access. +func (s *Session) handleGetConstantValue(ctx context.Context, params *CheckerNodeParams) (any, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + node, err := setup.sd.resolveNodeHandle(setup.program, params.Location) + if err != nil { + return nil, err + } + if node == nil { + return nil, nil + } + + return literalValueToJSON(setup.checker.GetConstantValue(node)), nil +} + +// handleGetSignatureFromDeclaration returns the signature of a function-like declaration. +func (s *Session) handleGetSignatureFromDeclaration(ctx context.Context, params *CheckerNodeParams) (*SignatureResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + node, err := setup.sd.resolveNodeHandle(setup.program, params.Location) + if err != nil { + return nil, err + } + + return setup.newSignatureResponse(setup.checker.GetSignatureFromDeclaration(node)), nil +} + +// handleGetExportSpecifierLocalTargetSymbol returns the local target symbol of an export specifier. +func (s *Session) handleGetExportSpecifierLocalTargetSymbol(ctx context.Context, params *CheckerNodeParams) (*SymbolResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + node, err := setup.sd.resolveNodeHandle(setup.program, params.Location) + if err != nil { + return nil, err + } + if node == nil { + return nil, nil + } + + symbol := setup.checker.GetExportSpecifierLocalTargetSymbol(node) + if symbol == nil { + return nil, nil + } + + return setup.newSymbolResponse(symbol), nil +} + +// handleGetAliasedSymbol resolves an alias symbol to its target. +func (s *Session) handleGetAliasedSymbol(ctx context.Context, params *CheckerSymbolParams) (*SymbolResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + symbol, err := setup.resolveSymbolHandle(params.Symbol) + if err != nil { + return nil, err + } + + return setup.newSymbolResponse(setup.checker.GetAliasedSymbol(symbol)), nil +} + +// handleGetImmediateAliasedSymbol resolves one level of alias indirection. +func (s *Session) handleGetImmediateAliasedSymbol(ctx context.Context, params *CheckerSymbolParams) (*SymbolResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + symbol, err := setup.resolveSymbolHandle(params.Symbol) + if err != nil { + return nil, err + } + if symbol == nil { + return nil, nil + } + + aliased := setup.checker.GetImmediateAliasedSymbol(symbol) + if aliased == nil { + return nil, nil + } + + return setup.newSymbolResponse(aliased), nil +} + +// handleGetExportsOfModule returns the resolved exports of a module symbol, +// including those introduced by `export *` and re-exports. +func (s *Session) handleGetExportsOfModule(ctx context.Context, params *CheckerSymbolParams) ([]*SymbolResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + symbol, err := setup.resolveSymbolHandle(params.Symbol) + if err != nil { + return nil, err + } + if symbol == nil { + return nil, nil + } + + exports := setup.checker.GetExportsOfModule(symbol) + if len(exports) == 0 { + return nil, nil + } + slices.SortFunc(exports, setup.checker.CompareSymbols) + + results := make([]*SymbolResponse, len(exports)) + for i, exp := range exports { + results[i] = setup.newSymbolResponse(exp) + } + + return results, nil +} + +// handleGetMemberInModuleExports returns an export by name from a module symbol. +func (s *Session) handleGetMemberInModuleExports(ctx context.Context, params *GetMemberInModuleExportsParams) (*SymbolResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + symbol, err := setup.resolveSymbolHandle(params.Symbol) + if err != nil { + return nil, err + } + if symbol == nil { + return nil, nil + } + + member := setup.checker.TryGetMemberInModuleExports(params.Name, symbol) + if member == nil { + return nil, nil + } + + return setup.newSymbolResponse(member), nil +} + +// handleGetJSDocTags returns the JSDoc tags of a symbol as structured name/text pairs. +func (s *Session) handleGetJSDocTags(ctx context.Context, params *CheckerSymbolParams) ([]*JSDocTagInfo, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + symbol, err := setup.resolveSymbolHandle(params.Symbol) + if err != nil { + return nil, err + } + if symbol == nil { + return nil, nil + } + + langSvc, err := s.setupLanguageService(setup.sd, setup.program, params.Project, "") + if err != nil { + return nil, err + } + + tags := langSvc.GetSymbolJSDocTags(symbol) + if len(tags) == 0 { + return nil, nil + } + results := make([]*JSDocTagInfo, len(tags)) + for i, tag := range tags { + results[i] = &JSDocTagInfo{Name: tag.Name, Text: tag.Text} + } + return results, nil +} + +// handleGetDocumentationComment returns the rendered documentation comment of a symbol as plain text. +func (s *Session) handleGetDocumentationComment(ctx context.Context, params *CheckerSymbolParams) (string, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return "", err + } + defer setup.done() + + symbol, err := setup.resolveSymbolHandle(params.Symbol) + if err != nil { + return "", err + } + if symbol == nil { + return "", nil + } + + langSvc, err := s.setupLanguageService(setup.sd, setup.program, params.Project, "") + if err != nil { + return "", err + } + + return langSvc.GetSymbolDocumentationComment(setup.checker, symbol), nil +} + +// handleGetTypeArguments returns the type arguments of a type reference. +func (s *Session) handleGetTypeArguments(ctx context.Context, params *CheckerTypeParams) ([]*TypeResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + t, err := setup.resolveTypeHandle(params.Type) + if err != nil { + return nil, err + } + + typeArgs := setup.checker.GetTypeArguments(t) + if len(typeArgs) == 0 { + return nil, nil + } + + results := make([]*TypeResponse, len(typeArgs)) + for i, ta := range typeArgs { + results[i] = setup.newTypeResponse(ta) + } + + return results, nil +} + +func (s *Session) handleGetTrueTypeOfConditionalType(ctx context.Context, params *GetTypePropertyParams) (*TypeResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + t, err := setup.sd.resolveTypeHandle(params.Project, params.Type) + if err != nil { + return nil, err + } + + return setup.sd.newTypeResponse(params.Project, setup.checker.GetTrueTypeOfConditionalType(t)), nil +} + +func (s *Session) handleGetFalseTypeOfConditionalType(ctx context.Context, params *GetTypePropertyParams) (*TypeResponse, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + t, err := setup.sd.resolveTypeHandle(params.Project, params.Type) + if err != nil { + return nil, err + } + + return setup.sd.newTypeResponse(params.Project, setup.checker.GetFalseTypeOfConditionalType(t)), nil +} + +func (sd *snapshotData) resolveNodeHandle(program *compiler.Program, handle NodeHandle) (*ast.Node, error) { + s := string(handle) + // Format: "index.kind.path" — we need index and path, kind is informational only. + firstDot := strings.IndexByte(s, '.') + if firstDot == -1 { + return nil, fmt.Errorf("%w: invalid node handle %q", ErrClientError, handle) + } + secondDot := strings.IndexByte(s[firstDot+1:], '.') + if secondDot == -1 { + return nil, fmt.Errorf("%w: invalid node handle %q", ErrClientError, handle) + } + secondDot += firstDot + 1 // adjust to absolute index + + idx, err := strconv.ParseUint(s[:firstDot], 10, 32) + if err != nil { + return nil, fmt.Errorf("%w: invalid node handle %q: %w", ErrClientError, handle, err) + } + path := tspath.Path(s[secondDot+1:]) + + sourceFile := program.GetSourceFileByPath(path) + if sourceFile == nil { + return nil, fmt.Errorf("%w: node handle %q could not be resolved (file may not be loaded or handle may be stale)", ErrClientError, handle) + } + table := encoder.GetNodeIndexTable(sourceFile) + + if table != nil && idx < uint64(len(table.Nodes)) { + node := table.Nodes[idx] + if node != nil { + return node, nil + } + } + return nil, fmt.Errorf("%w: node handle %q could not be resolved (file may not be loaded or handle may be stale)", ErrClientError, handle) +} + +// computeSnapshotChanges computes the per-project source file differences between +// two snapshots. It uses DiffOrderedMaps on projects to find changed/removed projects, +// then DiffMaps on FilesByPath for each changed project to collect file-level changes. +func computeSnapshotChanges(prev *project.Snapshot, next *project.Snapshot) *SnapshotChanges { + prevProjects := prev.ProjectCollection.ProjectsByPath() + nextProjects := next.ProjectCollection.ProjectsByPath() + + var changes SnapshotChanges + + collections.DiffOrderedMaps( + prevProjects, nextProjects, + // onAdded: new project — nothing to retain from previous snapshot. + func(_ tspath.Path, _ *project.Project) {}, + // onRemoved: project removed entirely. + func(_ tspath.Path, oldProj *project.Project) { + changes.RemovedProjects = append(changes.RemovedProjects, ProjectHandle(oldProj)) + }, + // onModified: project changed, diff its files. + func(_ tspath.Path, oldProj *project.Project, newProj *project.Project) { + if oldProj.GetProgram() == newProj.GetProgram() { + return + } + var oldFiles, newFiles map[tspath.Path]*ast.SourceFile + if p := oldProj.GetProgram(); p != nil { + oldFiles = p.FilesByPath() + } + if p := newProj.GetProgram(); p != nil { + newFiles = p.FilesByPath() + } + var projectChanges ProjectFileChanges + core.DiffMaps( + oldFiles, newFiles, + nil, // onAdded: new file in project, not a change. + func(path tspath.Path, _ *ast.SourceFile) { + projectChanges.DeletedFiles = append(projectChanges.DeletedFiles, path) + }, + func(path tspath.Path, _ *ast.SourceFile, _ *ast.SourceFile) { + projectChanges.ChangedFiles = append(projectChanges.ChangedFiles, path) + }, + ) + if len(projectChanges.ChangedFiles) > 0 || len(projectChanges.DeletedFiles) > 0 { + if changes.ChangedProjects == nil { + changes.ChangedProjects = make(map[ProjectID]*ProjectFileChanges) + } + changes.ChangedProjects[ProjectHandle(newProj)] = &projectChanges + } + }, + ) + + return &changes +} + +// Close closes the session and releases all active snapshots, +// regardless of their ref counts. +func (s *Session) Close() { + s.releaseOpenRefs() + + s.snapshotsMu.Lock() + defer s.snapshotsMu.Unlock() + for handle, sd := range s.snapshots { + sd.snapshot.Deref(s.projectSession) + delete(s.snapshots, handle) + } +} + +// releaseOpenRefs releases every project and file ref this session is holding open +// in the project session. This keeps the API's ref counts balanced when an API +// session is shut down while sharing a longer-lived project session (e.g. one +// backing an LSP server), so API-opened projects and files aren't leaked. Only +// refs the session currently holds are closed, so it never over-releases. +func (s *Session) releaseOpenRefs() { + s.updateMu.Lock() + defer s.updateMu.Unlock() + + if s.openProjects.Len() == 0 && s.openFiles.Len() == 0 { + return + } + + apiRequest := &project.APISnapshotRequest{} + if s.openProjects.Len() > 0 { + apiRequest.CloseProjects = s.openProjects.Clone() + } + if s.openFiles.Len() > 0 { + apiRequest.CloseFiles = s.openFiles.Clone() + } + snapshot, err := s.projectSession.APIUpdate(context.Background(), project.FileChangeSummary{}, apiRequest) + // APIUpdate returns a ref'd snapshot even on error; always release it. + snapshot.Deref(s.projectSession) + if err != nil { + return + } + + s.openProjects.Clear() + s.openFiles.Clear() +} + +func formatSessionID(id uint64) string { + return fmt.Sprintf("api-session-%d", id) +} + +// toPath converts a file name to a normalized path. +func (s *Session) toPath(fileName string) tspath.Path { + return tspath.ToPath(fileName, s.projectSession.GetCurrentDirectory(), s.projectSession.FS().UseCaseSensitiveFileNames()) +} + +// toFileChangeSummary converts API file changes to a project.FileChangeSummary. +func (s *Session) toFileChangeSummary(changes *APIFileChanges) project.FileChangeSummary { + if changes == nil { + return project.FileChangeSummary{} + } + var summary project.FileChangeSummary + if changes.InvalidateAll { + summary.InvalidateAll = true + summary.IncludesWatchChangeOutsideNodeModules = true + return summary + } + cwd := s.projectSession.GetCurrentDirectory() + for _, doc := range changes.Changed { + uri := doc.ToURI(cwd) + summary.Changed.Add(uri) + } + for _, doc := range changes.Created { + uri := doc.ToURI(cwd) + summary.Created.Add(uri) + } + for _, doc := range changes.Deleted { + uri := doc.ToURI(cwd) + summary.Deleted.Add(uri) + } + if summary.Changed.Len()+summary.Created.Len()+summary.Deleted.Len() > 0 { + summary.IncludesWatchChangeOutsideNodeModules = true + } + return summary +} + +// handleGetSyntacticDiagnostics returns syntactic diagnostics for a file or all files. +func (s *Session) handleGetSyntacticDiagnostics(ctx context.Context, params *GetDiagnosticsParams) ([]*DiagnosticResponse, error) { + ctx = core.WithCheckerLifetime(ctx, core.CheckerLifetimeDiagnostics) + sd, err := s.getSnapshotData(params.Snapshot) + if err != nil { + return nil, err + } + + program, err := sd.getProgram(params.Project) + if err != nil { + return nil, err + } + + sourceFile, err := s.resolveOptionalSourceFile(program, params.File) + if err != nil { + return nil, err + } + + diags := program.GetSyntacticDiagnostics(ctx, sourceFile) + return NewDiagnosticResponses(diags), nil +} + +// handleGetBindDiagnostics returns bind diagnostics for a file or all files. +func (s *Session) handleGetBindDiagnostics(ctx context.Context, params *GetDiagnosticsParams) ([]*DiagnosticResponse, error) { + ctx = core.WithCheckerLifetime(ctx, core.CheckerLifetimeDiagnostics) + sd, err := s.getSnapshotData(params.Snapshot) + if err != nil { + return nil, err + } + + program, err := sd.getProgram(params.Project) + if err != nil { + return nil, err + } + + sourceFile, err := s.resolveOptionalSourceFile(program, params.File) + if err != nil { + return nil, err + } + + diags := program.GetBindDiagnostics(ctx, sourceFile) + return NewDiagnosticResponses(diags), nil +} + +// handleGetSemanticDiagnostics returns semantic diagnostics for a file or all files. +func (s *Session) handleGetSemanticDiagnostics(ctx context.Context, params *GetDiagnosticsParams) ([]*DiagnosticResponse, error) { + ctx = core.WithCheckerLifetime(ctx, core.CheckerLifetimeDiagnostics) + sd, err := s.getSnapshotData(params.Snapshot) + if err != nil { + return nil, err + } + + program, err := sd.getProgram(params.Project) + if err != nil { + return nil, err + } + + sourceFile, err := s.resolveOptionalSourceFile(program, params.File) + if err != nil { + return nil, err + } + + diags := program.GetSemanticDiagnostics(ctx, sourceFile) + return NewDiagnosticResponses(diags), nil +} + +// handleGetSuggestionDiagnostics returns suggestion diagnostics for a file or all files. +func (s *Session) handleGetSuggestionDiagnostics(ctx context.Context, params *GetDiagnosticsParams) ([]*DiagnosticResponse, error) { + ctx = core.WithCheckerLifetime(ctx, core.CheckerLifetimeDiagnostics) + sd, err := s.getSnapshotData(params.Snapshot) + if err != nil { + return nil, err + } + + program, err := sd.getProgram(params.Project) + if err != nil { + return nil, err + } + + sourceFile, err := s.resolveOptionalSourceFile(program, params.File) + if err != nil { + return nil, err + } + + diags := program.GetSuggestionDiagnostics(ctx, sourceFile) + return NewDiagnosticResponses(diags), nil +} + +// handleGetDeclarationDiagnostics returns declaration diagnostics for a file or all files. +func (s *Session) handleGetDeclarationDiagnostics(ctx context.Context, params *GetDiagnosticsParams) ([]*DiagnosticResponse, error) { + ctx = core.WithCheckerLifetime(ctx, core.CheckerLifetimeDiagnostics) + sd, err := s.getSnapshotData(params.Snapshot) + if err != nil { + return nil, err + } + + program, err := sd.getProgram(params.Project) + if err != nil { + return nil, err + } + + sourceFile, err := s.resolveOptionalSourceFile(program, params.File) + if err != nil { + return nil, err + } + + diags := program.GetDeclarationDiagnostics(ctx, sourceFile) + return NewDiagnosticResponses(diags), nil +} + +// handleGetConfigFileParsingDiagnostics returns config file parsing diagnostics. +func (s *Session) handleGetConfigFileParsingDiagnostics(ctx context.Context, params *GetProjectDiagnosticsParams) ([]*DiagnosticResponse, error) { + sd, err := s.getSnapshotData(params.Snapshot) + if err != nil { + return nil, err + } + + program, err := sd.getProgram(params.Project) + if err != nil { + return nil, err + } + + diags := program.GetConfigFileParsingDiagnostics() + return NewDiagnosticResponses(diags), nil +} + +// handleGetProgramDiagnostics returns program-wide diagnostics, including options diagnostics. +func (s *Session) handleGetProgramDiagnostics(ctx context.Context, params *GetProjectDiagnosticsParams) ([]*DiagnosticResponse, error) { + sd, err := s.getSnapshotData(params.Snapshot) + if err != nil { + return nil, err + } + + program, err := sd.getProgram(params.Project) + if err != nil { + return nil, err + } + + diags := program.GetProgramDiagnostics() + return NewDiagnosticResponses(diags), nil +} + +// handleGetGlobalDiagnostics returns global (non-file-specific) semantic diagnostics. +func (s *Session) handleGetGlobalDiagnostics(ctx context.Context, params *GetProjectDiagnosticsParams) ([]*DiagnosticResponse, error) { + ctx = core.WithCheckerLifetime(ctx, core.CheckerLifetimeDiagnostics) + sd, err := s.getSnapshotData(params.Snapshot) + if err != nil { + return nil, err + } + + proj, err := sd.getProject(params.Project) + if err != nil { + return nil, err + } + + program := proj.GetProgram() + if program == nil { + return nil, fmt.Errorf("%w: project has no program", ErrClientError) + } + + // Global diagnostics are accumulated lazily by the project's checker pool as + // files are checked. Force a full semantic pass so any global (non-file-specific) + // diagnostics are produced; otherwise this would return an empty result for + // projects using an external checker pool (the typical API case), since + // compiler.Program.GetGlobalDiagnostics only reports for the internal pool. + program.GetSemanticDiagnostics(ctx, nil) + + diags := core.Filter(proj.GetProjectDiagnostics(ctx), func(d *ast.Diagnostic) bool { + return d.File() == nil + }) + return NewDiagnosticResponses(diags), nil +} + +// resolveOptionalSourceFile resolves an optional DocumentIdentifier to a source file. +// Returns nil if the identifier is nil (meaning all files). +func (s *Session) resolveOptionalSourceFile(program *compiler.Program, file *DocumentIdentifier) (*ast.SourceFile, error) { + if file == nil { + return nil, nil + } + sourceFile := program.GetSourceFile(file.ToFileName()) + if sourceFile == nil { + return nil, fmt.Errorf("%w: source file not found: %v", ErrClientError, file) + } + return sourceFile, nil +} + +// handleGetReferencesToSymbolInFile returns node handles for all identifiers in a file that reference the given symbol. +func (s *Session) handleGetReferencesToSymbolInFile(ctx context.Context, params *GetReferencesToSymbolInFileParams) ([]NodeHandle, error) { + setup, err := s.setupChecker(ctx, params.Snapshot, params.Project) + if err != nil { + return nil, err + } + defer setup.done() + + symbol, err := setup.resolveSymbolHandle(params.Symbol) + if err != nil { + return nil, err + } + if symbol == nil { + return nil, nil + } + + sourceFile := setup.program.GetSourceFile(params.File.ToFileName()) + if sourceFile == nil { + return nil, fmt.Errorf("%w: source file not found: %v", ErrClientError, params.File) + } + + nodes := setup.checker.GetReferencesToSymbolInFile(sourceFile, symbol) + result := make([]NodeHandle, len(nodes)) + for i, node := range nodes { + result[i] = setup.sd.nodeHandleFrom(node) + } + return result, nil +} + +func (s *Session) handleGetSignatureUsages(ctx context.Context, params *GetSignatureUsagesParams) ([]SignatureUsageResponse, error) { + sd, err := s.getSnapshotData(params.Snapshot) + if err != nil { + return nil, err + } + program, err := sd.getProgram(params.Project) + if err != nil { + return nil, err + } + + signatureDecl, err := sd.resolveNodeHandle(program, params.SignatureDecl) + if err != nil { + return nil, err + } + if signatureDecl == nil { + return nil, nil + } + + langSvc, err := s.setupLanguageService(sd, program, params.Project, "") + if err != nil { + return nil, err + } + + usages := langSvc.GetSignatureUsages(ctx, signatureDecl) + if usages == nil { + return nil, nil + } + + result := make([]SignatureUsageResponse, 0, len(usages)) + for _, u := range usages { + entry := SignatureUsageResponse{ + Name: sd.nodeHandleFrom(u.Name), + } + if u.Call != nil { + entry.Call = sd.nodeHandleFrom(u.Call) + } + result = append(result, entry) + } + return result, nil +} + +// handleGetCompletionsAtPosition returns completions at a position in a document. +func (s *Session) handleGetCompletionsAtPosition(ctx context.Context, params *GetCompletionsAtPositionParams) (*CompletionInfoResponse, error) { + if params.IncludeSymbol { + ctx = core.WithCheckerLifetime(ctx, core.CheckerLifetimeAPI) + } + sd, err := s.getSnapshotData(params.Snapshot) + if err != nil { + return nil, err + } + program, err := sd.getProgram(params.Project) + if err != nil { + return nil, err + } + sourceFile := program.GetSourceFile(params.File.ToFileName()) + if sourceFile == nil { + return nil, nil + } + langSvc, err := s.setupLanguageService(sd, program, params.Project, "") + if err != nil { + return nil, err + } + positionMap := sourceFile.GetPositionMap() + internalPos := positionMap.UTF16ToUTF8(int(params.Position)) + result, err := langSvc.GetCompletionsAtPosition(ctx, sourceFile, internalPos, params.TriggerCharacter, params.IncludeSymbol) + if err != nil || result == nil { + return nil, err + } + entries := make([]*CompletionEntryResponse, 0, len(result.Items)) + for _, item := range result.Items { + entry := &CompletionEntryResponse{ + Name: item.Label, + SortText: item.SortText, + InsertText: item.InsertText, + FilterText: item.FilterText, + Detail: item.Detail, + } + if item.Kind != nil { + entry.Kind = uint32(*item.Kind) + } + if item.LabelDetails != nil { + entry.LabelDetails = &CompletionEntryLabelDetailsResponse{ + Detail: item.LabelDetails.Detail, + Description: item.LabelDetails.Description, + } + } + if item.Symbol != nil { + entry.Symbol = sd.newSymbolResponse(item.Symbol, params.Project) + } + entries = append(entries, entry) + } + return &CompletionInfoResponse{ + IsIncomplete: result.IsIncomplete, + Entries: entries, + }, nil +} + +// handleGetReferencedSymbolsForNode returns node handles for all references found at a node. +func (s *Session) handleGetReferencedSymbolsForNode(ctx context.Context, params *GetReferencedSymbolsForNodeParams) ([]ReferencedSymbolEntry, error) { + sd, err := s.getSnapshotData(params.Snapshot) + if err != nil { + return nil, err + } + program, err := sd.getProgram(params.Project) + if err != nil { + return nil, err + } + + node, err := sd.resolveNodeHandle(program, params.Node) + if err != nil { + return nil, err + } + if node == nil { + return nil, nil + } + + langSvc, err := s.setupLanguageService(sd, program, params.Project, "") + if err != nil { + return nil, err + } + + sourceFiles := program.GetSourceFiles() + entries := langSvc.GetReferencedSymbolsForNode(ctx, params.Position, node, sourceFiles) + if entries == nil { + return nil, nil + } + + var result []ReferencedSymbolEntry + for _, entry := range entries { + defNode := entry.DefinitionNode() + if defNode == nil { + continue + } + var refs []NodeHandle + for _, ref := range entry.References() { + if ref.IsNodeEntry() { + refs = append(refs, sd.nodeHandleFrom(ref.Node())) + } + } + re := ReferencedSymbolEntry{ + Definition: sd.nodeHandleFrom(defNode), + References: refs, + } + if sym := entry.DefinitionSymbol(); sym != nil { + re.Symbol = sd.newSymbolResponse(sym, params.Project) + } + result = append(result, re) + } + return result, nil +} diff --git a/tools/tsgo/internal/api/session_apistate_test.go b/tools/tsgo/internal/api/session_apistate_test.go new file mode 100644 index 00000000..91aab090 --- /dev/null +++ b/tools/tsgo/internal/api/session_apistate_test.go @@ -0,0 +1,255 @@ +package api + +import ( + "context" + "testing" + + "github.com/microsoft/typescript-go/internal/bundled" + "github.com/microsoft/typescript-go/internal/lsp/lsproto" + "github.com/microsoft/typescript-go/internal/testutil/projecttestutil" + "github.com/microsoft/typescript-go/internal/tspath" + "gotest.tools/v3/assert" +) + +// TestSessionTracksAndReleasesAPIRefs verifies that an API session holds at most +// one ref per opened project/file (opens are idempotent) and releases exactly +// those refs when the session is closed, so it never leaks or over-releases refs +// in the underlying (potentially shared) project session. +func TestSessionTracksAndReleasesAPIRefs(t *testing.T) { + t.Parallel() + if !bundled.Embedded { + t.Skip("bundled files are not embedded") + } + + t.Run("project opens are idempotent and released on close", func(t *testing.T) { + t.Parallel() + const configFileName = "/home/projects/p/tsconfig.json" + files := map[string]any{ + configFileName: `{ "compilerOptions": { "strict": true } }`, + "/home/projects/p/src/index.ts": `export const x = 1;`, + } + projectSession, _ := projecttestutil.Setup(files) + defer projectSession.Close() + session := NewSession(projectSession, nil) + + _, err := session.handleUpdateSnapshot(context.Background(), &UpdateSnapshotParams{ + OpenProjects: []DocumentIdentifier{{FileName: configFileName}}, + }) + assert.NilError(t, err) + assert.Equal(t, session.openProjects.Len(), 1) + + // Opening the same project again must not take an additional ref. + _, err = session.handleUpdateSnapshot(context.Background(), &UpdateSnapshotParams{ + OpenProjects: []DocumentIdentifier{{FileName: configFileName}}, + }) + assert.NilError(t, err) + assert.Equal(t, session.openProjects.Len(), 1) + + assert.Assert(t, projectSession.Snapshot().ProjectCollection.ConfiguredProject(tspath.Path(configFileName)) != nil) + + // Closing the session releases the single API ref, so the project is no + // longer kept loaded. + session.Close() + assert.Equal(t, session.openProjects.Len(), 0) + assert.Assert(t, projectSession.Snapshot().ProjectCollection.ConfiguredProject(tspath.Path(configFileName)) == nil) + }) + + t.Run("explicit close releases the project ref", func(t *testing.T) { + t.Parallel() + const configFileName = "/home/projects/p/tsconfig.json" + files := map[string]any{ + configFileName: `{ "compilerOptions": { "strict": true } }`, + "/home/projects/p/src/index.ts": `export const x = 1;`, + } + projectSession, _ := projecttestutil.Setup(files) + defer projectSession.Close() + session := NewSession(projectSession, nil) + defer session.Close() + + _, err := session.handleUpdateSnapshot(context.Background(), &UpdateSnapshotParams{ + OpenProjects: []DocumentIdentifier{{FileName: configFileName}}, + }) + assert.NilError(t, err) + assert.Equal(t, session.openProjects.Len(), 1) + assert.Assert(t, projectSession.Snapshot().ProjectCollection.ConfiguredProject(tspath.Path(configFileName)) != nil) + + // Closing a project we hold releases the ref and unloads the project. + _, err = session.handleUpdateSnapshot(context.Background(), &UpdateSnapshotParams{ + CloseProjects: []DocumentIdentifier{{FileName: configFileName}}, + }) + assert.NilError(t, err) + assert.Equal(t, session.openProjects.Len(), 0) + assert.Assert(t, projectSession.Snapshot().ProjectCollection.ConfiguredProject(tspath.Path(configFileName)) == nil) + + // Closing a project we don't hold is a no-op (never over-releases). + _, err = session.handleUpdateSnapshot(context.Background(), &UpdateSnapshotParams{ + CloseProjects: []DocumentIdentifier{{FileName: configFileName}}, + }) + assert.NilError(t, err) + assert.Equal(t, session.openProjects.Len(), 0) + }) + + t.Run("file opens are idempotent and released on close", func(t *testing.T) { + t.Parallel() + const fileName = "/home/projects/p/src/index.ts" + files := map[string]any{ + "/home/projects/p/tsconfig.json": `{ "compilerOptions": { "strict": true } }`, + fileName: `export const x = 1;`, + } + projectSession, _ := projecttestutil.Setup(files) + defer projectSession.Close() + session := NewSession(projectSession, nil) + + _, err := session.handleUpdateSnapshot(context.Background(), &UpdateSnapshotParams{ + OpenFiles: []DocumentIdentifier{{FileName: fileName}}, + }) + assert.NilError(t, err) + assert.Equal(t, session.openFiles.Len(), 1) + + // Re-opening the same file must not take an additional ref. + _, err = session.handleUpdateSnapshot(context.Background(), &UpdateSnapshotParams{ + OpenFiles: []DocumentIdentifier{{FileName: fileName}}, + }) + assert.NilError(t, err) + assert.Equal(t, session.openFiles.Len(), 1) + + // The file should resolve to the configured project via ancestor search. + assert.Assert(t, projectSession.Snapshot().ProjectCollection.ConfiguredProject(tspath.Path("/home/projects/p/tsconfig.json")) != nil) + + // Closing a file we don't hold is a no-op (never over-releases). + _, err = session.handleUpdateSnapshot(context.Background(), &UpdateSnapshotParams{ + CloseFiles: []DocumentIdentifier{{FileName: "/home/projects/p/other.ts"}}, + }) + assert.NilError(t, err) + assert.Equal(t, session.openFiles.Len(), 1) + + // Explicitly closing the held file releases the ref. + _, err = session.handleUpdateSnapshot(context.Background(), &UpdateSnapshotParams{ + CloseFiles: []DocumentIdentifier{{FileName: fileName}}, + }) + assert.NilError(t, err) + assert.Equal(t, session.openFiles.Len(), 0) + + // Closing the file also tears down the configured project that was + // auto-loaded to serve it, instead of leaking it. + assert.Assert(t, + projectSession.Snapshot().ProjectCollection.ConfiguredProject(tspath.Path("/home/projects/p/tsconfig.json")) == nil, + "configured project auto-loaded for the API-opened file should be unloaded after close", + ) + + session.Close() + assert.Equal(t, session.openFiles.Len(), 0) + }) + + t.Run("relative file paths normalize consistently for open and close", func(t *testing.T) { + t.Parallel() + // The project session's current directory is "/", so a relative path + // resolves to the corresponding absolute path. + files := map[string]any{ + "/src/tsconfig.json": `{ "compilerOptions": { "strict": true } }`, + "/src/index.ts": `export const x = 1;`, + } + projectSession, _ := projecttestutil.Setup(files) + defer projectSession.Close() + session := NewSession(projectSession, nil) + defer session.Close() + + // Open via a relative path; it should be tracked under the absolute path + // and resolve to the containing configured project. + openResp, err := session.handleUpdateSnapshot(context.Background(), &UpdateSnapshotParams{ + OpenFiles: []DocumentIdentifier{{FileName: "src/index.ts"}}, + }) + assert.NilError(t, err) + assert.Equal(t, session.openFiles.Len(), 1) + assert.Assert(t, session.openFiles.Has(tspath.Path("/src/index.ts"))) + assert.Assert(t, projectSession.Snapshot().ProjectCollection.ConfiguredProject(tspath.Path("/src/tsconfig.json")) != nil) + + // getDefaultProjectForFile must also resolve a relative path to the same + // configured project (it builds a URI from the identifier internally). + proj, err := session.handleGetDefaultProjectForFile(context.Background(), &GetDefaultProjectForFileParams{ + Snapshot: openResp.Snapshot, + File: DocumentIdentifier{FileName: "src/index.ts"}, + }) + assert.NilError(t, err) + assert.Assert(t, proj != nil, "relative path should resolve to a default project") + assert.Equal(t, proj.ConfigFileName, "/src/tsconfig.json") + + // Re-opening via the absolute path must match the relative open (no new ref). + _, err = session.handleUpdateSnapshot(context.Background(), &UpdateSnapshotParams{ + OpenFiles: []DocumentIdentifier{{FileName: "/src/index.ts"}}, + }) + assert.NilError(t, err) + assert.Equal(t, session.openFiles.Len(), 1) + + // Closing via a relative path must match the path stored when opening. + _, err = session.handleUpdateSnapshot(context.Background(), &UpdateSnapshotParams{ + CloseFiles: []DocumentIdentifier{{FileName: "src/index.ts"}}, + }) + assert.NilError(t, err) + assert.Equal(t, session.openFiles.Len(), 0) + assert.Assert(t, + projectSession.Snapshot().ProjectCollection.ConfiguredProject(tspath.Path("/src/tsconfig.json")) == nil, + "configured project should be unloaded after closing the relatively-pathed file", + ) + }) +} + +// TestUpdateSnapshotResponseSkipsUnloadedAncestorProject verifies that API +// updateSnapshot does not report unloaded ancestor project placeholders. This +// covers the case where opening a file loads its nearest configured project +// while solution search discovers an ancestor tsconfig placeholder whose command +// line is still nil. +func TestUpdateSnapshotResponseSkipsUnloadedAncestorProject(t *testing.T) { + t.Parallel() + if !bundled.Embedded { + t.Skip("bundled files are not embedded") + } + + const ( + nestedConfigFileName = "/repo/packages/app/tsconfig.json" + ancestorConfigFileName = "/repo/packages/tsconfig.json" + fileName = "/repo/packages/app/src/index.ts" + ) + files := map[string]any{ + ancestorConfigFileName: `{ "files": [] }`, + nestedConfigFileName: `{ + "compilerOptions": { "composite": true }, + "include": ["**/*"] + }`, + fileName: `let s: string = 1234;`, + } + projectSession, _ := projecttestutil.Setup(files) + defer projectSession.Close() + + projectSession.DidOpenFile(context.Background(), lsproto.DocumentUri("file://"+fileName), 1, files[fileName].(string), lsproto.LanguageKindTypeScript) + snapshot := projectSession.Snapshot() + nestedProject := snapshot.ProjectCollection.ConfiguredProject(tspath.Path(nestedConfigFileName)) + assert.Assert(t, nestedProject != nil) + assert.Assert(t, nestedProject.CommandLine != nil) + ancestorProject := snapshot.ProjectCollection.ConfiguredProject(tspath.Path(ancestorConfigFileName)) + assert.Assert(t, ancestorProject != nil) + assert.Assert(t, ancestorProject.CommandLine == nil) + + session := NewSession(projectSession, nil) + defer session.Close() + + response, err := session.handleUpdateSnapshot(context.Background(), &UpdateSnapshotParams{ + OpenProjects: []DocumentIdentifier{{FileName: nestedConfigFileName}}, + }) + assert.NilError(t, err) + + var foundNestedProject bool + var foundAncestorProject bool + for _, project := range response.Projects { + switch project.ConfigFileName { + case nestedConfigFileName: + foundNestedProject = true + assert.Assert(t, project.RootFiles != nil) + assert.Assert(t, project.CompilerOptions != nil) + case ancestorConfigFileName: + foundAncestorProject = true + } + } + assert.Assert(t, foundNestedProject) + assert.Assert(t, !foundAncestorProject) +} diff --git a/tools/tsgo/internal/api/session_completion_test.go b/tools/tsgo/internal/api/session_completion_test.go new file mode 100644 index 00000000..e666db1c --- /dev/null +++ b/tools/tsgo/internal/api/session_completion_test.go @@ -0,0 +1,141 @@ +package api + +import ( + "context" + "testing" + + "github.com/microsoft/typescript-go/internal/bundled" + "github.com/microsoft/typescript-go/internal/testutil/projecttestutil" + "gotest.tools/v3/assert" +) + +// TestCompletionSymbolTypeIsResolvable reproduces a crash where requesting the +// type of a completion-provided symbol panicked with a nil pointer dereference. +// +// Completion ran on an ephemeral query checker (default lifetime), so members of +// a generic type such as `string[]` (= Array) were returned as +// *instantiated* symbols whose per-checker instantiation links live only on that +// query checker. GetTypeOfSymbol runs on the persistent API checker — a +// different instance — where those links are absent, so getTypeOfInstantiatedSymbol +// dereferenced a nil target and brought down the connection. +// +// The fix pins symbol-producing completion to the API checker, so the returned +// handles resolve on the same checker the client re-queries. +func TestCompletionSymbolTypeIsResolvable(t *testing.T) { + t.Parallel() + if !bundled.Embedded { + t.Skip("bundled files are not embedded") + } + + const fileName = "/home/projects/p/src/index.ts" + // The caret sits right after `people.`, requesting members of `string[]`. + const content = "declare const people: string[];\npeople." + + files := map[string]any{ + "/home/projects/p/tsconfig.json": `{ "compilerOptions": { "strict": true } }`, + fileName: content, + } + projectSession, _ := projecttestutil.Setup(files) + defer projectSession.Close() + session := NewSession(projectSession, nil) + defer session.Close() + + ctx := context.Background() + + snapshotResp, err := session.handleUpdateSnapshot(ctx, &UpdateSnapshotParams{ + OpenFiles: []DocumentIdentifier{{FileName: fileName}}, + }) + assert.NilError(t, err) + + proj, err := session.handleGetDefaultProjectForFile(ctx, &GetDefaultProjectForFileParams{ + Snapshot: snapshotResp.Snapshot, + File: DocumentIdentifier{FileName: fileName}, + }) + assert.NilError(t, err) + assert.Assert(t, proj != nil, "file should resolve to a default project") + + // content is pure ASCII, so the UTF-16 caret offset equals the byte length. + completions, err := session.handleGetCompletionsAtPosition(ctx, &GetCompletionsAtPositionParams{ + Snapshot: snapshotResp.Snapshot, + Project: proj.Id, + File: DocumentIdentifier{FileName: fileName}, + Position: uint32(len(content)), + IncludeSymbol: true, + }) + assert.NilError(t, err) + assert.Assert(t, completions != nil, "expected a completion list for array members") + + // Resolving the type of every completion symbol must not panic, and known + // members like `push` must produce a concrete type. + var sawSymbol, sawPush bool + for _, entry := range completions.Entries { + if entry.Symbol == nil { + continue + } + sawSymbol = true + typeResp, err := session.handleGetTypeOfSymbol(ctx, &GetTypeOfSymbolParams{ + Snapshot: snapshotResp.Snapshot, + Project: proj.Id, + Symbol: entry.Symbol.Id, + }) + assert.NilError(t, err) + assert.Assert(t, typeResp != nil, "type of completion symbol %q should resolve", entry.Name) + if entry.Name == "push" { + sawPush = true + } + } + assert.Assert(t, sawSymbol, "completion entries should include resolvable symbols") + assert.Assert(t, sawPush, "array member completions should include `push`") +} + +// TestCompletionOnInferredProject reproduces a crash where requesting completions +// for a loose file — one not part of any tsconfig.json, so it resolves to an +// inferred project — panicked with "ConfigFilePath called on non-configured +// project". +// +// setupLanguageService called Project.ConfigFilePath(), which is only valid for +// configured projects and panics for inferred ones. The fix uses Project.ID(), +// which returns the project's path for both configured and inferred projects without panicking. +func TestCompletionOnInferredProject(t *testing.T) { + t.Parallel() + if !bundled.Embedded { + t.Skip("bundled files are not embedded") + } + + // No tsconfig.json anywhere, so this file belongs to an inferred project. + const fileName = "/home/projects/p/src/index.ts" + const content = "declare const people: string[];\npeople." + + files := map[string]any{ + fileName: content, + } + projectSession, _ := projecttestutil.Setup(files) + defer projectSession.Close() + session := NewSession(projectSession, nil) + defer session.Close() + + ctx := context.Background() + + snapshotResp, err := session.handleUpdateSnapshot(ctx, &UpdateSnapshotParams{ + OpenFiles: []DocumentIdentifier{{FileName: fileName}}, + }) + assert.NilError(t, err) + + proj, err := session.handleGetDefaultProjectForFile(ctx, &GetDefaultProjectForFileParams{ + Snapshot: snapshotResp.Snapshot, + File: DocumentIdentifier{FileName: fileName}, + }) + assert.NilError(t, err) + assert.Assert(t, proj != nil, "file should resolve to an inferred default project") + + // This request previously panicked in setupLanguageService. + // content is pure ASCII, so the UTF-16 caret offset equals the byte length. + completions, err := session.handleGetCompletionsAtPosition(ctx, &GetCompletionsAtPositionParams{ + Snapshot: snapshotResp.Snapshot, + Project: proj.Id, + File: DocumentIdentifier{FileName: fileName}, + Position: uint32(len(content)), + }) + assert.NilError(t, err) + assert.Assert(t, completions != nil, "expected a completion list for array members") +} diff --git a/tools/tsgo/internal/api/stringer_generated.go b/tools/tsgo/internal/api/stringer_generated.go new file mode 100644 index 00000000..fab0c57d --- /dev/null +++ b/tools/tsgo/internal/api/stringer_generated.go @@ -0,0 +1,30 @@ +// Code generated by "stringer -type=MessageType -output=stringer_generated.go"; DO NOT EDIT. + +package api + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[MessageTypeUnknown-0] + _ = x[MessageTypeRequest-1] + _ = x[MessageTypeCallResponse-2] + _ = x[MessageTypeCallError-3] + _ = x[MessageTypeResponse-4] + _ = x[MessageTypeError-5] + _ = x[MessageTypeCall-6] +} + +const _MessageType_name = "MessageTypeUnknownMessageTypeRequestMessageTypeCallResponseMessageTypeCallErrorMessageTypeResponseMessageTypeErrorMessageTypeCall" + +var _MessageType_index = [...]uint8{0, 18, 36, 59, 79, 98, 114, 129} + +func (i MessageType) String() string { + idx := int(i) - 0 + if i < 0 || idx >= len(_MessageType_index)-1 { + return "MessageType(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _MessageType_name[_MessageType_index[idx]:_MessageType_index[idx+1]] +} diff --git a/tools/tsgo/internal/api/timing.go b/tools/tsgo/internal/api/timing.go new file mode 100644 index 00000000..b1a3512c --- /dev/null +++ b/tools/tsgo/internal/api/timing.go @@ -0,0 +1,136 @@ +package api + +import ( + "sync" + "time" +) + +// serverRecentRequestCapacity is the number of most-recent requests retained in +// the server-side timing ring buffer. +const serverRecentRequestCapacity = 5 + +// serverRequestTiming is a single server-side request's processing-time sample. +type serverRequestTiming struct { + // Method is the API method that was handled. + Method string `json:"method"` + // ProcessingTimeMs is the wall-clock time the server spent handling the + // request, in milliseconds. + ProcessingTimeMs float64 `json:"processingTimeMs"` + // Timestamp is the Unix time in milliseconds when the request completed. + Timestamp int64 `json:"timestamp"` +} + +// serverTimingTotals holds running totals accumulated across every handled request. +type serverTimingTotals struct { + // RequestCount is the total number of requests measured. + RequestCount uint64 `json:"requestCount"` + // TotalProcessingTimeMs is the sum of server processing time, in milliseconds. + TotalProcessingTimeMs float64 `json:"totalProcessingTimeMs"` +} + +// serverTimingInfo is a point-in-time snapshot of collected server timing, +// returned to clients in response to a getServerTiming request. +type serverTimingInfo struct { + // Enabled reports whether server-side timing collection is active. + Enabled bool `json:"enabled"` + // Totals are the running totals across every handled request. + Totals serverTimingTotals `json:"totals"` + // RecentRequests are the most recent requests, oldest to newest, up to + // serverRecentRequestCapacity. + RecentRequests []serverRequestTiming `json:"recentRequests"` +} + +// timingCollector accumulates per-request server processing times into running +// totals and a fixed-size ring buffer of the most recent requests. It is safe +// for concurrent use so the async connection can record from multiple request +// goroutines. +type timingCollector struct { + mu sync.Mutex + totals serverTimingTotals + // ring holds up to serverRecentRequestCapacity entries; once full, head + // marks the oldest entry. + ring []serverRequestTiming + head int +} + +func newTimingCollector() *timingCollector { + return &timingCollector{} +} + +// record adds a single request's processing time to the totals and ring buffer. +func (c *timingCollector) record(method string, d time.Duration) { + processingMs := durationToMillis(d) + + c.mu.Lock() + defer c.mu.Unlock() + + c.totals.RequestCount++ + c.totals.TotalProcessingTimeMs += processingMs + + entry := serverRequestTiming{ + Method: method, + ProcessingTimeMs: processingMs, + Timestamp: time.Now().UnixMilli(), + } + if len(c.ring) < serverRecentRequestCapacity { + c.ring = append(c.ring, entry) + } else { + c.ring[c.head] = entry + c.head = (c.head + 1) % serverRecentRequestCapacity + } +} + +// snapshot returns a copy of the currently collected timing information, with +// recent requests ordered from oldest to newest. +func (c *timingCollector) snapshot() serverTimingInfo { + c.mu.Lock() + defer c.mu.Unlock() + + recent := make([]serverRequestTiming, 0, len(c.ring)) + for i := range c.ring { + recent = append(recent, c.ring[(c.head+i)%len(c.ring)]) + } + return serverTimingInfo{ + Enabled: true, + Totals: c.totals, + RecentRequests: recent, + } +} + +// reset clears all accumulated totals and recent-request history. +func (c *timingCollector) reset() { + c.mu.Lock() + defer c.mu.Unlock() + + c.totals = serverTimingTotals{} + c.ring = nil + c.head = 0 +} + +// serverTimingSnapshot returns the collector's snapshot, or a disabled snapshot +// when timing collection is not enabled (collector is nil). +func serverTimingSnapshot(c *timingCollector) serverTimingInfo { + if c == nil { + return disabledServerTimingInfo() + } + return c.snapshot() +} + +// disabledServerTimingInfo is the snapshot returned when timing collection is +// not enabled. +func disabledServerTimingInfo() serverTimingInfo { + return serverTimingInfo{ + Enabled: false, + RecentRequests: []serverRequestTiming{}, + } +} + +// durationToMillis converts a duration to fractional milliseconds, clamped to be +// non-negative. It preserves sub-microsecond precision by converting from the +// full nanosecond duration. +func durationToMillis(d time.Duration) float64 { + if d < 0 { + return 0 + } + return float64(d) / float64(time.Millisecond) +} diff --git a/tools/tsgo/internal/api/timing_test.go b/tools/tsgo/internal/api/timing_test.go new file mode 100644 index 00000000..4ca22ba2 --- /dev/null +++ b/tools/tsgo/internal/api/timing_test.go @@ -0,0 +1,95 @@ +package api + +import ( + "testing" + "time" + + "gotest.tools/v3/assert" +) + +func TestTimingCollector(t *testing.T) { + t.Parallel() + + t.Run("accumulates totals and records recent requests", func(t *testing.T) { + t.Parallel() + c := newTimingCollector() + c.record("getSourceFile", 2*time.Millisecond) + c.record("getSymbolAtPosition", 500*time.Microsecond) + + snap := c.snapshot() + assert.Equal(t, snap.Enabled, true) + assert.Equal(t, snap.Totals.RequestCount, uint64(2)) + assert.Equal(t, snap.Totals.TotalProcessingTimeMs, 2.5) + assert.Equal(t, len(snap.RecentRequests), 2) + assert.Equal(t, snap.RecentRequests[0].Method, "getSourceFile") + assert.Equal(t, snap.RecentRequests[0].ProcessingTimeMs, 2.0) + assert.Equal(t, snap.RecentRequests[1].Method, "getSymbolAtPosition") + assert.Equal(t, snap.RecentRequests[1].ProcessingTimeMs, 0.5) + }) + + t.Run("ring buffer retains only the most recent requests, oldest to newest", func(t *testing.T) { + t.Parallel() + c := newTimingCollector() + methods := []string{"a", "b", "c", "d", "e", "f", "g"} + for _, m := range methods { + c.record(m, time.Millisecond) + } + + snap := c.snapshot() + assert.Equal(t, snap.Totals.RequestCount, uint64(7)) + assert.Equal(t, len(snap.RecentRequests), serverRecentRequestCapacity) + + // Expect the last 5 methods, oldest to newest. + want := methods[len(methods)-serverRecentRequestCapacity:] + for i, w := range want { + assert.Equal(t, snap.RecentRequests[i].Method, w) + } + }) + + t.Run("negative durations clamp to zero", func(t *testing.T) { + t.Parallel() + c := newTimingCollector() + c.record("x", -5*time.Second) + snap := c.snapshot() + assert.Equal(t, snap.Totals.TotalProcessingTimeMs, 0.0) + assert.Equal(t, snap.RecentRequests[0].ProcessingTimeMs, 0.0) + }) +} + +func TestServerTimingSnapshotDisabled(t *testing.T) { + t.Parallel() + snap := serverTimingSnapshot(nil) + assert.Equal(t, snap.Enabled, false) + assert.Equal(t, snap.Totals.RequestCount, uint64(0)) + assert.Equal(t, len(snap.RecentRequests), 0) +} + +func TestTimingCollectorReset(t *testing.T) { + t.Parallel() + c := newTimingCollector() + c.record("a", time.Millisecond) + c.record("b", time.Millisecond) + c.reset() + + snap := c.snapshot() + assert.Equal(t, snap.Enabled, true) + assert.Equal(t, snap.Totals.RequestCount, uint64(0)) + assert.Equal(t, snap.Totals.TotalProcessingTimeMs, 0.0) + assert.Equal(t, len(snap.RecentRequests), 0) + + // The collector remains usable after a reset. + c.record("c", 2*time.Millisecond) + snap = c.snapshot() + assert.Equal(t, snap.Totals.RequestCount, uint64(1)) + assert.Equal(t, snap.RecentRequests[0].Method, "c") +} + +func TestDurationToMillis(t *testing.T) { + t.Parallel() + assert.Equal(t, durationToMillis(1500*time.Microsecond), 1.5) + assert.Equal(t, durationToMillis(0), 0.0) + assert.Equal(t, durationToMillis(-5*time.Second), 0.0) + // Sub-microsecond durations retain precision rather than truncating to 0. + assert.Equal(t, durationToMillis(500*time.Nanosecond), 0.0005) + assert.Equal(t, durationToMillis(1234*time.Nanosecond), 0.001234) +} diff --git a/tools/tsgo/internal/api/transport.go b/tools/tsgo/internal/api/transport.go new file mode 100644 index 00000000..4eb3d26a --- /dev/null +++ b/tools/tsgo/internal/api/transport.go @@ -0,0 +1,95 @@ +package api + +import ( + "io" + "net" +) + +// Transport is an interface for accepting connections from API clients. +type Transport interface { + // Accept waits for and returns the next connection. + Accept() (io.ReadWriteCloser, error) + // Close stops the transport from accepting new connections. + Close() error +} + +// PipeTransport accepts connections on a Unix domain socket or Windows named pipe. +type PipeTransport struct { + listener net.Listener +} + +// NewPipeTransport creates a new transport listening on the given path. +// On Unix, this creates a Unix domain socket. On Windows, this creates a named pipe. +func NewPipeTransport(path string) (*PipeTransport, error) { + listener, err := newPipeListener(path) + if err != nil { + return nil, err + } + return &PipeTransport{listener: listener}, nil +} + +// Accept implements Transport. +func (t *PipeTransport) Accept() (io.ReadWriteCloser, error) { + return t.listener.Accept() +} + +// Close implements Transport. +func (t *PipeTransport) Close() error { + return t.listener.Close() +} + +// Path returns the path of the pipe/socket. +func (t *PipeTransport) Path() string { + return t.listener.Addr().String() +} + +// StdioTransport wraps stdin/stdout as a single connection transport. +// It only accepts one connection. +type StdioTransport struct { + stdin io.ReadCloser + stdout io.WriteCloser + used bool +} + +// NewStdioTransport creates a transport using the given stdin/stdout. +func NewStdioTransport(stdin io.ReadCloser, stdout io.WriteCloser) *StdioTransport { + return &StdioTransport{ + stdin: stdin, + stdout: stdout, + } +} + +// Accept implements Transport. +func (t *StdioTransport) Accept() (io.ReadWriteCloser, error) { + if t.used { + return nil, io.EOF + } + t.used = true + return &stdioConn{ + Reader: t.stdin, + Writer: t.stdout, + stdin: t.stdin, + stdout: t.stdout, + }, nil +} + +// Close implements Transport. +func (t *StdioTransport) Close() error { + return nil +} + +type stdioConn struct { + io.Reader + io.Writer + stdin io.ReadCloser + stdout io.WriteCloser +} + +func (c *stdioConn) Close() error { + err1 := c.stdin.Close() + err2 := c.stdout.Close() + if err1 != nil { + return err1 + } + return err2 +} diff --git a/tools/tsgo/internal/api/transport_unix.go b/tools/tsgo/internal/api/transport_unix.go new file mode 100644 index 00000000..667ed7c0 --- /dev/null +++ b/tools/tsgo/internal/api/transport_unix.go @@ -0,0 +1,22 @@ +//go:build !windows + +package api + +import ( + "net" + "os" + "path" +) + +// newPipeListener creates a Unix domain socket listener. +func newPipeListener(path string) (net.Listener, error) { + // Remove any existing socket file + _ = os.Remove(path) //nolint:forbidigo + return net.Listen("unix", path) +} + +// GeneratePipePath returns a platform-appropriate pipe path for the given name. +func GeneratePipePath(name string) string { + //nolint:forbidigo + return path.Join(os.TempDir(), name) +} diff --git a/tools/tsgo/internal/api/transport_windows.go b/tools/tsgo/internal/api/transport_windows.go new file mode 100644 index 00000000..c54967f4 --- /dev/null +++ b/tools/tsgo/internal/api/transport_windows.go @@ -0,0 +1,19 @@ +//go:build windows + +package api + +import ( + "net" + + "github.com/Microsoft/go-winio" +) + +// newPipeListener creates a Windows named pipe listener. +func newPipeListener(path string) (net.Listener, error) { + return winio.ListenPipe(path, nil) +} + +// GeneratePipePath returns a platform-appropriate pipe path for the given name. +func GeneratePipePath(name string) string { + return `\\.\pipe\` + name +} diff --git a/tools/tsgo/internal/ast/ast.go b/tools/tsgo/internal/ast/ast.go new file mode 100644 index 00000000..6dcd606b --- /dev/null +++ b/tools/tsgo/internal/ast/ast.go @@ -0,0 +1,3059 @@ +package ast + +import ( + "fmt" + "iter" + "strings" + "sync" + "sync/atomic" + + "github.com/microsoft/typescript-go/internal/collections" + "github.com/microsoft/typescript-go/internal/core" + "github.com/microsoft/typescript-go/internal/stringutil" + "github.com/microsoft/typescript-go/internal/tspath" + "github.com/zeebo/xxh3" +) + +// parseJSDocForNode is the package-level function for lazily parsing JSDoc. +// It is set by the parser package via init(). +var parseJSDocForNode func(*SourceFile, *Node) []*Node + +// SetParseJSDocForNode registers the lazy JSDoc parse function. Called from parser's init(). +func SetParseJSDocForNode(fn func(*SourceFile, *Node) []*Node) { + parseJSDocForNode = fn +} + +// Visitor + +type Visitor func(*Node) bool + +func visit(v Visitor, node *Node) bool { + if node != nil { + return v(node) + } + return false +} + +func visitNodes(v Visitor, nodes []*Node) bool { + for _, node := range nodes { //nolint:modernize + if v(node) { + return true + } + } + return false +} + +func visitNodeList(v Visitor, nodeList *NodeList) bool { + if nodeList != nil { + return visitNodes(v, nodeList.Nodes) + } + return false +} + +func visitModifiers(v Visitor, modifiers *ModifierList) bool { + if modifiers != nil { + return visitNodes(v, modifiers.Nodes) + } + return false +} + +type NodeFactoryHooks struct { + OnCreate func(node *Node) // Hooks the creation of a node. + OnUpdate func(node *Node, original *Node) // Hooks the updating of a node. + OnClone func(node *Node, original *Node) // Hooks the cloning of a node. +} + +type NodeFactoryCoercible interface { + AsNodeFactory() *NodeFactory +} + +func NewNodeFactory(hooks NodeFactoryHooks) *NodeFactory { + return &NodeFactory{hooks: hooks} +} + +func newNode(kind Kind, data nodeData, hooks NodeFactoryHooks) *Node { + n := data.AsNode() + n.Loc = core.UndefinedTextRange() + n.Kind = kind + n.data = data + if hooks.OnCreate != nil { + hooks.OnCreate(n) + } + return n +} + +func (f *NodeFactory) newNode(kind Kind, data nodeData) *Node { + f.nodeCount++ + return newNode(kind, data, f.hooks) +} + +func (f *NodeFactory) NodeCount() int { + return f.nodeCount +} + +func (f *NodeFactory) TextCount() int { + return f.textCount +} + +func (f *NodeFactory) AsNodeFactory() *NodeFactory { + return f +} + +func updateNode(updated *Node, original *Node, hooks NodeFactoryHooks) *Node { + if updated != original { + updated.Flags = original.Flags + updated.Loc = original.Loc + if hooks.OnUpdate != nil { + hooks.OnUpdate(updated, original) + } + } + return updated +} + +func cloneNode(updated *Node, original *Node, hooks NodeFactoryHooks) *Node { + updateNode(updated, original, hooks) + if updated != original && hooks.OnClone != nil { + hooks.OnClone(updated, original) + } + return updated +} + +// NodeList + +type NodeList struct { + Loc core.TextRange + Nodes []*Node +} + +func (f *NodeFactory) NewNodeList(nodes []*Node) *NodeList { + list := f.nodeListArena.New() + list.Loc = core.UndefinedTextRange() + list.Nodes = nodes + return list +} + +func (list *NodeList) Pos() int { return list.Loc.Pos() } +func (list *NodeList) End() int { return list.Loc.End() } + +func (list *NodeList) HasTrailingComma() bool { + if len(list.Nodes) == 0 { + return false + } + last := list.Nodes[len(list.Nodes)-1] + return last.End() < list.End() +} + +func (list *NodeList) Clone(f NodeFactoryCoercible) *NodeList { + result := f.AsNodeFactory().NewNodeList(list.Nodes) + result.Loc = list.Loc + return result +} + +// ModifierList + +type ModifierList struct { + NodeList + ModifierFlags ModifierFlags +} + +func (f *NodeFactory) NewModifierList(nodes []*Node) *ModifierList { + list := f.modifierListArena.New() + list.Loc = core.UndefinedTextRange() + list.Nodes = nodes + list.ModifierFlags = ModifiersToFlags(nodes) + return list +} + +func (list *ModifierList) Clone(f *NodeFactory) *ModifierList { + res := f.modifierListArena.New() + res.Loc = list.Loc + res.Nodes = list.Nodes + res.ModifierFlags = list.ModifierFlags + return res +} + +// AST Node +// Interface values stored in AST nodes are never typed nil values. Construction code must ensure that +// interface valued properties either store a true nil or a reference to a non-nil struct. + +type Node struct { + Kind Kind + Flags NodeFlags + Loc core.TextRange + id atomic.Uint64 + Parent *Node + data nodeData +} + +// Node accessors. Some accessors are implemented as methods on NodeData, others are implemented though +// type switches. Either approach is fine. Interface methods are likely more performant, but have higher +// code size costs because we have hundreds of implementations of the NodeData interface. + +func (n *Node) AsNode() *Node { return n } +func (n *Node) Pos() int { return n.Loc.Pos() } +func (n *Node) End() int { return n.Loc.End() } +func (n *Node) IterChildren() iter.Seq[*Node] { + // Implemented directly (rather than through the nodeData interface) so that the + // returned iterator and the visitor closure it passes to ForEachChild do not + // escape: an interface call is opaque to escape analysis. `true` stops a TS + // visitor early, whereas `false` stops a Go iterator yield, so the result is + // inverted. + return func(yield func(*Node) bool) { + n.ForEachChild(func(child *Node) bool { + return !yield(child) + }) + } +} +func (n *Node) Clone(f NodeFactoryCoercible) *Node { return n.data.Clone(f) } +func (n *Node) VisitEachChild(v *NodeVisitor) *Node { return n.data.VisitEachChild(v) } +func (n *Node) Name() *DeclarationName { return n.data.Name() } +func (n *Node) Modifiers() *ModifierList { return n.data.Modifiers() } +func (n *Node) FlowNodeData() *FlowNodeBase { return n.data.FlowNodeData() } +func (n *Node) DeclarationData() *DeclarationBase { return n.data.DeclarationData() } +func (n *Node) ExportableData() *ExportableBase { return n.data.ExportableData() } +func (n *Node) LocalsContainerData() *LocalsContainerBase { return n.data.LocalsContainerData() } +func (n *Node) FunctionLikeData() *FunctionLikeBase { return n.data.FunctionLikeData() } +func (n *Node) ParameterList() *ParameterList { return n.data.FunctionLikeData().Parameters } +func (n *Node) Parameters() []*ParameterDeclarationNode { return n.ParameterList().Nodes } +func (n *Node) ClassLikeData() *ClassLikeBase { return n.data.ClassLikeData() } +func (n *Node) BodyData() *BodyBase { return n.data.BodyData() } +func (n *Node) SubtreeFacts() SubtreeFacts { return n.data.SubtreeFacts() } +func (n *Node) propagateSubtreeFacts() SubtreeFacts { return n.data.propagateSubtreeFacts() } +func (n *Node) LiteralLikeData() *LiteralLikeNodeBase { return n.data.LiteralLikeData() } +func (n *Node) TemplateLiteralLikeData() *TemplateLiteralLikeNodeBase { + return n.data.TemplateLiteralLikeData() +} +func (n *Node) KindString() string { return n.Kind.String() } +func (n *Node) KindValue() int16 { return int16(n.Kind) } +func (n *Node) Decorators() []*Node { + if n.Modifiers() == nil { + return nil + } + return core.Filter(n.Modifiers().Nodes, IsDecorator) +} + +type MutableNode Node + +func (n *Node) AsMutable() *MutableNode { return (*MutableNode)(n) } +func (n *MutableNode) SetModifiers(modifiers *ModifierList) { n.data.setModifiers(modifiers) } + +func (n *Node) Symbol() *Symbol { + data := n.DeclarationData() + if data != nil { + return data.Symbol + } + return nil +} + +func (n *Node) LocalSymbol() *Symbol { + data := n.ExportableData() + if data != nil { + return data.LocalSymbol + } + return nil +} + +func (n *Node) Locals() SymbolTable { + data := n.LocalsContainerData() + if data != nil { + return data.Locals + } + return nil +} + +func (n *Node) Body() *Node { + data := n.BodyData() + if data != nil { + return data.Body + } + return nil +} + +func (n *Node) Text() string { + switch n.Kind { + case KindIdentifier: + return n.AsIdentifier().Text + case KindPrivateIdentifier: + return n.AsPrivateIdentifier().Text + case KindStringLiteral: + return n.AsStringLiteral().Text + case KindNumericLiteral: + return n.AsNumericLiteral().Text + case KindBigIntLiteral: + return n.AsBigIntLiteral().Text + case KindMetaProperty: + return n.AsMetaProperty().Name().Text() + case KindNoSubstitutionTemplateLiteral: + return n.AsNoSubstitutionTemplateLiteral().Text + case KindTemplateHead: + return n.AsTemplateHead().Text + case KindTemplateMiddle: + return n.AsTemplateMiddle().Text + case KindTemplateTail: + return n.AsTemplateTail().Text + case KindJsxNamespacedName: + return n.AsJsxNamespacedName().Namespace.Text() + ":" + n.AsJsxNamespacedName().name.Text() + case KindRegularExpressionLiteral: + return n.AsRegularExpressionLiteral().Text + case KindJSDocText: + return strings.Join(n.AsJSDocText().text, "") + case KindJSDocLink: + return strings.Join(n.AsJSDocLink().text, "") + case KindJSDocLinkCode: + return strings.Join(n.AsJSDocLinkCode().text, "") + case KindJSDocLinkPlain: + return strings.Join(n.AsJSDocLinkPlain().text, "") + } + panic(fmt.Sprintf("Unhandled case in Node.Text: %T", n.data)) +} + +func (n *Node) Expression() *Node { + switch n.Kind { + case KindPropertyAccessExpression: + return n.AsPropertyAccessExpression().Expression + case KindElementAccessExpression: + return n.AsElementAccessExpression().Expression + case KindParenthesizedExpression: + return n.AsParenthesizedExpression().Expression + case KindCallExpression: + return n.AsCallExpression().Expression + case KindNewExpression: + return n.AsNewExpression().Expression + case KindExpressionWithTypeArguments: + return n.AsExpressionWithTypeArguments().Expression + case KindComputedPropertyName: + return n.AsComputedPropertyName().Expression + case KindNonNullExpression: + return n.AsNonNullExpression().Expression + case KindTypeAssertionExpression: + return n.AsTypeAssertion().Expression + case KindAsExpression: + return n.AsAsExpression().Expression + case KindSatisfiesExpression: + return n.AsSatisfiesExpression().Expression + case KindTypeOfExpression: + return n.AsTypeOfExpression().Expression + case KindSpreadAssignment: + return n.AsSpreadAssignment().Expression + case KindSpreadElement: + return n.AsSpreadElement().Expression + case KindTemplateSpan: + return n.AsTemplateSpan().Expression + case KindDeleteExpression: + return n.AsDeleteExpression().Expression + case KindVoidExpression: + return n.AsVoidExpression().Expression + case KindAwaitExpression: + return n.AsAwaitExpression().Expression + case KindYieldExpression: + return n.AsYieldExpression().Expression + case KindPartiallyEmittedExpression: + return n.AsPartiallyEmittedExpression().Expression + case KindIfStatement: + return n.AsIfStatement().Expression + case KindDoStatement: + return n.AsDoStatement().Expression + case KindWhileStatement: + return n.AsWhileStatement().Expression + case KindWithStatement: + return n.AsWithStatement().Expression + case KindForInStatement, KindForOfStatement: + return n.AsForInOrOfStatement().Expression + case KindSwitchStatement: + return n.AsSwitchStatement().Expression + case KindCaseClause: + return n.AsCaseOrDefaultClause().Expression + case KindExpressionStatement: + return n.AsExpressionStatement().Expression + case KindReturnStatement: + return n.AsReturnStatement().Expression + case KindThrowStatement: + return n.AsThrowStatement().Expression + case KindExternalModuleReference: + return n.AsExternalModuleReference().Expression + case KindExportAssignment: + return n.AsExportAssignment().Expression + case KindDecorator: + return n.AsDecorator().Expression + case KindJsxExpression: + return n.AsJsxExpression().Expression + case KindJsxSpreadAttribute: + return n.AsJsxSpreadAttribute().Expression + } + panic("Unhandled case in Node.Expression: " + n.Kind.String()) +} + +func (n *Node) RawText() string { + switch n.Kind { + case KindTemplateHead: + return n.AsTemplateHead().RawText + case KindTemplateMiddle: + return n.AsTemplateMiddle().RawText + case KindTemplateTail: + return n.AsTemplateTail().RawText + } + panic("Unhandled case in Node.RawText: " + n.Kind.String()) +} + +func (m *MutableNode) SetExpression(expr *Node) { + n := (*Node)(m) + switch n.Kind { + case KindPropertyAccessExpression: + n.AsPropertyAccessExpression().Expression = expr + case KindElementAccessExpression: + n.AsElementAccessExpression().Expression = expr + case KindParenthesizedExpression: + n.AsParenthesizedExpression().Expression = expr + case KindCallExpression: + n.AsCallExpression().Expression = expr + case KindNewExpression: + n.AsNewExpression().Expression = expr + case KindExpressionWithTypeArguments: + n.AsExpressionWithTypeArguments().Expression = expr + case KindComputedPropertyName: + n.AsComputedPropertyName().Expression = expr + case KindNonNullExpression: + n.AsNonNullExpression().Expression = expr + case KindTypeAssertionExpression: + n.AsTypeAssertion().Expression = expr + case KindAsExpression: + n.AsAsExpression().Expression = expr + case KindSatisfiesExpression: + n.AsSatisfiesExpression().Expression = expr + case KindTypeOfExpression: + n.AsTypeOfExpression().Expression = expr + case KindSpreadAssignment: + n.AsSpreadAssignment().Expression = expr + case KindSpreadElement: + n.AsSpreadElement().Expression = expr + case KindTemplateSpan: + n.AsTemplateSpan().Expression = expr + case KindDeleteExpression: + n.AsDeleteExpression().Expression = expr + case KindVoidExpression: + n.AsVoidExpression().Expression = expr + case KindAwaitExpression: + n.AsAwaitExpression().Expression = expr + case KindYieldExpression: + n.AsYieldExpression().Expression = expr + case KindPartiallyEmittedExpression: + n.AsPartiallyEmittedExpression().Expression = expr + case KindIfStatement: + n.AsIfStatement().Expression = expr + case KindDoStatement: + n.AsDoStatement().Expression = expr + case KindWhileStatement: + n.AsWhileStatement().Expression = expr + case KindWithStatement: + n.AsWithStatement().Expression = expr + case KindForInStatement, KindForOfStatement: + n.AsForInOrOfStatement().Expression = expr + case KindSwitchStatement: + n.AsSwitchStatement().Expression = expr + case KindCaseClause: + n.AsCaseOrDefaultClause().Expression = expr + case KindExpressionStatement: + n.AsExpressionStatement().Expression = expr + case KindReturnStatement: + n.AsReturnStatement().Expression = expr + case KindThrowStatement: + n.AsThrowStatement().Expression = expr + case KindExternalModuleReference: + n.AsExternalModuleReference().Expression = expr + case KindExportAssignment: + n.AsExportAssignment().Expression = expr + case KindDecorator: + n.AsDecorator().Expression = expr + case KindJsxExpression: + n.AsJsxExpression().Expression = expr + case KindJsxSpreadAttribute: + n.AsJsxSpreadAttribute().Expression = expr + default: + panic("Unhandled case in mutableNode.SetExpression: " + n.Kind.String()) + } +} + +func (n *Node) ArgumentList() *NodeList { + switch n.Kind { + case KindCallExpression: + return n.AsCallExpression().Arguments + case KindNewExpression: + return n.AsNewExpression().Arguments + } + panic("Unhandled case in Node.Arguments: " + n.Kind.String()) +} + +func (n *Node) Arguments() []*Node { + list := n.ArgumentList() + if list != nil { + return list.Nodes + } + return nil +} + +func (n *Node) TypeArgumentList() *NodeList { + switch n.Kind { + case KindCallExpression: + return n.AsCallExpression().TypeArguments + case KindNewExpression: + return n.AsNewExpression().TypeArguments + case KindTaggedTemplateExpression: + return n.AsTaggedTemplateExpression().TypeArguments + case KindTypeReference: + return n.AsTypeReferenceNode().TypeArguments + case KindExpressionWithTypeArguments: + return n.AsExpressionWithTypeArguments().TypeArguments + case KindImportType: + return n.AsImportTypeNode().TypeArguments + case KindTypeQuery: + return n.AsTypeQueryNode().TypeArguments + case KindJsxOpeningElement: + return n.AsJsxOpeningElement().TypeArguments + case KindJsxSelfClosingElement: + return n.AsJsxSelfClosingElement().TypeArguments + } + panic("Unhandled case in Node.TypeArguments") +} + +func (n *Node) TypeArguments() []*Node { + list := n.TypeArgumentList() + if list != nil { + return list.Nodes + } + return nil +} + +func (n *Node) TypeParameterList() *NodeList { + switch n.Kind { + case KindClassDeclaration: + return n.AsClassDeclaration().TypeParameters + case KindClassExpression: + return n.AsClassExpression().TypeParameters + case KindInterfaceDeclaration: + return n.AsInterfaceDeclaration().TypeParameters + case KindTypeAliasDeclaration, KindJSTypeAliasDeclaration: + return n.AsTypeAliasDeclaration().TypeParameters + case KindJSDocTemplateTag: + return n.AsJSDocTemplateTag().TypeParameters + default: + funcLike := n.FunctionLikeData() + if funcLike != nil { + return funcLike.TypeParameters + } + } + panic("Unhandled case in Node.TypeParameterList") +} + +func (n *Node) TypeParameters() []*Node { + list := n.TypeParameterList() + if list != nil { + return list.Nodes + } + return nil +} + +func (n *Node) MemberList() *NodeList { + switch n.Kind { + case KindClassDeclaration: + return n.AsClassDeclaration().Members + case KindClassExpression: + return n.AsClassExpression().Members + case KindInterfaceDeclaration: + return n.AsInterfaceDeclaration().Members + case KindEnumDeclaration: + return n.AsEnumDeclaration().Members + case KindTypeLiteral: + return n.AsTypeLiteralNode().Members + case KindMappedType: + return n.AsMappedTypeNode().Members + } + panic("Unhandled case in Node.MemberList: " + n.Kind.String()) +} + +func (n *Node) Members() []*Node { + list := n.MemberList() + if list != nil { + return list.Nodes + } + return nil +} + +func (n *Node) StatementList() *NodeList { + switch n.Kind { + case KindSourceFile: + return n.AsSourceFile().Statements + case KindBlock: + return n.AsBlock().Statements + case KindModuleBlock: + return n.AsModuleBlock().Statements + case KindCaseClause, KindDefaultClause: + return n.AsCaseOrDefaultClause().Statements + } + panic("Unhandled case in Node.StatementList: " + n.Kind.String()) +} + +func (n *Node) Statements() []*Node { + list := n.StatementList() + if list != nil { + return list.Nodes + } + return nil +} + +func (n *Node) CanHaveStatements() bool { + switch n.Kind { + case KindSourceFile, KindBlock, KindModuleBlock, KindCaseClause, KindDefaultClause: + return true + default: + return false + } +} + +func (n *Node) ModifierFlags() ModifierFlags { + modifiers := n.Modifiers() + if modifiers != nil { + return modifiers.ModifierFlags + } + return ModifierFlagsNone +} + +func (n *Node) ModifierNodes() []*Node { + modifiers := n.Modifiers() + if modifiers != nil { + return modifiers.Nodes + } + return nil +} + +func (n *Node) Type() *Node { + switch n.Kind { + case KindVariableDeclaration: + return n.AsVariableDeclaration().Type + case KindParameter: + return n.AsParameterDeclaration().Type + case KindPropertySignature: + return n.AsPropertySignatureDeclaration().Type + case KindPropertyDeclaration: + return n.AsPropertyDeclaration().Type + case KindPropertyAssignment: + return n.AsPropertyAssignment().Type + case KindShorthandPropertyAssignment: + return n.AsShorthandPropertyAssignment().Type + case KindTypePredicate: + return n.AsTypePredicateNode().Type + case KindParenthesizedType: + return n.AsParenthesizedTypeNode().Type + case KindTypeOperator: + return n.AsTypeOperatorNode().Type + case KindMappedType: + return n.AsMappedTypeNode().Type + case KindTypeAssertionExpression: + return n.AsTypeAssertion().Type + case KindAsExpression: + return n.AsAsExpression().Type + case KindSatisfiesExpression: + return n.AsSatisfiesExpression().Type + case KindTypeAliasDeclaration, KindJSTypeAliasDeclaration: + return n.AsTypeAliasDeclaration().Type + case KindNamedTupleMember: + return n.AsNamedTupleMember().Type + case KindOptionalType: + return n.AsOptionalTypeNode().Type + case KindRestType: + return n.AsRestTypeNode().Type + case KindTemplateLiteralTypeSpan: + return n.AsTemplateLiteralTypeSpan().Type + case KindJSDocTypeExpression: + return n.AsJSDocTypeExpression().Type + case KindJSDocParameterTag, KindJSDocPropertyTag: + return n.AsJSDocParameterOrPropertyTag().TypeExpression + case KindJSDocNullableType: + return n.AsJSDocNullableType().Type + case KindJSDocNonNullableType: + return n.AsJSDocNonNullableType().Type + case KindJSDocOptionalType: + return n.AsJSDocOptionalType().Type + case KindExportAssignment: + return n.AsExportAssignment().Type + case KindBinaryExpression: + return n.AsBinaryExpression().Type + default: + if funcLike := n.FunctionLikeData(); funcLike != nil { + return funcLike.Type + } + } + return nil +} + +func (m *MutableNode) SetType(t *Node) { + n := (*Node)(m) + switch m.Kind { + case KindVariableDeclaration: + n.AsVariableDeclaration().Type = t + case KindParameter: + n.AsParameterDeclaration().Type = t + case KindPropertySignature: + n.AsPropertySignatureDeclaration().Type = t + case KindPropertyDeclaration: + n.AsPropertyDeclaration().Type = t + case KindPropertyAssignment: + n.AsPropertyAssignment().Type = t + case KindShorthandPropertyAssignment: + n.AsShorthandPropertyAssignment().Type = t + case KindTypePredicate: + n.AsTypePredicateNode().Type = t + case KindParenthesizedType: + n.AsParenthesizedTypeNode().Type = t + case KindTypeOperator: + n.AsTypeOperatorNode().Type = t + case KindMappedType: + n.AsMappedTypeNode().Type = t + case KindTypeAssertionExpression: + n.AsTypeAssertion().Type = t + case KindAsExpression: + n.AsAsExpression().Type = t + case KindSatisfiesExpression: + n.AsSatisfiesExpression().Type = t + case KindTypeAliasDeclaration, KindJSTypeAliasDeclaration: + n.AsTypeAliasDeclaration().Type = t + case KindNamedTupleMember: + n.AsNamedTupleMember().Type = t + case KindOptionalType: + n.AsOptionalTypeNode().Type = t + case KindRestType: + n.AsRestTypeNode().Type = t + case KindTemplateLiteralTypeSpan: + n.AsTemplateLiteralTypeSpan().Type = t + case KindJSDocTypeExpression: + n.AsJSDocTypeExpression().Type = t + case KindJSDocParameterTag, KindJSDocPropertyTag: + n.AsJSDocParameterOrPropertyTag().TypeExpression = t + case KindJSDocNullableType: + n.AsJSDocNullableType().Type = t + case KindJSDocNonNullableType: + n.AsJSDocNonNullableType().Type = t + case KindJSDocOptionalType: + n.AsJSDocOptionalType().Type = t + case KindExportAssignment: + n.AsExportAssignment().Type = t + case KindBinaryExpression: + n.AsBinaryExpression().Type = t + default: + if funcLike := n.FunctionLikeData(); funcLike != nil { + funcLike.Type = t + } else { + panic("Unhandled case in mutableNode.SetType: " + n.Kind.String()) + } + } +} + +func (n *Node) Initializer() *Node { + switch n.Kind { + case KindVariableDeclaration: + return n.AsVariableDeclaration().Initializer + case KindParameter: + return n.AsParameterDeclaration().Initializer + case KindBindingElement: + return n.AsBindingElement().Initializer + case KindPropertyDeclaration: + return n.AsPropertyDeclaration().Initializer + case KindPropertySignature: + return n.AsPropertySignatureDeclaration().Initializer + case KindPropertyAssignment: + return n.AsPropertyAssignment().Initializer + case KindEnumMember: + return n.AsEnumMember().Initializer + case KindForStatement: + return n.AsForStatement().Initializer + case KindForInStatement, KindForOfStatement: + return n.AsForInOrOfStatement().Initializer + case KindJsxAttribute: + return n.AsJsxAttribute().Initializer + } + panic("Unhandled case in Node.Initializer") +} + +func (m *MutableNode) SetInitializer(initializer *Node) { + n := (*Node)(m) + switch n.Kind { + case KindVariableDeclaration: + n.AsVariableDeclaration().Initializer = initializer + case KindParameter: + n.AsParameterDeclaration().Initializer = initializer + case KindBindingElement: + n.AsBindingElement().Initializer = initializer + case KindPropertyDeclaration: + n.AsPropertyDeclaration().Initializer = initializer + case KindPropertySignature: + n.AsPropertySignatureDeclaration().Initializer = initializer + case KindPropertyAssignment: + n.AsPropertyAssignment().Initializer = initializer + case KindEnumMember: + n.AsEnumMember().Initializer = initializer + case KindForStatement: + n.AsForStatement().Initializer = initializer + case KindForInStatement, KindForOfStatement: + n.AsForInOrOfStatement().Initializer = initializer + case KindJsxAttribute: + n.AsJsxAttribute().Initializer = initializer + default: + panic("Unhandled case in mutableNode.SetInitializer") + } +} + +func (n *Node) TagName() *Node { + switch n.Kind { + case KindJsxOpeningElement: + return n.AsJsxOpeningElement().TagName + case KindJsxClosingElement: + return n.AsJsxClosingElement().TagName + case KindJsxSelfClosingElement: + return n.AsJsxSelfClosingElement().TagName + case KindJSDocUnknownTag: + return n.AsJSDocUnknownTag().TagName + case KindJSDocAugmentsTag: + return n.AsJSDocAugmentsTag().TagName + case KindJSDocImplementsTag: + return n.AsJSDocImplementsTag().TagName + case KindJSDocDeprecatedTag: + return n.AsJSDocDeprecatedTag().TagName + case KindJSDocPublicTag: + return n.AsJSDocPublicTag().TagName + case KindJSDocPrivateTag: + return n.AsJSDocPrivateTag().TagName + case KindJSDocProtectedTag: + return n.AsJSDocProtectedTag().TagName + case KindJSDocReadonlyTag: + return n.AsJSDocReadonlyTag().TagName + case KindJSDocOverrideTag: + return n.AsJSDocOverrideTag().TagName + case KindJSDocCallbackTag: + return n.AsJSDocCallbackTag().TagName + case KindJSDocOverloadTag: + return n.AsJSDocOverloadTag().TagName + case KindJSDocParameterTag, KindJSDocPropertyTag: + return n.AsJSDocParameterOrPropertyTag().TagName + case KindJSDocReturnTag: + return n.AsJSDocReturnTag().TagName + case KindJSDocThisTag: + return n.AsJSDocThisTag().TagName + case KindJSDocTypeTag: + return n.AsJSDocTypeTag().TagName + case KindJSDocTemplateTag: + return n.AsJSDocTemplateTag().TagName + case KindJSDocTypedefTag: + return n.AsJSDocTypedefTag().TagName + case KindJSDocSeeTag: + return n.AsJSDocSeeTag().TagName + case KindJSDocSatisfiesTag: + return n.AsJSDocSatisfiesTag().TagName + case KindJSDocThrowsTag: + return n.AsJSDocThrowsTag().TagName + case KindJSDocImportTag: + return n.AsJSDocImportTag().TagName + } + panic("Unhandled case in Node.TagName: " + n.Kind.String()) +} + +func (n *Node) PropertyName() *Node { + switch n.Kind { + case KindImportSpecifier: + return n.AsImportSpecifier().PropertyName + case KindExportSpecifier: + return n.AsExportSpecifier().PropertyName + case KindBindingElement: + return n.AsBindingElement().PropertyName + } + return nil +} + +func (n *Node) PropertyNameOrName() *Node { + name := n.PropertyName() + if name == nil { + name = n.Name() + } + return name +} + +func (n *Node) IsTypeOnly() bool { + switch n.Kind { + case KindImportEqualsDeclaration: + return n.AsImportEqualsDeclaration().IsTypeOnly + case KindImportSpecifier: + return n.AsImportSpecifier().IsTypeOnly + case KindImportClause: + return n.AsImportClause().PhaseModifier == KindTypeKeyword + case KindExportDeclaration: + return n.AsExportDeclaration().IsTypeOnly + case KindExportSpecifier: + return n.AsExportSpecifier().IsTypeOnly + } + return false +} + +// If updating this function, also update `hasComment`. +func (n *Node) CommentList() *NodeList { + switch n.Kind { + case KindJSDoc: + return n.AsJSDoc().Comment + case KindJSDocUnknownTag: + return n.AsJSDocUnknownTag().Comment + case KindJSDocAugmentsTag: + return n.AsJSDocAugmentsTag().Comment + case KindJSDocImplementsTag: + return n.AsJSDocImplementsTag().Comment + case KindJSDocDeprecatedTag: + return n.AsJSDocDeprecatedTag().Comment + case KindJSDocPublicTag: + return n.AsJSDocPublicTag().Comment + case KindJSDocPrivateTag: + return n.AsJSDocPrivateTag().Comment + case KindJSDocProtectedTag: + return n.AsJSDocProtectedTag().Comment + case KindJSDocReadonlyTag: + return n.AsJSDocReadonlyTag().Comment + case KindJSDocOverrideTag: + return n.AsJSDocOverrideTag().Comment + case KindJSDocCallbackTag: + return n.AsJSDocCallbackTag().Comment + case KindJSDocOverloadTag: + return n.AsJSDocOverloadTag().Comment + case KindJSDocParameterTag, KindJSDocPropertyTag: + return n.AsJSDocParameterOrPropertyTag().Comment + case KindJSDocReturnTag: + return n.AsJSDocReturnTag().Comment + case KindJSDocThisTag: + return n.AsJSDocThisTag().Comment + case KindJSDocTypeTag: + return n.AsJSDocTypeTag().Comment + case KindJSDocTemplateTag: + return n.AsJSDocTemplateTag().Comment + case KindJSDocTypedefTag: + return n.AsJSDocTypedefTag().Comment + case KindJSDocSeeTag: + return n.AsJSDocSeeTag().Comment + case KindJSDocSatisfiesTag: + return n.AsJSDocSatisfiesTag().Comment + case KindJSDocThrowsTag: + return n.AsJSDocThrowsTag().Comment + case KindJSDocImportTag: + return n.AsJSDocImportTag().Comment + } + panic("Unhandled case in Node.CommentList: " + n.Kind.String()) +} + +func (n *Node) Comments() []*Node { + list := n.CommentList() + if list != nil { + return list.Nodes + } + return nil +} + +func (n *Node) Label() *Node { + switch n.Kind { + case KindLabeledStatement: + return n.AsLabeledStatement().Label + case KindBreakStatement: + return n.AsBreakStatement().Label + case KindContinueStatement: + return n.AsContinueStatement().Label + } + panic("Unhandled case in Node.Label: " + n.Kind.String()) +} + +func (n *Node) Attributes() *Node { + switch n.Kind { + case KindJsxOpeningElement: + return n.AsJsxOpeningElement().Attributes + case KindJsxSelfClosingElement: + return n.AsJsxSelfClosingElement().Attributes + } + panic("Unhandled case in Node.Attributes: " + n.Kind.String()) +} + +func (n *Node) Children() *NodeList { + switch n.Kind { + case KindJsxElement: + return n.AsJsxElement().Children + case KindJsxFragment: + return n.AsJsxFragment().Children + } + panic("Unhandled case in Node.Children: " + n.Kind.String()) +} + +func (n *Node) ModuleSpecifier() *Expression { + switch n.Kind { + case KindImportDeclaration, KindJSImportDeclaration: + return n.AsImportDeclaration().ModuleSpecifier + case KindExportDeclaration: + return n.AsExportDeclaration().ModuleSpecifier + case KindJSDocImportTag: + return n.AsJSDocImportTag().ModuleSpecifier + } + panic("Unhandled case in Node.ModuleSpecifier: " + n.Kind.String()) +} + +func (n *Node) ImportClause() *Node { + switch n.Kind { + case KindImportDeclaration, KindJSImportDeclaration: + return n.AsImportDeclaration().ImportClause + case KindJSDocImportTag: + return n.AsJSDocImportTag().ImportClause + } + panic("Unhandled case in Node.ImportClause: " + n.Kind.String()) +} + +func (n *Node) Statement() *Statement { + switch n.Kind { + case KindDoStatement: + return n.AsDoStatement().Statement + case KindWhileStatement: + return n.AsWhileStatement().Statement + case KindForStatement: + return n.AsForStatement().Statement + case KindForInStatement, KindForOfStatement: + return n.AsForInOrOfStatement().Statement + case KindWithStatement: + return n.AsWithStatement().Statement + case KindLabeledStatement: + return n.AsLabeledStatement().Statement + } + panic("Unhandled case in Node.Statement: " + n.Kind.String()) +} + +func (n *Node) PropertyList() *NodeList { + switch n.Kind { + case KindObjectLiteralExpression: + return n.AsObjectLiteralExpression().Properties + case KindJsxAttributes: + return n.AsJsxAttributes().Properties + } + panic("Unhandled case in Node.PropertyList: " + n.Kind.String()) +} + +func (n *Node) Properties() []*Node { + list := n.PropertyList() + if list != nil { + return list.Nodes + } + return nil +} + +func (n *Node) ElementList() *NodeList { + switch n.Kind { + case KindNamedImports: + return n.AsNamedImports().Elements + case KindNamedExports: + return n.AsNamedExports().Elements + case KindObjectBindingPattern, KindArrayBindingPattern: + return n.AsBindingPattern().Elements + case KindArrayLiteralExpression: + return n.AsArrayLiteralExpression().Elements + case KindTupleType: + return n.AsTupleTypeNode().Elements + } + panic("Unhandled case in Node.ElementList: " + n.Kind.String()) +} + +func (n *Node) Elements() []*Node { + list := n.ElementList() + if list != nil { + return list.Nodes + } + return nil +} + +func (n *Node) PostfixToken() *Node { + switch n.Kind { + case KindMethodDeclaration: + return n.AsMethodDeclaration().PostfixToken + case KindShorthandPropertyAssignment: + return n.AsShorthandPropertyAssignment().PostfixToken + case KindMethodSignature: + return n.AsMethodSignatureDeclaration().PostfixToken + case KindPropertySignature: + return n.AsPropertySignatureDeclaration().PostfixToken + case KindPropertyAssignment: + return n.AsPropertyAssignment().PostfixToken + case KindPropertyDeclaration: + return n.AsPropertyDeclaration().PostfixToken + case KindEnumMember: + return n.AsEnumMember().PostfixToken + case KindGetAccessor: + return n.AsGetAccessorDeclaration().PostfixToken + case KindSetAccessor: + return n.AsSetAccessorDeclaration().PostfixToken + } + return nil +} + +func (n *Node) QuestionToken() *TokenNode { + switch n.Kind { + case KindParameter: + return n.AsParameterDeclaration().QuestionToken + case KindConditionalExpression: + return n.AsConditionalExpression().QuestionToken + case KindMappedType: + return n.AsMappedTypeNode().QuestionToken + case KindNamedTupleMember: + return n.AsNamedTupleMember().QuestionToken + } + postfix := n.PostfixToken() + if postfix != nil && postfix.Kind == KindQuestionToken { + return postfix + } + return nil +} + +func (n *Node) QuestionDotToken() *Node { + switch n.Kind { + case KindElementAccessExpression: + return n.AsElementAccessExpression().QuestionDotToken + case KindPropertyAccessExpression: + return n.AsPropertyAccessExpression().QuestionDotToken + case KindCallExpression: + return n.AsCallExpression().QuestionDotToken + case KindTaggedTemplateExpression: + return n.AsTaggedTemplateExpression().QuestionDotToken + } + panic("Unhandled case in Node.QuestionDotToken: " + n.Kind.String()) +} + +func (n *Node) TypeExpression() *Node { + switch n.Kind { + case KindJSDocParameterTag, KindJSDocPropertyTag: + return n.AsJSDocParameterOrPropertyTag().TypeExpression + case KindJSDocReturnTag: + return n.AsJSDocReturnTag().TypeExpression + case KindJSDocTypeTag: + return n.AsJSDocTypeTag().TypeExpression + case KindJSDocTypedefTag: + return n.AsJSDocTypedefTag().TypeExpression + case KindJSDocCallbackTag: + return n.AsJSDocCallbackTag().TypeExpression + case KindJSDocSatisfiesTag: + return n.AsJSDocSatisfiesTag().TypeExpression + case KindJSDocThrowsTag: + return n.AsJSDocThrowsTag().TypeExpression + } + panic("Unhandled case in Node.TypeExpression: " + n.Kind.String()) +} + +func (n *Node) ClassName() *Node { + switch n.Kind { + case KindJSDocAugmentsTag: + return n.AsJSDocAugmentsTag().ClassName + case KindJSDocImplementsTag: + return n.AsJSDocImplementsTag().ClassName + } + panic("Unhandled case in Node.ClassName: " + n.Kind.String()) +} + +// Determines if `n` contains `descendant` by walking up the `Parent` pointers from `descendant`. This method panics if +// `descendant` or one of its ancestors is not parented except when that node is a `SourceFile`. +func (n *Node) Contains(descendant *Node) bool { + for descendant != nil { + if descendant == n { + return true + } + parent := descendant.Parent + if parent == nil && !IsSourceFile(descendant) { + panic("descendant is not parented") + } + descendant = parent + } + return false +} + +// Node casts + +func (n *Node) AsFlowSwitchClauseData() *FlowSwitchClauseData { + return n.data.(*FlowSwitchClauseData) +} + +func (n *Node) AsFlowReduceLabelData() *FlowReduceLabelData { + return n.data.(*FlowReduceLabelData) +} + +// NodeData + +type nodeData interface { + AsNode() *Node + ForEachChild(v Visitor) bool + VisitEachChild(v *NodeVisitor) *Node + Clone(v NodeFactoryCoercible) *Node + Name() *DeclarationName + Modifiers() *ModifierList + setModifiers(modifiers *ModifierList) + FlowNodeData() *FlowNodeBase + DeclarationData() *DeclarationBase + ExportableData() *ExportableBase + LocalsContainerData() *LocalsContainerBase + FunctionLikeData() *FunctionLikeBase + ClassLikeData() *ClassLikeBase + BodyData() *BodyBase + LiteralLikeData() *LiteralLikeNodeBase + TemplateLiteralLikeData() *TemplateLiteralLikeNodeBase + SubtreeFacts() SubtreeFacts + computeSubtreeFacts() SubtreeFacts + subtreeFactsWorker(self nodeData) SubtreeFacts + propagateSubtreeFacts() SubtreeFacts +} + +// NodeDefault + +type NodeDefault struct { + Node +} + +func (node *NodeDefault) AsNode() *Node { return &node.Node } +func (node *NodeDefault) ForEachChild(v Visitor) bool { return false } + +func (node *NodeDefault) VisitEachChild(v *NodeVisitor) *Node { return node.AsNode() } +func (node *NodeDefault) Clone(v NodeFactoryCoercible) *Node { return nil } +func (node *NodeDefault) Name() *DeclarationName { return nil } +func (node *NodeDefault) Modifiers() *ModifierList { return nil } +func (node *NodeDefault) setModifiers(modifiers *ModifierList) {} +func (node *NodeDefault) FlowNodeData() *FlowNodeBase { return nil } +func (node *NodeDefault) DeclarationData() *DeclarationBase { return nil } +func (node *NodeDefault) ExportableData() *ExportableBase { return nil } +func (node *NodeDefault) LocalsContainerData() *LocalsContainerBase { return nil } +func (node *NodeDefault) FunctionLikeData() *FunctionLikeBase { return nil } +func (node *NodeDefault) ClassLikeData() *ClassLikeBase { return nil } +func (node *NodeDefault) BodyData() *BodyBase { return nil } +func (node *NodeDefault) LiteralLikeData() *LiteralLikeNodeBase { return nil } +func (node *NodeDefault) TemplateLiteralLikeData() *TemplateLiteralLikeNodeBase { return nil } +func (node *NodeDefault) SubtreeFacts() SubtreeFacts { + return node.data.subtreeFactsWorker(node.data) +} + +func (node *NodeDefault) subtreeFactsWorker(self nodeData) SubtreeFacts { + // To avoid excessive conditional checks, the default implementation of subtreeFactsWorker directly invokes + // computeSubtreeFacts. More complex nodes should implement CompositeNodeBase, which overrides this + // method to cache the result. `self` is passed along to ensure we lookup `computeSubtreeFacts` on the + // correct type, as `CompositeNodeBase` does not, itself, inherit from `Node`. + return self.computeSubtreeFacts() +} + +func (node *NodeDefault) computeSubtreeFacts() SubtreeFacts { + return SubtreeFactsNone +} + +func (node *NodeDefault) propagateSubtreeFacts() SubtreeFacts { + return node.data.SubtreeFacts() & ^SubtreeExclusionsNode +} + +// NodeBase + +type NodeBase struct { + NodeDefault +} + +// Aliases for Node unions not covered by ast_generated.go + +type ( + NamedMember = Node // Node with NamedMemberBase + AnyValidImportOrReExport = Node // (ImportDeclaration | ExportDeclaration | JSDocImportTag) & { moduleSpecifier: StringLiteral } | ImportEqualsDeclaration & { moduleReference: ExternalModuleReference & { expression: StringLiteral }} | RequireOrImportCall | ValidImportTypeNode + ValidImportTypeNode = Node // ImportType & { argument: LiteralTypeNode & { literal: StringLiteral } } + TypeOnlyImportDeclaration = Node // ImportClause | ImportEqualsDeclaration | ImportSpecifier | NamespaceImport with isTypeOnly: true + StringLiteralLike = Node // StringLiteral | NoSubstitutionTemplateLiteral + ObjectLiteralLike = Node // ObjectLiteralExpression | ObjectBindingPattern + AnyImportOrRequireStatement = Node // AnyImportSyntax | RequireVariableStatement +) + +func IsWriteOnlyAccess(node *Node) bool { + return accessKind(node) == AccessKindWrite +} + +func IsWriteAccess(node *Node) bool { + return accessKind(node) != AccessKindRead +} + +func IsWriteAccessForReference(node *Node) bool { + decl := GetDeclarationFromName(node) + return (decl != nil && declarationIsWriteAccess(decl)) || node.Kind == KindDefaultKeyword || IsWriteAccess(node) +} + +func GetDeclarationFromName(name *Node) *Declaration { + if name == nil || name.Parent == nil { + return nil + } + parent := name.Parent + switch name.Kind { + case KindStringLiteral, KindNoSubstitutionTemplateLiteral, KindNumericLiteral: + if IsComputedPropertyName(parent) { + return parent.Parent + } + fallthrough + case KindIdentifier: + if IsDeclaration(parent) { + if parent.Name() == name { + return parent + } + return nil + } + if IsQualifiedName(parent) { + tag := parent.Parent + if IsJSDocParameterTag(tag) && tag.Name() == parent { + return tag + } + return nil + } + binExp := parent.Parent + if IsBinaryExpression(binExp) && GetAssignmentDeclarationKind(binExp) != JSDeclarationKindNone { + // (binExp.left as BindableStaticNameExpression).symbol || binExp.symbol + leftHasSymbol := false + if binExp.AsBinaryExpression().Left != nil && binExp.AsBinaryExpression().Left.Symbol() != nil { + leftHasSymbol = true + } + if leftHasSymbol || binExp.Symbol() != nil { + if GetNameOfDeclaration(binExp.AsNode()) == name { + return binExp.AsNode() + } + } + } + case KindPrivateIdentifier: + if IsDeclaration(parent) && parent.Name() == name { + return parent + } + } + return nil +} + +func declarationIsWriteAccess(decl *Node) bool { + if decl == nil { + return false + } + // Consider anything in an ambient declaration to be a write access since it may be coming from JS. + if decl.Flags&NodeFlagsAmbient != 0 { + return true + } + + switch decl.Kind { + case KindBinaryExpression, + KindBindingElement, + KindClassDeclaration, + KindClassExpression, + KindDefaultKeyword, + KindEnumDeclaration, + KindEnumMember, + KindExportSpecifier, + KindImportClause, // default import + KindImportEqualsDeclaration, + KindImportSpecifier, + KindInterfaceDeclaration, + KindJSDocCallbackTag, + KindJSDocTypedefTag, + KindJsxAttribute, + KindModuleDeclaration, + KindNamespaceExportDeclaration, + KindNamespaceImport, + KindNamespaceExport, + KindParameter, + KindShorthandPropertyAssignment, + KindTypeAliasDeclaration, + KindJSTypeAliasDeclaration, + KindTypeParameter: + return true + + case KindPropertyAssignment: + // In `({ x: y } = 0);`, `x` is not a write access. + return !IsArrayLiteralOrObjectLiteralDestructuringPattern(decl.Parent) + + case KindFunctionDeclaration, KindFunctionExpression, KindConstructor, KindMethodDeclaration, KindGetAccessor, KindSetAccessor: + // functions considered write if they provide a value (have a body) + switch decl.Kind { + case KindFunctionDeclaration: + return decl.AsFunctionDeclaration().Body != nil + case KindFunctionExpression: + return decl.AsFunctionExpression().Body != nil + case KindConstructor: + // constructor node stores body on the parent? treat same as others + return decl.AsConstructorDeclaration().Body != nil + case KindMethodDeclaration: + return decl.AsMethodDeclaration().Body != nil + case KindGetAccessor: + return decl.AsGetAccessorDeclaration().Body != nil + case KindSetAccessor: + return decl.AsSetAccessorDeclaration().Body != nil + } + return false + + case KindVariableDeclaration, KindPropertyDeclaration: + // variable/property write if initializer present or is in catch clause + var hasInit bool + switch decl.Kind { + case KindVariableDeclaration: + hasInit = decl.AsVariableDeclaration().Initializer != nil + case KindPropertyDeclaration: + hasInit = decl.AsPropertyDeclaration().Initializer != nil + } + return hasInit || IsCatchClause(decl.Parent) + + case KindMethodSignature, KindPropertySignature, KindJSDocPropertyTag, KindJSDocParameterTag: + return false + + default: + // preserve TS behavior: crash on unexpected kinds + panic("Unhandled case in declarationIsWriteAccess") + } +} + +func IsArrayLiteralOrObjectLiteralDestructuringPattern(node *Node) bool { + if !(IsArrayLiteralExpression(node) || IsObjectLiteralExpression(node)) { + return false + } + parent := node.Parent + // [a,b,c] from: + // [a, b, c] = someExpression; + if IsBinaryExpression(parent) && parent.AsBinaryExpression().Left == node && parent.AsBinaryExpression().OperatorToken.Kind == KindEqualsToken { + return true + } + // [a, b, c] from: + // for([a, b, c] of expression) + if IsForOfStatement(parent) && parent.Initializer() == node { + return true + } + // {x, a: {a, b, c} } = someExpression + if IsPropertyAssignment(parent) { + return IsArrayLiteralOrObjectLiteralDestructuringPattern(parent.Parent) + } + // [a, b, c] of + // [x, [a, b, c] ] = someExpression + return IsArrayLiteralOrObjectLiteralDestructuringPattern(parent) +} + +func accessKind(node *Node) AccessKind { + parent := node.Parent + if parent == nil { + return AccessKindRead + } + switch parent.Kind { + case KindParenthesizedExpression: + return accessKind(parent) + case KindPrefixUnaryExpression: + operator := parent.AsPrefixUnaryExpression().Operator + if operator == KindPlusPlusToken || operator == KindMinusMinusToken { + return AccessKindReadWrite + } + return AccessKindRead + case KindPostfixUnaryExpression: + operator := parent.AsPostfixUnaryExpression().Operator + if operator == KindPlusPlusToken || operator == KindMinusMinusToken { + return AccessKindReadWrite + } + return AccessKindRead + case KindBinaryExpression: + if parent.AsBinaryExpression().Left == node { + operator := parent.AsBinaryExpression().OperatorToken + if IsAssignmentOperator(operator.Kind) { + if operator.Kind == KindEqualsToken { + return AccessKindWrite + } + return AccessKindReadWrite + } + } + return AccessKindRead + case KindPropertyAccessExpression: + if parent.AsPropertyAccessExpression().Name() != node { + return AccessKindRead + } + return accessKind(parent) + case KindPropertyAssignment: + parentAccess := accessKind(parent.Parent) + // In `({ x: varname }) = { x: 1 }`, the left `x` is a read, the right `x` is a write. + if node == parent.AsPropertyAssignment().Name() { + return reverseAccessKind(parentAccess) + } + return parentAccess + case KindShorthandPropertyAssignment: + // Assume it's the local variable being accessed, since we don't check public properties for --noUnusedLocals. + if node == parent.AsShorthandPropertyAssignment().ObjectAssignmentInitializer { + return AccessKindRead + } + return accessKind(parent.Parent) + case KindArrayLiteralExpression: + return accessKind(parent) + case KindForInStatement, KindForOfStatement: + if node == parent.AsForInOrOfStatement().Initializer { + return AccessKindWrite + } + return AccessKindRead + default: + return AccessKindRead + } +} + +func reverseAccessKind(a AccessKind) AccessKind { + switch a { + case AccessKindRead: + return AccessKindWrite + case AccessKindWrite: + return AccessKindRead + case AccessKindReadWrite: + return AccessKindReadWrite + } + panic("Unhandled case in reverseAccessKind") +} + +type AccessKind int32 + +const ( + AccessKindRead AccessKind = iota // Only reads from a variable + AccessKindWrite // Only writes to a variable without ever reading it. E.g.: `x=1;`. + AccessKindReadWrite // Reads from and writes to a variable. E.g.: `f(x++);`, `x/=1`. +) + +// DeclarationBase + +func (node *DeclarationBase) DeclarationData() *DeclarationBase { return node } + +func IsDeclarationNode(node *Node) bool { + return node.DeclarationData() != nil +} + +// ExportableBase + +func (node *ExportableBase) ExportableData() *ExportableBase { return node } + +// ModifiersBase + +func (node *ModifiersBase) Modifiers() *ModifierList { return node.modifiers } +func (node *ModifiersBase) setModifiers(modifiers *ModifierList) { node.modifiers = modifiers } + +// LocalsContainerBase + +func (node *LocalsContainerBase) LocalsContainerData() *LocalsContainerBase { return node } + +func IsLocalsContainer(node *Node) bool { + return node.LocalsContainerData() != nil +} + +// FunctionLikeBase + +func (node *FunctionLikeBase) LocalsContainerData() *LocalsContainerBase { + return &node.LocalsContainerBase +} +func (node *FunctionLikeBase) FunctionLikeData() *FunctionLikeBase { return node } + +// BodyBase + +func (node *BodyBase) BodyData() *BodyBase { return node } + +// FunctionLikeWithBodyBase + +func (node *FunctionLikeWithBodyBase) LocalsContainerData() *LocalsContainerBase { + return &node.LocalsContainerBase +} + +func (node *FunctionLikeWithBodyBase) FunctionLikeData() *FunctionLikeBase { + return &node.FunctionLikeBase +} +func (node *FunctionLikeWithBodyBase) BodyData() *BodyBase { return &node.BodyBase } + +// FlowNodeBase + +func (node *FlowNodeBase) FlowNodeData() *FlowNodeBase { return node } + +// if you provide nil for file, this code will walk to the root of the tree to find the file +func (node *Node) JSDoc(file *SourceFile) []*Node { + if node.Flags&NodeFlagsHasJSDoc == 0 { + return nil + } + if file == nil { + file = GetSourceFileOfNode(node) + if file == nil { + return nil + } + } + if file.hasLazyJSDoc { + return file.resolveJSDoc(node) + } + return file.jsdocCache[node] +} + +// EagerJSDoc returns JSDoc nodes that have already been parsed and cached, +// without triggering lazy JSDoc parsing. +func (node *Node) EagerJSDoc(file *SourceFile) []*Node { + if node.Flags&NodeFlagsHasJSDoc == 0 { + return nil + } + if file == nil { + file = GetSourceFileOfNode(node) + if file == nil { + return nil + } + } + if file.hasLazyJSDoc { + file.jsdocMu.RLock() + jsdocs := file.jsdocCache[node] + file.jsdocMu.RUnlock() + return jsdocs + } + return file.jsdocCache[node] +} + +// CompositeBase + +func (node *CompositeBase) subtreeFactsWorker(self nodeData) SubtreeFacts { + // computeSubtreeFacts() is expected to be idempotent, so races will only impact time, not correctness. + facts := SubtreeFacts(node.facts.Load()) + if facts&SubtreeFactsComputed == 0 { + facts |= self.computeSubtreeFacts() | SubtreeFactsComputed + node.facts.Store(uint32(facts)) + } + return facts &^ SubtreeFactsComputed +} + +func (node *CompositeBase) computeSubtreeFacts() SubtreeFacts { + // This method must be implemented by the concrete node type. + panic("not implemented") +} + +// TypeSyntaxBase + +func (node *TypeSyntaxBase) computeSubtreeFacts() SubtreeFacts { return SubtreeContainsTypeScript } + +func (node *TypeSyntaxBase) propagateSubtreeFacts() SubtreeFacts { return SubtreeContainsTypeScript } + +func (node *Token) computeSubtreeFacts() SubtreeFacts { + switch node.Kind { + case KindUsingKeyword: + return SubtreeContainsUsing + case KindPublicKeyword, + KindPrivateKeyword, + KindProtectedKeyword, + KindReadonlyKeyword, + KindAbstractKeyword, + KindDeclareKeyword, + KindConstKeyword, + KindAnyKeyword, + KindNumberKeyword, + KindBigIntKeyword, + KindNeverKeyword, + KindObjectKeyword, + KindInKeyword, + KindOutKeyword, + KindOverrideKeyword, + KindStringKeyword, + KindBooleanKeyword, + KindSymbolKeyword, + KindVoidKeyword, + KindUnknownKeyword, + KindUndefinedKeyword, + KindExportKeyword: + return SubtreeContainsTypeScript + case KindAccessorKeyword: + return SubtreeContainsClassFields + case KindAsyncKeyword: + return SubtreeContainsAnyAwait + case KindSuperKeyword: + return SubtreeContainsLexicalSuper + case KindThisKeyword: + return SubtreeContainsLexicalThis + case KindAsteriskAsteriskToken, KindAsteriskAsteriskEqualsToken: + return SubtreeContainsExponentiationOperator + case KindQuestionQuestionToken: + return SubtreeContainsNullishCoalescing + case KindQuestionDotToken: + return SubtreeContainsOptionalChaining + case KindQuestionQuestionEqualsToken, KindBarBarEqualsToken, KindAmpersandAmpersandEqualsToken: + return SubtreeContainsLogicalAssignments + } + return SubtreeFactsNone +} + +func (node *PrivateIdentifier) computeSubtreeFacts() SubtreeFacts { + return SubtreeContainsClassFields +} + +func (f *NodeFactory) NewModifier(kind Kind) *Node { + return f.NewToken(kind) +} + +func (node *Decorator) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.Expression) | + SubtreeContainsTypeScript | + SubtreeContainsDecorators +} + +func (node *ForInOrOfStatement) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.Initializer) | + propagateSubtreeFacts(node.Expression) | + propagateSubtreeFacts(node.Statement) | + core.IfElse(node.AwaitModifier != nil, SubtreeContainsForAwaitOrAsyncGenerator, SubtreeFactsNone) +} + +func (node *ReturnStatement) computeSubtreeFacts() SubtreeFacts { + // return in an ES2018 async generator must be awaited + return propagateSubtreeFacts(node.Expression) | SubtreeContainsForAwaitOrAsyncGenerator +} + +func (node *CatchClause) computeSubtreeFacts() SubtreeFacts { + res := propagateSubtreeFacts(node.VariableDeclaration) | + propagateSubtreeFacts(node.Block) + if node.VariableDeclaration == nil { + res |= SubtreeContainsMissingCatchClauseVariable + } + return res +} + +func (node *CatchClause) propagateSubtreeFacts() SubtreeFacts { + return node.SubtreeFacts() & ^SubtreeExclusionsCatchClause +} + +func (node *VariableStatement) computeSubtreeFacts() SubtreeFacts { + if node.modifiers != nil && node.modifiers.ModifierFlags&ModifierFlagsAmbient != 0 { + return SubtreeContainsTypeScript + } else { + return propagateModifierListSubtreeFacts(node.modifiers) | + propagateSubtreeFacts(node.DeclarationList) + } +} + +func (node *VariableDeclaration) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.name) | + propagateEraseableSyntaxSubtreeFacts(node.ExclamationToken) | + propagateEraseableSyntaxSubtreeFacts(node.Type) | + propagateSubtreeFacts(node.Initializer) +} + +func (node *VariableDeclarationList) computeSubtreeFacts() SubtreeFacts { + return propagateNodeListSubtreeFacts(node.Declarations, propagateSubtreeFacts) | + core.IfElse(node.Flags&NodeFlagsUsing != 0, SubtreeContainsUsing, SubtreeFactsNone) +} + +func (node *VariableDeclarationList) propagateSubtreeFacts() SubtreeFacts { + return node.SubtreeFacts() & ^SubtreeExclusionsVariableDeclarationList +} + +func (node *BindingPattern) computeSubtreeFacts() SubtreeFacts { + switch node.Kind { + case KindObjectBindingPattern: + return propagateNodeListSubtreeFacts(node.Elements, propagateObjectBindingElementSubtreeFacts) + case KindArrayBindingPattern: + return propagateNodeListSubtreeFacts(node.Elements, propagateBindingElementSubtreeFacts) + default: + return SubtreeFactsNone + } +} + +func (node *BindingPattern) propagateSubtreeFacts() SubtreeFacts { + return node.SubtreeFacts() & ^SubtreeExclusionsBindingPattern +} + +func (node *ParameterDeclaration) computeSubtreeFacts() SubtreeFacts { + if node.name != nil && IsThisIdentifier(node.name) { + return SubtreeContainsTypeScript + } else { + return propagateModifierListSubtreeFacts(node.modifiers) | + propagateSubtreeFacts(node.name) | + propagateEraseableSyntaxSubtreeFacts(node.QuestionToken) | + propagateEraseableSyntaxSubtreeFacts(node.Type) | + propagateSubtreeFacts(node.Initializer) + } +} + +func (node *ParameterDeclaration) propagateSubtreeFacts() SubtreeFacts { + return node.SubtreeFacts() & ^SubtreeExclusionsParameter +} + +func (node *BindingElement) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.PropertyName) | + propagateSubtreeFacts(node.name) | + propagateSubtreeFacts(node.Initializer) | + core.IfElse(node.DotDotDotToken != nil, SubtreeContainsRestOrSpread, SubtreeFactsNone) +} + +func (node *FunctionDeclaration) computeSubtreeFacts() SubtreeFacts { + if node.Body == nil || node.ModifierFlags()&ModifierFlagsAmbient != 0 { + return SubtreeContainsTypeScript + } else { + isAsync := node.ModifierFlags()&ModifierFlagsAsync != 0 + isGenerator := node.AsteriskToken != nil + return propagateModifierListSubtreeFacts(node.modifiers) | + propagateSubtreeFacts(node.AsteriskToken) | + propagateSubtreeFacts(node.name) | + propagateEraseableSyntaxListSubtreeFacts(node.TypeParameters) | + propagateNodeListSubtreeFacts(node.Parameters, propagateSubtreeFacts) | + propagateEraseableSyntaxSubtreeFacts(node.Type) | + propagateEraseableSyntaxSubtreeFacts(node.FullSignature) | + propagateSubtreeFacts(node.Body) | + core.IfElse(isAsync && isGenerator, SubtreeContainsForAwaitOrAsyncGenerator, SubtreeFactsNone) | + core.IfElse(isAsync && !isGenerator, SubtreeContainsAnyAwait, SubtreeFactsNone) + } +} + +func (node *FunctionDeclaration) propagateSubtreeFacts() SubtreeFacts { + return node.SubtreeFacts() & ^SubtreeExclusionsFunction +} + +// ClassLikeBase + +func (node *ClassLikeBase) Name() *DeclarationName { return node.name } + +func (node *ClassLikeBase) ClassLikeData() *ClassLikeBase { return node } + +func (node *ClassLikeBase) computeSubtreeFacts() SubtreeFacts { + if node.modifiers != nil && node.modifiers.ModifierFlags&ModifierFlagsAmbient != 0 { + return SubtreeContainsTypeScript + } else { + return propagateModifierListSubtreeFacts(node.modifiers) | + propagateSubtreeFacts(node.name) | + propagateEraseableSyntaxListSubtreeFacts(node.TypeParameters) | + propagateNodeListSubtreeFacts(node.HeritageClauses, propagateSubtreeFacts) | + propagateNodeListSubtreeFacts(node.Members, propagateSubtreeFacts) + } +} + +func (node *ClassDeclaration) propagateSubtreeFacts() SubtreeFacts { + return node.SubtreeFacts() & ^SubtreeExclusionsClass +} + +func (node *ClassExpression) propagateSubtreeFacts() SubtreeFacts { + return node.SubtreeFacts() & ^SubtreeExclusionsClass +} + +func (node *HeritageClause) computeSubtreeFacts() SubtreeFacts { + switch node.Token { + case KindExtendsKeyword: + return propagateNodeListSubtreeFacts(node.Types, propagateSubtreeFacts) + case KindImplementsKeyword: + return SubtreeContainsTypeScript + default: + return SubtreeFactsNone + } +} + +func IsTypeOrJSTypeAliasDeclaration(node *Node) bool { + return node.Kind == KindTypeAliasDeclaration || node.Kind == KindJSTypeAliasDeclaration +} + +func (node *EnumMember) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.name) | + propagateSubtreeFacts(node.Initializer) | + SubtreeContainsTypeScript +} + +func (node *EnumDeclaration) computeSubtreeFacts() SubtreeFacts { + if node.modifiers != nil && node.modifiers.ModifierFlags&ModifierFlagsAmbient != 0 { + return SubtreeContainsTypeScript + } else { + return propagateModifierListSubtreeFacts(node.modifiers) | + propagateSubtreeFacts(node.name) | + propagateNodeListSubtreeFacts(node.Members, propagateSubtreeFacts) | + SubtreeContainsTypeScript + } +} + +func (node *ModuleDeclaration) computeSubtreeFacts() SubtreeFacts { + if node.ModifierFlags()&ModifierFlagsAmbient != 0 { + return SubtreeContainsTypeScript + } else { + return propagateModifierListSubtreeFacts(node.modifiers) | + propagateSubtreeFacts(node.name) | + propagateSubtreeFacts(node.Body) | + SubtreeContainsTypeScript + } +} + +func (node *ModuleDeclaration) propagateSubtreeFacts() SubtreeFacts { + return node.SubtreeFacts() & ^SubtreeExclusionsModule +} + +func (node *ImportEqualsDeclaration) computeSubtreeFacts() SubtreeFacts { + if node.IsTypeOnly || !IsExternalModuleReference(node.ModuleReference) { + return SubtreeContainsTypeScript + } else { + return propagateModifierListSubtreeFacts(node.modifiers) | + propagateSubtreeFacts(node.name) | + propagateSubtreeFacts(node.ModuleReference) + } +} + +func IsImportDeclarationOrJSImportDeclaration(node *Node) bool { + return node.Kind == KindImportDeclaration || node.Kind == KindJSImportDeclaration +} + +func (node *ImportSpecifier) computeSubtreeFacts() SubtreeFacts { + if node.IsTypeOnly { + return SubtreeContainsTypeScript + } else { + return propagateSubtreeFacts(node.PropertyName) | + propagateSubtreeFacts(node.name) + } +} + +func (node *ImportClause) computeSubtreeFacts() SubtreeFacts { + if node.PhaseModifier == KindTypeKeyword { + return SubtreeContainsTypeScript + } else { + return propagateSubtreeFacts(node.name) | + propagateSubtreeFacts(node.NamedBindings) + } +} + +func (node *ExportAssignment) computeSubtreeFacts() SubtreeFacts { + return propagateModifierListSubtreeFacts(node.modifiers) | propagateSubtreeFacts(node.Type) | propagateSubtreeFacts(node.Expression) | core.IfElse(node.IsExportEquals, SubtreeContainsTypeScript, SubtreeFactsNone) +} + +func IsAnyExportAssignment(node *Node) bool { + return node.Kind == KindExportAssignment +} + +func (node *ExportDeclaration) computeSubtreeFacts() SubtreeFacts { + return propagateModifierListSubtreeFacts(node.modifiers) | + propagateSubtreeFacts(node.ExportClause) | + propagateSubtreeFacts(node.ModuleSpecifier) | + propagateSubtreeFacts(node.Attributes) | + core.IfElse(node.IsTypeOnly, SubtreeContainsTypeScript, SubtreeFactsNone) +} + +func (node *ExportSpecifier) computeSubtreeFacts() SubtreeFacts { + if node.IsTypeOnly { + return SubtreeContainsTypeScript + } else { + return propagateSubtreeFacts(node.PropertyName) | + propagateSubtreeFacts(node.name) + } +} + +// NamedMemberBase + +func (node *NamedMemberBase) DeclarationData() *DeclarationBase { return &node.DeclarationBase } +func (node *NamedMemberBase) Modifiers() *ModifierList { return node.modifiers } +func (node *NamedMemberBase) setModifiers(modifiers *ModifierList) { node.modifiers = modifiers } +func (node *NamedMemberBase) Name() *DeclarationName { return node.name } + +func (node *ConstructorDeclaration) computeSubtreeFacts() SubtreeFacts { + if node.Body == nil { + return SubtreeContainsTypeScript + } else { + return propagateModifierListSubtreeFacts(node.modifiers) | + propagateEraseableSyntaxListSubtreeFacts(node.TypeParameters) | + propagateNodeListSubtreeFacts(node.Parameters, propagateSubtreeFacts) | + propagateEraseableSyntaxSubtreeFacts(node.Type) | + propagateEraseableSyntaxSubtreeFacts(node.FullSignature) | + propagateSubtreeFacts(node.Body) + } +} + +func (node *ConstructorDeclaration) propagateSubtreeFacts() SubtreeFacts { + return node.SubtreeFacts() & ^SubtreeExclusionsConstructor +} + +func (node *AccessorDeclarationBase) IsAccessorDeclaration() {} + +func (node *AccessorDeclarationBase) computeSubtreeFacts() SubtreeFacts { + if node.Body == nil { + return SubtreeContainsTypeScript + } else { + return propagateModifierListSubtreeFacts(node.modifiers) | + propagateSubtreeFacts(node.name) | + propagateEraseableSyntaxListSubtreeFacts(node.TypeParameters) | + propagateNodeListSubtreeFacts(node.Parameters, propagateSubtreeFacts) | + propagateEraseableSyntaxSubtreeFacts(node.Type) | + propagateEraseableSyntaxSubtreeFacts(node.FullSignature) | + propagateSubtreeFacts(node.Body) + } +} + +func (node *AccessorDeclarationBase) propagateSubtreeFacts() SubtreeFacts { + return node.SubtreeFacts() & ^SubtreeExclusionsAccessor | + propagateSubtreeFacts(node.name) +} + +func (node *MethodDeclaration) computeSubtreeFacts() SubtreeFacts { + if node.Body == nil { + return SubtreeContainsTypeScript + } else { + isAsync := node.modifiers != nil && node.modifiers.ModifierFlags&ModifierFlagsAsync != 0 + isGenerator := node.AsteriskToken != nil + return propagateModifierListSubtreeFacts(node.modifiers) | + propagateSubtreeFacts(node.AsteriskToken) | + propagateSubtreeFacts(node.name) | + propagateEraseableSyntaxSubtreeFacts(node.PostfixToken) | + propagateEraseableSyntaxListSubtreeFacts(node.TypeParameters) | + propagateNodeListSubtreeFacts(node.Parameters, propagateSubtreeFacts) | + propagateSubtreeFacts(node.Body) | + propagateEraseableSyntaxSubtreeFacts(node.Type) | + propagateEraseableSyntaxSubtreeFacts(node.FullSignature) | + core.IfElse(isAsync && isGenerator, SubtreeContainsForAwaitOrAsyncGenerator, SubtreeFactsNone) | + core.IfElse(isAsync && !isGenerator, SubtreeContainsAnyAwait, SubtreeFactsNone) + } +} + +func (node *MethodDeclaration) propagateSubtreeFacts() SubtreeFacts { + return node.SubtreeFacts() & ^SubtreeExclusionsMethod | + propagateSubtreeFacts(node.name) +} + +func (node *PropertyDeclaration) computeSubtreeFacts() SubtreeFacts { + return propagateModifierListSubtreeFacts(node.modifiers) | + propagateSubtreeFacts(node.name) | + propagateEraseableSyntaxSubtreeFacts(node.PostfixToken) | + propagateEraseableSyntaxSubtreeFacts(node.Type) | + propagateSubtreeFacts(node.Initializer) | + SubtreeContainsClassFields +} + +func (node *PropertyDeclaration) propagateSubtreeFacts() SubtreeFacts { + return node.SubtreeFacts() & ^SubtreeExclusionsProperty | + propagateSubtreeFacts(node.name) +} + +func (node *ClassStaticBlockDeclaration) computeSubtreeFacts() SubtreeFacts { + return propagateModifierListSubtreeFacts(node.modifiers) | + propagateSubtreeFacts(node.Body) | + SubtreeContainsClassFields +} + +func (node *KeywordExpression) computeSubtreeFacts() SubtreeFacts { + switch node.Kind { + case KindThisKeyword: + return SubtreeContainsLexicalThis + case KindSuperKeyword: + return SubtreeContainsLexicalSuper + } + return SubtreeFactsNone +} + +// TemplateLiteralLikeBase + +func (node *LiteralLikeNodeBase) LiteralLikeData() *LiteralLikeNodeBase { return node } + +func (node *BigIntLiteral) computeSubtreeFacts() SubtreeFacts { + return SubtreeFactsNone // `bigint` is not downleveled in any way +} + +func (node *Identifier) computeSubtreeFacts() SubtreeFacts { + return SubtreeContainsIdentifier +} + +func (node *NoSubstitutionTemplateLiteral) computeSubtreeFacts() SubtreeFacts { + if node.TemplateFlags&TokenFlagsContainsInvalidEscape != 0 { + return SubtreeContainsInvalidTemplateEscape + } + return SubtreeFactsNone +} + +func (node *BinaryExpression) computeSubtreeFacts() SubtreeFacts { + facts := propagateModifierListSubtreeFacts(node.modifiers) | + propagateSubtreeFacts(node.Left) | + propagateSubtreeFacts(node.Type) | + propagateSubtreeFacts(node.OperatorToken) | + propagateSubtreeFacts(node.Right) | + core.IfElse(node.OperatorToken.Kind == KindInKeyword && IsPrivateIdentifier(node.Left), SubtreeContainsClassFields|SubtreeContainsPrivateIdentifierInExpression, SubtreeFactsNone) + if node.OperatorToken.Kind == KindEqualsToken { + if (IsObjectLiteralExpression(node.Left) || IsArrayLiteralExpression(node.Left)) && ContainsObjectRestOrSpread(node.Left) { + facts |= SubtreeContainsObjectRestOrSpread + } + } + return facts +} + +func (node *BinaryExpression) setModifiers(modifiers *ModifierList) { node.modifiers = modifiers } + +func (node *YieldExpression) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.Expression) | SubtreeContainsForAwaitOrAsyncGenerator +} + +func (node *ArrowFunction) computeSubtreeFacts() SubtreeFacts { + return propagateModifierListSubtreeFacts(node.modifiers) | + propagateEraseableSyntaxListSubtreeFacts(node.TypeParameters) | + propagateNodeListSubtreeFacts(node.Parameters, propagateSubtreeFacts) | + propagateEraseableSyntaxSubtreeFacts(node.Type) | + propagateEraseableSyntaxSubtreeFacts(node.FullSignature) | + propagateSubtreeFacts(node.Body) | + core.IfElse(node.ModifierFlags()&ModifierFlagsAsync != 0, SubtreeContainsAnyAwait, SubtreeFactsNone) +} + +func (node *ArrowFunction) propagateSubtreeFacts() SubtreeFacts { + return node.SubtreeFacts() & ^SubtreeExclusionsArrowFunction +} + +func (node *FunctionExpression) computeSubtreeFacts() SubtreeFacts { + isAsync := node.modifiers != nil && node.modifiers.ModifierFlags&ModifierFlagsAsync != 0 + isGenerator := node.AsteriskToken != nil + return propagateModifierListSubtreeFacts(node.modifiers) | + propagateSubtreeFacts(node.AsteriskToken) | + propagateSubtreeFacts(node.name) | + propagateEraseableSyntaxListSubtreeFacts(node.TypeParameters) | + propagateNodeListSubtreeFacts(node.Parameters, propagateSubtreeFacts) | + propagateEraseableSyntaxSubtreeFacts(node.Type) | + propagateEraseableSyntaxSubtreeFacts(node.FullSignature) | + propagateSubtreeFacts(node.Body) | + core.IfElse(isAsync && isGenerator, SubtreeContainsForAwaitOrAsyncGenerator, SubtreeFactsNone) | + core.IfElse(isAsync && !isGenerator, SubtreeContainsAnyAwait, SubtreeFactsNone) +} + +func (node *FunctionExpression) propagateSubtreeFacts() SubtreeFacts { + return node.SubtreeFacts() & ^SubtreeExclusionsFunction +} + +func (node *AsExpression) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.Expression) | SubtreeContainsTypeScript +} + +func (node *AsExpression) propagateSubtreeFacts() SubtreeFacts { + return node.SubtreeFacts() & ^SubtreeExclusionsOuterExpression +} + +func (node *SatisfiesExpression) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.Expression) | SubtreeContainsTypeScript +} + +func (node *SatisfiesExpression) propagateSubtreeFacts() SubtreeFacts { + return node.SubtreeFacts() & ^SubtreeExclusionsOuterExpression +} + +func (node *PropertyAccessExpression) computeSubtreeFacts() SubtreeFacts { + privateName := SubtreeFactsNone + if !IsIdentifier(node.name) { + privateName = SubtreeContainsPrivateIdentifierInExpression + } + return propagateSubtreeFacts(node.Expression) | + propagateSubtreeFacts(node.QuestionDotToken) | + propagateSubtreeFacts(node.name) | privateName +} + +func (node *PropertyAccessExpression) propagateSubtreeFacts() SubtreeFacts { + return node.SubtreeFacts() & ^SubtreeExclusionsPropertyAccess +} + +func (node *ElementAccessExpression) propagateSubtreeFacts() SubtreeFacts { + return node.SubtreeFacts() & ^SubtreeExclusionsElementAccess +} + +func (node *CallExpression) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.Expression) | + propagateSubtreeFacts(node.QuestionDotToken) | + propagateEraseableSyntaxListSubtreeFacts(node.TypeArguments) | + propagateNodeListSubtreeFacts(node.Arguments, propagateSubtreeFacts) | + core.IfElse(node.Expression.Kind == KindImportKeyword, SubtreeContainsDynamicImport, SubtreeFactsNone) +} + +func (node *CallExpression) propagateSubtreeFacts() SubtreeFacts { + return node.SubtreeFacts() & ^SubtreeExclusionsCall +} + +func (node *NewExpression) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.Expression) | + propagateEraseableSyntaxListSubtreeFacts(node.TypeArguments) | + propagateNodeListSubtreeFacts(node.Arguments, propagateSubtreeFacts) +} + +func (node *NewExpression) propagateSubtreeFacts() SubtreeFacts { + return node.SubtreeFacts() & ^SubtreeExclusionsNew +} + +func (node *MetaProperty) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.name) &^ SubtreeContainsIdentifier +} + +func (node *NonNullExpression) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.Expression) | SubtreeContainsTypeScript +} + +func (node *SpreadElement) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.Expression) | SubtreeContainsRestOrSpread +} + +func (node *TaggedTemplateExpression) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.Tag) | + propagateSubtreeFacts(node.QuestionDotToken) | + propagateEraseableSyntaxListSubtreeFacts(node.TypeArguments) | + propagateSubtreeFacts(node.Template) +} + +// Hand-written subtree facts for nontrivial generated nodes. + +func (node *ArrayLiteralExpression) propagateSubtreeFacts() SubtreeFacts { + return node.SubtreeFacts() & ^SubtreeExclusionsArrayLiteral +} + +func (node *ObjectLiteralExpression) propagateSubtreeFacts() SubtreeFacts { + return node.SubtreeFacts() & ^SubtreeExclusionsObjectLiteral +} + +func (node *SpreadAssignment) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.Expression) | SubtreeContainsESObjectRestOrSpread | SubtreeContainsObjectRestOrSpread +} + +func (node *PropertyAssignment) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.name) | + propagateSubtreeFacts(node.Type) | + propagateSubtreeFacts(node.Initializer) +} + +func (node *ShorthandPropertyAssignment) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.name) | + propagateSubtreeFacts(node.Type) | + propagateSubtreeFacts(node.ObjectAssignmentInitializer) | + SubtreeContainsTypeScript +} + +func (node *AwaitExpression) computeSubtreeFacts() SubtreeFacts { + // await in an ES2018 async generator must use `yield __await(expr)` + return propagateSubtreeFacts(node.Expression) | SubtreeContainsAwait | SubtreeContainsAnyAwait | SubtreeContainsForAwaitOrAsyncGenerator +} + +func (node *TypeAssertion) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.Expression) | SubtreeContainsTypeScript +} + +func (node *TypeAssertion) propagateSubtreeFacts() SubtreeFacts { + return node.SubtreeFacts() & ^SubtreeExclusionsOuterExpression +} + +func (node *ExpressionWithTypeArguments) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.Expression) | + propagateEraseableSyntaxListSubtreeFacts(node.TypeArguments) +} + +func (node *ImportAttributesNode) GetResolutionModeOverride( /* !!! grammarErrorOnNode?: (node: Node, diagnostic: DiagnosticMessage) => void*/ ) (core.ResolutionMode, bool) { + if node == nil { + return core.ResolutionModeNone, false + } + + attributes := node.AsImportAttributes().Attributes + + if len(attributes.Nodes) != 1 { + // !!! + // grammarErrorOnNode?.( + // node, + // node.token === SyntaxKind.WithKeyword + // ? Diagnostics.Type_import_attributes_should_have_exactly_one_key_resolution_mode_with_value_import_or_require + // : Diagnostics.Type_import_assertions_should_have_exactly_one_key_resolution_mode_with_value_import_or_require, + // ); + return core.ResolutionModeNone, false + } + + elem := attributes.Nodes[0].AsImportAttribute() + if !IsStringLiteralLike(elem.Name()) { + return core.ResolutionModeNone, false + } + if elem.Name().Text() != "resolution-mode" { + // !!! + // grammarErrorOnNode?.( + // elem.name, + // node.token === SyntaxKind.WithKeyword + // ? Diagnostics.resolution_mode_is_the_only_valid_key_for_type_import_attributes + // : Diagnostics.resolution_mode_is_the_only_valid_key_for_type_import_assertions, + // ); + return core.ResolutionModeNone, false + } + if !IsStringLiteralLike(elem.Value) { + return core.ResolutionModeNone, false + } + if elem.Value.Text() != "import" && elem.Value.Text() != "require" { + // !!! + // grammarErrorOnNode?.(elem.value, Diagnostics.resolution_mode_should_be_either_require_or_import); + return core.ResolutionModeNone, false + } + if elem.Value.Text() == "import" { + return core.ResolutionModeESM, true + } else { + return core.ModuleKindCommonJS, true + } +} + +// FunctionOrConstructorTypeNodeBase + +func (node *FunctionOrConstructorTypeNodeBase) DeclarationData() *DeclarationBase { + return node.FunctionLikeBase.DeclarationData() +} + +func (node *TemplateLiteralLikeNodeBase) LiteralLikeData() *LiteralLikeNodeBase { + return &node.LiteralLikeNodeBase +} + +func (node *TemplateLiteralLikeNodeBase) TemplateLiteralLikeData() *TemplateLiteralLikeNodeBase { + return node +} + +func (node *TemplateHead) computeSubtreeFacts() SubtreeFacts { + if node.TemplateFlags&TokenFlagsContainsInvalidEscape != 0 { + return SubtreeContainsInvalidTemplateEscape + } + return SubtreeFactsNone +} + +func (node *TemplateMiddle) computeSubtreeFacts() SubtreeFacts { + if node.TemplateFlags&TokenFlagsContainsInvalidEscape != 0 { + return SubtreeContainsInvalidTemplateEscape + } + return SubtreeFactsNone +} + +func (node *TemplateTail) computeSubtreeFacts() SubtreeFacts { + if node.TemplateFlags&TokenFlagsContainsInvalidEscape != 0 { + return SubtreeContainsInvalidTemplateEscape + } + return SubtreeFactsNone +} + +func (node *JsxElement) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.OpeningElement) | + propagateNodeListSubtreeFacts(node.Children, propagateSubtreeFacts) | + propagateSubtreeFacts(node.ClosingElement) | + SubtreeContainsJsx +} + +func (node *JsxAttributes) computeSubtreeFacts() SubtreeFacts { + return propagateNodeListSubtreeFacts(node.Properties, propagateSubtreeFacts) | + SubtreeContainsJsx +} + +func (node *JsxNamespacedName) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.Namespace) | + propagateSubtreeFacts(node.name) | + SubtreeContainsJsx +} + +func (node *JsxOpeningElement) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.TagName) | + propagateEraseableSyntaxListSubtreeFacts(node.TypeArguments) | + propagateSubtreeFacts(node.Attributes) | + SubtreeContainsJsx +} + +func (node *JsxSelfClosingElement) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.TagName) | + propagateEraseableSyntaxListSubtreeFacts(node.TypeArguments) | + propagateSubtreeFacts(node.Attributes) | + SubtreeContainsJsx +} + +func (node *JsxFragment) computeSubtreeFacts() SubtreeFacts { + return propagateNodeListSubtreeFacts(node.Children, propagateSubtreeFacts) | + SubtreeContainsJsx +} + +func (node *JsxOpeningFragment) computeSubtreeFacts() SubtreeFacts { + return SubtreeContainsJsx +} + +func (node *JsxClosingFragment) computeSubtreeFacts() SubtreeFacts { + return SubtreeContainsJsx +} + +func (node *JsxAttribute) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.name) | + propagateSubtreeFacts(node.Initializer) | + SubtreeContainsJsx +} + +func (node *JsxSpreadAttribute) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.Expression) | SubtreeContainsJsx +} + +func (node *JsxClosingElement) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.TagName) | SubtreeContainsJsx +} + +func (node *JsxExpression) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.Expression) | SubtreeContainsJsx +} + +func (node *JsxText) computeSubtreeFacts() SubtreeFacts { + return SubtreeContainsJsx +} + +/// JSDoc nodes /// + +// JSDoc + +func (node *Node) IsJSDoc() bool { + return node.Kind == KindJSDoc +} + +// JSDocText + +// PatternAmbientModule + +type PatternAmbientModule struct { + Pattern core.Pattern + Symbol *Symbol +} + +type CommentDirectiveKind int32 + +const ( + CommentDirectiveKindUnknown CommentDirectiveKind = iota + CommentDirectiveKindExpectError + CommentDirectiveKindIgnore +) + +type CommentDirective struct { + Loc core.TextRange + Kind CommentDirectiveKind +} + +// SourceFile + +type SourceFileMetaData struct { + PackageJsonType string + PackageJsonDirectory string + ImpliedNodeFormat core.ResolutionMode +} + +// SourceFileDataKey identifies lazily-computed data attached to a SourceFile by +// another package. Prefer regular SourceFile fields for ast-owned data. +type SourceFileDataKey[T any] struct { + key sourceFileDataKey + _ [0]T +} + +type sourceFileDataKey uint64 + +var sourceFileDataKeyCounter atomic.Uint64 + +type sourceFileDataCell[T any] struct { + once sync.Once + value T +} + +func NewSourceFileDataKey[T any]() *SourceFileDataKey[T] { + return &SourceFileDataKey[T]{key: sourceFileDataKey(sourceFileDataKeyCounter.Add(1))} +} + +func GetOrComputeSourceFileData[T any](file *SourceFile, key *SourceFileDataKey[T], compute func(*SourceFile) T) T { + cell := getSourceFileDataCell(file, key) + cell.once.Do(func() { + cell.value = compute(file) + }) + return cell.value +} + +func getSourceFileDataCell[T any](file *SourceFile, key *SourceFileDataKey[T]) *sourceFileDataCell[T] { + if key == nil || key.key == 0 { + panic("invalid SourceFileDataKey; use NewSourceFileDataKey") + } + + file.dataMu.Lock() + defer file.dataMu.Unlock() + + if file.data == nil { + file.data = make(map[sourceFileDataKey]any) + } + if cell, ok := file.data[key.key]; ok { + return cell.(*sourceFileDataCell[T]) + } + cell := &sourceFileDataCell[T]{} + file.data[key.key] = cell + return cell +} + +type CheckJsDirective struct { + Enabled bool + Range CommentRange +} + +type HasFileName interface { + FileName() string + Path() tspath.Path +} + +type TokenCacheKey struct { + parent *Node + loc core.TextRange +} + +type SourceFile struct { + NodeBase + DeclarationBase + LocalsContainerBase + CompositeBase + + // Fields set by NewSourceFile + fileName string // For debugging convenience + parseOptions SourceFileParseOptions + text string + Statements *NodeList // NodeList[*Statement] + EndOfFileToken *TokenNode // TokenNode[*EndOfFileToken] + + // Fields for lazily-computed data owned by packages outside ast. + dataMu sync.Mutex + data map[sourceFileDataKey]any + + // Fields set by parser + diagnostics []*Diagnostic + jsDiagnostics []*Diagnostic + jsdocDiagnostics []*Diagnostic + LanguageVariant core.LanguageVariant + ScriptKind core.ScriptKind + IsDeclarationFile bool + ContainsNonASCII bool + UsesUriStyleNodeCoreModules core.Tristate + Identifiers map[string]string + IdentifierCount int + imports []*LiteralLikeNode // []LiteralLikeNode + ModuleAugmentations []*ModuleName // []ModuleName + AmbientModuleNames []string + CommentDirectives []CommentDirective + jsdocCache map[*Node][]*Node + jsdocMu sync.RWMutex + hasLazyJSDoc bool + ReparsedClones []*Node + Pragmas []Pragma + ReferencedFiles []*FileReference + TypeReferenceDirectives []*FileReference + LibReferenceDirectives []*FileReference + CheckJsDirective *CheckJsDirective + NodeCount int + TextCount int + CommonJSModuleIndicator *Node + // If this is the SourceFile itself, then this module was "forced" + // to be an external module (previously "true"). + ExternalModuleIndicator *Node + + // Fields set by binder + + isBound atomic.Bool + bindOnce sync.Once + bindDiagnostics []*Diagnostic + BindSuggestionDiagnostics []*Diagnostic + EndFlowNode *FlowNode + SymbolCount int + ClassifiableNames collections.Set[string] + PatternAmbientModules []*PatternAmbientModule + GlobalExports SymbolTable + + // Fields set by ECMALineMap + + ecmaLineMapMu sync.RWMutex + ecmaLineMap []core.TextPos + + // Fields set by language service + + Hash xxh3.Uint128 + tokenCacheMu sync.Mutex + tokenCache map[TokenCacheKey]*Node + tokenFactory *NodeFactory + declarationMapMu sync.Mutex + declarationMap map[string][]*Node + nameTableOnce sync.Once + nameTable map[string]int + + // Fields for UTF-8 to UTF-16 position mapping + + positionMapOnce sync.Once + positionMap *PositionMap +} + +func (f *NodeFactory) NewSourceFile(opts SourceFileParseOptions, text string, statements *NodeList, endOfFileToken *TokenNode) *Node { + if tspath.GetEncodedRootLength(opts.FileName) == 0 || opts.FileName != tspath.NormalizePath(opts.FileName) { + panic(fmt.Sprintf("fileName should be normalized and absolute: %q", opts.FileName)) + } + data := &SourceFile{} + data.fileName = opts.FileName + data.parseOptions = opts + data.text = text + data.ContainsNonASCII = stringutil.ContainsNonASCII(text) + data.Statements = statements + data.EndOfFileToken = endOfFileToken + return f.newNode(KindSourceFile, data) +} + +func (node *SourceFile) ParseOptions() SourceFileParseOptions { + return node.parseOptions +} + +func (node *SourceFile) Text() string { + return node.text +} + +func (node *SourceFile) FileName() string { + return node.parseOptions.FileName +} + +func (node *SourceFile) Path() tspath.Path { + return node.parseOptions.Path +} + +func (node *SourceFile) Imports() []*LiteralLikeNode { + return node.imports +} + +func (node *SourceFile) Diagnostics() []*Diagnostic { + return node.diagnostics +} + +func (node *SourceFile) SetDiagnostics(diags []*Diagnostic) { + node.diagnostics = diags +} + +func (node *SourceFile) JSDiagnostics() []*Diagnostic { + return node.jsDiagnostics +} + +func (node *SourceFile) SetJSDiagnostics(diags []*Diagnostic) { + node.jsDiagnostics = diags +} + +func (node *SourceFile) JSDocDiagnostics() []*Diagnostic { + return node.jsdocDiagnostics +} + +func (node *SourceFile) SetJSDocDiagnostics(diags []*Diagnostic) { + node.jsdocDiagnostics = diags +} + +func (node *SourceFile) SetJSDocCache(cache map[*Node][]*Node) { + node.jsdocCache = cache +} + +func (node *SourceFile) SetHasLazyJSDoc(lazy bool) { + node.hasLazyJSDoc = lazy +} + +func (node *SourceFile) resolveJSDoc(n *Node) []*Node { + if parseJSDocForNode == nil { + panic("resolveJSDoc called but parseJSDocForNode is not registered; ensure the parser package is imported") + } + // Fast path: check cache under read lock + node.jsdocMu.RLock() + if jsdocs, ok := node.jsdocCache[n]; ok { + node.jsdocMu.RUnlock() + return jsdocs + } + node.jsdocMu.RUnlock() + + // Slow path: parse and cache under write lock + node.jsdocMu.Lock() + defer node.jsdocMu.Unlock() + // Double-check after acquiring write lock + if jsdocs, ok := node.jsdocCache[n]; ok { + return jsdocs + } + jsdocs := parseJSDocForNode(node, n) + if node.jsdocCache == nil { + node.jsdocCache = make(map[*Node][]*Node) + } + node.jsdocCache[n] = jsdocs + return jsdocs +} + +func (node *SourceFile) BindDiagnostics() []*Diagnostic { + return node.bindDiagnostics +} + +func (node *SourceFile) SetBindDiagnostics(diags []*Diagnostic) { + node.bindDiagnostics = diags +} + +func (node *SourceFile) ForEachChild(v Visitor) bool { + return visitNodeList(v, node.Statements) || visit(v, node.EndOfFileToken) +} + +func (node *SourceFile) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateSourceFile(node, v.visitTopLevelStatements(node.Statements), v.visitToken(node.EndOfFileToken)) +} + +func (node *SourceFile) IsJS() bool { + return IsSourceFileJS(node) +} + +func (node *SourceFile) copyFrom(other *SourceFile) { + // Do not copy fields set by NewSourceFile (Text, FileName, Path, or Statements) + node.LanguageVariant = other.LanguageVariant + node.ScriptKind = other.ScriptKind + node.IsDeclarationFile = other.IsDeclarationFile + node.ContainsNonASCII = other.ContainsNonASCII + node.UsesUriStyleNodeCoreModules = other.UsesUriStyleNodeCoreModules + node.Identifiers = other.Identifiers + node.imports = other.imports + node.ModuleAugmentations = other.ModuleAugmentations + node.AmbientModuleNames = other.AmbientModuleNames + node.CommentDirectives = other.CommentDirectives + node.Pragmas = other.Pragmas + node.ReferencedFiles = other.ReferencedFiles + node.TypeReferenceDirectives = other.TypeReferenceDirectives + node.LibReferenceDirectives = other.LibReferenceDirectives + node.CommonJSModuleIndicator = other.CommonJSModuleIndicator + node.ExternalModuleIndicator = other.ExternalModuleIndicator + node.Flags |= other.Flags +} + +func (node *SourceFile) Clone(f NodeFactoryCoercible) *Node { + updated := f.AsNodeFactory().NewSourceFile(node.parseOptions, node.text, node.Statements, node.EndOfFileToken) + newFile := updated.AsSourceFile() + newFile.copyFrom(node) + return cloneNode(updated, node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *SourceFile) computeSubtreeFacts() SubtreeFacts { + return propagateNodeListSubtreeFacts(node.Statements, propagateSubtreeFacts) +} + +func (f *NodeFactory) UpdateSourceFile(node *SourceFile, statements *StatementList, endOfFileToken *TokenNode) *Node { + if statements != node.Statements || endOfFileToken != node.EndOfFileToken { + updated := f.NewSourceFile(node.parseOptions, node.text, statements, endOfFileToken).AsSourceFile() + updated.copyFrom(node) + return updateNode(updated.AsNode(), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *SourceFile) ECMALineMap() []core.TextPos { + node.ecmaLineMapMu.RLock() + lineMap := node.ecmaLineMap + node.ecmaLineMapMu.RUnlock() + if lineMap == nil { + node.ecmaLineMapMu.Lock() + defer node.ecmaLineMapMu.Unlock() + lineMap = node.ecmaLineMap + if lineMap == nil { + lineMap = core.ComputeECMALineStarts(node.Text()) + node.ecmaLineMap = lineMap + } + } + return lineMap +} + +// GetNameTable returns a map of all names in the file to their positions. +// If the name appears more than once, the value is -1. +func (file *SourceFile) GetNameTable() map[string]int { + file.nameTableOnce.Do(func() { + nameTable := make(map[string]int, file.IdentifierCount) + + var walk func(node *Node) bool + walk = func(node *Node) bool { + if IsIdentifier(node) && !IsTagName(node) && node.Text() != "" || + IsStringOrNumericLiteralLike(node) && literalIsName(node) || + IsPrivateIdentifier(node) { + text := node.Text() + if _, ok := nameTable[text]; ok { + nameTable[text] = -1 + } else { + nameTable[text] = node.Pos() + } + } + + node.ForEachChild(walk) + jsdocNodes := node.JSDoc(file) + for _, jsdoc := range jsdocNodes { + jsdoc.ForEachChild(walk) + } + return false + } + file.ForEachChild(walk) + + file.nameTable = nameTable + }) + return file.nameTable +} + +func (node *SourceFile) IsBound() bool { + return node.isBound.Load() +} + +// GetPositionMap returns the PositionMap for this source file, computing it lazily. +func (file *SourceFile) GetPositionMap() *PositionMap { + file.positionMapOnce.Do(func() { + if !file.ContainsNonASCII { + file.positionMap = &PositionMap{asciiOnly: true} + } else { + file.positionMap = ComputePositionMap(file.Text()) + } + }) + return file.positionMap +} + +func (node *SourceFile) BindOnce(bind func()) { + node.bindOnce.Do(func() { + bind() + node.isBound.Store(true) + }) +} + +// Gets a token from the file's token cache, or creates it if it does not already exist. +// This function should NOT be used for creating synthetic tokens that are not in the file in the first place. +func (node *SourceFile) GetOrCreateToken( + kind Kind, + pos int, + end int, + parent *Node, + flags TokenFlags, +) *TokenNode { + node.tokenCacheMu.Lock() + defer node.tokenCacheMu.Unlock() + loc := core.NewTextRange(pos, end) + key := TokenCacheKey{parent, loc} + if token, ok := node.tokenCache[key]; ok { + if token.Kind != kind { + panic(fmt.Sprintf("Token cache mismatch: %v != %v", token.Kind, kind)) + } + return token + } + if parent.Flags&NodeFlagsReparsed != 0 { + panic(fmt.Sprintf("Cannot create token from reparsed node of kind %v", parent.Kind)) + } + if node.tokenCache == nil { + node.tokenCache = make(map[TokenCacheKey]*Node) + } + token := createToken(kind, node, pos, end, flags) + token.Loc = loc + token.Parent = parent + node.tokenCache[key] = token + return token +} + +// `kind` should be a token kind. +func createToken(kind Kind, file *SourceFile, pos, end int, flags TokenFlags) *Node { + if file.tokenFactory == nil { + file.tokenFactory = NewNodeFactory(NodeFactoryHooks{}) + } + text := file.text[pos:end] + switch kind { + case KindNumericLiteral: + return file.tokenFactory.NewNumericLiteral(text, flags) + case KindBigIntLiteral: + return file.tokenFactory.NewBigIntLiteral(text, flags) + case KindStringLiteral: + return file.tokenFactory.NewStringLiteral(text, flags) + case KindJsxText, KindJsxTextAllWhiteSpaces: + return file.tokenFactory.NewJsxText(text, kind == KindJsxTextAllWhiteSpaces) + case KindRegularExpressionLiteral: + return file.tokenFactory.NewRegularExpressionLiteral(text, flags) + case KindNoSubstitutionTemplateLiteral: + return file.tokenFactory.NewNoSubstitutionTemplateLiteral(text, flags) + case KindTemplateHead: + return file.tokenFactory.NewTemplateHead(text, "" /*rawText*/, flags) + case KindTemplateMiddle: + return file.tokenFactory.NewTemplateMiddle(text, "" /*rawText*/, flags) + case KindTemplateTail: + return file.tokenFactory.NewTemplateTail(text, "" /*rawText*/, flags) + case KindIdentifier: + return file.tokenFactory.NewIdentifier(text) + case KindPrivateIdentifier: + return file.tokenFactory.NewPrivateIdentifier(text) + default: // Punctuation and keywords + return file.tokenFactory.NewToken(kind) + } +} + +func (node *SourceFile) GetDeclarationMap() map[string][]*Node { + node.declarationMapMu.Lock() + defer node.declarationMapMu.Unlock() + if node.declarationMap == nil { + node.declarationMap = node.computeDeclarationMap() + } + return node.declarationMap +} + +func (node *SourceFile) computeDeclarationMap() map[string][]*Node { + result := make(map[string][]*Node) + + addDeclaration := func(declaration *Node) { + name := GetDeclarationName(declaration) + if name != "" { + result[name] = append(result[name], declaration) + } + } + + var visit func(*Node) bool + visit = func(node *Node) bool { + switch node.Kind { + case KindFunctionDeclaration, KindFunctionExpression, KindMethodDeclaration, KindMethodSignature: + declarationName := GetDeclarationName(node) + if declarationName != "" { + declarations := result[declarationName] + var lastDeclaration *Node + if len(declarations) != 0 { + lastDeclaration = declarations[len(declarations)-1] + } + // Check whether this declaration belongs to an "overload group". + if lastDeclaration != nil && node.Parent == lastDeclaration.Parent && node.Symbol() == lastDeclaration.Symbol() { + // Overwrite the last declaration if it was an overload and this one is an implementation. + if node.Body() != nil && lastDeclaration.Body() == nil { + declarations[len(declarations)-1] = node + } + } else { + result[declarationName] = append(result[declarationName], node) + } + } + node.ForEachChild(visit) + case KindClassDeclaration, KindClassExpression, KindInterfaceDeclaration, KindTypeAliasDeclaration, KindEnumDeclaration, KindModuleDeclaration, + KindImportEqualsDeclaration, KindImportClause, KindNamespaceImport, KindGetAccessor, KindSetAccessor, KindTypeLiteral: + addDeclaration(node) + node.ForEachChild(visit) + case KindImportSpecifier, KindExportSpecifier: + if node.PropertyName() != nil { + addDeclaration(node) + } + case KindParameter: + // Only consider parameter properties + if !HasSyntacticModifier(node, ModifierFlagsParameterPropertyModifier) { + break + } + fallthrough + case KindVariableDeclaration, KindBindingElement: + name := node.Name() + if name != nil { + if IsBindingPattern(name) { + node.Name().ForEachChild(visit) + } else { + if node.Initializer() != nil { + visit(node.Initializer()) + } + addDeclaration(node) + } + } + case KindEnumMember, KindPropertyDeclaration, KindPropertySignature: + addDeclaration(node) + case KindExportDeclaration: + // Handle named exports case e.g.: + // export {a, b as B} from "mod"; + exportClause := node.AsExportDeclaration().ExportClause + if exportClause != nil { + if IsNamedExports(exportClause) { + for _, element := range exportClause.Elements() { + visit(element) + } + } else { + visit(exportClause.AsNamespaceExport().Name()) + } + } + case KindImportDeclaration: + importClause := node.AsImportDeclaration().ImportClause + if importClause != nil { + // Handle default import case e.g.: + // import d from "mod"; + if importClause.Name() != nil { + addDeclaration(importClause.Name()) + } + // Handle named bindings in imports e.g.: + // import * as NS from "mod"; + // import {a, b as B} from "mod"; + namedBindings := importClause.AsImportClause().NamedBindings + if namedBindings != nil { + if namedBindings.Kind == KindNamespaceImport { + addDeclaration(namedBindings) + } else { + for _, element := range namedBindings.Elements() { + visit(element) + } + } + } + } + case KindBinaryExpression: + switch GetAssignmentDeclarationKind(node) { + case JSDeclarationKindExportsProperty, JSDeclarationKindThisProperty, JSDeclarationKindProperty: + addDeclaration(node) + } + node.ForEachChild(visit) + default: + node.ForEachChild(visit) + } + return false + } + node.ForEachChild(visit) + return result +} + +func GetDeclarationName(declaration *Node) string { + name := GetNonAssignedNameOfDeclaration(declaration) + if name != nil { + if IsComputedPropertyName(name) { + if IsStringOrNumericLiteralLike(name.Expression()) { + return name.Expression().Text() + } + if IsPropertyAccessExpression(name.Expression()) { + return name.Expression().Name().Text() + } + } else if IsPropertyName(name) { + return name.Text() + } + } + return "" +} + +type SourceFileLike interface { + Text() string + ECMALineMap() []core.TextPos +} + +type CommentRange struct { + core.TextRange + Kind Kind + HasTrailingNewLine bool +} + +func (f *NodeFactory) NewCommentRange(kind Kind, pos int, end int, hasTrailingNewLine bool) CommentRange { + return CommentRange{ + TextRange: core.NewTextRange(pos, end), + Kind: kind, + HasTrailingNewLine: hasTrailingNewLine, + } +} + +type FileReference struct { + core.TextRange + FileName string + ResolutionMode core.ResolutionMode + Preserve bool +} + +type PragmaArgument struct { + core.TextRange + Name string + Value string +} + +type Pragma struct { + CommentRange + Name string + Args map[string]PragmaArgument +} + +type PragmaKindFlags = uint8 + +const ( + PragmaKindTripleSlashXML PragmaKindFlags = 1 << iota + PragmaKindSingleLine + PragmaKindMultiLine + PragmaKindFlagsNone PragmaKindFlags = 0 + PragmaKindAll = PragmaKindTripleSlashXML | PragmaKindSingleLine | PragmaKindMultiLine + PragmaKindDefault = PragmaKindAll +) + +type PragmaArgumentSpecification struct { + Name string + Optional bool + CaptureSpan bool +} +type PragmaSpecification struct { + Args []PragmaArgumentSpecification + Kind PragmaKindFlags +} + +func (spec *PragmaSpecification) IsTripleSlash() bool { + return (spec.Kind & PragmaKindTripleSlashXML) > 0 +} + +// Hand-written visitor implementations for nodes with runtime-dependent +// child ordering. Generated code in ast_generated.go delegates to these. + +func forEachChild_JSDocParameterOrPropertyTag(node *JSDocParameterOrPropertyTag, v Visitor) bool { + return visit(v, node.TagName) || + (node.IsNameFirst && + (visit(v, node.name) || visit(v, node.TypeExpression))) || + (!node.IsNameFirst && + (visit(v, node.TypeExpression) || visit(v, node.name))) || + visitNodeList(v, node.Comment) +} + +func visitEachChild_JSDocParameterOrPropertyTag(node *JSDocParameterOrPropertyTag, v *NodeVisitor) *Node { + return v.Factory.UpdateJSDocParameterOrPropertyTag(node, v.visitNode(node.TagName), v.visitNode(node.name), node.IsBracketed, v.visitNode(node.TypeExpression), node.IsNameFirst, v.visitNodes(node.Comment)) +} + +func (f *NodeFactory) ReleaseArenas() { + *f = NodeFactory{ + hooks: f.hooks, + textCount: f.textCount, + nodeCount: f.nodeCount, + } +} diff --git a/tools/tsgo/internal/ast/ast_generated.go b/tools/tsgo/internal/ast/ast_generated.go new file mode 100644 index 00000000..d8560488 --- /dev/null +++ b/tools/tsgo/internal/ast/ast_generated.go @@ -0,0 +1,10047 @@ +// Code generated by _scripts/generate-go-ast.ts. DO NOT EDIT. + +package ast + +import ( + "sync/atomic" + + "github.com/microsoft/typescript-go/internal/core" +) + +var ( + _ = core.Same[string] // prevent unused import + _ atomic.Uint32 // prevent unused import +) + +// ────────────────────────────────────────────────────────────────────── +// NodeFactory +// ────────────────────────────────────────────────────────────────────── + +type NodeFactory struct { + hooks NodeFactoryHooks + arrayTypeNodeArena core.Arena[ArrayTypeNode] + binaryExpressionArena core.Arena[BinaryExpression] + blockArena core.Arena[Block] + callExpressionArena core.Arena[CallExpression] + conditionalExpressionArena core.Arena[ConditionalExpression] + constructSignatureDeclarationArena core.Arena[ConstructSignatureDeclaration] + elementAccessExpressionArena core.Arena[ElementAccessExpression] + expressionStatementArena core.Arena[ExpressionStatement] + expressionWithTypeArgumentsArena core.Arena[ExpressionWithTypeArguments] + functionDeclarationArena core.Arena[FunctionDeclaration] + functionTypeNodeArena core.Arena[FunctionTypeNode] + heritageClauseArena core.Arena[HeritageClause] + identifierArena core.Arena[Identifier] + ifStatementArena core.Arena[IfStatement] + importSpecifierArena core.Arena[ImportSpecifier] + indexedAccessTypeNodeArena core.Arena[IndexedAccessTypeNode] + interfaceDeclarationArena core.Arena[InterfaceDeclaration] + intersectionTypeNodeArena core.Arena[IntersectionTypeNode] + jsdocArena core.Arena[JSDoc] + jsdocDeprecatedTagArena core.Arena[JSDocDeprecatedTag] + jsdocTextArena core.Arena[JSDocText] + jsdocUnknownTagArena core.Arena[JSDocUnknownTag] + keywordExpressionArena core.Arena[KeywordExpression] + keywordTypeNodeArena core.Arena[KeywordTypeNode] + literalTypeNodeArena core.Arena[LiteralTypeNode] + methodSignatureDeclarationArena core.Arena[MethodSignatureDeclaration] + modifierListArena core.Arena[ModifierList] + nodeListArena core.Arena[NodeList] + numericLiteralArena core.Arena[NumericLiteral] + parameterDeclarationArena core.Arena[ParameterDeclaration] + parenthesizedExpressionArena core.Arena[ParenthesizedExpression] + parenthesizedTypeNodeArena core.Arena[ParenthesizedTypeNode] + prefixUnaryExpressionArena core.Arena[PrefixUnaryExpression] + propertyAccessExpressionArena core.Arena[PropertyAccessExpression] + propertyAssignmentArena core.Arena[PropertyAssignment] + propertySignatureDeclarationArena core.Arena[PropertySignatureDeclaration] + returnStatementArena core.Arena[ReturnStatement] + stringLiteralArena core.Arena[StringLiteral] + tokenArena core.Arena[Token] + typeAliasDeclarationArena core.Arena[TypeAliasDeclaration] + typeLiteralNodeArena core.Arena[TypeLiteralNode] + typeOperatorNodeArena core.Arena[TypeOperatorNode] + typeParameterDeclarationArena core.Arena[TypeParameterDeclaration] + typeReferenceNodeArena core.Arena[TypeReferenceNode] + unionTypeNodeArena core.Arena[UnionTypeNode] + variableDeclarationArena core.Arena[VariableDeclaration] + variableDeclarationListArena core.Arena[VariableDeclarationList] + variableStatementArena core.Arena[VariableStatement] + + nodeCount int + textCount int +} + +// ────────────────────────────────────────────────────────────────────── +// Base struct definitions +// ────────────────────────────────────────────────────────────────────── + +type StatementBase struct { + NodeBase + FlowNodeBase +} + +type IterationStatementBase struct { + StatementBase + Statement *Statement +} + +type ExpressionBase struct { + NodeBase +} + +type UnaryExpressionBase struct { + ExpressionBase +} + +type UpdateExpressionBase struct { + UnaryExpressionBase +} + +type LeftHandSideExpressionBase struct { + UpdateExpressionBase +} + +type MemberExpressionBase struct { + LeftHandSideExpressionBase +} + +type PrimaryExpressionBase struct { + MemberExpressionBase +} + +type TypeNodeBase struct { + NodeBase + TypeSyntaxBase +} + +type NodeWithTypeArgumentsBase struct { + TypeNodeBase + TypeArguments *TypeList // Optional +} + +type JSDocTypeBase struct { + TypeNodeBase +} + +type DeclarationBase struct { + Symbol *Symbol +} + +type ExportableBase struct { + LocalSymbol *Symbol +} + +type ModifiersBase struct { + modifiers *ModifierList // Optional +} + +type LocalsContainerBase struct { + Locals SymbolTable + NextContainer *Node +} + +type FlowNodeBase struct { + FlowNode *FlowNode +} + +type CompositeBase struct { + facts atomic.Uint32 +} + +type TypeSyntaxBase struct{} + +type FunctionLikeBase struct { + DeclarationBase + LocalsContainerBase + TypeParameters *TypeParameterList // Optional + Parameters *ParameterList + Type *TypeNode // Optional + FullSignature *TypeNode // Optional +} + +type BodyBase struct { + AsteriskToken *AsteriskToken // Optional + Body *NodeBody // Optional + EndFlowNode *FlowNode +} + +type FunctionLikeWithBodyBase struct { + FunctionLikeBase + BodyBase +} + +type ClassLikeBase struct { + DeclarationBase + ExportableBase + ModifiersBase + LocalsContainerBase + CompositeBase + name *IdentifierNode // Optional + TypeParameters *TypeParameterList // Optional + HeritageClauses *HeritageClauseList // Optional + Members *ClassElementList +} + +type LiteralLikeNodeBase struct { + Text string + TokenFlags TokenFlags +} + +type LiteralExpressionBase struct { + LiteralLikeNodeBase + PrimaryExpressionBase +} + +type TemplateLiteralLikeNodeBase struct { + LiteralLikeNodeBase + RawText string + TemplateFlags TokenFlags +} + +type TypeElementBase struct{} + +type ClassElementBase struct{} + +type NamedMemberBase struct { + DeclarationBase + ModifiersBase + name *PropertyName + PostfixToken *TokenNode // Optional +} + +type ObjectLiteralElementBase struct{} + +type AccessorDeclarationBase struct { + NamedMemberBase + FunctionLikeWithBodyBase + FlowNodeBase + TypeElementBase + ClassElementBase + ObjectLiteralElementBase + CompositeBase + NodeBase +} + +type FunctionOrConstructorTypeNodeBase struct { + TypeNodeBase + ModifiersBase + FunctionLikeBase +} + +type UnionOrIntersectionTypeNodeBase struct { + TypeNodeBase + Types *TypeList +} + +type JSDocTagBase struct { + NodeBase + TagName *IdentifierNode + Comment *NodeList // Optional +} + +type JSDocCommentBase struct { + NodeBase + text []string +} + +// ────────────────────────────────────────────────────────────────────── +// Node type aliases +// ────────────────────────────────────────────────────────────────────── + +type ( + TokenNode = Node + IdentifierNode = Node + PrivateIdentifierNode = Node + QualifiedNameNode = Node + ComputedPropertyNameNode = Node + DecoratorNode = Node + EmptyStatementNode = Node + IfStatementNode = Node + DoStatementNode = Node + WhileStatementNode = Node + ForStatementNode = Node + ForInOrOfStatementNode = Node + BreakStatementNode = Node + ContinueStatementNode = Node + ReturnStatementNode = Node + WithStatementNode = Node + SwitchStatementNode = Node + CaseBlockNode = Node + CaseOrDefaultClauseNode = Node + ThrowStatementNode = Node + TryStatementNode = Node + CatchClauseNode = Node + DebuggerStatementNode = Node + LabeledStatementNode = Node + ExpressionStatementNode = Node + BlockNode = Node + VariableStatementNode = Node + VariableDeclarationNode = Node + VariableDeclarationListNode = Node + BindingPatternNode = Node + ParameterDeclarationNode = Node + BindingElementNode = Node + MissingDeclarationNode = Node + FunctionDeclarationNode = Node + ClassDeclarationNode = Node + ClassExpressionNode = Node + HeritageClauseNode = Node + InterfaceDeclarationNode = Node + TypeAliasDeclarationNode = Node + EnumMemberNode = Node + EnumDeclarationNode = Node + ModuleBlockNode = Node + NotEmittedStatementNode = Node + NotEmittedTypeElementNode = Node + ImportDeclarationNode = Node + ExternalModuleReferenceNode = Node + NamespaceImportNode = Node + NamedImportsNode = Node + ExportAssignmentNode = Node + NamespaceExportDeclarationNode = Node + NamespaceExportNode = Node + NamedExportsNode = Node + ExportSpecifierNode = Node + CallSignatureDeclarationNode = Node + ConstructSignatureDeclarationNode = Node + ConstructorDeclarationNode = Node + GetAccessorDeclarationNode = Node + SetAccessorDeclarationNode = Node + IndexSignatureDeclarationNode = Node + MethodSignatureDeclarationNode = Node + MethodDeclarationNode = Node + PropertySignatureDeclarationNode = Node + PropertyDeclarationNode = Node + SemicolonClassElementNode = Node + ClassStaticBlockDeclarationNode = Node + OmittedExpressionNode = Node + KeywordExpressionNode = Node + StringLiteralNode = Node + NumericLiteralNode = Node + BigIntLiteralNode = Node + RegularExpressionLiteralNode = Node + NoSubstitutionTemplateLiteralNode = Node + BinaryExpressionNode = Node + PrefixUnaryExpressionNode = Node + PostfixUnaryExpressionNode = Node + YieldExpressionNode = Node + ArrowFunctionNode = Node + FunctionExpressionNode = Node + AsExpressionNode = Node + SatisfiesExpressionNode = Node + ConditionalExpressionNode = Node + PropertyAccessExpressionNode = Node + ElementAccessExpressionNode = Node + CallExpressionNode = Node + NewExpressionNode = Node + MetaPropertyNode = Node + NonNullExpressionNode = Node + SpreadElementNode = Node + TemplateExpressionNode = Node + TemplateSpanNode = Node + TaggedTemplateExpressionNode = Node + ParenthesizedExpressionNode = Node + ArrayLiteralExpressionNode = Node + ObjectLiteralExpressionNode = Node + SpreadAssignmentNode = Node + PropertyAssignmentNode = Node + ShorthandPropertyAssignmentNode = Node + DeleteExpressionNode = Node + TypeOfExpressionNode = Node + VoidExpressionNode = Node + AwaitExpressionNode = Node + TypeAssertionNode = Node + KeywordTypeNodeNode = Node + UnionTypeNodeNode = Node + IntersectionTypeNodeNode = Node + ConditionalTypeNodeNode = Node + TypeOperatorNodeNode = Node + InferTypeNodeNode = Node + ArrayTypeNodeNode = Node + IndexedAccessTypeNodeNode = Node + TypeReferenceNodeNode = Node + ExpressionWithTypeArgumentsNode = Node + LiteralTypeNodeNode = Node + ThisTypeNodeNode = Node + TypePredicateNodeNode = Node + ImportAttributeNode = Node + ImportAttributesNode = Node + TypeQueryNodeNode = Node + MappedTypeNodeNode = Node + TypeLiteralNodeNode = Node + TupleTypeNodeNode = Node + NamedTupleMemberNode = Node + OptionalTypeNodeNode = Node + RestTypeNodeNode = Node + ParenthesizedTypeNodeNode = Node + FunctionTypeNodeNode = Node + ConstructorTypeNodeNode = Node + TemplateHeadNode = Node + TemplateMiddleNode = Node + TemplateTailNode = Node + TemplateLiteralTypeNodeNode = Node + TemplateLiteralTypeSpanNode = Node + SyntheticExpressionNode = Node + PartiallyEmittedExpressionNode = Node + JsxElementNode = Node + JsxAttributesNode = Node + JsxNamespacedNameNode = Node + JsxOpeningElementNode = Node + JsxSelfClosingElementNode = Node + JsxFragmentNode = Node + JsxOpeningFragmentNode = Node + JsxClosingFragmentNode = Node + JsxAttributeNode = Node + JsxSpreadAttributeNode = Node + JsxClosingElementNode = Node + JsxExpressionNode = Node + JsxTextNode = Node + SyntaxListNode = Node + JSDocNode = Node + JSDocTypeExpressionNode = Node + JSDocNonNullableTypeNode = Node + JSDocNullableTypeNode = Node + JSDocAllTypeNode = Node + JSDocVariadicTypeNode = Node + JSDocOptionalTypeNode = Node + JSDocTypeTagNode = Node + JSDocUnknownTagNode = Node + JSDocTemplateTagNode = Node + JSDocReturnTagNode = Node + JSDocPublicTagNode = Node + JSDocPrivateTagNode = Node + JSDocProtectedTagNode = Node + JSDocReadonlyTagNode = Node + JSDocOverrideTagNode = Node + JSDocDeprecatedTagNode = Node + JSDocSeeTagNode = Node + JSDocImplementsTagNode = Node + JSDocAugmentsTagNode = Node + JSDocSatisfiesTagNode = Node + JSDocThrowsTagNode = Node + JSDocThisTagNode = Node + JSDocImportTagNode = Node + JSDocCallbackTagNode = Node + JSDocOverloadTagNode = Node + JSDocTypedefTagNode = Node + JSDocSignatureNode = Node + JSDocNameReferenceNode = Node + SourceFileNode = Node + ModuleDeclarationNode = Node + ImportEqualsDeclarationNode = Node + ExportDeclarationNode = Node + ImportTypeNodeNode = Node + ImportClauseNode = Node + ImportSpecifierNode = Node + JSDocTextNode = Node + JSDocLinkNode = Node + JSDocLinkPlainNode = Node + JSDocLinkCodeNode = Node + TypeParameterDeclarationNode = Node + SyntheticReferenceExpressionNode = Node + JSDocTypeLiteralNode = Node + JSDocParameterOrPropertyTagNode = Node + EndOfFile = Node + DotToken = Node + DotDotDotToken = Node + QuestionToken = Node + ExclamationToken = Node + ColonToken = Node + EqualsToken = Node + AsteriskToken = Node + EqualsGreaterThanToken = Node + PlusToken = Node + MinusToken = Node + QuestionDotToken = Node + AssertsKeyword = Node + AssertKeyword = Node + AwaitKeyword = Node + CaseKeyword = Node + AbstractKeyword = Node + AccessorKeyword = Node + AsyncKeyword = Node + ConstKeyword = Node + DeclareKeyword = Node + DefaultKeyword = Node + ExportKeyword = Node + InKeyword = Node + PrivateKeyword = Node + ProtectedKeyword = Node + PublicKeyword = Node + ReadonlyKeyword = Node + OutKeyword = Node + OverrideKeyword = Node + StaticKeyword = Node + BinaryOperatorToken = Node + AssignmentOperatorToken = Node + NullLiteral = Node + TrueLiteral = Node + FalseLiteral = Node + ThisExpression = Node + SuperExpression = Node + ImportExpression = Node +) + +type ( + StatementList = NodeList // NodeList[*Statement] + CaseClausesList = NodeList // NodeList[*CaseOrDefaultClause] + VariableDeclarationNodeList = NodeList // NodeList[*VariableDeclaration] + BindingElementList = NodeList // NodeList[*BindingElement] + TypeParameterList = NodeList // NodeList[*TypeParameterDeclaration] + ParameterList = NodeList // NodeList[*ParameterDeclaration] + HeritageClauseList = NodeList // NodeList[*HeritageClause] + ClassElementList = NodeList // NodeList[*ClassElement] + TypeElementList = NodeList // NodeList[*TypeElement] + ExpressionWithTypeArgumentsList = NodeList // NodeList[*ExpressionWithTypeArguments] + EnumMemberList = NodeList // NodeList[*EnumMember] + ImportSpecifierList = NodeList // NodeList[*ImportSpecifier] + ExportSpecifierList = NodeList // NodeList[*ExportSpecifier] + TypeArgumentList = NodeList // NodeList[*TypeNode] + ArgumentList = NodeList // NodeList[*Expression] + TemplateSpanList = NodeList // NodeList[*TemplateSpan] + ElementList = NodeList // NodeList[*Expression] + PropertyDefinitionList = NodeList // NodeList[*ObjectLiteralElement] + TypeList = NodeList // NodeList[*TypeNode] + ImportAttributeList = NodeList // NodeList[*ImportAttribute] + TemplateLiteralTypeSpanList = NodeList // NodeList[*TemplateLiteralTypeSpan] + JsxChildList = NodeList // NodeList[*JsxChild] + JsxAttributeList = NodeList // NodeList[*JsxAttributeLike] +) + +// ────────────────────────────────────────────────────────────────────── +// Node union aliases +// ────────────────────────────────────────────────────────────────────── + +type ( + Expression = Node // Node with ExpressionBase + Statement = Node // Node with StatementBase + TypeNode = Node // Node with TypeNodeBase + BlockOrExpression = Node // Block | Expression + NodeBody = Node // Block | Expression | ModuleBlock | ModuleDeclaration + AccessExpression = Node // PropertyAccessExpression | ElementAccessExpression + DeclarationName = Node // Identifier | PrivateIdentifier | StringLiteral | NumericLiteral | BigIntLiteral | NoSubstitutionTemplateLiteral | ComputedPropertyName | BindingPattern | ElementAccessExpression + ModuleName = Node // Identifier | StringLiteral + ModuleExportName = Node // Identifier | StringLiteral + PropertyName = Node // Identifier | StringLiteral | NoSubstitutionTemplateLiteral | NumericLiteral | ComputedPropertyName | PrivateIdentifier | BigIntLiteral + ModuleBody = Node // ModuleBlock | ModuleDeclaration + JSDocFullName = Node // Identifier | ModuleDeclaration + ForInitializer = Node // Expression | MissingDeclaration | VariableDeclarationList + ModuleReference = Node // Identifier | QualifiedName | ExternalModuleReference + NamedImportBindings = Node // NamespaceImport | NamedImports + NamedExportBindings = Node // NamespaceExport | NamedExports + MemberName = Node // Identifier | PrivateIdentifier + EntityName = Node // Identifier | QualifiedName + BindingName = Node // Identifier | BindingPattern + ModifierLike = Node // Modifier | Decorator + JsxChild = Node // JsxText | JsxExpression | JsxElement | JsxSelfClosingElement | JsxFragment + JsxAttributeLike = Node // JsxAttribute | JsxSpreadAttribute + JsxAttributeName = Node // Identifier | JsxNamespacedName + JsxAttributeValue = Node // StringLiteral | JsxExpression | JsxElement | JsxSelfClosingElement | JsxFragment + JsxTagNameExpression = Node // Identifier | ThisExpression | JsxTagNamePropertyAccess | JsxNamespacedName + ClassLikeDeclaration = Node // ClassDeclaration | ClassExpression + AccessorDeclaration = Node // GetAccessorDeclaration | SetAccessorDeclaration + LiteralLikeNode = Node // StringLiteral | NumericLiteral | BigIntLiteral | RegularExpressionLiteral | TemplateLiteralLikeNode | JsxText + LiteralExpression = Node // StringLiteral | NumericLiteral | BigIntLiteral | RegularExpressionLiteral | NoSubstitutionTemplateLiteral + UnionOrIntersectionTypeNode = Node // UnionTypeNode | IntersectionTypeNode + TemplateLiteralLikeNode = Node // PseudoLiteralSyntaxKind + TemplateMiddleOrTail = Node // TemplateMiddle | TemplateTail + TemplateLiteral = Node // TemplateExpression | NoSubstitutionTemplateLiteral + TypePredicateParameterName = Node // Identifier | ThisTypeNode + ImportAttributeName = Node // Identifier | StringLiteral + LeftHandSideExpression = Node // Node with LeftHandSideExpressionBase + JSDocComment = Node // JSDocText | JSDocLink | JSDocLinkCode | JSDocLinkPlain + SignatureDeclaration = Node // CallSignatureDeclaration | ConstructSignatureDeclaration | MethodSignatureDeclaration | IndexSignatureDeclaration | FunctionTypeNode | ConstructorTypeNode | FunctionDeclaration | MethodDeclaration | ConstructorDeclaration | AccessorDeclaration | FunctionExpression | ArrowFunction + StringLiteralLikeNode = Node // StringLiteral | NoSubstitutionTemplateLiteral + NumericOrStringLikeLiteral = Node // StringLiteralLikeNode | NumericLiteral + ObjectLiteralLikeNode = Node // ObjectLiteralExpression | ObjectBindingPattern + ObjectTypeDeclaration = Node // ClassLikeDeclaration | InterfaceDeclaration | TypeLiteralNode + JsxOpeningLikeElement = Node // JsxOpeningElement | JsxSelfClosingElement + NamedImportsOrExports = Node // NamedImports | NamedExports + BreakOrContinueStatement = Node // BreakStatement | ContinueStatement + CallLikeExpression = Node // CallExpression | NewExpression | TaggedTemplateExpression | Decorator | JsxOpeningLikeElement | BinaryExpression + FunctionLikeDeclaration = Node // FunctionDeclaration | MethodDeclaration | GetAccessorDeclaration | SetAccessorDeclaration | ConstructorDeclaration | FunctionExpression | ArrowFunction + VariableOrParameterDeclaration = Node // VariableDeclaration | ParameterDeclaration + VariableOrPropertyDeclaration = Node // VariableDeclaration | PropertyDeclaration + CallOrNewExpression = Node // CallExpression | NewExpression + ImportClauseOrBindingPattern = Node // ImportClause | BindingPattern + AnyImportSyntax = Node // ImportDeclaration | ImportEqualsDeclaration + Declaration = Node // Node with DeclarationBase + ClassElement = Node // Node with ClassElementBase + TypeElement = Node // Node with TypeElementBase + ObjectLiteralElement = Node // Node with ObjectLiteralElementBase + JSDocTag = Node // Node with JSDocTagBase + ArrayBindingElement = Node // BindingElement | OmittedExpression + AssertionExpression = Node // TypeAssertion | AsExpression + BooleanLiteral = Node // TrueLiteral | FalseLiteral + ConciseBody = Node // Block | Expression + DestructuringAssignment = Node // ObjectDestructuringAssignment | ArrayDestructuringAssignment + LiteralToken = Node // NumericLiteral | BigIntLiteral | StringLiteral | JsxText | RegularExpressionLiteral | NoSubstitutionTemplateLiteral + Modifier = Node // ModifierSyntaxKind + ObjectLiteralElementLike = Node // PropertyAssignment | ShorthandPropertyAssignment | SpreadAssignment | MethodDeclaration | GetAccessorDeclaration | SetAccessorDeclaration + PropertyNameLiteral = Node // Identifier | StringLiteral | NumericLiteral + PseudoLiteralToken = Node // PseudoLiteralSyntaxKind + TemplateLiteralToken = Node // NoSubstitutionTemplateLiteral | PseudoLiteralToken + ArrayDestructuringAssignment = Node // BinaryExpression + ObjectDestructuringAssignment = Node // BinaryExpression + FunctionBody = Node // Block + IncrementExpression = Node // UpdateExpressionBase +) + +// ────────────────────────────────────────────────────────────────────── +// Token +// ────────────────────────────────────────────────────────────────────── + +type Token struct { + NodeBase +} + +func (f *NodeFactory) NewToken(kind TokenSyntaxKind) *Node { + data := f.tokenArena.New() + return f.newNode(kind, data) +} + +func (node *Token) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewToken(node.Kind), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsToken(node *Node) bool { + switch node.Kind { + case KindUnknown, KindEndOfFile, KindSingleLineCommentTrivia, KindMultiLineCommentTrivia, KindNewLineTrivia, KindWhitespaceTrivia, KindConflictMarkerTrivia, KindNonTextFileMarkerTrivia, KindNumericLiteral, KindBigIntLiteral, KindStringLiteral, KindJsxText, KindJsxTextAllWhiteSpaces, KindRegularExpressionLiteral, KindNoSubstitutionTemplateLiteral, KindTemplateHead, KindTemplateMiddle, KindTemplateTail, KindOpenBraceToken, KindCloseBraceToken, KindOpenParenToken, KindCloseParenToken, KindOpenBracketToken, KindCloseBracketToken, KindDotToken, KindDotDotDotToken, KindSemicolonToken, KindCommaToken, KindQuestionDotToken, KindLessThanToken, KindLessThanSlashToken, KindGreaterThanToken, KindLessThanEqualsToken, KindGreaterThanEqualsToken, KindEqualsEqualsToken, KindExclamationEqualsToken, KindEqualsEqualsEqualsToken, KindExclamationEqualsEqualsToken, KindEqualsGreaterThanToken, KindPlusToken, KindMinusToken, KindAsteriskToken, KindAsteriskAsteriskToken, KindSlashToken, KindPercentToken, KindPlusPlusToken, KindMinusMinusToken, KindLessThanLessThanToken, KindGreaterThanGreaterThanToken, KindGreaterThanGreaterThanGreaterThanToken, KindAmpersandToken, KindBarToken, KindCaretToken, KindExclamationToken, KindTildeToken, KindAmpersandAmpersandToken, KindBarBarToken, KindQuestionToken, KindColonToken, KindAtToken, KindQuestionQuestionToken, KindBacktickToken, KindHashToken, KindEqualsToken, KindPlusEqualsToken, KindMinusEqualsToken, KindAsteriskEqualsToken, KindAsteriskAsteriskEqualsToken, KindSlashEqualsToken, KindPercentEqualsToken, KindLessThanLessThanEqualsToken, KindGreaterThanGreaterThanEqualsToken, KindGreaterThanGreaterThanGreaterThanEqualsToken, KindAmpersandEqualsToken, KindBarEqualsToken, KindBarBarEqualsToken, KindAmpersandAmpersandEqualsToken, KindQuestionQuestionEqualsToken, KindCaretEqualsToken, KindIdentifier, KindPrivateIdentifier, KindJSDocCommentTextToken, KindBreakKeyword, KindCaseKeyword, KindCatchKeyword, KindClassKeyword, KindConstKeyword, KindContinueKeyword, KindDebuggerKeyword, KindDefaultKeyword, KindDeleteKeyword, KindDoKeyword, KindElseKeyword, KindEnumKeyword, KindExportKeyword, KindExtendsKeyword, KindFalseKeyword, KindFinallyKeyword, KindForKeyword, KindFunctionKeyword, KindIfKeyword, KindImportKeyword, KindInKeyword, KindInstanceOfKeyword, KindNewKeyword, KindNullKeyword, KindReturnKeyword, KindSuperKeyword, KindSwitchKeyword, KindThisKeyword, KindThrowKeyword, KindTrueKeyword, KindTryKeyword, KindTypeOfKeyword, KindVarKeyword, KindVoidKeyword, KindWhileKeyword, KindWithKeyword, KindImplementsKeyword, KindInterfaceKeyword, KindLetKeyword, KindPackageKeyword, KindPrivateKeyword, KindProtectedKeyword, KindPublicKeyword, KindStaticKeyword, KindYieldKeyword, KindAbstractKeyword, KindAccessorKeyword, KindAsKeyword, KindAssertsKeyword, KindAssertKeyword, KindAnyKeyword, KindAsyncKeyword, KindAwaitKeyword, KindBooleanKeyword, KindConstructorKeyword, KindDeclareKeyword, KindGetKeyword, KindImmediateKeyword, KindInferKeyword, KindIntrinsicKeyword, KindIsKeyword, KindKeyOfKeyword, KindModuleKeyword, KindNamespaceKeyword, KindNeverKeyword, KindOutKeyword, KindReadonlyKeyword, KindRequireKeyword, KindNumberKeyword, KindObjectKeyword, KindSatisfiesKeyword, KindSetKeyword, KindStringKeyword, KindSymbolKeyword, KindTypeKeyword, KindUndefinedKeyword, KindUniqueKeyword, KindUnknownKeyword, KindUsingKeyword, KindFromKeyword, KindGlobalKeyword, KindBigIntKeyword, KindOverrideKeyword, KindOfKeyword, KindDeferKeyword: + return true + } + return false +} + +// ────────────────────────────────────────────────────────────────────── +// Identifier +// ────────────────────────────────────────────────────────────────────── + +type Identifier struct { + PrimaryExpressionBase + FlowNodeBase + Text string +} + +func (f *NodeFactory) NewIdentifier(text string) *Node { + data := f.identifierArena.New() + data.Text = text + f.textCount++ + return f.newNode(KindIdentifier, data) +} + +func (node *Identifier) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewIdentifier(node.Text), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsIdentifier(node *Node) bool { + return node.Kind == KindIdentifier +} + +// ────────────────────────────────────────────────────────────────────── +// PrivateIdentifier +// ────────────────────────────────────────────────────────────────────── + +type PrivateIdentifier struct { + PrimaryExpressionBase + Text string +} + +func (f *NodeFactory) NewPrivateIdentifier(text string) *Node { + data := &PrivateIdentifier{} + data.Text = text + f.textCount++ + return f.newNode(KindPrivateIdentifier, data) +} + +func (node *PrivateIdentifier) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewPrivateIdentifier(node.Text), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsPrivateIdentifier(node *Node) bool { + return node.Kind == KindPrivateIdentifier +} + +// ────────────────────────────────────────────────────────────────────── +// QualifiedName +// ────────────────────────────────────────────────────────────────────── + +type QualifiedName struct { + NodeBase + FlowNodeBase + CompositeBase + Left *EntityName + Right *IdentifierNode +} + +func (f *NodeFactory) NewQualifiedName(left *EntityName, right *IdentifierNode) *Node { + data := &QualifiedName{} + data.Left = left + data.Right = right + return f.newNode(KindQualifiedName, data) +} + +func (f *NodeFactory) UpdateQualifiedName(node *QualifiedName, left *EntityName, right *IdentifierNode) *Node { + if left != node.Left || right != node.Right { + return updateNode(f.NewQualifiedName(left, right), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *QualifiedName) ForEachChild(v Visitor) bool { + return visit(v, node.Left) || visit(v, node.Right) +} + +func (node *QualifiedName) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateQualifiedName(node, v.visitNode(node.Left), v.visitNode(node.Right)) +} + +func (node *QualifiedName) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewQualifiedName(node.Left, node.Right), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *QualifiedName) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.Left) | + propagateSubtreeFacts(node.Right) +} + +func IsQualifiedName(node *Node) bool { + return node.Kind == KindQualifiedName +} + +// ────────────────────────────────────────────────────────────────────── +// ComputedPropertyName +// ────────────────────────────────────────────────────────────────────── + +type ComputedPropertyName struct { + NodeBase + CompositeBase + Expression *Expression +} + +func (f *NodeFactory) NewComputedPropertyName(expression *Expression) *Node { + data := &ComputedPropertyName{} + data.Expression = expression + return f.newNode(KindComputedPropertyName, data) +} + +func (f *NodeFactory) UpdateComputedPropertyName(node *ComputedPropertyName, expression *Expression) *Node { + if expression != node.Expression { + return updateNode(f.NewComputedPropertyName(expression), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *ComputedPropertyName) ForEachChild(v Visitor) bool { + return visit(v, node.Expression) +} + +func (node *ComputedPropertyName) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateComputedPropertyName(node, v.visitNode(node.Expression)) +} + +func (node *ComputedPropertyName) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewComputedPropertyName(node.Expression), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *ComputedPropertyName) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.Expression) +} + +func IsComputedPropertyName(node *Node) bool { + return node.Kind == KindComputedPropertyName +} + +// ────────────────────────────────────────────────────────────────────── +// Decorator +// ────────────────────────────────────────────────────────────────────── + +type Decorator struct { + NodeBase + CompositeBase + Expression *LeftHandSideExpression +} + +func (f *NodeFactory) NewDecorator(expression *LeftHandSideExpression) *Node { + data := &Decorator{} + data.Expression = expression + return f.newNode(KindDecorator, data) +} + +func (f *NodeFactory) UpdateDecorator(node *Decorator, expression *LeftHandSideExpression) *Node { + if expression != node.Expression { + return updateNode(f.NewDecorator(expression), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *Decorator) ForEachChild(v Visitor) bool { + return visit(v, node.Expression) +} + +func (node *Decorator) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateDecorator(node, v.visitNode(node.Expression)) +} + +func (node *Decorator) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewDecorator(node.Expression), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsDecorator(node *Node) bool { + return node.Kind == KindDecorator +} + +// ────────────────────────────────────────────────────────────────────── +// EmptyStatement +// ────────────────────────────────────────────────────────────────────── + +type EmptyStatement struct { + StatementBase +} + +func (f *NodeFactory) NewEmptyStatement() *Node { + data := &EmptyStatement{} + return f.newNode(KindEmptyStatement, data) +} + +func (node *EmptyStatement) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewEmptyStatement(), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsEmptyStatement(node *Node) bool { + return node.Kind == KindEmptyStatement +} + +// ────────────────────────────────────────────────────────────────────── +// IfStatement +// ────────────────────────────────────────────────────────────────────── + +type IfStatement struct { + StatementBase + CompositeBase + Expression *Expression + ThenStatement *Statement + ElseStatement *Statement // Optional +} + +func (f *NodeFactory) NewIfStatement(expression *Expression, thenStatement *Statement, elseStatement *Statement) *Node { + data := f.ifStatementArena.New() + data.Expression = expression + data.ThenStatement = thenStatement + data.ElseStatement = elseStatement + return f.newNode(KindIfStatement, data) +} + +func (f *NodeFactory) UpdateIfStatement(node *IfStatement, expression *Expression, thenStatement *Statement, elseStatement *Statement) *Node { + if expression != node.Expression || thenStatement != node.ThenStatement || elseStatement != node.ElseStatement { + return updateNode(f.NewIfStatement(expression, thenStatement, elseStatement), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *IfStatement) ForEachChild(v Visitor) bool { + return visit(v, node.Expression) || visit(v, node.ThenStatement) || visit(v, node.ElseStatement) +} + +func (node *IfStatement) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateIfStatement(node, v.visitNode(node.Expression), v.visitEmbeddedStatement(node.ThenStatement), v.visitEmbeddedStatement(node.ElseStatement)) +} + +func (node *IfStatement) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewIfStatement(node.Expression, node.ThenStatement, node.ElseStatement), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *IfStatement) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.Expression) | + propagateSubtreeFacts(node.ThenStatement) | + propagateSubtreeFacts(node.ElseStatement) +} + +func IsIfStatement(node *Node) bool { + return node.Kind == KindIfStatement +} + +// ────────────────────────────────────────────────────────────────────── +// DoStatement +// ────────────────────────────────────────────────────────────────────── + +type DoStatement struct { + IterationStatementBase + CompositeBase + Expression *Expression +} + +func (f *NodeFactory) NewDoStatement(statement *Statement, expression *Expression) *Node { + data := &DoStatement{} + data.Statement = statement + data.Expression = expression + return f.newNode(KindDoStatement, data) +} + +func (f *NodeFactory) UpdateDoStatement(node *DoStatement, statement *Statement, expression *Expression) *Node { + if statement != node.Statement || expression != node.Expression { + return updateNode(f.NewDoStatement(statement, expression), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *DoStatement) ForEachChild(v Visitor) bool { + return visit(v, node.Statement) || visit(v, node.Expression) +} + +func (node *DoStatement) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateDoStatement(node, v.visitIterationBody(node.Statement), v.visitNode(node.Expression)) +} + +func (node *DoStatement) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewDoStatement(node.Statement, node.Expression), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *DoStatement) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.Statement) | + propagateSubtreeFacts(node.Expression) +} + +func IsDoStatement(node *Node) bool { + return node.Kind == KindDoStatement +} + +// ────────────────────────────────────────────────────────────────────── +// WhileStatement +// ────────────────────────────────────────────────────────────────────── + +type WhileStatement struct { + IterationStatementBase + CompositeBase + Expression *Expression +} + +func (f *NodeFactory) NewWhileStatement(expression *Expression, statement *Statement) *Node { + data := &WhileStatement{} + data.Expression = expression + data.Statement = statement + return f.newNode(KindWhileStatement, data) +} + +func (f *NodeFactory) UpdateWhileStatement(node *WhileStatement, expression *Expression, statement *Statement) *Node { + if expression != node.Expression || statement != node.Statement { + return updateNode(f.NewWhileStatement(expression, statement), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *WhileStatement) ForEachChild(v Visitor) bool { + return visit(v, node.Expression) || visit(v, node.Statement) +} + +func (node *WhileStatement) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateWhileStatement(node, v.visitNode(node.Expression), v.visitIterationBody(node.Statement)) +} + +func (node *WhileStatement) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewWhileStatement(node.Expression, node.Statement), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *WhileStatement) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.Expression) | + propagateSubtreeFacts(node.Statement) +} + +func IsWhileStatement(node *Node) bool { + return node.Kind == KindWhileStatement +} + +// ────────────────────────────────────────────────────────────────────── +// ForStatement +// ────────────────────────────────────────────────────────────────────── + +type ForStatement struct { + IterationStatementBase + LocalsContainerBase + CompositeBase + Initializer *ForInitializer // Optional + Condition *Expression // Optional + Incrementor *Expression // Optional +} + +func (f *NodeFactory) NewForStatement(initializer *ForInitializer, condition *Expression, incrementor *Expression, statement *Statement) *Node { + data := &ForStatement{} + data.Initializer = initializer + data.Condition = condition + data.Incrementor = incrementor + data.Statement = statement + return f.newNode(KindForStatement, data) +} + +func (f *NodeFactory) UpdateForStatement(node *ForStatement, initializer *ForInitializer, condition *Expression, incrementor *Expression, statement *Statement) *Node { + if initializer != node.Initializer || condition != node.Condition || incrementor != node.Incrementor || statement != node.Statement { + return updateNode(f.NewForStatement(initializer, condition, incrementor, statement), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *ForStatement) ForEachChild(v Visitor) bool { + return visit(v, node.Initializer) || + visit(v, node.Condition) || + visit(v, node.Incrementor) || + visit(v, node.Statement) +} + +func (node *ForStatement) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateForStatement(node, v.visitNode(node.Initializer), v.visitNode(node.Condition), v.visitNode(node.Incrementor), v.visitIterationBody(node.Statement)) +} + +func (node *ForStatement) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewForStatement(node.Initializer, node.Condition, node.Incrementor, node.Statement), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *ForStatement) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.Initializer) | + propagateSubtreeFacts(node.Condition) | + propagateSubtreeFacts(node.Incrementor) | + propagateSubtreeFacts(node.Statement) +} + +func IsForStatement(node *Node) bool { + return node.Kind == KindForStatement +} + +// ────────────────────────────────────────────────────────────────────── +// ForInOrOfStatement +// ────────────────────────────────────────────────────────────────────── + +type ForInOrOfStatement struct { + StatementBase + LocalsContainerBase + CompositeBase + AwaitModifier *AwaitKeyword // Optional + Initializer *ForInitializer + Expression *Expression + Statement *Statement +} + +func (f *NodeFactory) NewForInOrOfStatement(kind Kind, awaitModifier *AwaitKeyword, initializer *ForInitializer, expression *Expression, statement *Statement) *Node { + data := &ForInOrOfStatement{} + data.AwaitModifier = awaitModifier + data.Initializer = initializer + data.Expression = expression + data.Statement = statement + return f.newNode(kind, data) +} + +func (f *NodeFactory) UpdateForInOrOfStatement(node *ForInOrOfStatement, awaitModifier *AwaitKeyword, initializer *ForInitializer, expression *Expression, statement *Statement) *Node { + if awaitModifier != node.AwaitModifier || initializer != node.Initializer || expression != node.Expression || statement != node.Statement { + return updateNode(f.NewForInOrOfStatement(node.Kind, awaitModifier, initializer, expression, statement), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *ForInOrOfStatement) ForEachChild(v Visitor) bool { + return visit(v, node.AwaitModifier) || + visit(v, node.Initializer) || + visit(v, node.Expression) || + visit(v, node.Statement) +} + +func (node *ForInOrOfStatement) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateForInOrOfStatement(node, v.visitNode(node.AwaitModifier), v.visitNode(node.Initializer), v.visitNode(node.Expression), v.visitIterationBody(node.Statement)) +} + +func (node *ForInOrOfStatement) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewForInOrOfStatement(node.Kind, node.AwaitModifier, node.Initializer, node.Expression, node.Statement), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsForInStatement(node *Node) bool { + return node.Kind == KindForInStatement +} + +func IsForOfStatement(node *Node) bool { + return node.Kind == KindForOfStatement +} + +// ────────────────────────────────────────────────────────────────────── +// BreakStatement +// ────────────────────────────────────────────────────────────────────── + +type BreakStatement struct { + StatementBase + Label *IdentifierNode // Optional +} + +func (f *NodeFactory) NewBreakStatement(label *IdentifierNode) *Node { + data := &BreakStatement{} + data.Label = label + return f.newNode(KindBreakStatement, data) +} + +func (f *NodeFactory) UpdateBreakStatement(node *BreakStatement, label *IdentifierNode) *Node { + if label != node.Label { + return updateNode(f.NewBreakStatement(label), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *BreakStatement) ForEachChild(v Visitor) bool { + return visit(v, node.Label) +} + +func (node *BreakStatement) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateBreakStatement(node, v.visitNode(node.Label)) +} + +func (node *BreakStatement) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewBreakStatement(node.Label), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsBreakStatement(node *Node) bool { + return node.Kind == KindBreakStatement +} + +// ────────────────────────────────────────────────────────────────────── +// ContinueStatement +// ────────────────────────────────────────────────────────────────────── + +type ContinueStatement struct { + StatementBase + Label *IdentifierNode // Optional +} + +func (f *NodeFactory) NewContinueStatement(label *IdentifierNode) *Node { + data := &ContinueStatement{} + data.Label = label + return f.newNode(KindContinueStatement, data) +} + +func (f *NodeFactory) UpdateContinueStatement(node *ContinueStatement, label *IdentifierNode) *Node { + if label != node.Label { + return updateNode(f.NewContinueStatement(label), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *ContinueStatement) ForEachChild(v Visitor) bool { + return visit(v, node.Label) +} + +func (node *ContinueStatement) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateContinueStatement(node, v.visitNode(node.Label)) +} + +func (node *ContinueStatement) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewContinueStatement(node.Label), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsContinueStatement(node *Node) bool { + return node.Kind == KindContinueStatement +} + +// ────────────────────────────────────────────────────────────────────── +// ReturnStatement +// ────────────────────────────────────────────────────────────────────── + +type ReturnStatement struct { + StatementBase + CompositeBase + Expression *Expression // Optional +} + +func (f *NodeFactory) NewReturnStatement(expression *Expression) *Node { + data := f.returnStatementArena.New() + data.Expression = expression + return f.newNode(KindReturnStatement, data) +} + +func (f *NodeFactory) UpdateReturnStatement(node *ReturnStatement, expression *Expression) *Node { + if expression != node.Expression { + return updateNode(f.NewReturnStatement(expression), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *ReturnStatement) ForEachChild(v Visitor) bool { + return visit(v, node.Expression) +} + +func (node *ReturnStatement) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateReturnStatement(node, v.visitNode(node.Expression)) +} + +func (node *ReturnStatement) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewReturnStatement(node.Expression), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsReturnStatement(node *Node) bool { + return node.Kind == KindReturnStatement +} + +// ────────────────────────────────────────────────────────────────────── +// WithStatement +// ────────────────────────────────────────────────────────────────────── + +type WithStatement struct { + StatementBase + CompositeBase + Expression *Expression + Statement *Statement +} + +func (f *NodeFactory) NewWithStatement(expression *Expression, statement *Statement) *Node { + data := &WithStatement{} + data.Expression = expression + data.Statement = statement + return f.newNode(KindWithStatement, data) +} + +func (f *NodeFactory) UpdateWithStatement(node *WithStatement, expression *Expression, statement *Statement) *Node { + if expression != node.Expression || statement != node.Statement { + return updateNode(f.NewWithStatement(expression, statement), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *WithStatement) ForEachChild(v Visitor) bool { + return visit(v, node.Expression) || visit(v, node.Statement) +} + +func (node *WithStatement) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateWithStatement(node, v.visitNode(node.Expression), v.visitEmbeddedStatement(node.Statement)) +} + +func (node *WithStatement) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewWithStatement(node.Expression, node.Statement), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *WithStatement) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.Expression) | + propagateSubtreeFacts(node.Statement) +} + +func IsWithStatement(node *Node) bool { + return node.Kind == KindWithStatement +} + +// ────────────────────────────────────────────────────────────────────── +// SwitchStatement +// ────────────────────────────────────────────────────────────────────── + +type SwitchStatement struct { + StatementBase + CompositeBase + Expression *Expression + CaseBlock *CaseBlockNode +} + +func (f *NodeFactory) NewSwitchStatement(expression *Expression, caseBlock *CaseBlockNode) *Node { + data := &SwitchStatement{} + data.Expression = expression + data.CaseBlock = caseBlock + return f.newNode(KindSwitchStatement, data) +} + +func (f *NodeFactory) UpdateSwitchStatement(node *SwitchStatement, expression *Expression, caseBlock *CaseBlockNode) *Node { + if expression != node.Expression || caseBlock != node.CaseBlock { + return updateNode(f.NewSwitchStatement(expression, caseBlock), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *SwitchStatement) ForEachChild(v Visitor) bool { + return visit(v, node.Expression) || visit(v, node.CaseBlock) +} + +func (node *SwitchStatement) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateSwitchStatement(node, v.visitNode(node.Expression), v.visitNode(node.CaseBlock)) +} + +func (node *SwitchStatement) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewSwitchStatement(node.Expression, node.CaseBlock), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *SwitchStatement) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.Expression) | + propagateSubtreeFacts(node.CaseBlock) +} + +func IsSwitchStatement(node *Node) bool { + return node.Kind == KindSwitchStatement +} + +// ────────────────────────────────────────────────────────────────────── +// CaseBlock +// ────────────────────────────────────────────────────────────────────── + +type CaseBlock struct { + NodeBase + LocalsContainerBase + CompositeBase + Clauses *CaseClausesList +} + +func (f *NodeFactory) NewCaseBlock(clauses *CaseClausesList) *Node { + data := &CaseBlock{} + data.Clauses = clauses + return f.newNode(KindCaseBlock, data) +} + +func (f *NodeFactory) UpdateCaseBlock(node *CaseBlock, clauses *CaseClausesList) *Node { + if clauses != node.Clauses { + return updateNode(f.NewCaseBlock(clauses), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *CaseBlock) ForEachChild(v Visitor) bool { + return visitNodeList(v, node.Clauses) +} + +func (node *CaseBlock) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateCaseBlock(node, v.visitNodes(node.Clauses)) +} + +func (node *CaseBlock) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewCaseBlock(node.Clauses), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *CaseBlock) computeSubtreeFacts() SubtreeFacts { + return propagateNodeListSubtreeFacts(node.Clauses, propagateSubtreeFacts) +} + +func IsCaseBlock(node *Node) bool { + return node.Kind == KindCaseBlock +} + +// ────────────────────────────────────────────────────────────────────── +// CaseOrDefaultClause +// ────────────────────────────────────────────────────────────────────── + +type CaseOrDefaultClause struct { + NodeBase + CompositeBase + Expression *Expression + Statements *StatementList + FallthroughFlowNode *FlowNode +} + +func (f *NodeFactory) NewCaseOrDefaultClause(kind Kind, expression *Expression, statements *StatementList) *Node { + data := &CaseOrDefaultClause{} + data.Expression = expression + data.Statements = statements + return f.newNode(kind, data) +} + +func (f *NodeFactory) UpdateCaseOrDefaultClause(node *CaseOrDefaultClause, expression *Expression, statements *StatementList) *Node { + if expression != node.Expression || statements != node.Statements { + return updateNode(f.NewCaseOrDefaultClause(node.Kind, expression, statements), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *CaseOrDefaultClause) ForEachChild(v Visitor) bool { + return visit(v, node.Expression) || visitNodeList(v, node.Statements) +} + +func (node *CaseOrDefaultClause) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateCaseOrDefaultClause(node, v.visitNode(node.Expression), v.visitNodes(node.Statements)) +} + +func (node *CaseOrDefaultClause) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewCaseOrDefaultClause(node.Kind, node.Expression, node.Statements), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *CaseOrDefaultClause) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.Expression) | + propagateNodeListSubtreeFacts(node.Statements, propagateSubtreeFacts) +} + +func IsCaseClause(node *Node) bool { + return node.Kind == KindCaseClause +} + +func IsDefaultClause(node *Node) bool { + return node.Kind == KindDefaultClause +} + +// ────────────────────────────────────────────────────────────────────── +// ThrowStatement +// ────────────────────────────────────────────────────────────────────── + +type ThrowStatement struct { + StatementBase + CompositeBase + Expression *Expression +} + +func (f *NodeFactory) NewThrowStatement(expression *Expression) *Node { + data := &ThrowStatement{} + data.Expression = expression + return f.newNode(KindThrowStatement, data) +} + +func (f *NodeFactory) UpdateThrowStatement(node *ThrowStatement, expression *Expression) *Node { + if expression != node.Expression { + return updateNode(f.NewThrowStatement(expression), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *ThrowStatement) ForEachChild(v Visitor) bool { + return visit(v, node.Expression) +} + +func (node *ThrowStatement) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateThrowStatement(node, v.visitNode(node.Expression)) +} + +func (node *ThrowStatement) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewThrowStatement(node.Expression), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *ThrowStatement) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.Expression) +} + +func IsThrowStatement(node *Node) bool { + return node.Kind == KindThrowStatement +} + +// ────────────────────────────────────────────────────────────────────── +// TryStatement +// ────────────────────────────────────────────────────────────────────── + +type TryStatement struct { + StatementBase + CompositeBase + TryBlock *BlockNode + CatchClause *CatchClauseNode // Optional + FinallyBlock *BlockNode // Optional +} + +func (f *NodeFactory) NewTryStatement(tryBlock *BlockNode, catchClause *CatchClauseNode, finallyBlock *BlockNode) *Node { + data := &TryStatement{} + data.TryBlock = tryBlock + data.CatchClause = catchClause + data.FinallyBlock = finallyBlock + return f.newNode(KindTryStatement, data) +} + +func (f *NodeFactory) UpdateTryStatement(node *TryStatement, tryBlock *BlockNode, catchClause *CatchClauseNode, finallyBlock *BlockNode) *Node { + if tryBlock != node.TryBlock || catchClause != node.CatchClause || finallyBlock != node.FinallyBlock { + return updateNode(f.NewTryStatement(tryBlock, catchClause, finallyBlock), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *TryStatement) ForEachChild(v Visitor) bool { + return visit(v, node.TryBlock) || visit(v, node.CatchClause) || visit(v, node.FinallyBlock) +} + +func (node *TryStatement) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateTryStatement(node, v.visitNode(node.TryBlock), v.visitNode(node.CatchClause), v.visitNode(node.FinallyBlock)) +} + +func (node *TryStatement) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewTryStatement(node.TryBlock, node.CatchClause, node.FinallyBlock), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *TryStatement) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.TryBlock) | + propagateSubtreeFacts(node.CatchClause) | + propagateSubtreeFacts(node.FinallyBlock) +} + +func IsTryStatement(node *Node) bool { + return node.Kind == KindTryStatement +} + +// ────────────────────────────────────────────────────────────────────── +// CatchClause +// ────────────────────────────────────────────────────────────────────── + +type CatchClause struct { + NodeBase + LocalsContainerBase + CompositeBase + VariableDeclaration *VariableDeclarationNode // Optional + Block *BlockNode +} + +func (f *NodeFactory) NewCatchClause(variableDeclaration *VariableDeclarationNode, block *BlockNode) *Node { + data := &CatchClause{} + data.VariableDeclaration = variableDeclaration + data.Block = block + return f.newNode(KindCatchClause, data) +} + +func (f *NodeFactory) UpdateCatchClause(node *CatchClause, variableDeclaration *VariableDeclarationNode, block *BlockNode) *Node { + if variableDeclaration != node.VariableDeclaration || block != node.Block { + return updateNode(f.NewCatchClause(variableDeclaration, block), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *CatchClause) ForEachChild(v Visitor) bool { + return visit(v, node.VariableDeclaration) || visit(v, node.Block) +} + +func (node *CatchClause) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateCatchClause(node, v.visitNode(node.VariableDeclaration), v.visitNode(node.Block)) +} + +func (node *CatchClause) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewCatchClause(node.VariableDeclaration, node.Block), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsCatchClause(node *Node) bool { + return node.Kind == KindCatchClause +} + +// ────────────────────────────────────────────────────────────────────── +// DebuggerStatement +// ────────────────────────────────────────────────────────────────────── + +type DebuggerStatement struct { + StatementBase +} + +func (f *NodeFactory) NewDebuggerStatement() *Node { + data := &DebuggerStatement{} + return f.newNode(KindDebuggerStatement, data) +} + +func (node *DebuggerStatement) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewDebuggerStatement(), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsDebuggerStatement(node *Node) bool { + return node.Kind == KindDebuggerStatement +} + +// ────────────────────────────────────────────────────────────────────── +// LabeledStatement +// ────────────────────────────────────────────────────────────────────── + +type LabeledStatement struct { + StatementBase + Label *IdentifierNode + Statement *Statement +} + +func (f *NodeFactory) NewLabeledStatement(label *IdentifierNode, statement *Statement) *Node { + data := &LabeledStatement{} + data.Label = label + data.Statement = statement + return f.newNode(KindLabeledStatement, data) +} + +func (f *NodeFactory) UpdateLabeledStatement(node *LabeledStatement, label *IdentifierNode, statement *Statement) *Node { + if label != node.Label || statement != node.Statement { + return updateNode(f.NewLabeledStatement(label, statement), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *LabeledStatement) ForEachChild(v Visitor) bool { + return visit(v, node.Label) || visit(v, node.Statement) +} + +func (node *LabeledStatement) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateLabeledStatement(node, v.visitNode(node.Label), v.visitEmbeddedStatement(node.Statement)) +} + +func (node *LabeledStatement) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewLabeledStatement(node.Label, node.Statement), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *LabeledStatement) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.Label) | + propagateSubtreeFacts(node.Statement) +} + +func IsLabeledStatement(node *Node) bool { + return node.Kind == KindLabeledStatement +} + +// ────────────────────────────────────────────────────────────────────── +// ExpressionStatement +// ────────────────────────────────────────────────────────────────────── + +type ExpressionStatement struct { + StatementBase + Expression *Expression +} + +func (f *NodeFactory) NewExpressionStatement(expression *Expression) *Node { + data := f.expressionStatementArena.New() + data.Expression = expression + return f.newNode(KindExpressionStatement, data) +} + +func (f *NodeFactory) UpdateExpressionStatement(node *ExpressionStatement, expression *Expression) *Node { + if expression != node.Expression { + return updateNode(f.NewExpressionStatement(expression), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *ExpressionStatement) ForEachChild(v Visitor) bool { + return visit(v, node.Expression) +} + +func (node *ExpressionStatement) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateExpressionStatement(node, v.visitNode(node.Expression)) +} + +func (node *ExpressionStatement) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewExpressionStatement(node.Expression), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *ExpressionStatement) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.Expression) +} + +func IsExpressionStatement(node *Node) bool { + return node.Kind == KindExpressionStatement +} + +// ────────────────────────────────────────────────────────────────────── +// Block +// ────────────────────────────────────────────────────────────────────── + +type Block struct { + StatementBase + LocalsContainerBase + CompositeBase + Statements *StatementList + MultiLine bool +} + +func (f *NodeFactory) NewBlock(statements *StatementList, multiLine bool) *Node { + data := f.blockArena.New() + data.Statements = statements + data.MultiLine = multiLine + return f.newNode(KindBlock, data) +} + +func (f *NodeFactory) UpdateBlock(node *Block, statements *StatementList, multiLine bool) *Node { + if statements != node.Statements || multiLine != node.MultiLine { + return updateNode(f.NewBlock(statements, multiLine), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *Block) ForEachChild(v Visitor) bool { + return visitNodeList(v, node.Statements) +} + +func (node *Block) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateBlock(node, v.visitNodes(node.Statements), node.MultiLine) +} + +func (node *Block) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewBlock(node.Statements, node.MultiLine), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *Block) computeSubtreeFacts() SubtreeFacts { + return propagateNodeListSubtreeFacts(node.Statements, propagateSubtreeFacts) +} + +func IsBlock(node *Node) bool { + return node.Kind == KindBlock +} + +// ────────────────────────────────────────────────────────────────────── +// VariableStatement +// ────────────────────────────────────────────────────────────────────── + +type VariableStatement struct { + StatementBase + ModifiersBase + CompositeBase + DeclarationList *VariableDeclarationListNode +} + +func (f *NodeFactory) NewVariableStatement(modifiers *ModifierList, declarationList *VariableDeclarationListNode) *Node { + data := f.variableStatementArena.New() + data.modifiers = modifiers + data.DeclarationList = declarationList + return f.newNode(KindVariableStatement, data) +} + +func (f *NodeFactory) UpdateVariableStatement(node *VariableStatement, modifiers *ModifierList, declarationList *VariableDeclarationListNode) *Node { + if modifiers != node.modifiers || declarationList != node.DeclarationList { + return updateNode(f.NewVariableStatement(modifiers, declarationList), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *VariableStatement) ForEachChild(v Visitor) bool { + return visitModifiers(v, node.modifiers) || visit(v, node.DeclarationList) +} + +func (node *VariableStatement) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateVariableStatement(node, v.visitModifiers(node.modifiers), v.visitNode(node.DeclarationList)) +} + +func (node *VariableStatement) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewVariableStatement(node.Modifiers(), node.DeclarationList), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsVariableStatement(node *Node) bool { + return node.Kind == KindVariableStatement +} + +// ────────────────────────────────────────────────────────────────────── +// VariableDeclaration +// ────────────────────────────────────────────────────────────────────── + +type VariableDeclaration struct { + NodeBase + DeclarationBase + ExportableBase + CompositeBase + name *BindingName + ExclamationToken *ExclamationToken // Optional + Type *TypeNode // Optional + Initializer *Expression // Optional +} + +func (f *NodeFactory) NewVariableDeclaration(name *BindingName, exclamationToken *ExclamationToken, typeNode *TypeNode, initializer *Expression) *Node { + data := f.variableDeclarationArena.New() + data.name = name + data.ExclamationToken = exclamationToken + data.Type = typeNode + data.Initializer = initializer + return f.newNode(KindVariableDeclaration, data) +} + +func (f *NodeFactory) UpdateVariableDeclaration(node *VariableDeclaration, name *BindingName, exclamationToken *ExclamationToken, typeNode *TypeNode, initializer *Expression) *Node { + if name != node.name || exclamationToken != node.ExclamationToken || typeNode != node.Type || initializer != node.Initializer { + return updateNode(f.NewVariableDeclaration(name, exclamationToken, typeNode, initializer), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *VariableDeclaration) ForEachChild(v Visitor) bool { + return visit(v, node.name) || + visit(v, node.ExclamationToken) || + visit(v, node.Type) || + visit(v, node.Initializer) +} + +func (node *VariableDeclaration) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateVariableDeclaration(node, v.visitNode(node.name), v.visitNode(node.ExclamationToken), v.visitNode(node.Type), v.visitNode(node.Initializer)) +} + +func (node *VariableDeclaration) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewVariableDeclaration(node.name, node.ExclamationToken, node.Type, node.Initializer), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *VariableDeclaration) Name() *DeclarationName { + return node.name +} + +func IsVariableDeclaration(node *Node) bool { + return node.Kind == KindVariableDeclaration +} + +// ────────────────────────────────────────────────────────────────────── +// VariableDeclarationList +// ────────────────────────────────────────────────────────────────────── + +type VariableDeclarationList struct { + NodeBase + CompositeBase + Declarations *VariableDeclarationNodeList +} + +func (f *NodeFactory) NewVariableDeclarationList(declarations *VariableDeclarationNodeList, flags NodeFlags) *Node { + data := f.variableDeclarationListArena.New() + data.Declarations = declarations + node := f.newNode(KindVariableDeclarationList, data) + node.Flags = flags + return node +} + +func (f *NodeFactory) UpdateVariableDeclarationList(node *VariableDeclarationList, declarations *VariableDeclarationNodeList, flags NodeFlags) *Node { + if declarations != node.Declarations || flags != node.Flags { + return updateNode(f.NewVariableDeclarationList(declarations, flags), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *VariableDeclarationList) ForEachChild(v Visitor) bool { + return visitNodeList(v, node.Declarations) +} + +func (node *VariableDeclarationList) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateVariableDeclarationList(node, v.visitNodes(node.Declarations), node.Flags) +} + +func (node *VariableDeclarationList) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewVariableDeclarationList(node.Declarations, node.Flags), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsVariableDeclarationList(node *Node) bool { + return node.Kind == KindVariableDeclarationList +} + +// ────────────────────────────────────────────────────────────────────── +// BindingPattern +// ────────────────────────────────────────────────────────────────────── + +type BindingPattern struct { + NodeBase + CompositeBase + Elements *BindingElementList +} + +func (f *NodeFactory) NewBindingPattern(kind Kind, elements *BindingElementList) *Node { + data := &BindingPattern{} + data.Elements = elements + return f.newNode(kind, data) +} + +func (f *NodeFactory) UpdateBindingPattern(node *BindingPattern, elements *BindingElementList) *Node { + if elements != node.Elements { + return updateNode(f.NewBindingPattern(node.Kind, elements), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *BindingPattern) ForEachChild(v Visitor) bool { + return visitNodeList(v, node.Elements) +} + +func (node *BindingPattern) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateBindingPattern(node, v.visitNodes(node.Elements)) +} + +func (node *BindingPattern) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewBindingPattern(node.Kind, node.Elements), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsObjectBindingPattern(node *Node) bool { + return node.Kind == KindObjectBindingPattern +} + +func IsArrayBindingPattern(node *Node) bool { + return node.Kind == KindArrayBindingPattern +} + +// ────────────────────────────────────────────────────────────────────── +// ParameterDeclaration +// ────────────────────────────────────────────────────────────────────── + +type ParameterDeclaration struct { + NodeBase + DeclarationBase + ModifiersBase + CompositeBase + DotDotDotToken *DotDotDotToken // Optional + name *BindingName + QuestionToken *QuestionToken // Optional + Type *TypeNode // Optional + Initializer *Expression // Optional +} + +func (f *NodeFactory) NewParameterDeclaration(modifiers *ModifierList, dotDotDotToken *DotDotDotToken, name *BindingName, questionToken *QuestionToken, typeNode *TypeNode, initializer *Expression) *Node { + data := f.parameterDeclarationArena.New() + data.modifiers = modifiers + data.DotDotDotToken = dotDotDotToken + data.name = name + data.QuestionToken = questionToken + data.Type = typeNode + data.Initializer = initializer + return f.newNode(KindParameter, data) +} + +func (f *NodeFactory) UpdateParameterDeclaration(node *ParameterDeclaration, modifiers *ModifierList, dotDotDotToken *DotDotDotToken, name *BindingName, questionToken *QuestionToken, typeNode *TypeNode, initializer *Expression) *Node { + if modifiers != node.modifiers || dotDotDotToken != node.DotDotDotToken || name != node.name || questionToken != node.QuestionToken || typeNode != node.Type || initializer != node.Initializer { + return updateNode(f.NewParameterDeclaration(modifiers, dotDotDotToken, name, questionToken, typeNode, initializer), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *ParameterDeclaration) ForEachChild(v Visitor) bool { + return visitModifiers(v, node.modifiers) || + visit(v, node.DotDotDotToken) || + visit(v, node.name) || + visit(v, node.QuestionToken) || + visit(v, node.Type) || + visit(v, node.Initializer) +} + +func (node *ParameterDeclaration) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateParameterDeclaration(node, v.visitModifiers(node.modifiers), v.visitNode(node.DotDotDotToken), v.visitNode(node.name), v.visitNode(node.QuestionToken), v.visitNode(node.Type), v.visitNode(node.Initializer)) +} + +func (node *ParameterDeclaration) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewParameterDeclaration(node.Modifiers(), node.DotDotDotToken, node.name, node.QuestionToken, node.Type, node.Initializer), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *ParameterDeclaration) Name() *DeclarationName { + return node.name +} + +func IsParameterDeclaration(node *Node) bool { + return node.Kind == KindParameter +} + +// ────────────────────────────────────────────────────────────────────── +// BindingElement +// ────────────────────────────────────────────────────────────────────── + +type BindingElement struct { + NodeBase + DeclarationBase + ExportableBase + FlowNodeBase + CompositeBase + DotDotDotToken *DotDotDotToken // Optional + PropertyName *PropertyName // Optional + name *BindingName // Optional + Initializer *Expression // Optional +} + +func (f *NodeFactory) NewBindingElement(dotDotDotToken *DotDotDotToken, propertyName *PropertyName, name *BindingName, initializer *Expression) *Node { + data := &BindingElement{} + data.DotDotDotToken = dotDotDotToken + data.PropertyName = propertyName + data.name = name + data.Initializer = initializer + return f.newNode(KindBindingElement, data) +} + +func (f *NodeFactory) UpdateBindingElement(node *BindingElement, dotDotDotToken *DotDotDotToken, propertyName *PropertyName, name *BindingName, initializer *Expression) *Node { + if dotDotDotToken != node.DotDotDotToken || propertyName != node.PropertyName || name != node.name || initializer != node.Initializer { + return updateNode(f.NewBindingElement(dotDotDotToken, propertyName, name, initializer), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *BindingElement) ForEachChild(v Visitor) bool { + return visit(v, node.DotDotDotToken) || + visit(v, node.PropertyName) || + visit(v, node.name) || + visit(v, node.Initializer) +} + +func (node *BindingElement) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateBindingElement(node, v.visitNode(node.DotDotDotToken), v.visitNode(node.PropertyName), v.visitNode(node.name), v.visitNode(node.Initializer)) +} + +func (node *BindingElement) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewBindingElement(node.DotDotDotToken, node.PropertyName, node.name, node.Initializer), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *BindingElement) Name() *DeclarationName { + return node.name +} + +func IsBindingElement(node *Node) bool { + return node.Kind == KindBindingElement +} + +// ────────────────────────────────────────────────────────────────────── +// MissingDeclaration +// ────────────────────────────────────────────────────────────────────── + +type MissingDeclaration struct { + StatementBase + DeclarationBase + ModifiersBase +} + +func (f *NodeFactory) NewMissingDeclaration(modifiers *ModifierList) *Node { + data := &MissingDeclaration{} + data.modifiers = modifiers + return f.newNode(KindMissingDeclaration, data) +} + +func (f *NodeFactory) UpdateMissingDeclaration(node *MissingDeclaration, modifiers *ModifierList) *Node { + if modifiers != node.modifiers { + return updateNode(f.NewMissingDeclaration(modifiers), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *MissingDeclaration) ForEachChild(v Visitor) bool { + return visitModifiers(v, node.modifiers) +} + +func (node *MissingDeclaration) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateMissingDeclaration(node, v.visitModifiers(node.modifiers)) +} + +func (node *MissingDeclaration) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewMissingDeclaration(node.Modifiers()), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsMissingDeclaration(node *Node) bool { + return node.Kind == KindMissingDeclaration +} + +// ────────────────────────────────────────────────────────────────────── +// FunctionDeclaration +// ────────────────────────────────────────────────────────────────────── + +type FunctionDeclaration struct { + DeclarationBase + StatementBase + ExportableBase + ModifiersBase + FunctionLikeWithBodyBase + CompositeBase + name *IdentifierNode // Optional + ReturnFlowNode *FlowNode +} + +func (f *NodeFactory) NewFunctionDeclaration(modifiers *ModifierList, asteriskToken *AsteriskToken, name *IdentifierNode, typeParameters *TypeParameterList, parameters *ParameterList, typeNode *TypeNode, fullSignature *TypeNode, body *FunctionBody) *Node { + data := f.functionDeclarationArena.New() + data.modifiers = modifiers + data.AsteriskToken = asteriskToken + data.name = name + data.TypeParameters = typeParameters + data.Parameters = parameters + data.Type = typeNode + data.FullSignature = fullSignature + data.Body = body + return f.newNode(KindFunctionDeclaration, data) +} + +func (f *NodeFactory) UpdateFunctionDeclaration(node *FunctionDeclaration, modifiers *ModifierList, asteriskToken *AsteriskToken, name *IdentifierNode, typeParameters *TypeParameterList, parameters *ParameterList, typeNode *TypeNode, fullSignature *TypeNode, body *FunctionBody) *Node { + if modifiers != node.modifiers || asteriskToken != node.AsteriskToken || name != node.name || typeParameters != node.TypeParameters || parameters != node.Parameters || typeNode != node.Type || fullSignature != node.FullSignature || body != node.Body { + return updateNode(f.NewFunctionDeclaration(modifiers, asteriskToken, name, typeParameters, parameters, typeNode, fullSignature, body), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *FunctionDeclaration) ForEachChild(v Visitor) bool { + return visitModifiers(v, node.modifiers) || + visit(v, node.AsteriskToken) || + visit(v, node.name) || + visitNodeList(v, node.TypeParameters) || + visitNodeList(v, node.Parameters) || + visit(v, node.Type) || + visit(v, node.FullSignature) || + visit(v, node.Body) +} + +func (node *FunctionDeclaration) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateFunctionDeclaration(node, v.visitModifiers(node.modifiers), v.visitNode(node.AsteriskToken), v.visitNode(node.name), v.visitNodes(node.TypeParameters), v.visitParameters(node.Parameters), v.visitNode(node.Type), v.visitNode(node.FullSignature), v.visitFunctionBody(node.Body)) +} + +func (node *FunctionDeclaration) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewFunctionDeclaration(node.Modifiers(), node.AsteriskToken, node.name, node.TypeParameters, node.Parameters, node.Type, node.FullSignature, node.Body), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *FunctionDeclaration) Name() *DeclarationName { + return node.name +} + +func IsFunctionDeclaration(node *Node) bool { + return node.Kind == KindFunctionDeclaration +} + +// ────────────────────────────────────────────────────────────────────── +// ClassDeclaration +// ────────────────────────────────────────────────────────────────────── + +type ClassDeclaration struct { + DeclarationBase + StatementBase + ClassLikeBase +} + +func (f *NodeFactory) NewClassDeclaration(modifiers *ModifierList, name *IdentifierNode, typeParameters *TypeParameterList, heritageClauses *HeritageClauseList, members *ClassElementList) *Node { + data := &ClassDeclaration{} + data.modifiers = modifiers + data.name = name + data.TypeParameters = typeParameters + data.HeritageClauses = heritageClauses + data.Members = members + return f.newNode(KindClassDeclaration, data) +} + +func (f *NodeFactory) UpdateClassDeclaration(node *ClassDeclaration, modifiers *ModifierList, name *IdentifierNode, typeParameters *TypeParameterList, heritageClauses *HeritageClauseList, members *ClassElementList) *Node { + if modifiers != node.modifiers || name != node.name || typeParameters != node.TypeParameters || heritageClauses != node.HeritageClauses || members != node.Members { + return updateNode(f.NewClassDeclaration(modifiers, name, typeParameters, heritageClauses, members), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *ClassDeclaration) ForEachChild(v Visitor) bool { + return visitModifiers(v, node.modifiers) || + visit(v, node.name) || + visitNodeList(v, node.TypeParameters) || + visitNodeList(v, node.HeritageClauses) || + visitNodeList(v, node.Members) +} + +func (node *ClassDeclaration) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateClassDeclaration(node, v.visitModifiers(node.modifiers), v.visitNode(node.name), v.visitNodes(node.TypeParameters), v.visitNodes(node.HeritageClauses), v.visitNodes(node.Members)) +} + +func (node *ClassDeclaration) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewClassDeclaration(node.Modifiers(), node.name, node.TypeParameters, node.HeritageClauses, node.Members), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *ClassDeclaration) Name() *DeclarationName { + return node.name +} + +func IsClassDeclaration(node *Node) bool { + return node.Kind == KindClassDeclaration +} + +// ────────────────────────────────────────────────────────────────────── +// ClassExpression +// ────────────────────────────────────────────────────────────────────── + +type ClassExpression struct { + PrimaryExpressionBase + ClassLikeBase +} + +func (f *NodeFactory) NewClassExpression(modifiers *ModifierList, name *IdentifierNode, typeParameters *TypeParameterList, heritageClauses *HeritageClauseList, members *ClassElementList) *Node { + data := &ClassExpression{} + data.modifiers = modifiers + data.name = name + data.TypeParameters = typeParameters + data.HeritageClauses = heritageClauses + data.Members = members + return f.newNode(KindClassExpression, data) +} + +func (f *NodeFactory) UpdateClassExpression(node *ClassExpression, modifiers *ModifierList, name *IdentifierNode, typeParameters *TypeParameterList, heritageClauses *HeritageClauseList, members *ClassElementList) *Node { + if modifiers != node.modifiers || name != node.name || typeParameters != node.TypeParameters || heritageClauses != node.HeritageClauses || members != node.Members { + return updateNode(f.NewClassExpression(modifiers, name, typeParameters, heritageClauses, members), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *ClassExpression) ForEachChild(v Visitor) bool { + return visitModifiers(v, node.modifiers) || + visit(v, node.name) || + visitNodeList(v, node.TypeParameters) || + visitNodeList(v, node.HeritageClauses) || + visitNodeList(v, node.Members) +} + +func (node *ClassExpression) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateClassExpression(node, v.visitModifiers(node.modifiers), v.visitNode(node.name), v.visitNodes(node.TypeParameters), v.visitNodes(node.HeritageClauses), v.visitNodes(node.Members)) +} + +func (node *ClassExpression) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewClassExpression(node.Modifiers(), node.name, node.TypeParameters, node.HeritageClauses, node.Members), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *ClassExpression) Name() *DeclarationName { + return node.name +} + +func IsClassExpression(node *Node) bool { + return node.Kind == KindClassExpression +} + +// ────────────────────────────────────────────────────────────────────── +// HeritageClause +// ────────────────────────────────────────────────────────────────────── + +type HeritageClause struct { + NodeBase + CompositeBase + Token Kind + Types *ExpressionWithTypeArgumentsList +} + +func (f *NodeFactory) NewHeritageClause(token Kind, types *ExpressionWithTypeArgumentsList) *Node { + data := f.heritageClauseArena.New() + data.Token = token + data.Types = types + return f.newNode(KindHeritageClause, data) +} + +func (f *NodeFactory) UpdateHeritageClause(node *HeritageClause, token Kind, types *ExpressionWithTypeArgumentsList) *Node { + if token != node.Token || types != node.Types { + return updateNode(f.NewHeritageClause(token, types), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *HeritageClause) ForEachChild(v Visitor) bool { + return visitNodeList(v, node.Types) +} + +func (node *HeritageClause) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateHeritageClause(node, node.Token, v.visitNodes(node.Types)) +} + +func (node *HeritageClause) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewHeritageClause(node.Token, node.Types), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsHeritageClause(node *Node) bool { + return node.Kind == KindHeritageClause +} + +// ────────────────────────────────────────────────────────────────────── +// InterfaceDeclaration +// ────────────────────────────────────────────────────────────────────── + +type InterfaceDeclaration struct { + DeclarationBase + StatementBase + ExportableBase + ModifiersBase + TypeSyntaxBase + name *IdentifierNode + TypeParameters *TypeParameterList // Optional + HeritageClauses *HeritageClauseList // Optional + Members *TypeElementList +} + +func (f *NodeFactory) NewInterfaceDeclaration(modifiers *ModifierList, name *IdentifierNode, typeParameters *TypeParameterList, heritageClauses *HeritageClauseList, members *TypeElementList) *Node { + data := f.interfaceDeclarationArena.New() + data.modifiers = modifiers + data.name = name + data.TypeParameters = typeParameters + data.HeritageClauses = heritageClauses + data.Members = members + return f.newNode(KindInterfaceDeclaration, data) +} + +func (f *NodeFactory) UpdateInterfaceDeclaration(node *InterfaceDeclaration, modifiers *ModifierList, name *IdentifierNode, typeParameters *TypeParameterList, heritageClauses *HeritageClauseList, members *TypeElementList) *Node { + if modifiers != node.modifiers || name != node.name || typeParameters != node.TypeParameters || heritageClauses != node.HeritageClauses || members != node.Members { + return updateNode(f.NewInterfaceDeclaration(modifiers, name, typeParameters, heritageClauses, members), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *InterfaceDeclaration) ForEachChild(v Visitor) bool { + return visitModifiers(v, node.modifiers) || + visit(v, node.name) || + visitNodeList(v, node.TypeParameters) || + visitNodeList(v, node.HeritageClauses) || + visitNodeList(v, node.Members) +} + +func (node *InterfaceDeclaration) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateInterfaceDeclaration(node, v.visitModifiers(node.modifiers), v.visitNode(node.name), v.visitNodes(node.TypeParameters), v.visitNodes(node.HeritageClauses), v.visitNodes(node.Members)) +} + +func (node *InterfaceDeclaration) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewInterfaceDeclaration(node.Modifiers(), node.name, node.TypeParameters, node.HeritageClauses, node.Members), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *InterfaceDeclaration) Name() *DeclarationName { + return node.name +} + +func IsInterfaceDeclaration(node *Node) bool { + return node.Kind == KindInterfaceDeclaration +} + +// ────────────────────────────────────────────────────────────────────── +// TypeAliasDeclaration +// ────────────────────────────────────────────────────────────────────── + +type TypeAliasDeclaration struct { + DeclarationBase + StatementBase + ExportableBase + ModifiersBase + LocalsContainerBase + TypeSyntaxBase + name *IdentifierNode + TypeParameters *TypeParameterList // Optional + Type *TypeNode +} + +func (f *NodeFactory) NewTypeAliasDeclaration(modifiers *ModifierList, name *IdentifierNode, typeParameters *TypeParameterList, typeNode *TypeNode) *Node { + data := f.typeAliasDeclarationArena.New() + data.modifiers = modifiers + data.name = name + data.TypeParameters = typeParameters + data.Type = typeNode + return f.newNode(KindTypeAliasDeclaration, data) +} + +func (f *NodeFactory) NewJSTypeAliasDeclaration(modifiers *ModifierList, name *IdentifierNode, typeParameters *TypeParameterList, typeNode *TypeNode) *Node { + data := f.typeAliasDeclarationArena.New() + data.modifiers = modifiers + data.name = name + data.TypeParameters = typeParameters + data.Type = typeNode + return f.newNode(KindJSTypeAliasDeclaration, data) +} + +func (f *NodeFactory) UpdateTypeAliasDeclaration(node *TypeAliasDeclaration, modifiers *ModifierList, name *IdentifierNode, typeParameters *TypeParameterList, typeNode *TypeNode) *Node { + if modifiers != node.modifiers || name != node.name || typeParameters != node.TypeParameters || typeNode != node.Type { + switch node.Kind { + case KindTypeAliasDeclaration: + return updateNode(f.NewTypeAliasDeclaration(modifiers, name, typeParameters, typeNode), node.AsNode(), f.hooks) + case KindJSTypeAliasDeclaration: + return updateNode(f.NewJSTypeAliasDeclaration(modifiers, name, typeParameters, typeNode), node.AsNode(), f.hooks) + default: + panic("unexpected kind in UpdateTypeAliasDeclaration: " + node.Kind.String()) + } + } + return node.AsNode() +} + +func (node *TypeAliasDeclaration) ForEachChild(v Visitor) bool { + return visitModifiers(v, node.modifiers) || + visit(v, node.name) || + visitNodeList(v, node.TypeParameters) || + visit(v, node.Type) +} + +func (node *TypeAliasDeclaration) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateTypeAliasDeclaration(node, v.visitModifiers(node.modifiers), v.visitNode(node.name), v.visitNodes(node.TypeParameters), v.visitNode(node.Type)) +} + +func (node *TypeAliasDeclaration) Clone(f NodeFactoryCoercible) *Node { + switch node.Kind { + case KindTypeAliasDeclaration: + return cloneNode(f.AsNodeFactory().NewTypeAliasDeclaration(node.Modifiers(), node.name, node.TypeParameters, node.Type), node.AsNode(), f.AsNodeFactory().hooks) + case KindJSTypeAliasDeclaration: + return cloneNode(f.AsNodeFactory().NewJSTypeAliasDeclaration(node.Modifiers(), node.name, node.TypeParameters, node.Type), node.AsNode(), f.AsNodeFactory().hooks) + default: + panic("unexpected kind in TypeAliasDeclaration.Clone: " + node.Kind.String()) + } +} + +func (node *TypeAliasDeclaration) Name() *DeclarationName { + return node.name +} + +func IsTypeAliasDeclaration(node *Node) bool { + return node.Kind == KindTypeAliasDeclaration +} + +func IsJSTypeAliasDeclaration(node *Node) bool { + return node.Kind == KindJSTypeAliasDeclaration +} + +// ────────────────────────────────────────────────────────────────────── +// EnumMember +// ────────────────────────────────────────────────────────────────────── + +type EnumMember struct { + NodeBase + NamedMemberBase + CompositeBase + Initializer *Expression // Optional +} + +func (f *NodeFactory) NewEnumMember(name *PropertyName, initializer *Expression) *Node { + data := &EnumMember{} + data.name = name + data.Initializer = initializer + return f.newNode(KindEnumMember, data) +} + +func (f *NodeFactory) UpdateEnumMember(node *EnumMember, name *PropertyName, initializer *Expression) *Node { + if name != node.name || initializer != node.Initializer { + return updateNode(f.NewEnumMember(name, initializer), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *EnumMember) ForEachChild(v Visitor) bool { + return visit(v, node.name) || visit(v, node.Initializer) +} + +func (node *EnumMember) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateEnumMember(node, v.visitNode(node.name), v.visitNode(node.Initializer)) +} + +func (node *EnumMember) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewEnumMember(node.name, node.Initializer), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *EnumMember) Name() *DeclarationName { + return node.name +} + +func IsEnumMember(node *Node) bool { + return node.Kind == KindEnumMember +} + +// ────────────────────────────────────────────────────────────────────── +// EnumDeclaration +// ────────────────────────────────────────────────────────────────────── + +type EnumDeclaration struct { + DeclarationBase + StatementBase + ExportableBase + ModifiersBase + CompositeBase + name *IdentifierNode + Members *EnumMemberList +} + +func (f *NodeFactory) NewEnumDeclaration(modifiers *ModifierList, name *IdentifierNode, members *EnumMemberList) *Node { + data := &EnumDeclaration{} + data.modifiers = modifiers + data.name = name + data.Members = members + return f.newNode(KindEnumDeclaration, data) +} + +func (f *NodeFactory) UpdateEnumDeclaration(node *EnumDeclaration, modifiers *ModifierList, name *IdentifierNode, members *EnumMemberList) *Node { + if modifiers != node.modifiers || name != node.name || members != node.Members { + return updateNode(f.NewEnumDeclaration(modifiers, name, members), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *EnumDeclaration) ForEachChild(v Visitor) bool { + return visitModifiers(v, node.modifiers) || visit(v, node.name) || visitNodeList(v, node.Members) +} + +func (node *EnumDeclaration) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateEnumDeclaration(node, v.visitModifiers(node.modifiers), v.visitNode(node.name), v.visitNodes(node.Members)) +} + +func (node *EnumDeclaration) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewEnumDeclaration(node.Modifiers(), node.name, node.Members), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *EnumDeclaration) Name() *DeclarationName { + return node.name +} + +func IsEnumDeclaration(node *Node) bool { + return node.Kind == KindEnumDeclaration +} + +// ────────────────────────────────────────────────────────────────────── +// ModuleBlock +// ────────────────────────────────────────────────────────────────────── + +type ModuleBlock struct { + StatementBase + CompositeBase + Statements *StatementList +} + +func (f *NodeFactory) NewModuleBlock(statements *StatementList) *Node { + data := &ModuleBlock{} + data.Statements = statements + return f.newNode(KindModuleBlock, data) +} + +func (f *NodeFactory) UpdateModuleBlock(node *ModuleBlock, statements *StatementList) *Node { + if statements != node.Statements { + return updateNode(f.NewModuleBlock(statements), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *ModuleBlock) ForEachChild(v Visitor) bool { + return visitNodeList(v, node.Statements) +} + +func (node *ModuleBlock) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateModuleBlock(node, v.visitNodes(node.Statements)) +} + +func (node *ModuleBlock) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewModuleBlock(node.Statements), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *ModuleBlock) computeSubtreeFacts() SubtreeFacts { + return propagateNodeListSubtreeFacts(node.Statements, propagateSubtreeFacts) +} + +func IsModuleBlock(node *Node) bool { + return node.Kind == KindModuleBlock +} + +// ────────────────────────────────────────────────────────────────────── +// NotEmittedStatement +// ────────────────────────────────────────────────────────────────────── + +type NotEmittedStatement struct { + StatementBase +} + +func (f *NodeFactory) NewNotEmittedStatement() *Node { + data := &NotEmittedStatement{} + return f.newNode(KindNotEmittedStatement, data) +} + +func (node *NotEmittedStatement) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewNotEmittedStatement(), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsNotEmittedStatement(node *Node) bool { + return node.Kind == KindNotEmittedStatement +} + +// ────────────────────────────────────────────────────────────────────── +// NotEmittedTypeElement +// ────────────────────────────────────────────────────────────────────── + +type NotEmittedTypeElement struct { + NodeBase + TypeElementBase +} + +func (f *NodeFactory) NewNotEmittedTypeElement() *Node { + data := &NotEmittedTypeElement{} + return f.newNode(KindNotEmittedTypeElement, data) +} + +func (node *NotEmittedTypeElement) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewNotEmittedTypeElement(), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsNotEmittedTypeElement(node *Node) bool { + return node.Kind == KindNotEmittedTypeElement +} + +// ────────────────────────────────────────────────────────────────────── +// ImportDeclaration +// ────────────────────────────────────────────────────────────────────── + +type ImportDeclaration struct { + StatementBase + ModifiersBase + CompositeBase + DeclarationBase + ImportClause *ImportClauseNode // Optional + ModuleSpecifier *Expression + Attributes *ImportAttributesNode // Optional +} + +func (f *NodeFactory) NewImportDeclaration(modifiers *ModifierList, importClause *ImportClauseNode, moduleSpecifier *Expression, attributes *ImportAttributesNode) *Node { + data := &ImportDeclaration{} + data.modifiers = modifiers + data.ImportClause = importClause + data.ModuleSpecifier = moduleSpecifier + data.Attributes = attributes + return f.newNode(KindImportDeclaration, data) +} + +func (f *NodeFactory) NewJSImportDeclaration(modifiers *ModifierList, importClause *ImportClauseNode, moduleSpecifier *Expression, attributes *ImportAttributesNode) *Node { + data := &ImportDeclaration{} + data.modifiers = modifiers + data.ImportClause = importClause + data.ModuleSpecifier = moduleSpecifier + data.Attributes = attributes + return f.newNode(KindJSImportDeclaration, data) +} + +func (f *NodeFactory) UpdateImportDeclaration(node *ImportDeclaration, modifiers *ModifierList, importClause *ImportClauseNode, moduleSpecifier *Expression, attributes *ImportAttributesNode) *Node { + if modifiers != node.modifiers || importClause != node.ImportClause || moduleSpecifier != node.ModuleSpecifier || attributes != node.Attributes { + switch node.Kind { + case KindImportDeclaration: + return updateNode(f.NewImportDeclaration(modifiers, importClause, moduleSpecifier, attributes), node.AsNode(), f.hooks) + case KindJSImportDeclaration: + return updateNode(f.NewJSImportDeclaration(modifiers, importClause, moduleSpecifier, attributes), node.AsNode(), f.hooks) + default: + panic("unexpected kind in UpdateImportDeclaration: " + node.Kind.String()) + } + } + return node.AsNode() +} + +func (node *ImportDeclaration) ForEachChild(v Visitor) bool { + return visitModifiers(v, node.modifiers) || + visit(v, node.ImportClause) || + visit(v, node.ModuleSpecifier) || + visit(v, node.Attributes) +} + +func (node *ImportDeclaration) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateImportDeclaration(node, v.visitModifiers(node.modifiers), v.visitNode(node.ImportClause), v.visitNode(node.ModuleSpecifier), v.visitNode(node.Attributes)) +} + +func (node *ImportDeclaration) Clone(f NodeFactoryCoercible) *Node { + switch node.Kind { + case KindImportDeclaration: + return cloneNode(f.AsNodeFactory().NewImportDeclaration(node.Modifiers(), node.ImportClause, node.ModuleSpecifier, node.Attributes), node.AsNode(), f.AsNodeFactory().hooks) + case KindJSImportDeclaration: + return cloneNode(f.AsNodeFactory().NewJSImportDeclaration(node.Modifiers(), node.ImportClause, node.ModuleSpecifier, node.Attributes), node.AsNode(), f.AsNodeFactory().hooks) + default: + panic("unexpected kind in ImportDeclaration.Clone: " + node.Kind.String()) + } +} + +func (node *ImportDeclaration) computeSubtreeFacts() SubtreeFacts { + return propagateModifierListSubtreeFacts(node.modifiers) | + propagateSubtreeFacts(node.ImportClause) | + propagateSubtreeFacts(node.ModuleSpecifier) | + propagateSubtreeFacts(node.Attributes) +} + +func IsImportDeclaration(node *Node) bool { + return node.Kind == KindImportDeclaration +} + +func IsJSImportDeclaration(node *Node) bool { + return node.Kind == KindJSImportDeclaration +} + +// ────────────────────────────────────────────────────────────────────── +// ExternalModuleReference +// ────────────────────────────────────────────────────────────────────── + +type ExternalModuleReference struct { + NodeBase + Expression *Expression +} + +func (f *NodeFactory) NewExternalModuleReference(expression *Expression) *Node { + data := &ExternalModuleReference{} + data.Expression = expression + return f.newNode(KindExternalModuleReference, data) +} + +func (f *NodeFactory) UpdateExternalModuleReference(node *ExternalModuleReference, expression *Expression) *Node { + if expression != node.Expression { + return updateNode(f.NewExternalModuleReference(expression), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *ExternalModuleReference) ForEachChild(v Visitor) bool { + return visit(v, node.Expression) +} + +func (node *ExternalModuleReference) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateExternalModuleReference(node, v.visitNode(node.Expression)) +} + +func (node *ExternalModuleReference) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewExternalModuleReference(node.Expression), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *ExternalModuleReference) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.Expression) +} + +func IsExternalModuleReference(node *Node) bool { + return node.Kind == KindExternalModuleReference +} + +// ────────────────────────────────────────────────────────────────────── +// NamespaceImport +// ────────────────────────────────────────────────────────────────────── + +type NamespaceImport struct { + NodeBase + DeclarationBase + ExportableBase + name *IdentifierNode +} + +func (f *NodeFactory) NewNamespaceImport(name *IdentifierNode) *Node { + data := &NamespaceImport{} + data.name = name + return f.newNode(KindNamespaceImport, data) +} + +func (f *NodeFactory) UpdateNamespaceImport(node *NamespaceImport, name *IdentifierNode) *Node { + if name != node.name { + return updateNode(f.NewNamespaceImport(name), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *NamespaceImport) ForEachChild(v Visitor) bool { + return visit(v, node.name) +} + +func (node *NamespaceImport) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateNamespaceImport(node, v.visitNode(node.name)) +} + +func (node *NamespaceImport) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewNamespaceImport(node.name), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *NamespaceImport) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.name) +} + +func (node *NamespaceImport) Name() *DeclarationName { + return node.name +} + +func IsNamespaceImport(node *Node) bool { + return node.Kind == KindNamespaceImport +} + +// ────────────────────────────────────────────────────────────────────── +// NamedImports +// ────────────────────────────────────────────────────────────────────── + +type NamedImports struct { + NodeBase + CompositeBase + Elements *ImportSpecifierList +} + +func (f *NodeFactory) NewNamedImports(elements *ImportSpecifierList) *Node { + data := &NamedImports{} + data.Elements = elements + return f.newNode(KindNamedImports, data) +} + +func (f *NodeFactory) UpdateNamedImports(node *NamedImports, elements *ImportSpecifierList) *Node { + if elements != node.Elements { + return updateNode(f.NewNamedImports(elements), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *NamedImports) ForEachChild(v Visitor) bool { + return visitNodeList(v, node.Elements) +} + +func (node *NamedImports) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateNamedImports(node, v.visitNodes(node.Elements)) +} + +func (node *NamedImports) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewNamedImports(node.Elements), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *NamedImports) computeSubtreeFacts() SubtreeFacts { + return propagateNodeListSubtreeFacts(node.Elements, propagateSubtreeFacts) +} + +func IsNamedImports(node *Node) bool { + return node.Kind == KindNamedImports +} + +// ────────────────────────────────────────────────────────────────────── +// ExportAssignment +// ────────────────────────────────────────────────────────────────────── + +type ExportAssignment struct { + DeclarationBase + StatementBase + ModifiersBase + CompositeBase + IsExportEquals bool + Type *TypeNode + Expression *Expression +} + +func (f *NodeFactory) NewExportAssignment(modifiers *ModifierList, isExportEquals bool, typeNode *TypeNode, expression *Expression) *Node { + data := &ExportAssignment{} + data.modifiers = modifiers + data.IsExportEquals = isExportEquals + data.Type = typeNode + data.Expression = expression + return f.newNode(KindExportAssignment, data) +} + +func (f *NodeFactory) UpdateExportAssignment(node *ExportAssignment, modifiers *ModifierList, isExportEquals bool, typeNode *TypeNode, expression *Expression) *Node { + if modifiers != node.modifiers || isExportEquals != node.IsExportEquals || typeNode != node.Type || expression != node.Expression { + return updateNode(f.NewExportAssignment(modifiers, isExportEquals, typeNode, expression), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *ExportAssignment) ForEachChild(v Visitor) bool { + return visitModifiers(v, node.modifiers) || visit(v, node.Type) || visit(v, node.Expression) +} + +func (node *ExportAssignment) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateExportAssignment(node, v.visitModifiers(node.modifiers), node.IsExportEquals, v.visitNode(node.Type), v.visitNode(node.Expression)) +} + +func (node *ExportAssignment) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewExportAssignment(node.Modifiers(), node.IsExportEquals, node.Type, node.Expression), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsExportAssignment(node *Node) bool { + return node.Kind == KindExportAssignment +} + +// ────────────────────────────────────────────────────────────────────── +// NamespaceExportDeclaration +// ────────────────────────────────────────────────────────────────────── + +type NamespaceExportDeclaration struct { + DeclarationBase + StatementBase + ModifiersBase + TypeSyntaxBase + name *IdentifierNode +} + +func (f *NodeFactory) NewNamespaceExportDeclaration(modifiers *ModifierList, name *IdentifierNode) *Node { + data := &NamespaceExportDeclaration{} + data.modifiers = modifiers + data.name = name + return f.newNode(KindNamespaceExportDeclaration, data) +} + +func (f *NodeFactory) UpdateNamespaceExportDeclaration(node *NamespaceExportDeclaration, modifiers *ModifierList, name *IdentifierNode) *Node { + if modifiers != node.modifiers || name != node.name { + return updateNode(f.NewNamespaceExportDeclaration(modifiers, name), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *NamespaceExportDeclaration) ForEachChild(v Visitor) bool { + return visitModifiers(v, node.modifiers) || visit(v, node.name) +} + +func (node *NamespaceExportDeclaration) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateNamespaceExportDeclaration(node, v.visitModifiers(node.modifiers), v.visitNode(node.name)) +} + +func (node *NamespaceExportDeclaration) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewNamespaceExportDeclaration(node.Modifiers(), node.name), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *NamespaceExportDeclaration) Name() *DeclarationName { + return node.name +} + +func IsNamespaceExportDeclaration(node *Node) bool { + return node.Kind == KindNamespaceExportDeclaration +} + +// ────────────────────────────────────────────────────────────────────── +// NamespaceExport +// ────────────────────────────────────────────────────────────────────── + +type NamespaceExport struct { + NodeBase + DeclarationBase + name *ModuleExportName +} + +func (f *NodeFactory) NewNamespaceExport(name *ModuleExportName) *Node { + data := &NamespaceExport{} + data.name = name + return f.newNode(KindNamespaceExport, data) +} + +func (f *NodeFactory) UpdateNamespaceExport(node *NamespaceExport, name *ModuleExportName) *Node { + if name != node.name { + return updateNode(f.NewNamespaceExport(name), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *NamespaceExport) ForEachChild(v Visitor) bool { + return visit(v, node.name) +} + +func (node *NamespaceExport) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateNamespaceExport(node, v.visitNode(node.name)) +} + +func (node *NamespaceExport) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewNamespaceExport(node.name), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *NamespaceExport) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.name) +} + +func (node *NamespaceExport) Name() *DeclarationName { + return node.name +} + +func IsNamespaceExport(node *Node) bool { + return node.Kind == KindNamespaceExport +} + +// ────────────────────────────────────────────────────────────────────── +// NamedExports +// ────────────────────────────────────────────────────────────────────── + +type NamedExports struct { + NodeBase + CompositeBase + Elements *ExportSpecifierList +} + +func (f *NodeFactory) NewNamedExports(elements *ExportSpecifierList) *Node { + data := &NamedExports{} + data.Elements = elements + return f.newNode(KindNamedExports, data) +} + +func (f *NodeFactory) UpdateNamedExports(node *NamedExports, elements *ExportSpecifierList) *Node { + if elements != node.Elements { + return updateNode(f.NewNamedExports(elements), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *NamedExports) ForEachChild(v Visitor) bool { + return visitNodeList(v, node.Elements) +} + +func (node *NamedExports) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateNamedExports(node, v.visitNodes(node.Elements)) +} + +func (node *NamedExports) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewNamedExports(node.Elements), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *NamedExports) computeSubtreeFacts() SubtreeFacts { + return propagateNodeListSubtreeFacts(node.Elements, propagateSubtreeFacts) +} + +func IsNamedExports(node *Node) bool { + return node.Kind == KindNamedExports +} + +// ────────────────────────────────────────────────────────────────────── +// ExportSpecifier +// ────────────────────────────────────────────────────────────────────── + +type ExportSpecifier struct { + NodeBase + DeclarationBase + ExportableBase + CompositeBase + IsTypeOnly bool + PropertyName *ModuleExportName // Optional + name *ModuleExportName +} + +func (f *NodeFactory) NewExportSpecifier(isTypeOnly bool, propertyName *ModuleExportName, name *ModuleExportName) *Node { + data := &ExportSpecifier{} + data.IsTypeOnly = isTypeOnly + data.PropertyName = propertyName + data.name = name + return f.newNode(KindExportSpecifier, data) +} + +func (f *NodeFactory) UpdateExportSpecifier(node *ExportSpecifier, isTypeOnly bool, propertyName *ModuleExportName, name *ModuleExportName) *Node { + if isTypeOnly != node.IsTypeOnly || propertyName != node.PropertyName || name != node.name { + return updateNode(f.NewExportSpecifier(isTypeOnly, propertyName, name), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *ExportSpecifier) ForEachChild(v Visitor) bool { + return visit(v, node.PropertyName) || visit(v, node.name) +} + +func (node *ExportSpecifier) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateExportSpecifier(node, node.IsTypeOnly, v.visitNode(node.PropertyName), v.visitNode(node.name)) +} + +func (node *ExportSpecifier) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewExportSpecifier(node.IsTypeOnly, node.PropertyName, node.name), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *ExportSpecifier) Name() *DeclarationName { + return node.name +} + +func IsExportSpecifier(node *Node) bool { + return node.Kind == KindExportSpecifier +} + +// ────────────────────────────────────────────────────────────────────── +// CallSignatureDeclaration +// ────────────────────────────────────────────────────────────────────── + +type CallSignatureDeclaration struct { + NodeBase + DeclarationBase + FunctionLikeBase + TypeElementBase + TypeSyntaxBase +} + +func (f *NodeFactory) NewCallSignatureDeclaration(typeParameters *TypeParameterList, parameters *ParameterList, typeNode *TypeNode) *Node { + data := &CallSignatureDeclaration{} + data.TypeParameters = typeParameters + data.Parameters = parameters + data.Type = typeNode + return f.newNode(KindCallSignature, data) +} + +func (f *NodeFactory) UpdateCallSignatureDeclaration(node *CallSignatureDeclaration, typeParameters *TypeParameterList, parameters *ParameterList, typeNode *TypeNode) *Node { + if typeParameters != node.TypeParameters || parameters != node.Parameters || typeNode != node.Type { + return updateNode(f.NewCallSignatureDeclaration(typeParameters, parameters, typeNode), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *CallSignatureDeclaration) ForEachChild(v Visitor) bool { + return visitNodeList(v, node.TypeParameters) || visitNodeList(v, node.Parameters) || visit(v, node.Type) +} + +func (node *CallSignatureDeclaration) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateCallSignatureDeclaration(node, v.visitNodes(node.TypeParameters), v.visitNodes(node.Parameters), v.visitNode(node.Type)) +} + +func (node *CallSignatureDeclaration) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewCallSignatureDeclaration(node.TypeParameters, node.Parameters, node.Type), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsCallSignatureDeclaration(node *Node) bool { + return node.Kind == KindCallSignature +} + +// ────────────────────────────────────────────────────────────────────── +// ConstructSignatureDeclaration +// ────────────────────────────────────────────────────────────────────── + +type ConstructSignatureDeclaration struct { + NodeBase + DeclarationBase + FunctionLikeBase + TypeElementBase + TypeSyntaxBase +} + +func (f *NodeFactory) NewConstructSignatureDeclaration(typeParameters *TypeParameterList, parameters *ParameterList, typeNode *TypeNode) *Node { + data := f.constructSignatureDeclarationArena.New() + data.TypeParameters = typeParameters + data.Parameters = parameters + data.Type = typeNode + return f.newNode(KindConstructSignature, data) +} + +func (f *NodeFactory) UpdateConstructSignatureDeclaration(node *ConstructSignatureDeclaration, typeParameters *TypeParameterList, parameters *ParameterList, typeNode *TypeNode) *Node { + if typeParameters != node.TypeParameters || parameters != node.Parameters || typeNode != node.Type { + return updateNode(f.NewConstructSignatureDeclaration(typeParameters, parameters, typeNode), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *ConstructSignatureDeclaration) ForEachChild(v Visitor) bool { + return visitNodeList(v, node.TypeParameters) || visitNodeList(v, node.Parameters) || visit(v, node.Type) +} + +func (node *ConstructSignatureDeclaration) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateConstructSignatureDeclaration(node, v.visitNodes(node.TypeParameters), v.visitNodes(node.Parameters), v.visitNode(node.Type)) +} + +func (node *ConstructSignatureDeclaration) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewConstructSignatureDeclaration(node.TypeParameters, node.Parameters, node.Type), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsConstructSignatureDeclaration(node *Node) bool { + return node.Kind == KindConstructSignature +} + +// ────────────────────────────────────────────────────────────────────── +// ConstructorDeclaration +// ────────────────────────────────────────────────────────────────────── + +type ConstructorDeclaration struct { + NodeBase + DeclarationBase + ModifiersBase + FunctionLikeWithBodyBase + ClassElementBase + CompositeBase + ReturnFlowNode *FlowNode +} + +func (f *NodeFactory) NewConstructorDeclaration(modifiers *ModifierList, typeParameters *TypeParameterList, parameters *ParameterList, typeNode *TypeNode, fullSignature *TypeNode, body *FunctionBody) *Node { + data := &ConstructorDeclaration{} + data.modifiers = modifiers + data.TypeParameters = typeParameters + data.Parameters = parameters + data.Type = typeNode + data.FullSignature = fullSignature + data.Body = body + return f.newNode(KindConstructor, data) +} + +func (f *NodeFactory) UpdateConstructorDeclaration(node *ConstructorDeclaration, modifiers *ModifierList, typeParameters *TypeParameterList, parameters *ParameterList, typeNode *TypeNode, fullSignature *TypeNode, body *FunctionBody) *Node { + if modifiers != node.modifiers || typeParameters != node.TypeParameters || parameters != node.Parameters || typeNode != node.Type || fullSignature != node.FullSignature || body != node.Body { + return updateNode(f.NewConstructorDeclaration(modifiers, typeParameters, parameters, typeNode, fullSignature, body), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *ConstructorDeclaration) ForEachChild(v Visitor) bool { + return visitModifiers(v, node.modifiers) || + visitNodeList(v, node.TypeParameters) || + visitNodeList(v, node.Parameters) || + visit(v, node.Type) || + visit(v, node.FullSignature) || + visit(v, node.Body) +} + +func (node *ConstructorDeclaration) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateConstructorDeclaration(node, v.visitModifiers(node.modifiers), v.visitNodes(node.TypeParameters), v.visitParameters(node.Parameters), v.visitNode(node.Type), v.visitNode(node.FullSignature), v.visitFunctionBody(node.Body)) +} + +func (node *ConstructorDeclaration) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewConstructorDeclaration(node.Modifiers(), node.TypeParameters, node.Parameters, node.Type, node.FullSignature, node.Body), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsConstructorDeclaration(node *Node) bool { + return node.Kind == KindConstructor +} + +// ────────────────────────────────────────────────────────────────────── +// GetAccessorDeclaration +// ────────────────────────────────────────────────────────────────────── + +type GetAccessorDeclaration struct { + AccessorDeclarationBase +} + +func (f *NodeFactory) NewGetAccessorDeclaration(modifiers *ModifierList, name *PropertyName, typeParameters *TypeParameterList, parameters *ParameterList, typeNode *TypeNode, fullSignature *TypeNode, body *FunctionBody) *Node { + data := &GetAccessorDeclaration{} + data.modifiers = modifiers + data.name = name + data.TypeParameters = typeParameters + data.Parameters = parameters + data.Type = typeNode + data.FullSignature = fullSignature + data.Body = body + return f.newNode(KindGetAccessor, data) +} + +func (f *NodeFactory) UpdateGetAccessorDeclaration(node *GetAccessorDeclaration, modifiers *ModifierList, name *PropertyName, typeParameters *TypeParameterList, parameters *ParameterList, typeNode *TypeNode, fullSignature *TypeNode, body *FunctionBody) *Node { + if modifiers != node.modifiers || name != node.name || typeParameters != node.TypeParameters || parameters != node.Parameters || typeNode != node.Type || fullSignature != node.FullSignature || body != node.Body { + return updateNode(f.NewGetAccessorDeclaration(modifiers, name, typeParameters, parameters, typeNode, fullSignature, body), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *GetAccessorDeclaration) ForEachChild(v Visitor) bool { + return visitModifiers(v, node.modifiers) || + visit(v, node.name) || + visitNodeList(v, node.TypeParameters) || + visitNodeList(v, node.Parameters) || + visit(v, node.Type) || + visit(v, node.FullSignature) || + visit(v, node.Body) +} + +func (node *GetAccessorDeclaration) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateGetAccessorDeclaration(node, v.visitModifiers(node.modifiers), v.visitNode(node.name), v.visitNodes(node.TypeParameters), v.visitParameters(node.Parameters), v.visitNode(node.Type), v.visitNode(node.FullSignature), v.visitFunctionBody(node.Body)) +} + +func (node *GetAccessorDeclaration) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewGetAccessorDeclaration(node.Modifiers(), node.name, node.TypeParameters, node.Parameters, node.Type, node.FullSignature, node.Body), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *GetAccessorDeclaration) Name() *DeclarationName { + return node.name +} + +func IsGetAccessorDeclaration(node *Node) bool { + return node.Kind == KindGetAccessor +} + +// ────────────────────────────────────────────────────────────────────── +// SetAccessorDeclaration +// ────────────────────────────────────────────────────────────────────── + +type SetAccessorDeclaration struct { + AccessorDeclarationBase +} + +func (f *NodeFactory) NewSetAccessorDeclaration(modifiers *ModifierList, name *PropertyName, typeParameters *TypeParameterList, parameters *ParameterList, typeNode *TypeNode, fullSignature *TypeNode, body *FunctionBody) *Node { + data := &SetAccessorDeclaration{} + data.modifiers = modifiers + data.name = name + data.TypeParameters = typeParameters + data.Parameters = parameters + data.Type = typeNode + data.FullSignature = fullSignature + data.Body = body + return f.newNode(KindSetAccessor, data) +} + +func (f *NodeFactory) UpdateSetAccessorDeclaration(node *SetAccessorDeclaration, modifiers *ModifierList, name *PropertyName, typeParameters *TypeParameterList, parameters *ParameterList, typeNode *TypeNode, fullSignature *TypeNode, body *FunctionBody) *Node { + if modifiers != node.modifiers || name != node.name || typeParameters != node.TypeParameters || parameters != node.Parameters || typeNode != node.Type || fullSignature != node.FullSignature || body != node.Body { + return updateNode(f.NewSetAccessorDeclaration(modifiers, name, typeParameters, parameters, typeNode, fullSignature, body), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *SetAccessorDeclaration) ForEachChild(v Visitor) bool { + return visitModifiers(v, node.modifiers) || + visit(v, node.name) || + visitNodeList(v, node.TypeParameters) || + visitNodeList(v, node.Parameters) || + visit(v, node.Type) || + visit(v, node.FullSignature) || + visit(v, node.Body) +} + +func (node *SetAccessorDeclaration) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateSetAccessorDeclaration(node, v.visitModifiers(node.modifiers), v.visitNode(node.name), v.visitNodes(node.TypeParameters), v.visitParameters(node.Parameters), v.visitNode(node.Type), v.visitNode(node.FullSignature), v.visitFunctionBody(node.Body)) +} + +func (node *SetAccessorDeclaration) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewSetAccessorDeclaration(node.Modifiers(), node.name, node.TypeParameters, node.Parameters, node.Type, node.FullSignature, node.Body), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *SetAccessorDeclaration) Name() *DeclarationName { + return node.name +} + +func IsSetAccessorDeclaration(node *Node) bool { + return node.Kind == KindSetAccessor +} + +// ────────────────────────────────────────────────────────────────────── +// IndexSignatureDeclaration +// ────────────────────────────────────────────────────────────────────── + +type IndexSignatureDeclaration struct { + NodeBase + DeclarationBase + ModifiersBase + FunctionLikeBase + TypeElementBase + ClassElementBase + TypeSyntaxBase +} + +func (f *NodeFactory) NewIndexSignatureDeclaration(modifiers *ModifierList, parameters *ParameterList, typeNode *TypeNode) *Node { + data := &IndexSignatureDeclaration{} + data.modifiers = modifiers + data.Parameters = parameters + data.Type = typeNode + return f.newNode(KindIndexSignature, data) +} + +func (f *NodeFactory) UpdateIndexSignatureDeclaration(node *IndexSignatureDeclaration, modifiers *ModifierList, parameters *ParameterList, typeNode *TypeNode) *Node { + if modifiers != node.modifiers || parameters != node.Parameters || typeNode != node.Type { + return updateNode(f.NewIndexSignatureDeclaration(modifiers, parameters, typeNode), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *IndexSignatureDeclaration) ForEachChild(v Visitor) bool { + return visitModifiers(v, node.modifiers) || visitNodeList(v, node.Parameters) || visit(v, node.Type) +} + +func (node *IndexSignatureDeclaration) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateIndexSignatureDeclaration(node, v.visitModifiers(node.modifiers), v.visitNodes(node.Parameters), v.visitNode(node.Type)) +} + +func (node *IndexSignatureDeclaration) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewIndexSignatureDeclaration(node.Modifiers(), node.Parameters, node.Type), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsIndexSignatureDeclaration(node *Node) bool { + return node.Kind == KindIndexSignature +} + +// ────────────────────────────────────────────────────────────────────── +// MethodSignatureDeclaration +// ────────────────────────────────────────────────────────────────────── + +type MethodSignatureDeclaration struct { + NodeBase + NamedMemberBase + FunctionLikeBase + TypeElementBase + TypeSyntaxBase +} + +func (f *NodeFactory) NewMethodSignatureDeclaration(modifiers *ModifierList, name *PropertyName, postfixToken *TokenNode, typeParameters *TypeParameterList, parameters *ParameterList, typeNode *TypeNode) *Node { + data := f.methodSignatureDeclarationArena.New() + data.modifiers = modifiers + data.name = name + data.PostfixToken = postfixToken + data.TypeParameters = typeParameters + data.Parameters = parameters + data.Type = typeNode + return f.newNode(KindMethodSignature, data) +} + +func (f *NodeFactory) UpdateMethodSignatureDeclaration(node *MethodSignatureDeclaration, modifiers *ModifierList, name *PropertyName, postfixToken *TokenNode, typeParameters *TypeParameterList, parameters *ParameterList, typeNode *TypeNode) *Node { + if modifiers != node.modifiers || name != node.name || postfixToken != node.PostfixToken || typeParameters != node.TypeParameters || parameters != node.Parameters || typeNode != node.Type { + return updateNode(f.NewMethodSignatureDeclaration(modifiers, name, postfixToken, typeParameters, parameters, typeNode), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *MethodSignatureDeclaration) ForEachChild(v Visitor) bool { + return visitModifiers(v, node.modifiers) || + visit(v, node.name) || + visit(v, node.PostfixToken) || + visitNodeList(v, node.TypeParameters) || + visitNodeList(v, node.Parameters) || + visit(v, node.Type) +} + +func (node *MethodSignatureDeclaration) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateMethodSignatureDeclaration(node, v.visitModifiers(node.modifiers), v.visitNode(node.name), v.visitNode(node.PostfixToken), v.visitNodes(node.TypeParameters), v.visitNodes(node.Parameters), v.visitNode(node.Type)) +} + +func (node *MethodSignatureDeclaration) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewMethodSignatureDeclaration(node.Modifiers(), node.name, node.PostfixToken, node.TypeParameters, node.Parameters, node.Type), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *MethodSignatureDeclaration) Name() *DeclarationName { + return node.name +} + +func IsMethodSignatureDeclaration(node *Node) bool { + return node.Kind == KindMethodSignature +} + +// ────────────────────────────────────────────────────────────────────── +// MethodDeclaration +// ────────────────────────────────────────────────────────────────────── + +type MethodDeclaration struct { + NodeBase + NamedMemberBase + FunctionLikeWithBodyBase + FlowNodeBase + ClassElementBase + ObjectLiteralElementBase + CompositeBase +} + +func (f *NodeFactory) NewMethodDeclaration(modifiers *ModifierList, asteriskToken *AsteriskToken, name *PropertyName, postfixToken *TokenNode, typeParameters *TypeParameterList, parameters *ParameterList, typeNode *TypeNode, fullSignature *TypeNode, body *FunctionBody) *Node { + data := &MethodDeclaration{} + data.modifiers = modifiers + data.AsteriskToken = asteriskToken + data.name = name + data.PostfixToken = postfixToken + data.TypeParameters = typeParameters + data.Parameters = parameters + data.Type = typeNode + data.FullSignature = fullSignature + data.Body = body + return f.newNode(KindMethodDeclaration, data) +} + +func (f *NodeFactory) UpdateMethodDeclaration(node *MethodDeclaration, modifiers *ModifierList, asteriskToken *AsteriskToken, name *PropertyName, postfixToken *TokenNode, typeParameters *TypeParameterList, parameters *ParameterList, typeNode *TypeNode, fullSignature *TypeNode, body *FunctionBody) *Node { + if modifiers != node.modifiers || asteriskToken != node.AsteriskToken || name != node.name || postfixToken != node.PostfixToken || typeParameters != node.TypeParameters || parameters != node.Parameters || typeNode != node.Type || fullSignature != node.FullSignature || body != node.Body { + return updateNode(f.NewMethodDeclaration(modifiers, asteriskToken, name, postfixToken, typeParameters, parameters, typeNode, fullSignature, body), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *MethodDeclaration) ForEachChild(v Visitor) bool { + return visitModifiers(v, node.modifiers) || + visit(v, node.AsteriskToken) || + visit(v, node.name) || + visit(v, node.PostfixToken) || + visitNodeList(v, node.TypeParameters) || + visitNodeList(v, node.Parameters) || + visit(v, node.Type) || + visit(v, node.FullSignature) || + visit(v, node.Body) +} + +func (node *MethodDeclaration) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateMethodDeclaration(node, v.visitModifiers(node.modifiers), v.visitNode(node.AsteriskToken), v.visitNode(node.name), v.visitNode(node.PostfixToken), v.visitNodes(node.TypeParameters), v.visitParameters(node.Parameters), v.visitNode(node.Type), v.visitNode(node.FullSignature), v.visitFunctionBody(node.Body)) +} + +func (node *MethodDeclaration) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewMethodDeclaration(node.Modifiers(), node.AsteriskToken, node.name, node.PostfixToken, node.TypeParameters, node.Parameters, node.Type, node.FullSignature, node.Body), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *MethodDeclaration) Name() *DeclarationName { + return node.name +} + +func IsMethodDeclaration(node *Node) bool { + return node.Kind == KindMethodDeclaration +} + +// ────────────────────────────────────────────────────────────────────── +// PropertySignatureDeclaration +// ────────────────────────────────────────────────────────────────────── + +type PropertySignatureDeclaration struct { + NodeBase + NamedMemberBase + TypeElementBase + TypeSyntaxBase + Type *TypeNode + Initializer *Expression +} + +func (f *NodeFactory) NewPropertySignatureDeclaration(modifiers *ModifierList, name *PropertyName, postfixToken *TokenNode, typeNode *TypeNode, initializer *Expression) *Node { + data := f.propertySignatureDeclarationArena.New() + data.modifiers = modifiers + data.name = name + data.PostfixToken = postfixToken + data.Type = typeNode + data.Initializer = initializer + return f.newNode(KindPropertySignature, data) +} + +func (f *NodeFactory) UpdatePropertySignatureDeclaration(node *PropertySignatureDeclaration, modifiers *ModifierList, name *PropertyName, postfixToken *TokenNode, typeNode *TypeNode, initializer *Expression) *Node { + if modifiers != node.modifiers || name != node.name || postfixToken != node.PostfixToken || typeNode != node.Type || initializer != node.Initializer { + return updateNode(f.NewPropertySignatureDeclaration(modifiers, name, postfixToken, typeNode, initializer), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *PropertySignatureDeclaration) ForEachChild(v Visitor) bool { + return visitModifiers(v, node.modifiers) || + visit(v, node.name) || + visit(v, node.PostfixToken) || + visit(v, node.Type) || + visit(v, node.Initializer) +} + +func (node *PropertySignatureDeclaration) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdatePropertySignatureDeclaration(node, v.visitModifiers(node.modifiers), v.visitNode(node.name), v.visitNode(node.PostfixToken), v.visitNode(node.Type), v.visitNode(node.Initializer)) +} + +func (node *PropertySignatureDeclaration) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewPropertySignatureDeclaration(node.Modifiers(), node.name, node.PostfixToken, node.Type, node.Initializer), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *PropertySignatureDeclaration) Name() *DeclarationName { + return node.name +} + +func IsPropertySignatureDeclaration(node *Node) bool { + return node.Kind == KindPropertySignature +} + +// ────────────────────────────────────────────────────────────────────── +// PropertyDeclaration +// ────────────────────────────────────────────────────────────────────── + +type PropertyDeclaration struct { + NodeBase + NamedMemberBase + ClassElementBase + CompositeBase + Type *TypeNode // Optional + Initializer *Expression // Optional +} + +func (f *NodeFactory) NewPropertyDeclaration(modifiers *ModifierList, name *PropertyName, postfixToken *TokenNode, typeNode *TypeNode, initializer *Expression) *Node { + data := &PropertyDeclaration{} + data.modifiers = modifiers + data.name = name + data.PostfixToken = postfixToken + data.Type = typeNode + data.Initializer = initializer + return f.newNode(KindPropertyDeclaration, data) +} + +func (f *NodeFactory) UpdatePropertyDeclaration(node *PropertyDeclaration, modifiers *ModifierList, name *PropertyName, postfixToken *TokenNode, typeNode *TypeNode, initializer *Expression) *Node { + if modifiers != node.modifiers || name != node.name || postfixToken != node.PostfixToken || typeNode != node.Type || initializer != node.Initializer { + return updateNode(f.NewPropertyDeclaration(modifiers, name, postfixToken, typeNode, initializer), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *PropertyDeclaration) ForEachChild(v Visitor) bool { + return visitModifiers(v, node.modifiers) || + visit(v, node.name) || + visit(v, node.PostfixToken) || + visit(v, node.Type) || + visit(v, node.Initializer) +} + +func (node *PropertyDeclaration) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdatePropertyDeclaration(node, v.visitModifiers(node.modifiers), v.visitNode(node.name), v.visitNode(node.PostfixToken), v.visitNode(node.Type), v.visitNode(node.Initializer)) +} + +func (node *PropertyDeclaration) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewPropertyDeclaration(node.Modifiers(), node.name, node.PostfixToken, node.Type, node.Initializer), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *PropertyDeclaration) Name() *DeclarationName { + return node.name +} + +func IsPropertyDeclaration(node *Node) bool { + return node.Kind == KindPropertyDeclaration +} + +// ────────────────────────────────────────────────────────────────────── +// SemicolonClassElement +// ────────────────────────────────────────────────────────────────────── + +type SemicolonClassElement struct { + NodeBase + DeclarationBase + ClassElementBase +} + +func (f *NodeFactory) NewSemicolonClassElement() *Node { + data := &SemicolonClassElement{} + return f.newNode(KindSemicolonClassElement, data) +} + +func (node *SemicolonClassElement) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewSemicolonClassElement(), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsSemicolonClassElement(node *Node) bool { + return node.Kind == KindSemicolonClassElement +} + +// ────────────────────────────────────────────────────────────────────── +// ClassStaticBlockDeclaration +// ────────────────────────────────────────────────────────────────────── + +type ClassStaticBlockDeclaration struct { + NodeBase + DeclarationBase + ModifiersBase + LocalsContainerBase + ClassElementBase + CompositeBase + Body *BlockNode + ReturnFlowNode *FlowNode +} + +func (f *NodeFactory) NewClassStaticBlockDeclaration(modifiers *ModifierList, body *BlockNode) *Node { + data := &ClassStaticBlockDeclaration{} + data.modifiers = modifiers + data.Body = body + return f.newNode(KindClassStaticBlockDeclaration, data) +} + +func (f *NodeFactory) UpdateClassStaticBlockDeclaration(node *ClassStaticBlockDeclaration, modifiers *ModifierList, body *BlockNode) *Node { + if modifiers != node.modifiers || body != node.Body { + return updateNode(f.NewClassStaticBlockDeclaration(modifiers, body), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *ClassStaticBlockDeclaration) ForEachChild(v Visitor) bool { + return visitModifiers(v, node.modifiers) || visit(v, node.Body) +} + +func (node *ClassStaticBlockDeclaration) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateClassStaticBlockDeclaration(node, v.visitModifiers(node.modifiers), v.visitNode(node.Body)) +} + +func (node *ClassStaticBlockDeclaration) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewClassStaticBlockDeclaration(node.Modifiers(), node.Body), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsClassStaticBlockDeclaration(node *Node) bool { + return node.Kind == KindClassStaticBlockDeclaration +} + +// ────────────────────────────────────────────────────────────────────── +// OmittedExpression +// ────────────────────────────────────────────────────────────────────── + +type OmittedExpression struct { + ExpressionBase +} + +func (f *NodeFactory) NewOmittedExpression() *Node { + data := &OmittedExpression{} + return f.newNode(KindOmittedExpression, data) +} + +func (node *OmittedExpression) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewOmittedExpression(), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsOmittedExpression(node *Node) bool { + return node.Kind == KindOmittedExpression +} + +// ────────────────────────────────────────────────────────────────────── +// KeywordExpression +// ────────────────────────────────────────────────────────────────────── + +type KeywordExpression struct { + ExpressionBase + FlowNodeBase +} + +func (f *NodeFactory) NewKeywordExpression(kind KeywordExpressionSyntaxKind) *Node { + data := f.keywordExpressionArena.New() + return f.newNode(kind, data) +} + +func (node *KeywordExpression) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewKeywordExpression(node.Kind), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsKeywordExpression(node *Node) bool { + switch node.Kind { + case KindNullKeyword, KindTrueKeyword, KindFalseKeyword, KindThisKeyword, KindSuperKeyword, KindImportKeyword: + return true + } + return false +} + +// ────────────────────────────────────────────────────────────────────── +// StringLiteral +// ────────────────────────────────────────────────────────────────────── + +type StringLiteral struct { + LiteralExpressionBase +} + +func (f *NodeFactory) NewStringLiteral(text string, tokenFlags TokenFlags) *Node { + data := f.stringLiteralArena.New() + data.Text = text + data.TokenFlags = tokenFlags & TokenFlagsStringLiteralFlags + f.textCount++ + return f.newNode(KindStringLiteral, data) +} + +func (node *StringLiteral) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewStringLiteral(node.Text, node.TokenFlags), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsStringLiteral(node *Node) bool { + return node.Kind == KindStringLiteral +} + +// ────────────────────────────────────────────────────────────────────── +// NumericLiteral +// ────────────────────────────────────────────────────────────────────── + +type NumericLiteral struct { + LiteralExpressionBase +} + +func (f *NodeFactory) NewNumericLiteral(text string, tokenFlags TokenFlags) *Node { + data := f.numericLiteralArena.New() + data.Text = text + data.TokenFlags = tokenFlags & TokenFlagsNumericLiteralFlags + f.textCount++ + return f.newNode(KindNumericLiteral, data) +} + +func (node *NumericLiteral) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewNumericLiteral(node.Text, node.TokenFlags), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsNumericLiteral(node *Node) bool { + return node.Kind == KindNumericLiteral +} + +// ────────────────────────────────────────────────────────────────────── +// BigIntLiteral +// ────────────────────────────────────────────────────────────────────── + +type BigIntLiteral struct { + LiteralExpressionBase +} + +func (f *NodeFactory) NewBigIntLiteral(text string, tokenFlags TokenFlags) *Node { + data := &BigIntLiteral{} + data.Text = text + data.TokenFlags = tokenFlags & TokenFlagsNumericLiteralFlags + f.textCount++ + return f.newNode(KindBigIntLiteral, data) +} + +func (node *BigIntLiteral) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewBigIntLiteral(node.Text, node.TokenFlags), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsBigIntLiteral(node *Node) bool { + return node.Kind == KindBigIntLiteral +} + +// ────────────────────────────────────────────────────────────────────── +// RegularExpressionLiteral +// ────────────────────────────────────────────────────────────────────── + +type RegularExpressionLiteral struct { + LiteralExpressionBase +} + +func (f *NodeFactory) NewRegularExpressionLiteral(text string, tokenFlags TokenFlags) *Node { + data := &RegularExpressionLiteral{} + data.Text = text + data.TokenFlags = tokenFlags & TokenFlagsRegularExpressionLiteralFlags + f.textCount++ + return f.newNode(KindRegularExpressionLiteral, data) +} + +func (node *RegularExpressionLiteral) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewRegularExpressionLiteral(node.Text, node.TokenFlags), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsRegularExpressionLiteral(node *Node) bool { + return node.Kind == KindRegularExpressionLiteral +} + +// ────────────────────────────────────────────────────────────────────── +// NoSubstitutionTemplateLiteral +// ────────────────────────────────────────────────────────────────────── + +type NoSubstitutionTemplateLiteral struct { + ExpressionBase + TemplateLiteralLikeNodeBase + DeclarationBase +} + +func (f *NodeFactory) NewNoSubstitutionTemplateLiteral(text string, templateFlags TokenFlags) *Node { + data := &NoSubstitutionTemplateLiteral{} + data.Text = text + data.TemplateFlags = templateFlags & TokenFlagsTemplateLiteralLikeFlags + f.textCount++ + return f.newNode(KindNoSubstitutionTemplateLiteral, data) +} + +func (node *NoSubstitutionTemplateLiteral) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewNoSubstitutionTemplateLiteral(node.Text, node.TemplateFlags), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsNoSubstitutionTemplateLiteral(node *Node) bool { + return node.Kind == KindNoSubstitutionTemplateLiteral +} + +// ────────────────────────────────────────────────────────────────────── +// BinaryExpression +// ────────────────────────────────────────────────────────────────────── + +type BinaryExpression struct { + ExpressionBase + DeclarationBase + ModifiersBase + CompositeBase + Left *Expression + Type *TypeNode // Optional + OperatorToken *BinaryOperatorToken + Right *Expression +} + +func (f *NodeFactory) NewBinaryExpression(modifiers *ModifierList, left *Expression, typeNode *TypeNode, operatorToken *BinaryOperatorToken, right *Expression) *Node { + data := f.binaryExpressionArena.New() + data.modifiers = modifiers + data.Left = left + data.Type = typeNode + data.OperatorToken = operatorToken + data.Right = right + return f.newNode(KindBinaryExpression, data) +} + +func (f *NodeFactory) UpdateBinaryExpression(node *BinaryExpression, modifiers *ModifierList, left *Expression, typeNode *TypeNode, operatorToken *BinaryOperatorToken, right *Expression) *Node { + if modifiers != node.modifiers || left != node.Left || typeNode != node.Type || operatorToken != node.OperatorToken || right != node.Right { + return updateNode(f.NewBinaryExpression(modifiers, left, typeNode, operatorToken, right), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *BinaryExpression) ForEachChild(v Visitor) bool { + return visitModifiers(v, node.modifiers) || + visit(v, node.Left) || + visit(v, node.Type) || + visit(v, node.OperatorToken) || + visit(v, node.Right) +} + +func (node *BinaryExpression) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateBinaryExpression(node, v.visitModifiers(node.modifiers), v.visitNode(node.Left), v.visitNode(node.Type), v.visitNode(node.OperatorToken), v.visitNode(node.Right)) +} + +func (node *BinaryExpression) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewBinaryExpression(node.Modifiers(), node.Left, node.Type, node.OperatorToken, node.Right), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsBinaryExpression(node *Node) bool { + return node.Kind == KindBinaryExpression +} + +// ────────────────────────────────────────────────────────────────────── +// PrefixUnaryExpression +// ────────────────────────────────────────────────────────────────────── + +type PrefixUnaryExpression struct { + UpdateExpressionBase + Operator Kind + Operand *Expression +} + +func (f *NodeFactory) NewPrefixUnaryExpression(operator Kind, operand *Expression) *Node { + data := f.prefixUnaryExpressionArena.New() + data.Operator = operator + data.Operand = operand + return f.newNode(KindPrefixUnaryExpression, data) +} + +func (f *NodeFactory) UpdatePrefixUnaryExpression(node *PrefixUnaryExpression, operator Kind, operand *Expression) *Node { + if operator != node.Operator || operand != node.Operand { + return updateNode(f.NewPrefixUnaryExpression(operator, operand), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *PrefixUnaryExpression) ForEachChild(v Visitor) bool { + return visit(v, node.Operand) +} + +func (node *PrefixUnaryExpression) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdatePrefixUnaryExpression(node, node.Operator, v.visitNode(node.Operand)) +} + +func (node *PrefixUnaryExpression) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewPrefixUnaryExpression(node.Operator, node.Operand), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *PrefixUnaryExpression) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.Operand) +} + +func IsPrefixUnaryExpression(node *Node) bool { + return node.Kind == KindPrefixUnaryExpression +} + +// ────────────────────────────────────────────────────────────────────── +// PostfixUnaryExpression +// ────────────────────────────────────────────────────────────────────── + +type PostfixUnaryExpression struct { + UpdateExpressionBase + Operand *Expression + Operator Kind +} + +func (f *NodeFactory) NewPostfixUnaryExpression(operand *Expression, operator Kind) *Node { + data := &PostfixUnaryExpression{} + data.Operand = operand + data.Operator = operator + return f.newNode(KindPostfixUnaryExpression, data) +} + +func (f *NodeFactory) UpdatePostfixUnaryExpression(node *PostfixUnaryExpression, operand *Expression, operator Kind) *Node { + if operand != node.Operand || operator != node.Operator { + return updateNode(f.NewPostfixUnaryExpression(operand, operator), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *PostfixUnaryExpression) ForEachChild(v Visitor) bool { + return visit(v, node.Operand) +} + +func (node *PostfixUnaryExpression) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdatePostfixUnaryExpression(node, v.visitNode(node.Operand), node.Operator) +} + +func (node *PostfixUnaryExpression) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewPostfixUnaryExpression(node.Operand, node.Operator), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *PostfixUnaryExpression) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.Operand) +} + +func IsPostfixUnaryExpression(node *Node) bool { + return node.Kind == KindPostfixUnaryExpression +} + +// ────────────────────────────────────────────────────────────────────── +// YieldExpression +// ────────────────────────────────────────────────────────────────────── + +type YieldExpression struct { + ExpressionBase + AsteriskToken *AsteriskToken // Optional + Expression *Expression // Optional +} + +func (f *NodeFactory) NewYieldExpression(asteriskToken *AsteriskToken, expression *Expression) *Node { + data := &YieldExpression{} + data.AsteriskToken = asteriskToken + data.Expression = expression + return f.newNode(KindYieldExpression, data) +} + +func (f *NodeFactory) UpdateYieldExpression(node *YieldExpression, asteriskToken *AsteriskToken, expression *Expression) *Node { + if asteriskToken != node.AsteriskToken || expression != node.Expression { + return updateNode(f.NewYieldExpression(asteriskToken, expression), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *YieldExpression) ForEachChild(v Visitor) bool { + return visit(v, node.AsteriskToken) || visit(v, node.Expression) +} + +func (node *YieldExpression) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateYieldExpression(node, v.visitNode(node.AsteriskToken), v.visitNode(node.Expression)) +} + +func (node *YieldExpression) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewYieldExpression(node.AsteriskToken, node.Expression), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsYieldExpression(node *Node) bool { + return node.Kind == KindYieldExpression +} + +// ────────────────────────────────────────────────────────────────────── +// ArrowFunction +// ────────────────────────────────────────────────────────────────────── + +type ArrowFunction struct { + ExpressionBase + DeclarationBase + ModifiersBase + FunctionLikeWithBodyBase + FlowNodeBase + CompositeBase + EqualsGreaterThanToken *EqualsGreaterThanToken +} + +func (f *NodeFactory) NewArrowFunction(modifiers *ModifierList, typeParameters *TypeParameterList, parameters *ParameterList, typeNode *TypeNode, fullSignature *TypeNode, equalsGreaterThanToken *EqualsGreaterThanToken, body *ConciseBody) *Node { + data := &ArrowFunction{} + data.modifiers = modifiers + data.TypeParameters = typeParameters + data.Parameters = parameters + data.Type = typeNode + data.FullSignature = fullSignature + data.EqualsGreaterThanToken = equalsGreaterThanToken + data.Body = body + return f.newNode(KindArrowFunction, data) +} + +func (f *NodeFactory) UpdateArrowFunction(node *ArrowFunction, modifiers *ModifierList, typeParameters *TypeParameterList, parameters *ParameterList, typeNode *TypeNode, fullSignature *TypeNode, equalsGreaterThanToken *EqualsGreaterThanToken, body *ConciseBody) *Node { + if modifiers != node.modifiers || typeParameters != node.TypeParameters || parameters != node.Parameters || typeNode != node.Type || fullSignature != node.FullSignature || equalsGreaterThanToken != node.EqualsGreaterThanToken || body != node.Body { + return updateNode(f.NewArrowFunction(modifiers, typeParameters, parameters, typeNode, fullSignature, equalsGreaterThanToken, body), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *ArrowFunction) ForEachChild(v Visitor) bool { + return visitModifiers(v, node.modifiers) || + visitNodeList(v, node.TypeParameters) || + visitNodeList(v, node.Parameters) || + visit(v, node.Type) || + visit(v, node.FullSignature) || + visit(v, node.EqualsGreaterThanToken) || + visit(v, node.Body) +} + +func (node *ArrowFunction) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateArrowFunction(node, v.visitModifiers(node.modifiers), v.visitNodes(node.TypeParameters), v.visitParameters(node.Parameters), v.visitNode(node.Type), v.visitNode(node.FullSignature), v.visitNode(node.EqualsGreaterThanToken), v.visitFunctionBody(node.Body)) +} + +func (node *ArrowFunction) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewArrowFunction(node.Modifiers(), node.TypeParameters, node.Parameters, node.Type, node.FullSignature, node.EqualsGreaterThanToken, node.Body), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsArrowFunction(node *Node) bool { + return node.Kind == KindArrowFunction +} + +// ────────────────────────────────────────────────────────────────────── +// FunctionExpression +// ────────────────────────────────────────────────────────────────────── + +type FunctionExpression struct { + PrimaryExpressionBase + DeclarationBase + ModifiersBase + FunctionLikeWithBodyBase + FlowNodeBase + CompositeBase + name *IdentifierNode // Optional + ReturnFlowNode *FlowNode +} + +func (f *NodeFactory) NewFunctionExpression(modifiers *ModifierList, asteriskToken *AsteriskToken, name *IdentifierNode, typeParameters *TypeParameterList, parameters *ParameterList, typeNode *TypeNode, fullSignature *TypeNode, body *FunctionBody) *Node { + data := &FunctionExpression{} + data.modifiers = modifiers + data.AsteriskToken = asteriskToken + data.name = name + data.TypeParameters = typeParameters + data.Parameters = parameters + data.Type = typeNode + data.FullSignature = fullSignature + data.Body = body + return f.newNode(KindFunctionExpression, data) +} + +func (f *NodeFactory) UpdateFunctionExpression(node *FunctionExpression, modifiers *ModifierList, asteriskToken *AsteriskToken, name *IdentifierNode, typeParameters *TypeParameterList, parameters *ParameterList, typeNode *TypeNode, fullSignature *TypeNode, body *FunctionBody) *Node { + if modifiers != node.modifiers || asteriskToken != node.AsteriskToken || name != node.name || typeParameters != node.TypeParameters || parameters != node.Parameters || typeNode != node.Type || fullSignature != node.FullSignature || body != node.Body { + return updateNode(f.NewFunctionExpression(modifiers, asteriskToken, name, typeParameters, parameters, typeNode, fullSignature, body), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *FunctionExpression) ForEachChild(v Visitor) bool { + return visitModifiers(v, node.modifiers) || + visit(v, node.AsteriskToken) || + visit(v, node.name) || + visitNodeList(v, node.TypeParameters) || + visitNodeList(v, node.Parameters) || + visit(v, node.Type) || + visit(v, node.FullSignature) || + visit(v, node.Body) +} + +func (node *FunctionExpression) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateFunctionExpression(node, v.visitModifiers(node.modifiers), v.visitNode(node.AsteriskToken), v.visitNode(node.name), v.visitNodes(node.TypeParameters), v.visitParameters(node.Parameters), v.visitNode(node.Type), v.visitNode(node.FullSignature), v.visitFunctionBody(node.Body)) +} + +func (node *FunctionExpression) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewFunctionExpression(node.Modifiers(), node.AsteriskToken, node.name, node.TypeParameters, node.Parameters, node.Type, node.FullSignature, node.Body), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *FunctionExpression) Name() *DeclarationName { + return node.name +} + +func IsFunctionExpression(node *Node) bool { + return node.Kind == KindFunctionExpression +} + +// ────────────────────────────────────────────────────────────────────── +// AsExpression +// ────────────────────────────────────────────────────────────────────── + +type AsExpression struct { + ExpressionBase + Expression *Expression + Type *TypeNode +} + +func (f *NodeFactory) NewAsExpression(expression *Expression, typeNode *TypeNode) *Node { + data := &AsExpression{} + data.Expression = expression + data.Type = typeNode + return f.newNode(KindAsExpression, data) +} + +func (f *NodeFactory) UpdateAsExpression(node *AsExpression, expression *Expression, typeNode *TypeNode) *Node { + if expression != node.Expression || typeNode != node.Type { + return updateNode(f.NewAsExpression(expression, typeNode), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *AsExpression) ForEachChild(v Visitor) bool { + return visit(v, node.Expression) || visit(v, node.Type) +} + +func (node *AsExpression) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateAsExpression(node, v.visitNode(node.Expression), v.visitNode(node.Type)) +} + +func (node *AsExpression) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewAsExpression(node.Expression, node.Type), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsAsExpression(node *Node) bool { + return node.Kind == KindAsExpression +} + +// ────────────────────────────────────────────────────────────────────── +// SatisfiesExpression +// ────────────────────────────────────────────────────────────────────── + +type SatisfiesExpression struct { + ExpressionBase + Expression *Expression + Type *TypeNode +} + +func (f *NodeFactory) NewSatisfiesExpression(expression *Expression, typeNode *TypeNode) *Node { + data := &SatisfiesExpression{} + data.Expression = expression + data.Type = typeNode + return f.newNode(KindSatisfiesExpression, data) +} + +func (f *NodeFactory) UpdateSatisfiesExpression(node *SatisfiesExpression, expression *Expression, typeNode *TypeNode) *Node { + if expression != node.Expression || typeNode != node.Type { + return updateNode(f.NewSatisfiesExpression(expression, typeNode), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *SatisfiesExpression) ForEachChild(v Visitor) bool { + return visit(v, node.Expression) || visit(v, node.Type) +} + +func (node *SatisfiesExpression) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateSatisfiesExpression(node, v.visitNode(node.Expression), v.visitNode(node.Type)) +} + +func (node *SatisfiesExpression) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewSatisfiesExpression(node.Expression, node.Type), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsSatisfiesExpression(node *Node) bool { + return node.Kind == KindSatisfiesExpression +} + +// ────────────────────────────────────────────────────────────────────── +// ConditionalExpression +// ────────────────────────────────────────────────────────────────────── + +type ConditionalExpression struct { + ExpressionBase + CompositeBase + Condition *Expression + QuestionToken *QuestionToken + WhenTrue *Expression + ColonToken *ColonToken + WhenFalse *Expression +} + +func (f *NodeFactory) NewConditionalExpression(condition *Expression, questionToken *QuestionToken, whenTrue *Expression, colonToken *ColonToken, whenFalse *Expression) *Node { + data := f.conditionalExpressionArena.New() + data.Condition = condition + data.QuestionToken = questionToken + data.WhenTrue = whenTrue + data.ColonToken = colonToken + data.WhenFalse = whenFalse + return f.newNode(KindConditionalExpression, data) +} + +func (f *NodeFactory) UpdateConditionalExpression(node *ConditionalExpression, condition *Expression, questionToken *QuestionToken, whenTrue *Expression, colonToken *ColonToken, whenFalse *Expression) *Node { + if condition != node.Condition || questionToken != node.QuestionToken || whenTrue != node.WhenTrue || colonToken != node.ColonToken || whenFalse != node.WhenFalse { + return updateNode(f.NewConditionalExpression(condition, questionToken, whenTrue, colonToken, whenFalse), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *ConditionalExpression) ForEachChild(v Visitor) bool { + return visit(v, node.Condition) || + visit(v, node.QuestionToken) || + visit(v, node.WhenTrue) || + visit(v, node.ColonToken) || + visit(v, node.WhenFalse) +} + +func (node *ConditionalExpression) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateConditionalExpression(node, v.visitNode(node.Condition), v.visitNode(node.QuestionToken), v.visitNode(node.WhenTrue), v.visitNode(node.ColonToken), v.visitNode(node.WhenFalse)) +} + +func (node *ConditionalExpression) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewConditionalExpression(node.Condition, node.QuestionToken, node.WhenTrue, node.ColonToken, node.WhenFalse), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *ConditionalExpression) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.Condition) | + propagateSubtreeFacts(node.QuestionToken) | + propagateSubtreeFacts(node.WhenTrue) | + propagateSubtreeFacts(node.ColonToken) | + propagateSubtreeFacts(node.WhenFalse) +} + +func IsConditionalExpression(node *Node) bool { + return node.Kind == KindConditionalExpression +} + +// ────────────────────────────────────────────────────────────────────── +// PropertyAccessExpression +// ────────────────────────────────────────────────────────────────────── + +type PropertyAccessExpression struct { + MemberExpressionBase + FlowNodeBase + CompositeBase + Expression *Expression + QuestionDotToken *QuestionDotToken // Optional + name *MemberName +} + +func (f *NodeFactory) NewPropertyAccessExpression(expression *Expression, questionDotToken *QuestionDotToken, name *MemberName, flags NodeFlags) *Node { + data := f.propertyAccessExpressionArena.New() + data.Expression = expression + data.QuestionDotToken = questionDotToken + data.name = name + node := f.newNode(KindPropertyAccessExpression, data) + node.Flags |= flags & NodeFlagsOptionalChain + return node +} + +func (f *NodeFactory) UpdatePropertyAccessExpression(node *PropertyAccessExpression, expression *Expression, questionDotToken *QuestionDotToken, name *MemberName, flags NodeFlags) *Node { + if expression != node.Expression || questionDotToken != node.QuestionDotToken || name != node.name || flags != node.Flags { + return updateNode(f.NewPropertyAccessExpression(expression, questionDotToken, name, flags), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *PropertyAccessExpression) ForEachChild(v Visitor) bool { + return visit(v, node.Expression) || visit(v, node.QuestionDotToken) || visit(v, node.name) +} + +func (node *PropertyAccessExpression) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdatePropertyAccessExpression(node, v.visitNode(node.Expression), v.visitNode(node.QuestionDotToken), v.visitNode(node.name), node.Flags) +} + +func (node *PropertyAccessExpression) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewPropertyAccessExpression(node.Expression, node.QuestionDotToken, node.name, node.Flags), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *PropertyAccessExpression) Name() *DeclarationName { + return node.name +} + +func IsPropertyAccessExpression(node *Node) bool { + return node.Kind == KindPropertyAccessExpression +} + +// ────────────────────────────────────────────────────────────────────── +// ElementAccessExpression +// ────────────────────────────────────────────────────────────────────── + +type ElementAccessExpression struct { + MemberExpressionBase + FlowNodeBase + CompositeBase + Expression *Expression + QuestionDotToken *QuestionDotToken // Optional + ArgumentExpression *Expression +} + +func (f *NodeFactory) NewElementAccessExpression(expression *Expression, questionDotToken *QuestionDotToken, argumentExpression *Expression, flags NodeFlags) *Node { + data := f.elementAccessExpressionArena.New() + data.Expression = expression + data.QuestionDotToken = questionDotToken + data.ArgumentExpression = argumentExpression + node := f.newNode(KindElementAccessExpression, data) + node.Flags |= flags & NodeFlagsOptionalChain + return node +} + +func (f *NodeFactory) UpdateElementAccessExpression(node *ElementAccessExpression, expression *Expression, questionDotToken *QuestionDotToken, argumentExpression *Expression, flags NodeFlags) *Node { + if expression != node.Expression || questionDotToken != node.QuestionDotToken || argumentExpression != node.ArgumentExpression || flags != node.Flags { + return updateNode(f.NewElementAccessExpression(expression, questionDotToken, argumentExpression, flags), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *ElementAccessExpression) ForEachChild(v Visitor) bool { + return visit(v, node.Expression) || visit(v, node.QuestionDotToken) || visit(v, node.ArgumentExpression) +} + +func (node *ElementAccessExpression) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateElementAccessExpression(node, v.visitNode(node.Expression), v.visitNode(node.QuestionDotToken), v.visitNode(node.ArgumentExpression), node.Flags) +} + +func (node *ElementAccessExpression) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewElementAccessExpression(node.Expression, node.QuestionDotToken, node.ArgumentExpression, node.Flags), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *ElementAccessExpression) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.Expression) | + propagateSubtreeFacts(node.QuestionDotToken) | + propagateSubtreeFacts(node.ArgumentExpression) +} + +func IsElementAccessExpression(node *Node) bool { + return node.Kind == KindElementAccessExpression +} + +// ────────────────────────────────────────────────────────────────────── +// CallExpression +// ────────────────────────────────────────────────────────────────────── + +type CallExpression struct { + LeftHandSideExpressionBase + DeclarationBase + CompositeBase + Expression *Expression + QuestionDotToken *QuestionDotToken // Optional + TypeArguments *TypeList // Optional + Arguments *ElementList +} + +func (f *NodeFactory) NewCallExpression(expression *Expression, questionDotToken *QuestionDotToken, typeArguments *TypeList, arguments *ElementList, flags NodeFlags) *Node { + data := f.callExpressionArena.New() + data.Expression = expression + data.QuestionDotToken = questionDotToken + data.TypeArguments = typeArguments + data.Arguments = arguments + node := f.newNode(KindCallExpression, data) + node.Flags |= flags & NodeFlagsOptionalChain + return node +} + +func (f *NodeFactory) UpdateCallExpression(node *CallExpression, expression *Expression, questionDotToken *QuestionDotToken, typeArguments *TypeList, arguments *ElementList, flags NodeFlags) *Node { + if expression != node.Expression || questionDotToken != node.QuestionDotToken || typeArguments != node.TypeArguments || arguments != node.Arguments || flags != node.Flags { + return updateNode(f.NewCallExpression(expression, questionDotToken, typeArguments, arguments, flags), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *CallExpression) ForEachChild(v Visitor) bool { + return visit(v, node.Expression) || + visit(v, node.QuestionDotToken) || + visitNodeList(v, node.TypeArguments) || + visitNodeList(v, node.Arguments) +} + +func (node *CallExpression) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateCallExpression(node, v.visitNode(node.Expression), v.visitNode(node.QuestionDotToken), v.visitNodes(node.TypeArguments), v.visitNodes(node.Arguments), node.Flags) +} + +func (node *CallExpression) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewCallExpression(node.Expression, node.QuestionDotToken, node.TypeArguments, node.Arguments, node.Flags), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsCallExpression(node *Node) bool { + return node.Kind == KindCallExpression +} + +// ────────────────────────────────────────────────────────────────────── +// NewExpression +// ────────────────────────────────────────────────────────────────────── + +type NewExpression struct { + PrimaryExpressionBase + CompositeBase + Expression *Expression + TypeArguments *TypeList // Optional + Arguments *ElementList // Optional +} + +func (f *NodeFactory) NewNewExpression(expression *Expression, typeArguments *TypeList, arguments *ElementList) *Node { + data := &NewExpression{} + data.Expression = expression + data.TypeArguments = typeArguments + data.Arguments = arguments + return f.newNode(KindNewExpression, data) +} + +func (f *NodeFactory) UpdateNewExpression(node *NewExpression, expression *Expression, typeArguments *TypeList, arguments *ElementList) *Node { + if expression != node.Expression || typeArguments != node.TypeArguments || arguments != node.Arguments { + return updateNode(f.NewNewExpression(expression, typeArguments, arguments), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *NewExpression) ForEachChild(v Visitor) bool { + return visit(v, node.Expression) || visitNodeList(v, node.TypeArguments) || visitNodeList(v, node.Arguments) +} + +func (node *NewExpression) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateNewExpression(node, v.visitNode(node.Expression), v.visitNodes(node.TypeArguments), v.visitNodes(node.Arguments)) +} + +func (node *NewExpression) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewNewExpression(node.Expression, node.TypeArguments, node.Arguments), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsNewExpression(node *Node) bool { + return node.Kind == KindNewExpression +} + +// ────────────────────────────────────────────────────────────────────── +// MetaProperty +// ────────────────────────────────────────────────────────────────────── + +type MetaProperty struct { + PrimaryExpressionBase + FlowNodeBase + CompositeBase + KeywordToken Kind + name *IdentifierNode +} + +func (f *NodeFactory) NewMetaProperty(keywordToken Kind, name *IdentifierNode) *Node { + data := &MetaProperty{} + data.KeywordToken = keywordToken + data.name = name + return f.newNode(KindMetaProperty, data) +} + +func (f *NodeFactory) UpdateMetaProperty(node *MetaProperty, keywordToken Kind, name *IdentifierNode) *Node { + if keywordToken != node.KeywordToken || name != node.name { + return updateNode(f.NewMetaProperty(keywordToken, name), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *MetaProperty) ForEachChild(v Visitor) bool { + return visit(v, node.name) +} + +func (node *MetaProperty) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateMetaProperty(node, node.KeywordToken, v.visitNode(node.name)) +} + +func (node *MetaProperty) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewMetaProperty(node.KeywordToken, node.name), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *MetaProperty) Name() *DeclarationName { + return node.name +} + +func IsMetaProperty(node *Node) bool { + return node.Kind == KindMetaProperty +} + +// ────────────────────────────────────────────────────────────────────── +// NonNullExpression +// ────────────────────────────────────────────────────────────────────── + +type NonNullExpression struct { + LeftHandSideExpressionBase + Expression *Expression +} + +func (f *NodeFactory) NewNonNullExpression(expression *Expression, flags NodeFlags) *Node { + data := &NonNullExpression{} + data.Expression = expression + node := f.newNode(KindNonNullExpression, data) + node.Flags |= flags & NodeFlagsOptionalChain + return node +} + +func (f *NodeFactory) UpdateNonNullExpression(node *NonNullExpression, expression *Expression, flags NodeFlags) *Node { + if expression != node.Expression || flags != node.Flags { + return updateNode(f.NewNonNullExpression(expression, flags), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *NonNullExpression) ForEachChild(v Visitor) bool { + return visit(v, node.Expression) +} + +func (node *NonNullExpression) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateNonNullExpression(node, v.visitNode(node.Expression), node.Flags) +} + +func (node *NonNullExpression) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewNonNullExpression(node.Expression, node.Flags), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsNonNullExpression(node *Node) bool { + return node.Kind == KindNonNullExpression +} + +// ────────────────────────────────────────────────────────────────────── +// SpreadElement +// ────────────────────────────────────────────────────────────────────── + +type SpreadElement struct { + ExpressionBase + Expression *Expression +} + +func (f *NodeFactory) NewSpreadElement(expression *Expression) *Node { + data := &SpreadElement{} + data.Expression = expression + return f.newNode(KindSpreadElement, data) +} + +func (f *NodeFactory) UpdateSpreadElement(node *SpreadElement, expression *Expression) *Node { + if expression != node.Expression { + return updateNode(f.NewSpreadElement(expression), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *SpreadElement) ForEachChild(v Visitor) bool { + return visit(v, node.Expression) +} + +func (node *SpreadElement) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateSpreadElement(node, v.visitNode(node.Expression)) +} + +func (node *SpreadElement) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewSpreadElement(node.Expression), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsSpreadElement(node *Node) bool { + return node.Kind == KindSpreadElement +} + +// ────────────────────────────────────────────────────────────────────── +// TemplateExpression +// ────────────────────────────────────────────────────────────────────── + +type TemplateExpression struct { + PrimaryExpressionBase + CompositeBase + Head *TemplateHeadNode + TemplateSpans *TemplateSpanList +} + +func (f *NodeFactory) NewTemplateExpression(head *TemplateHeadNode, templateSpans *TemplateSpanList) *Node { + data := &TemplateExpression{} + data.Head = head + data.TemplateSpans = templateSpans + return f.newNode(KindTemplateExpression, data) +} + +func (f *NodeFactory) UpdateTemplateExpression(node *TemplateExpression, head *TemplateHeadNode, templateSpans *TemplateSpanList) *Node { + if head != node.Head || templateSpans != node.TemplateSpans { + return updateNode(f.NewTemplateExpression(head, templateSpans), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *TemplateExpression) ForEachChild(v Visitor) bool { + return visit(v, node.Head) || visitNodeList(v, node.TemplateSpans) +} + +func (node *TemplateExpression) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateTemplateExpression(node, v.visitNode(node.Head), v.visitNodes(node.TemplateSpans)) +} + +func (node *TemplateExpression) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewTemplateExpression(node.Head, node.TemplateSpans), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *TemplateExpression) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.Head) | + propagateNodeListSubtreeFacts(node.TemplateSpans, propagateSubtreeFacts) +} + +func IsTemplateExpression(node *Node) bool { + return node.Kind == KindTemplateExpression +} + +// ────────────────────────────────────────────────────────────────────── +// TemplateSpan +// ────────────────────────────────────────────────────────────────────── + +type TemplateSpan struct { + NodeBase + Expression *Expression + Literal *TemplateMiddleOrTail +} + +func (f *NodeFactory) NewTemplateSpan(expression *Expression, literal *TemplateMiddleOrTail) *Node { + data := &TemplateSpan{} + data.Expression = expression + data.Literal = literal + return f.newNode(KindTemplateSpan, data) +} + +func (f *NodeFactory) UpdateTemplateSpan(node *TemplateSpan, expression *Expression, literal *TemplateMiddleOrTail) *Node { + if expression != node.Expression || literal != node.Literal { + return updateNode(f.NewTemplateSpan(expression, literal), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *TemplateSpan) ForEachChild(v Visitor) bool { + return visit(v, node.Expression) || visit(v, node.Literal) +} + +func (node *TemplateSpan) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateTemplateSpan(node, v.visitNode(node.Expression), v.visitNode(node.Literal)) +} + +func (node *TemplateSpan) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewTemplateSpan(node.Expression, node.Literal), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *TemplateSpan) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.Expression) | + propagateSubtreeFacts(node.Literal) +} + +func IsTemplateSpan(node *Node) bool { + return node.Kind == KindTemplateSpan +} + +// ────────────────────────────────────────────────────────────────────── +// TaggedTemplateExpression +// ────────────────────────────────────────────────────────────────────── + +type TaggedTemplateExpression struct { + MemberExpressionBase + CompositeBase + Tag *Expression + QuestionDotToken *QuestionDotToken + TypeArguments *TypeList // Optional + Template *TemplateLiteral +} + +func (f *NodeFactory) NewTaggedTemplateExpression(tag *Expression, questionDotToken *QuestionDotToken, typeArguments *TypeList, template *TemplateLiteral, flags NodeFlags) *Node { + data := &TaggedTemplateExpression{} + data.Tag = tag + data.QuestionDotToken = questionDotToken + data.TypeArguments = typeArguments + data.Template = template + node := f.newNode(KindTaggedTemplateExpression, data) + node.Flags |= flags & NodeFlagsOptionalChain + return node +} + +func (f *NodeFactory) UpdateTaggedTemplateExpression(node *TaggedTemplateExpression, tag *Expression, questionDotToken *QuestionDotToken, typeArguments *TypeList, template *TemplateLiteral, flags NodeFlags) *Node { + if tag != node.Tag || questionDotToken != node.QuestionDotToken || typeArguments != node.TypeArguments || template != node.Template || flags != node.Flags { + return updateNode(f.NewTaggedTemplateExpression(tag, questionDotToken, typeArguments, template, flags), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *TaggedTemplateExpression) ForEachChild(v Visitor) bool { + return visit(v, node.Tag) || + visit(v, node.QuestionDotToken) || + visitNodeList(v, node.TypeArguments) || + visit(v, node.Template) +} + +func (node *TaggedTemplateExpression) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateTaggedTemplateExpression(node, v.visitNode(node.Tag), v.visitNode(node.QuestionDotToken), v.visitNodes(node.TypeArguments), v.visitNode(node.Template), node.Flags) +} + +func (node *TaggedTemplateExpression) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewTaggedTemplateExpression(node.Tag, node.QuestionDotToken, node.TypeArguments, node.Template, node.Flags), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsTaggedTemplateExpression(node *Node) bool { + return node.Kind == KindTaggedTemplateExpression +} + +// ────────────────────────────────────────────────────────────────────── +// ParenthesizedExpression +// ────────────────────────────────────────────────────────────────────── + +type ParenthesizedExpression struct { + PrimaryExpressionBase + Expression *Expression +} + +func (f *NodeFactory) NewParenthesizedExpression(expression *Expression) *Node { + data := f.parenthesizedExpressionArena.New() + data.Expression = expression + return f.newNode(KindParenthesizedExpression, data) +} + +func (f *NodeFactory) UpdateParenthesizedExpression(node *ParenthesizedExpression, expression *Expression) *Node { + if expression != node.Expression { + return updateNode(f.NewParenthesizedExpression(expression), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *ParenthesizedExpression) ForEachChild(v Visitor) bool { + return visit(v, node.Expression) +} + +func (node *ParenthesizedExpression) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateParenthesizedExpression(node, v.visitNode(node.Expression)) +} + +func (node *ParenthesizedExpression) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewParenthesizedExpression(node.Expression), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *ParenthesizedExpression) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.Expression) +} + +func IsParenthesizedExpression(node *Node) bool { + return node.Kind == KindParenthesizedExpression +} + +// ────────────────────────────────────────────────────────────────────── +// ArrayLiteralExpression +// ────────────────────────────────────────────────────────────────────── + +type ArrayLiteralExpression struct { + PrimaryExpressionBase + CompositeBase + Elements *ElementList + MultiLine bool +} + +func (f *NodeFactory) NewArrayLiteralExpression(elements *ElementList, multiLine bool) *Node { + data := &ArrayLiteralExpression{} + data.Elements = elements + data.MultiLine = multiLine + return f.newNode(KindArrayLiteralExpression, data) +} + +func (f *NodeFactory) UpdateArrayLiteralExpression(node *ArrayLiteralExpression, elements *ElementList, multiLine bool) *Node { + if elements != node.Elements || multiLine != node.MultiLine { + return updateNode(f.NewArrayLiteralExpression(elements, multiLine), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *ArrayLiteralExpression) ForEachChild(v Visitor) bool { + return visitNodeList(v, node.Elements) +} + +func (node *ArrayLiteralExpression) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateArrayLiteralExpression(node, v.visitNodes(node.Elements), node.MultiLine) +} + +func (node *ArrayLiteralExpression) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewArrayLiteralExpression(node.Elements, node.MultiLine), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *ArrayLiteralExpression) computeSubtreeFacts() SubtreeFacts { + return propagateNodeListSubtreeFacts(node.Elements, propagateSubtreeFacts) +} + +func IsArrayLiteralExpression(node *Node) bool { + return node.Kind == KindArrayLiteralExpression +} + +// ────────────────────────────────────────────────────────────────────── +// ObjectLiteralExpression +// ────────────────────────────────────────────────────────────────────── + +type ObjectLiteralExpression struct { + PrimaryExpressionBase + DeclarationBase + CompositeBase + Properties *NodeList + MultiLine bool +} + +func (f *NodeFactory) NewObjectLiteralExpression(properties *NodeList, multiLine bool) *Node { + data := &ObjectLiteralExpression{} + data.Properties = properties + data.MultiLine = multiLine + return f.newNode(KindObjectLiteralExpression, data) +} + +func (f *NodeFactory) UpdateObjectLiteralExpression(node *ObjectLiteralExpression, properties *NodeList, multiLine bool) *Node { + if properties != node.Properties || multiLine != node.MultiLine { + return updateNode(f.NewObjectLiteralExpression(properties, multiLine), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *ObjectLiteralExpression) ForEachChild(v Visitor) bool { + return visitNodeList(v, node.Properties) +} + +func (node *ObjectLiteralExpression) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateObjectLiteralExpression(node, v.visitNodes(node.Properties), node.MultiLine) +} + +func (node *ObjectLiteralExpression) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewObjectLiteralExpression(node.Properties, node.MultiLine), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *ObjectLiteralExpression) computeSubtreeFacts() SubtreeFacts { + return propagateNodeListSubtreeFacts(node.Properties, propagateSubtreeFacts) +} + +func IsObjectLiteralExpression(node *Node) bool { + return node.Kind == KindObjectLiteralExpression +} + +// ────────────────────────────────────────────────────────────────────── +// SpreadAssignment +// ────────────────────────────────────────────────────────────────────── + +type SpreadAssignment struct { + NodeBase + DeclarationBase + ObjectLiteralElementBase + Expression *Expression +} + +func (f *NodeFactory) NewSpreadAssignment(expression *Expression) *Node { + data := &SpreadAssignment{} + data.Expression = expression + return f.newNode(KindSpreadAssignment, data) +} + +func (f *NodeFactory) UpdateSpreadAssignment(node *SpreadAssignment, expression *Expression) *Node { + if expression != node.Expression { + return updateNode(f.NewSpreadAssignment(expression), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *SpreadAssignment) ForEachChild(v Visitor) bool { + return visit(v, node.Expression) +} + +func (node *SpreadAssignment) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateSpreadAssignment(node, v.visitNode(node.Expression)) +} + +func (node *SpreadAssignment) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewSpreadAssignment(node.Expression), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsSpreadAssignment(node *Node) bool { + return node.Kind == KindSpreadAssignment +} + +// ────────────────────────────────────────────────────────────────────── +// PropertyAssignment +// ────────────────────────────────────────────────────────────────────── + +type PropertyAssignment struct { + NodeBase + NamedMemberBase + ObjectLiteralElementBase + CompositeBase + Type *TypeNode + Initializer *Expression +} + +func (f *NodeFactory) NewPropertyAssignment(modifiers *ModifierList, name *PropertyName, postfixToken *TokenNode, typeNode *TypeNode, initializer *Expression) *Node { + data := f.propertyAssignmentArena.New() + data.modifiers = modifiers + data.name = name + data.PostfixToken = postfixToken + data.Type = typeNode + data.Initializer = initializer + return f.newNode(KindPropertyAssignment, data) +} + +func (f *NodeFactory) UpdatePropertyAssignment(node *PropertyAssignment, modifiers *ModifierList, name *PropertyName, postfixToken *TokenNode, typeNode *TypeNode, initializer *Expression) *Node { + if modifiers != node.modifiers || name != node.name || postfixToken != node.PostfixToken || typeNode != node.Type || initializer != node.Initializer { + return updateNode(f.NewPropertyAssignment(modifiers, name, postfixToken, typeNode, initializer), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *PropertyAssignment) ForEachChild(v Visitor) bool { + return visitModifiers(v, node.modifiers) || + visit(v, node.name) || + visit(v, node.PostfixToken) || + visit(v, node.Type) || + visit(v, node.Initializer) +} + +func (node *PropertyAssignment) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdatePropertyAssignment(node, v.visitModifiers(node.modifiers), v.visitNode(node.name), v.visitNode(node.PostfixToken), v.visitNode(node.Type), v.visitNode(node.Initializer)) +} + +func (node *PropertyAssignment) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewPropertyAssignment(node.Modifiers(), node.name, node.PostfixToken, node.Type, node.Initializer), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *PropertyAssignment) Name() *DeclarationName { + return node.name +} + +func IsPropertyAssignment(node *Node) bool { + return node.Kind == KindPropertyAssignment +} + +// ────────────────────────────────────────────────────────────────────── +// ShorthandPropertyAssignment +// ────────────────────────────────────────────────────────────────────── + +type ShorthandPropertyAssignment struct { + NodeBase + NamedMemberBase + ObjectLiteralElementBase + CompositeBase + Type *TypeNode + EqualsToken *EqualsToken // Optional + ObjectAssignmentInitializer *Expression // Optional +} + +func (f *NodeFactory) NewShorthandPropertyAssignment(modifiers *ModifierList, name *PropertyName, postfixToken *TokenNode, typeNode *TypeNode, equalsToken *EqualsToken, objectAssignmentInitializer *Expression) *Node { + data := &ShorthandPropertyAssignment{} + data.modifiers = modifiers + data.name = name + data.PostfixToken = postfixToken + data.Type = typeNode + data.EqualsToken = equalsToken + data.ObjectAssignmentInitializer = objectAssignmentInitializer + return f.newNode(KindShorthandPropertyAssignment, data) +} + +func (f *NodeFactory) UpdateShorthandPropertyAssignment(node *ShorthandPropertyAssignment, modifiers *ModifierList, name *PropertyName, postfixToken *TokenNode, typeNode *TypeNode, equalsToken *EqualsToken, objectAssignmentInitializer *Expression) *Node { + if modifiers != node.modifiers || name != node.name || postfixToken != node.PostfixToken || typeNode != node.Type || equalsToken != node.EqualsToken || objectAssignmentInitializer != node.ObjectAssignmentInitializer { + return updateNode(f.NewShorthandPropertyAssignment(modifiers, name, postfixToken, typeNode, equalsToken, objectAssignmentInitializer), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *ShorthandPropertyAssignment) ForEachChild(v Visitor) bool { + return visitModifiers(v, node.modifiers) || + visit(v, node.name) || + visit(v, node.PostfixToken) || + visit(v, node.Type) || + visit(v, node.EqualsToken) || + visit(v, node.ObjectAssignmentInitializer) +} + +func (node *ShorthandPropertyAssignment) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateShorthandPropertyAssignment(node, v.visitModifiers(node.modifiers), v.visitNode(node.name), v.visitNode(node.PostfixToken), v.visitNode(node.Type), v.visitNode(node.EqualsToken), v.visitNode(node.ObjectAssignmentInitializer)) +} + +func (node *ShorthandPropertyAssignment) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewShorthandPropertyAssignment(node.Modifiers(), node.name, node.PostfixToken, node.Type, node.EqualsToken, node.ObjectAssignmentInitializer), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *ShorthandPropertyAssignment) Name() *DeclarationName { + return node.name +} + +func IsShorthandPropertyAssignment(node *Node) bool { + return node.Kind == KindShorthandPropertyAssignment +} + +// ────────────────────────────────────────────────────────────────────── +// DeleteExpression +// ────────────────────────────────────────────────────────────────────── + +type DeleteExpression struct { + UnaryExpressionBase + Expression *Expression +} + +func (f *NodeFactory) NewDeleteExpression(expression *Expression) *Node { + data := &DeleteExpression{} + data.Expression = expression + return f.newNode(KindDeleteExpression, data) +} + +func (f *NodeFactory) UpdateDeleteExpression(node *DeleteExpression, expression *Expression) *Node { + if expression != node.Expression { + return updateNode(f.NewDeleteExpression(expression), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *DeleteExpression) ForEachChild(v Visitor) bool { + return visit(v, node.Expression) +} + +func (node *DeleteExpression) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateDeleteExpression(node, v.visitNode(node.Expression)) +} + +func (node *DeleteExpression) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewDeleteExpression(node.Expression), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *DeleteExpression) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.Expression) +} + +func IsDeleteExpression(node *Node) bool { + return node.Kind == KindDeleteExpression +} + +// ────────────────────────────────────────────────────────────────────── +// TypeOfExpression +// ────────────────────────────────────────────────────────────────────── + +type TypeOfExpression struct { + UnaryExpressionBase + Expression *Expression +} + +func (f *NodeFactory) NewTypeOfExpression(expression *Expression) *Node { + data := &TypeOfExpression{} + data.Expression = expression + return f.newNode(KindTypeOfExpression, data) +} + +func (f *NodeFactory) UpdateTypeOfExpression(node *TypeOfExpression, expression *Expression) *Node { + if expression != node.Expression { + return updateNode(f.NewTypeOfExpression(expression), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *TypeOfExpression) ForEachChild(v Visitor) bool { + return visit(v, node.Expression) +} + +func (node *TypeOfExpression) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateTypeOfExpression(node, v.visitNode(node.Expression)) +} + +func (node *TypeOfExpression) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewTypeOfExpression(node.Expression), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *TypeOfExpression) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.Expression) +} + +func IsTypeOfExpression(node *Node) bool { + return node.Kind == KindTypeOfExpression +} + +// ────────────────────────────────────────────────────────────────────── +// VoidExpression +// ────────────────────────────────────────────────────────────────────── + +type VoidExpression struct { + UnaryExpressionBase + Expression *Expression +} + +func (f *NodeFactory) NewVoidExpression(expression *Expression) *Node { + data := &VoidExpression{} + data.Expression = expression + return f.newNode(KindVoidExpression, data) +} + +func (f *NodeFactory) UpdateVoidExpression(node *VoidExpression, expression *Expression) *Node { + if expression != node.Expression { + return updateNode(f.NewVoidExpression(expression), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *VoidExpression) ForEachChild(v Visitor) bool { + return visit(v, node.Expression) +} + +func (node *VoidExpression) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateVoidExpression(node, v.visitNode(node.Expression)) +} + +func (node *VoidExpression) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewVoidExpression(node.Expression), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *VoidExpression) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.Expression) +} + +func IsVoidExpression(node *Node) bool { + return node.Kind == KindVoidExpression +} + +// ────────────────────────────────────────────────────────────────────── +// AwaitExpression +// ────────────────────────────────────────────────────────────────────── + +type AwaitExpression struct { + UnaryExpressionBase + Expression *Expression +} + +func (f *NodeFactory) NewAwaitExpression(expression *Expression) *Node { + data := &AwaitExpression{} + data.Expression = expression + return f.newNode(KindAwaitExpression, data) +} + +func (f *NodeFactory) UpdateAwaitExpression(node *AwaitExpression, expression *Expression) *Node { + if expression != node.Expression { + return updateNode(f.NewAwaitExpression(expression), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *AwaitExpression) ForEachChild(v Visitor) bool { + return visit(v, node.Expression) +} + +func (node *AwaitExpression) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateAwaitExpression(node, v.visitNode(node.Expression)) +} + +func (node *AwaitExpression) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewAwaitExpression(node.Expression), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsAwaitExpression(node *Node) bool { + return node.Kind == KindAwaitExpression +} + +// ────────────────────────────────────────────────────────────────────── +// TypeAssertion +// ────────────────────────────────────────────────────────────────────── + +type TypeAssertion struct { + UnaryExpressionBase + Type *TypeNode + Expression *Expression +} + +func (f *NodeFactory) NewTypeAssertion(typeNode *TypeNode, expression *Expression) *Node { + data := &TypeAssertion{} + data.Type = typeNode + data.Expression = expression + return f.newNode(KindTypeAssertionExpression, data) +} + +func (f *NodeFactory) UpdateTypeAssertion(node *TypeAssertion, typeNode *TypeNode, expression *Expression) *Node { + if typeNode != node.Type || expression != node.Expression { + return updateNode(f.NewTypeAssertion(typeNode, expression), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *TypeAssertion) ForEachChild(v Visitor) bool { + return visit(v, node.Type) || visit(v, node.Expression) +} + +func (node *TypeAssertion) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateTypeAssertion(node, v.visitNode(node.Type), v.visitNode(node.Expression)) +} + +func (node *TypeAssertion) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewTypeAssertion(node.Type, node.Expression), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsTypeAssertion(node *Node) bool { + return node.Kind == KindTypeAssertionExpression +} + +// ────────────────────────────────────────────────────────────────────── +// KeywordTypeNode +// ────────────────────────────────────────────────────────────────────── + +type KeywordTypeNode struct { + TypeNodeBase +} + +func (f *NodeFactory) NewKeywordTypeNode(kind KeywordTypeSyntaxKind) *Node { + data := f.keywordTypeNodeArena.New() + return f.newNode(kind, data) +} + +func (node *KeywordTypeNode) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewKeywordTypeNode(node.Kind), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsKeywordTypeNode(node *Node) bool { + switch node.Kind { + case KindAnyKeyword, KindBigIntKeyword, KindBooleanKeyword, KindIntrinsicKeyword, KindNeverKeyword, KindNumberKeyword, KindObjectKeyword, KindStringKeyword, KindSymbolKeyword, KindUndefinedKeyword, KindUnknownKeyword, KindVoidKeyword: + return true + } + return false +} + +// ────────────────────────────────────────────────────────────────────── +// UnionTypeNode +// ────────────────────────────────────────────────────────────────────── + +type UnionTypeNode struct { + TypeNodeBase + UnionOrIntersectionTypeNodeBase +} + +func (f *NodeFactory) NewUnionTypeNode(types *TypeList) *Node { + data := f.unionTypeNodeArena.New() + data.Types = types + return f.newNode(KindUnionType, data) +} + +func (f *NodeFactory) UpdateUnionTypeNode(node *UnionTypeNode, types *TypeList) *Node { + if types != node.Types { + return updateNode(f.NewUnionTypeNode(types), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *UnionTypeNode) ForEachChild(v Visitor) bool { + return visitNodeList(v, node.Types) +} + +func (node *UnionTypeNode) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateUnionTypeNode(node, v.visitNodes(node.Types)) +} + +func (node *UnionTypeNode) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewUnionTypeNode(node.Types), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsUnionTypeNode(node *Node) bool { + return node.Kind == KindUnionType +} + +// ────────────────────────────────────────────────────────────────────── +// IntersectionTypeNode +// ────────────────────────────────────────────────────────────────────── + +type IntersectionTypeNode struct { + TypeNodeBase + UnionOrIntersectionTypeNodeBase +} + +func (f *NodeFactory) NewIntersectionTypeNode(types *TypeList) *Node { + data := f.intersectionTypeNodeArena.New() + data.Types = types + return f.newNode(KindIntersectionType, data) +} + +func (f *NodeFactory) UpdateIntersectionTypeNode(node *IntersectionTypeNode, types *TypeList) *Node { + if types != node.Types { + return updateNode(f.NewIntersectionTypeNode(types), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *IntersectionTypeNode) ForEachChild(v Visitor) bool { + return visitNodeList(v, node.Types) +} + +func (node *IntersectionTypeNode) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateIntersectionTypeNode(node, v.visitNodes(node.Types)) +} + +func (node *IntersectionTypeNode) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewIntersectionTypeNode(node.Types), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsIntersectionTypeNode(node *Node) bool { + return node.Kind == KindIntersectionType +} + +// ────────────────────────────────────────────────────────────────────── +// ConditionalTypeNode +// ────────────────────────────────────────────────────────────────────── + +type ConditionalTypeNode struct { + TypeNodeBase + LocalsContainerBase + CheckType *TypeNode + ExtendsType *TypeNode + TrueType *TypeNode + FalseType *TypeNode +} + +func (f *NodeFactory) NewConditionalTypeNode(checkType *TypeNode, extendsType *TypeNode, trueType *TypeNode, falseType *TypeNode) *Node { + data := &ConditionalTypeNode{} + data.CheckType = checkType + data.ExtendsType = extendsType + data.TrueType = trueType + data.FalseType = falseType + return f.newNode(KindConditionalType, data) +} + +func (f *NodeFactory) UpdateConditionalTypeNode(node *ConditionalTypeNode, checkType *TypeNode, extendsType *TypeNode, trueType *TypeNode, falseType *TypeNode) *Node { + if checkType != node.CheckType || extendsType != node.ExtendsType || trueType != node.TrueType || falseType != node.FalseType { + return updateNode(f.NewConditionalTypeNode(checkType, extendsType, trueType, falseType), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *ConditionalTypeNode) ForEachChild(v Visitor) bool { + return visit(v, node.CheckType) || + visit(v, node.ExtendsType) || + visit(v, node.TrueType) || + visit(v, node.FalseType) +} + +func (node *ConditionalTypeNode) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateConditionalTypeNode(node, v.visitNode(node.CheckType), v.visitNode(node.ExtendsType), v.visitNode(node.TrueType), v.visitNode(node.FalseType)) +} + +func (node *ConditionalTypeNode) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewConditionalTypeNode(node.CheckType, node.ExtendsType, node.TrueType, node.FalseType), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsConditionalTypeNode(node *Node) bool { + return node.Kind == KindConditionalType +} + +// ────────────────────────────────────────────────────────────────────── +// TypeOperatorNode +// ────────────────────────────────────────────────────────────────────── + +type TypeOperatorNode struct { + TypeNodeBase + Operator Kind + Type *TypeNode +} + +func (f *NodeFactory) NewTypeOperatorNode(operator Kind, typeNode *TypeNode) *Node { + data := f.typeOperatorNodeArena.New() + data.Operator = operator + data.Type = typeNode + return f.newNode(KindTypeOperator, data) +} + +func (f *NodeFactory) UpdateTypeOperatorNode(node *TypeOperatorNode, operator Kind, typeNode *TypeNode) *Node { + if operator != node.Operator || typeNode != node.Type { + return updateNode(f.NewTypeOperatorNode(operator, typeNode), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *TypeOperatorNode) ForEachChild(v Visitor) bool { + return visit(v, node.Type) +} + +func (node *TypeOperatorNode) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateTypeOperatorNode(node, node.Operator, v.visitNode(node.Type)) +} + +func (node *TypeOperatorNode) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewTypeOperatorNode(node.Operator, node.Type), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsTypeOperatorNode(node *Node) bool { + return node.Kind == KindTypeOperator +} + +// ────────────────────────────────────────────────────────────────────── +// InferTypeNode +// ────────────────────────────────────────────────────────────────────── + +type InferTypeNode struct { + TypeNodeBase + TypeParameter *TypeParameterDeclarationNode +} + +func (f *NodeFactory) NewInferTypeNode(typeParameter *TypeParameterDeclarationNode) *Node { + data := &InferTypeNode{} + data.TypeParameter = typeParameter + return f.newNode(KindInferType, data) +} + +func (f *NodeFactory) UpdateInferTypeNode(node *InferTypeNode, typeParameter *TypeParameterDeclarationNode) *Node { + if typeParameter != node.TypeParameter { + return updateNode(f.NewInferTypeNode(typeParameter), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *InferTypeNode) ForEachChild(v Visitor) bool { + return visit(v, node.TypeParameter) +} + +func (node *InferTypeNode) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateInferTypeNode(node, v.visitNode(node.TypeParameter)) +} + +func (node *InferTypeNode) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewInferTypeNode(node.TypeParameter), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsInferTypeNode(node *Node) bool { + return node.Kind == KindInferType +} + +// ────────────────────────────────────────────────────────────────────── +// ArrayTypeNode +// ────────────────────────────────────────────────────────────────────── + +type ArrayTypeNode struct { + TypeNodeBase + ElementType *TypeNode +} + +func (f *NodeFactory) NewArrayTypeNode(elementType *TypeNode) *Node { + data := f.arrayTypeNodeArena.New() + data.ElementType = elementType + return f.newNode(KindArrayType, data) +} + +func (f *NodeFactory) UpdateArrayTypeNode(node *ArrayTypeNode, elementType *TypeNode) *Node { + if elementType != node.ElementType { + return updateNode(f.NewArrayTypeNode(elementType), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *ArrayTypeNode) ForEachChild(v Visitor) bool { + return visit(v, node.ElementType) +} + +func (node *ArrayTypeNode) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateArrayTypeNode(node, v.visitNode(node.ElementType)) +} + +func (node *ArrayTypeNode) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewArrayTypeNode(node.ElementType), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsArrayTypeNode(node *Node) bool { + return node.Kind == KindArrayType +} + +// ────────────────────────────────────────────────────────────────────── +// IndexedAccessTypeNode +// ────────────────────────────────────────────────────────────────────── + +type IndexedAccessTypeNode struct { + TypeNodeBase + ObjectType *TypeNode + IndexType *TypeNode +} + +func (f *NodeFactory) NewIndexedAccessTypeNode(objectType *TypeNode, indexType *TypeNode) *Node { + data := f.indexedAccessTypeNodeArena.New() + data.ObjectType = objectType + data.IndexType = indexType + return f.newNode(KindIndexedAccessType, data) +} + +func (f *NodeFactory) UpdateIndexedAccessTypeNode(node *IndexedAccessTypeNode, objectType *TypeNode, indexType *TypeNode) *Node { + if objectType != node.ObjectType || indexType != node.IndexType { + return updateNode(f.NewIndexedAccessTypeNode(objectType, indexType), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *IndexedAccessTypeNode) ForEachChild(v Visitor) bool { + return visit(v, node.ObjectType) || visit(v, node.IndexType) +} + +func (node *IndexedAccessTypeNode) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateIndexedAccessTypeNode(node, v.visitNode(node.ObjectType), v.visitNode(node.IndexType)) +} + +func (node *IndexedAccessTypeNode) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewIndexedAccessTypeNode(node.ObjectType, node.IndexType), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsIndexedAccessTypeNode(node *Node) bool { + return node.Kind == KindIndexedAccessType +} + +// ────────────────────────────────────────────────────────────────────── +// TypeReferenceNode +// ────────────────────────────────────────────────────────────────────── + +type TypeReferenceNode struct { + NodeWithTypeArgumentsBase + TypeName *EntityName +} + +func (f *NodeFactory) NewTypeReferenceNode(typeName *EntityName, typeArguments *TypeList) *Node { + data := f.typeReferenceNodeArena.New() + data.TypeName = typeName + data.TypeArguments = typeArguments + return f.newNode(KindTypeReference, data) +} + +func (f *NodeFactory) UpdateTypeReferenceNode(node *TypeReferenceNode, typeName *EntityName, typeArguments *TypeList) *Node { + if typeName != node.TypeName || typeArguments != node.TypeArguments { + return updateNode(f.NewTypeReferenceNode(typeName, typeArguments), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *TypeReferenceNode) ForEachChild(v Visitor) bool { + return visit(v, node.TypeName) || visitNodeList(v, node.TypeArguments) +} + +func (node *TypeReferenceNode) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateTypeReferenceNode(node, v.visitNode(node.TypeName), v.visitNodes(node.TypeArguments)) +} + +func (node *TypeReferenceNode) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewTypeReferenceNode(node.TypeName, node.TypeArguments), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsTypeReferenceNode(node *Node) bool { + return node.Kind == KindTypeReference +} + +// ────────────────────────────────────────────────────────────────────── +// ExpressionWithTypeArguments +// ────────────────────────────────────────────────────────────────────── + +type ExpressionWithTypeArguments struct { + MemberExpressionBase + CompositeBase + Expression *Expression + TypeArguments *TypeList // Optional +} + +func (f *NodeFactory) NewExpressionWithTypeArguments(expression *Expression, typeArguments *TypeList) *Node { + data := f.expressionWithTypeArgumentsArena.New() + data.Expression = expression + data.TypeArguments = typeArguments + return f.newNode(KindExpressionWithTypeArguments, data) +} + +func (f *NodeFactory) UpdateExpressionWithTypeArguments(node *ExpressionWithTypeArguments, expression *Expression, typeArguments *TypeList) *Node { + if expression != node.Expression || typeArguments != node.TypeArguments { + return updateNode(f.NewExpressionWithTypeArguments(expression, typeArguments), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *ExpressionWithTypeArguments) ForEachChild(v Visitor) bool { + return visit(v, node.Expression) || visitNodeList(v, node.TypeArguments) +} + +func (node *ExpressionWithTypeArguments) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateExpressionWithTypeArguments(node, v.visitNode(node.Expression), v.visitNodes(node.TypeArguments)) +} + +func (node *ExpressionWithTypeArguments) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewExpressionWithTypeArguments(node.Expression, node.TypeArguments), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsExpressionWithTypeArguments(node *Node) bool { + return node.Kind == KindExpressionWithTypeArguments +} + +// ────────────────────────────────────────────────────────────────────── +// LiteralTypeNode +// ────────────────────────────────────────────────────────────────────── + +type LiteralTypeNode struct { + TypeNodeBase + Literal *Node +} + +func (f *NodeFactory) NewLiteralTypeNode(literal *Node) *Node { + data := f.literalTypeNodeArena.New() + data.Literal = literal + return f.newNode(KindLiteralType, data) +} + +func (f *NodeFactory) UpdateLiteralTypeNode(node *LiteralTypeNode, literal *Node) *Node { + if literal != node.Literal { + return updateNode(f.NewLiteralTypeNode(literal), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *LiteralTypeNode) ForEachChild(v Visitor) bool { + return visit(v, node.Literal) +} + +func (node *LiteralTypeNode) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateLiteralTypeNode(node, v.visitNode(node.Literal)) +} + +func (node *LiteralTypeNode) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewLiteralTypeNode(node.Literal), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsLiteralTypeNode(node *Node) bool { + return node.Kind == KindLiteralType +} + +// ────────────────────────────────────────────────────────────────────── +// ThisTypeNode +// ────────────────────────────────────────────────────────────────────── + +type ThisTypeNode struct { + TypeNodeBase +} + +func (f *NodeFactory) NewThisTypeNode() *Node { + data := &ThisTypeNode{} + return f.newNode(KindThisType, data) +} + +func (node *ThisTypeNode) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewThisTypeNode(), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsThisTypeNode(node *Node) bool { + return node.Kind == KindThisType +} + +// ────────────────────────────────────────────────────────────────────── +// TypePredicateNode +// ────────────────────────────────────────────────────────────────────── + +type TypePredicateNode struct { + TypeNodeBase + AssertsModifier *AssertsKeyword // Optional + ParameterName *TypePredicateParameterName + Type *TypeNode // Optional +} + +func (f *NodeFactory) NewTypePredicateNode(assertsModifier *AssertsKeyword, parameterName *TypePredicateParameterName, typeNode *TypeNode) *Node { + data := &TypePredicateNode{} + data.AssertsModifier = assertsModifier + data.ParameterName = parameterName + data.Type = typeNode + return f.newNode(KindTypePredicate, data) +} + +func (f *NodeFactory) UpdateTypePredicateNode(node *TypePredicateNode, assertsModifier *AssertsKeyword, parameterName *TypePredicateParameterName, typeNode *TypeNode) *Node { + if assertsModifier != node.AssertsModifier || parameterName != node.ParameterName || typeNode != node.Type { + return updateNode(f.NewTypePredicateNode(assertsModifier, parameterName, typeNode), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *TypePredicateNode) ForEachChild(v Visitor) bool { + return visit(v, node.AssertsModifier) || visit(v, node.ParameterName) || visit(v, node.Type) +} + +func (node *TypePredicateNode) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateTypePredicateNode(node, v.visitNode(node.AssertsModifier), v.visitNode(node.ParameterName), v.visitNode(node.Type)) +} + +func (node *TypePredicateNode) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewTypePredicateNode(node.AssertsModifier, node.ParameterName, node.Type), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsTypePredicateNode(node *Node) bool { + return node.Kind == KindTypePredicate +} + +// ────────────────────────────────────────────────────────────────────── +// ImportAttribute +// ────────────────────────────────────────────────────────────────────── + +type ImportAttribute struct { + NodeBase + CompositeBase + name *ImportAttributeName + Value *Expression +} + +func (f *NodeFactory) NewImportAttribute(name *ImportAttributeName, value *Expression) *Node { + data := &ImportAttribute{} + data.name = name + data.Value = value + return f.newNode(KindImportAttribute, data) +} + +func (f *NodeFactory) UpdateImportAttribute(node *ImportAttribute, name *ImportAttributeName, value *Expression) *Node { + if name != node.name || value != node.Value { + return updateNode(f.NewImportAttribute(name, value), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *ImportAttribute) ForEachChild(v Visitor) bool { + return visit(v, node.name) || visit(v, node.Value) +} + +func (node *ImportAttribute) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateImportAttribute(node, v.visitNode(node.name), v.visitNode(node.Value)) +} + +func (node *ImportAttribute) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewImportAttribute(node.name, node.Value), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *ImportAttribute) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.name) | + propagateSubtreeFacts(node.Value) +} + +func (node *ImportAttribute) Name() *DeclarationName { + return node.name +} + +func IsImportAttribute(node *Node) bool { + return node.Kind == KindImportAttribute +} + +// ────────────────────────────────────────────────────────────────────── +// ImportAttributes +// ────────────────────────────────────────────────────────────────────── + +type ImportAttributes struct { + NodeBase + CompositeBase + Token Kind + Attributes *ImportAttributeList + MultiLine bool +} + +func (f *NodeFactory) NewImportAttributes(token Kind, attributes *ImportAttributeList, multiLine bool) *Node { + data := &ImportAttributes{} + data.Token = token + data.Attributes = attributes + data.MultiLine = multiLine + return f.newNode(KindImportAttributes, data) +} + +func (f *NodeFactory) UpdateImportAttributes(node *ImportAttributes, token Kind, attributes *ImportAttributeList, multiLine bool) *Node { + if token != node.Token || attributes != node.Attributes || multiLine != node.MultiLine { + return updateNode(f.NewImportAttributes(token, attributes, multiLine), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *ImportAttributes) ForEachChild(v Visitor) bool { + return visitNodeList(v, node.Attributes) +} + +func (node *ImportAttributes) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateImportAttributes(node, node.Token, v.visitNodes(node.Attributes), node.MultiLine) +} + +func (node *ImportAttributes) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewImportAttributes(node.Token, node.Attributes, node.MultiLine), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *ImportAttributes) computeSubtreeFacts() SubtreeFacts { + return propagateNodeListSubtreeFacts(node.Attributes, propagateSubtreeFacts) +} + +func IsImportAttributes(node *Node) bool { + return node.Kind == KindImportAttributes +} + +// ────────────────────────────────────────────────────────────────────── +// TypeQueryNode +// ────────────────────────────────────────────────────────────────────── + +type TypeQueryNode struct { + NodeWithTypeArgumentsBase + ExprName *EntityName +} + +func (f *NodeFactory) NewTypeQueryNode(exprName *EntityName, typeArguments *TypeList) *Node { + data := &TypeQueryNode{} + data.ExprName = exprName + data.TypeArguments = typeArguments + return f.newNode(KindTypeQuery, data) +} + +func (f *NodeFactory) UpdateTypeQueryNode(node *TypeQueryNode, exprName *EntityName, typeArguments *TypeList) *Node { + if exprName != node.ExprName || typeArguments != node.TypeArguments { + return updateNode(f.NewTypeQueryNode(exprName, typeArguments), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *TypeQueryNode) ForEachChild(v Visitor) bool { + return visit(v, node.ExprName) || visitNodeList(v, node.TypeArguments) +} + +func (node *TypeQueryNode) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateTypeQueryNode(node, v.visitNode(node.ExprName), v.visitNodes(node.TypeArguments)) +} + +func (node *TypeQueryNode) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewTypeQueryNode(node.ExprName, node.TypeArguments), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsTypeQueryNode(node *Node) bool { + return node.Kind == KindTypeQuery +} + +// ────────────────────────────────────────────────────────────────────── +// MappedTypeNode +// ────────────────────────────────────────────────────────────────────── + +type MappedTypeNode struct { + TypeNodeBase + DeclarationBase + LocalsContainerBase + ReadonlyToken *TokenNode // Optional + TypeParameter *TypeParameterDeclarationNode + NameType *TypeNode // Optional + QuestionToken *TokenNode // Optional + Type *TypeNode // Optional + Members *TypeElementList // Optional +} + +func (f *NodeFactory) NewMappedTypeNode(readonlyToken *TokenNode, typeParameter *TypeParameterDeclarationNode, nameType *TypeNode, questionToken *TokenNode, typeNode *TypeNode, members *TypeElementList) *Node { + data := &MappedTypeNode{} + data.ReadonlyToken = readonlyToken + data.TypeParameter = typeParameter + data.NameType = nameType + data.QuestionToken = questionToken + data.Type = typeNode + data.Members = members + return f.newNode(KindMappedType, data) +} + +func (f *NodeFactory) UpdateMappedTypeNode(node *MappedTypeNode, readonlyToken *TokenNode, typeParameter *TypeParameterDeclarationNode, nameType *TypeNode, questionToken *TokenNode, typeNode *TypeNode, members *TypeElementList) *Node { + if readonlyToken != node.ReadonlyToken || typeParameter != node.TypeParameter || nameType != node.NameType || questionToken != node.QuestionToken || typeNode != node.Type || members != node.Members { + return updateNode(f.NewMappedTypeNode(readonlyToken, typeParameter, nameType, questionToken, typeNode, members), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *MappedTypeNode) ForEachChild(v Visitor) bool { + return visit(v, node.ReadonlyToken) || + visit(v, node.TypeParameter) || + visit(v, node.NameType) || + visit(v, node.QuestionToken) || + visit(v, node.Type) || + visitNodeList(v, node.Members) +} + +func (node *MappedTypeNode) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateMappedTypeNode(node, v.visitNode(node.ReadonlyToken), v.visitNode(node.TypeParameter), v.visitNode(node.NameType), v.visitNode(node.QuestionToken), v.visitNode(node.Type), v.visitNodes(node.Members)) +} + +func (node *MappedTypeNode) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewMappedTypeNode(node.ReadonlyToken, node.TypeParameter, node.NameType, node.QuestionToken, node.Type, node.Members), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsMappedTypeNode(node *Node) bool { + return node.Kind == KindMappedType +} + +// ────────────────────────────────────────────────────────────────────── +// TypeLiteralNode +// ────────────────────────────────────────────────────────────────────── + +type TypeLiteralNode struct { + TypeNodeBase + DeclarationBase + Members *TypeElementList +} + +func (f *NodeFactory) NewTypeLiteralNode(members *TypeElementList) *Node { + data := f.typeLiteralNodeArena.New() + data.Members = members + return f.newNode(KindTypeLiteral, data) +} + +func (f *NodeFactory) UpdateTypeLiteralNode(node *TypeLiteralNode, members *TypeElementList) *Node { + if members != node.Members { + return updateNode(f.NewTypeLiteralNode(members), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *TypeLiteralNode) ForEachChild(v Visitor) bool { + return visitNodeList(v, node.Members) +} + +func (node *TypeLiteralNode) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateTypeLiteralNode(node, v.visitNodes(node.Members)) +} + +func (node *TypeLiteralNode) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewTypeLiteralNode(node.Members), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsTypeLiteralNode(node *Node) bool { + return node.Kind == KindTypeLiteral +} + +// ────────────────────────────────────────────────────────────────────── +// TupleTypeNode +// ────────────────────────────────────────────────────────────────────── + +type TupleTypeNode struct { + TypeNodeBase + Elements *TypeList +} + +func (f *NodeFactory) NewTupleTypeNode(elements *TypeList) *Node { + data := &TupleTypeNode{} + data.Elements = elements + return f.newNode(KindTupleType, data) +} + +func (f *NodeFactory) UpdateTupleTypeNode(node *TupleTypeNode, elements *TypeList) *Node { + if elements != node.Elements { + return updateNode(f.NewTupleTypeNode(elements), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *TupleTypeNode) ForEachChild(v Visitor) bool { + return visitNodeList(v, node.Elements) +} + +func (node *TupleTypeNode) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateTupleTypeNode(node, v.visitNodes(node.Elements)) +} + +func (node *TupleTypeNode) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewTupleTypeNode(node.Elements), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsTupleTypeNode(node *Node) bool { + return node.Kind == KindTupleType +} + +// ────────────────────────────────────────────────────────────────────── +// NamedTupleMember +// ────────────────────────────────────────────────────────────────────── + +type NamedTupleMember struct { + TypeNodeBase + DeclarationBase + DotDotDotToken *DotDotDotToken // Optional + name *IdentifierNode + QuestionToken *QuestionToken // Optional + Type *TypeNode +} + +func (f *NodeFactory) NewNamedTupleMember(dotDotDotToken *DotDotDotToken, name *IdentifierNode, questionToken *QuestionToken, typeNode *TypeNode) *Node { + data := &NamedTupleMember{} + data.DotDotDotToken = dotDotDotToken + data.name = name + data.QuestionToken = questionToken + data.Type = typeNode + return f.newNode(KindNamedTupleMember, data) +} + +func (f *NodeFactory) UpdateNamedTupleMember(node *NamedTupleMember, dotDotDotToken *DotDotDotToken, name *IdentifierNode, questionToken *QuestionToken, typeNode *TypeNode) *Node { + if dotDotDotToken != node.DotDotDotToken || name != node.name || questionToken != node.QuestionToken || typeNode != node.Type { + return updateNode(f.NewNamedTupleMember(dotDotDotToken, name, questionToken, typeNode), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *NamedTupleMember) ForEachChild(v Visitor) bool { + return visit(v, node.DotDotDotToken) || + visit(v, node.name) || + visit(v, node.QuestionToken) || + visit(v, node.Type) +} + +func (node *NamedTupleMember) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateNamedTupleMember(node, v.visitNode(node.DotDotDotToken), v.visitNode(node.name), v.visitNode(node.QuestionToken), v.visitNode(node.Type)) +} + +func (node *NamedTupleMember) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewNamedTupleMember(node.DotDotDotToken, node.name, node.QuestionToken, node.Type), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *NamedTupleMember) Name() *DeclarationName { + return node.name +} + +func IsNamedTupleMember(node *Node) bool { + return node.Kind == KindNamedTupleMember +} + +// ────────────────────────────────────────────────────────────────────── +// OptionalTypeNode +// ────────────────────────────────────────────────────────────────────── + +type OptionalTypeNode struct { + TypeNodeBase + Type *TypeNode +} + +func (f *NodeFactory) NewOptionalTypeNode(typeNode *TypeNode) *Node { + data := &OptionalTypeNode{} + data.Type = typeNode + return f.newNode(KindOptionalType, data) +} + +func (f *NodeFactory) UpdateOptionalTypeNode(node *OptionalTypeNode, typeNode *TypeNode) *Node { + if typeNode != node.Type { + return updateNode(f.NewOptionalTypeNode(typeNode), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *OptionalTypeNode) ForEachChild(v Visitor) bool { + return visit(v, node.Type) +} + +func (node *OptionalTypeNode) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateOptionalTypeNode(node, v.visitNode(node.Type)) +} + +func (node *OptionalTypeNode) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewOptionalTypeNode(node.Type), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsOptionalTypeNode(node *Node) bool { + return node.Kind == KindOptionalType +} + +// ────────────────────────────────────────────────────────────────────── +// RestTypeNode +// ────────────────────────────────────────────────────────────────────── + +type RestTypeNode struct { + TypeNodeBase + Type *TypeNode +} + +func (f *NodeFactory) NewRestTypeNode(typeNode *TypeNode) *Node { + data := &RestTypeNode{} + data.Type = typeNode + return f.newNode(KindRestType, data) +} + +func (f *NodeFactory) UpdateRestTypeNode(node *RestTypeNode, typeNode *TypeNode) *Node { + if typeNode != node.Type { + return updateNode(f.NewRestTypeNode(typeNode), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *RestTypeNode) ForEachChild(v Visitor) bool { + return visit(v, node.Type) +} + +func (node *RestTypeNode) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateRestTypeNode(node, v.visitNode(node.Type)) +} + +func (node *RestTypeNode) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewRestTypeNode(node.Type), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsRestTypeNode(node *Node) bool { + return node.Kind == KindRestType +} + +// ────────────────────────────────────────────────────────────────────── +// ParenthesizedTypeNode +// ────────────────────────────────────────────────────────────────────── + +type ParenthesizedTypeNode struct { + TypeNodeBase + Type *TypeNode +} + +func (f *NodeFactory) NewParenthesizedTypeNode(typeNode *TypeNode) *Node { + data := f.parenthesizedTypeNodeArena.New() + data.Type = typeNode + return f.newNode(KindParenthesizedType, data) +} + +func (f *NodeFactory) UpdateParenthesizedTypeNode(node *ParenthesizedTypeNode, typeNode *TypeNode) *Node { + if typeNode != node.Type { + return updateNode(f.NewParenthesizedTypeNode(typeNode), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *ParenthesizedTypeNode) ForEachChild(v Visitor) bool { + return visit(v, node.Type) +} + +func (node *ParenthesizedTypeNode) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateParenthesizedTypeNode(node, v.visitNode(node.Type)) +} + +func (node *ParenthesizedTypeNode) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewParenthesizedTypeNode(node.Type), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsParenthesizedTypeNode(node *Node) bool { + return node.Kind == KindParenthesizedType +} + +// ────────────────────────────────────────────────────────────────────── +// FunctionTypeNode +// ────────────────────────────────────────────────────────────────────── + +type FunctionTypeNode struct { + TypeNodeBase + FunctionOrConstructorTypeNodeBase +} + +func (f *NodeFactory) NewFunctionTypeNode(typeParameters *TypeParameterList, parameters *ParameterList, typeNode *TypeNode) *Node { + data := f.functionTypeNodeArena.New() + data.TypeParameters = typeParameters + data.Parameters = parameters + data.Type = typeNode + return f.newNode(KindFunctionType, data) +} + +func (f *NodeFactory) UpdateFunctionTypeNode(node *FunctionTypeNode, typeParameters *TypeParameterList, parameters *ParameterList, typeNode *TypeNode) *Node { + if typeParameters != node.TypeParameters || parameters != node.Parameters || typeNode != node.Type { + return updateNode(f.NewFunctionTypeNode(typeParameters, parameters, typeNode), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *FunctionTypeNode) ForEachChild(v Visitor) bool { + return visitNodeList(v, node.TypeParameters) || visitNodeList(v, node.Parameters) || visit(v, node.Type) +} + +func (node *FunctionTypeNode) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateFunctionTypeNode(node, v.visitNodes(node.TypeParameters), v.visitNodes(node.Parameters), v.visitNode(node.Type)) +} + +func (node *FunctionTypeNode) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewFunctionTypeNode(node.TypeParameters, node.Parameters, node.Type), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsFunctionTypeNode(node *Node) bool { + return node.Kind == KindFunctionType +} + +// ────────────────────────────────────────────────────────────────────── +// ConstructorTypeNode +// ────────────────────────────────────────────────────────────────────── + +type ConstructorTypeNode struct { + TypeNodeBase + FunctionOrConstructorTypeNodeBase +} + +func (f *NodeFactory) NewConstructorTypeNode(modifiers *ModifierList, typeParameters *TypeParameterList, parameters *ParameterList, typeNode *TypeNode) *Node { + data := &ConstructorTypeNode{} + data.modifiers = modifiers + data.TypeParameters = typeParameters + data.Parameters = parameters + data.Type = typeNode + return f.newNode(KindConstructorType, data) +} + +func (f *NodeFactory) UpdateConstructorTypeNode(node *ConstructorTypeNode, modifiers *ModifierList, typeParameters *TypeParameterList, parameters *ParameterList, typeNode *TypeNode) *Node { + if modifiers != node.modifiers || typeParameters != node.TypeParameters || parameters != node.Parameters || typeNode != node.Type { + return updateNode(f.NewConstructorTypeNode(modifiers, typeParameters, parameters, typeNode), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *ConstructorTypeNode) ForEachChild(v Visitor) bool { + return visitModifiers(v, node.modifiers) || + visitNodeList(v, node.TypeParameters) || + visitNodeList(v, node.Parameters) || + visit(v, node.Type) +} + +func (node *ConstructorTypeNode) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateConstructorTypeNode(node, v.visitModifiers(node.modifiers), v.visitNodes(node.TypeParameters), v.visitNodes(node.Parameters), v.visitNode(node.Type)) +} + +func (node *ConstructorTypeNode) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewConstructorTypeNode(node.Modifiers(), node.TypeParameters, node.Parameters, node.Type), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsConstructorTypeNode(node *Node) bool { + return node.Kind == KindConstructorType +} + +// ────────────────────────────────────────────────────────────────────── +// TemplateHead +// ────────────────────────────────────────────────────────────────────── + +type TemplateHead struct { + NodeBase + TemplateLiteralLikeNodeBase +} + +func (f *NodeFactory) NewTemplateHead(text string, rawText string, templateFlags TokenFlags) *Node { + data := &TemplateHead{} + data.Text = text + data.RawText = rawText + data.TemplateFlags = templateFlags & TokenFlagsTemplateLiteralLikeFlags + f.textCount++ + return f.newNode(KindTemplateHead, data) +} + +func (node *TemplateHead) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewTemplateHead(node.Text, node.RawText, node.TemplateFlags), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsTemplateHead(node *Node) bool { + return node.Kind == KindTemplateHead +} + +// ────────────────────────────────────────────────────────────────────── +// TemplateMiddle +// ────────────────────────────────────────────────────────────────────── + +type TemplateMiddle struct { + NodeBase + TemplateLiteralLikeNodeBase +} + +func (f *NodeFactory) NewTemplateMiddle(text string, rawText string, templateFlags TokenFlags) *Node { + data := &TemplateMiddle{} + data.Text = text + data.RawText = rawText + data.TemplateFlags = templateFlags & TokenFlagsTemplateLiteralLikeFlags + f.textCount++ + return f.newNode(KindTemplateMiddle, data) +} + +func (node *TemplateMiddle) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewTemplateMiddle(node.Text, node.RawText, node.TemplateFlags), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsTemplateMiddle(node *Node) bool { + return node.Kind == KindTemplateMiddle +} + +// ────────────────────────────────────────────────────────────────────── +// TemplateTail +// ────────────────────────────────────────────────────────────────────── + +type TemplateTail struct { + NodeBase + TemplateLiteralLikeNodeBase +} + +func (f *NodeFactory) NewTemplateTail(text string, rawText string, templateFlags TokenFlags) *Node { + data := &TemplateTail{} + data.Text = text + data.RawText = rawText + data.TemplateFlags = templateFlags & TokenFlagsTemplateLiteralLikeFlags + f.textCount++ + return f.newNode(KindTemplateTail, data) +} + +func (node *TemplateTail) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewTemplateTail(node.Text, node.RawText, node.TemplateFlags), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsTemplateTail(node *Node) bool { + return node.Kind == KindTemplateTail +} + +// ────────────────────────────────────────────────────────────────────── +// TemplateLiteralTypeNode +// ────────────────────────────────────────────────────────────────────── + +type TemplateLiteralTypeNode struct { + TypeNodeBase + Head *TemplateHeadNode + TemplateSpans *TemplateLiteralTypeSpanList +} + +func (f *NodeFactory) NewTemplateLiteralTypeNode(head *TemplateHeadNode, templateSpans *TemplateLiteralTypeSpanList) *Node { + data := &TemplateLiteralTypeNode{} + data.Head = head + data.TemplateSpans = templateSpans + return f.newNode(KindTemplateLiteralType, data) +} + +func (f *NodeFactory) UpdateTemplateLiteralTypeNode(node *TemplateLiteralTypeNode, head *TemplateHeadNode, templateSpans *TemplateLiteralTypeSpanList) *Node { + if head != node.Head || templateSpans != node.TemplateSpans { + return updateNode(f.NewTemplateLiteralTypeNode(head, templateSpans), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *TemplateLiteralTypeNode) ForEachChild(v Visitor) bool { + return visit(v, node.Head) || visitNodeList(v, node.TemplateSpans) +} + +func (node *TemplateLiteralTypeNode) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateTemplateLiteralTypeNode(node, v.visitNode(node.Head), v.visitNodes(node.TemplateSpans)) +} + +func (node *TemplateLiteralTypeNode) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewTemplateLiteralTypeNode(node.Head, node.TemplateSpans), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsTemplateLiteralTypeNode(node *Node) bool { + return node.Kind == KindTemplateLiteralType +} + +// ────────────────────────────────────────────────────────────────────── +// TemplateLiteralTypeSpan +// ────────────────────────────────────────────────────────────────────── + +type TemplateLiteralTypeSpan struct { + TypeNodeBase + Type *TypeNode + Literal *TemplateMiddleOrTail +} + +func (f *NodeFactory) NewTemplateLiteralTypeSpan(typeNode *TypeNode, literal *TemplateMiddleOrTail) *Node { + data := &TemplateLiteralTypeSpan{} + data.Type = typeNode + data.Literal = literal + return f.newNode(KindTemplateLiteralTypeSpan, data) +} + +func (f *NodeFactory) UpdateTemplateLiteralTypeSpan(node *TemplateLiteralTypeSpan, typeNode *TypeNode, literal *TemplateMiddleOrTail) *Node { + if typeNode != node.Type || literal != node.Literal { + return updateNode(f.NewTemplateLiteralTypeSpan(typeNode, literal), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *TemplateLiteralTypeSpan) ForEachChild(v Visitor) bool { + return visit(v, node.Type) || visit(v, node.Literal) +} + +func (node *TemplateLiteralTypeSpan) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateTemplateLiteralTypeSpan(node, v.visitNode(node.Type), v.visitNode(node.Literal)) +} + +func (node *TemplateLiteralTypeSpan) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewTemplateLiteralTypeSpan(node.Type, node.Literal), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsTemplateLiteralTypeSpan(node *Node) bool { + return node.Kind == KindTemplateLiteralTypeSpan +} + +// ────────────────────────────────────────────────────────────────────── +// SyntheticExpression +// ────────────────────────────────────────────────────────────────────── + +type SyntheticExpression struct { + ExpressionBase + Type any + IsSpread bool + TupleNameSource *Node // Optional +} + +func (f *NodeFactory) NewSyntheticExpression(typeNode any, isSpread bool, tupleNameSource *Node) *Node { + data := &SyntheticExpression{} + data.Type = typeNode + data.IsSpread = isSpread + data.TupleNameSource = tupleNameSource + return f.newNode(KindSyntheticExpression, data) +} + +func (f *NodeFactory) UpdateSyntheticExpression(node *SyntheticExpression, typeNode any, isSpread bool, tupleNameSource *Node) *Node { + if typeNode != node.Type || isSpread != node.IsSpread || tupleNameSource != node.TupleNameSource { + return updateNode(f.NewSyntheticExpression(typeNode, isSpread, tupleNameSource), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *SyntheticExpression) ForEachChild(v Visitor) bool { + return visit(v, node.TupleNameSource) +} + +func (node *SyntheticExpression) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateSyntheticExpression(node, node.Type, node.IsSpread, v.visitNode(node.TupleNameSource)) +} + +func (node *SyntheticExpression) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewSyntheticExpression(node.Type, node.IsSpread, node.TupleNameSource), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsSyntheticExpression(node *Node) bool { + return node.Kind == KindSyntheticExpression +} + +// ────────────────────────────────────────────────────────────────────── +// PartiallyEmittedExpression +// ────────────────────────────────────────────────────────────────────── + +type PartiallyEmittedExpression struct { + LeftHandSideExpressionBase + Expression *Expression +} + +func (f *NodeFactory) NewPartiallyEmittedExpression(expression *Expression) *Node { + data := &PartiallyEmittedExpression{} + data.Expression = expression + return f.newNode(KindPartiallyEmittedExpression, data) +} + +func (f *NodeFactory) UpdatePartiallyEmittedExpression(node *PartiallyEmittedExpression, expression *Expression) *Node { + if expression != node.Expression { + return updateNode(f.NewPartiallyEmittedExpression(expression), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *PartiallyEmittedExpression) ForEachChild(v Visitor) bool { + return visit(v, node.Expression) +} + +func (node *PartiallyEmittedExpression) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdatePartiallyEmittedExpression(node, v.visitNode(node.Expression)) +} + +func (node *PartiallyEmittedExpression) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewPartiallyEmittedExpression(node.Expression), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *PartiallyEmittedExpression) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.Expression) +} + +func IsPartiallyEmittedExpression(node *Node) bool { + return node.Kind == KindPartiallyEmittedExpression +} + +// ────────────────────────────────────────────────────────────────────── +// JsxElement +// ────────────────────────────────────────────────────────────────────── + +type JsxElement struct { + PrimaryExpressionBase + CompositeBase + OpeningElement *JsxOpeningElementNode + Children *JsxChildList + ClosingElement *JsxClosingElementNode +} + +func (f *NodeFactory) NewJsxElement(openingElement *JsxOpeningElementNode, children *JsxChildList, closingElement *JsxClosingElementNode) *Node { + data := &JsxElement{} + data.OpeningElement = openingElement + data.Children = children + data.ClosingElement = closingElement + return f.newNode(KindJsxElement, data) +} + +func (f *NodeFactory) UpdateJsxElement(node *JsxElement, openingElement *JsxOpeningElementNode, children *JsxChildList, closingElement *JsxClosingElementNode) *Node { + if openingElement != node.OpeningElement || children != node.Children || closingElement != node.ClosingElement { + return updateNode(f.NewJsxElement(openingElement, children, closingElement), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *JsxElement) ForEachChild(v Visitor) bool { + return visit(v, node.OpeningElement) || visitNodeList(v, node.Children) || visit(v, node.ClosingElement) +} + +func (node *JsxElement) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateJsxElement(node, v.visitNode(node.OpeningElement), v.visitNodes(node.Children), v.visitNode(node.ClosingElement)) +} + +func (node *JsxElement) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewJsxElement(node.OpeningElement, node.Children, node.ClosingElement), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsJsxElement(node *Node) bool { + return node.Kind == KindJsxElement +} + +// ────────────────────────────────────────────────────────────────────── +// JsxAttributes +// ────────────────────────────────────────────────────────────────────── + +type JsxAttributes struct { + PrimaryExpressionBase + DeclarationBase + CompositeBase + Properties *JsxAttributeList +} + +func (f *NodeFactory) NewJsxAttributes(properties *JsxAttributeList) *Node { + data := &JsxAttributes{} + data.Properties = properties + return f.newNode(KindJsxAttributes, data) +} + +func (f *NodeFactory) UpdateJsxAttributes(node *JsxAttributes, properties *JsxAttributeList) *Node { + if properties != node.Properties { + return updateNode(f.NewJsxAttributes(properties), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *JsxAttributes) ForEachChild(v Visitor) bool { + return visitNodeList(v, node.Properties) +} + +func (node *JsxAttributes) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateJsxAttributes(node, v.visitNodes(node.Properties)) +} + +func (node *JsxAttributes) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewJsxAttributes(node.Properties), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsJsxAttributes(node *Node) bool { + return node.Kind == KindJsxAttributes +} + +// ────────────────────────────────────────────────────────────────────── +// JsxNamespacedName +// ────────────────────────────────────────────────────────────────────── + +type JsxNamespacedName struct { + ExpressionBase + CompositeBase + Namespace *IdentifierNode + name *IdentifierNode +} + +func (f *NodeFactory) NewJsxNamespacedName(namespace *IdentifierNode, name *IdentifierNode) *Node { + data := &JsxNamespacedName{} + data.Namespace = namespace + data.name = name + return f.newNode(KindJsxNamespacedName, data) +} + +func (f *NodeFactory) UpdateJsxNamespacedName(node *JsxNamespacedName, namespace *IdentifierNode, name *IdentifierNode) *Node { + if namespace != node.Namespace || name != node.name { + return updateNode(f.NewJsxNamespacedName(namespace, name), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *JsxNamespacedName) ForEachChild(v Visitor) bool { + return visit(v, node.Namespace) || visit(v, node.name) +} + +func (node *JsxNamespacedName) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateJsxNamespacedName(node, v.visitNode(node.Namespace), v.visitNode(node.name)) +} + +func (node *JsxNamespacedName) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewJsxNamespacedName(node.Namespace, node.name), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *JsxNamespacedName) Name() *DeclarationName { + return node.name +} + +func IsJsxNamespacedName(node *Node) bool { + return node.Kind == KindJsxNamespacedName +} + +// ────────────────────────────────────────────────────────────────────── +// JsxOpeningElement +// ────────────────────────────────────────────────────────────────────── + +type JsxOpeningElement struct { + ExpressionBase + CompositeBase + TagName *JsxTagNameExpression + TypeArguments *TypeList // Optional + Attributes *JsxAttributesNode +} + +func (f *NodeFactory) NewJsxOpeningElement(tagName *JsxTagNameExpression, typeArguments *TypeList, attributes *JsxAttributesNode) *Node { + data := &JsxOpeningElement{} + data.TagName = tagName + data.TypeArguments = typeArguments + data.Attributes = attributes + return f.newNode(KindJsxOpeningElement, data) +} + +func (f *NodeFactory) UpdateJsxOpeningElement(node *JsxOpeningElement, tagName *JsxTagNameExpression, typeArguments *TypeList, attributes *JsxAttributesNode) *Node { + if tagName != node.TagName || typeArguments != node.TypeArguments || attributes != node.Attributes { + return updateNode(f.NewJsxOpeningElement(tagName, typeArguments, attributes), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *JsxOpeningElement) ForEachChild(v Visitor) bool { + return visit(v, node.TagName) || visitNodeList(v, node.TypeArguments) || visit(v, node.Attributes) +} + +func (node *JsxOpeningElement) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateJsxOpeningElement(node, v.visitNode(node.TagName), v.visitNodes(node.TypeArguments), v.visitNode(node.Attributes)) +} + +func (node *JsxOpeningElement) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewJsxOpeningElement(node.TagName, node.TypeArguments, node.Attributes), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsJsxOpeningElement(node *Node) bool { + return node.Kind == KindJsxOpeningElement +} + +// ────────────────────────────────────────────────────────────────────── +// JsxSelfClosingElement +// ────────────────────────────────────────────────────────────────────── + +type JsxSelfClosingElement struct { + PrimaryExpressionBase + CompositeBase + TagName *JsxTagNameExpression + TypeArguments *TypeList // Optional + Attributes *JsxAttributesNode +} + +func (f *NodeFactory) NewJsxSelfClosingElement(tagName *JsxTagNameExpression, typeArguments *TypeList, attributes *JsxAttributesNode) *Node { + data := &JsxSelfClosingElement{} + data.TagName = tagName + data.TypeArguments = typeArguments + data.Attributes = attributes + return f.newNode(KindJsxSelfClosingElement, data) +} + +func (f *NodeFactory) UpdateJsxSelfClosingElement(node *JsxSelfClosingElement, tagName *JsxTagNameExpression, typeArguments *TypeList, attributes *JsxAttributesNode) *Node { + if tagName != node.TagName || typeArguments != node.TypeArguments || attributes != node.Attributes { + return updateNode(f.NewJsxSelfClosingElement(tagName, typeArguments, attributes), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *JsxSelfClosingElement) ForEachChild(v Visitor) bool { + return visit(v, node.TagName) || visitNodeList(v, node.TypeArguments) || visit(v, node.Attributes) +} + +func (node *JsxSelfClosingElement) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateJsxSelfClosingElement(node, v.visitNode(node.TagName), v.visitNodes(node.TypeArguments), v.visitNode(node.Attributes)) +} + +func (node *JsxSelfClosingElement) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewJsxSelfClosingElement(node.TagName, node.TypeArguments, node.Attributes), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsJsxSelfClosingElement(node *Node) bool { + return node.Kind == KindJsxSelfClosingElement +} + +// ────────────────────────────────────────────────────────────────────── +// JsxFragment +// ────────────────────────────────────────────────────────────────────── + +type JsxFragment struct { + PrimaryExpressionBase + CompositeBase + OpeningFragment *JsxOpeningFragmentNode + Children *JsxChildList + ClosingFragment *JsxClosingFragmentNode +} + +func (f *NodeFactory) NewJsxFragment(openingFragment *JsxOpeningFragmentNode, children *JsxChildList, closingFragment *JsxClosingFragmentNode) *Node { + data := &JsxFragment{} + data.OpeningFragment = openingFragment + data.Children = children + data.ClosingFragment = closingFragment + return f.newNode(KindJsxFragment, data) +} + +func (f *NodeFactory) UpdateJsxFragment(node *JsxFragment, openingFragment *JsxOpeningFragmentNode, children *JsxChildList, closingFragment *JsxClosingFragmentNode) *Node { + if openingFragment != node.OpeningFragment || children != node.Children || closingFragment != node.ClosingFragment { + return updateNode(f.NewJsxFragment(openingFragment, children, closingFragment), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *JsxFragment) ForEachChild(v Visitor) bool { + return visit(v, node.OpeningFragment) || visitNodeList(v, node.Children) || visit(v, node.ClosingFragment) +} + +func (node *JsxFragment) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateJsxFragment(node, v.visitNode(node.OpeningFragment), v.visitNodes(node.Children), v.visitNode(node.ClosingFragment)) +} + +func (node *JsxFragment) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewJsxFragment(node.OpeningFragment, node.Children, node.ClosingFragment), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsJsxFragment(node *Node) bool { + return node.Kind == KindJsxFragment +} + +// ────────────────────────────────────────────────────────────────────── +// JsxOpeningFragment +// ────────────────────────────────────────────────────────────────────── + +type JsxOpeningFragment struct { + ExpressionBase +} + +func (f *NodeFactory) NewJsxOpeningFragment() *Node { + data := &JsxOpeningFragment{} + return f.newNode(KindJsxOpeningFragment, data) +} + +func (node *JsxOpeningFragment) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewJsxOpeningFragment(), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsJsxOpeningFragment(node *Node) bool { + return node.Kind == KindJsxOpeningFragment +} + +// ────────────────────────────────────────────────────────────────────── +// JsxClosingFragment +// ────────────────────────────────────────────────────────────────────── + +type JsxClosingFragment struct { + ExpressionBase +} + +func (f *NodeFactory) NewJsxClosingFragment() *Node { + data := &JsxClosingFragment{} + return f.newNode(KindJsxClosingFragment, data) +} + +func (node *JsxClosingFragment) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewJsxClosingFragment(), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsJsxClosingFragment(node *Node) bool { + return node.Kind == KindJsxClosingFragment +} + +// ────────────────────────────────────────────────────────────────────── +// JsxAttribute +// ────────────────────────────────────────────────────────────────────── + +type JsxAttribute struct { + NodeBase + DeclarationBase + CompositeBase + name *JsxAttributeName + Initializer *JsxAttributeValue // Optional +} + +func (f *NodeFactory) NewJsxAttribute(name *JsxAttributeName, initializer *JsxAttributeValue) *Node { + data := &JsxAttribute{} + data.name = name + data.Initializer = initializer + return f.newNode(KindJsxAttribute, data) +} + +func (f *NodeFactory) UpdateJsxAttribute(node *JsxAttribute, name *JsxAttributeName, initializer *JsxAttributeValue) *Node { + if name != node.name || initializer != node.Initializer { + return updateNode(f.NewJsxAttribute(name, initializer), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *JsxAttribute) ForEachChild(v Visitor) bool { + return visit(v, node.name) || visit(v, node.Initializer) +} + +func (node *JsxAttribute) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateJsxAttribute(node, v.visitNode(node.name), v.visitNode(node.Initializer)) +} + +func (node *JsxAttribute) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewJsxAttribute(node.name, node.Initializer), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *JsxAttribute) Name() *DeclarationName { + return node.name +} + +func IsJsxAttribute(node *Node) bool { + return node.Kind == KindJsxAttribute +} + +// ────────────────────────────────────────────────────────────────────── +// JsxSpreadAttribute +// ────────────────────────────────────────────────────────────────────── + +type JsxSpreadAttribute struct { + ObjectLiteralElementBase + NodeBase + Expression *Expression +} + +func (f *NodeFactory) NewJsxSpreadAttribute(expression *Expression) *Node { + data := &JsxSpreadAttribute{} + data.Expression = expression + return f.newNode(KindJsxSpreadAttribute, data) +} + +func (f *NodeFactory) UpdateJsxSpreadAttribute(node *JsxSpreadAttribute, expression *Expression) *Node { + if expression != node.Expression { + return updateNode(f.NewJsxSpreadAttribute(expression), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *JsxSpreadAttribute) ForEachChild(v Visitor) bool { + return visit(v, node.Expression) +} + +func (node *JsxSpreadAttribute) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateJsxSpreadAttribute(node, v.visitNode(node.Expression)) +} + +func (node *JsxSpreadAttribute) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewJsxSpreadAttribute(node.Expression), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsJsxSpreadAttribute(node *Node) bool { + return node.Kind == KindJsxSpreadAttribute +} + +// ────────────────────────────────────────────────────────────────────── +// JsxClosingElement +// ────────────────────────────────────────────────────────────────────── + +type JsxClosingElement struct { + NodeBase + TagName *JsxTagNameExpression +} + +func (f *NodeFactory) NewJsxClosingElement(tagName *JsxTagNameExpression) *Node { + data := &JsxClosingElement{} + data.TagName = tagName + return f.newNode(KindJsxClosingElement, data) +} + +func (f *NodeFactory) UpdateJsxClosingElement(node *JsxClosingElement, tagName *JsxTagNameExpression) *Node { + if tagName != node.TagName { + return updateNode(f.NewJsxClosingElement(tagName), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *JsxClosingElement) ForEachChild(v Visitor) bool { + return visit(v, node.TagName) +} + +func (node *JsxClosingElement) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateJsxClosingElement(node, v.visitNode(node.TagName)) +} + +func (node *JsxClosingElement) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewJsxClosingElement(node.TagName), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsJsxClosingElement(node *Node) bool { + return node.Kind == KindJsxClosingElement +} + +// ────────────────────────────────────────────────────────────────────── +// JsxExpression +// ────────────────────────────────────────────────────────────────────── + +type JsxExpression struct { + ExpressionBase + DotDotDotToken *DotDotDotToken // Optional + Expression *Expression // Optional +} + +func (f *NodeFactory) NewJsxExpression(dotDotDotToken *DotDotDotToken, expression *Expression) *Node { + data := &JsxExpression{} + data.DotDotDotToken = dotDotDotToken + data.Expression = expression + return f.newNode(KindJsxExpression, data) +} + +func (f *NodeFactory) UpdateJsxExpression(node *JsxExpression, dotDotDotToken *DotDotDotToken, expression *Expression) *Node { + if dotDotDotToken != node.DotDotDotToken || expression != node.Expression { + return updateNode(f.NewJsxExpression(dotDotDotToken, expression), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *JsxExpression) ForEachChild(v Visitor) bool { + return visit(v, node.DotDotDotToken) || visit(v, node.Expression) +} + +func (node *JsxExpression) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateJsxExpression(node, v.visitNode(node.DotDotDotToken), v.visitNode(node.Expression)) +} + +func (node *JsxExpression) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewJsxExpression(node.DotDotDotToken, node.Expression), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsJsxExpression(node *Node) bool { + return node.Kind == KindJsxExpression +} + +// ────────────────────────────────────────────────────────────────────── +// JsxText +// ────────────────────────────────────────────────────────────────────── + +type JsxText struct { + ExpressionBase + LiteralLikeNodeBase + ContainsOnlyTriviaWhiteSpaces bool +} + +func (f *NodeFactory) NewJsxText(text string, containsOnlyTriviaWhiteSpaces bool) *Node { + data := &JsxText{} + data.Text = text + data.ContainsOnlyTriviaWhiteSpaces = containsOnlyTriviaWhiteSpaces + f.textCount++ + return f.newNode(KindJsxText, data) +} + +func (node *JsxText) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewJsxText(node.Text, node.ContainsOnlyTriviaWhiteSpaces), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsJsxText(node *Node) bool { + return node.Kind == KindJsxText +} + +// ────────────────────────────────────────────────────────────────────── +// SyntaxList +// ────────────────────────────────────────────────────────────────────── + +type SyntaxList struct { + NodeBase + Children []*Node +} + +func (f *NodeFactory) NewSyntaxList(children []*Node) *Node { + data := &SyntaxList{} + data.Children = children + return f.newNode(KindSyntaxList, data) +} + +func (f *NodeFactory) UpdateSyntaxList(node *SyntaxList, children []*Node) *Node { + if !core.Same(children, node.Children) { + return updateNode(f.NewSyntaxList(children), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *SyntaxList) ForEachChild(v Visitor) bool { + return visitNodes(v, node.Children) +} + +func (node *SyntaxList) VisitEachChild(v *NodeVisitor) *Node { + children := core.SameMap(node.Children, func(n *Node) *Node { return v.visitNode(n) }) + return v.Factory.UpdateSyntaxList(node, children) +} + +func (node *SyntaxList) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewSyntaxList(node.Children), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsSyntaxList(node *Node) bool { + return node.Kind == KindSyntaxList +} + +// ────────────────────────────────────────────────────────────────────── +// JSDoc +// ────────────────────────────────────────────────────────────────────── + +type JSDoc struct { + NodeBase + Comment *NodeList + Tags *NodeList // Optional +} + +func (f *NodeFactory) NewJSDoc(comment *NodeList, tags *NodeList) *Node { + data := f.jsdocArena.New() + data.Comment = comment + data.Tags = tags + return f.newNode(KindJSDoc, data) +} + +func (f *NodeFactory) UpdateJSDoc(node *JSDoc, comment *NodeList, tags *NodeList) *Node { + if comment != node.Comment || tags != node.Tags { + return updateNode(f.NewJSDoc(comment, tags), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *JSDoc) ForEachChild(v Visitor) bool { + return visitNodeList(v, node.Comment) || visitNodeList(v, node.Tags) +} + +func (node *JSDoc) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateJSDoc(node, v.visitNodes(node.Comment), v.visitNodes(node.Tags)) +} + +func (node *JSDoc) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewJSDoc(node.Comment, node.Tags), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsJSDoc(node *Node) bool { + return node.Kind == KindJSDoc +} + +// ────────────────────────────────────────────────────────────────────── +// JSDocTypeExpression +// ────────────────────────────────────────────────────────────────────── + +type JSDocTypeExpression struct { + TypeNodeBase + Type *TypeNode +} + +func (f *NodeFactory) NewJSDocTypeExpression(typeNode *TypeNode) *Node { + data := &JSDocTypeExpression{} + data.Type = typeNode + return f.newNode(KindJSDocTypeExpression, data) +} + +func (f *NodeFactory) UpdateJSDocTypeExpression(node *JSDocTypeExpression, typeNode *TypeNode) *Node { + if typeNode != node.Type { + return updateNode(f.NewJSDocTypeExpression(typeNode), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *JSDocTypeExpression) ForEachChild(v Visitor) bool { + return visit(v, node.Type) +} + +func (node *JSDocTypeExpression) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateJSDocTypeExpression(node, v.visitNode(node.Type)) +} + +func (node *JSDocTypeExpression) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewJSDocTypeExpression(node.Type), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsJSDocTypeExpression(node *Node) bool { + return node.Kind == KindJSDocTypeExpression +} + +// ────────────────────────────────────────────────────────────────────── +// JSDocNonNullableType +// ────────────────────────────────────────────────────────────────────── + +type JSDocNonNullableType struct { + JSDocTypeBase + Type *TypeNode +} + +func (f *NodeFactory) NewJSDocNonNullableType(typeNode *TypeNode) *Node { + data := &JSDocNonNullableType{} + data.Type = typeNode + return f.newNode(KindJSDocNonNullableType, data) +} + +func (f *NodeFactory) UpdateJSDocNonNullableType(node *JSDocNonNullableType, typeNode *TypeNode) *Node { + if typeNode != node.Type { + return updateNode(f.NewJSDocNonNullableType(typeNode), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *JSDocNonNullableType) ForEachChild(v Visitor) bool { + return visit(v, node.Type) +} + +func (node *JSDocNonNullableType) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateJSDocNonNullableType(node, v.visitNode(node.Type)) +} + +func (node *JSDocNonNullableType) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewJSDocNonNullableType(node.Type), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsJSDocNonNullableType(node *Node) bool { + return node.Kind == KindJSDocNonNullableType +} + +// ────────────────────────────────────────────────────────────────────── +// JSDocNullableType +// ────────────────────────────────────────────────────────────────────── + +type JSDocNullableType struct { + JSDocTypeBase + Type *TypeNode +} + +func (f *NodeFactory) NewJSDocNullableType(typeNode *TypeNode) *Node { + data := &JSDocNullableType{} + data.Type = typeNode + return f.newNode(KindJSDocNullableType, data) +} + +func (f *NodeFactory) UpdateJSDocNullableType(node *JSDocNullableType, typeNode *TypeNode) *Node { + if typeNode != node.Type { + return updateNode(f.NewJSDocNullableType(typeNode), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *JSDocNullableType) ForEachChild(v Visitor) bool { + return visit(v, node.Type) +} + +func (node *JSDocNullableType) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateJSDocNullableType(node, v.visitNode(node.Type)) +} + +func (node *JSDocNullableType) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewJSDocNullableType(node.Type), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsJSDocNullableType(node *Node) bool { + return node.Kind == KindJSDocNullableType +} + +// ────────────────────────────────────────────────────────────────────── +// JSDocAllType +// ────────────────────────────────────────────────────────────────────── + +type JSDocAllType struct { + JSDocTypeBase +} + +func (f *NodeFactory) NewJSDocAllType() *Node { + data := &JSDocAllType{} + return f.newNode(KindJSDocAllType, data) +} + +func (node *JSDocAllType) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewJSDocAllType(), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsJSDocAllType(node *Node) bool { + return node.Kind == KindJSDocAllType +} + +// ────────────────────────────────────────────────────────────────────── +// JSDocVariadicType +// ────────────────────────────────────────────────────────────────────── + +type JSDocVariadicType struct { + JSDocTypeBase + Type *TypeNode +} + +func (f *NodeFactory) NewJSDocVariadicType(typeNode *TypeNode) *Node { + data := &JSDocVariadicType{} + data.Type = typeNode + return f.newNode(KindJSDocVariadicType, data) +} + +func (f *NodeFactory) UpdateJSDocVariadicType(node *JSDocVariadicType, typeNode *TypeNode) *Node { + if typeNode != node.Type { + return updateNode(f.NewJSDocVariadicType(typeNode), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *JSDocVariadicType) ForEachChild(v Visitor) bool { + return visit(v, node.Type) +} + +func (node *JSDocVariadicType) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateJSDocVariadicType(node, v.visitNode(node.Type)) +} + +func (node *JSDocVariadicType) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewJSDocVariadicType(node.Type), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsJSDocVariadicType(node *Node) bool { + return node.Kind == KindJSDocVariadicType +} + +// ────────────────────────────────────────────────────────────────────── +// JSDocOptionalType +// ────────────────────────────────────────────────────────────────────── + +type JSDocOptionalType struct { + JSDocTypeBase + Type *TypeNode +} + +func (f *NodeFactory) NewJSDocOptionalType(typeNode *TypeNode) *Node { + data := &JSDocOptionalType{} + data.Type = typeNode + return f.newNode(KindJSDocOptionalType, data) +} + +func (f *NodeFactory) UpdateJSDocOptionalType(node *JSDocOptionalType, typeNode *TypeNode) *Node { + if typeNode != node.Type { + return updateNode(f.NewJSDocOptionalType(typeNode), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *JSDocOptionalType) ForEachChild(v Visitor) bool { + return visit(v, node.Type) +} + +func (node *JSDocOptionalType) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateJSDocOptionalType(node, v.visitNode(node.Type)) +} + +func (node *JSDocOptionalType) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewJSDocOptionalType(node.Type), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsJSDocOptionalType(node *Node) bool { + return node.Kind == KindJSDocOptionalType +} + +// ────────────────────────────────────────────────────────────────────── +// JSDocTypeTag +// ────────────────────────────────────────────────────────────────────── + +type JSDocTypeTag struct { + JSDocTagBase + TypeExpression *Node +} + +func (f *NodeFactory) NewJSDocTypeTag(tagName *IdentifierNode, typeExpression *Node, comment *NodeList) *Node { + data := &JSDocTypeTag{} + data.TagName = tagName + data.TypeExpression = typeExpression + data.Comment = comment + return f.newNode(KindJSDocTypeTag, data) +} + +func (f *NodeFactory) UpdateJSDocTypeTag(node *JSDocTypeTag, tagName *IdentifierNode, typeExpression *Node, comment *NodeList) *Node { + if tagName != node.TagName || typeExpression != node.TypeExpression || comment != node.Comment { + return updateNode(f.NewJSDocTypeTag(tagName, typeExpression, comment), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *JSDocTypeTag) ForEachChild(v Visitor) bool { + return visit(v, node.TagName) || visit(v, node.TypeExpression) || visitNodeList(v, node.Comment) +} + +func (node *JSDocTypeTag) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateJSDocTypeTag(node, v.visitNode(node.TagName), v.visitNode(node.TypeExpression), v.visitNodes(node.Comment)) +} + +func (node *JSDocTypeTag) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewJSDocTypeTag(node.TagName, node.TypeExpression, node.Comment), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsJSDocTypeTag(node *Node) bool { + return node.Kind == KindJSDocTypeTag +} + +// ────────────────────────────────────────────────────────────────────── +// JSDocUnknownTag +// ────────────────────────────────────────────────────────────────────── + +type JSDocUnknownTag struct { + JSDocTagBase +} + +func (f *NodeFactory) NewJSDocUnknownTag(tagName *IdentifierNode, comment *NodeList) *Node { + data := f.jsdocUnknownTagArena.New() + data.TagName = tagName + data.Comment = comment + return f.newNode(KindJSDocUnknownTag, data) +} + +func (f *NodeFactory) UpdateJSDocUnknownTag(node *JSDocUnknownTag, tagName *IdentifierNode, comment *NodeList) *Node { + if tagName != node.TagName || comment != node.Comment { + return updateNode(f.NewJSDocUnknownTag(tagName, comment), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *JSDocUnknownTag) ForEachChild(v Visitor) bool { + return visit(v, node.TagName) || visitNodeList(v, node.Comment) +} + +func (node *JSDocUnknownTag) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateJSDocUnknownTag(node, v.visitNode(node.TagName), v.visitNodes(node.Comment)) +} + +func (node *JSDocUnknownTag) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewJSDocUnknownTag(node.TagName, node.Comment), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsJSDocUnknownTag(node *Node) bool { + return node.Kind == KindJSDocUnknownTag +} + +// ────────────────────────────────────────────────────────────────────── +// JSDocTemplateTag +// ────────────────────────────────────────────────────────────────────── + +type JSDocTemplateTag struct { + JSDocTagBase + Constraint *Node + TypeParameters *TypeParameterList +} + +func (f *NodeFactory) NewJSDocTemplateTag(tagName *IdentifierNode, constraint *Node, typeParameters *TypeParameterList, comment *NodeList) *Node { + data := &JSDocTemplateTag{} + data.TagName = tagName + data.Constraint = constraint + data.TypeParameters = typeParameters + data.Comment = comment + return f.newNode(KindJSDocTemplateTag, data) +} + +func (f *NodeFactory) UpdateJSDocTemplateTag(node *JSDocTemplateTag, tagName *IdentifierNode, constraint *Node, typeParameters *TypeParameterList, comment *NodeList) *Node { + if tagName != node.TagName || constraint != node.Constraint || typeParameters != node.TypeParameters || comment != node.Comment { + return updateNode(f.NewJSDocTemplateTag(tagName, constraint, typeParameters, comment), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *JSDocTemplateTag) ForEachChild(v Visitor) bool { + return visit(v, node.TagName) || + visit(v, node.Constraint) || + visitNodeList(v, node.TypeParameters) || + visitNodeList(v, node.Comment) +} + +func (node *JSDocTemplateTag) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateJSDocTemplateTag(node, v.visitNode(node.TagName), v.visitNode(node.Constraint), v.visitNodes(node.TypeParameters), v.visitNodes(node.Comment)) +} + +func (node *JSDocTemplateTag) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewJSDocTemplateTag(node.TagName, node.Constraint, node.TypeParameters, node.Comment), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsJSDocTemplateTag(node *Node) bool { + return node.Kind == KindJSDocTemplateTag +} + +// ────────────────────────────────────────────────────────────────────── +// JSDocReturnTag +// ────────────────────────────────────────────────────────────────────── + +type JSDocReturnTag struct { + JSDocTagBase + TypeExpression *TypeNode // Optional +} + +func (f *NodeFactory) NewJSDocReturnTag(tagName *IdentifierNode, typeExpression *TypeNode, comment *NodeList) *Node { + data := &JSDocReturnTag{} + data.TagName = tagName + data.TypeExpression = typeExpression + data.Comment = comment + return f.newNode(KindJSDocReturnTag, data) +} + +func (f *NodeFactory) UpdateJSDocReturnTag(node *JSDocReturnTag, tagName *IdentifierNode, typeExpression *TypeNode, comment *NodeList) *Node { + if tagName != node.TagName || typeExpression != node.TypeExpression || comment != node.Comment { + return updateNode(f.NewJSDocReturnTag(tagName, typeExpression, comment), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *JSDocReturnTag) ForEachChild(v Visitor) bool { + return visit(v, node.TagName) || visit(v, node.TypeExpression) || visitNodeList(v, node.Comment) +} + +func (node *JSDocReturnTag) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateJSDocReturnTag(node, v.visitNode(node.TagName), v.visitNode(node.TypeExpression), v.visitNodes(node.Comment)) +} + +func (node *JSDocReturnTag) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewJSDocReturnTag(node.TagName, node.TypeExpression, node.Comment), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsJSDocReturnTag(node *Node) bool { + return node.Kind == KindJSDocReturnTag +} + +// ────────────────────────────────────────────────────────────────────── +// JSDocPublicTag +// ────────────────────────────────────────────────────────────────────── + +type JSDocPublicTag struct { + JSDocTagBase +} + +func (f *NodeFactory) NewJSDocPublicTag(tagName *IdentifierNode, comment *NodeList) *Node { + data := &JSDocPublicTag{} + data.TagName = tagName + data.Comment = comment + return f.newNode(KindJSDocPublicTag, data) +} + +func (f *NodeFactory) UpdateJSDocPublicTag(node *JSDocPublicTag, tagName *IdentifierNode, comment *NodeList) *Node { + if tagName != node.TagName || comment != node.Comment { + return updateNode(f.NewJSDocPublicTag(tagName, comment), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *JSDocPublicTag) ForEachChild(v Visitor) bool { + return visit(v, node.TagName) || visitNodeList(v, node.Comment) +} + +func (node *JSDocPublicTag) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateJSDocPublicTag(node, v.visitNode(node.TagName), v.visitNodes(node.Comment)) +} + +func (node *JSDocPublicTag) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewJSDocPublicTag(node.TagName, node.Comment), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsJSDocPublicTag(node *Node) bool { + return node.Kind == KindJSDocPublicTag +} + +// ────────────────────────────────────────────────────────────────────── +// JSDocPrivateTag +// ────────────────────────────────────────────────────────────────────── + +type JSDocPrivateTag struct { + JSDocTagBase +} + +func (f *NodeFactory) NewJSDocPrivateTag(tagName *IdentifierNode, comment *NodeList) *Node { + data := &JSDocPrivateTag{} + data.TagName = tagName + data.Comment = comment + return f.newNode(KindJSDocPrivateTag, data) +} + +func (f *NodeFactory) UpdateJSDocPrivateTag(node *JSDocPrivateTag, tagName *IdentifierNode, comment *NodeList) *Node { + if tagName != node.TagName || comment != node.Comment { + return updateNode(f.NewJSDocPrivateTag(tagName, comment), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *JSDocPrivateTag) ForEachChild(v Visitor) bool { + return visit(v, node.TagName) || visitNodeList(v, node.Comment) +} + +func (node *JSDocPrivateTag) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateJSDocPrivateTag(node, v.visitNode(node.TagName), v.visitNodes(node.Comment)) +} + +func (node *JSDocPrivateTag) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewJSDocPrivateTag(node.TagName, node.Comment), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsJSDocPrivateTag(node *Node) bool { + return node.Kind == KindJSDocPrivateTag +} + +// ────────────────────────────────────────────────────────────────────── +// JSDocProtectedTag +// ────────────────────────────────────────────────────────────────────── + +type JSDocProtectedTag struct { + JSDocTagBase +} + +func (f *NodeFactory) NewJSDocProtectedTag(tagName *IdentifierNode, comment *NodeList) *Node { + data := &JSDocProtectedTag{} + data.TagName = tagName + data.Comment = comment + return f.newNode(KindJSDocProtectedTag, data) +} + +func (f *NodeFactory) UpdateJSDocProtectedTag(node *JSDocProtectedTag, tagName *IdentifierNode, comment *NodeList) *Node { + if tagName != node.TagName || comment != node.Comment { + return updateNode(f.NewJSDocProtectedTag(tagName, comment), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *JSDocProtectedTag) ForEachChild(v Visitor) bool { + return visit(v, node.TagName) || visitNodeList(v, node.Comment) +} + +func (node *JSDocProtectedTag) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateJSDocProtectedTag(node, v.visitNode(node.TagName), v.visitNodes(node.Comment)) +} + +func (node *JSDocProtectedTag) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewJSDocProtectedTag(node.TagName, node.Comment), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsJSDocProtectedTag(node *Node) bool { + return node.Kind == KindJSDocProtectedTag +} + +// ────────────────────────────────────────────────────────────────────── +// JSDocReadonlyTag +// ────────────────────────────────────────────────────────────────────── + +type JSDocReadonlyTag struct { + JSDocTagBase +} + +func (f *NodeFactory) NewJSDocReadonlyTag(tagName *IdentifierNode, comment *NodeList) *Node { + data := &JSDocReadonlyTag{} + data.TagName = tagName + data.Comment = comment + return f.newNode(KindJSDocReadonlyTag, data) +} + +func (f *NodeFactory) UpdateJSDocReadonlyTag(node *JSDocReadonlyTag, tagName *IdentifierNode, comment *NodeList) *Node { + if tagName != node.TagName || comment != node.Comment { + return updateNode(f.NewJSDocReadonlyTag(tagName, comment), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *JSDocReadonlyTag) ForEachChild(v Visitor) bool { + return visit(v, node.TagName) || visitNodeList(v, node.Comment) +} + +func (node *JSDocReadonlyTag) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateJSDocReadonlyTag(node, v.visitNode(node.TagName), v.visitNodes(node.Comment)) +} + +func (node *JSDocReadonlyTag) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewJSDocReadonlyTag(node.TagName, node.Comment), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsJSDocReadonlyTag(node *Node) bool { + return node.Kind == KindJSDocReadonlyTag +} + +// ────────────────────────────────────────────────────────────────────── +// JSDocOverrideTag +// ────────────────────────────────────────────────────────────────────── + +type JSDocOverrideTag struct { + JSDocTagBase +} + +func (f *NodeFactory) NewJSDocOverrideTag(tagName *IdentifierNode, comment *NodeList) *Node { + data := &JSDocOverrideTag{} + data.TagName = tagName + data.Comment = comment + return f.newNode(KindJSDocOverrideTag, data) +} + +func (f *NodeFactory) UpdateJSDocOverrideTag(node *JSDocOverrideTag, tagName *IdentifierNode, comment *NodeList) *Node { + if tagName != node.TagName || comment != node.Comment { + return updateNode(f.NewJSDocOverrideTag(tagName, comment), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *JSDocOverrideTag) ForEachChild(v Visitor) bool { + return visit(v, node.TagName) || visitNodeList(v, node.Comment) +} + +func (node *JSDocOverrideTag) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateJSDocOverrideTag(node, v.visitNode(node.TagName), v.visitNodes(node.Comment)) +} + +func (node *JSDocOverrideTag) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewJSDocOverrideTag(node.TagName, node.Comment), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsJSDocOverrideTag(node *Node) bool { + return node.Kind == KindJSDocOverrideTag +} + +// ────────────────────────────────────────────────────────────────────── +// JSDocDeprecatedTag +// ────────────────────────────────────────────────────────────────────── + +type JSDocDeprecatedTag struct { + JSDocTagBase +} + +func (f *NodeFactory) NewJSDocDeprecatedTag(tagName *IdentifierNode, comment *NodeList) *Node { + data := f.jsdocDeprecatedTagArena.New() + data.TagName = tagName + data.Comment = comment + return f.newNode(KindJSDocDeprecatedTag, data) +} + +func (f *NodeFactory) UpdateJSDocDeprecatedTag(node *JSDocDeprecatedTag, tagName *IdentifierNode, comment *NodeList) *Node { + if tagName != node.TagName || comment != node.Comment { + return updateNode(f.NewJSDocDeprecatedTag(tagName, comment), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *JSDocDeprecatedTag) ForEachChild(v Visitor) bool { + return visit(v, node.TagName) || visitNodeList(v, node.Comment) +} + +func (node *JSDocDeprecatedTag) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateJSDocDeprecatedTag(node, v.visitNode(node.TagName), v.visitNodes(node.Comment)) +} + +func (node *JSDocDeprecatedTag) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewJSDocDeprecatedTag(node.TagName, node.Comment), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsJSDocDeprecatedTag(node *Node) bool { + return node.Kind == KindJSDocDeprecatedTag +} + +// ────────────────────────────────────────────────────────────────────── +// JSDocSeeTag +// ────────────────────────────────────────────────────────────────────── + +type JSDocSeeTag struct { + JSDocTagBase + NameExpression *TypeNode +} + +func (f *NodeFactory) NewJSDocSeeTag(tagName *IdentifierNode, nameExpression *TypeNode, comment *NodeList) *Node { + data := &JSDocSeeTag{} + data.TagName = tagName + data.NameExpression = nameExpression + data.Comment = comment + return f.newNode(KindJSDocSeeTag, data) +} + +func (f *NodeFactory) UpdateJSDocSeeTag(node *JSDocSeeTag, tagName *IdentifierNode, nameExpression *TypeNode, comment *NodeList) *Node { + if tagName != node.TagName || nameExpression != node.NameExpression || comment != node.Comment { + return updateNode(f.NewJSDocSeeTag(tagName, nameExpression, comment), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *JSDocSeeTag) ForEachChild(v Visitor) bool { + return visit(v, node.TagName) || visit(v, node.NameExpression) || visitNodeList(v, node.Comment) +} + +func (node *JSDocSeeTag) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateJSDocSeeTag(node, v.visitNode(node.TagName), v.visitNode(node.NameExpression), v.visitNodes(node.Comment)) +} + +func (node *JSDocSeeTag) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewJSDocSeeTag(node.TagName, node.NameExpression, node.Comment), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsJSDocSeeTag(node *Node) bool { + return node.Kind == KindJSDocSeeTag +} + +// ────────────────────────────────────────────────────────────────────── +// JSDocImplementsTag +// ────────────────────────────────────────────────────────────────────── + +type JSDocImplementsTag struct { + JSDocTagBase + ClassName *ExpressionWithTypeArgumentsNode +} + +func (f *NodeFactory) NewJSDocImplementsTag(tagName *IdentifierNode, className *ExpressionWithTypeArgumentsNode, comment *NodeList) *Node { + data := &JSDocImplementsTag{} + data.TagName = tagName + data.ClassName = className + data.Comment = comment + return f.newNode(KindJSDocImplementsTag, data) +} + +func (f *NodeFactory) UpdateJSDocImplementsTag(node *JSDocImplementsTag, tagName *IdentifierNode, className *ExpressionWithTypeArgumentsNode, comment *NodeList) *Node { + if tagName != node.TagName || className != node.ClassName || comment != node.Comment { + return updateNode(f.NewJSDocImplementsTag(tagName, className, comment), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *JSDocImplementsTag) ForEachChild(v Visitor) bool { + return visit(v, node.TagName) || visit(v, node.ClassName) || visitNodeList(v, node.Comment) +} + +func (node *JSDocImplementsTag) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateJSDocImplementsTag(node, v.visitNode(node.TagName), v.visitNode(node.ClassName), v.visitNodes(node.Comment)) +} + +func (node *JSDocImplementsTag) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewJSDocImplementsTag(node.TagName, node.ClassName, node.Comment), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsJSDocImplementsTag(node *Node) bool { + return node.Kind == KindJSDocImplementsTag +} + +// ────────────────────────────────────────────────────────────────────── +// JSDocAugmentsTag +// ────────────────────────────────────────────────────────────────────── + +type JSDocAugmentsTag struct { + JSDocTagBase + ClassName *ExpressionWithTypeArgumentsNode +} + +func (f *NodeFactory) NewJSDocAugmentsTag(tagName *IdentifierNode, className *ExpressionWithTypeArgumentsNode, comment *NodeList) *Node { + data := &JSDocAugmentsTag{} + data.TagName = tagName + data.ClassName = className + data.Comment = comment + return f.newNode(KindJSDocAugmentsTag, data) +} + +func (f *NodeFactory) UpdateJSDocAugmentsTag(node *JSDocAugmentsTag, tagName *IdentifierNode, className *ExpressionWithTypeArgumentsNode, comment *NodeList) *Node { + if tagName != node.TagName || className != node.ClassName || comment != node.Comment { + return updateNode(f.NewJSDocAugmentsTag(tagName, className, comment), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *JSDocAugmentsTag) ForEachChild(v Visitor) bool { + return visit(v, node.TagName) || visit(v, node.ClassName) || visitNodeList(v, node.Comment) +} + +func (node *JSDocAugmentsTag) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateJSDocAugmentsTag(node, v.visitNode(node.TagName), v.visitNode(node.ClassName), v.visitNodes(node.Comment)) +} + +func (node *JSDocAugmentsTag) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewJSDocAugmentsTag(node.TagName, node.ClassName, node.Comment), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsJSDocAugmentsTag(node *Node) bool { + return node.Kind == KindJSDocAugmentsTag +} + +// ────────────────────────────────────────────────────────────────────── +// JSDocSatisfiesTag +// ────────────────────────────────────────────────────────────────────── + +type JSDocSatisfiesTag struct { + JSDocTagBase + TypeExpression *TypeNode +} + +func (f *NodeFactory) NewJSDocSatisfiesTag(tagName *IdentifierNode, typeExpression *TypeNode, comment *NodeList) *Node { + data := &JSDocSatisfiesTag{} + data.TagName = tagName + data.TypeExpression = typeExpression + data.Comment = comment + return f.newNode(KindJSDocSatisfiesTag, data) +} + +func (f *NodeFactory) UpdateJSDocSatisfiesTag(node *JSDocSatisfiesTag, tagName *IdentifierNode, typeExpression *TypeNode, comment *NodeList) *Node { + if tagName != node.TagName || typeExpression != node.TypeExpression || comment != node.Comment { + return updateNode(f.NewJSDocSatisfiesTag(tagName, typeExpression, comment), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *JSDocSatisfiesTag) ForEachChild(v Visitor) bool { + return visit(v, node.TagName) || visit(v, node.TypeExpression) || visitNodeList(v, node.Comment) +} + +func (node *JSDocSatisfiesTag) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateJSDocSatisfiesTag(node, v.visitNode(node.TagName), v.visitNode(node.TypeExpression), v.visitNodes(node.Comment)) +} + +func (node *JSDocSatisfiesTag) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewJSDocSatisfiesTag(node.TagName, node.TypeExpression, node.Comment), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsJSDocSatisfiesTag(node *Node) bool { + return node.Kind == KindJSDocSatisfiesTag +} + +// ────────────────────────────────────────────────────────────────────── +// JSDocThrowsTag +// ────────────────────────────────────────────────────────────────────── + +type JSDocThrowsTag struct { + JSDocTagBase + TypeExpression *TypeNode // Optional +} + +func (f *NodeFactory) NewJSDocThrowsTag(tagName *IdentifierNode, typeExpression *TypeNode, comment *NodeList) *Node { + data := &JSDocThrowsTag{} + data.TagName = tagName + data.TypeExpression = typeExpression + data.Comment = comment + return f.newNode(KindJSDocThrowsTag, data) +} + +func (f *NodeFactory) UpdateJSDocThrowsTag(node *JSDocThrowsTag, tagName *IdentifierNode, typeExpression *TypeNode, comment *NodeList) *Node { + if tagName != node.TagName || typeExpression != node.TypeExpression || comment != node.Comment { + return updateNode(f.NewJSDocThrowsTag(tagName, typeExpression, comment), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *JSDocThrowsTag) ForEachChild(v Visitor) bool { + return visit(v, node.TagName) || visit(v, node.TypeExpression) || visitNodeList(v, node.Comment) +} + +func (node *JSDocThrowsTag) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateJSDocThrowsTag(node, v.visitNode(node.TagName), v.visitNode(node.TypeExpression), v.visitNodes(node.Comment)) +} + +func (node *JSDocThrowsTag) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewJSDocThrowsTag(node.TagName, node.TypeExpression, node.Comment), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsJSDocThrowsTag(node *Node) bool { + return node.Kind == KindJSDocThrowsTag +} + +// ────────────────────────────────────────────────────────────────────── +// JSDocThisTag +// ────────────────────────────────────────────────────────────────────── + +type JSDocThisTag struct { + JSDocTagBase + TypeExpression *TypeNode +} + +func (f *NodeFactory) NewJSDocThisTag(tagName *IdentifierNode, typeExpression *TypeNode, comment *NodeList) *Node { + data := &JSDocThisTag{} + data.TagName = tagName + data.TypeExpression = typeExpression + data.Comment = comment + return f.newNode(KindJSDocThisTag, data) +} + +func (f *NodeFactory) UpdateJSDocThisTag(node *JSDocThisTag, tagName *IdentifierNode, typeExpression *TypeNode, comment *NodeList) *Node { + if tagName != node.TagName || typeExpression != node.TypeExpression || comment != node.Comment { + return updateNode(f.NewJSDocThisTag(tagName, typeExpression, comment), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *JSDocThisTag) ForEachChild(v Visitor) bool { + return visit(v, node.TagName) || visit(v, node.TypeExpression) || visitNodeList(v, node.Comment) +} + +func (node *JSDocThisTag) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateJSDocThisTag(node, v.visitNode(node.TagName), v.visitNode(node.TypeExpression), v.visitNodes(node.Comment)) +} + +func (node *JSDocThisTag) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewJSDocThisTag(node.TagName, node.TypeExpression, node.Comment), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsJSDocThisTag(node *Node) bool { + return node.Kind == KindJSDocThisTag +} + +// ────────────────────────────────────────────────────────────────────── +// JSDocImportTag +// ────────────────────────────────────────────────────────────────────── + +type JSDocImportTag struct { + JSDocTagBase + ImportClause *ImportClauseNode // Optional + ModuleSpecifier *Expression + Attributes *ImportAttributesNode // Optional +} + +func (f *NodeFactory) NewJSDocImportTag(tagName *IdentifierNode, importClause *ImportClauseNode, moduleSpecifier *Expression, attributes *ImportAttributesNode, comment *NodeList) *Node { + data := &JSDocImportTag{} + data.TagName = tagName + data.ImportClause = importClause + data.ModuleSpecifier = moduleSpecifier + data.Attributes = attributes + data.Comment = comment + return f.newNode(KindJSDocImportTag, data) +} + +func (f *NodeFactory) UpdateJSDocImportTag(node *JSDocImportTag, tagName *IdentifierNode, importClause *ImportClauseNode, moduleSpecifier *Expression, attributes *ImportAttributesNode, comment *NodeList) *Node { + if tagName != node.TagName || importClause != node.ImportClause || moduleSpecifier != node.ModuleSpecifier || attributes != node.Attributes || comment != node.Comment { + return updateNode(f.NewJSDocImportTag(tagName, importClause, moduleSpecifier, attributes, comment), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *JSDocImportTag) ForEachChild(v Visitor) bool { + return visit(v, node.TagName) || + visit(v, node.ImportClause) || + visit(v, node.ModuleSpecifier) || + visit(v, node.Attributes) || + visitNodeList(v, node.Comment) +} + +func (node *JSDocImportTag) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateJSDocImportTag(node, v.visitNode(node.TagName), v.visitNode(node.ImportClause), v.visitNode(node.ModuleSpecifier), v.visitNode(node.Attributes), v.visitNodes(node.Comment)) +} + +func (node *JSDocImportTag) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewJSDocImportTag(node.TagName, node.ImportClause, node.ModuleSpecifier, node.Attributes, node.Comment), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsJSDocImportTag(node *Node) bool { + return node.Kind == KindJSDocImportTag +} + +// ────────────────────────────────────────────────────────────────────── +// JSDocCallbackTag +// ────────────────────────────────────────────────────────────────────── + +type JSDocCallbackTag struct { + JSDocTagBase + TypeExpression *TypeNode + name *JSDocFullName // Optional +} + +func (f *NodeFactory) NewJSDocCallbackTag(tagName *IdentifierNode, typeExpression *TypeNode, name *JSDocFullName, comment *NodeList) *Node { + data := &JSDocCallbackTag{} + data.TagName = tagName + data.TypeExpression = typeExpression + data.name = name + data.Comment = comment + return f.newNode(KindJSDocCallbackTag, data) +} + +func (f *NodeFactory) UpdateJSDocCallbackTag(node *JSDocCallbackTag, tagName *IdentifierNode, typeExpression *TypeNode, name *JSDocFullName, comment *NodeList) *Node { + if tagName != node.TagName || typeExpression != node.TypeExpression || name != node.name || comment != node.Comment { + return updateNode(f.NewJSDocCallbackTag(tagName, typeExpression, name, comment), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *JSDocCallbackTag) ForEachChild(v Visitor) bool { + return visit(v, node.TagName) || + visit(v, node.TypeExpression) || + visit(v, node.name) || + visitNodeList(v, node.Comment) +} + +func (node *JSDocCallbackTag) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateJSDocCallbackTag(node, v.visitNode(node.TagName), v.visitNode(node.TypeExpression), v.visitNode(node.name), v.visitNodes(node.Comment)) +} + +func (node *JSDocCallbackTag) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewJSDocCallbackTag(node.TagName, node.TypeExpression, node.name, node.Comment), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *JSDocCallbackTag) Name() *DeclarationName { + return node.name +} + +func IsJSDocCallbackTag(node *Node) bool { + return node.Kind == KindJSDocCallbackTag +} + +// ────────────────────────────────────────────────────────────────────── +// JSDocOverloadTag +// ────────────────────────────────────────────────────────────────────── + +type JSDocOverloadTag struct { + JSDocTagBase + TypeExpression *TypeNode +} + +func (f *NodeFactory) NewJSDocOverloadTag(tagName *IdentifierNode, typeExpression *TypeNode, comment *NodeList) *Node { + data := &JSDocOverloadTag{} + data.TagName = tagName + data.TypeExpression = typeExpression + data.Comment = comment + return f.newNode(KindJSDocOverloadTag, data) +} + +func (f *NodeFactory) UpdateJSDocOverloadTag(node *JSDocOverloadTag, tagName *IdentifierNode, typeExpression *TypeNode, comment *NodeList) *Node { + if tagName != node.TagName || typeExpression != node.TypeExpression || comment != node.Comment { + return updateNode(f.NewJSDocOverloadTag(tagName, typeExpression, comment), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *JSDocOverloadTag) ForEachChild(v Visitor) bool { + return visit(v, node.TagName) || visit(v, node.TypeExpression) || visitNodeList(v, node.Comment) +} + +func (node *JSDocOverloadTag) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateJSDocOverloadTag(node, v.visitNode(node.TagName), v.visitNode(node.TypeExpression), v.visitNodes(node.Comment)) +} + +func (node *JSDocOverloadTag) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewJSDocOverloadTag(node.TagName, node.TypeExpression, node.Comment), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsJSDocOverloadTag(node *Node) bool { + return node.Kind == KindJSDocOverloadTag +} + +// ────────────────────────────────────────────────────────────────────── +// JSDocTypedefTag +// ────────────────────────────────────────────────────────────────────── + +type JSDocTypedefTag struct { + JSDocTagBase + TypeExpression *Node // Optional + name *JSDocFullName // Optional +} + +func (f *NodeFactory) NewJSDocTypedefTag(tagName *IdentifierNode, typeExpression *Node, name *JSDocFullName, comment *NodeList) *Node { + data := &JSDocTypedefTag{} + data.TagName = tagName + data.TypeExpression = typeExpression + data.name = name + data.Comment = comment + return f.newNode(KindJSDocTypedefTag, data) +} + +func (f *NodeFactory) UpdateJSDocTypedefTag(node *JSDocTypedefTag, tagName *IdentifierNode, typeExpression *Node, name *JSDocFullName, comment *NodeList) *Node { + if tagName != node.TagName || typeExpression != node.TypeExpression || name != node.name || comment != node.Comment { + return updateNode(f.NewJSDocTypedefTag(tagName, typeExpression, name, comment), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *JSDocTypedefTag) ForEachChild(v Visitor) bool { + return visit(v, node.TagName) || + visit(v, node.TypeExpression) || + visit(v, node.name) || + visitNodeList(v, node.Comment) +} + +func (node *JSDocTypedefTag) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateJSDocTypedefTag(node, v.visitNode(node.TagName), v.visitNode(node.TypeExpression), v.visitNode(node.name), v.visitNodes(node.Comment)) +} + +func (node *JSDocTypedefTag) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewJSDocTypedefTag(node.TagName, node.TypeExpression, node.name, node.Comment), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *JSDocTypedefTag) Name() *DeclarationName { + return node.name +} + +func IsJSDocTypedefTag(node *Node) bool { + return node.Kind == KindJSDocTypedefTag +} + +// ────────────────────────────────────────────────────────────────────── +// JSDocSignature +// ────────────────────────────────────────────────────────────────────── + +type JSDocSignature struct { + JSDocTypeBase + FunctionLikeBase +} + +func (f *NodeFactory) NewJSDocSignature(typeParameters *TypeParameterList, parameters *ParameterList, typeNode *TypeNode) *Node { + data := &JSDocSignature{} + data.TypeParameters = typeParameters + data.Parameters = parameters + data.Type = typeNode + return f.newNode(KindJSDocSignature, data) +} + +func (f *NodeFactory) UpdateJSDocSignature(node *JSDocSignature, typeParameters *TypeParameterList, parameters *ParameterList, typeNode *TypeNode) *Node { + if typeParameters != node.TypeParameters || parameters != node.Parameters || typeNode != node.Type { + return updateNode(f.NewJSDocSignature(typeParameters, parameters, typeNode), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *JSDocSignature) ForEachChild(v Visitor) bool { + return visitNodeList(v, node.TypeParameters) || visitNodeList(v, node.Parameters) || visit(v, node.Type) +} + +func (node *JSDocSignature) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateJSDocSignature(node, v.visitNodes(node.TypeParameters), v.visitNodes(node.Parameters), v.visitNode(node.Type)) +} + +func (node *JSDocSignature) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewJSDocSignature(node.TypeParameters, node.Parameters, node.Type), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsJSDocSignature(node *Node) bool { + return node.Kind == KindJSDocSignature +} + +// ────────────────────────────────────────────────────────────────────── +// JSDocNameReference +// ────────────────────────────────────────────────────────────────────── + +type JSDocNameReference struct { + TypeNodeBase + name *EntityName +} + +func (f *NodeFactory) NewJSDocNameReference(name *EntityName) *Node { + data := &JSDocNameReference{} + data.name = name + return f.newNode(KindJSDocNameReference, data) +} + +func (f *NodeFactory) UpdateJSDocNameReference(node *JSDocNameReference, name *EntityName) *Node { + if name != node.name { + return updateNode(f.NewJSDocNameReference(name), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *JSDocNameReference) ForEachChild(v Visitor) bool { + return visit(v, node.name) +} + +func (node *JSDocNameReference) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateJSDocNameReference(node, v.visitNode(node.name)) +} + +func (node *JSDocNameReference) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewJSDocNameReference(node.name), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *JSDocNameReference) Name() *DeclarationName { + return node.name +} + +func IsJSDocNameReference(node *Node) bool { + return node.Kind == KindJSDocNameReference +} + +// ────────────────────────────────────────────────────────────────────── +// SourceFile +// ────────────────────────────────────────────────────────────────────── + +func IsSourceFile(node *Node) bool { + return node.Kind == KindSourceFile +} + +// Struct and factory methods hand-written in ./ast.go +// ────────────────────────────────────────────────────────────────────── +// ModuleDeclaration +// ────────────────────────────────────────────────────────────────────── + +type ModuleDeclaration struct { + DeclarationBase + StatementBase + ExportableBase + ModifiersBase + LocalsContainerBase + BodyBase + CompositeBase + Keyword Kind + name *ModuleName +} + +func (f *NodeFactory) NewModuleDeclaration(modifiers *ModifierList, keyword Kind, name *ModuleName, body *ModuleBody) *Node { + data := &ModuleDeclaration{} + data.modifiers = modifiers + data.Keyword = keyword + data.name = name + data.Body = body + return f.newNode(KindModuleDeclaration, data) +} + +func (f *NodeFactory) UpdateModuleDeclaration(node *ModuleDeclaration, modifiers *ModifierList, keyword Kind, name *ModuleName, body *ModuleBody) *Node { + if modifiers != node.modifiers || keyword != node.Keyword || name != node.name || body != node.Body { + return updateNode(f.NewModuleDeclaration(modifiers, keyword, name, body), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *ModuleDeclaration) ForEachChild(v Visitor) bool { + return visitModifiers(v, node.modifiers) || visit(v, node.name) || visit(v, node.Body) +} + +func (node *ModuleDeclaration) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateModuleDeclaration(node, v.visitModifiers(node.modifiers), node.Keyword, v.visitNode(node.name), v.visitNode(node.Body)) +} + +func (node *ModuleDeclaration) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewModuleDeclaration(node.Modifiers(), node.Keyword, node.name, node.Body), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *ModuleDeclaration) Name() *DeclarationName { + return node.name +} + +func IsModuleDeclaration(node *Node) bool { + return node.Kind == KindModuleDeclaration +} + +// ────────────────────────────────────────────────────────────────────── +// ImportEqualsDeclaration +// ────────────────────────────────────────────────────────────────────── + +type ImportEqualsDeclaration struct { + DeclarationBase + StatementBase + ExportableBase + ModifiersBase + CompositeBase + IsTypeOnly bool + name *IdentifierNode + ModuleReference *ModuleReference +} + +func (f *NodeFactory) NewImportEqualsDeclaration(modifiers *ModifierList, isTypeOnly bool, name *IdentifierNode, moduleReference *ModuleReference) *Node { + data := &ImportEqualsDeclaration{} + data.modifiers = modifiers + data.IsTypeOnly = isTypeOnly + data.name = name + data.ModuleReference = moduleReference + return f.newNode(KindImportEqualsDeclaration, data) +} + +func (f *NodeFactory) UpdateImportEqualsDeclaration(node *ImportEqualsDeclaration, modifiers *ModifierList, isTypeOnly bool, name *IdentifierNode, moduleReference *ModuleReference) *Node { + if modifiers != node.modifiers || isTypeOnly != node.IsTypeOnly || name != node.name || moduleReference != node.ModuleReference { + return updateNode(f.NewImportEqualsDeclaration(modifiers, isTypeOnly, name, moduleReference), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *ImportEqualsDeclaration) ForEachChild(v Visitor) bool { + return visitModifiers(v, node.modifiers) || visit(v, node.name) || visit(v, node.ModuleReference) +} + +func (node *ImportEqualsDeclaration) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateImportEqualsDeclaration(node, v.visitModifiers(node.modifiers), node.IsTypeOnly, v.visitNode(node.name), v.visitNode(node.ModuleReference)) +} + +func (node *ImportEqualsDeclaration) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewImportEqualsDeclaration(node.Modifiers(), node.IsTypeOnly, node.name, node.ModuleReference), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *ImportEqualsDeclaration) Name() *DeclarationName { + return node.name +} + +func IsImportEqualsDeclaration(node *Node) bool { + return node.Kind == KindImportEqualsDeclaration +} + +// ────────────────────────────────────────────────────────────────────── +// ExportDeclaration +// ────────────────────────────────────────────────────────────────────── + +type ExportDeclaration struct { + DeclarationBase + StatementBase + ModifiersBase + CompositeBase + IsTypeOnly bool + ExportClause *NamedExportBindings // Optional + ModuleSpecifier *Expression // Optional + Attributes *ImportAttributesNode // Optional +} + +func (f *NodeFactory) NewExportDeclaration(modifiers *ModifierList, isTypeOnly bool, exportClause *NamedExportBindings, moduleSpecifier *Expression, attributes *ImportAttributesNode) *Node { + data := &ExportDeclaration{} + data.modifiers = modifiers + data.IsTypeOnly = isTypeOnly + data.ExportClause = exportClause + data.ModuleSpecifier = moduleSpecifier + data.Attributes = attributes + return f.newNode(KindExportDeclaration, data) +} + +func (f *NodeFactory) UpdateExportDeclaration(node *ExportDeclaration, modifiers *ModifierList, isTypeOnly bool, exportClause *NamedExportBindings, moduleSpecifier *Expression, attributes *ImportAttributesNode) *Node { + if modifiers != node.modifiers || isTypeOnly != node.IsTypeOnly || exportClause != node.ExportClause || moduleSpecifier != node.ModuleSpecifier || attributes != node.Attributes { + return updateNode(f.NewExportDeclaration(modifiers, isTypeOnly, exportClause, moduleSpecifier, attributes), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *ExportDeclaration) ForEachChild(v Visitor) bool { + return visitModifiers(v, node.modifiers) || + visit(v, node.ExportClause) || + visit(v, node.ModuleSpecifier) || + visit(v, node.Attributes) +} + +func (node *ExportDeclaration) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateExportDeclaration(node, v.visitModifiers(node.modifiers), node.IsTypeOnly, v.visitNode(node.ExportClause), v.visitNode(node.ModuleSpecifier), v.visitNode(node.Attributes)) +} + +func (node *ExportDeclaration) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewExportDeclaration(node.Modifiers(), node.IsTypeOnly, node.ExportClause, node.ModuleSpecifier, node.Attributes), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsExportDeclaration(node *Node) bool { + return node.Kind == KindExportDeclaration +} + +// ────────────────────────────────────────────────────────────────────── +// ImportTypeNode +// ────────────────────────────────────────────────────────────────────── + +type ImportTypeNode struct { + NodeWithTypeArgumentsBase + IsTypeOf bool + Argument *TypeNode + Attributes *ImportAttributesNode // Optional + Qualifier *EntityName // Optional +} + +func (f *NodeFactory) NewImportTypeNode(isTypeOf bool, argument *TypeNode, attributes *ImportAttributesNode, qualifier *EntityName, typeArguments *TypeList) *Node { + data := &ImportTypeNode{} + data.IsTypeOf = isTypeOf + data.Argument = argument + data.Attributes = attributes + data.Qualifier = qualifier + data.TypeArguments = typeArguments + return f.newNode(KindImportType, data) +} + +func (f *NodeFactory) UpdateImportTypeNode(node *ImportTypeNode, isTypeOf bool, argument *TypeNode, attributes *ImportAttributesNode, qualifier *EntityName, typeArguments *TypeList) *Node { + if isTypeOf != node.IsTypeOf || argument != node.Argument || attributes != node.Attributes || qualifier != node.Qualifier || typeArguments != node.TypeArguments { + return updateNode(f.NewImportTypeNode(isTypeOf, argument, attributes, qualifier, typeArguments), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *ImportTypeNode) ForEachChild(v Visitor) bool { + return visit(v, node.Argument) || + visit(v, node.Attributes) || + visit(v, node.Qualifier) || + visitNodeList(v, node.TypeArguments) +} + +func (node *ImportTypeNode) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateImportTypeNode(node, node.IsTypeOf, v.visitNode(node.Argument), v.visitNode(node.Attributes), v.visitNode(node.Qualifier), v.visitNodes(node.TypeArguments)) +} + +func (node *ImportTypeNode) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewImportTypeNode(node.IsTypeOf, node.Argument, node.Attributes, node.Qualifier, node.TypeArguments), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsImportTypeNode(node *Node) bool { + return node.Kind == KindImportType +} + +// ────────────────────────────────────────────────────────────────────── +// ImportClause +// ────────────────────────────────────────────────────────────────────── + +type ImportClause struct { + NodeBase + DeclarationBase + ExportableBase + CompositeBase + PhaseModifier ImportPhaseModifierSyntaxKind // Optional + name *IdentifierNode // Optional + NamedBindings *NamedImportBindings // Optional +} + +func (f *NodeFactory) NewImportClause(phaseModifier ImportPhaseModifierSyntaxKind, name *IdentifierNode, namedBindings *NamedImportBindings) *Node { + data := &ImportClause{} + data.PhaseModifier = phaseModifier + data.name = name + data.NamedBindings = namedBindings + return f.newNode(KindImportClause, data) +} + +func (f *NodeFactory) UpdateImportClause(node *ImportClause, phaseModifier ImportPhaseModifierSyntaxKind, name *IdentifierNode, namedBindings *NamedImportBindings) *Node { + if phaseModifier != node.PhaseModifier || name != node.name || namedBindings != node.NamedBindings { + return updateNode(f.NewImportClause(phaseModifier, name, namedBindings), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *ImportClause) ForEachChild(v Visitor) bool { + return visit(v, node.name) || visit(v, node.NamedBindings) +} + +func (node *ImportClause) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateImportClause(node, node.PhaseModifier, v.visitNode(node.name), v.visitNode(node.NamedBindings)) +} + +func (node *ImportClause) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewImportClause(node.PhaseModifier, node.name, node.NamedBindings), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *ImportClause) Name() *DeclarationName { + return node.name +} + +func IsImportClause(node *Node) bool { + return node.Kind == KindImportClause +} + +// ────────────────────────────────────────────────────────────────────── +// ImportSpecifier +// ────────────────────────────────────────────────────────────────────── + +type ImportSpecifier struct { + NodeBase + DeclarationBase + ExportableBase + CompositeBase + IsTypeOnly bool + PropertyName *ModuleExportName // Optional + name *IdentifierNode +} + +func (f *NodeFactory) NewImportSpecifier(isTypeOnly bool, propertyName *ModuleExportName, name *IdentifierNode) *Node { + data := f.importSpecifierArena.New() + data.IsTypeOnly = isTypeOnly + data.PropertyName = propertyName + data.name = name + return f.newNode(KindImportSpecifier, data) +} + +func (f *NodeFactory) UpdateImportSpecifier(node *ImportSpecifier, isTypeOnly bool, propertyName *ModuleExportName, name *IdentifierNode) *Node { + if isTypeOnly != node.IsTypeOnly || propertyName != node.PropertyName || name != node.name { + return updateNode(f.NewImportSpecifier(isTypeOnly, propertyName, name), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *ImportSpecifier) ForEachChild(v Visitor) bool { + return visit(v, node.PropertyName) || visit(v, node.name) +} + +func (node *ImportSpecifier) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateImportSpecifier(node, node.IsTypeOnly, v.visitNode(node.PropertyName), v.visitNode(node.name)) +} + +func (node *ImportSpecifier) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewImportSpecifier(node.IsTypeOnly, node.PropertyName, node.name), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *ImportSpecifier) Name() *DeclarationName { + return node.name +} + +func IsImportSpecifier(node *Node) bool { + return node.Kind == KindImportSpecifier +} + +// ────────────────────────────────────────────────────────────────────── +// JSDocText +// ────────────────────────────────────────────────────────────────────── + +type JSDocText struct { + JSDocCommentBase +} + +func (f *NodeFactory) NewJSDocText(text []string) *Node { + data := f.jsdocTextArena.New() + data.text = text + f.textCount++ + return f.newNode(KindJSDocText, data) +} + +func (node *JSDocText) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewJSDocText(node.text), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsJSDocText(node *Node) bool { + return node.Kind == KindJSDocText +} + +// ────────────────────────────────────────────────────────────────────── +// JSDocLink +// ────────────────────────────────────────────────────────────────────── + +type JSDocLink struct { + JSDocCommentBase + name *EntityName // Optional +} + +func (f *NodeFactory) NewJSDocLink(name *EntityName, text []string) *Node { + data := &JSDocLink{} + data.name = name + data.text = text + f.textCount++ + return f.newNode(KindJSDocLink, data) +} + +func (f *NodeFactory) UpdateJSDocLink(node *JSDocLink, name *EntityName, text []string) *Node { + if name != node.name || !core.Same(text, node.text) { + return updateNode(f.NewJSDocLink(name, text), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *JSDocLink) ForEachChild(v Visitor) bool { + return visit(v, node.name) +} + +func (node *JSDocLink) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateJSDocLink(node, v.visitNode(node.name), node.text) +} + +func (node *JSDocLink) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewJSDocLink(node.name, node.text), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *JSDocLink) Name() *DeclarationName { + return node.name +} + +func IsJSDocLink(node *Node) bool { + return node.Kind == KindJSDocLink +} + +// ────────────────────────────────────────────────────────────────────── +// JSDocLinkPlain +// ────────────────────────────────────────────────────────────────────── + +type JSDocLinkPlain struct { + JSDocCommentBase + name *EntityName // Optional +} + +func (f *NodeFactory) NewJSDocLinkPlain(name *EntityName, text []string) *Node { + data := &JSDocLinkPlain{} + data.name = name + data.text = text + f.textCount++ + return f.newNode(KindJSDocLinkPlain, data) +} + +func (f *NodeFactory) UpdateJSDocLinkPlain(node *JSDocLinkPlain, name *EntityName, text []string) *Node { + if name != node.name || !core.Same(text, node.text) { + return updateNode(f.NewJSDocLinkPlain(name, text), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *JSDocLinkPlain) ForEachChild(v Visitor) bool { + return visit(v, node.name) +} + +func (node *JSDocLinkPlain) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateJSDocLinkPlain(node, v.visitNode(node.name), node.text) +} + +func (node *JSDocLinkPlain) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewJSDocLinkPlain(node.name, node.text), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *JSDocLinkPlain) Name() *DeclarationName { + return node.name +} + +func IsJSDocLinkPlain(node *Node) bool { + return node.Kind == KindJSDocLinkPlain +} + +// ────────────────────────────────────────────────────────────────────── +// JSDocLinkCode +// ────────────────────────────────────────────────────────────────────── + +type JSDocLinkCode struct { + JSDocCommentBase + name *EntityName // Optional +} + +func (f *NodeFactory) NewJSDocLinkCode(name *EntityName, text []string) *Node { + data := &JSDocLinkCode{} + data.name = name + data.text = text + f.textCount++ + return f.newNode(KindJSDocLinkCode, data) +} + +func (f *NodeFactory) UpdateJSDocLinkCode(node *JSDocLinkCode, name *EntityName, text []string) *Node { + if name != node.name || !core.Same(text, node.text) { + return updateNode(f.NewJSDocLinkCode(name, text), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *JSDocLinkCode) ForEachChild(v Visitor) bool { + return visit(v, node.name) +} + +func (node *JSDocLinkCode) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateJSDocLinkCode(node, v.visitNode(node.name), node.text) +} + +func (node *JSDocLinkCode) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewJSDocLinkCode(node.name, node.text), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *JSDocLinkCode) Name() *DeclarationName { + return node.name +} + +func IsJSDocLinkCode(node *Node) bool { + return node.Kind == KindJSDocLinkCode +} + +// ────────────────────────────────────────────────────────────────────── +// TypeParameterDeclaration +// ────────────────────────────────────────────────────────────────────── + +type TypeParameterDeclaration struct { + NodeBase + DeclarationBase + ModifiersBase + TypeSyntaxBase + name *IdentifierNode + Constraint *TypeNode // Optional + Expression *Expression // Optional + DefaultType *TypeNode // Optional +} + +func (f *NodeFactory) NewTypeParameterDeclaration(modifiers *ModifierList, name *IdentifierNode, constraint *TypeNode, expression *Expression, defaultType *TypeNode) *Node { + data := f.typeParameterDeclarationArena.New() + data.modifiers = modifiers + data.name = name + data.Constraint = constraint + data.Expression = expression + data.DefaultType = defaultType + return f.newNode(KindTypeParameter, data) +} + +func (f *NodeFactory) UpdateTypeParameterDeclaration(node *TypeParameterDeclaration, modifiers *ModifierList, name *IdentifierNode, constraint *TypeNode, expression *Expression, defaultType *TypeNode) *Node { + if modifiers != node.modifiers || name != node.name || constraint != node.Constraint || expression != node.Expression || defaultType != node.DefaultType { + return updateNode(f.NewTypeParameterDeclaration(modifiers, name, constraint, expression, defaultType), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *TypeParameterDeclaration) ForEachChild(v Visitor) bool { + return visitModifiers(v, node.modifiers) || + visit(v, node.name) || + visit(v, node.Constraint) || + visit(v, node.Expression) || + visit(v, node.DefaultType) +} + +func (node *TypeParameterDeclaration) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateTypeParameterDeclaration(node, v.visitModifiers(node.modifiers), v.visitNode(node.name), v.visitNode(node.Constraint), v.visitNode(node.Expression), v.visitNode(node.DefaultType)) +} + +func (node *TypeParameterDeclaration) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewTypeParameterDeclaration(node.Modifiers(), node.name, node.Constraint, node.Expression, node.DefaultType), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *TypeParameterDeclaration) Name() *DeclarationName { + return node.name +} + +func IsTypeParameterDeclaration(node *Node) bool { + return node.Kind == KindTypeParameter +} + +// ────────────────────────────────────────────────────────────────────── +// SyntheticReferenceExpression +// ────────────────────────────────────────────────────────────────────── + +type SyntheticReferenceExpression struct { + ExpressionBase + Expression *Expression + ThisArg *Expression +} + +func (f *NodeFactory) NewSyntheticReferenceExpression(expression *Expression, thisArg *Expression) *Node { + data := &SyntheticReferenceExpression{} + data.Expression = expression + data.ThisArg = thisArg + return f.newNode(KindSyntheticReferenceExpression, data) +} + +func (f *NodeFactory) UpdateSyntheticReferenceExpression(node *SyntheticReferenceExpression, expression *Expression, thisArg *Expression) *Node { + if expression != node.Expression || thisArg != node.ThisArg { + return updateNode(f.NewSyntheticReferenceExpression(expression, thisArg), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *SyntheticReferenceExpression) ForEachChild(v Visitor) bool { + return visit(v, node.Expression) || visit(v, node.ThisArg) +} + +func (node *SyntheticReferenceExpression) VisitEachChild(v *NodeVisitor) *Node { + return v.Factory.UpdateSyntheticReferenceExpression(node, v.visitNode(node.Expression), v.visitNode(node.ThisArg)) +} + +func (node *SyntheticReferenceExpression) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewSyntheticReferenceExpression(node.Expression, node.ThisArg), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *SyntheticReferenceExpression) computeSubtreeFacts() SubtreeFacts { + return propagateSubtreeFacts(node.Expression) | + propagateSubtreeFacts(node.ThisArg) +} + +func IsSyntheticReferenceExpression(node *Node) bool { + return node.Kind == KindSyntheticReferenceExpression +} + +// ────────────────────────────────────────────────────────────────────── +// JSDocTypeLiteral +// ────────────────────────────────────────────────────────────────────── + +type JSDocTypeLiteral struct { + JSDocTypeBase + DeclarationBase + JSDocPropertyTags []*Node // Optional + IsArrayType bool +} + +func (f *NodeFactory) NewJSDocTypeLiteral(jsdocPropertyTags []*Node, isArrayType bool) *Node { + data := &JSDocTypeLiteral{} + data.JSDocPropertyTags = jsdocPropertyTags + data.IsArrayType = isArrayType + return f.newNode(KindJSDocTypeLiteral, data) +} + +func (f *NodeFactory) UpdateJSDocTypeLiteral(node *JSDocTypeLiteral, jsdocPropertyTags []*Node, isArrayType bool) *Node { + if !core.Same(jsdocPropertyTags, node.JSDocPropertyTags) || isArrayType != node.IsArrayType { + return updateNode(f.NewJSDocTypeLiteral(jsdocPropertyTags, isArrayType), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *JSDocTypeLiteral) ForEachChild(v Visitor) bool { + return visitNodes(v, node.JSDocPropertyTags) +} + +func (node *JSDocTypeLiteral) VisitEachChild(v *NodeVisitor) *Node { + jsdocPropertyTags := core.SameMap(node.JSDocPropertyTags, func(n *Node) *Node { return v.visitNode(n) }) + return v.Factory.UpdateJSDocTypeLiteral(node, jsdocPropertyTags, node.IsArrayType) +} + +func (node *JSDocTypeLiteral) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewJSDocTypeLiteral(node.JSDocPropertyTags, node.IsArrayType), node.AsNode(), f.AsNodeFactory().hooks) +} + +func IsJSDocTypeLiteral(node *Node) bool { + return node.Kind == KindJSDocTypeLiteral +} + +// ────────────────────────────────────────────────────────────────────── +// JSDocParameterOrPropertyTag +// ────────────────────────────────────────────────────────────────────── + +type JSDocParameterOrPropertyTag struct { + JSDocTagBase + name *EntityName + IsBracketed bool + TypeExpression *TypeNode // Optional + IsNameFirst bool +} + +func (f *NodeFactory) NewJSDocParameterOrPropertyTag(kind Kind, tagName *IdentifierNode, name *EntityName, isBracketed bool, typeExpression *TypeNode, isNameFirst bool, comment *NodeList) *Node { + data := &JSDocParameterOrPropertyTag{} + data.TagName = tagName + data.name = name + data.IsBracketed = isBracketed + data.TypeExpression = typeExpression + data.IsNameFirst = isNameFirst + data.Comment = comment + return f.newNode(kind, data) +} + +func (f *NodeFactory) UpdateJSDocParameterOrPropertyTag(node *JSDocParameterOrPropertyTag, tagName *IdentifierNode, name *EntityName, isBracketed bool, typeExpression *TypeNode, isNameFirst bool, comment *NodeList) *Node { + if tagName != node.TagName || name != node.name || isBracketed != node.IsBracketed || typeExpression != node.TypeExpression || isNameFirst != node.IsNameFirst || comment != node.Comment { + return updateNode(f.NewJSDocParameterOrPropertyTag(node.Kind, tagName, name, isBracketed, typeExpression, isNameFirst, comment), node.AsNode(), f.hooks) + } + return node.AsNode() +} + +func (node *JSDocParameterOrPropertyTag) ForEachChild(v Visitor) bool { + return forEachChild_JSDocParameterOrPropertyTag(node, v) +} + +func (node *JSDocParameterOrPropertyTag) VisitEachChild(v *NodeVisitor) *Node { + return visitEachChild_JSDocParameterOrPropertyTag(node, v) +} + +func (node *JSDocParameterOrPropertyTag) Clone(f NodeFactoryCoercible) *Node { + return cloneNode(f.AsNodeFactory().NewJSDocParameterOrPropertyTag(node.Kind, node.TagName, node.name, node.IsBracketed, node.TypeExpression, node.IsNameFirst, node.Comment), node.AsNode(), f.AsNodeFactory().hooks) +} + +func (node *JSDocParameterOrPropertyTag) Name() *DeclarationName { + return node.name +} + +func IsJSDocParameterTag(node *Node) bool { + return node.Kind == KindJSDocParameterTag +} + +func IsJSDocPropertyTag(node *Node) bool { + return node.Kind == KindJSDocPropertyTag +} + +// ────────────────────────────────────────────────────────────────────── +// ForEachChild dispatch +// ────────────────────────────────────────────────────────────────────── + +func (n *Node) ForEachChild(v Visitor) bool { + switch n.Kind { + case KindQualifiedName: + return n.data.(*QualifiedName).ForEachChild(v) + case KindComputedPropertyName: + return n.data.(*ComputedPropertyName).ForEachChild(v) + case KindDecorator: + return n.data.(*Decorator).ForEachChild(v) + case KindIfStatement: + return n.data.(*IfStatement).ForEachChild(v) + case KindDoStatement: + return n.data.(*DoStatement).ForEachChild(v) + case KindWhileStatement: + return n.data.(*WhileStatement).ForEachChild(v) + case KindForStatement: + return n.data.(*ForStatement).ForEachChild(v) + case KindForInStatement, KindForOfStatement: + return n.data.(*ForInOrOfStatement).ForEachChild(v) + case KindBreakStatement: + return n.data.(*BreakStatement).ForEachChild(v) + case KindContinueStatement: + return n.data.(*ContinueStatement).ForEachChild(v) + case KindReturnStatement: + return n.data.(*ReturnStatement).ForEachChild(v) + case KindWithStatement: + return n.data.(*WithStatement).ForEachChild(v) + case KindSwitchStatement: + return n.data.(*SwitchStatement).ForEachChild(v) + case KindCaseBlock: + return n.data.(*CaseBlock).ForEachChild(v) + case KindCaseClause, KindDefaultClause: + return n.data.(*CaseOrDefaultClause).ForEachChild(v) + case KindThrowStatement: + return n.data.(*ThrowStatement).ForEachChild(v) + case KindTryStatement: + return n.data.(*TryStatement).ForEachChild(v) + case KindCatchClause: + return n.data.(*CatchClause).ForEachChild(v) + case KindLabeledStatement: + return n.data.(*LabeledStatement).ForEachChild(v) + case KindExpressionStatement: + return n.data.(*ExpressionStatement).ForEachChild(v) + case KindBlock: + return n.data.(*Block).ForEachChild(v) + case KindVariableStatement: + return n.data.(*VariableStatement).ForEachChild(v) + case KindVariableDeclaration: + return n.data.(*VariableDeclaration).ForEachChild(v) + case KindVariableDeclarationList: + return n.data.(*VariableDeclarationList).ForEachChild(v) + case KindObjectBindingPattern, KindArrayBindingPattern: + return n.data.(*BindingPattern).ForEachChild(v) + case KindParameter: + return n.data.(*ParameterDeclaration).ForEachChild(v) + case KindBindingElement: + return n.data.(*BindingElement).ForEachChild(v) + case KindMissingDeclaration: + return n.data.(*MissingDeclaration).ForEachChild(v) + case KindFunctionDeclaration: + return n.data.(*FunctionDeclaration).ForEachChild(v) + case KindClassDeclaration: + return n.data.(*ClassDeclaration).ForEachChild(v) + case KindClassExpression: + return n.data.(*ClassExpression).ForEachChild(v) + case KindHeritageClause: + return n.data.(*HeritageClause).ForEachChild(v) + case KindInterfaceDeclaration: + return n.data.(*InterfaceDeclaration).ForEachChild(v) + case KindTypeAliasDeclaration, KindJSTypeAliasDeclaration: + return n.data.(*TypeAliasDeclaration).ForEachChild(v) + case KindEnumMember: + return n.data.(*EnumMember).ForEachChild(v) + case KindEnumDeclaration: + return n.data.(*EnumDeclaration).ForEachChild(v) + case KindModuleBlock: + return n.data.(*ModuleBlock).ForEachChild(v) + case KindImportDeclaration, KindJSImportDeclaration: + return n.data.(*ImportDeclaration).ForEachChild(v) + case KindExternalModuleReference: + return n.data.(*ExternalModuleReference).ForEachChild(v) + case KindNamespaceImport: + return n.data.(*NamespaceImport).ForEachChild(v) + case KindNamedImports: + return n.data.(*NamedImports).ForEachChild(v) + case KindExportAssignment: + return n.data.(*ExportAssignment).ForEachChild(v) + case KindNamespaceExportDeclaration: + return n.data.(*NamespaceExportDeclaration).ForEachChild(v) + case KindNamespaceExport: + return n.data.(*NamespaceExport).ForEachChild(v) + case KindNamedExports: + return n.data.(*NamedExports).ForEachChild(v) + case KindExportSpecifier: + return n.data.(*ExportSpecifier).ForEachChild(v) + case KindCallSignature: + return n.data.(*CallSignatureDeclaration).ForEachChild(v) + case KindConstructSignature: + return n.data.(*ConstructSignatureDeclaration).ForEachChild(v) + case KindConstructor: + return n.data.(*ConstructorDeclaration).ForEachChild(v) + case KindGetAccessor: + return n.data.(*GetAccessorDeclaration).ForEachChild(v) + case KindSetAccessor: + return n.data.(*SetAccessorDeclaration).ForEachChild(v) + case KindIndexSignature: + return n.data.(*IndexSignatureDeclaration).ForEachChild(v) + case KindMethodSignature: + return n.data.(*MethodSignatureDeclaration).ForEachChild(v) + case KindMethodDeclaration: + return n.data.(*MethodDeclaration).ForEachChild(v) + case KindPropertySignature: + return n.data.(*PropertySignatureDeclaration).ForEachChild(v) + case KindPropertyDeclaration: + return n.data.(*PropertyDeclaration).ForEachChild(v) + case KindClassStaticBlockDeclaration: + return n.data.(*ClassStaticBlockDeclaration).ForEachChild(v) + case KindBinaryExpression: + return n.data.(*BinaryExpression).ForEachChild(v) + case KindPrefixUnaryExpression: + return n.data.(*PrefixUnaryExpression).ForEachChild(v) + case KindPostfixUnaryExpression: + return n.data.(*PostfixUnaryExpression).ForEachChild(v) + case KindYieldExpression: + return n.data.(*YieldExpression).ForEachChild(v) + case KindArrowFunction: + return n.data.(*ArrowFunction).ForEachChild(v) + case KindFunctionExpression: + return n.data.(*FunctionExpression).ForEachChild(v) + case KindAsExpression: + return n.data.(*AsExpression).ForEachChild(v) + case KindSatisfiesExpression: + return n.data.(*SatisfiesExpression).ForEachChild(v) + case KindConditionalExpression: + return n.data.(*ConditionalExpression).ForEachChild(v) + case KindPropertyAccessExpression: + return n.data.(*PropertyAccessExpression).ForEachChild(v) + case KindElementAccessExpression: + return n.data.(*ElementAccessExpression).ForEachChild(v) + case KindCallExpression: + return n.data.(*CallExpression).ForEachChild(v) + case KindNewExpression: + return n.data.(*NewExpression).ForEachChild(v) + case KindMetaProperty: + return n.data.(*MetaProperty).ForEachChild(v) + case KindNonNullExpression: + return n.data.(*NonNullExpression).ForEachChild(v) + case KindSpreadElement: + return n.data.(*SpreadElement).ForEachChild(v) + case KindTemplateExpression: + return n.data.(*TemplateExpression).ForEachChild(v) + case KindTemplateSpan: + return n.data.(*TemplateSpan).ForEachChild(v) + case KindTaggedTemplateExpression: + return n.data.(*TaggedTemplateExpression).ForEachChild(v) + case KindParenthesizedExpression: + return n.data.(*ParenthesizedExpression).ForEachChild(v) + case KindArrayLiteralExpression: + return n.data.(*ArrayLiteralExpression).ForEachChild(v) + case KindObjectLiteralExpression: + return n.data.(*ObjectLiteralExpression).ForEachChild(v) + case KindSpreadAssignment: + return n.data.(*SpreadAssignment).ForEachChild(v) + case KindPropertyAssignment: + return n.data.(*PropertyAssignment).ForEachChild(v) + case KindShorthandPropertyAssignment: + return n.data.(*ShorthandPropertyAssignment).ForEachChild(v) + case KindDeleteExpression: + return n.data.(*DeleteExpression).ForEachChild(v) + case KindTypeOfExpression: + return n.data.(*TypeOfExpression).ForEachChild(v) + case KindVoidExpression: + return n.data.(*VoidExpression).ForEachChild(v) + case KindAwaitExpression: + return n.data.(*AwaitExpression).ForEachChild(v) + case KindTypeAssertionExpression: + return n.data.(*TypeAssertion).ForEachChild(v) + case KindUnionType: + return n.data.(*UnionTypeNode).ForEachChild(v) + case KindIntersectionType: + return n.data.(*IntersectionTypeNode).ForEachChild(v) + case KindConditionalType: + return n.data.(*ConditionalTypeNode).ForEachChild(v) + case KindTypeOperator: + return n.data.(*TypeOperatorNode).ForEachChild(v) + case KindInferType: + return n.data.(*InferTypeNode).ForEachChild(v) + case KindArrayType: + return n.data.(*ArrayTypeNode).ForEachChild(v) + case KindIndexedAccessType: + return n.data.(*IndexedAccessTypeNode).ForEachChild(v) + case KindTypeReference: + return n.data.(*TypeReferenceNode).ForEachChild(v) + case KindExpressionWithTypeArguments: + return n.data.(*ExpressionWithTypeArguments).ForEachChild(v) + case KindLiteralType: + return n.data.(*LiteralTypeNode).ForEachChild(v) + case KindTypePredicate: + return n.data.(*TypePredicateNode).ForEachChild(v) + case KindImportAttribute: + return n.data.(*ImportAttribute).ForEachChild(v) + case KindImportAttributes: + return n.data.(*ImportAttributes).ForEachChild(v) + case KindTypeQuery: + return n.data.(*TypeQueryNode).ForEachChild(v) + case KindMappedType: + return n.data.(*MappedTypeNode).ForEachChild(v) + case KindTypeLiteral: + return n.data.(*TypeLiteralNode).ForEachChild(v) + case KindTupleType: + return n.data.(*TupleTypeNode).ForEachChild(v) + case KindNamedTupleMember: + return n.data.(*NamedTupleMember).ForEachChild(v) + case KindOptionalType: + return n.data.(*OptionalTypeNode).ForEachChild(v) + case KindRestType: + return n.data.(*RestTypeNode).ForEachChild(v) + case KindParenthesizedType: + return n.data.(*ParenthesizedTypeNode).ForEachChild(v) + case KindFunctionType: + return n.data.(*FunctionTypeNode).ForEachChild(v) + case KindConstructorType: + return n.data.(*ConstructorTypeNode).ForEachChild(v) + case KindTemplateLiteralType: + return n.data.(*TemplateLiteralTypeNode).ForEachChild(v) + case KindTemplateLiteralTypeSpan: + return n.data.(*TemplateLiteralTypeSpan).ForEachChild(v) + case KindSyntheticExpression: + return n.data.(*SyntheticExpression).ForEachChild(v) + case KindPartiallyEmittedExpression: + return n.data.(*PartiallyEmittedExpression).ForEachChild(v) + case KindJsxElement: + return n.data.(*JsxElement).ForEachChild(v) + case KindJsxAttributes: + return n.data.(*JsxAttributes).ForEachChild(v) + case KindJsxNamespacedName: + return n.data.(*JsxNamespacedName).ForEachChild(v) + case KindJsxOpeningElement: + return n.data.(*JsxOpeningElement).ForEachChild(v) + case KindJsxSelfClosingElement: + return n.data.(*JsxSelfClosingElement).ForEachChild(v) + case KindJsxFragment: + return n.data.(*JsxFragment).ForEachChild(v) + case KindJsxAttribute: + return n.data.(*JsxAttribute).ForEachChild(v) + case KindJsxSpreadAttribute: + return n.data.(*JsxSpreadAttribute).ForEachChild(v) + case KindJsxClosingElement: + return n.data.(*JsxClosingElement).ForEachChild(v) + case KindJsxExpression: + return n.data.(*JsxExpression).ForEachChild(v) + case KindSyntaxList: + return n.data.(*SyntaxList).ForEachChild(v) + case KindJSDoc: + return n.data.(*JSDoc).ForEachChild(v) + case KindJSDocTypeExpression: + return n.data.(*JSDocTypeExpression).ForEachChild(v) + case KindJSDocNonNullableType: + return n.data.(*JSDocNonNullableType).ForEachChild(v) + case KindJSDocNullableType: + return n.data.(*JSDocNullableType).ForEachChild(v) + case KindJSDocVariadicType: + return n.data.(*JSDocVariadicType).ForEachChild(v) + case KindJSDocOptionalType: + return n.data.(*JSDocOptionalType).ForEachChild(v) + case KindJSDocTypeTag: + return n.data.(*JSDocTypeTag).ForEachChild(v) + case KindJSDocUnknownTag: + return n.data.(*JSDocUnknownTag).ForEachChild(v) + case KindJSDocTemplateTag: + return n.data.(*JSDocTemplateTag).ForEachChild(v) + case KindJSDocReturnTag: + return n.data.(*JSDocReturnTag).ForEachChild(v) + case KindJSDocPublicTag: + return n.data.(*JSDocPublicTag).ForEachChild(v) + case KindJSDocPrivateTag: + return n.data.(*JSDocPrivateTag).ForEachChild(v) + case KindJSDocProtectedTag: + return n.data.(*JSDocProtectedTag).ForEachChild(v) + case KindJSDocReadonlyTag: + return n.data.(*JSDocReadonlyTag).ForEachChild(v) + case KindJSDocOverrideTag: + return n.data.(*JSDocOverrideTag).ForEachChild(v) + case KindJSDocDeprecatedTag: + return n.data.(*JSDocDeprecatedTag).ForEachChild(v) + case KindJSDocSeeTag: + return n.data.(*JSDocSeeTag).ForEachChild(v) + case KindJSDocImplementsTag: + return n.data.(*JSDocImplementsTag).ForEachChild(v) + case KindJSDocAugmentsTag: + return n.data.(*JSDocAugmentsTag).ForEachChild(v) + case KindJSDocSatisfiesTag: + return n.data.(*JSDocSatisfiesTag).ForEachChild(v) + case KindJSDocThrowsTag: + return n.data.(*JSDocThrowsTag).ForEachChild(v) + case KindJSDocThisTag: + return n.data.(*JSDocThisTag).ForEachChild(v) + case KindJSDocImportTag: + return n.data.(*JSDocImportTag).ForEachChild(v) + case KindJSDocCallbackTag: + return n.data.(*JSDocCallbackTag).ForEachChild(v) + case KindJSDocOverloadTag: + return n.data.(*JSDocOverloadTag).ForEachChild(v) + case KindJSDocTypedefTag: + return n.data.(*JSDocTypedefTag).ForEachChild(v) + case KindJSDocSignature: + return n.data.(*JSDocSignature).ForEachChild(v) + case KindJSDocNameReference: + return n.data.(*JSDocNameReference).ForEachChild(v) + case KindSourceFile: + return n.data.(*SourceFile).ForEachChild(v) + case KindModuleDeclaration: + return n.data.(*ModuleDeclaration).ForEachChild(v) + case KindImportEqualsDeclaration: + return n.data.(*ImportEqualsDeclaration).ForEachChild(v) + case KindExportDeclaration: + return n.data.(*ExportDeclaration).ForEachChild(v) + case KindImportType: + return n.data.(*ImportTypeNode).ForEachChild(v) + case KindImportClause: + return n.data.(*ImportClause).ForEachChild(v) + case KindImportSpecifier: + return n.data.(*ImportSpecifier).ForEachChild(v) + case KindJSDocLink: + return n.data.(*JSDocLink).ForEachChild(v) + case KindJSDocLinkPlain: + return n.data.(*JSDocLinkPlain).ForEachChild(v) + case KindJSDocLinkCode: + return n.data.(*JSDocLinkCode).ForEachChild(v) + case KindTypeParameter: + return n.data.(*TypeParameterDeclaration).ForEachChild(v) + case KindSyntheticReferenceExpression: + return n.data.(*SyntheticReferenceExpression).ForEachChild(v) + case KindJSDocTypeLiteral: + return n.data.(*JSDocTypeLiteral).ForEachChild(v) + case KindJSDocParameterTag, KindJSDocPropertyTag: + return n.data.(*JSDocParameterOrPropertyTag).ForEachChild(v) + default: + return false + } +} + +// ────────────────────────────────────────────────────────────────────── +// As*() cast methods +// ────────────────────────────────────────────────────────────────────── + +func (n *Node) AsToken() *Token { + return n.data.(*Token) +} + +func (n *Node) AsIdentifier() *Identifier { + return n.data.(*Identifier) +} + +func (n *Node) AsPrivateIdentifier() *PrivateIdentifier { + return n.data.(*PrivateIdentifier) +} + +func (n *Node) AsQualifiedName() *QualifiedName { + return n.data.(*QualifiedName) +} + +func (n *Node) AsComputedPropertyName() *ComputedPropertyName { + return n.data.(*ComputedPropertyName) +} + +func (n *Node) AsDecorator() *Decorator { + return n.data.(*Decorator) +} + +func (n *Node) AsEmptyStatement() *EmptyStatement { + return n.data.(*EmptyStatement) +} + +func (n *Node) AsIfStatement() *IfStatement { + return n.data.(*IfStatement) +} + +func (n *Node) AsDoStatement() *DoStatement { + return n.data.(*DoStatement) +} + +func (n *Node) AsWhileStatement() *WhileStatement { + return n.data.(*WhileStatement) +} + +func (n *Node) AsForStatement() *ForStatement { + return n.data.(*ForStatement) +} + +func (n *Node) AsForInOrOfStatement() *ForInOrOfStatement { + return n.data.(*ForInOrOfStatement) +} + +func (n *Node) AsBreakStatement() *BreakStatement { + return n.data.(*BreakStatement) +} + +func (n *Node) AsContinueStatement() *ContinueStatement { + return n.data.(*ContinueStatement) +} + +func (n *Node) AsReturnStatement() *ReturnStatement { + return n.data.(*ReturnStatement) +} + +func (n *Node) AsWithStatement() *WithStatement { + return n.data.(*WithStatement) +} + +func (n *Node) AsSwitchStatement() *SwitchStatement { + return n.data.(*SwitchStatement) +} + +func (n *Node) AsCaseBlock() *CaseBlock { + return n.data.(*CaseBlock) +} + +func (n *Node) AsCaseOrDefaultClause() *CaseOrDefaultClause { + return n.data.(*CaseOrDefaultClause) +} + +func (n *Node) AsThrowStatement() *ThrowStatement { + return n.data.(*ThrowStatement) +} + +func (n *Node) AsTryStatement() *TryStatement { + return n.data.(*TryStatement) +} + +func (n *Node) AsCatchClause() *CatchClause { + return n.data.(*CatchClause) +} + +func (n *Node) AsDebuggerStatement() *DebuggerStatement { + return n.data.(*DebuggerStatement) +} + +func (n *Node) AsLabeledStatement() *LabeledStatement { + return n.data.(*LabeledStatement) +} + +func (n *Node) AsExpressionStatement() *ExpressionStatement { + return n.data.(*ExpressionStatement) +} + +func (n *Node) AsBlock() *Block { + return n.data.(*Block) +} + +func (n *Node) AsVariableStatement() *VariableStatement { + return n.data.(*VariableStatement) +} + +func (n *Node) AsVariableDeclaration() *VariableDeclaration { + return n.data.(*VariableDeclaration) +} + +func (n *Node) AsVariableDeclarationList() *VariableDeclarationList { + return n.data.(*VariableDeclarationList) +} + +func (n *Node) AsBindingPattern() *BindingPattern { + return n.data.(*BindingPattern) +} + +func (n *Node) AsParameterDeclaration() *ParameterDeclaration { + return n.data.(*ParameterDeclaration) +} + +func (n *Node) AsBindingElement() *BindingElement { + return n.data.(*BindingElement) +} + +func (n *Node) AsMissingDeclaration() *MissingDeclaration { + return n.data.(*MissingDeclaration) +} + +func (n *Node) AsFunctionDeclaration() *FunctionDeclaration { + return n.data.(*FunctionDeclaration) +} + +func (n *Node) AsClassDeclaration() *ClassDeclaration { + return n.data.(*ClassDeclaration) +} + +func (n *Node) AsClassExpression() *ClassExpression { + return n.data.(*ClassExpression) +} + +func (n *Node) AsHeritageClause() *HeritageClause { + return n.data.(*HeritageClause) +} + +func (n *Node) AsInterfaceDeclaration() *InterfaceDeclaration { + return n.data.(*InterfaceDeclaration) +} + +func (n *Node) AsTypeAliasDeclaration() *TypeAliasDeclaration { + return n.data.(*TypeAliasDeclaration) +} + +func (n *Node) AsEnumMember() *EnumMember { + return n.data.(*EnumMember) +} + +func (n *Node) AsEnumDeclaration() *EnumDeclaration { + return n.data.(*EnumDeclaration) +} + +func (n *Node) AsModuleBlock() *ModuleBlock { + return n.data.(*ModuleBlock) +} + +func (n *Node) AsNotEmittedStatement() *NotEmittedStatement { + return n.data.(*NotEmittedStatement) +} + +func (n *Node) AsNotEmittedTypeElement() *NotEmittedTypeElement { + return n.data.(*NotEmittedTypeElement) +} + +func (n *Node) AsImportDeclaration() *ImportDeclaration { + return n.data.(*ImportDeclaration) +} + +func (n *Node) AsExternalModuleReference() *ExternalModuleReference { + return n.data.(*ExternalModuleReference) +} + +func (n *Node) AsNamespaceImport() *NamespaceImport { + return n.data.(*NamespaceImport) +} + +func (n *Node) AsNamedImports() *NamedImports { + return n.data.(*NamedImports) +} + +func (n *Node) AsExportAssignment() *ExportAssignment { + return n.data.(*ExportAssignment) +} + +func (n *Node) AsNamespaceExportDeclaration() *NamespaceExportDeclaration { + return n.data.(*NamespaceExportDeclaration) +} + +func (n *Node) AsNamespaceExport() *NamespaceExport { + return n.data.(*NamespaceExport) +} + +func (n *Node) AsNamedExports() *NamedExports { + return n.data.(*NamedExports) +} + +func (n *Node) AsExportSpecifier() *ExportSpecifier { + return n.data.(*ExportSpecifier) +} + +func (n *Node) AsCallSignatureDeclaration() *CallSignatureDeclaration { + return n.data.(*CallSignatureDeclaration) +} + +func (n *Node) AsConstructSignatureDeclaration() *ConstructSignatureDeclaration { + return n.data.(*ConstructSignatureDeclaration) +} + +func (n *Node) AsConstructorDeclaration() *ConstructorDeclaration { + return n.data.(*ConstructorDeclaration) +} + +func (n *Node) AsGetAccessorDeclaration() *GetAccessorDeclaration { + return n.data.(*GetAccessorDeclaration) +} + +func (n *Node) AsSetAccessorDeclaration() *SetAccessorDeclaration { + return n.data.(*SetAccessorDeclaration) +} + +func (n *Node) AsIndexSignatureDeclaration() *IndexSignatureDeclaration { + return n.data.(*IndexSignatureDeclaration) +} + +func (n *Node) AsMethodSignatureDeclaration() *MethodSignatureDeclaration { + return n.data.(*MethodSignatureDeclaration) +} + +func (n *Node) AsMethodDeclaration() *MethodDeclaration { + return n.data.(*MethodDeclaration) +} + +func (n *Node) AsPropertySignatureDeclaration() *PropertySignatureDeclaration { + return n.data.(*PropertySignatureDeclaration) +} + +func (n *Node) AsPropertyDeclaration() *PropertyDeclaration { + return n.data.(*PropertyDeclaration) +} + +func (n *Node) AsSemicolonClassElement() *SemicolonClassElement { + return n.data.(*SemicolonClassElement) +} + +func (n *Node) AsClassStaticBlockDeclaration() *ClassStaticBlockDeclaration { + return n.data.(*ClassStaticBlockDeclaration) +} + +func (n *Node) AsOmittedExpression() *OmittedExpression { + return n.data.(*OmittedExpression) +} + +func (n *Node) AsKeywordExpression() *KeywordExpression { + return n.data.(*KeywordExpression) +} + +func (n *Node) AsStringLiteral() *StringLiteral { + return n.data.(*StringLiteral) +} + +func (n *Node) AsNumericLiteral() *NumericLiteral { + return n.data.(*NumericLiteral) +} + +func (n *Node) AsBigIntLiteral() *BigIntLiteral { + return n.data.(*BigIntLiteral) +} + +func (n *Node) AsRegularExpressionLiteral() *RegularExpressionLiteral { + return n.data.(*RegularExpressionLiteral) +} + +func (n *Node) AsNoSubstitutionTemplateLiteral() *NoSubstitutionTemplateLiteral { + return n.data.(*NoSubstitutionTemplateLiteral) +} + +func (n *Node) AsBinaryExpression() *BinaryExpression { + return n.data.(*BinaryExpression) +} + +func (n *Node) AsPrefixUnaryExpression() *PrefixUnaryExpression { + return n.data.(*PrefixUnaryExpression) +} + +func (n *Node) AsPostfixUnaryExpression() *PostfixUnaryExpression { + return n.data.(*PostfixUnaryExpression) +} + +func (n *Node) AsYieldExpression() *YieldExpression { + return n.data.(*YieldExpression) +} + +func (n *Node) AsArrowFunction() *ArrowFunction { + return n.data.(*ArrowFunction) +} + +func (n *Node) AsFunctionExpression() *FunctionExpression { + return n.data.(*FunctionExpression) +} + +func (n *Node) AsAsExpression() *AsExpression { + return n.data.(*AsExpression) +} + +func (n *Node) AsSatisfiesExpression() *SatisfiesExpression { + return n.data.(*SatisfiesExpression) +} + +func (n *Node) AsConditionalExpression() *ConditionalExpression { + return n.data.(*ConditionalExpression) +} + +func (n *Node) AsPropertyAccessExpression() *PropertyAccessExpression { + return n.data.(*PropertyAccessExpression) +} + +func (n *Node) AsElementAccessExpression() *ElementAccessExpression { + return n.data.(*ElementAccessExpression) +} + +func (n *Node) AsCallExpression() *CallExpression { + return n.data.(*CallExpression) +} + +func (n *Node) AsNewExpression() *NewExpression { + return n.data.(*NewExpression) +} + +func (n *Node) AsMetaProperty() *MetaProperty { + return n.data.(*MetaProperty) +} + +func (n *Node) AsNonNullExpression() *NonNullExpression { + return n.data.(*NonNullExpression) +} + +func (n *Node) AsSpreadElement() *SpreadElement { + return n.data.(*SpreadElement) +} + +func (n *Node) AsTemplateExpression() *TemplateExpression { + return n.data.(*TemplateExpression) +} + +func (n *Node) AsTemplateSpan() *TemplateSpan { + return n.data.(*TemplateSpan) +} + +func (n *Node) AsTaggedTemplateExpression() *TaggedTemplateExpression { + return n.data.(*TaggedTemplateExpression) +} + +func (n *Node) AsParenthesizedExpression() *ParenthesizedExpression { + return n.data.(*ParenthesizedExpression) +} + +func (n *Node) AsArrayLiteralExpression() *ArrayLiteralExpression { + return n.data.(*ArrayLiteralExpression) +} + +func (n *Node) AsObjectLiteralExpression() *ObjectLiteralExpression { + return n.data.(*ObjectLiteralExpression) +} + +func (n *Node) AsSpreadAssignment() *SpreadAssignment { + return n.data.(*SpreadAssignment) +} + +func (n *Node) AsPropertyAssignment() *PropertyAssignment { + return n.data.(*PropertyAssignment) +} + +func (n *Node) AsShorthandPropertyAssignment() *ShorthandPropertyAssignment { + return n.data.(*ShorthandPropertyAssignment) +} + +func (n *Node) AsDeleteExpression() *DeleteExpression { + return n.data.(*DeleteExpression) +} + +func (n *Node) AsTypeOfExpression() *TypeOfExpression { + return n.data.(*TypeOfExpression) +} + +func (n *Node) AsVoidExpression() *VoidExpression { + return n.data.(*VoidExpression) +} + +func (n *Node) AsAwaitExpression() *AwaitExpression { + return n.data.(*AwaitExpression) +} + +func (n *Node) AsTypeAssertion() *TypeAssertion { + return n.data.(*TypeAssertion) +} + +func (n *Node) AsKeywordTypeNode() *KeywordTypeNode { + return n.data.(*KeywordTypeNode) +} + +func (n *Node) AsUnionTypeNode() *UnionTypeNode { + return n.data.(*UnionTypeNode) +} + +func (n *Node) AsIntersectionTypeNode() *IntersectionTypeNode { + return n.data.(*IntersectionTypeNode) +} + +func (n *Node) AsConditionalTypeNode() *ConditionalTypeNode { + return n.data.(*ConditionalTypeNode) +} + +func (n *Node) AsTypeOperatorNode() *TypeOperatorNode { + return n.data.(*TypeOperatorNode) +} + +func (n *Node) AsInferTypeNode() *InferTypeNode { + return n.data.(*InferTypeNode) +} + +func (n *Node) AsArrayTypeNode() *ArrayTypeNode { + return n.data.(*ArrayTypeNode) +} + +func (n *Node) AsIndexedAccessTypeNode() *IndexedAccessTypeNode { + return n.data.(*IndexedAccessTypeNode) +} + +func (n *Node) AsTypeReferenceNode() *TypeReferenceNode { + return n.data.(*TypeReferenceNode) +} + +func (n *Node) AsExpressionWithTypeArguments() *ExpressionWithTypeArguments { + return n.data.(*ExpressionWithTypeArguments) +} + +func (n *Node) AsLiteralTypeNode() *LiteralTypeNode { + return n.data.(*LiteralTypeNode) +} + +func (n *Node) AsThisTypeNode() *ThisTypeNode { + return n.data.(*ThisTypeNode) +} + +func (n *Node) AsTypePredicateNode() *TypePredicateNode { + return n.data.(*TypePredicateNode) +} + +func (n *Node) AsImportAttribute() *ImportAttribute { + return n.data.(*ImportAttribute) +} + +func (n *Node) AsImportAttributes() *ImportAttributes { + return n.data.(*ImportAttributes) +} + +func (n *Node) AsTypeQueryNode() *TypeQueryNode { + return n.data.(*TypeQueryNode) +} + +func (n *Node) AsMappedTypeNode() *MappedTypeNode { + return n.data.(*MappedTypeNode) +} + +func (n *Node) AsTypeLiteralNode() *TypeLiteralNode { + return n.data.(*TypeLiteralNode) +} + +func (n *Node) AsTupleTypeNode() *TupleTypeNode { + return n.data.(*TupleTypeNode) +} + +func (n *Node) AsNamedTupleMember() *NamedTupleMember { + return n.data.(*NamedTupleMember) +} + +func (n *Node) AsOptionalTypeNode() *OptionalTypeNode { + return n.data.(*OptionalTypeNode) +} + +func (n *Node) AsRestTypeNode() *RestTypeNode { + return n.data.(*RestTypeNode) +} + +func (n *Node) AsParenthesizedTypeNode() *ParenthesizedTypeNode { + return n.data.(*ParenthesizedTypeNode) +} + +func (n *Node) AsFunctionTypeNode() *FunctionTypeNode { + return n.data.(*FunctionTypeNode) +} + +func (n *Node) AsConstructorTypeNode() *ConstructorTypeNode { + return n.data.(*ConstructorTypeNode) +} + +func (n *Node) AsTemplateHead() *TemplateHead { + return n.data.(*TemplateHead) +} + +func (n *Node) AsTemplateMiddle() *TemplateMiddle { + return n.data.(*TemplateMiddle) +} + +func (n *Node) AsTemplateTail() *TemplateTail { + return n.data.(*TemplateTail) +} + +func (n *Node) AsTemplateLiteralTypeNode() *TemplateLiteralTypeNode { + return n.data.(*TemplateLiteralTypeNode) +} + +func (n *Node) AsTemplateLiteralTypeSpan() *TemplateLiteralTypeSpan { + return n.data.(*TemplateLiteralTypeSpan) +} + +func (n *Node) AsSyntheticExpression() *SyntheticExpression { + return n.data.(*SyntheticExpression) +} + +func (n *Node) AsPartiallyEmittedExpression() *PartiallyEmittedExpression { + return n.data.(*PartiallyEmittedExpression) +} + +func (n *Node) AsJsxElement() *JsxElement { + return n.data.(*JsxElement) +} + +func (n *Node) AsJsxAttributes() *JsxAttributes { + return n.data.(*JsxAttributes) +} + +func (n *Node) AsJsxNamespacedName() *JsxNamespacedName { + return n.data.(*JsxNamespacedName) +} + +func (n *Node) AsJsxOpeningElement() *JsxOpeningElement { + return n.data.(*JsxOpeningElement) +} + +func (n *Node) AsJsxSelfClosingElement() *JsxSelfClosingElement { + return n.data.(*JsxSelfClosingElement) +} + +func (n *Node) AsJsxFragment() *JsxFragment { + return n.data.(*JsxFragment) +} + +func (n *Node) AsJsxOpeningFragment() *JsxOpeningFragment { + return n.data.(*JsxOpeningFragment) +} + +func (n *Node) AsJsxClosingFragment() *JsxClosingFragment { + return n.data.(*JsxClosingFragment) +} + +func (n *Node) AsJsxAttribute() *JsxAttribute { + return n.data.(*JsxAttribute) +} + +func (n *Node) AsJsxSpreadAttribute() *JsxSpreadAttribute { + return n.data.(*JsxSpreadAttribute) +} + +func (n *Node) AsJsxClosingElement() *JsxClosingElement { + return n.data.(*JsxClosingElement) +} + +func (n *Node) AsJsxExpression() *JsxExpression { + return n.data.(*JsxExpression) +} + +func (n *Node) AsJsxText() *JsxText { + return n.data.(*JsxText) +} + +func (n *Node) AsSyntaxList() *SyntaxList { + return n.data.(*SyntaxList) +} + +func (n *Node) AsJSDoc() *JSDoc { + return n.data.(*JSDoc) +} + +func (n *Node) AsJSDocTypeExpression() *JSDocTypeExpression { + return n.data.(*JSDocTypeExpression) +} + +func (n *Node) AsJSDocNonNullableType() *JSDocNonNullableType { + return n.data.(*JSDocNonNullableType) +} + +func (n *Node) AsJSDocNullableType() *JSDocNullableType { + return n.data.(*JSDocNullableType) +} + +func (n *Node) AsJSDocAllType() *JSDocAllType { + return n.data.(*JSDocAllType) +} + +func (n *Node) AsJSDocVariadicType() *JSDocVariadicType { + return n.data.(*JSDocVariadicType) +} + +func (n *Node) AsJSDocOptionalType() *JSDocOptionalType { + return n.data.(*JSDocOptionalType) +} + +func (n *Node) AsJSDocTypeTag() *JSDocTypeTag { + return n.data.(*JSDocTypeTag) +} + +func (n *Node) AsJSDocUnknownTag() *JSDocUnknownTag { + return n.data.(*JSDocUnknownTag) +} + +func (n *Node) AsJSDocTemplateTag() *JSDocTemplateTag { + return n.data.(*JSDocTemplateTag) +} + +func (n *Node) AsJSDocReturnTag() *JSDocReturnTag { + return n.data.(*JSDocReturnTag) +} + +func (n *Node) AsJSDocPublicTag() *JSDocPublicTag { + return n.data.(*JSDocPublicTag) +} + +func (n *Node) AsJSDocPrivateTag() *JSDocPrivateTag { + return n.data.(*JSDocPrivateTag) +} + +func (n *Node) AsJSDocProtectedTag() *JSDocProtectedTag { + return n.data.(*JSDocProtectedTag) +} + +func (n *Node) AsJSDocReadonlyTag() *JSDocReadonlyTag { + return n.data.(*JSDocReadonlyTag) +} + +func (n *Node) AsJSDocOverrideTag() *JSDocOverrideTag { + return n.data.(*JSDocOverrideTag) +} + +func (n *Node) AsJSDocDeprecatedTag() *JSDocDeprecatedTag { + return n.data.(*JSDocDeprecatedTag) +} + +func (n *Node) AsJSDocSeeTag() *JSDocSeeTag { + return n.data.(*JSDocSeeTag) +} + +func (n *Node) AsJSDocImplementsTag() *JSDocImplementsTag { + return n.data.(*JSDocImplementsTag) +} + +func (n *Node) AsJSDocAugmentsTag() *JSDocAugmentsTag { + return n.data.(*JSDocAugmentsTag) +} + +func (n *Node) AsJSDocSatisfiesTag() *JSDocSatisfiesTag { + return n.data.(*JSDocSatisfiesTag) +} + +func (n *Node) AsJSDocThrowsTag() *JSDocThrowsTag { + return n.data.(*JSDocThrowsTag) +} + +func (n *Node) AsJSDocThisTag() *JSDocThisTag { + return n.data.(*JSDocThisTag) +} + +func (n *Node) AsJSDocImportTag() *JSDocImportTag { + return n.data.(*JSDocImportTag) +} + +func (n *Node) AsJSDocCallbackTag() *JSDocCallbackTag { + return n.data.(*JSDocCallbackTag) +} + +func (n *Node) AsJSDocOverloadTag() *JSDocOverloadTag { + return n.data.(*JSDocOverloadTag) +} + +func (n *Node) AsJSDocTypedefTag() *JSDocTypedefTag { + return n.data.(*JSDocTypedefTag) +} + +func (n *Node) AsJSDocSignature() *JSDocSignature { + return n.data.(*JSDocSignature) +} + +func (n *Node) AsJSDocNameReference() *JSDocNameReference { + return n.data.(*JSDocNameReference) +} + +func (n *Node) AsSourceFile() *SourceFile { + return n.data.(*SourceFile) +} + +func (n *Node) AsModuleDeclaration() *ModuleDeclaration { + return n.data.(*ModuleDeclaration) +} + +func (n *Node) AsImportEqualsDeclaration() *ImportEqualsDeclaration { + return n.data.(*ImportEqualsDeclaration) +} + +func (n *Node) AsExportDeclaration() *ExportDeclaration { + return n.data.(*ExportDeclaration) +} + +func (n *Node) AsImportTypeNode() *ImportTypeNode { + return n.data.(*ImportTypeNode) +} + +func (n *Node) AsImportClause() *ImportClause { + return n.data.(*ImportClause) +} + +func (n *Node) AsImportSpecifier() *ImportSpecifier { + return n.data.(*ImportSpecifier) +} + +func (n *Node) AsJSDocText() *JSDocText { + return n.data.(*JSDocText) +} + +func (n *Node) AsJSDocLink() *JSDocLink { + return n.data.(*JSDocLink) +} + +func (n *Node) AsJSDocLinkPlain() *JSDocLinkPlain { + return n.data.(*JSDocLinkPlain) +} + +func (n *Node) AsJSDocLinkCode() *JSDocLinkCode { + return n.data.(*JSDocLinkCode) +} + +func (n *Node) AsTypeParameterDeclaration() *TypeParameterDeclaration { + return n.data.(*TypeParameterDeclaration) +} + +func (n *Node) AsSyntheticReferenceExpression() *SyntheticReferenceExpression { + return n.data.(*SyntheticReferenceExpression) +} + +func (n *Node) AsJSDocTypeLiteral() *JSDocTypeLiteral { + return n.data.(*JSDocTypeLiteral) +} + +func (n *Node) AsJSDocParameterOrPropertyTag() *JSDocParameterOrPropertyTag { + return n.data.(*JSDocParameterOrPropertyTag) +} + +// ────────────────────────────────────────────────────────────────────── +// Kind alias guards +// ────────────────────────────────────────────────────────────────────── + +func IsTriviaKind(kind Kind) bool { + switch kind { + case KindSingleLineCommentTrivia, KindMultiLineCommentTrivia, KindNewLineTrivia, KindWhitespaceTrivia, KindConflictMarkerTrivia: + return true + } + return false +} + +func IsLiteralKind(kind Kind) bool { + return kind >= KindFirstLiteralToken && kind <= KindLastLiteralToken +} + +func IsPseudoLiteralKind(kind Kind) bool { + switch kind { + case KindTemplateHead, KindTemplateMiddle, KindTemplateTail: + return true + } + return false +} + +func IsPunctuationKind(kind Kind) bool { + return kind >= KindFirstPunctuation && kind <= KindLastPunctuation +} + +func IsKeywordKind(kind Kind) bool { + return kind >= KindFirstKeyword && kind <= KindLastKeyword +} + +func IsModifierKind(kind Kind) bool { + switch kind { + case KindAbstractKeyword, KindAccessorKeyword, KindAsyncKeyword, KindConstKeyword, KindDeclareKeyword, KindDefaultKeyword, KindExportKeyword, KindInKeyword, KindPrivateKeyword, KindProtectedKeyword, KindPublicKeyword, KindReadonlyKeyword, KindOutKeyword, KindOverrideKeyword, KindStaticKeyword: + return true + } + return false +} + +func IsKeywordTypeKind(kind Kind) bool { + switch kind { + case KindAnyKeyword, KindBigIntKeyword, KindBooleanKeyword, KindIntrinsicKeyword, KindNeverKeyword, KindNumberKeyword, KindObjectKeyword, KindStringKeyword, KindSymbolKeyword, KindUndefinedKeyword, KindUnknownKeyword, KindVoidKeyword: + return true + } + return false +} + +func IsKeywordExpressionKind(kind Kind) bool { + switch kind { + case KindNullKeyword, KindTrueKeyword, KindFalseKeyword, KindThisKeyword, KindSuperKeyword, KindImportKeyword: + return true + } + return false +} + +func IsTokenKind(kind Kind) bool { + return kind >= KindFirstToken && kind <= KindLastToken +} + +func IsJsxTokenKind(kind Kind) bool { + switch kind { + case KindLessThanSlashToken, KindEndOfFile, KindConflictMarkerTrivia, KindJsxText, KindJsxTextAllWhiteSpaces, KindOpenBraceToken, KindLessThanToken: + return true + } + return false +} + +func IsJSDocNodeKind(kind Kind) bool { + return kind >= KindFirstJSDocNode && kind <= KindLastJSDocNode +} + +func IsImportPhaseModifierKind(kind Kind) bool { + switch kind { + case KindTypeKeyword, KindDeferKeyword: + return true + } + return false +} + +func IsPostfixUnaryOperator(kind Kind) bool { + switch kind { + case KindPlusPlusToken, KindMinusMinusToken: + return true + } + return false +} + +func IsPrefixUnaryOperator(kind Kind) bool { + switch kind { + case KindPlusToken, KindMinusToken, KindTildeToken, KindExclamationToken, KindPlusPlusToken, KindMinusMinusToken: + return true + } + return false +} + +func IsAssignmentOperator(kind Kind) bool { + switch kind { + case KindEqualsToken, KindPlusEqualsToken, KindMinusEqualsToken, KindAsteriskAsteriskEqualsToken, KindAsteriskEqualsToken, KindSlashEqualsToken, KindPercentEqualsToken, KindAmpersandEqualsToken, KindBarEqualsToken, KindCaretEqualsToken, KindLessThanLessThanEqualsToken, KindGreaterThanGreaterThanGreaterThanEqualsToken, KindGreaterThanGreaterThanEqualsToken, KindBarBarEqualsToken, KindAmpersandAmpersandEqualsToken, KindQuestionQuestionEqualsToken: + return true + } + return false +} + +func IsBinaryOperator(kind Kind) bool { + switch kind { + case KindQuestionQuestionToken, KindAsteriskAsteriskToken, KindAsteriskToken, KindSlashToken, KindPercentToken, KindPlusToken, KindMinusToken, KindLessThanLessThanToken, KindGreaterThanGreaterThanToken, KindGreaterThanGreaterThanGreaterThanToken, KindLessThanToken, KindLessThanEqualsToken, KindGreaterThanToken, KindGreaterThanEqualsToken, KindInstanceOfKeyword, KindInKeyword, KindEqualsEqualsToken, KindEqualsEqualsEqualsToken, KindExclamationEqualsEqualsToken, KindExclamationEqualsToken, KindAmpersandToken, KindBarToken, KindCaretToken, KindAmpersandAmpersandToken, KindBarBarToken, KindEqualsToken, KindPlusEqualsToken, KindMinusEqualsToken, KindAsteriskAsteriskEqualsToken, KindAsteriskEqualsToken, KindSlashEqualsToken, KindPercentEqualsToken, KindAmpersandEqualsToken, KindBarEqualsToken, KindCaretEqualsToken, KindLessThanLessThanEqualsToken, KindGreaterThanGreaterThanGreaterThanEqualsToken, KindGreaterThanGreaterThanEqualsToken, KindBarBarEqualsToken, KindAmpersandAmpersandEqualsToken, KindQuestionQuestionEqualsToken, KindCommaToken: + return true + } + return false +} + +func IsExponentiationOperator(kind Kind) bool { + switch kind { + case KindAsteriskAsteriskToken: + return true + } + return false +} + +func IsMultiplicativeOperator(kind Kind) bool { + switch kind { + case KindAsteriskToken, KindSlashToken, KindPercentToken: + return true + } + return false +} + +func IsMultiplicativeOperatorOrHigher(kind Kind) bool { + switch kind { + case KindAsteriskAsteriskToken, KindAsteriskToken, KindSlashToken, KindPercentToken: + return true + } + return false +} + +func IsAdditiveOperator(kind Kind) bool { + switch kind { + case KindPlusToken, KindMinusToken: + return true + } + return false +} + +func IsAdditiveOperatorOrHigher(kind Kind) bool { + switch kind { + case KindAsteriskAsteriskToken, KindAsteriskToken, KindSlashToken, KindPercentToken, KindPlusToken, KindMinusToken: + return true + } + return false +} + +func IsShiftOperator(kind Kind) bool { + switch kind { + case KindLessThanLessThanToken, KindGreaterThanGreaterThanToken, KindGreaterThanGreaterThanGreaterThanToken: + return true + } + return false +} + +func IsShiftOperatorOrHigher(kind Kind) bool { + switch kind { + case KindAsteriskAsteriskToken, KindAsteriskToken, KindSlashToken, KindPercentToken, KindPlusToken, KindMinusToken, KindLessThanLessThanToken, KindGreaterThanGreaterThanToken, KindGreaterThanGreaterThanGreaterThanToken: + return true + } + return false +} + +func IsRelationalOperator(kind Kind) bool { + switch kind { + case KindLessThanToken, KindLessThanEqualsToken, KindGreaterThanToken, KindGreaterThanEqualsToken, KindInstanceOfKeyword, KindInKeyword: + return true + } + return false +} + +func IsRelationalOperatorOrHigher(kind Kind) bool { + switch kind { + case KindAsteriskAsteriskToken, KindAsteriskToken, KindSlashToken, KindPercentToken, KindPlusToken, KindMinusToken, KindLessThanLessThanToken, KindGreaterThanGreaterThanToken, KindGreaterThanGreaterThanGreaterThanToken, KindLessThanToken, KindLessThanEqualsToken, KindGreaterThanToken, KindGreaterThanEqualsToken, KindInstanceOfKeyword, KindInKeyword: + return true + } + return false +} + +func IsEqualityOperator(kind Kind) bool { + switch kind { + case KindEqualsEqualsToken, KindEqualsEqualsEqualsToken, KindExclamationEqualsEqualsToken, KindExclamationEqualsToken: + return true + } + return false +} + +func IsEqualityOperatorOrHigher(kind Kind) bool { + switch kind { + case KindAsteriskAsteriskToken, KindAsteriskToken, KindSlashToken, KindPercentToken, KindPlusToken, KindMinusToken, KindLessThanLessThanToken, KindGreaterThanGreaterThanToken, KindGreaterThanGreaterThanGreaterThanToken, KindLessThanToken, KindLessThanEqualsToken, KindGreaterThanToken, KindGreaterThanEqualsToken, KindInstanceOfKeyword, KindInKeyword, KindEqualsEqualsToken, KindEqualsEqualsEqualsToken, KindExclamationEqualsEqualsToken, KindExclamationEqualsToken: + return true + } + return false +} + +func IsBitwiseOperator(kind Kind) bool { + switch kind { + case KindAmpersandToken, KindBarToken, KindCaretToken: + return true + } + return false +} + +func IsBitwiseOperatorOrHigher(kind Kind) bool { + switch kind { + case KindAsteriskAsteriskToken, KindAsteriskToken, KindSlashToken, KindPercentToken, KindPlusToken, KindMinusToken, KindLessThanLessThanToken, KindGreaterThanGreaterThanToken, KindGreaterThanGreaterThanGreaterThanToken, KindLessThanToken, KindLessThanEqualsToken, KindGreaterThanToken, KindGreaterThanEqualsToken, KindInstanceOfKeyword, KindInKeyword, KindEqualsEqualsToken, KindEqualsEqualsEqualsToken, KindExclamationEqualsEqualsToken, KindExclamationEqualsToken, KindAmpersandToken, KindBarToken, KindCaretToken: + return true + } + return false +} + +func IsLogicalOperator(kind Kind) bool { + switch kind { + case KindAmpersandAmpersandToken, KindBarBarToken: + return true + } + return false +} + +func IsLogicalOperatorOrHigher(kind Kind) bool { + switch kind { + case KindAsteriskAsteriskToken, KindAsteriskToken, KindSlashToken, KindPercentToken, KindPlusToken, KindMinusToken, KindLessThanLessThanToken, KindGreaterThanGreaterThanToken, KindGreaterThanGreaterThanGreaterThanToken, KindLessThanToken, KindLessThanEqualsToken, KindGreaterThanToken, KindGreaterThanEqualsToken, KindInstanceOfKeyword, KindInKeyword, KindEqualsEqualsToken, KindEqualsEqualsEqualsToken, KindExclamationEqualsEqualsToken, KindExclamationEqualsToken, KindAmpersandToken, KindBarToken, KindCaretToken, KindAmpersandAmpersandToken, KindBarBarToken: + return true + } + return false +} + +func IsCompoundAssignmentOperator(kind Kind) bool { + switch kind { + case KindPlusEqualsToken, KindMinusEqualsToken, KindAsteriskAsteriskEqualsToken, KindAsteriskEqualsToken, KindSlashEqualsToken, KindPercentEqualsToken, KindAmpersandEqualsToken, KindBarEqualsToken, KindCaretEqualsToken, KindLessThanLessThanEqualsToken, KindGreaterThanGreaterThanGreaterThanEqualsToken, KindGreaterThanGreaterThanEqualsToken, KindBarBarEqualsToken, KindAmpersandAmpersandEqualsToken, KindQuestionQuestionEqualsToken: + return true + } + return false +} + +func IsAssignmentOperatorOrHigher(kind Kind) bool { + switch kind { + case KindQuestionQuestionToken, KindAsteriskAsteriskToken, KindAsteriskToken, KindSlashToken, KindPercentToken, KindPlusToken, KindMinusToken, KindLessThanLessThanToken, KindGreaterThanGreaterThanToken, KindGreaterThanGreaterThanGreaterThanToken, KindLessThanToken, KindLessThanEqualsToken, KindGreaterThanToken, KindGreaterThanEqualsToken, KindInstanceOfKeyword, KindInKeyword, KindEqualsEqualsToken, KindEqualsEqualsEqualsToken, KindExclamationEqualsEqualsToken, KindExclamationEqualsToken, KindAmpersandToken, KindBarToken, KindCaretToken, KindAmpersandAmpersandToken, KindBarBarToken, KindEqualsToken, KindPlusEqualsToken, KindMinusEqualsToken, KindAsteriskAsteriskEqualsToken, KindAsteriskEqualsToken, KindSlashEqualsToken, KindPercentEqualsToken, KindAmpersandEqualsToken, KindBarEqualsToken, KindCaretEqualsToken, KindLessThanLessThanEqualsToken, KindGreaterThanGreaterThanGreaterThanEqualsToken, KindGreaterThanGreaterThanEqualsToken, KindBarBarEqualsToken, KindAmpersandAmpersandEqualsToken, KindQuestionQuestionEqualsToken: + return true + } + return false +} + +func IsLogicalOrCoalescingAssignmentOperator(kind Kind) bool { + switch kind { + case KindAmpersandAmpersandEqualsToken, KindBarBarEqualsToken, KindQuestionQuestionEqualsToken: + return true + } + return false +} diff --git a/tools/tsgo/internal/ast/checkflags.go b/tools/tsgo/internal/ast/checkflags.go new file mode 100644 index 00000000..b928d387 --- /dev/null +++ b/tools/tsgo/internal/ast/checkflags.go @@ -0,0 +1,36 @@ +package ast + +// CheckFlags + +type CheckFlags uint32 + +const ( + CheckFlagsNone CheckFlags = 0 + CheckFlagsInstantiated CheckFlags = 1 << 0 // Instantiated symbol + CheckFlagsSyntheticProperty CheckFlags = 1 << 1 // Property in union or intersection type + CheckFlagsSyntheticMethod CheckFlags = 1 << 2 // Method in union or intersection type + CheckFlagsReadonly CheckFlags = 1 << 3 // Readonly transient symbol + CheckFlagsReadPartial CheckFlags = 1 << 4 // Synthetic property present in some but not all constituents + CheckFlagsWritePartial CheckFlags = 1 << 5 // Synthetic property present in some but only satisfied by an index signature in others + CheckFlagsHasNonUniformType CheckFlags = 1 << 6 // Synthetic property with non-uniform type in constituents + CheckFlagsHasLiteralType CheckFlags = 1 << 7 // Synthetic property with at least one literal type in constituents + CheckFlagsContainsPublic CheckFlags = 1 << 8 // Synthetic property with public constituent(s) + CheckFlagsContainsProtected CheckFlags = 1 << 9 // Synthetic property with protected constituent(s) + CheckFlagsContainsPrivate CheckFlags = 1 << 10 // Synthetic property with private constituent(s) + CheckFlagsContainsStatic CheckFlags = 1 << 11 // Synthetic property with static constituent(s) + CheckFlagsLate CheckFlags = 1 << 12 // Late-bound symbol for a computed property with a dynamic name + CheckFlagsReverseMapped CheckFlags = 1 << 13 // Property of reverse-inferred homomorphic mapped type + CheckFlagsOptionalParameter CheckFlags = 1 << 14 // Optional parameter + CheckFlagsRestParameter CheckFlags = 1 << 15 // Rest parameter + CheckFlagsDeferredType CheckFlags = 1 << 16 // Calculation of the type of this symbol is deferred due to processing costs, should be fetched with `getTypeOfSymbolWithDeferredType` + CheckFlagsHasNeverType CheckFlags = 1 << 17 // Synthetic property with at least one never type in constituents + CheckFlagsMapped CheckFlags = 1 << 18 // Property of mapped type + CheckFlagsStripOptional CheckFlags = 1 << 19 // Strip optionality in mapped property + CheckFlagsUnresolved CheckFlags = 1 << 20 // Unresolved type alias symbol + CheckFlagsIsDiscriminantComputed CheckFlags = 1 << 21 // IsDiscriminant flags has been computed + CheckFlagsIsDiscriminant CheckFlags = 1 << 22 // Discriminant property + CheckFlagsIndexSymbol CheckFlags = 1 << 23 // Synthetic property created from index signature + CheckFlagsSynthetic = CheckFlagsSyntheticProperty | CheckFlagsSyntheticMethod + CheckFlagsNonUniformAndLiteral = CheckFlagsHasNonUniformType | CheckFlagsHasLiteralType + CheckFlagsPartial = CheckFlagsReadPartial | CheckFlagsWritePartial +) diff --git a/tools/tsgo/internal/ast/deepclone.go b/tools/tsgo/internal/ast/deepclone.go new file mode 100644 index 00000000..48e9b489 --- /dev/null +++ b/tools/tsgo/internal/ast/deepclone.go @@ -0,0 +1,86 @@ +package ast + +import "github.com/microsoft/typescript-go/internal/core" + +// Ideally, this would get cached on the node factory so there's only ever one set of closures made per factory +func getDeepCloneVisitor(f *NodeFactory, syntheticLocation bool) *NodeVisitor { + var visitor *NodeVisitor + visitor = NewNodeVisitor( + func(node *Node) *Node { + visited := visitor.VisitEachChild(node) + if visited != node { + if syntheticLocation { + visited.Loc = core.NewTextRange(-1, -1) + } + return visited + } + c := node.Clone(f) // forcibly clone leaf nodes, which will then cascade new nodes/arrays upwards via `update` calls + // In strada, `factory.cloneNode` was dynamic and did _not_ clone positions for any "special cases", meanwhile + // Node.Clone in corsa reliably uses `Update` calls for all nodes and so copies locations by default. + // Deep clones are done to copy a node across files, so here, we explicitly make the location range synthetic on all cloned nodes + if syntheticLocation { + c.Loc = core.NewTextRange(-1, -1) + } + return c + }, + f, + NodeVisitorHooks{ + VisitNodes: func(nodes *NodeList, v *NodeVisitor) *NodeList { + if nodes == nil { + return nil + } + visited := v.VisitNodes(nodes) + var newList *NodeList + if visited != nodes { + newList = visited + } else { + newList = nodes.Clone(v.Factory) + } + if syntheticLocation { + newList.Loc = core.NewTextRange(-1, -1) + if nodes.HasTrailingComma() { + newList.Nodes[len(newList.Nodes)-1].Loc = core.NewTextRange(-2, -2) + } + } + return newList + }, + VisitModifiers: func(nodes *ModifierList, v *NodeVisitor) *ModifierList { + if nodes == nil { + return nil + } + visited := v.VisitModifiers(nodes) + var newList *ModifierList + if visited != nodes { + newList = visited + } else { + newList = nodes.Clone(v.Factory) + } + if syntheticLocation { + newList.Loc = core.NewTextRange(-1, -1) + if nodes.HasTrailingComma() { + newList.Nodes[len(newList.Nodes)-1].Loc = core.NewTextRange(-2, -2) + } + } + return newList + }, + }, + ) + return visitor +} + +func (f *NodeFactory) DeepCloneNode(node *Node) *Node { + return getDeepCloneVisitor(f, true /*syntheticLocation*/).VisitNode(node) +} + +func (f *NodeFactory) DeepCloneReparse(node *Node) *Node { + if node != nil { + node = getDeepCloneVisitor(f, false /*syntheticLocation*/).VisitNode(node) + SetParentInChildren(node) + node.Flags |= NodeFlagsReparsed + } + return node +} + +func (f *NodeFactory) DeepCloneReparseModifiers(modifiers *ModifierList) *ModifierList { + return getDeepCloneVisitor(f, false /*syntheticLocation*/).VisitModifiers(modifiers) +} diff --git a/tools/tsgo/internal/ast/deepclone_test.go b/tools/tsgo/internal/ast/deepclone_test.go new file mode 100644 index 00000000..c2caf7b4 --- /dev/null +++ b/tools/tsgo/internal/ast/deepclone_test.go @@ -0,0 +1,599 @@ +package ast_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/ast" + "github.com/microsoft/typescript-go/internal/testutil/parsetestutil" + "gotest.tools/v3/assert" +) + +type NodeComparisonWorkItem struct { + original *ast.Node + copy *ast.Node +} + +func getChildren(node *ast.Node) []*ast.Node { + children := []*ast.Node{} + node.VisitEachChild(ast.NewNodeVisitor(func(node *ast.Node) *ast.Node { + children = append(children, node) + return node + }, nil, ast.NodeVisitorHooks{})) + return children +} + +func TestDeepCloneNodeSanityCheck(t *testing.T) { + t.Parallel() + data := []struct { + title string + input string + jsx bool + }{ + {title: "StringLiteral#1", input: `;"test"`}, + {title: "StringLiteral#2", input: `;'test'`}, + {title: "NumericLiteral", input: `0`}, + {title: "BigIntLiteral", input: `0n`}, + {title: "BooleanLiteral#1", input: `true`}, + {title: "BooleanLiteral#2", input: `false`}, + {title: "NoSubstitutionTemplateLiteral", input: "``"}, + {title: "RegularExpressionLiteral#1", input: `/a/`}, + {title: "RegularExpressionLiteral#2", input: `/a/g`}, + {title: "NullLiteral", input: `null`}, + {title: "ThisExpression", input: `this`}, + {title: "SuperExpression", input: `super()`}, + {title: "ImportExpression", input: `import()`}, + {title: "PropertyAccess#1", input: `a.b`}, + {title: "PropertyAccess#2", input: `a.#b`}, + {title: "PropertyAccess#3", input: `a?.b`}, + {title: "PropertyAccess#4", input: `a?.b.c`}, + {title: "PropertyAccess#5", input: `1..b`}, + {title: "PropertyAccess#6", input: `1.0.b`}, + {title: "PropertyAccess#7", input: `0x1.b`}, + {title: "PropertyAccess#8", input: `0b1.b`}, + {title: "PropertyAccess#9", input: `0o1.b`}, + {title: "PropertyAccess#10", input: `10e1.b`}, + {title: "PropertyAccess#11", input: `10E1.b`}, + {title: "ElementAccess#1", input: `a[b]`}, + {title: "ElementAccess#2", input: `a?.[b]`}, + {title: "ElementAccess#3", input: `a?.[b].c`}, + {title: "CallExpression#1", input: `a()`}, + {title: "CallExpression#2", input: `a()`}, + {title: "CallExpression#3", input: `a(b)`}, + {title: "CallExpression#4", input: `a(b)`}, + {title: "CallExpression#5", input: `a(b).c`}, + {title: "CallExpression#6", input: `a(b).c`}, + {title: "CallExpression#7", input: `a?.(b)`}, + {title: "CallExpression#8", input: `a?.(b)`}, + {title: "CallExpression#9", input: `a?.(b).c`}, + {title: "CallExpression#10", input: `a?.(b).c`}, + {title: "CallExpression#11", input: `a()`}, + {title: "CallExpression#12", input: `a()`}, + {title: "NewExpression#1", input: `new a`}, + {title: "NewExpression#2", input: `new a.b`}, + {title: "NewExpression#3", input: `new a()`}, + {title: "NewExpression#4", input: `new a.b()`}, + {title: "NewExpression#5", input: `new a()`}, + {title: "NewExpression#6", input: `new a.b()`}, + {title: "NewExpression#7", input: `new a(b)`}, + {title: "NewExpression#8", input: `new a.b(c)`}, + {title: "NewExpression#9", input: `new a(b)`}, + {title: "NewExpression#10", input: `new a.b(c)`}, + {title: "NewExpression#11", input: `new a(b).c`}, + {title: "NewExpression#12", input: `new a(b).c`}, + {title: "TaggedTemplateExpression#1", input: "tag``"}, + {title: "TaggedTemplateExpression#2", input: "tag``"}, + {title: "TypeAssertionExpression#1", input: `a`}, + {title: "FunctionExpression#1", input: `(function(){})`}, + {title: "FunctionExpression#2", input: `(function f(){})`}, + {title: "FunctionExpression#3", input: `(function*f(){})`}, + {title: "FunctionExpression#4", input: `(async function f(){})`}, + {title: "FunctionExpression#5", input: `(async function*f(){})`}, + {title: "FunctionExpression#6", input: `(function(){})`}, + {title: "FunctionExpression#7", input: `(function(a){})`}, + {title: "FunctionExpression#8", input: `(function():T{})`}, + {title: "ArrowFunction#1", input: `a=>{}`}, + {title: "ArrowFunction#2", input: `()=>{}`}, + {title: "ArrowFunction#3", input: `(a)=>{}`}, + {title: "ArrowFunction#4", input: `(a)=>{}`}, + {title: "ArrowFunction#5", input: `async a=>{}`}, + {title: "ArrowFunction#6", input: `async()=>{}`}, + {title: "ArrowFunction#7", input: `async()=>{}`}, + {title: "ArrowFunction#8", input: `():T=>{}`}, + {title: "ArrowFunction#9", input: `()=>a`}, + {title: "DeleteExpression", input: `delete a`}, + {title: "TypeOfExpression", input: `typeof a`}, + {title: "VoidExpression", input: `void a`}, + {title: "AwaitExpression", input: `await a`}, + {title: "PrefixUnaryExpression#1", input: `+a`}, + {title: "PrefixUnaryExpression#2", input: `++a`}, + {title: "PrefixUnaryExpression#3", input: `+ +a`}, + {title: "PrefixUnaryExpression#4", input: `+ ++a`}, + {title: "PrefixUnaryExpression#5", input: `-a`}, + {title: "PrefixUnaryExpression#6", input: `--a`}, + {title: "PrefixUnaryExpression#7", input: `- -a`}, + {title: "PrefixUnaryExpression#8", input: `- --a`}, + {title: "PrefixUnaryExpression#9", input: `+-a`}, + {title: "PrefixUnaryExpression#10", input: `+--a`}, + {title: "PrefixUnaryExpression#11", input: `-+a`}, + {title: "PrefixUnaryExpression#12", input: `-++a`}, + {title: "PrefixUnaryExpression#13", input: `~a`}, + {title: "PrefixUnaryExpression#14", input: `!a`}, + {title: "PostfixUnaryExpression#1", input: `a++`}, + {title: "PostfixUnaryExpression#2", input: `a--`}, + {title: "BinaryExpression#1", input: `a,b`}, + {title: "BinaryExpression#2", input: `a+b`}, + {title: "BinaryExpression#3", input: `a**b`}, + {title: "BinaryExpression#4", input: `a instanceof b`}, + {title: "BinaryExpression#5", input: `a in b`}, + {title: "ConditionalExpression", input: `a?b:c`}, + {title: "TemplateExpression#1", input: "`a${b}c`"}, + {title: "TemplateExpression#2", input: "`a${b}c${d}e`"}, + {title: "YieldExpression#1", input: `(function*() { yield })`}, + {title: "YieldExpression#2", input: `(function*() { yield a })`}, + {title: "YieldExpression#3", input: `(function*() { yield*a })`}, + {title: "SpreadElement", input: `[...a]`}, + {title: "ClassExpression#1", input: `(class {})`}, + {title: "ClassExpression#2", input: `(class a {})`}, + {title: "ClassExpression#3", input: `(class{})`}, + {title: "ClassExpression#4", input: `(class a{})`}, + {title: "ClassExpression#5", input: `(class extends b {})`}, + {title: "ClassExpression#6", input: `(class a extends b {})`}, + {title: "ClassExpression#7", input: `(class implements b {})`}, + {title: "ClassExpression#8", input: `(class a implements b {})`}, + {title: "ClassExpression#9", input: `(class implements b, c {})`}, + {title: "ClassExpression#10", input: `(class a implements b, c {})`}, + {title: "ClassExpression#11", input: `(class extends b implements c, d {})`}, + {title: "ClassExpression#12", input: `(class a extends b implements c, d {})`}, + {title: "ClassExpression#13", input: `(@a class {})`}, + {title: "OmittedExpression", input: `[,]`}, + {title: "ExpressionWithTypeArguments", input: `a`}, + {title: "AsExpression", input: `a as T`}, + {title: "SatisfiesExpression", input: `a satisfies T`}, + {title: "NonNullExpression", input: `a!`}, + {title: "MetaProperty#1", input: `new.target`}, + {title: "MetaProperty#2", input: `import.meta`}, + {title: "ArrayLiteralExpression#1", input: `[]`}, + {title: "ArrayLiteralExpression#2", input: `[a]`}, + {title: "ArrayLiteralExpression#3", input: `[a,]`}, + {title: "ArrayLiteralExpression#4", input: `[,a]`}, + {title: "ArrayLiteralExpression#5", input: `[...a]`}, + {title: "ObjectLiteralExpression#1", input: `({})`}, + {title: "ObjectLiteralExpression#2", input: `({a,})`}, + {title: "ShorthandPropertyAssignment", input: `({a})`}, + {title: "PropertyAssignment", input: `({a:b})`}, + {title: "SpreadAssignment", input: `({...a})`}, + {title: "Block", input: `{}`}, + {title: "VariableStatement#1", input: `var a`}, + {title: "VariableStatement#2", input: `let a`}, + {title: "VariableStatement#3", input: `const a = b`}, + {title: "VariableStatement#4", input: `using a = b`}, + {title: "VariableStatement#5", input: `await using a = b`}, + {title: "EmptyStatement", input: `;`}, + {title: "IfStatement#1", input: `if(a);`}, + {title: "IfStatement#2", input: `if(a);else;`}, + {title: "IfStatement#3", input: `if(a);else{}`}, + {title: "IfStatement#4", input: `if(a);else if(b);`}, + {title: "IfStatement#5", input: `if(a);else if(b) {}`}, + {title: "IfStatement#6", input: `if(a) {}`}, + {title: "IfStatement#7", input: `if(a) {} else;`}, + {title: "IfStatement#8", input: `if(a) {} else {}`}, + {title: "IfStatement#9", input: `if(a) {} else if(b);`}, + {title: "IfStatement#10", input: `if(a) {} else if(b){}`}, + {title: "DoStatement#1", input: `do;while(a);`}, + {title: "DoStatement#2", input: `do {} while(a);`}, + {title: "WhileStatement#1", input: `while(a);`}, + {title: "WhileStatement#2", input: `while(a) {}`}, + {title: "ForStatement#1", input: `for(;;);`}, + {title: "ForStatement#2", input: `for(a;;);`}, + {title: "ForStatement#3", input: `for(var a;;);`}, + {title: "ForStatement#4", input: `for(;a;);`}, + {title: "ForStatement#5", input: `for(;;a);`}, + {title: "ForStatement#6", input: `for(;;){}`}, + {title: "ForInStatement#1", input: `for(a in b);`}, + {title: "ForInStatement#2", input: `for(var a in b);`}, + {title: "ForInStatement#3", input: `for(a in b){}`}, + {title: "ForOfStatement#1", input: `for(a of b);`}, + {title: "ForOfStatement#2", input: `for(var a of b);`}, + {title: "ForOfStatement#3", input: `for(a of b){}`}, + {title: "ForOfStatement#4", input: `for await(a of b);`}, + {title: "ForOfStatement#5", input: `for await(var a of b);`}, + {title: "ForOfStatement#6", input: `for await(a of b){}`}, + {title: "ContinueStatement#1", input: `continue`}, + {title: "ContinueStatement#2", input: `continue a`}, + {title: "BreakStatement#1", input: `break`}, + {title: "BreakStatement#2", input: `break a`}, + {title: "ReturnStatement#1", input: `return`}, + {title: "ReturnStatement#2", input: `return a`}, + {title: "WithStatement#1", input: `with(a);`}, + {title: "WithStatement#2", input: `with(a){}`}, + {title: "SwitchStatement", input: `switch (a) {}`}, + {title: "CaseClause#1", input: `switch (a) {case b:}`}, + {title: "CaseClause#2", input: `switch (a) {case b:;}`}, + {title: "DefaultClause#1", input: `switch (a) {default:}`}, + {title: "DefaultClause#2", input: `switch (a) {default:;}`}, + {title: "LabeledStatement", input: `a:;`}, + {title: "ThrowStatement", input: `throw a`}, + {title: "TryStatement#1", input: `try {} catch {}`}, + {title: "TryStatement#2", input: `try {} finally {}`}, + {title: "TryStatement#3", input: `try {} catch {} finally {}`}, + {title: "DebuggerStatement", input: `debugger`}, + {title: "FunctionDeclaration#1", input: `export default function(){}`}, + {title: "FunctionDeclaration#2", input: `function f(){}`}, + {title: "FunctionDeclaration#3", input: `function*f(){}`}, + {title: "FunctionDeclaration#4", input: `async function f(){}`}, + {title: "FunctionDeclaration#5", input: `async function*f(){}`}, + {title: "FunctionDeclaration#6", input: `function f(){}`}, + {title: "FunctionDeclaration#7", input: `function f(a){}`}, + {title: "FunctionDeclaration#8", input: `function f():T{}`}, + {title: "FunctionDeclaration#9", input: `function f();`}, + {title: "ClassDeclaration#1", input: `class a {}`}, + {title: "ClassDeclaration#2", input: `class a{}`}, + {title: "ClassDeclaration#3", input: `class a extends b {}`}, + {title: "ClassDeclaration#4", input: `class a implements b {}`}, + {title: "ClassDeclaration#5", input: `class a implements b, c {}`}, + {title: "ClassDeclaration#6", input: `class a extends b implements c, d {}`}, + {title: "ClassDeclaration#7", input: `export default class {}`}, + {title: "ClassDeclaration#8", input: `export default class{}`}, + {title: "ClassDeclaration#9", input: `export default class extends b {}`}, + {title: "ClassDeclaration#10", input: `export default class implements b {}`}, + {title: "ClassDeclaration#11", input: `export default class implements b, c {}`}, + {title: "ClassDeclaration#12", input: `export default class extends b implements c, d {}`}, + {title: "ClassDeclaration#13", input: `@a class b {}`}, + {title: "ClassDeclaration#14", input: `@a export class b {}`}, + {title: "ClassDeclaration#15", input: `export @a class b {}`}, + {title: "InterfaceDeclaration#1", input: `interface a {}`}, + {title: "InterfaceDeclaration#2", input: `interface a{}`}, + {title: "InterfaceDeclaration#3", input: `interface a extends b {}`}, + {title: "InterfaceDeclaration#4", input: `interface a extends b, c {}`}, + {title: "TypeAliasDeclaration#1", input: `type a = b`}, + {title: "TypeAliasDeclaration#2", input: `type a = b`}, + {title: "EnumDeclaration#1", input: `enum a{}`}, + {title: "EnumDeclaration#2", input: `enum a{b}`}, + {title: "EnumDeclaration#3", input: `enum a{b=c}`}, + {title: "ModuleDeclaration#1", input: `module a{}`}, + {title: "ModuleDeclaration#2", input: `module a.b{}`}, + {title: "ModuleDeclaration#3", input: `module "a";`}, + {title: "ModuleDeclaration#4", input: `module "a"{}`}, + {title: "ModuleDeclaration#5", input: `namespace a{}`}, + {title: "ModuleDeclaration#6", input: `namespace a.b{}`}, + {title: "ModuleDeclaration#7", input: `global;`}, + {title: "ModuleDeclaration#8", input: `global{}`}, + {title: "ImportEqualsDeclaration#1", input: `import a = b`}, + {title: "ImportEqualsDeclaration#2", input: `import a = b.c`}, + {title: "ImportEqualsDeclaration#3", input: `import a = require("b")`}, + {title: "ImportEqualsDeclaration#4", input: `export import a = b`}, + {title: "ImportEqualsDeclaration#5", input: `export import a = require("b")`}, + {title: "ImportEqualsDeclaration#6", input: `import type a = b`}, + {title: "ImportEqualsDeclaration#7", input: `import type a = b.c`}, + {title: "ImportEqualsDeclaration#8", input: `import type a = require("b")`}, + {title: "ImportDeclaration#1", input: `import "a"`}, + {title: "ImportDeclaration#2", input: `import a from "b"`}, + {title: "ImportDeclaration#3", input: `import type a from "b"`}, + {title: "ImportDeclaration#4", input: `import * as a from "b"`}, + {title: "ImportDeclaration#5", input: `import type * as a from "b"`}, + {title: "ImportDeclaration#6", input: `import {} from "b"`}, + {title: "ImportDeclaration#7", input: `import type {} from "b"`}, + {title: "ImportDeclaration#8", input: `import { a } from "b"`}, + {title: "ImportDeclaration#9", input: `import type { a } from "b"`}, + {title: "ImportDeclaration#8", input: `import { a as b } from "c"`}, + {title: "ImportDeclaration#9", input: `import type { a as b } from "c"`}, + {title: "ImportDeclaration#10", input: `import { "a" as b } from "c"`}, + {title: "ImportDeclaration#11", input: `import type { "a" as b } from "c"`}, + {title: "ImportDeclaration#12", input: `import a, {} from "b"`}, + {title: "ImportDeclaration#13", input: `import a, * as b from "c"`}, + {title: "ImportDeclaration#14", input: `import {} from "a" with {}`}, + {title: "ImportDeclaration#15", input: `import {} from "a" with { b: "c" }`}, + {title: "ImportDeclaration#16", input: `import {} from "a" with { "b": "c" }`}, + {title: "ExportAssignment#1", input: `export = a`}, + {title: "ExportAssignment#2", input: `export default a`}, + {title: "NamespaceExportDeclaration", input: `export as namespace a`}, + {title: "ExportDeclaration#1", input: `export * from "a"`}, + {title: "ExportDeclaration#2", input: `export type * from "a"`}, + {title: "ExportDeclaration#3", input: `export * as a from "b"`}, + {title: "ExportDeclaration#4", input: `export type * as a from "b"`}, + {title: "ExportDeclaration#5", input: `export { } from "a"`}, + {title: "ExportDeclaration#6", input: `export type { } from "a"`}, + {title: "ExportDeclaration#7", input: `export { a } from "b"`}, + {title: "ExportDeclaration#8", input: `export { type a } from "b"`}, + {title: "ExportDeclaration#9", input: `export type { a } from "b"`}, + {title: "ExportDeclaration#10", input: `export { a as b } from "c"`}, + {title: "ExportDeclaration#11", input: `export { type a as b } from "c"`}, + {title: "ExportDeclaration#12", input: `export type { a as b } from "c"`}, + {title: "ExportDeclaration#13", input: `export { a as "b" } from "c"`}, + {title: "ExportDeclaration#14", input: `export { type a as "b" } from "c"`}, + {title: "ExportDeclaration#15", input: `export type { a as "b" } from "c"`}, + {title: "ExportDeclaration#16", input: `export { "a" } from "b"`}, + {title: "ExportDeclaration#17", input: `export { type "a" } from "b"`}, + {title: "ExportDeclaration#18", input: `export type { "a" } from "b"`}, + {title: "ExportDeclaration#19", input: `export { "a" as b } from "c"`}, + {title: "ExportDeclaration#20", input: `export { type "a" as b } from "c"`}, + {title: "ExportDeclaration#21", input: `export type { "a" as b } from "c"`}, + {title: "ExportDeclaration#22", input: `export { "a" as "b" } from "c"`}, + {title: "ExportDeclaration#23", input: `export { type "a" as "b" } from "c"`}, + {title: "ExportDeclaration#24", input: `export type { "a" as "b" } from "c"`}, + {title: "ExportDeclaration#25", input: `export { }`}, + {title: "ExportDeclaration#26", input: `export type { }`}, + {title: "ExportDeclaration#27", input: `export { a }`}, + {title: "ExportDeclaration#28", input: `export { type a }`}, + {title: "ExportDeclaration#29", input: `export type { a }`}, + {title: "ExportDeclaration#30", input: `export { a as b }`}, + {title: "ExportDeclaration#31", input: `export { type a as b }`}, + {title: "ExportDeclaration#32", input: `export type { a as b }`}, + {title: "ExportDeclaration#33", input: `export { a as "b" }`}, + {title: "ExportDeclaration#34", input: `export { type a as "b" }`}, + {title: "ExportDeclaration#35", input: `export type { a as "b" }`}, + {title: "ExportDeclaration#36", input: `export {} from "a" with {}`}, + {title: "ExportDeclaration#37", input: `export {} from "a" with { b: "c" }`}, + {title: "ExportDeclaration#38", input: `export {} from "a" with { "b": "c" }`}, + {title: "KeywordTypeNode#1", input: `type T = any`}, + {title: "KeywordTypeNode#2", input: `type T = unknown`}, + {title: "KeywordTypeNode#3", input: `type T = never`}, + {title: "KeywordTypeNode#4", input: `type T = void`}, + {title: "KeywordTypeNode#5", input: `type T = undefined`}, + {title: "KeywordTypeNode#6", input: `type T = null`}, + {title: "KeywordTypeNode#7", input: `type T = object`}, + {title: "KeywordTypeNode#8", input: `type T = string`}, + {title: "KeywordTypeNode#9", input: `type T = symbol`}, + {title: "KeywordTypeNode#10", input: `type T = number`}, + {title: "KeywordTypeNode#11", input: `type T = bigint`}, + {title: "KeywordTypeNode#12", input: `type T = boolean`}, + {title: "KeywordTypeNode#13", input: `type T = intrinsic`}, + {title: "TypePredicateNode#1", input: `function f(): asserts a`}, + {title: "TypePredicateNode#2", input: `function f(): asserts a is b`}, + {title: "TypePredicateNode#3", input: `function f(): asserts this`}, + {title: "TypePredicateNode#4", input: `function f(): asserts this is b`}, + {title: "TypeReferenceNode#1", input: `type T = a`}, + {title: "TypeReferenceNode#2", input: `type T = a.b`}, + {title: "TypeReferenceNode#3", input: `type T = a`}, + {title: "TypeReferenceNode#4", input: `type T = a.b`}, + {title: "FunctionTypeNode#1", input: `type T = () => a`}, + {title: "FunctionTypeNode#2", input: `type T = () => a`}, + {title: "FunctionTypeNode#3", input: `type T = (a) => b`}, + {title: "ConstructorTypeNode#1", input: `type T = new () => a`}, + {title: "ConstructorTypeNode#2", input: `type T = new () => a`}, + {title: "ConstructorTypeNode#3", input: `type T = new (a) => b`}, + {title: "ConstructorTypeNode#4", input: `type T = abstract new () => a`}, + {title: "TypeQueryNode#1", input: `type T = typeof a`}, + {title: "TypeQueryNode#2", input: `type T = typeof a.b`}, + {title: "TypeQueryNode#3", input: `type T = typeof a`}, + {title: "TypeLiteralNode#1", input: `type T = {}`}, + {title: "TypeLiteralNode#2", input: `type T = {a}`}, + {title: "ArrayTypeNode", input: `type T = a[]`}, + {title: "TupleTypeNode#1", input: `type T = []`}, + {title: "TupleTypeNode#2", input: `type T = [a]`}, + {title: "TupleTypeNode#3", input: `type T = [a,]`}, + {title: "RestTypeNode", input: `type T = [...a]`}, + {title: "OptionalTypeNode", input: `type T = [a?]`}, + {title: "NamedTupleMember#1", input: `type T = [a: b]`}, + {title: "NamedTupleMember#2", input: `type T = [a?: b]`}, + {title: "NamedTupleMember#3", input: `type T = [...a: b]`}, + {title: "UnionTypeNode#1", input: `type T = a | b`}, + {title: "UnionTypeNode#2", input: `type T = a | b | c`}, + {title: "UnionTypeNode#3", input: `type T = | a | b`}, + {title: "IntersectionTypeNode#1", input: `type T = a & b`}, + {title: "IntersectionTypeNode#2", input: `type T = a & b & c`}, + {title: "IntersectionTypeNode#3", input: `type T = & a & b`}, + {title: "ConditionalTypeNode", input: `type T = a extends b ? c : d`}, + {title: "InferTypeNode#1", input: `type T = a extends infer b ? c : d`}, + {title: "InferTypeNode#2", input: `type T = a extends infer b extends c ? d : e`}, + {title: "ParenthesizedTypeNode", input: `type T = (U)`}, + {title: "ThisTypeNode", input: `type T = this`}, + {title: "TypeOperatorNode#1", input: `type T = keyof U`}, + {title: "TypeOperatorNode#2", input: `type T = readonly U[]`}, + {title: "TypeOperatorNode#3", input: `type T = unique symbol`}, + {title: "IndexedAccessTypeNode", input: `type T = a[b]`}, + {title: "MappedTypeNode#1", input: `type T = { [a in b]: c }`}, + {title: "MappedTypeNode#2", input: `type T = { [a in b as c]: d }`}, + {title: "MappedTypeNode#3", input: `type T = { readonly [a in b]: c }`}, + {title: "MappedTypeNode#4", input: `type T = { +readonly [a in b]: c }`}, + {title: "MappedTypeNode#5", input: `type T = { -readonly [a in b]: c }`}, + {title: "MappedTypeNode#6", input: `type T = { [a in b]?: c }`}, + {title: "MappedTypeNode#7", input: `type T = { [a in b]+?: c }`}, + {title: "MappedTypeNode#8", input: `type T = { [a in b]-?: c }`}, + {title: "MappedTypeNode#9", input: `type T = { [a in b]: c; d }`}, + {title: "LiteralTypeNode#1", input: `type T = null`}, + {title: "LiteralTypeNode#2", input: `type T = true`}, + {title: "LiteralTypeNode#3", input: `type T = false`}, + {title: "LiteralTypeNode#4", input: `type T = ""`}, + {title: "LiteralTypeNode#5", input: "type T = ''"}, + {title: "LiteralTypeNode#6", input: "type T = ``"}, + {title: "LiteralTypeNode#7", input: `type T = 0`}, + {title: "LiteralTypeNode#8", input: `type T = 0n`}, + {title: "LiteralTypeNode#9", input: `type T = -0`}, + {title: "LiteralTypeNode#10", input: `type T = -0n`}, + {title: "TemplateTypeNode#1", input: "type T = `a${b}c`"}, + {title: "TemplateTypeNode#2", input: "type T = `a${b}c${d}e`"}, + {title: "ImportTypeNode#1", input: `type T = import(a)`}, + {title: "ImportTypeNode#2", input: `type T = import(a).b`}, + {title: "ImportTypeNode#3", input: `type T = import(a).b`}, + {title: "ImportTypeNode#4", input: `type T = typeof import(a)`}, + {title: "ImportTypeNode#5", input: `type T = typeof import(a).b`}, + {title: "ImportTypeNode#6", input: `type T = import(a, { with: { } })`}, + {title: "ImportTypeNode#6", input: `type T = import(a, { with: { b: "c" } })`}, + {title: "ImportTypeNode#7", input: `type T = import(a, { with: { "b": "c" } })`}, + {title: "PropertySignature#1", input: "interface I {a}"}, + {title: "PropertySignature#2", input: "interface I {readonly a}"}, + {title: "PropertySignature#3", input: "interface I {\"a\"}"}, + {title: "PropertySignature#4", input: "interface I {'a'}"}, + {title: "PropertySignature#5", input: "interface I {0}"}, + {title: "PropertySignature#6", input: "interface I {0n}"}, + {title: "PropertySignature#7", input: "interface I {[a]}"}, + {title: "PropertySignature#8", input: "interface I {a?}"}, + {title: "PropertySignature#9", input: "interface I {a: b}"}, + {title: "MethodSignature#1", input: "interface I {a()}"}, + {title: "MethodSignature#2", input: "interface I {\"a\"()}"}, + {title: "MethodSignature#3", input: "interface I {'a'()}"}, + {title: "MethodSignature#4", input: "interface I {0()}"}, + {title: "MethodSignature#5", input: "interface I {0n()}"}, + {title: "MethodSignature#6", input: "interface I {[a]()}"}, + {title: "MethodSignature#7", input: "interface I {a?()}"}, + {title: "MethodSignature#8", input: "interface I {a()}"}, + {title: "MethodSignature#9", input: "interface I {a(): b}"}, + {title: "MethodSignature#10", input: "interface I {a(b): c}"}, + {title: "CallSignature#1", input: "interface I {()}"}, + {title: "CallSignature#2", input: "interface I {():a}"}, + {title: "CallSignature#3", input: "interface I {(p)}"}, + {title: "CallSignature#4", input: "interface I {()}"}, + {title: "ConstructSignature#1", input: "interface I {new ()}"}, + {title: "ConstructSignature#2", input: "interface I {new ():a}"}, + {title: "ConstructSignature#3", input: "interface I {new (p)}"}, + {title: "ConstructSignature#4", input: "interface I {new ()}"}, + {title: "IndexSignatureDeclaration#1", input: "interface I {[a]}"}, + {title: "IndexSignatureDeclaration#2", input: "interface I {[a: b]}"}, + {title: "IndexSignatureDeclaration#3", input: "interface I {[a: b]: c}"}, + {title: "PropertyDeclaration#1", input: "class C {a}"}, + {title: "PropertyDeclaration#2", input: "class C {readonly a}"}, + {title: "PropertyDeclaration#3", input: "class C {static a}"}, + {title: "PropertyDeclaration#4", input: "class C {accessor a}"}, + {title: "PropertyDeclaration#5", input: "class C {\"a\"}"}, + {title: "PropertyDeclaration#6", input: "class C {'a'}"}, + {title: "PropertyDeclaration#7", input: "class C {0}"}, + {title: "PropertyDeclaration#8", input: "class C {0n}"}, + {title: "PropertyDeclaration#9", input: "class C {[a]}"}, + {title: "PropertyDeclaration#10", input: "class C {#a}"}, + {title: "PropertyDeclaration#11", input: "class C {a?}"}, + {title: "PropertyDeclaration#12", input: "class C {a!}"}, + {title: "PropertyDeclaration#13", input: "class C {a: b}"}, + {title: "PropertyDeclaration#14", input: "class C {a = b}"}, + {title: "PropertyDeclaration#15", input: "class C {@a b}"}, + {title: "MethodDeclaration#1", input: "class C {a()}"}, + {title: "MethodDeclaration#2", input: "class C {\"a\"()}"}, + {title: "MethodDeclaration#3", input: "class C {'a'()}"}, + {title: "MethodDeclaration#4", input: "class C {0()}"}, + {title: "MethodDeclaration#5", input: "class C {0n()}"}, + {title: "MethodDeclaration#6", input: "class C {[a]()}"}, + {title: "MethodDeclaration#7", input: "class C {#a()}"}, + {title: "MethodDeclaration#8", input: "class C {a?()}"}, + {title: "MethodDeclaration#9", input: "class C {a()}"}, + {title: "MethodDeclaration#10", input: "class C {a(): b}"}, + {title: "MethodDeclaration#11", input: "class C {a(b): c}"}, + {title: "MethodDeclaration#12", input: "class C {a() {} }"}, + {title: "MethodDeclaration#13", input: "class C {@a b() {} }"}, + {title: "MethodDeclaration#14", input: "class C {static a() {} }"}, + {title: "MethodDeclaration#15", input: "class C {async a() {} }"}, + {title: "GetAccessorDeclaration#1", input: "class C {get a()}"}, + {title: "GetAccessorDeclaration#2", input: "class C {get \"a\"()}"}, + {title: "GetAccessorDeclaration#3", input: "class C {get 'a'()}"}, + {title: "GetAccessorDeclaration#4", input: "class C {get 0()}"}, + {title: "GetAccessorDeclaration#5", input: "class C {get 0n()}"}, + {title: "GetAccessorDeclaration#6", input: "class C {get [a]()}"}, + {title: "GetAccessorDeclaration#7", input: "class C {get #a()}"}, + {title: "GetAccessorDeclaration#8", input: "class C {get a(): b}"}, + {title: "GetAccessorDeclaration#9", input: "class C {get a(b): c}"}, + {title: "GetAccessorDeclaration#10", input: "class C {get a() {} }"}, + {title: "GetAccessorDeclaration#11", input: "class C {@a get b() {} }"}, + {title: "GetAccessorDeclaration#12", input: "class C {static get a() {} }"}, + {title: "SetAccessorDeclaration#1", input: "class C {set a()}"}, + {title: "SetAccessorDeclaration#2", input: "class C {set \"a\"()}"}, + {title: "SetAccessorDeclaration#3", input: "class C {set 'a'()}"}, + {title: "SetAccessorDeclaration#4", input: "class C {set 0()}"}, + {title: "SetAccessorDeclaration#5", input: "class C {set 0n()}"}, + {title: "SetAccessorDeclaration#6", input: "class C {set [a]()}"}, + {title: "SetAccessorDeclaration#7", input: "class C {set #a()}"}, + {title: "SetAccessorDeclaration#8", input: "class C {set a(): b}"}, + {title: "SetAccessorDeclaration#9", input: "class C {set a(b): c}"}, + {title: "SetAccessorDeclaration#10", input: "class C {set a() {} }"}, + {title: "SetAccessorDeclaration#11", input: "class C {@a set b() {} }"}, + {title: "SetAccessorDeclaration#12", input: "class C {static set a() {} }"}, + {title: "ConstructorDeclaration#1", input: "class C {constructor()}"}, + {title: "ConstructorDeclaration#2", input: "class C {constructor(): b}"}, + {title: "ConstructorDeclaration#3", input: "class C {constructor(b): c}"}, + {title: "ConstructorDeclaration#4", input: "class C {constructor() {} }"}, + {title: "ConstructorDeclaration#5", input: "class C {@a constructor() {} }"}, + {title: "ConstructorDeclaration#6", input: "class C {private constructor() {} }"}, + {title: "ClassStaticBlockDeclaration", input: "class C {static { }}"}, + {title: "SemicolonClassElement#1", input: "class C {;}"}, + {title: "ParameterDeclaration#1", input: "function f(a)"}, + {title: "ParameterDeclaration#2", input: "function f(a: b)"}, + {title: "ParameterDeclaration#3", input: "function f(a = b)"}, + {title: "ParameterDeclaration#4", input: "function f(a?)"}, + {title: "ParameterDeclaration#5", input: "function f(...a)"}, + {title: "ParameterDeclaration#6", input: "function f(this)"}, + {title: "ParameterDeclaration#7", input: "function f(a,)"}, + {title: "ObjectBindingPattern#1", input: "function f({})"}, + {title: "ObjectBindingPattern#2", input: "function f({a})"}, + {title: "ObjectBindingPattern#3", input: "function f({a = b})"}, + {title: "ObjectBindingPattern#4", input: "function f({a: b})"}, + {title: "ObjectBindingPattern#5", input: "function f({a: b = c})"}, + {title: "ObjectBindingPattern#6", input: "function f({\"a\": b})"}, + {title: "ObjectBindingPattern#7", input: "function f({'a': b})"}, + {title: "ObjectBindingPattern#8", input: "function f({0: b})"}, + {title: "ObjectBindingPattern#9", input: "function f({[a]: b})"}, + {title: "ObjectBindingPattern#10", input: "function f({...a})"}, + {title: "ObjectBindingPattern#11", input: "function f({a: {}})"}, + {title: "ObjectBindingPattern#12", input: "function f({a: []})"}, + {title: "ArrayBindingPattern#1", input: "function f([])"}, + {title: "ArrayBindingPattern#2", input: "function f([,])"}, + {title: "ArrayBindingPattern#3", input: "function f([a])"}, + {title: "ArrayBindingPattern#4", input: "function f([a, b])"}, + {title: "ArrayBindingPattern#5", input: "function f([a, , b])"}, + {title: "ArrayBindingPattern#6", input: "function f([a = b])"}, + {title: "ArrayBindingPattern#7", input: "function f([...a])"}, + {title: "ArrayBindingPattern#8", input: "function f([{}])"}, + {title: "ArrayBindingPattern#9", input: "function f([[]])"}, + {title: "TypeParameterDeclaration#1", input: "function f();"}, + {title: "TypeParameterDeclaration#2", input: "function f();"}, + {title: "TypeParameterDeclaration#3", input: "function f();"}, + {title: "TypeParameterDeclaration#4", input: "function f();"}, + {title: "TypeParameterDeclaration#5", input: "function f();"}, + {title: "TypeParameterDeclaration#6", input: "function f();"}, + {title: "TypeParameterDeclaration#7", input: "function f();"}, + {title: "JsxElement1", input: ""}, + {title: "JsxElement2", input: ""}, + {title: "JsxElement3", input: ""}, + {title: "JsxElement4", input: ""}, + {title: "JsxElement5", input: ">"}, + {title: "JsxElement6", input: ""}, + {title: "JsxElement7", input: "b"}, + {title: "JsxElement8", input: "{b}"}, + {title: "JsxElement9", input: ""}, + {title: "JsxElement10", input: ""}, + {title: "JsxElement11", input: "<>"}, + {title: "JsxSelfClosingElement1", input: ""}, + {title: "JsxSelfClosingElement2", input: ""}, + {title: "JsxSelfClosingElement3", input: ""}, + {title: "JsxSelfClosingElement4", input: ""}, + {title: "JsxSelfClosingElement5", input: " />"}, + {title: "JsxSelfClosingElement6", input: ""}, + {title: "JsxFragment1", input: "<>"}, + {title: "JsxFragment2", input: "<>b"}, + {title: "JsxFragment3", input: "<>{b}"}, + {title: "JsxFragment4", input: "<>"}, + {title: "JsxFragment5", input: "<>"}, + {title: "JsxFragment6", input: "<><>"}, + {title: "JsxAttribute1", input: ""}, + {title: "JsxAttribute2", input: ""}, + {title: "JsxAttribute3", input: ""}, + {title: "JsxAttribute4", input: ""}, + {title: "JsxAttribute5", input: ""}, + {title: "JsxAttribute6", input: "/>"}, + {title: "JsxAttribute7", input: "/>"}, + {title: "JsxAttribute8", input: "/>"}, + {title: "JsxSpreadAttribute", input: ""}, + } + for _, rec := range data { + t.Run("Clone "+rec.title, func(t *testing.T) { + t.Parallel() + + factory := &ast.NodeFactory{} + file := parsetestutil.ParseTypeScript(rec.input, false).AsNode() + clone := factory.DeepCloneNode(file.AsNode()).AsNode() + + work := []NodeComparisonWorkItem{{file, clone}} + + for len(work) > 0 { + nextWork := []NodeComparisonWorkItem{} + for _, item := range work { + assert.Assert(t, item.original != item.copy) + originalChildren := getChildren(item.original) + copyChildren := getChildren(item.copy) + assert.Equal(t, len(originalChildren), len(copyChildren)) + for i, child := range originalChildren { + nextWork = append(nextWork, NodeComparisonWorkItem{child, copyChildren[i]}) + } + } + work = nextWork + } + }) + } +} diff --git a/tools/tsgo/internal/ast/diagnostic.go b/tools/tsgo/internal/ast/diagnostic.go new file mode 100644 index 00000000..8eadefd4 --- /dev/null +++ b/tools/tsgo/internal/ast/diagnostic.go @@ -0,0 +1,362 @@ +package ast + +import ( + "slices" + "strings" + "sync" + + "github.com/microsoft/typescript-go/internal/collections" + "github.com/microsoft/typescript-go/internal/core" + "github.com/microsoft/typescript-go/internal/diagnostics" + "github.com/microsoft/typescript-go/internal/locale" +) + +// RepopulateDiagnosticKind indicates the kind of repopulation for a diagnostic chain entry. +type RepopulateDiagnosticKind int + +const ( + RepopulateModeMismatch RepopulateDiagnosticKind = 1 + RepopulateModuleNotFound RepopulateDiagnosticKind = 2 +) + +// RepopulateDiagnosticInfo stores information needed to recompute a diagnostic chain entry +// during incremental builds when the program state may have changed. +type RepopulateDiagnosticInfo struct { + Kind RepopulateDiagnosticKind + ModuleReference string + Mode core.ResolutionMode + PackageName string +} + +// Diagnostic + +type Diagnostic struct { + file *SourceFile + loc core.TextRange + code int32 + category diagnostics.Category + // Original message; may be nil. + message *diagnostics.Message + messageKey diagnostics.Key + messageArgs []string + messageChain []*Diagnostic + relatedInformation []*Diagnostic + reportsUnnecessary bool + reportsDeprecated bool + skippedOnNoEmit bool + repopulateInfo *RepopulateDiagnosticInfo +} + +func (d *Diagnostic) File() *SourceFile { return d.file } +func (d *Diagnostic) Pos() int { return d.loc.Pos() } +func (d *Diagnostic) End() int { return d.loc.End() } +func (d *Diagnostic) Len() int { return d.loc.Len() } +func (d *Diagnostic) Loc() core.TextRange { return d.loc } +func (d *Diagnostic) Code() int32 { return d.code } +func (d *Diagnostic) Category() diagnostics.Category { return d.category } +func (d *Diagnostic) MessageKey() diagnostics.Key { return d.messageKey } +func (d *Diagnostic) MessageArgs() []string { return d.messageArgs } +func (d *Diagnostic) MessageChain() []*Diagnostic { return d.messageChain } +func (d *Diagnostic) RelatedInformation() []*Diagnostic { return d.relatedInformation } +func (d *Diagnostic) ReportsUnnecessary() bool { return d.reportsUnnecessary } +func (d *Diagnostic) ReportsDeprecated() bool { return d.reportsDeprecated } +func (d *Diagnostic) SkippedOnNoEmit() bool { return d.skippedOnNoEmit } +func (d *Diagnostic) RepopulateInfo() *RepopulateDiagnosticInfo { return d.repopulateInfo } + +func (d *Diagnostic) SetFile(file *SourceFile) { d.file = file } +func (d *Diagnostic) SetLocation(loc core.TextRange) { d.loc = loc } +func (d *Diagnostic) SetCategory(category diagnostics.Category) { d.category = category } +func (d *Diagnostic) SetSkippedOnNoEmit() { d.skippedOnNoEmit = true } +func (d *Diagnostic) SetRepopulateInfo(info *RepopulateDiagnosticInfo) { d.repopulateInfo = info } + +func (d *Diagnostic) SetMessageChain(messageChain []*Diagnostic) *Diagnostic { + d.messageChain = messageChain + return d +} + +func (d *Diagnostic) AddMessageChain(messageChain *Diagnostic) *Diagnostic { + if messageChain != nil { + d.messageChain = append(d.messageChain, messageChain) + } + return d +} + +func (d *Diagnostic) SetRelatedInfo(relatedInformation []*Diagnostic) *Diagnostic { + d.relatedInformation = relatedInformation + return d +} + +func (d *Diagnostic) AddRelatedInfo(relatedInformation *Diagnostic) *Diagnostic { + if relatedInformation != nil { + d.relatedInformation = append(d.relatedInformation, relatedInformation) + } + return d +} + +func (d *Diagnostic) Clone() *Diagnostic { + result := *d + return &result +} + +func (d *Diagnostic) Localize(locale locale.Locale) string { + return diagnostics.Localize(locale, d.message, d.messageKey, d.messageArgs...) +} + +// For debugging only. +func (d *Diagnostic) String() string { + return diagnostics.Localize(locale.Default, d.message, d.messageKey, d.messageArgs...) +} + +func NewDiagnosticFromSerialized( + file *SourceFile, + loc core.TextRange, + code int32, + category diagnostics.Category, + messageKey diagnostics.Key, + messageArgs []string, + messageChain []*Diagnostic, + relatedInformation []*Diagnostic, + reportsUnnecessary bool, + reportsDeprecated bool, + skippedOnNoEmit bool, +) *Diagnostic { + return &Diagnostic{ + file: file, + loc: loc, + code: code, + category: category, + messageKey: messageKey, + messageArgs: messageArgs, + messageChain: messageChain, + relatedInformation: relatedInformation, + reportsUnnecessary: reportsUnnecessary, + reportsDeprecated: reportsDeprecated, + skippedOnNoEmit: skippedOnNoEmit, + } +} + +func NewDiagnostic(file *SourceFile, loc core.TextRange, message *diagnostics.Message, args ...any) *Diagnostic { + return &Diagnostic{ + file: file, + loc: loc, + code: message.Code(), + category: message.Category(), + message: message, + messageKey: message.Key(), + messageArgs: diagnostics.StringifyArgs(args), + reportsUnnecessary: message.ReportsUnnecessary(), + reportsDeprecated: message.ReportsDeprecated(), + } +} + +func NewDiagnosticChain(chain *Diagnostic, message *diagnostics.Message, args ...any) *Diagnostic { + if chain != nil { + return NewDiagnostic(chain.file, chain.loc, message, args...).AddMessageChain(chain).SetRelatedInfo(chain.relatedInformation) + } + return NewDiagnostic(nil, core.TextRange{}, message, args...) +} + +func NewCompilerDiagnostic(message *diagnostics.Message, args ...any) *Diagnostic { + return NewDiagnostic(nil, core.UndefinedTextRange(), message, args...) +} + +type DiagnosticsCollection struct { + mu sync.Mutex + count int + fileDiagnostics map[string][]*Diagnostic + fileDiagnosticsSorted collections.Set[string] + nonFileDiagnostics []*Diagnostic + nonFileDiagnosticsSorted bool +} + +func (c *DiagnosticsCollection) Add(diagnostic *Diagnostic) { + c.mu.Lock() + defer c.mu.Unlock() + + c.count++ + + if diagnostic.File() != nil { + fileName := diagnostic.File().FileName() + if c.fileDiagnostics == nil { + c.fileDiagnostics = make(map[string][]*Diagnostic) + } + c.fileDiagnostics[fileName] = append(c.fileDiagnostics[fileName], diagnostic) + c.fileDiagnosticsSorted.Delete(fileName) + } else { + c.nonFileDiagnostics = append(c.nonFileDiagnostics, diagnostic) + c.nonFileDiagnosticsSorted = false + } +} + +func (c *DiagnosticsCollection) Lookup(diagnostic *Diagnostic) *Diagnostic { + c.mu.Lock() + defer c.mu.Unlock() + + var diagnostics []*Diagnostic + if diagnostic.File() != nil { + diagnostics = c.getDiagnosticsForFileLocked(diagnostic.File().FileName()) + } else { + diagnostics = c.getGlobalDiagnosticsLocked() + } + if i, ok := slices.BinarySearchFunc(diagnostics, diagnostic, CompareDiagnostics); ok { + return diagnostics[i] + } + return nil +} + +func (c *DiagnosticsCollection) GetGlobalDiagnostics() []*Diagnostic { + c.mu.Lock() + defer c.mu.Unlock() + + return c.getGlobalDiagnosticsLocked() +} + +func (c *DiagnosticsCollection) getGlobalDiagnosticsLocked() []*Diagnostic { + if !c.nonFileDiagnosticsSorted { + slices.SortStableFunc(c.nonFileDiagnostics, CompareDiagnostics) + c.nonFileDiagnosticsSorted = true + } + return slices.Clone(c.nonFileDiagnostics) +} + +func (c *DiagnosticsCollection) GetDiagnosticsForFile(fileName string) []*Diagnostic { + c.mu.Lock() + defer c.mu.Unlock() + + return c.getDiagnosticsForFileLocked(fileName) +} + +func (c *DiagnosticsCollection) getDiagnosticsForFileLocked(fileName string) []*Diagnostic { + if !c.fileDiagnosticsSorted.Has(fileName) { + slices.SortStableFunc(c.fileDiagnostics[fileName], CompareDiagnostics) + c.fileDiagnosticsSorted.Add(fileName) + } + return slices.Clone(c.fileDiagnostics[fileName]) +} + +func (c *DiagnosticsCollection) GetDiagnostics() []*Diagnostic { + c.mu.Lock() + defer c.mu.Unlock() + + diagnostics := make([]*Diagnostic, 0, c.count) + diagnostics = append(diagnostics, c.nonFileDiagnostics...) + for _, diags := range c.fileDiagnostics { + diagnostics = append(diagnostics, diags...) + } + slices.SortFunc(diagnostics, CompareDiagnostics) + return diagnostics +} + +func getDiagnosticPath(d *Diagnostic) string { + if d.File() != nil { + return d.File().FileName() + } + return "" +} + +func EqualDiagnostics(d1, d2 *Diagnostic) bool { + if d1 == d2 { + return true + } + return EqualDiagnosticsNoRelatedInfo(d1, d2) && + slices.EqualFunc(d1.RelatedInformation(), d2.RelatedInformation(), EqualDiagnostics) +} + +func EqualDiagnosticsNoRelatedInfo(d1, d2 *Diagnostic) bool { + if d1 == d2 { + return true + } + return getDiagnosticPath(d1) == getDiagnosticPath(d2) && + d1.Loc() == d2.Loc() && + d1.Code() == d2.Code() && + slices.Equal(d1.MessageArgs(), d2.MessageArgs()) && + slices.EqualFunc(d1.MessageChain(), d2.MessageChain(), equalMessageChain) +} + +func equalMessageChain(c1, c2 *Diagnostic) bool { + if c1 == c2 { + return true + } + return c1.Code() == c2.Code() && + slices.Equal(c1.MessageArgs(), c2.MessageArgs()) && + slices.EqualFunc(c1.MessageChain(), c2.MessageChain(), equalMessageChain) +} + +func compareMessageChainSize(c1, c2 []*Diagnostic) int { + c := len(c2) - len(c1) + if c != 0 { + return c + } + for i := range c1 { + c = compareMessageChainSize(c1[i].MessageChain(), c2[i].MessageChain()) + if c != 0 { + return c + } + } + return 0 +} + +func compareMessageChainContent(c1, c2 []*Diagnostic) int { + for i := range c1 { + c := slices.Compare(c1[i].MessageArgs(), c2[i].MessageArgs()) + if c != 0 { + return c + } + if c1[i].MessageChain() != nil { + c = compareMessageChainContent(c1[i].MessageChain(), c2[i].MessageChain()) + if c != 0 { + return c + } + } + } + return 0 +} + +func compareRelatedInfo(r1, r2 []*Diagnostic) int { + c := len(r2) - len(r1) + if c != 0 { + return c + } + for i := range r1 { + c = CompareDiagnostics(r1[i], r2[i]) + if c != 0 { + return c + } + } + return 0 +} + +func CompareDiagnostics(d1, d2 *Diagnostic) int { + if d1 == d2 { + return 0 + } + c := strings.Compare(getDiagnosticPath(d1), getDiagnosticPath(d2)) + if c != 0 { + return c + } + c = d1.Loc().Pos() - d2.Loc().Pos() + if c != 0 { + return c + } + c = d1.Loc().End() - d2.Loc().End() + if c != 0 { + return c + } + c = int(d1.Code()) - int(d2.Code()) + if c != 0 { + return c + } + c = slices.Compare(d1.MessageArgs(), d2.MessageArgs()) + if c != 0 { + return c + } + c = compareMessageChainSize(d1.MessageChain(), d2.MessageChain()) + if c != 0 { + return c + } + c = compareMessageChainContent(d1.MessageChain(), d2.MessageChain()) + if c != 0 { + return c + } + return compareRelatedInfo(d1.RelatedInformation(), d2.RelatedInformation()) +} diff --git a/tools/tsgo/internal/ast/flow.go b/tools/tsgo/internal/ast/flow.go new file mode 100644 index 00000000..274a92f7 --- /dev/null +++ b/tools/tsgo/internal/ast/flow.go @@ -0,0 +1,75 @@ +package ast + +// FlowFlags + +type FlowFlags uint32 + +const ( + FlowFlagsUnreachable FlowFlags = 1 << 0 // Unreachable code + FlowFlagsStart FlowFlags = 1 << 1 // Start of flow graph + FlowFlagsBranchLabel FlowFlags = 1 << 2 // Non-looping junction + FlowFlagsLoopLabel FlowFlags = 1 << 3 // Looping junction + FlowFlagsAssignment FlowFlags = 1 << 4 // Assignment + FlowFlagsTrueCondition FlowFlags = 1 << 5 // Condition known to be true + FlowFlagsFalseCondition FlowFlags = 1 << 6 // Condition known to be false + FlowFlagsSwitchClause FlowFlags = 1 << 7 // Switch statement clause + FlowFlagsArrayMutation FlowFlags = 1 << 8 // Potential array mutation + FlowFlagsCall FlowFlags = 1 << 9 // Potential assertion call + FlowFlagsReduceLabel FlowFlags = 1 << 10 // Temporarily reduce antecedents of label + FlowFlagsReferenced FlowFlags = 1 << 11 // Referenced as antecedent once + FlowFlagsShared FlowFlags = 1 << 12 // Referenced as antecedent more than once + FlowFlagsLabel = FlowFlagsBranchLabel | FlowFlagsLoopLabel + FlowFlagsCondition = FlowFlagsTrueCondition | FlowFlagsFalseCondition +) + +// FlowNode + +type FlowNode struct { + Flags FlowFlags + Node *Node // Associated AST node + Antecedent *FlowNode // Antecedent for all but FlowLabel + Antecedents *FlowList // Linked list of antecedents for FlowLabel +} + +type FlowList struct { + Flow *FlowNode + Next *FlowList +} + +type FlowLabel = FlowNode + +// FlowSwitchClauseData (synthetic AST node for FlowFlagsSwitchClause) + +type FlowSwitchClauseData struct { + NodeBase + SwitchStatement *Node + ClauseStart int32 // Start index of case/default clause range + ClauseEnd int32 // End index of case/default clause range +} + +func NewFlowSwitchClauseData(switchStatement *Node, clauseStart int, clauseEnd int) *Node { + node := &FlowSwitchClauseData{} + node.SwitchStatement = switchStatement + node.ClauseStart = int32(clauseStart) + node.ClauseEnd = int32(clauseEnd) + return newNode(KindUnknown, node, NodeFactoryHooks{}) +} + +func (node *FlowSwitchClauseData) IsEmpty() bool { + return node.ClauseStart == node.ClauseEnd +} + +// FlowReduceLabelData (synthetic AST node for FlowFlagsReduceLabel) + +type FlowReduceLabelData struct { + NodeBase + Target *FlowLabel // Target label + Antecedents *FlowList // Temporary antecedent list +} + +func NewFlowReduceLabelData(target *FlowLabel, antecedents *FlowList) *Node { + node := &FlowReduceLabelData{} + node.Target = target + node.Antecedents = antecedents + return newNode(KindUnknown, node, NodeFactoryHooks{}) +} diff --git a/tools/tsgo/internal/ast/functionflags.go b/tools/tsgo/internal/ast/functionflags.go new file mode 100644 index 00000000..431d40b5 --- /dev/null +++ b/tools/tsgo/internal/ast/functionflags.go @@ -0,0 +1,37 @@ +package ast + +type FunctionFlags uint32 + +const ( + FunctionFlagsNormal FunctionFlags = 0 + FunctionFlagsGenerator FunctionFlags = 1 << 0 + FunctionFlagsAsync FunctionFlags = 1 << 1 + FunctionFlagsInvalid FunctionFlags = 1 << 2 + FunctionFlagsAsyncGenerator FunctionFlags = FunctionFlagsAsync | FunctionFlagsGenerator +) + +func GetFunctionFlags(node *Node) FunctionFlags { + if node == nil { + return FunctionFlagsInvalid + } + data := node.BodyData() + if data == nil { + return FunctionFlagsInvalid + } + flags := FunctionFlagsNormal + switch node.Kind { + case KindFunctionDeclaration, KindFunctionExpression, KindMethodDeclaration: + if data.AsteriskToken != nil { + flags |= FunctionFlagsGenerator + } + fallthrough + case KindArrowFunction: + if HasSyntacticModifier(node, ModifierFlagsAsync) { + flags |= FunctionFlagsAsync + } + } + if data.Body == nil { + flags |= FunctionFlagsInvalid + } + return flags +} diff --git a/tools/tsgo/internal/ast/ids.go b/tools/tsgo/internal/ast/ids.go new file mode 100644 index 00000000..63e415de --- /dev/null +++ b/tools/tsgo/internal/ast/ids.go @@ -0,0 +1,6 @@ +package ast + +type ( + NodeId uint64 + SymbolId uint64 +) diff --git a/tools/tsgo/internal/ast/kind_generated.go b/tools/tsgo/internal/ast/kind_generated.go new file mode 100644 index 00000000..f3568448 --- /dev/null +++ b/tools/tsgo/internal/ast/kind_generated.go @@ -0,0 +1,463 @@ +// Code generated by _scripts/generate-go-ast.ts. DO NOT EDIT. + +package ast + +//go:generate go tool golang.org/x/tools/cmd/stringer -type=Kind -output=kind_stringer_generated.go +//go:generate npx dprint fmt kind_stringer_generated.go + +type Kind int16 + +const ( + KindUnknown Kind = iota + KindEndOfFile + KindSingleLineCommentTrivia + KindMultiLineCommentTrivia + KindNewLineTrivia + KindWhitespaceTrivia + KindConflictMarkerTrivia + KindNonTextFileMarkerTrivia + KindNumericLiteral + KindBigIntLiteral + KindStringLiteral + KindJsxText + KindJsxTextAllWhiteSpaces + KindRegularExpressionLiteral + KindNoSubstitutionTemplateLiteral + // Pseudo-literals + KindTemplateHead + KindTemplateMiddle + KindTemplateTail + // Punctuation + KindOpenBraceToken + KindCloseBraceToken + KindOpenParenToken + KindCloseParenToken + KindOpenBracketToken + KindCloseBracketToken + KindDotToken + KindDotDotDotToken + KindSemicolonToken + KindCommaToken + KindQuestionDotToken + KindLessThanToken + KindLessThanSlashToken + KindGreaterThanToken + KindLessThanEqualsToken + KindGreaterThanEqualsToken + KindEqualsEqualsToken + KindExclamationEqualsToken + KindEqualsEqualsEqualsToken + KindExclamationEqualsEqualsToken + KindEqualsGreaterThanToken + KindPlusToken + KindMinusToken + KindAsteriskToken + KindAsteriskAsteriskToken + KindSlashToken + KindPercentToken + KindPlusPlusToken + KindMinusMinusToken + KindLessThanLessThanToken + KindGreaterThanGreaterThanToken + KindGreaterThanGreaterThanGreaterThanToken + KindAmpersandToken + KindBarToken + KindCaretToken + KindExclamationToken + KindTildeToken + KindAmpersandAmpersandToken + KindBarBarToken + KindQuestionToken + KindColonToken + KindAtToken + KindQuestionQuestionToken + // Only the JSDoc scanner produces BacktickToken. The normal scanner produces NoSubstitutionTemplateLiteral and related kinds. + KindBacktickToken + // Only the JSDoc scanner produces HashToken. The normal scanner produces PrivateIdentifier. + KindHashToken + // Assignments + KindEqualsToken + KindPlusEqualsToken + KindMinusEqualsToken + KindAsteriskEqualsToken + KindAsteriskAsteriskEqualsToken + KindSlashEqualsToken + KindPercentEqualsToken + KindLessThanLessThanEqualsToken + KindGreaterThanGreaterThanEqualsToken + KindGreaterThanGreaterThanGreaterThanEqualsToken + KindAmpersandEqualsToken + KindBarEqualsToken + KindBarBarEqualsToken + KindAmpersandAmpersandEqualsToken + KindQuestionQuestionEqualsToken + KindCaretEqualsToken + // Identifiers and PrivateIdentifier + KindIdentifier + KindPrivateIdentifier + KindJSDocCommentTextToken + // Reserved words + KindBreakKeyword + KindCaseKeyword + KindCatchKeyword + KindClassKeyword + KindConstKeyword + KindContinueKeyword + KindDebuggerKeyword + KindDefaultKeyword + KindDeleteKeyword + KindDoKeyword + KindElseKeyword + KindEnumKeyword + KindExportKeyword + KindExtendsKeyword + KindFalseKeyword + KindFinallyKeyword + KindForKeyword + KindFunctionKeyword + KindIfKeyword + KindImportKeyword + KindInKeyword + KindInstanceOfKeyword + KindNewKeyword + KindNullKeyword + KindReturnKeyword + KindSuperKeyword + KindSwitchKeyword + KindThisKeyword + KindThrowKeyword + KindTrueKeyword + KindTryKeyword + KindTypeOfKeyword + KindVarKeyword + KindVoidKeyword + KindWhileKeyword + KindWithKeyword + // Strict mode reserved words + KindImplementsKeyword + KindInterfaceKeyword + KindLetKeyword + KindPackageKeyword + KindPrivateKeyword + KindProtectedKeyword + KindPublicKeyword + KindStaticKeyword + KindYieldKeyword + // Contextual keywords + KindAbstractKeyword + KindAccessorKeyword + KindAsKeyword + KindAssertsKeyword + KindAssertKeyword + KindAnyKeyword + KindAsyncKeyword + KindAwaitKeyword + KindBooleanKeyword + KindConstructorKeyword + KindDeclareKeyword + KindGetKeyword + KindImmediateKeyword + KindInferKeyword + KindIntrinsicKeyword + KindIsKeyword + KindKeyOfKeyword + KindModuleKeyword + KindNamespaceKeyword + KindNeverKeyword + KindOutKeyword + KindReadonlyKeyword + KindRequireKeyword + KindNumberKeyword + KindObjectKeyword + KindSatisfiesKeyword + KindSetKeyword + KindStringKeyword + KindSymbolKeyword + KindTypeKeyword + KindUndefinedKeyword + KindUniqueKeyword + KindUnknownKeyword + KindUsingKeyword + KindFromKeyword + KindGlobalKeyword + KindBigIntKeyword + KindOverrideKeyword + KindOfKeyword + KindDeferKeyword // LastKeyword and LastToken and LastContextualKeyword + // Parse tree nodes + // Names + KindQualifiedName + KindComputedPropertyName + // Signature elements + KindTypeParameter + KindParameter + KindDecorator + // TypeMember + KindPropertySignature + KindPropertyDeclaration + KindMethodSignature + KindMethodDeclaration + KindClassStaticBlockDeclaration + KindConstructor + KindGetAccessor + KindSetAccessor + KindCallSignature + KindConstructSignature + KindIndexSignature + // Type + KindTypePredicate + KindTypeReference + KindFunctionType + KindConstructorType + KindTypeQuery + KindTypeLiteral + KindArrayType + KindTupleType + KindOptionalType + KindRestType + KindUnionType + KindIntersectionType + KindConditionalType + KindInferType + KindParenthesizedType + KindThisType + KindTypeOperator + KindIndexedAccessType + KindMappedType + KindLiteralType + KindNamedTupleMember + KindTemplateLiteralType + KindTemplateLiteralTypeSpan + KindImportType + // Binding patterns + KindObjectBindingPattern + KindArrayBindingPattern + KindBindingElement + // Expression + KindArrayLiteralExpression + KindObjectLiteralExpression + KindPropertyAccessExpression + KindElementAccessExpression + KindCallExpression + KindNewExpression + KindTaggedTemplateExpression + KindTypeAssertionExpression + KindParenthesizedExpression + KindFunctionExpression + KindArrowFunction + KindDeleteExpression + KindTypeOfExpression + KindVoidExpression + KindAwaitExpression + KindPrefixUnaryExpression + KindPostfixUnaryExpression + KindBinaryExpression + KindConditionalExpression + KindTemplateExpression + KindYieldExpression + KindSpreadElement + KindClassExpression + KindOmittedExpression + KindExpressionWithTypeArguments + KindAsExpression + KindNonNullExpression + KindMetaProperty + KindSyntheticExpression + KindSatisfiesExpression + // Misc + KindTemplateSpan + KindSemicolonClassElement + // Element + KindBlock + KindEmptyStatement + KindVariableStatement + KindExpressionStatement + KindIfStatement + KindDoStatement + KindWhileStatement + KindForStatement + KindForInStatement + KindForOfStatement + KindContinueStatement + KindBreakStatement + KindReturnStatement + KindWithStatement + KindSwitchStatement + KindLabeledStatement + KindThrowStatement + KindTryStatement + KindDebuggerStatement + KindVariableDeclaration + KindVariableDeclarationList + KindFunctionDeclaration + KindClassDeclaration + KindInterfaceDeclaration + KindTypeAliasDeclaration + KindEnumDeclaration + KindModuleDeclaration + KindModuleBlock + KindCaseBlock + KindNamespaceExportDeclaration + KindImportEqualsDeclaration + KindImportDeclaration + KindImportClause + KindNamespaceImport + KindNamedImports + KindImportSpecifier + KindExportAssignment + KindExportDeclaration + KindNamedExports + KindNamespaceExport + KindExportSpecifier + KindMissingDeclaration + // Module references + KindExternalModuleReference + // JSX + KindJsxElement + KindJsxSelfClosingElement + KindJsxOpeningElement + KindJsxClosingElement + KindJsxFragment + KindJsxOpeningFragment + KindJsxClosingFragment + KindJsxAttribute + KindJsxAttributes + KindJsxSpreadAttribute + KindJsxExpression + KindJsxNamespacedName + // Clauses + KindCaseClause + KindDefaultClause + KindHeritageClause + KindCatchClause + // Import attributes + KindImportAttributes + KindImportAttribute + // Property assignments + KindPropertyAssignment + KindShorthandPropertyAssignment + KindSpreadAssignment + // Enum + KindEnumMember + // Top-level nodes + KindSourceFile + // JSDoc nodes + KindJSDocTypeExpression + KindJSDocNameReference + KindJSDocAllType // The * type + KindJSDocNullableType + KindJSDocNonNullableType + KindJSDocOptionalType + KindJSDocVariadicType + KindJSDoc + KindJSDocText + KindJSDocTypeLiteral + KindJSDocSignature + KindJSDocLink + KindJSDocLinkCode + KindJSDocLinkPlain + KindJSDocUnknownTag + KindJSDocAugmentsTag + KindJSDocImplementsTag + KindJSDocDeprecatedTag + KindJSDocPublicTag + KindJSDocPrivateTag + KindJSDocProtectedTag + KindJSDocReadonlyTag + KindJSDocOverrideTag + KindJSDocCallbackTag + KindJSDocOverloadTag + KindJSDocParameterTag + KindJSDocReturnTag + KindJSDocThisTag + KindJSDocTypeTag + KindJSDocTemplateTag + KindJSDocTypedefTag + KindJSDocSeeTag + KindJSDocPropertyTag + KindJSDocThrowsTag + KindJSDocSatisfiesTag + KindJSDocImportTag + // Synthesized list + KindSyntaxList + // Reparsed JS nodes + KindJSTypeAliasDeclaration + KindJSImportDeclaration + // Transformation nodes + KindNotEmittedStatement + KindPartiallyEmittedExpression + KindSyntheticReferenceExpression + KindNotEmittedTypeElement + KindCount + KindFirstAssignment = KindEqualsToken + KindLastAssignment = KindCaretEqualsToken + KindFirstCompoundAssignment = KindPlusEqualsToken + KindLastCompoundAssignment = KindCaretEqualsToken + KindFirstReservedWord = KindBreakKeyword + KindLastReservedWord = KindWithKeyword + KindFirstKeyword = KindBreakKeyword + KindLastKeyword = KindDeferKeyword + KindFirstFutureReservedWord = KindImplementsKeyword + KindLastFutureReservedWord = KindYieldKeyword + KindFirstTypeNode = KindTypePredicate + KindLastTypeNode = KindImportType + KindFirstPunctuation = KindOpenBraceToken + KindLastPunctuation = KindCaretEqualsToken + KindFirstToken = KindUnknown + KindLastToken = KindLastKeyword + KindFirstLiteralToken = KindNumericLiteral + KindLastLiteralToken = KindNoSubstitutionTemplateLiteral + KindFirstTemplateToken = KindNoSubstitutionTemplateLiteral + KindLastTemplateToken = KindTemplateTail + KindFirstBinaryOperator = KindLessThanToken + KindLastBinaryOperator = KindCaretEqualsToken + KindFirstStatement = KindVariableStatement + KindLastStatement = KindDebuggerStatement + KindFirstNode = KindQualifiedName + KindFirstJSDocNode = KindJSDocTypeExpression + KindLastJSDocNode = KindJSDocImportTag + KindFirstJSDocTagNode = KindJSDocUnknownTag + KindLastJSDocTagNode = KindJSDocImportTag + KindFirstContextualKeyword = KindAbstractKeyword + KindLastContextualKeyword = KindDeferKeyword + KindLastUnaryOperator = KindTildeToken + KindFirstTriviaToken = KindSingleLineCommentTrivia + KindLastTriviaToken = KindConflictMarkerTrivia +) + +type ( + TriviaSyntaxKind = Kind // KindSingleLineCommentTrivia | KindMultiLineCommentTrivia | KindNewLineTrivia | KindWhitespaceTrivia | KindConflictMarkerTrivia + LiteralSyntaxKind = Kind // KindNumericLiteral | KindBigIntLiteral | KindStringLiteral | KindJsxText | KindJsxTextAllWhiteSpaces | KindRegularExpressionLiteral | KindNoSubstitutionTemplateLiteral + PseudoLiteralSyntaxKind = Kind // KindTemplateHead | KindTemplateMiddle | KindTemplateTail + PunctuationSyntaxKind = Kind // KindOpenBraceToken | KindCloseBraceToken | KindOpenParenToken | KindCloseParenToken | KindOpenBracketToken | KindCloseBracketToken | KindDotToken | KindDotDotDotToken | KindSemicolonToken | KindCommaToken | KindQuestionDotToken | KindLessThanToken | KindLessThanSlashToken | KindGreaterThanToken | KindLessThanEqualsToken | KindGreaterThanEqualsToken | KindEqualsEqualsToken | KindExclamationEqualsToken | KindEqualsEqualsEqualsToken | KindExclamationEqualsEqualsToken | KindEqualsGreaterThanToken | KindPlusToken | KindMinusToken | KindAsteriskToken | KindAsteriskAsteriskToken | KindSlashToken | KindPercentToken | KindPlusPlusToken | KindMinusMinusToken | KindLessThanLessThanToken | KindGreaterThanGreaterThanToken | KindGreaterThanGreaterThanGreaterThanToken | KindAmpersandToken | KindBarToken | KindCaretToken | KindExclamationToken | KindTildeToken | KindAmpersandAmpersandToken | KindBarBarToken | KindQuestionToken | KindColonToken | KindAtToken | KindQuestionQuestionToken | KindBacktickToken | KindHashToken | KindEqualsToken | KindPlusEqualsToken | KindMinusEqualsToken | KindAsteriskEqualsToken | KindAsteriskAsteriskEqualsToken | KindSlashEqualsToken | KindPercentEqualsToken | KindLessThanLessThanEqualsToken | KindGreaterThanGreaterThanEqualsToken | KindGreaterThanGreaterThanGreaterThanEqualsToken | KindAmpersandEqualsToken | KindBarEqualsToken | KindBarBarEqualsToken | KindAmpersandAmpersandEqualsToken | KindQuestionQuestionEqualsToken | KindCaretEqualsToken + KeywordSyntaxKind = Kind // KindBreakKeyword | KindCaseKeyword | KindCatchKeyword | KindClassKeyword | KindConstKeyword | KindContinueKeyword | KindDebuggerKeyword | KindDefaultKeyword | KindDeleteKeyword | KindDoKeyword | KindElseKeyword | KindEnumKeyword | KindExportKeyword | KindExtendsKeyword | KindFalseKeyword | KindFinallyKeyword | KindForKeyword | KindFunctionKeyword | KindIfKeyword | KindImportKeyword | KindInKeyword | KindInstanceOfKeyword | KindNewKeyword | KindNullKeyword | KindReturnKeyword | KindSuperKeyword | KindSwitchKeyword | KindThisKeyword | KindThrowKeyword | KindTrueKeyword | KindTryKeyword | KindTypeOfKeyword | KindVarKeyword | KindVoidKeyword | KindWhileKeyword | KindWithKeyword | KindImplementsKeyword | KindInterfaceKeyword | KindLetKeyword | KindPackageKeyword | KindPrivateKeyword | KindProtectedKeyword | KindPublicKeyword | KindStaticKeyword | KindYieldKeyword | KindAbstractKeyword | KindAccessorKeyword | KindAsKeyword | KindAssertsKeyword | KindAssertKeyword | KindAnyKeyword | KindAsyncKeyword | KindAwaitKeyword | KindBooleanKeyword | KindConstructorKeyword | KindDeclareKeyword | KindGetKeyword | KindImmediateKeyword | KindInferKeyword | KindIntrinsicKeyword | KindIsKeyword | KindKeyOfKeyword | KindModuleKeyword | KindNamespaceKeyword | KindNeverKeyword | KindOutKeyword | KindReadonlyKeyword | KindRequireKeyword | KindNumberKeyword | KindObjectKeyword | KindSatisfiesKeyword | KindSetKeyword | KindStringKeyword | KindSymbolKeyword | KindTypeKeyword | KindUndefinedKeyword | KindUniqueKeyword | KindUnknownKeyword | KindUsingKeyword | KindFromKeyword | KindGlobalKeyword | KindBigIntKeyword | KindOverrideKeyword | KindOfKeyword | KindDeferKeyword + ModifierSyntaxKind = Kind // KindAbstractKeyword | KindAccessorKeyword | KindAsyncKeyword | KindConstKeyword | KindDeclareKeyword | KindDefaultKeyword | KindExportKeyword | KindInKeyword | KindPrivateKeyword | KindProtectedKeyword | KindPublicKeyword | KindReadonlyKeyword | KindOutKeyword | KindOverrideKeyword | KindStaticKeyword + KeywordTypeSyntaxKind = Kind // KindAnyKeyword | KindBigIntKeyword | KindBooleanKeyword | KindIntrinsicKeyword | KindNeverKeyword | KindNumberKeyword | KindObjectKeyword | KindStringKeyword | KindSymbolKeyword | KindUndefinedKeyword | KindUnknownKeyword | KindVoidKeyword + KeywordExpressionSyntaxKind = Kind // KindNullKeyword | KindTrueKeyword | KindFalseKeyword | KindThisKeyword | KindSuperKeyword | KindImportKeyword + TokenSyntaxKind = Kind // KindUnknown | KindEndOfFile | KindSingleLineCommentTrivia | KindMultiLineCommentTrivia | KindNewLineTrivia | KindWhitespaceTrivia | KindConflictMarkerTrivia | KindNonTextFileMarkerTrivia | KindNumericLiteral | KindBigIntLiteral | KindStringLiteral | KindJsxText | KindJsxTextAllWhiteSpaces | KindRegularExpressionLiteral | KindNoSubstitutionTemplateLiteral | KindTemplateHead | KindTemplateMiddle | KindTemplateTail | KindOpenBraceToken | KindCloseBraceToken | KindOpenParenToken | KindCloseParenToken | KindOpenBracketToken | KindCloseBracketToken | KindDotToken | KindDotDotDotToken | KindSemicolonToken | KindCommaToken | KindQuestionDotToken | KindLessThanToken | KindLessThanSlashToken | KindGreaterThanToken | KindLessThanEqualsToken | KindGreaterThanEqualsToken | KindEqualsEqualsToken | KindExclamationEqualsToken | KindEqualsEqualsEqualsToken | KindExclamationEqualsEqualsToken | KindEqualsGreaterThanToken | KindPlusToken | KindMinusToken | KindAsteriskToken | KindAsteriskAsteriskToken | KindSlashToken | KindPercentToken | KindPlusPlusToken | KindMinusMinusToken | KindLessThanLessThanToken | KindGreaterThanGreaterThanToken | KindGreaterThanGreaterThanGreaterThanToken | KindAmpersandToken | KindBarToken | KindCaretToken | KindExclamationToken | KindTildeToken | KindAmpersandAmpersandToken | KindBarBarToken | KindQuestionToken | KindColonToken | KindAtToken | KindQuestionQuestionToken | KindBacktickToken | KindHashToken | KindEqualsToken | KindPlusEqualsToken | KindMinusEqualsToken | KindAsteriskEqualsToken | KindAsteriskAsteriskEqualsToken | KindSlashEqualsToken | KindPercentEqualsToken | KindLessThanLessThanEqualsToken | KindGreaterThanGreaterThanEqualsToken | KindGreaterThanGreaterThanGreaterThanEqualsToken | KindAmpersandEqualsToken | KindBarEqualsToken | KindBarBarEqualsToken | KindAmpersandAmpersandEqualsToken | KindQuestionQuestionEqualsToken | KindCaretEqualsToken | KindIdentifier | KindPrivateIdentifier | KindJSDocCommentTextToken | KindBreakKeyword | KindCaseKeyword | KindCatchKeyword | KindClassKeyword | KindConstKeyword | KindContinueKeyword | KindDebuggerKeyword | KindDefaultKeyword | KindDeleteKeyword | KindDoKeyword | KindElseKeyword | KindEnumKeyword | KindExportKeyword | KindExtendsKeyword | KindFalseKeyword | KindFinallyKeyword | KindForKeyword | KindFunctionKeyword | KindIfKeyword | KindImportKeyword | KindInKeyword | KindInstanceOfKeyword | KindNewKeyword | KindNullKeyword | KindReturnKeyword | KindSuperKeyword | KindSwitchKeyword | KindThisKeyword | KindThrowKeyword | KindTrueKeyword | KindTryKeyword | KindTypeOfKeyword | KindVarKeyword | KindVoidKeyword | KindWhileKeyword | KindWithKeyword | KindImplementsKeyword | KindInterfaceKeyword | KindLetKeyword | KindPackageKeyword | KindPrivateKeyword | KindProtectedKeyword | KindPublicKeyword | KindStaticKeyword | KindYieldKeyword | KindAbstractKeyword | KindAccessorKeyword | KindAsKeyword | KindAssertsKeyword | KindAssertKeyword | KindAnyKeyword | KindAsyncKeyword | KindAwaitKeyword | KindBooleanKeyword | KindConstructorKeyword | KindDeclareKeyword | KindGetKeyword | KindImmediateKeyword | KindInferKeyword | KindIntrinsicKeyword | KindIsKeyword | KindKeyOfKeyword | KindModuleKeyword | KindNamespaceKeyword | KindNeverKeyword | KindOutKeyword | KindReadonlyKeyword | KindRequireKeyword | KindNumberKeyword | KindObjectKeyword | KindSatisfiesKeyword | KindSetKeyword | KindStringKeyword | KindSymbolKeyword | KindTypeKeyword | KindUndefinedKeyword | KindUniqueKeyword | KindUnknownKeyword | KindUsingKeyword | KindFromKeyword | KindGlobalKeyword | KindBigIntKeyword | KindOverrideKeyword | KindOfKeyword | KindDeferKeyword + JsxTokenSyntaxKind = Kind // KindLessThanSlashToken | KindEndOfFile | KindConflictMarkerTrivia | KindJsxText | KindJsxTextAllWhiteSpaces | KindOpenBraceToken | KindLessThanToken + JSDocNodeSyntaxKind = Kind // KindJSDocTypeExpression | KindJSDocNameReference | KindJSDocAllType | KindJSDocNullableType | KindJSDocNonNullableType | KindJSDocOptionalType | KindJSDocVariadicType | KindJSDoc | KindJSDocText | KindJSDocTypeLiteral | KindJSDocSignature | KindJSDocLink | KindJSDocLinkCode | KindJSDocLinkPlain | KindJSDocUnknownTag | KindJSDocAugmentsTag | KindJSDocImplementsTag | KindJSDocDeprecatedTag | KindJSDocPublicTag | KindJSDocPrivateTag | KindJSDocProtectedTag | KindJSDocReadonlyTag | KindJSDocOverrideTag | KindJSDocCallbackTag | KindJSDocOverloadTag | KindJSDocParameterTag | KindJSDocReturnTag | KindJSDocThisTag | KindJSDocTypeTag | KindJSDocTemplateTag | KindJSDocTypedefTag | KindJSDocSeeTag | KindJSDocPropertyTag | KindJSDocThrowsTag | KindJSDocSatisfiesTag | KindJSDocImportTag + ImportPhaseModifierSyntaxKind = Kind // KindTypeKeyword | KindDeferKeyword + PostfixUnaryOperator = Kind // KindPlusPlusToken | KindMinusMinusToken + PrefixUnaryOperator = Kind // KindPlusToken | KindMinusToken | KindTildeToken | KindExclamationToken | KindPlusPlusToken | KindMinusMinusToken + AssignmentOperator = Kind // KindEqualsToken | KindPlusEqualsToken | KindMinusEqualsToken | KindAsteriskAsteriskEqualsToken | KindAsteriskEqualsToken | KindSlashEqualsToken | KindPercentEqualsToken | KindAmpersandEqualsToken | KindBarEqualsToken | KindCaretEqualsToken | KindLessThanLessThanEqualsToken | KindGreaterThanGreaterThanGreaterThanEqualsToken | KindGreaterThanGreaterThanEqualsToken | KindBarBarEqualsToken | KindAmpersandAmpersandEqualsToken | KindQuestionQuestionEqualsToken + BinaryOperator = Kind // KindQuestionQuestionToken | KindAsteriskAsteriskToken | KindAsteriskToken | KindSlashToken | KindPercentToken | KindPlusToken | KindMinusToken | KindLessThanLessThanToken | KindGreaterThanGreaterThanToken | KindGreaterThanGreaterThanGreaterThanToken | KindLessThanToken | KindLessThanEqualsToken | KindGreaterThanToken | KindGreaterThanEqualsToken | KindInstanceOfKeyword | KindInKeyword | KindEqualsEqualsToken | KindEqualsEqualsEqualsToken | KindExclamationEqualsEqualsToken | KindExclamationEqualsToken | KindAmpersandToken | KindBarToken | KindCaretToken | KindAmpersandAmpersandToken | KindBarBarToken | KindEqualsToken | KindPlusEqualsToken | KindMinusEqualsToken | KindAsteriskAsteriskEqualsToken | KindAsteriskEqualsToken | KindSlashEqualsToken | KindPercentEqualsToken | KindAmpersandEqualsToken | KindBarEqualsToken | KindCaretEqualsToken | KindLessThanLessThanEqualsToken | KindGreaterThanGreaterThanGreaterThanEqualsToken | KindGreaterThanGreaterThanEqualsToken | KindBarBarEqualsToken | KindAmpersandAmpersandEqualsToken | KindQuestionQuestionEqualsToken | KindCommaToken + ExponentiationOperator = Kind // KindAsteriskAsteriskToken + MultiplicativeOperator = Kind // KindAsteriskToken | KindSlashToken | KindPercentToken + MultiplicativeOperatorOrHigher = Kind // KindAsteriskAsteriskToken | KindAsteriskToken | KindSlashToken | KindPercentToken + AdditiveOperator = Kind // KindPlusToken | KindMinusToken + AdditiveOperatorOrHigher = Kind // KindAsteriskAsteriskToken | KindAsteriskToken | KindSlashToken | KindPercentToken | KindPlusToken | KindMinusToken + ShiftOperator = Kind // KindLessThanLessThanToken | KindGreaterThanGreaterThanToken | KindGreaterThanGreaterThanGreaterThanToken + ShiftOperatorOrHigher = Kind // KindAsteriskAsteriskToken | KindAsteriskToken | KindSlashToken | KindPercentToken | KindPlusToken | KindMinusToken | KindLessThanLessThanToken | KindGreaterThanGreaterThanToken | KindGreaterThanGreaterThanGreaterThanToken + RelationalOperator = Kind // KindLessThanToken | KindLessThanEqualsToken | KindGreaterThanToken | KindGreaterThanEqualsToken | KindInstanceOfKeyword | KindInKeyword + RelationalOperatorOrHigher = Kind // KindAsteriskAsteriskToken | KindAsteriskToken | KindSlashToken | KindPercentToken | KindPlusToken | KindMinusToken | KindLessThanLessThanToken | KindGreaterThanGreaterThanToken | KindGreaterThanGreaterThanGreaterThanToken | KindLessThanToken | KindLessThanEqualsToken | KindGreaterThanToken | KindGreaterThanEqualsToken | KindInstanceOfKeyword | KindInKeyword + EqualityOperator = Kind // KindEqualsEqualsToken | KindEqualsEqualsEqualsToken | KindExclamationEqualsEqualsToken | KindExclamationEqualsToken + EqualityOperatorOrHigher = Kind // KindAsteriskAsteriskToken | KindAsteriskToken | KindSlashToken | KindPercentToken | KindPlusToken | KindMinusToken | KindLessThanLessThanToken | KindGreaterThanGreaterThanToken | KindGreaterThanGreaterThanGreaterThanToken | KindLessThanToken | KindLessThanEqualsToken | KindGreaterThanToken | KindGreaterThanEqualsToken | KindInstanceOfKeyword | KindInKeyword | KindEqualsEqualsToken | KindEqualsEqualsEqualsToken | KindExclamationEqualsEqualsToken | KindExclamationEqualsToken + BitwiseOperator = Kind // KindAmpersandToken | KindBarToken | KindCaretToken + BitwiseOperatorOrHigher = Kind // KindAsteriskAsteriskToken | KindAsteriskToken | KindSlashToken | KindPercentToken | KindPlusToken | KindMinusToken | KindLessThanLessThanToken | KindGreaterThanGreaterThanToken | KindGreaterThanGreaterThanGreaterThanToken | KindLessThanToken | KindLessThanEqualsToken | KindGreaterThanToken | KindGreaterThanEqualsToken | KindInstanceOfKeyword | KindInKeyword | KindEqualsEqualsToken | KindEqualsEqualsEqualsToken | KindExclamationEqualsEqualsToken | KindExclamationEqualsToken | KindAmpersandToken | KindBarToken | KindCaretToken + LogicalOperator = Kind // KindAmpersandAmpersandToken | KindBarBarToken + LogicalOperatorOrHigher = Kind // KindAsteriskAsteriskToken | KindAsteriskToken | KindSlashToken | KindPercentToken | KindPlusToken | KindMinusToken | KindLessThanLessThanToken | KindGreaterThanGreaterThanToken | KindGreaterThanGreaterThanGreaterThanToken | KindLessThanToken | KindLessThanEqualsToken | KindGreaterThanToken | KindGreaterThanEqualsToken | KindInstanceOfKeyword | KindInKeyword | KindEqualsEqualsToken | KindEqualsEqualsEqualsToken | KindExclamationEqualsEqualsToken | KindExclamationEqualsToken | KindAmpersandToken | KindBarToken | KindCaretToken | KindAmpersandAmpersandToken | KindBarBarToken + CompoundAssignmentOperator = Kind // KindPlusEqualsToken | KindMinusEqualsToken | KindAsteriskAsteriskEqualsToken | KindAsteriskEqualsToken | KindSlashEqualsToken | KindPercentEqualsToken | KindAmpersandEqualsToken | KindBarEqualsToken | KindCaretEqualsToken | KindLessThanLessThanEqualsToken | KindGreaterThanGreaterThanGreaterThanEqualsToken | KindGreaterThanGreaterThanEqualsToken | KindBarBarEqualsToken | KindAmpersandAmpersandEqualsToken | KindQuestionQuestionEqualsToken + AssignmentOperatorOrHigher = Kind // KindQuestionQuestionToken | KindAsteriskAsteriskToken | KindAsteriskToken | KindSlashToken | KindPercentToken | KindPlusToken | KindMinusToken | KindLessThanLessThanToken | KindGreaterThanGreaterThanToken | KindGreaterThanGreaterThanGreaterThanToken | KindLessThanToken | KindLessThanEqualsToken | KindGreaterThanToken | KindGreaterThanEqualsToken | KindInstanceOfKeyword | KindInKeyword | KindEqualsEqualsToken | KindEqualsEqualsEqualsToken | KindExclamationEqualsEqualsToken | KindExclamationEqualsToken | KindAmpersandToken | KindBarToken | KindCaretToken | KindAmpersandAmpersandToken | KindBarBarToken | KindEqualsToken | KindPlusEqualsToken | KindMinusEqualsToken | KindAsteriskAsteriskEqualsToken | KindAsteriskEqualsToken | KindSlashEqualsToken | KindPercentEqualsToken | KindAmpersandEqualsToken | KindBarEqualsToken | KindCaretEqualsToken | KindLessThanLessThanEqualsToken | KindGreaterThanGreaterThanGreaterThanEqualsToken | KindGreaterThanGreaterThanEqualsToken | KindBarBarEqualsToken | KindAmpersandAmpersandEqualsToken | KindQuestionQuestionEqualsToken + LogicalOrCoalescingAssignmentOperator = Kind // KindAmpersandAmpersandEqualsToken | KindBarBarEqualsToken | KindQuestionQuestionEqualsToken +) diff --git a/tools/tsgo/internal/ast/kind_stringer_generated.go b/tools/tsgo/internal/ast/kind_stringer_generated.go new file mode 100644 index 00000000..b80f1ba5 --- /dev/null +++ b/tools/tsgo/internal/ast/kind_stringer_generated.go @@ -0,0 +1,375 @@ +// Code generated by "stringer -type=Kind -output=kind_stringer_generated.go"; DO NOT EDIT. + +package ast + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[KindUnknown-0] + _ = x[KindEndOfFile-1] + _ = x[KindSingleLineCommentTrivia-2] + _ = x[KindMultiLineCommentTrivia-3] + _ = x[KindNewLineTrivia-4] + _ = x[KindWhitespaceTrivia-5] + _ = x[KindConflictMarkerTrivia-6] + _ = x[KindNonTextFileMarkerTrivia-7] + _ = x[KindNumericLiteral-8] + _ = x[KindBigIntLiteral-9] + _ = x[KindStringLiteral-10] + _ = x[KindJsxText-11] + _ = x[KindJsxTextAllWhiteSpaces-12] + _ = x[KindRegularExpressionLiteral-13] + _ = x[KindNoSubstitutionTemplateLiteral-14] + _ = x[KindTemplateHead-15] + _ = x[KindTemplateMiddle-16] + _ = x[KindTemplateTail-17] + _ = x[KindOpenBraceToken-18] + _ = x[KindCloseBraceToken-19] + _ = x[KindOpenParenToken-20] + _ = x[KindCloseParenToken-21] + _ = x[KindOpenBracketToken-22] + _ = x[KindCloseBracketToken-23] + _ = x[KindDotToken-24] + _ = x[KindDotDotDotToken-25] + _ = x[KindSemicolonToken-26] + _ = x[KindCommaToken-27] + _ = x[KindQuestionDotToken-28] + _ = x[KindLessThanToken-29] + _ = x[KindLessThanSlashToken-30] + _ = x[KindGreaterThanToken-31] + _ = x[KindLessThanEqualsToken-32] + _ = x[KindGreaterThanEqualsToken-33] + _ = x[KindEqualsEqualsToken-34] + _ = x[KindExclamationEqualsToken-35] + _ = x[KindEqualsEqualsEqualsToken-36] + _ = x[KindExclamationEqualsEqualsToken-37] + _ = x[KindEqualsGreaterThanToken-38] + _ = x[KindPlusToken-39] + _ = x[KindMinusToken-40] + _ = x[KindAsteriskToken-41] + _ = x[KindAsteriskAsteriskToken-42] + _ = x[KindSlashToken-43] + _ = x[KindPercentToken-44] + _ = x[KindPlusPlusToken-45] + _ = x[KindMinusMinusToken-46] + _ = x[KindLessThanLessThanToken-47] + _ = x[KindGreaterThanGreaterThanToken-48] + _ = x[KindGreaterThanGreaterThanGreaterThanToken-49] + _ = x[KindAmpersandToken-50] + _ = x[KindBarToken-51] + _ = x[KindCaretToken-52] + _ = x[KindExclamationToken-53] + _ = x[KindTildeToken-54] + _ = x[KindAmpersandAmpersandToken-55] + _ = x[KindBarBarToken-56] + _ = x[KindQuestionToken-57] + _ = x[KindColonToken-58] + _ = x[KindAtToken-59] + _ = x[KindQuestionQuestionToken-60] + _ = x[KindBacktickToken-61] + _ = x[KindHashToken-62] + _ = x[KindEqualsToken-63] + _ = x[KindPlusEqualsToken-64] + _ = x[KindMinusEqualsToken-65] + _ = x[KindAsteriskEqualsToken-66] + _ = x[KindAsteriskAsteriskEqualsToken-67] + _ = x[KindSlashEqualsToken-68] + _ = x[KindPercentEqualsToken-69] + _ = x[KindLessThanLessThanEqualsToken-70] + _ = x[KindGreaterThanGreaterThanEqualsToken-71] + _ = x[KindGreaterThanGreaterThanGreaterThanEqualsToken-72] + _ = x[KindAmpersandEqualsToken-73] + _ = x[KindBarEqualsToken-74] + _ = x[KindBarBarEqualsToken-75] + _ = x[KindAmpersandAmpersandEqualsToken-76] + _ = x[KindQuestionQuestionEqualsToken-77] + _ = x[KindCaretEqualsToken-78] + _ = x[KindIdentifier-79] + _ = x[KindPrivateIdentifier-80] + _ = x[KindJSDocCommentTextToken-81] + _ = x[KindBreakKeyword-82] + _ = x[KindCaseKeyword-83] + _ = x[KindCatchKeyword-84] + _ = x[KindClassKeyword-85] + _ = x[KindConstKeyword-86] + _ = x[KindContinueKeyword-87] + _ = x[KindDebuggerKeyword-88] + _ = x[KindDefaultKeyword-89] + _ = x[KindDeleteKeyword-90] + _ = x[KindDoKeyword-91] + _ = x[KindElseKeyword-92] + _ = x[KindEnumKeyword-93] + _ = x[KindExportKeyword-94] + _ = x[KindExtendsKeyword-95] + _ = x[KindFalseKeyword-96] + _ = x[KindFinallyKeyword-97] + _ = x[KindForKeyword-98] + _ = x[KindFunctionKeyword-99] + _ = x[KindIfKeyword-100] + _ = x[KindImportKeyword-101] + _ = x[KindInKeyword-102] + _ = x[KindInstanceOfKeyword-103] + _ = x[KindNewKeyword-104] + _ = x[KindNullKeyword-105] + _ = x[KindReturnKeyword-106] + _ = x[KindSuperKeyword-107] + _ = x[KindSwitchKeyword-108] + _ = x[KindThisKeyword-109] + _ = x[KindThrowKeyword-110] + _ = x[KindTrueKeyword-111] + _ = x[KindTryKeyword-112] + _ = x[KindTypeOfKeyword-113] + _ = x[KindVarKeyword-114] + _ = x[KindVoidKeyword-115] + _ = x[KindWhileKeyword-116] + _ = x[KindWithKeyword-117] + _ = x[KindImplementsKeyword-118] + _ = x[KindInterfaceKeyword-119] + _ = x[KindLetKeyword-120] + _ = x[KindPackageKeyword-121] + _ = x[KindPrivateKeyword-122] + _ = x[KindProtectedKeyword-123] + _ = x[KindPublicKeyword-124] + _ = x[KindStaticKeyword-125] + _ = x[KindYieldKeyword-126] + _ = x[KindAbstractKeyword-127] + _ = x[KindAccessorKeyword-128] + _ = x[KindAsKeyword-129] + _ = x[KindAssertsKeyword-130] + _ = x[KindAssertKeyword-131] + _ = x[KindAnyKeyword-132] + _ = x[KindAsyncKeyword-133] + _ = x[KindAwaitKeyword-134] + _ = x[KindBooleanKeyword-135] + _ = x[KindConstructorKeyword-136] + _ = x[KindDeclareKeyword-137] + _ = x[KindGetKeyword-138] + _ = x[KindImmediateKeyword-139] + _ = x[KindInferKeyword-140] + _ = x[KindIntrinsicKeyword-141] + _ = x[KindIsKeyword-142] + _ = x[KindKeyOfKeyword-143] + _ = x[KindModuleKeyword-144] + _ = x[KindNamespaceKeyword-145] + _ = x[KindNeverKeyword-146] + _ = x[KindOutKeyword-147] + _ = x[KindReadonlyKeyword-148] + _ = x[KindRequireKeyword-149] + _ = x[KindNumberKeyword-150] + _ = x[KindObjectKeyword-151] + _ = x[KindSatisfiesKeyword-152] + _ = x[KindSetKeyword-153] + _ = x[KindStringKeyword-154] + _ = x[KindSymbolKeyword-155] + _ = x[KindTypeKeyword-156] + _ = x[KindUndefinedKeyword-157] + _ = x[KindUniqueKeyword-158] + _ = x[KindUnknownKeyword-159] + _ = x[KindUsingKeyword-160] + _ = x[KindFromKeyword-161] + _ = x[KindGlobalKeyword-162] + _ = x[KindBigIntKeyword-163] + _ = x[KindOverrideKeyword-164] + _ = x[KindOfKeyword-165] + _ = x[KindDeferKeyword-166] + _ = x[KindQualifiedName-167] + _ = x[KindComputedPropertyName-168] + _ = x[KindTypeParameter-169] + _ = x[KindParameter-170] + _ = x[KindDecorator-171] + _ = x[KindPropertySignature-172] + _ = x[KindPropertyDeclaration-173] + _ = x[KindMethodSignature-174] + _ = x[KindMethodDeclaration-175] + _ = x[KindClassStaticBlockDeclaration-176] + _ = x[KindConstructor-177] + _ = x[KindGetAccessor-178] + _ = x[KindSetAccessor-179] + _ = x[KindCallSignature-180] + _ = x[KindConstructSignature-181] + _ = x[KindIndexSignature-182] + _ = x[KindTypePredicate-183] + _ = x[KindTypeReference-184] + _ = x[KindFunctionType-185] + _ = x[KindConstructorType-186] + _ = x[KindTypeQuery-187] + _ = x[KindTypeLiteral-188] + _ = x[KindArrayType-189] + _ = x[KindTupleType-190] + _ = x[KindOptionalType-191] + _ = x[KindRestType-192] + _ = x[KindUnionType-193] + _ = x[KindIntersectionType-194] + _ = x[KindConditionalType-195] + _ = x[KindInferType-196] + _ = x[KindParenthesizedType-197] + _ = x[KindThisType-198] + _ = x[KindTypeOperator-199] + _ = x[KindIndexedAccessType-200] + _ = x[KindMappedType-201] + _ = x[KindLiteralType-202] + _ = x[KindNamedTupleMember-203] + _ = x[KindTemplateLiteralType-204] + _ = x[KindTemplateLiteralTypeSpan-205] + _ = x[KindImportType-206] + _ = x[KindObjectBindingPattern-207] + _ = x[KindArrayBindingPattern-208] + _ = x[KindBindingElement-209] + _ = x[KindArrayLiteralExpression-210] + _ = x[KindObjectLiteralExpression-211] + _ = x[KindPropertyAccessExpression-212] + _ = x[KindElementAccessExpression-213] + _ = x[KindCallExpression-214] + _ = x[KindNewExpression-215] + _ = x[KindTaggedTemplateExpression-216] + _ = x[KindTypeAssertionExpression-217] + _ = x[KindParenthesizedExpression-218] + _ = x[KindFunctionExpression-219] + _ = x[KindArrowFunction-220] + _ = x[KindDeleteExpression-221] + _ = x[KindTypeOfExpression-222] + _ = x[KindVoidExpression-223] + _ = x[KindAwaitExpression-224] + _ = x[KindPrefixUnaryExpression-225] + _ = x[KindPostfixUnaryExpression-226] + _ = x[KindBinaryExpression-227] + _ = x[KindConditionalExpression-228] + _ = x[KindTemplateExpression-229] + _ = x[KindYieldExpression-230] + _ = x[KindSpreadElement-231] + _ = x[KindClassExpression-232] + _ = x[KindOmittedExpression-233] + _ = x[KindExpressionWithTypeArguments-234] + _ = x[KindAsExpression-235] + _ = x[KindNonNullExpression-236] + _ = x[KindMetaProperty-237] + _ = x[KindSyntheticExpression-238] + _ = x[KindSatisfiesExpression-239] + _ = x[KindTemplateSpan-240] + _ = x[KindSemicolonClassElement-241] + _ = x[KindBlock-242] + _ = x[KindEmptyStatement-243] + _ = x[KindVariableStatement-244] + _ = x[KindExpressionStatement-245] + _ = x[KindIfStatement-246] + _ = x[KindDoStatement-247] + _ = x[KindWhileStatement-248] + _ = x[KindForStatement-249] + _ = x[KindForInStatement-250] + _ = x[KindForOfStatement-251] + _ = x[KindContinueStatement-252] + _ = x[KindBreakStatement-253] + _ = x[KindReturnStatement-254] + _ = x[KindWithStatement-255] + _ = x[KindSwitchStatement-256] + _ = x[KindLabeledStatement-257] + _ = x[KindThrowStatement-258] + _ = x[KindTryStatement-259] + _ = x[KindDebuggerStatement-260] + _ = x[KindVariableDeclaration-261] + _ = x[KindVariableDeclarationList-262] + _ = x[KindFunctionDeclaration-263] + _ = x[KindClassDeclaration-264] + _ = x[KindInterfaceDeclaration-265] + _ = x[KindTypeAliasDeclaration-266] + _ = x[KindEnumDeclaration-267] + _ = x[KindModuleDeclaration-268] + _ = x[KindModuleBlock-269] + _ = x[KindCaseBlock-270] + _ = x[KindNamespaceExportDeclaration-271] + _ = x[KindImportEqualsDeclaration-272] + _ = x[KindImportDeclaration-273] + _ = x[KindImportClause-274] + _ = x[KindNamespaceImport-275] + _ = x[KindNamedImports-276] + _ = x[KindImportSpecifier-277] + _ = x[KindExportAssignment-278] + _ = x[KindExportDeclaration-279] + _ = x[KindNamedExports-280] + _ = x[KindNamespaceExport-281] + _ = x[KindExportSpecifier-282] + _ = x[KindMissingDeclaration-283] + _ = x[KindExternalModuleReference-284] + _ = x[KindJsxElement-285] + _ = x[KindJsxSelfClosingElement-286] + _ = x[KindJsxOpeningElement-287] + _ = x[KindJsxClosingElement-288] + _ = x[KindJsxFragment-289] + _ = x[KindJsxOpeningFragment-290] + _ = x[KindJsxClosingFragment-291] + _ = x[KindJsxAttribute-292] + _ = x[KindJsxAttributes-293] + _ = x[KindJsxSpreadAttribute-294] + _ = x[KindJsxExpression-295] + _ = x[KindJsxNamespacedName-296] + _ = x[KindCaseClause-297] + _ = x[KindDefaultClause-298] + _ = x[KindHeritageClause-299] + _ = x[KindCatchClause-300] + _ = x[KindImportAttributes-301] + _ = x[KindImportAttribute-302] + _ = x[KindPropertyAssignment-303] + _ = x[KindShorthandPropertyAssignment-304] + _ = x[KindSpreadAssignment-305] + _ = x[KindEnumMember-306] + _ = x[KindSourceFile-307] + _ = x[KindJSDocTypeExpression-308] + _ = x[KindJSDocNameReference-309] + _ = x[KindJSDocAllType-310] + _ = x[KindJSDocNullableType-311] + _ = x[KindJSDocNonNullableType-312] + _ = x[KindJSDocOptionalType-313] + _ = x[KindJSDocVariadicType-314] + _ = x[KindJSDoc-315] + _ = x[KindJSDocText-316] + _ = x[KindJSDocTypeLiteral-317] + _ = x[KindJSDocSignature-318] + _ = x[KindJSDocLink-319] + _ = x[KindJSDocLinkCode-320] + _ = x[KindJSDocLinkPlain-321] + _ = x[KindJSDocUnknownTag-322] + _ = x[KindJSDocAugmentsTag-323] + _ = x[KindJSDocImplementsTag-324] + _ = x[KindJSDocDeprecatedTag-325] + _ = x[KindJSDocPublicTag-326] + _ = x[KindJSDocPrivateTag-327] + _ = x[KindJSDocProtectedTag-328] + _ = x[KindJSDocReadonlyTag-329] + _ = x[KindJSDocOverrideTag-330] + _ = x[KindJSDocCallbackTag-331] + _ = x[KindJSDocOverloadTag-332] + _ = x[KindJSDocParameterTag-333] + _ = x[KindJSDocReturnTag-334] + _ = x[KindJSDocThisTag-335] + _ = x[KindJSDocTypeTag-336] + _ = x[KindJSDocTemplateTag-337] + _ = x[KindJSDocTypedefTag-338] + _ = x[KindJSDocSeeTag-339] + _ = x[KindJSDocPropertyTag-340] + _ = x[KindJSDocThrowsTag-341] + _ = x[KindJSDocSatisfiesTag-342] + _ = x[KindJSDocImportTag-343] + _ = x[KindSyntaxList-344] + _ = x[KindJSTypeAliasDeclaration-345] + _ = x[KindJSImportDeclaration-346] + _ = x[KindNotEmittedStatement-347] + _ = x[KindPartiallyEmittedExpression-348] + _ = x[KindSyntheticReferenceExpression-349] + _ = x[KindNotEmittedTypeElement-350] + _ = x[KindCount-351] +} + +const _Kind_name = "KindUnknownKindEndOfFileKindSingleLineCommentTriviaKindMultiLineCommentTriviaKindNewLineTriviaKindWhitespaceTriviaKindConflictMarkerTriviaKindNonTextFileMarkerTriviaKindNumericLiteralKindBigIntLiteralKindStringLiteralKindJsxTextKindJsxTextAllWhiteSpacesKindRegularExpressionLiteralKindNoSubstitutionTemplateLiteralKindTemplateHeadKindTemplateMiddleKindTemplateTailKindOpenBraceTokenKindCloseBraceTokenKindOpenParenTokenKindCloseParenTokenKindOpenBracketTokenKindCloseBracketTokenKindDotTokenKindDotDotDotTokenKindSemicolonTokenKindCommaTokenKindQuestionDotTokenKindLessThanTokenKindLessThanSlashTokenKindGreaterThanTokenKindLessThanEqualsTokenKindGreaterThanEqualsTokenKindEqualsEqualsTokenKindExclamationEqualsTokenKindEqualsEqualsEqualsTokenKindExclamationEqualsEqualsTokenKindEqualsGreaterThanTokenKindPlusTokenKindMinusTokenKindAsteriskTokenKindAsteriskAsteriskTokenKindSlashTokenKindPercentTokenKindPlusPlusTokenKindMinusMinusTokenKindLessThanLessThanTokenKindGreaterThanGreaterThanTokenKindGreaterThanGreaterThanGreaterThanTokenKindAmpersandTokenKindBarTokenKindCaretTokenKindExclamationTokenKindTildeTokenKindAmpersandAmpersandTokenKindBarBarTokenKindQuestionTokenKindColonTokenKindAtTokenKindQuestionQuestionTokenKindBacktickTokenKindHashTokenKindEqualsTokenKindPlusEqualsTokenKindMinusEqualsTokenKindAsteriskEqualsTokenKindAsteriskAsteriskEqualsTokenKindSlashEqualsTokenKindPercentEqualsTokenKindLessThanLessThanEqualsTokenKindGreaterThanGreaterThanEqualsTokenKindGreaterThanGreaterThanGreaterThanEqualsTokenKindAmpersandEqualsTokenKindBarEqualsTokenKindBarBarEqualsTokenKindAmpersandAmpersandEqualsTokenKindQuestionQuestionEqualsTokenKindCaretEqualsTokenKindIdentifierKindPrivateIdentifierKindJSDocCommentTextTokenKindBreakKeywordKindCaseKeywordKindCatchKeywordKindClassKeywordKindConstKeywordKindContinueKeywordKindDebuggerKeywordKindDefaultKeywordKindDeleteKeywordKindDoKeywordKindElseKeywordKindEnumKeywordKindExportKeywordKindExtendsKeywordKindFalseKeywordKindFinallyKeywordKindForKeywordKindFunctionKeywordKindIfKeywordKindImportKeywordKindInKeywordKindInstanceOfKeywordKindNewKeywordKindNullKeywordKindReturnKeywordKindSuperKeywordKindSwitchKeywordKindThisKeywordKindThrowKeywordKindTrueKeywordKindTryKeywordKindTypeOfKeywordKindVarKeywordKindVoidKeywordKindWhileKeywordKindWithKeywordKindImplementsKeywordKindInterfaceKeywordKindLetKeywordKindPackageKeywordKindPrivateKeywordKindProtectedKeywordKindPublicKeywordKindStaticKeywordKindYieldKeywordKindAbstractKeywordKindAccessorKeywordKindAsKeywordKindAssertsKeywordKindAssertKeywordKindAnyKeywordKindAsyncKeywordKindAwaitKeywordKindBooleanKeywordKindConstructorKeywordKindDeclareKeywordKindGetKeywordKindImmediateKeywordKindInferKeywordKindIntrinsicKeywordKindIsKeywordKindKeyOfKeywordKindModuleKeywordKindNamespaceKeywordKindNeverKeywordKindOutKeywordKindReadonlyKeywordKindRequireKeywordKindNumberKeywordKindObjectKeywordKindSatisfiesKeywordKindSetKeywordKindStringKeywordKindSymbolKeywordKindTypeKeywordKindUndefinedKeywordKindUniqueKeywordKindUnknownKeywordKindUsingKeywordKindFromKeywordKindGlobalKeywordKindBigIntKeywordKindOverrideKeywordKindOfKeywordKindDeferKeywordKindQualifiedNameKindComputedPropertyNameKindTypeParameterKindParameterKindDecoratorKindPropertySignatureKindPropertyDeclarationKindMethodSignatureKindMethodDeclarationKindClassStaticBlockDeclarationKindConstructorKindGetAccessorKindSetAccessorKindCallSignatureKindConstructSignatureKindIndexSignatureKindTypePredicateKindTypeReferenceKindFunctionTypeKindConstructorTypeKindTypeQueryKindTypeLiteralKindArrayTypeKindTupleTypeKindOptionalTypeKindRestTypeKindUnionTypeKindIntersectionTypeKindConditionalTypeKindInferTypeKindParenthesizedTypeKindThisTypeKindTypeOperatorKindIndexedAccessTypeKindMappedTypeKindLiteralTypeKindNamedTupleMemberKindTemplateLiteralTypeKindTemplateLiteralTypeSpanKindImportTypeKindObjectBindingPatternKindArrayBindingPatternKindBindingElementKindArrayLiteralExpressionKindObjectLiteralExpressionKindPropertyAccessExpressionKindElementAccessExpressionKindCallExpressionKindNewExpressionKindTaggedTemplateExpressionKindTypeAssertionExpressionKindParenthesizedExpressionKindFunctionExpressionKindArrowFunctionKindDeleteExpressionKindTypeOfExpressionKindVoidExpressionKindAwaitExpressionKindPrefixUnaryExpressionKindPostfixUnaryExpressionKindBinaryExpressionKindConditionalExpressionKindTemplateExpressionKindYieldExpressionKindSpreadElementKindClassExpressionKindOmittedExpressionKindExpressionWithTypeArgumentsKindAsExpressionKindNonNullExpressionKindMetaPropertyKindSyntheticExpressionKindSatisfiesExpressionKindTemplateSpanKindSemicolonClassElementKindBlockKindEmptyStatementKindVariableStatementKindExpressionStatementKindIfStatementKindDoStatementKindWhileStatementKindForStatementKindForInStatementKindForOfStatementKindContinueStatementKindBreakStatementKindReturnStatementKindWithStatementKindSwitchStatementKindLabeledStatementKindThrowStatementKindTryStatementKindDebuggerStatementKindVariableDeclarationKindVariableDeclarationListKindFunctionDeclarationKindClassDeclarationKindInterfaceDeclarationKindTypeAliasDeclarationKindEnumDeclarationKindModuleDeclarationKindModuleBlockKindCaseBlockKindNamespaceExportDeclarationKindImportEqualsDeclarationKindImportDeclarationKindImportClauseKindNamespaceImportKindNamedImportsKindImportSpecifierKindExportAssignmentKindExportDeclarationKindNamedExportsKindNamespaceExportKindExportSpecifierKindMissingDeclarationKindExternalModuleReferenceKindJsxElementKindJsxSelfClosingElementKindJsxOpeningElementKindJsxClosingElementKindJsxFragmentKindJsxOpeningFragmentKindJsxClosingFragmentKindJsxAttributeKindJsxAttributesKindJsxSpreadAttributeKindJsxExpressionKindJsxNamespacedNameKindCaseClauseKindDefaultClauseKindHeritageClauseKindCatchClauseKindImportAttributesKindImportAttributeKindPropertyAssignmentKindShorthandPropertyAssignmentKindSpreadAssignmentKindEnumMemberKindSourceFileKindJSDocTypeExpressionKindJSDocNameReferenceKindJSDocAllTypeKindJSDocNullableTypeKindJSDocNonNullableTypeKindJSDocOptionalTypeKindJSDocVariadicTypeKindJSDocKindJSDocTextKindJSDocTypeLiteralKindJSDocSignatureKindJSDocLinkKindJSDocLinkCodeKindJSDocLinkPlainKindJSDocUnknownTagKindJSDocAugmentsTagKindJSDocImplementsTagKindJSDocDeprecatedTagKindJSDocPublicTagKindJSDocPrivateTagKindJSDocProtectedTagKindJSDocReadonlyTagKindJSDocOverrideTagKindJSDocCallbackTagKindJSDocOverloadTagKindJSDocParameterTagKindJSDocReturnTagKindJSDocThisTagKindJSDocTypeTagKindJSDocTemplateTagKindJSDocTypedefTagKindJSDocSeeTagKindJSDocPropertyTagKindJSDocThrowsTagKindJSDocSatisfiesTagKindJSDocImportTagKindSyntaxListKindJSTypeAliasDeclarationKindJSImportDeclarationKindNotEmittedStatementKindPartiallyEmittedExpressionKindSyntheticReferenceExpressionKindNotEmittedTypeElementKindCount" + +var _Kind_index = [...]uint16{0, 11, 24, 51, 77, 94, 114, 138, 165, 183, 200, 217, 228, 253, 281, 314, 330, 348, 364, 382, 401, 419, 438, 458, 479, 491, 509, 527, 541, 561, 578, 600, 620, 643, 669, 690, 716, 743, 775, 801, 814, 828, 845, 870, 884, 900, 917, 936, 961, 992, 1034, 1052, 1064, 1078, 1098, 1112, 1139, 1154, 1171, 1185, 1196, 1221, 1238, 1251, 1266, 1285, 1305, 1328, 1359, 1379, 1401, 1432, 1469, 1517, 1541, 1559, 1580, 1613, 1644, 1664, 1678, 1699, 1724, 1740, 1755, 1771, 1787, 1803, 1822, 1841, 1859, 1876, 1889, 1904, 1919, 1936, 1954, 1970, 1988, 2002, 2021, 2034, 2051, 2064, 2085, 2099, 2114, 2131, 2147, 2164, 2179, 2195, 2210, 2224, 2241, 2255, 2270, 2286, 2301, 2322, 2342, 2356, 2374, 2392, 2412, 2429, 2446, 2462, 2481, 2500, 2513, 2531, 2548, 2562, 2578, 2594, 2612, 2634, 2652, 2666, 2686, 2702, 2722, 2735, 2751, 2768, 2788, 2804, 2818, 2837, 2855, 2872, 2889, 2909, 2923, 2940, 2957, 2972, 2992, 3009, 3027, 3043, 3058, 3075, 3092, 3111, 3124, 3140, 3157, 3181, 3198, 3211, 3224, 3245, 3268, 3287, 3308, 3339, 3354, 3369, 3384, 3401, 3423, 3441, 3458, 3475, 3491, 3510, 3523, 3538, 3551, 3564, 3580, 3592, 3605, 3625, 3644, 3657, 3678, 3690, 3706, 3727, 3741, 3756, 3776, 3799, 3826, 3840, 3864, 3887, 3905, 3931, 3958, 3986, 4013, 4031, 4048, 4076, 4103, 4130, 4152, 4169, 4189, 4209, 4227, 4246, 4271, 4297, 4317, 4342, 4364, 4383, 4400, 4419, 4440, 4471, 4487, 4508, 4524, 4547, 4570, 4586, 4611, 4620, 4638, 4659, 4682, 4697, 4712, 4730, 4746, 4764, 4782, 4803, 4821, 4840, 4857, 4876, 4896, 4914, 4930, 4951, 4974, 5001, 5024, 5044, 5068, 5092, 5111, 5132, 5147, 5160, 5190, 5217, 5238, 5254, 5273, 5289, 5308, 5328, 5349, 5365, 5384, 5403, 5425, 5452, 5466, 5491, 5512, 5533, 5548, 5570, 5592, 5608, 5625, 5647, 5664, 5685, 5699, 5716, 5734, 5749, 5769, 5788, 5810, 5841, 5861, 5875, 5889, 5912, 5934, 5950, 5971, 5995, 6016, 6037, 6046, 6059, 6079, 6097, 6110, 6127, 6145, 6164, 6184, 6206, 6228, 6246, 6265, 6286, 6306, 6326, 6346, 6366, 6387, 6405, 6421, 6437, 6457, 6476, 6491, 6511, 6529, 6550, 6568, 6582, 6608, 6631, 6654, 6684, 6716, 6741, 6750} + +func (i Kind) String() string { + idx := int(i) - 0 + if i < 0 || idx >= len(_Kind_index)-1 { + return "Kind(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _Kind_name[_Kind_index[idx]:_Kind_index[idx+1]] +} diff --git a/tools/tsgo/internal/ast/modifierflags.go b/tools/tsgo/internal/ast/modifierflags.go new file mode 100644 index 00000000..dfc4c21c --- /dev/null +++ b/tools/tsgo/internal/ast/modifierflags.go @@ -0,0 +1,53 @@ +package ast + +type ModifierFlags uint32 + +const ( + ModifierFlagsNone ModifierFlags = 0 + // Syntactic/JSDoc modifiers + ModifierFlagsPublic ModifierFlags = 1 << 0 // Property/Method + ModifierFlagsPrivate ModifierFlags = 1 << 1 // Property/Method + ModifierFlagsProtected ModifierFlags = 1 << 2 // Property/Method + ModifierFlagsReadonly ModifierFlags = 1 << 3 // Property/Method + ModifierFlagsOverride ModifierFlags = 1 << 4 // Override method + // Syntactic-only modifiers + ModifierFlagsExport ModifierFlags = 1 << 5 // Declarations + ModifierFlagsAbstract ModifierFlags = 1 << 6 // Class/Method/ConstructSignature + ModifierFlagsAmbient ModifierFlags = 1 << 7 // Declarations (declare keyword) + ModifierFlagsStatic ModifierFlags = 1 << 8 // Property/Method + ModifierFlagsAccessor ModifierFlags = 1 << 9 // Property + ModifierFlagsAsync ModifierFlags = 1 << 10 // Property/Method/Function + ModifierFlagsDefault ModifierFlags = 1 << 11 // Function/Class (export default declaration) + ModifierFlagsConst ModifierFlags = 1 << 12 // Const enum + ModifierFlagsIn ModifierFlags = 1 << 13 // Contravariance modifier + ModifierFlagsOut ModifierFlags = 1 << 14 // Covariance modifier + ModifierFlagsDecorator ModifierFlags = 1 << 15 // Contains a decorator + // JSDoc-only modifiers + ModifierFlagsDeprecated ModifierFlags = 1 << 16 // Deprecated tag + // Cache-only JSDoc-modifiers. Should match order of Syntactic/JSDoc modifiers, above. + ModifierFlagsJSDocPublic ModifierFlags = 1 << 23 // if this value changes, `selectEffectiveModifierFlags` must change accordingly + ModifierFlagsJSDocPrivate ModifierFlags = 1 << 24 + ModifierFlagsJSDocProtected ModifierFlags = 1 << 25 + ModifierFlagsJSDocReadonly ModifierFlags = 1 << 26 + ModifierFlagsJSDocOverride ModifierFlags = 1 << 27 + ModifierFlagsHasComputedJSDocModifiers ModifierFlags = 1 << 28 // Indicates the computed modifier flags include modifiers from JSDoc. + ModifierFlagsHasComputedFlags ModifierFlags = 1 << 29 // Modifier flags have been computed + + ModifierFlagsSyntacticOrJSDocModifiers = ModifierFlagsPublic | ModifierFlagsPrivate | ModifierFlagsProtected | ModifierFlagsReadonly | ModifierFlagsOverride + ModifierFlagsSyntacticOnlyModifiers = ModifierFlagsExport | ModifierFlagsAmbient | ModifierFlagsAbstract | ModifierFlagsStatic | ModifierFlagsAccessor | ModifierFlagsAsync | ModifierFlagsDefault | ModifierFlagsConst | ModifierFlagsIn | ModifierFlagsOut | ModifierFlagsDecorator + ModifierFlagsSyntacticModifiers = ModifierFlagsSyntacticOrJSDocModifiers | ModifierFlagsSyntacticOnlyModifiers + ModifierFlagsJSDocCacheOnlyModifiers = ModifierFlagsJSDocPublic | ModifierFlagsJSDocPrivate | ModifierFlagsJSDocProtected | ModifierFlagsJSDocReadonly | ModifierFlagsJSDocOverride + ModifierFlagsJSDocOnlyModifiers = ModifierFlagsDeprecated + ModifierFlagsNonCacheOnlyModifiers = ModifierFlagsSyntacticOrJSDocModifiers | ModifierFlagsSyntacticOnlyModifiers | ModifierFlagsJSDocOnlyModifiers + + ModifierFlagsAccessibilityModifier = ModifierFlagsPublic | ModifierFlagsPrivate | ModifierFlagsProtected + // Accessibility modifiers and 'readonly' can be attached to a parameter in a constructor to make it a property. + ModifierFlagsParameterPropertyModifier = ModifierFlagsAccessibilityModifier | ModifierFlagsReadonly | ModifierFlagsOverride + ModifierFlagsNonPublicAccessibilityModifier = ModifierFlagsPrivate | ModifierFlagsProtected + + ModifierFlagsTypeScriptModifier = ModifierFlagsAmbient | ModifierFlagsPublic | ModifierFlagsPrivate | ModifierFlagsProtected | ModifierFlagsReadonly | ModifierFlagsAbstract | ModifierFlagsConst | ModifierFlagsOverride | ModifierFlagsIn | ModifierFlagsOut + ModifierFlagsExportDefault = ModifierFlagsExport | ModifierFlagsDefault + ModifierFlagsAll = ModifierFlagsExport | ModifierFlagsAmbient | ModifierFlagsPublic | ModifierFlagsPrivate | ModifierFlagsProtected | ModifierFlagsStatic | ModifierFlagsReadonly | ModifierFlagsAbstract | ModifierFlagsAccessor | ModifierFlagsAsync | ModifierFlagsDefault | ModifierFlagsConst | ModifierFlagsDeprecated | ModifierFlagsOverride | ModifierFlagsIn | ModifierFlagsOut | ModifierFlagsDecorator + ModifierFlagsModifier = ModifierFlagsAll & ^ModifierFlagsDecorator + ModifierFlagsJavaScript = ModifierFlagsExport | ModifierFlagsStatic | ModifierFlagsAccessor | ModifierFlagsAsync | ModifierFlagsDefault +) diff --git a/tools/tsgo/internal/ast/nodeflags.go b/tools/tsgo/internal/ast/nodeflags.go new file mode 100644 index 00000000..2c808601 --- /dev/null +++ b/tools/tsgo/internal/ast/nodeflags.go @@ -0,0 +1,73 @@ +package ast + +type NodeFlags uint32 + +const ( + NodeFlagsNone NodeFlags = 0 + NodeFlagsLet NodeFlags = 1 << 0 // Variable declaration + NodeFlagsConst NodeFlags = 1 << 1 // Variable declaration + NodeFlagsUsing NodeFlags = 1 << 2 // Variable declaration + NodeFlagsReparsed NodeFlags = 1 << 3 // Node was synthesized during parsing + NodeFlagsSynthesized NodeFlags = 1 << 4 // Node was synthesized during transformation + NodeFlagsOptionalChain NodeFlags = 1 << 5 // Chained MemberExpression rooted to a pseudo-OptionalExpression + NodeFlagsExportContext NodeFlags = 1 << 6 // Export context (initialized by binding) + NodeFlagsContainsThis NodeFlags = 1 << 7 // Interface contains references to "this" + NodeFlagsHasImplicitReturn NodeFlags = 1 << 8 // If function implicitly returns on one of codepaths (initialized by binding) + NodeFlagsHasExplicitReturn NodeFlags = 1 << 9 // If function has explicit reachable return on one of codepaths (initialized by binding) + NodeFlagsDisallowInContext NodeFlags = 1 << 10 // If node was parsed in a context where 'in-expressions' are not allowed + NodeFlagsYieldContext NodeFlags = 1 << 11 // If node was parsed in the 'yield' context created when parsing a generator + NodeFlagsDecoratorContext NodeFlags = 1 << 12 // If node was parsed as part of a decorator + NodeFlagsAwaitContext NodeFlags = 1 << 13 // If node was parsed in the 'await' context created when parsing an async function + NodeFlagsDisallowConditionalTypesContext NodeFlags = 1 << 14 // If node was parsed in a context where conditional types are not allowed + NodeFlagsThisNodeHasError NodeFlags = 1 << 15 // If the parser encountered an error when parsing the code that created this node + NodeFlagsJavaScriptFile NodeFlags = 1 << 16 // If node was parsed in a JavaScript + NodeFlagsThisNodeOrAnySubNodesHasError NodeFlags = 1 << 17 // If this node or any of its children had an error + NodeFlagsHasAsyncFunctions NodeFlags = 1 << 18 // If the file has async functions (initialized by binding) + // NodeFlagsHasAggregatedChildData is deprecated. Use `subtreeFacts` instead. + + // These flags will be set when the parser encounters a dynamic import expression or 'import.meta' to avoid + // walking the tree if the flags are not set. However, these flags are just a approximation + // (hence why it's named "PossiblyContainsDynamicImport") because once set, the flags never get cleared. + // During editing, if a dynamic import is removed, incremental parsing will *NOT* clear this flag. + // This means that the tree will always be traversed during module resolution, or when looking for external module indicators. + // However, the removal operation should not occur often and in the case of the + // removal, it is likely that users will add the import anyway. + // The advantage of this approach is its simplicity. For the case of batch compilation, + // we guarantee that users won't have to pay the price of walking the tree if a dynamic import isn't used. + NodeFlagsPossiblyContainsDynamicImport NodeFlags = 1 << 19 + NodeFlagsPossiblyContainsImportMeta NodeFlags = 1 << 20 + + NodeFlagsHasJSDoc NodeFlags = 1 << 21 // If node has preceding JSDoc comment(s) + NodeFlagsJSDoc NodeFlags = 1 << 22 // If node was parsed inside jsdoc + NodeFlagsAmbient NodeFlags = 1 << 23 // If node was inside an ambient context -- a declaration file, or inside something with the `declare` modifier. + NodeFlagsInWithStatement NodeFlags = 1 << 24 // If any ancestor of node was the `statement` of a WithStatement (not the `expression`) + NodeFlagsJsonFile NodeFlags = 1 << 25 // If node was parsed in a Json + NodeFlagsPossiblyContainsDeprecatedTag NodeFlags = 1 << 26 // Set during parse if comment text contains '@deprecated'; must confirm via JSDoc lookup + NodeFlagsUnreachable NodeFlags = 1 << 27 // If node is unreachable according to the binder + NodeFlagsReparserTransformedLiteral NodeFlags = 1 << 28 // If node was transformed during parsing, making its' naive text source not match the AST + + NodeFlagsBlockScoped = NodeFlagsLet | NodeFlagsConst | NodeFlagsUsing + NodeFlagsConstant = NodeFlagsConst | NodeFlagsUsing + NodeFlagsAwaitUsing = NodeFlagsConst | NodeFlagsUsing // Variable declaration (NOTE: on a single node these flags would otherwise be mutually exclusive) + + NodeFlagsReachabilityCheckFlags = NodeFlagsHasImplicitReturn | NodeFlagsHasExplicitReturn + NodeFlagsReachabilityAndEmitFlags = NodeFlagsReachabilityCheckFlags | NodeFlagsHasAsyncFunctions + + // Parsing context flags + NodeFlagsContextFlags NodeFlags = NodeFlagsDisallowInContext | NodeFlagsDisallowConditionalTypesContext | NodeFlagsYieldContext | NodeFlagsDecoratorContext | NodeFlagsAwaitContext | NodeFlagsJavaScriptFile | NodeFlagsInWithStatement | NodeFlagsAmbient + + // Exclude these flags when parsing a Type + NodeFlagsTypeExcludesFlags NodeFlags = NodeFlagsYieldContext | NodeFlagsAwaitContext + + // Represents all flags that are potentially set once and + // never cleared on SourceFiles which get re-used in between incremental parses. + // See the comment above on `PossiblyContainsDynamicImport` and `PossiblyContainsImportMeta`. + NodeFlagsPermanentlySetIncrementalFlags NodeFlags = NodeFlagsPossiblyContainsDynamicImport | NodeFlagsPossiblyContainsImportMeta + + // The following flags repurpose other NodeFlags as different meanings for Identifier nodes + NodeFlagsIdentifierHasExtendedUnicodeEscape NodeFlags = NodeFlagsContainsThis // Indicates whether the identifier contains an extended unicode escape sequence + NodeFlagsIdentifierIsInJSDocNamespace NodeFlags = NodeFlagsHasAsyncFunctions // Indicates the identifier is the innermost name of a JSDoc namespace declaration + + // The following flag repurposes other NodeFlags for ModuleDeclaration nodes + NodeFlagsNestedNamespace NodeFlags = NodeFlagsOptionalChain // If ModuleDeclaration is a nested namespace (e.g. inner part of A.B.C) +) diff --git a/tools/tsgo/internal/ast/parseoptions.go b/tools/tsgo/internal/ast/parseoptions.go new file mode 100644 index 00000000..3d27ddab --- /dev/null +++ b/tools/tsgo/internal/ast/parseoptions.go @@ -0,0 +1,149 @@ +package ast + +import ( + "github.com/microsoft/typescript-go/internal/core" + "github.com/microsoft/typescript-go/internal/tspath" +) + +type SourceFileParseOptions struct { + FileName string + Path tspath.Path + ExternalModuleIndicatorOptions ExternalModuleIndicatorOptions +} + +type ExternalModuleIndicatorOptions struct { + JSX bool + Force bool +} + +func GetExternalModuleIndicatorOptions(fileName string, options *core.CompilerOptions, metadata SourceFileMetaData) ExternalModuleIndicatorOptions { + if tspath.IsDeclarationFileName(fileName) { + return ExternalModuleIndicatorOptions{} + } + + switch options.GetEmitModuleDetectionKind() { + case core.ModuleDetectionKindForce: + // All non-declaration files are modules, declaration files still do the usual isFileProbablyExternalModule + return ExternalModuleIndicatorOptions{Force: true} + case core.ModuleDetectionKindLegacy: + // Files are modules if they have imports, exports, or import.meta + return ExternalModuleIndicatorOptions{} + case core.ModuleDetectionKindAuto: + // If module is nodenext or node16, all esm format files are modules + // If jsx is react-jsx or react-jsxdev then jsx tags force module-ness + // otherwise, the presence of import or export statments (or import.meta) implies module-ness + return ExternalModuleIndicatorOptions{ + JSX: options.Jsx == core.JsxEmitReactJSX || options.Jsx == core.JsxEmitReactJSXDev, + Force: isFileForcedToBeModuleByFormat(fileName, options, metadata), + } + default: + return ExternalModuleIndicatorOptions{} + } +} + +var isFileForcedToBeModuleByFormatExtensions = []string{tspath.ExtensionCjs, tspath.ExtensionCts, tspath.ExtensionMjs, tspath.ExtensionMts} + +func isFileForcedToBeModuleByFormat(fileName string, options *core.CompilerOptions, metadata SourceFileMetaData) bool { + // Excludes declaration files - they still require an explicit `export {}` or the like + // for back compat purposes. The only non-declaration files _not_ forced to be a module are `.js` files + // that aren't esm-mode (meaning not in a `type: module` scope). + if GetImpliedNodeFormatForEmitWorker(fileName, options.GetEmitModuleKind(), metadata) == core.ModuleKindESNext || tspath.FileExtensionIsOneOf(fileName, isFileForcedToBeModuleByFormatExtensions) { + return true + } + return false +} + +func SetExternalModuleIndicator(file *SourceFile, opts ExternalModuleIndicatorOptions) { + file.ExternalModuleIndicator = getExternalModuleIndicator(file, opts) +} + +func getExternalModuleIndicator(file *SourceFile, opts ExternalModuleIndicatorOptions) *Node { + if file.ScriptKind == core.ScriptKindJSON { + return nil + } + + if node := isFileProbablyExternalModule(file); node != nil { + return node + } + + if file.IsDeclarationFile { + return nil + } + + if opts.JSX { + if node := isFileModuleFromUsingJSXTag(file); node != nil { + return node + } + } + + if opts.Force { + return file.AsNode() + } + + return nil +} + +func isFileProbablyExternalModule(sourceFile *SourceFile) *Node { + for _, statement := range sourceFile.Statements.Nodes { + if isAnExternalModuleIndicatorNode(statement) { + return statement + } + } + return getImportMetaIfNecessary(sourceFile) +} + +func isAnExternalModuleIndicatorNode(node *Node) bool { + return HasSyntacticModifier(node, ModifierFlagsExport) || + IsImportEqualsDeclaration(node) && IsExternalModuleReference(node.AsImportEqualsDeclaration().ModuleReference) || + IsImportDeclaration(node) || IsExportAssignment(node) || IsExportDeclaration(node) +} + +func getImportMetaIfNecessary(sourceFile *SourceFile) *Node { + if sourceFile.AsNode().Flags&NodeFlagsPossiblyContainsImportMeta != 0 { + return findChildNode(sourceFile.AsNode(), IsImportMeta) + } + return nil +} + +func findChildNode(root *Node, check func(*Node) bool) *Node { + var result *Node + var visit func(*Node) bool + visit = func(node *Node) bool { + if check(node) { + result = node + return true + } + return node.ForEachChild(visit) + } + visit(root) + return result +} + +func isFileModuleFromUsingJSXTag(file *SourceFile) *Node { + return walkTreeForJSXTags(file.AsNode()) +} + +// This is a somewhat unavoidable full tree walk to locate a JSX tag - `import.meta` requires the same, +// but we avoid that walk (or parts of it) if at all possible using the `PossiblyContainsImportMeta` node flag. +// Unfortunately, there's no `NodeFlag` space to do the same for JSX. +func walkTreeForJSXTags(node *Node) *Node { + var found *Node + + var visitor func(node *Node) bool + visitor = func(node *Node) bool { + if found != nil { + return true + } + if node.SubtreeFacts()&SubtreeContainsJsx == 0 { + return false + } + if IsJsxOpeningLikeElement(node) || IsJsxFragment(node) { + found = node + return true + } + return node.ForEachChild(visitor) + } + visitor(node) + + return found +} diff --git a/tools/tsgo/internal/ast/positionmap.go b/tools/tsgo/internal/ast/positionmap.go new file mode 100644 index 00000000..a7e6e807 --- /dev/null +++ b/tools/tsgo/internal/ast/positionmap.go @@ -0,0 +1,111 @@ +package ast + +import ( + "unicode/utf8" + + "github.com/microsoft/typescript-go/internal/stringutil" +) + +// PositionMap provides bidirectional mapping between UTF-8 byte offsets (used by Go) +// and UTF-16 code unit offsets (used by JavaScript/TypeScript). +// +// For ASCII-only text, the two are identical. For text containing non-ASCII characters, +// the offsets diverge because multi-byte UTF-8 sequences map to different numbers of +// UTF-16 code units: +// - U+0000..U+007F: 1 byte in UTF-8, 1 code unit in UTF-16 +// - U+0080..U+07FF: 2 bytes in UTF-8, 1 code unit in UTF-16 +// - U+0800..U+FFFF: 3 bytes in UTF-8, 1 code unit in UTF-16 +// - U+10000..U+10FFFF: 4 bytes in UTF-8, 2 code units in UTF-16 (surrogate pair) +type PositionMap struct { + // asciiOnly is true if the text contains only ASCII characters, + // meaning UTF-8 byte offsets and UTF-16 code unit offsets are identical. + asciiOnly bool + // For each multi-byte character, we store: + // - the UTF-8 byte offset of the character + // - the cumulative delta (utf8Offset - utf16Offset) at that character + // This allows O(log n) conversion in either direction. + // + // entries[i].utf8Pos is the byte offset of the i-th multi-byte character. + // entries[i].delta is the total (utf8 - utf16) difference accumulated + // through and including the i-th multi-byte character. + entries []positionMapEntry +} + +type positionMapEntry struct { + utf8Pos int // UTF-8 byte offset AFTER this multi-byte character + delta int // cumulative (utf8 - utf16) offset difference after this character +} + +// ComputePositionMap builds a PositionMap for the given text. +func ComputePositionMap(text string) *PositionMap { + pm := &PositionMap{} + delta := 0 + for i := 0; i < len(text); { + b := text[i] + if b < utf8.RuneSelf { + i++ + continue + } + r, size := stringutil.DecodeJSStringRune(text[i:]) + utf16Size := 1 + if r >= 0x10000 { + utf16Size = 2 + } + delta += size - utf16Size + pm.entries = append(pm.entries, positionMapEntry{utf8Pos: i + size, delta: delta}) + i += size + } + pm.asciiOnly = len(pm.entries) == 0 + return pm +} + +// IsAsciiOnly returns true if the text is ASCII-only, +// meaning UTF-8 and UTF-16 offsets are identical. +func (pm *PositionMap) IsAsciiOnly() bool { + return pm.asciiOnly +} + +// UTF8ToUTF16 converts a UTF-8 byte offset to a UTF-16 code unit offset. +func (pm *PositionMap) UTF8ToUTF16(utf8Offset int) int { + if pm.asciiOnly { + return utf8Offset + } + // Binary search: find the last entry where utf8Pos <= utf8Offset + lo, hi := 0, len(pm.entries) + for lo < hi { + mid := lo + (hi-lo)/2 + if pm.entries[mid].utf8Pos <= utf8Offset { + lo = mid + 1 + } else { + hi = mid + } + } + if lo == 0 { + // Before any multi-byte character + return utf8Offset + } + return utf8Offset - pm.entries[lo-1].delta +} + +// UTF16ToUTF8 converts a UTF-16 code unit offset to a UTF-8 byte offset. +func (pm *PositionMap) UTF16ToUTF8(utf16Offset int) int { + if pm.asciiOnly { + return utf16Offset + } + // We need the last entry where (utf8Pos - delta) <= utf16Offset. + // (utf8Pos - delta) is the UTF-16 offset of that entry's character. + lo, hi := 0, len(pm.entries) + for lo < hi { + mid := lo + (hi-lo)/2 + utf16Pos := pm.entries[mid].utf8Pos - pm.entries[mid].delta + if utf16Pos <= utf16Offset { + lo = mid + 1 + } else { + hi = mid + } + } + if lo == 0 { + return utf16Offset + } + return utf16Offset + pm.entries[lo-1].delta +} diff --git a/tools/tsgo/internal/ast/positionmap_test.go b/tools/tsgo/internal/ast/positionmap_test.go new file mode 100644 index 00000000..f496f367 --- /dev/null +++ b/tools/tsgo/internal/ast/positionmap_test.go @@ -0,0 +1,225 @@ +package ast_test + +import ( + "os" + "strings" + "testing" + + "github.com/microsoft/typescript-go/internal/ast" + "github.com/microsoft/typescript-go/internal/stringutil" +) + +func TestPositionMapASCII(t *testing.T) { + t.Parallel() + text := "const x = 1;" + pm := ast.ComputePositionMap(text) + if !pm.IsAsciiOnly() { + t.Fatal("expected ASCII-only") + } + for i := 0; i <= len(text); i++ { + if got := pm.UTF8ToUTF16(i); got != i { + t.Errorf("UTF8ToUTF16(%d) = %d, want %d", i, got, i) + } + if got := pm.UTF16ToUTF8(i); got != i { + t.Errorf("UTF16ToUTF8(%d) = %d, want %d", i, got, i) + } + } +} + +func TestPositionMapTwoByte(t *testing.T) { + t.Parallel() + // "café" — é (U+00E9) is 2 bytes UTF-8, 1 code unit UTF-16 + text := "const café = 1;\nconst x = 2;" + pm := ast.ComputePositionMap(text) + if pm.IsAsciiOnly() { + t.Fatal("expected non-ASCII") + } + + // Everything before é (byte offset 9) should be identity + for i := range 10 { + if got := pm.UTF8ToUTF16(i); got != i { + t.Errorf("before é: UTF8ToUTF16(%d) = %d, want %d", i, got, i) + } + } + + // é starts at UTF-8 byte 9, UTF-16 offset 9: same + if got := pm.UTF8ToUTF16(9); got != 9 { + t.Errorf("at é: UTF8ToUTF16(9) = %d, want 9", got) + } + + // After é (byte 11 in UTF-8 = code unit 10 in UTF-16), delta is 1 + // ' ' after café: UTF-8 byte 11, UTF-16 offset 10 + if got := pm.UTF8ToUTF16(11); got != 10 { + t.Errorf("after é: UTF8ToUTF16(11) = %d, want 10", got) + } + + // 'x' on second line: UTF-8 byte 23, UTF-16 offset 22 + xUTF8 := strings.LastIndex(text, "x") + if got := pm.UTF8ToUTF16(xUTF8); got != xUTF8-1 { + t.Errorf("at x: UTF8ToUTF16(%d) = %d, want %d", xUTF8, got, xUTF8-1) + } + + // Reverse: UTF-16 offset 22 should map to UTF-8 byte 23 + xUTF16 := xUTF8 - 1 + if got := pm.UTF16ToUTF8(xUTF16); got != xUTF8 { + t.Errorf("reverse at x: UTF16ToUTF8(%d) = %d, want %d", xUTF16, got, xUTF8) + } +} + +func TestPositionMapFourByte(t *testing.T) { + t.Parallel() + // 🎉 (U+1F389) is 4 bytes UTF-8, 2 code units UTF-16 + text := `const a = "🎉";` + "\nconst b = 2;" + pm := ast.ComputePositionMap(text) + if pm.IsAsciiOnly() { + t.Fatal("expected non-ASCII") + } + + // 🎉 starts at byte 11 (after `const a = "`) + // UTF-8: bytes 11-14 (4 bytes), UTF-16: units 11-12 (2 code units) + // After 🎉: UTF-8 byte 15, UTF-16 offset 13. Delta = 2. + + // 'b' on second line + bUTF8 := strings.LastIndex(text, "b") + bUTF16 := bUTF8 - 2 // delta of 2 from emoji + if got := pm.UTF8ToUTF16(bUTF8); got != bUTF16 { + t.Errorf("at b: UTF8ToUTF16(%d) = %d, want %d", bUTF8, got, bUTF16) + } + if got := pm.UTF16ToUTF8(bUTF16); got != bUTF8 { + t.Errorf("reverse at b: UTF16ToUTF8(%d) = %d, want %d", bUTF16, got, bUTF8) + } +} + +func TestPositionMapMultipleNonASCII(t *testing.T) { + t.Parallel() + // Mix of 2-byte and 4-byte characters + // "à" (U+00E0) = 2 bytes UTF-8, 1 code unit UTF-16 (delta +1) + // "🎉" (U+1F389) = 4 bytes UTF-8, 2 code units UTF-16 (delta +2) + text := "à🎉x" + pm := ast.ComputePositionMap(text) + + // à: UTF-8 [0,2), UTF-16 [0,1) + // 🎉: UTF-8 [2,6), UTF-16 [1,3) + // x: UTF-8 [6,7), UTF-16 [3,4) + tests := []struct { + utf8 int + utf16 int + }{ + {0, 0}, + {2, 1}, // start of 🎉 + {6, 3}, // x + {7, 4}, // end + } + for _, tt := range tests { + if got := pm.UTF8ToUTF16(tt.utf8); got != tt.utf16 { + t.Errorf("UTF8ToUTF16(%d) = %d, want %d", tt.utf8, got, tt.utf16) + } + if got := pm.UTF16ToUTF8(tt.utf16); got != tt.utf8 { + t.Errorf("UTF16ToUTF8(%d) = %d, want %d", tt.utf16, got, tt.utf8) + } + } +} + +func TestPositionMapLoneSurrogateSentinel(t *testing.T) { + t.Parallel() + text := "a" + stringutil.EncodeJSStringRune(0xD800) + "b" + pm := ast.ComputePositionMap(text) + if pm.IsAsciiOnly() { + t.Fatal("expected non-ASCII") + } + + if got := pm.UTF8ToUTF16(len(text)); got != 3 { + t.Errorf("UTF8ToUTF16(%d) = %d, want 3", len(text), got) + } + if got := pm.UTF16ToUTF8(2); got != len(text)-1 { + t.Errorf("UTF16ToUTF8(2) = %d, want %d", got, len(text)-1) + } +} + +func TestPositionMapRoundtrip(t *testing.T) { + t.Parallel() + text := "let café = \"🎉\"; // naïve" + pm := ast.ComputePositionMap(text) + + // Convert every valid UTF-16 position to UTF-8 and back + utf16Len := pm.UTF8ToUTF16(len(text)) + for i := 0; i <= utf16Len; i++ { + utf8Pos := pm.UTF16ToUTF8(i) + back := pm.UTF8ToUTF16(utf8Pos) + if back != i { + t.Errorf("roundtrip UTF16->UTF8->UTF16: %d -> %d -> %d", i, utf8Pos, back) + } + } +} + +func BenchmarkComputePositionMap_ASCII(b *testing.B) { + // ~10KB of ASCII TypeScript-like code + line := "const variable = someFunction(argument1, argument2);\n" + text := strings.Repeat(line, 200) + b.ResetTimer() + for range b.N { + ast.ComputePositionMap(text) + } +} + +func BenchmarkComputePositionMap_NonASCII(b *testing.B) { + // Mix of ASCII and non-ASCII (comments with unicode) + line := "const café = \"héllo wörld 🎉\";\n" + text := strings.Repeat(line, 200) + b.ResetTimer() + for range b.N { + ast.ComputePositionMap(text) + } +} + +func BenchmarkUTF8ToUTF16_ASCII(b *testing.B) { + line := "const variable = someFunction(argument1, argument2);\n" + text := strings.Repeat(line, 200) + pm := ast.ComputePositionMap(text) + positions := []int{0, 100, 500, 1000, 5000, len(text) - 1} + b.ResetTimer() + for range b.N { + for _, p := range positions { + pm.UTF8ToUTF16(p) + } + } +} + +func BenchmarkUTF8ToUTF16_NonASCII(b *testing.B) { + line := "const café = \"héllo wörld 🎉\";\n" + text := strings.Repeat(line, 200) + pm := ast.ComputePositionMap(text) + positions := []int{0, 100, 500, 1000, 5000, len(text) - 1} + b.ResetTimer() + for range b.N { + for _, p := range positions { + pm.UTF8ToUTF16(p) + } + } +} + +func BenchmarkUTF16ToUTF8_NonASCII(b *testing.B) { + line := "const café = \"héllo wörld 🎉\";\n" + text := strings.Repeat(line, 200) + pm := ast.ComputePositionMap(text) + utf16Len := pm.UTF8ToUTF16(len(text)) + positions := []int{0, 100, 500, 1000, 3000, utf16Len - 1} + b.ResetTimer() + for range b.N { + for _, p := range positions { + pm.UTF16ToUTF8(p) + } + } +} + +func BenchmarkComputePositionMap_CheckerTS(b *testing.B) { + data, err := os.ReadFile("../../_submodules/TypeScript/src/compiler/checker.ts") + if err != nil { + b.Skip("checker.ts not available:", err) + } + text := string(data) + b.ResetTimer() + for range b.N { + ast.ComputePositionMap(text) + } +} diff --git a/tools/tsgo/internal/ast/precedence.go b/tools/tsgo/internal/ast/precedence.go new file mode 100644 index 00000000..bfaaa679 --- /dev/null +++ b/tools/tsgo/internal/ast/precedence.go @@ -0,0 +1,717 @@ +package ast + +import ( + "fmt" +) + +type OperatorPrecedence int + +const ( + // Expression: + // AssignmentExpression + // Expression `,` AssignmentExpression + OperatorPrecedenceComma OperatorPrecedence = iota + // NOTE: `Spread` is higher than `Comma` due to how it is parsed in |ElementList| + // SpreadElement: + // `...` AssignmentExpression + OperatorPrecedenceSpread + // AssignmentExpression: + // ConditionalExpression + // YieldExpression + // ArrowFunction + // AsyncArrowFunction + // LeftHandSideExpression `=` AssignmentExpression + // LeftHandSideExpression AssignmentOperator AssignmentExpression + // + // NOTE: AssignmentExpression is broken down into several precedences due to the requirements + // of the parenthesizer rules. + // AssignmentExpression: YieldExpression + // YieldExpression: + // `yield` + // `yield` AssignmentExpression + // `yield` `*` AssignmentExpression + OperatorPrecedenceYield + // AssignmentExpression: LeftHandSideExpression `=` AssignmentExpression + // AssignmentExpression: LeftHandSideExpression AssignmentOperator AssignmentExpression + // AssignmentOperator: one of + // `*=` `/=` `%=` `+=` `-=` `<<=` `>>=` `>>>=` `&=` `^=` `|=` `**=` + OperatorPrecedenceAssignment + // NOTE: `Conditional` is considered higher than `Assignment` here, but in reality they have + // the same precedence. + // AssignmentExpression: ConditionalExpression + // ConditionalExpression: + // ShortCircuitExpression + // ShortCircuitExpression `?` AssignmentExpression `:` AssignmentExpression + OperatorPrecedenceConditional + // LogicalORExpression: + // LogicalANDExpression + // LogicalORExpression `||` LogicalANDExpression + OperatorPrecedenceLogicalOR + // LogicalANDExpression: + // BitwiseORExpression + // LogicalANDExprerssion `&&` BitwiseORExpression + OperatorPrecedenceLogicalAND + // BitwiseORExpression: + // BitwiseXORExpression + // BitwiseORExpression `|` BitwiseXORExpression + OperatorPrecedenceBitwiseOR + // BitwiseXORExpression: + // BitwiseANDExpression + // BitwiseXORExpression `^` BitwiseANDExpression + OperatorPrecedenceBitwiseXOR + // BitwiseANDExpression: + // EqualityExpression + // BitwiseANDExpression `&` EqualityExpression + OperatorPrecedenceBitwiseAND + // EqualityExpression: + // RelationalExpression + // EqualityExpression `==` RelationalExpression + // EqualityExpression `!=` RelationalExpression + // EqualityExpression `===` RelationalExpression + // EqualityExpression `!==` RelationalExpression + OperatorPrecedenceEquality + // RelationalExpression: + // ShiftExpression + // RelationalExpression `<` ShiftExpression + // RelationalExpression `>` ShiftExpression + // RelationalExpression `<=` ShiftExpression + // RelationalExpression `>=` ShiftExpression + // RelationalExpression `instanceof` ShiftExpression + // RelationalExpression `in` ShiftExpression + // [+TypeScript] RelationalExpression `as` Type + OperatorPrecedenceRelational + // ShiftExpression: + // AdditiveExpression + // ShiftExpression `<<` AdditiveExpression + // ShiftExpression `>>` AdditiveExpression + // ShiftExpression `>>>` AdditiveExpression + OperatorPrecedenceShift + // AdditiveExpression: + // MultiplicativeExpression + // AdditiveExpression `+` MultiplicativeExpression + // AdditiveExpression `-` MultiplicativeExpression + OperatorPrecedenceAdditive + // MultiplicativeExpression: + // ExponentiationExpression + // MultiplicativeExpression MultiplicativeOperator ExponentiationExpression + // MultiplicativeOperator: one of `*`, `/`, `%` + OperatorPrecedenceMultiplicative + // ExponentiationExpression: + // UnaryExpression + // UpdateExpression `**` ExponentiationExpression + OperatorPrecedenceExponentiation + // UnaryExpression: + // UpdateExpression + // `delete` UnaryExpression + // `void` UnaryExpression + // `typeof` UnaryExpression + // `+` UnaryExpression + // `-` UnaryExpression + // `~` UnaryExpression + // `!` UnaryExpression + // AwaitExpression + // UpdateExpression: // TODO: Do we need to investigate the precedence here? + // `++` UnaryExpression + // `--` UnaryExpression + OperatorPrecedenceUnary + // UpdateExpression: + // LeftHandSideExpression + // LeftHandSideExpression `++` + // LeftHandSideExpression `--` + OperatorPrecedenceUpdate + // LeftHandSideExpression: + // NewExpression + // NewExpression: + // MemberExpression + // `new` NewExpression + OperatorPrecedenceLeftHandSide + // LeftHandSideExpression: + // OptionalExpression + // OptionalExpression: + // MemberExpression OptionalChain + // CallExpression OptionalChain + // OptionalExpression OptionalChain + OperatorPrecedenceOptionalChain + // LeftHandSideExpression: + // CallExpression + // CallExpression: + // CoverCallExpressionAndAsyncArrowHead + // SuperCall + // ImportCall + // CallExpression Arguments + // CallExpression `[` Expression `]` + // CallExpression `.` IdentifierName + // CallExpression TemplateLiteral + // MemberExpression: + // PrimaryExpression + // MemberExpression `[` Expression `]` + // MemberExpression `.` IdentifierName + // MemberExpression TemplateLiteral + // SuperProperty + // MetaProperty + // `new` MemberExpression Arguments + OperatorPrecedenceMember + // TODO: JSXElement? + // PrimaryExpression: + // `this` + // IdentifierReference + // Literal + // ArrayLiteral + // ObjectLiteral + // FunctionExpression + // ClassExpression + // GeneratorExpression + // AsyncFunctionExpression + // AsyncGeneratorExpression + // RegularExpressionLiteral + // TemplateLiteral + OperatorPrecedencePrimary + // PrimaryExpression: + // CoverParenthesizedExpressionAndArrowParameterList + OperatorPrecedenceParentheses + OperatorPrecedenceLowest = OperatorPrecedenceComma + OperatorPrecedenceHighest = OperatorPrecedenceParentheses + OperatorPrecedenceDisallowComma = OperatorPrecedenceYield + // ShortCircuitExpression: + // LogicalORExpression + // CoalesceExpression + // CoalesceExpression: + // CoalesceExpressionHead `??` BitwiseORExpression + // CoalesceExpressionHead: + // CoalesceExpression + // BitwiseORExpression + OperatorPrecedenceCoalesce = OperatorPrecedenceLogicalOR + // -1 is lower than all other precedences. Returning it will cause binary expression + // parsing to stop. + OperatorPrecedenceInvalid OperatorPrecedence = -1 +) + +func getOperator(expression *Expression) Kind { + switch expression.Kind { + case KindBinaryExpression: + return expression.AsBinaryExpression().OperatorToken.Kind + case KindPrefixUnaryExpression: + return expression.AsPrefixUnaryExpression().Operator + case KindPostfixUnaryExpression: + return expression.AsPostfixUnaryExpression().Operator + default: + return expression.Kind + } +} + +// Gets the precedence of an expression +func GetExpressionPrecedence(expression *Expression) OperatorPrecedence { + operator := getOperator(expression) + var flags OperatorPrecedenceFlags + if expression.Kind == KindNewExpression && expression.ArgumentList() == nil { + flags = OperatorPrecedenceFlagsNewWithoutArguments + } else if IsOptionalChain(expression) { + flags = OperatorPrecedenceFlagsOptionalChain + } + return GetOperatorPrecedence(expression.Kind, operator, flags) +} + +type OperatorPrecedenceFlags int + +const ( + OperatorPrecedenceFlagsNone OperatorPrecedenceFlags = 0 + OperatorPrecedenceFlagsNewWithoutArguments OperatorPrecedenceFlags = 1 << 0 + OperatorPrecedenceFlagsOptionalChain OperatorPrecedenceFlags = 1 << 1 +) + +// Gets the precedence of an operator +func GetOperatorPrecedence(nodeKind Kind, operatorKind Kind, flags OperatorPrecedenceFlags) OperatorPrecedence { + switch nodeKind { + case KindSpreadElement: + return OperatorPrecedenceSpread + case KindYieldExpression: + return OperatorPrecedenceYield + // !!! By necessity, this differs from the old compiler to better align with ParenthesizerRules. consider backporting + case KindArrowFunction: + return OperatorPrecedenceAssignment + case KindConditionalExpression: + return OperatorPrecedenceConditional + case KindBinaryExpression: + switch operatorKind { + case KindCommaToken: + return OperatorPrecedenceComma + + case KindEqualsToken, + KindPlusEqualsToken, + KindMinusEqualsToken, + KindAsteriskAsteriskEqualsToken, + KindAsteriskEqualsToken, + KindSlashEqualsToken, + KindPercentEqualsToken, + KindLessThanLessThanEqualsToken, + KindGreaterThanGreaterThanEqualsToken, + KindGreaterThanGreaterThanGreaterThanEqualsToken, + KindAmpersandEqualsToken, + KindCaretEqualsToken, + KindBarEqualsToken, + KindBarBarEqualsToken, + KindAmpersandAmpersandEqualsToken, + KindQuestionQuestionEqualsToken: + return OperatorPrecedenceAssignment + + default: + return GetBinaryOperatorPrecedence(operatorKind) + } + // TODO: Should prefix `++` and `--` be moved to the `Update` precedence? + case KindTypeAssertionExpression, + KindNonNullExpression, + KindPrefixUnaryExpression, + KindTypeOfExpression, + KindVoidExpression, + KindDeleteExpression, + KindAwaitExpression: + return OperatorPrecedenceUnary + + case KindPostfixUnaryExpression: + return OperatorPrecedenceUpdate + + // !!! By necessity, this differs from the old compiler to better align with ParenthesizerRules. consider backporting + case KindPropertyAccessExpression, KindElementAccessExpression: + if flags&OperatorPrecedenceFlagsOptionalChain != 0 { + return OperatorPrecedenceOptionalChain + } + return OperatorPrecedenceMember + + case KindCallExpression: + if flags&OperatorPrecedenceFlagsOptionalChain != 0 { + return OperatorPrecedenceOptionalChain + } + return OperatorPrecedenceMember + + // !!! By necessity, this differs from the old compiler to better align with ParenthesizerRules. consider backporting + case KindNewExpression: + if flags&OperatorPrecedenceFlagsNewWithoutArguments != 0 { + return OperatorPrecedenceLeftHandSide + } + return OperatorPrecedenceMember + + // !!! By necessity, this differs from the old compiler to better align with ParenthesizerRules. consider backporting + case KindTaggedTemplateExpression, KindMetaProperty, KindExpressionWithTypeArguments: + return OperatorPrecedenceMember + + case KindAsExpression, + KindSatisfiesExpression: + return OperatorPrecedenceRelational + + case KindThisKeyword, + KindSuperKeyword, + KindImportKeyword, + KindIdentifier, + KindPrivateIdentifier, + KindNullKeyword, + KindTrueKeyword, + KindFalseKeyword, + KindNumericLiteral, + KindBigIntLiteral, + KindStringLiteral, + KindArrayLiteralExpression, + KindObjectLiteralExpression, + KindFunctionExpression, + KindClassExpression, + KindRegularExpressionLiteral, + KindNoSubstitutionTemplateLiteral, + KindTemplateExpression, + KindOmittedExpression, + KindJsxElement, + KindJsxSelfClosingElement, + KindJsxFragment, + KindMissingDeclaration: + return OperatorPrecedencePrimary + + // !!! By necessity, this differs from the old compiler to support emit. consider backporting + case KindParenthesizedExpression: + return OperatorPrecedenceParentheses + + default: + return OperatorPrecedenceInvalid + } +} + +// Gets the precedence of a binary operator +func GetBinaryOperatorPrecedence(operatorKind Kind) OperatorPrecedence { + switch operatorKind { + case KindQuestionQuestionToken: + return OperatorPrecedenceCoalesce + case KindBarBarToken: + return OperatorPrecedenceLogicalOR + case KindAmpersandAmpersandToken: + return OperatorPrecedenceLogicalAND + case KindBarToken: + return OperatorPrecedenceBitwiseOR + case KindCaretToken: + return OperatorPrecedenceBitwiseXOR + case KindAmpersandToken: + return OperatorPrecedenceBitwiseAND + case KindEqualsEqualsToken, KindExclamationEqualsToken, KindEqualsEqualsEqualsToken, KindExclamationEqualsEqualsToken: + return OperatorPrecedenceEquality + case KindLessThanToken, KindGreaterThanToken, KindLessThanEqualsToken, KindGreaterThanEqualsToken, + KindInstanceOfKeyword, KindInKeyword, KindAsKeyword, KindSatisfiesKeyword: + return OperatorPrecedenceRelational + case KindLessThanLessThanToken, KindGreaterThanGreaterThanToken, KindGreaterThanGreaterThanGreaterThanToken: + return OperatorPrecedenceShift + case KindPlusToken, KindMinusToken: + return OperatorPrecedenceAdditive + case KindAsteriskToken, KindSlashToken, KindPercentToken: + return OperatorPrecedenceMultiplicative + case KindAsteriskAsteriskToken: + return OperatorPrecedenceExponentiation + } + // -1 is lower than all other precedences. Returning it will cause binary expression + // parsing to stop. + return OperatorPrecedenceInvalid +} + +// Gets the leftmost expression of an expression, e.g. `a` in `a.b`, `a[b]`, `a++`, `a+b`, `a?b:c`, `a as B`, etc. +func GetLeftmostExpression(node *Expression, stopAtCallExpressions bool) *Expression { + for { + switch node.Kind { + case KindPostfixUnaryExpression: + node = node.AsPostfixUnaryExpression().Operand + continue + case KindBinaryExpression: + node = node.AsBinaryExpression().Left + continue + case KindConditionalExpression: + node = node.AsConditionalExpression().Condition + continue + case KindTaggedTemplateExpression: + node = node.AsTaggedTemplateExpression().Tag + continue + case KindCallExpression: + if stopAtCallExpressions { + return node + } + fallthrough + case KindAsExpression, + KindElementAccessExpression, + KindPropertyAccessExpression, + KindNonNullExpression, + KindPartiallyEmittedExpression, + KindSatisfiesExpression: + node = node.Expression() + continue + } + return node + } +} + +type TypePrecedence int32 + +const ( + // Conditional precedence (lowest) + // + // Type[Extends]: + // ConditionalTypeNode[?Extends] + // + // ConditionalTypeNode[Extends]: + // [~Extends] UnionTypeNode `extends` Type[+Extends] `?` Type[~Extends] `:` Type[~Extends] + // + TypePrecedenceConditional TypePrecedence = iota + + // JSDoc precedence (optional and variadic types) + // + // JSDocType: + // `...`? Type `=`? + TypePrecedenceJSDoc + + // Function precedence + // + // Type[Extends]: + // ConditionalTypeNode[?Extends] + // FunctionTypeNode[?Extends] + // ConstructorTypeNode[?Extends] + // + // ConditionalTypeNode[Extends]: + // UnionTypeNode + // + // FunctionTypeNode[Extends]: + // TypeParameters? ArrowParameters `=>` Type[?Extends] + // + // ConstructorTypeNode[Extends]: + // `abstract`? TypeParameters? ArrowParameters `=>` Type[?Extends] + // + TypePrecedenceFunction + + // Union precedence + // + // UnionTypeNode: + // `|`? UnionTypeNoBar + // + // UnionTypeNoBar: + // IntersectionTypeNode + // UnionTypeNoBar `|` IntersectionTypeNode + // + TypePrecedenceUnion + + // Intersection precedence + // + // IntersectionTypeNode: + // `&`? IntersectionTypeNoAmpersand + // + // IntersectionTypeNoAmpersand: + // TypeOperatorNode + // IntersectionTypeNoAmpersand `&` TypeOperatorNode + // + TypePrecedenceIntersection + + // TypeOperatorNode precedence + // + // TypeOperatorNode: + // PostfixType + // InferTypeNode + // `keyof` TypeOperatorNode + // `unique` TypeOperatorNode + // `readonly` PostfixType + // + // InferTypeNode: + // `infer` BindingIdentifier + // `infer` BindingIdentifier `extends` Type[+Extends] + // + TypePrecedenceTypeOperator + + // Postfix precedence + // + // PostfixType: + // NonArrayType + // OptionalTypeNode + // ArrayTypeNode + // IndexedAccessTypeNode + // + // OptionalTypeNode: + // PostfixType `?` + // + // ArrayTypeNode: + // PostfixType `[` `]` + // + // IndexedAccessTypeNode: + // PostfixType `[` Type[~Extends] `]` + // + TypePrecedencePostfix + + // NonArray precedence (highest) + // + // NonArrayType: + // KeywordType + // LiteralTypeNode + // ThisTypeNode + // ImportType + // TypeQueryNode + // MappedTypeNode + // TypeLiteralNode + // TupleTypeNode + // ParenthesizedTypeNode + // TypePredicateNode + // TypeReferenceNode + // TemplateType + // + // KeywordType: one of + // `any` `unknown` `string` `number` `bigint` + // `symbol` `boolean` `undefined` `never` `object` + // `intrinsic` `void` + // + // LiteralTypeNode: + // StringLiteral + // NoSubstitutionTemplateLiteral + // NumericLiteral + // BigIntLiteral + // `-` NumericLiteral + // `-` BigIntLiteral + // `true` + // `false` + // `null` + // + // ThisTypeNode: + // `this` + // + // ImportType: + // `typeof`? `import` `(` Type[~Extends] `,`? `)` ImportTypeQualifier? TypeArguments? + // `typeof`? `import` `(` Type[~Extends] `,` ImportTypeAttributes `,`? `)` ImportTypeQualifier? TypeArguments? + // + // ImportTypeQualifier: + // `.` EntityName + // + // ImportTypeAttributes: + // `{` `with` `:` ImportAttributes `,`? `}` + // + // TypeQueryNode: + // + // MappedTypeNode: + // `{` MappedTypePrefix? MappedTypePropertyName MappedTypeSuffix? `:` Type[~Extends] `;` `}` + // + // MappedTypePrefix: + // `readonly` + // `+` `readonly` + // `-` `readonly` + // + // MappedTypePropertyName: + // `[` BindingIdentifier `in` Type[~Extends] `]` + // `[` BindingIdentifier `in` Type[~Extends] `as` Type[~Extends] `]` + // + // MappedTypeSuffix: + // `?` + // `+` `?` + // `-` `?` + // + // TypeLiteralNode: + // `{` TypeElementList `}` + // + // TypeElementList: + // [empty] + // TypeElementList TypeElement + // + // TypeElement: + // PropertySignatureDeclaration + // MethodSignatureDeclaration + // IndexSignatureDeclaration + // CallSignatureDeclaration + // ConstructSignatureDeclaration + // + // PropertySignatureDeclaration: + // PropertyName `?`? TypeAnnotation? `;` + // + // MethodSignatureDeclaration: + // PropertyName `?`? TypeParameters? `(` FormalParameterList `)` TypeAnnotation? `;` + // `get` PropertyName TypeParameters? `(` FormalParameterList `)` TypeAnnotation? `;` // GetAccessorDeclaration + // `set` PropertyName TypeParameters? `(` FormalParameterList `)` TypeAnnotation? `;` // SetAccessorDeclaration + // + // IndexSignatureDeclaration: + // `[` IdentifierName`]` TypeAnnotation `;` + // + // CallSignatureDeclaration: + // TypeParameters? `(` FormalParameterList `)` TypeAnnotation? `;` + // + // ConstructSignatureDeclaration: + // `new` TypeParameters? `(` FormalParameterList `)` TypeAnnotation? `;` + // + // TupleTypeNode: + // `[` `]` + // `[` NamedTupleElementTypes `,`? `]` + // `[` TupleElementTypes `,`? `]` + // + // NamedTupleElementTypes: + // NamedTupleMember + // NamedTupleElementTypes `,` NamedTupleMember + // + // NamedTupleMember: + // IdentifierName `?`? `:` Type[~Extends] + // `...` IdentifierName `:` Type[~Extends] + // + // TupleElementTypes: + // TupleElementType + // TupleElementTypes `,` TupleElementType + // + // TupleElementType: + // Type[~Extends] + // OptionalTypeNode + // RestTypeNode + // + // RestTypeNode: + // `...` Type[~Extends] + // + // ParenthesizedTypeNode: + // `(` Type[~Extends] `)` + // + // TypePredicateNode: + // `asserts`? TypePredicateParameterName + // `asserts`? TypePredicateParameterName `is` Type[~Extends] + // + // TypePredicateParameterName: + // `this` + // IdentifierReference + // + // TypeReferenceNode: + // EntityName TypeArguments? + // + // TemplateType: + // TemplateHead Type[~Extends] TemplateTypeSpans + // + // TemplateTypeSpans: + // TemplateTail + // TemplateTypeMiddleList TemplateTail + // + // TemplateTypeMiddleList: + // TemplateMiddle Type[~Extends] + // TemplateTypeMiddleList TemplateMiddle Type[~Extends] + // + // TypeArguments: + // `<` TypeArgumentList `,`? `>` + // + // TypeArgumentList: + // Type[~Extends] + // TypeArgumentList `,` Type[~Extends] + // + TypePrecedenceNonArray + + TypePrecedenceLowest = TypePrecedenceConditional + TypePrecedenceHighest = TypePrecedenceNonArray +) + +// Gets the precedence of a TypeNode +func GetTypeNodePrecedence(n *TypeNode) TypePrecedence { + switch n.Kind { + case KindConditionalType: + return TypePrecedenceConditional + case KindJSDocOptionalType, KindJSDocVariadicType: + return TypePrecedenceJSDoc + case KindFunctionType, KindConstructorType: + return TypePrecedenceFunction + case KindUnionType: + return TypePrecedenceUnion + case KindIntersectionType: + return TypePrecedenceIntersection + case KindTypeOperator: + return TypePrecedenceTypeOperator + case KindInferType: + if n.AsInferTypeNode().TypeParameter.AsTypeParameterDeclaration().Constraint != nil { + // `infer T extends U` must be treated as FunctionTypeNode precedence as the `extends` clause eagerly consumes + // TypeNode + return TypePrecedenceFunction + } + return TypePrecedenceTypeOperator + case KindIndexedAccessType, KindArrayType, KindOptionalType: + return TypePrecedencePostfix + case KindTypeQuery: + // TypeQueryNode is actually a NonArrayType, but we treat it as TypeOperatorNode + // precedence so that it is parenthesized when used in a PostfixType + // context (e.g., `(typeof C)[]` instead of `typeof C[]`) + return TypePrecedenceTypeOperator + case KindAnyKeyword, + KindUnknownKeyword, + KindStringKeyword, + KindNumberKeyword, + KindBigIntKeyword, + KindSymbolKeyword, + KindBooleanKeyword, + KindUndefinedKeyword, + KindNeverKeyword, + KindObjectKeyword, + KindIntrinsicKeyword, + KindVoidKeyword, + KindJSDocAllType, + KindJSDocNullableType, + KindJSDocNonNullableType, + KindLiteralType, + KindTypePredicate, + KindTypeReference, + KindTypeLiteral, + KindTupleType, + KindRestType, + KindParenthesizedType, + KindThisType, + KindMappedType, + KindNamedTupleMember, + KindTemplateLiteralType, + KindImportType, + // These occur in pseudo-types like `f.C`, where `f` is a generic function and `C` is a local type + KindPropertyAccessExpression, + KindExpressionWithTypeArguments: + return TypePrecedenceNonArray + default: + panic(fmt.Sprintf("unhandled TypeNode: %v", n.Kind)) + } +} diff --git a/tools/tsgo/internal/ast/subtreefacts.go b/tools/tsgo/internal/ast/subtreefacts.go new file mode 100644 index 00000000..4b514b42 --- /dev/null +++ b/tools/tsgo/internal/ast/subtreefacts.go @@ -0,0 +1,133 @@ +package ast + +import ( + "github.com/microsoft/typescript-go/internal/core" +) + +type SubtreeFacts uint32 + +const ( + // Facts + // - Flags used to indicate that a node or subtree contains syntax relevant to a specific transform + + SubtreeContainsTypeScript SubtreeFacts = 1 << iota + SubtreeContainsJsx + SubtreeContainsESDecorators + SubtreeContainsUsing + SubtreeContainsClassStaticBlocks + SubtreeContainsESClassFields + SubtreeContainsLogicalAssignments + SubtreeContainsNullishCoalescing + SubtreeContainsOptionalChaining + SubtreeContainsMissingCatchClauseVariable + SubtreeContainsESObjectRestOrSpread // subtree has a `...` somewhere inside it, never cleared + SubtreeContainsForAwaitOrAsyncGenerator + SubtreeContainsAnyAwait + SubtreeContainsExponentiationOperator + + // Markers + // - Flags used to indicate that a node or subtree contains a particular kind of syntax. + + SubtreeContainsLexicalThis + SubtreeContainsLexicalSuper + SubtreeContainsRestOrSpread // marker on any `...` - cleared on binding pattern exit + SubtreeContainsObjectRestOrSpread // marker on any `{...x}` - cleared on most scope exits + SubtreeContainsAwait + SubtreeContainsDynamicImport + SubtreeContainsClassFields + SubtreeContainsDecorators + SubtreeContainsIdentifier + SubtreeContainsPrivateIdentifierInExpression + SubtreeContainsInvalidTemplateEscape + + SubtreeFactsComputed // NOTE: This should always be last + SubtreeFactsNone SubtreeFacts = 0 + + // Aliases (unused, for documentation purposes only - correspond to combinations in transformers/estransforms/definitions.go) + + SubtreeContainsESNext = SubtreeContainsESDecorators | SubtreeContainsUsing + SubtreeContainsES2022 = SubtreeContainsClassStaticBlocks | SubtreeContainsESClassFields + SubtreeContainsES2021 = SubtreeContainsLogicalAssignments + SubtreeContainsES2020 = SubtreeContainsNullishCoalescing | SubtreeContainsOptionalChaining + SubtreeContainsES2019 = SubtreeContainsMissingCatchClauseVariable + SubtreeContainsES2018 = SubtreeContainsESObjectRestOrSpread | SubtreeContainsForAwaitOrAsyncGenerator | SubtreeContainsInvalidTemplateEscape + SubtreeContainsES2017 = SubtreeContainsAnyAwait + SubtreeContainsES2016 = SubtreeContainsExponentiationOperator + + // Scope Exclusions + // - Bitmasks that exclude flags from propagating out of a specific context + // into the subtree flags of their container. + + SubtreeExclusionsNode = SubtreeFactsComputed + SubtreeExclusionsEraseable = ^SubtreeContainsTypeScript + SubtreeExclusionsOuterExpression = SubtreeExclusionsNode + SubtreeExclusionsPropertyAccess = SubtreeExclusionsNode + SubtreeExclusionsElementAccess = SubtreeExclusionsNode + SubtreeExclusionsArrowFunction = SubtreeExclusionsNode | SubtreeContainsAwait | SubtreeContainsObjectRestOrSpread + SubtreeExclusionsFunction = SubtreeExclusionsNode | SubtreeContainsLexicalThis | SubtreeContainsLexicalSuper | SubtreeContainsAwait | SubtreeContainsObjectRestOrSpread + SubtreeExclusionsConstructor = SubtreeExclusionsNode | SubtreeContainsLexicalThis | SubtreeContainsLexicalSuper | SubtreeContainsAwait | SubtreeContainsObjectRestOrSpread + SubtreeExclusionsMethod = SubtreeExclusionsNode | SubtreeContainsLexicalThis | SubtreeContainsLexicalSuper | SubtreeContainsAwait | SubtreeContainsObjectRestOrSpread + SubtreeExclusionsAccessor = SubtreeExclusionsNode | SubtreeContainsLexicalThis | SubtreeContainsLexicalSuper | SubtreeContainsAwait | SubtreeContainsObjectRestOrSpread + SubtreeExclusionsProperty = SubtreeExclusionsNode | SubtreeContainsLexicalThis | SubtreeContainsLexicalSuper + SubtreeExclusionsClass = SubtreeExclusionsNode + SubtreeExclusionsModule = SubtreeExclusionsNode | SubtreeContainsLexicalThis | SubtreeContainsLexicalSuper + SubtreeExclusionsObjectLiteral = SubtreeExclusionsNode | SubtreeContainsObjectRestOrSpread + SubtreeExclusionsArrayLiteral = SubtreeExclusionsNode + SubtreeExclusionsCall = SubtreeExclusionsNode + SubtreeExclusionsNew = SubtreeExclusionsNode + SubtreeExclusionsVariableDeclarationList = SubtreeExclusionsNode | SubtreeContainsObjectRestOrSpread + SubtreeExclusionsParameter = SubtreeExclusionsNode + SubtreeExclusionsCatchClause = SubtreeExclusionsNode | SubtreeContainsObjectRestOrSpread + SubtreeExclusionsBindingPattern = SubtreeExclusionsNode | SubtreeContainsRestOrSpread + + // Masks + // - Additional bitmasks + + SubtreeContainsLexicalThisOrSuper = SubtreeContainsLexicalThis | SubtreeContainsLexicalSuper +) + +func propagateEraseableSyntaxListSubtreeFacts(children *TypeArgumentList) SubtreeFacts { + return core.IfElse(children != nil, SubtreeContainsTypeScript, SubtreeFactsNone) +} + +func propagateEraseableSyntaxSubtreeFacts(child *TypeNode) SubtreeFacts { + return core.IfElse(child != nil, SubtreeContainsTypeScript, SubtreeFactsNone) +} + +func propagateObjectBindingElementSubtreeFacts(child *BindingElementNode) SubtreeFacts { + facts := propagateSubtreeFacts(child) + if facts&SubtreeContainsRestOrSpread != 0 { + facts &^= SubtreeContainsRestOrSpread + facts |= SubtreeContainsObjectRestOrSpread | SubtreeContainsESObjectRestOrSpread + } + return facts +} + +func propagateBindingElementSubtreeFacts(child *BindingElementNode) SubtreeFacts { + return propagateSubtreeFacts(child) & ^SubtreeContainsRestOrSpread +} + +func propagateSubtreeFacts(child *Node) SubtreeFacts { + if child == nil { + return SubtreeFactsNone + } + return child.propagateSubtreeFacts() +} + +func propagateNodeListSubtreeFacts(children *NodeList, propagate func(*Node) SubtreeFacts) SubtreeFacts { + if children == nil { + return SubtreeFactsNone + } + facts := SubtreeFactsNone + for _, child := range children.Nodes { + facts |= propagate(child) + } + return facts +} + +func propagateModifierListSubtreeFacts(children *ModifierList) SubtreeFacts { + if children == nil { + return SubtreeFactsNone + } + return propagateNodeListSubtreeFacts(&children.NodeList, propagateSubtreeFacts) +} diff --git a/tools/tsgo/internal/ast/symbol.go b/tools/tsgo/internal/ast/symbol.go new file mode 100644 index 00000000..f4ffd566 --- /dev/null +++ b/tools/tsgo/internal/ast/symbol.go @@ -0,0 +1,103 @@ +package ast + +import ( + "strings" + "sync/atomic" +) + +// Symbol + +type Symbol struct { + Flags SymbolFlags + CheckFlags CheckFlags // Non-zero only in transient symbols created by Checker + Name string + Declarations []*Node + ValueDeclaration *Node + Members SymbolTable + Exports SymbolTable + id atomic.Uint64 + Parent *Symbol + ExportSymbol *Symbol +} + +func (s *Symbol) IsExternalModule() bool { + return s.Flags&SymbolFlagsModule != 0 && len(s.Name) > 0 && s.Name[0] == '"' +} + +func (s *Symbol) IsStatic() bool { + if s.ValueDeclaration == nil { + return false + } + modifierFlags := s.ValueDeclaration.ModifierFlags() + return modifierFlags&ModifierFlagsStatic != 0 +} + +// See comment on `declareModuleMember` in `binder.go`. +func (s *Symbol) CombinedLocalAndExportSymbolFlags() SymbolFlags { + if s.ExportSymbol != nil { + return s.Flags | s.ExportSymbol.Flags + } + return s.Flags +} + +// SymbolTable + +type SymbolTable map[string]*Symbol + +const InternalSymbolNamePrefix = "\xFE" // Invalid UTF8 sequence, will never occur as IdentifierName + +const ( + InternalSymbolNameCall = InternalSymbolNamePrefix + "call" // Call signatures + InternalSymbolNameConstructor = InternalSymbolNamePrefix + "constructor" // Constructor implementations + InternalSymbolNameNew = InternalSymbolNamePrefix + "new" // Constructor signatures + InternalSymbolNameIndex = InternalSymbolNamePrefix + "index" // Index signatures + InternalSymbolNameExportStar = InternalSymbolNamePrefix + "export" // Module export * declarations + InternalSymbolNameGlobal = InternalSymbolNamePrefix + "global" // Global self-reference + InternalSymbolNameMissing = InternalSymbolNamePrefix + "missing" // Indicates missing symbol + InternalSymbolNameType = InternalSymbolNamePrefix + "type" // Anonymous type literal symbol + InternalSymbolNameObject = InternalSymbolNamePrefix + "object" // Anonymous object literal declaration + InternalSymbolNameJSXAttributes = InternalSymbolNamePrefix + "jsxAttributes" // Anonymous JSX attributes object literal declaration + InternalSymbolNameClass = InternalSymbolNamePrefix + "class" // Unnamed class expression + InternalSymbolNameFunction = InternalSymbolNamePrefix + "function" // Unnamed function expression + InternalSymbolNameComputed = InternalSymbolNamePrefix + "computed" // Computed property name declaration with dynamic name + InternalSymbolNameAssignmentDeclaration = InternalSymbolNamePrefix + "assignment" // Assignment declarations + InternalSymbolNameInstantiationExpression = InternalSymbolNamePrefix + "instantiationExpression" // Instantiation expressions + InternalSymbolNameImportAttributes = InternalSymbolNamePrefix + "importAttributes" + InternalSymbolNameExportEquals = "export=" // Export assignment symbol + InternalSymbolNameDefault = "default" // Default export symbol (technically not wholly internal, but included here for usability) + InternalSymbolNameThis = "this" + InternalSymbolNameModuleExports = "module.exports" +) + +func SymbolName(symbol *Symbol) string { + if symbol.ValueDeclaration != nil && IsPrivateIdentifierClassElementDeclaration(symbol.ValueDeclaration) { + return symbol.ValueDeclaration.Name().Text() + } + return symbol.Name +} + +// EscapeAllInternalSymbolNames replaces internal symbol name markers ("\xFE") with "__". +func EscapeAllInternalSymbolNames(name string) string { + return strings.ReplaceAll(name, InternalSymbolNamePrefix, "__") +} + +func EscapeInternalSymbolName(name string) string { + if rest, ok := strings.CutPrefix(name, InternalSymbolNamePrefix); ok { + return "__" + rest + } + return name +} + +// EscapeSymbolName converts a binder symbol name into its escaped "__String" +// form. Internal names (prefixed with the "\xFE" sentinel) become "__"-prefixed, +// and user names that already begin with "__" gain an extra leading underscore +// so they can be distinguished from internal names. +func EscapeSymbolName(name string) string { + if rest, ok := strings.CutPrefix(name, InternalSymbolNamePrefix); ok { + return "__" + rest + } + if len(name) >= 2 && name[0] == '_' && name[1] == '_' { + return "_" + name + } + return name +} diff --git a/tools/tsgo/internal/ast/symbolflags.go b/tools/tsgo/internal/ast/symbolflags.go new file mode 100644 index 00000000..b7ee8a3d --- /dev/null +++ b/tools/tsgo/internal/ast/symbolflags.go @@ -0,0 +1,86 @@ +package ast + +// SymbolFlags + +type SymbolFlags uint32 + +const ( + SymbolFlagsNone SymbolFlags = 0 + SymbolFlagsFunctionScopedVariable SymbolFlags = 1 << 0 // Variable (var) or parameter + SymbolFlagsBlockScopedVariable SymbolFlags = 1 << 1 // A block-scoped variable (let or const) + SymbolFlagsProperty SymbolFlags = 1 << 2 // Property or enum member + SymbolFlagsEnumMember SymbolFlags = 1 << 3 // Enum member + SymbolFlagsFunction SymbolFlags = 1 << 4 // Function + SymbolFlagsClass SymbolFlags = 1 << 5 // Class + SymbolFlagsInterface SymbolFlags = 1 << 6 // Interface + SymbolFlagsConstEnum SymbolFlags = 1 << 7 // Const enum + SymbolFlagsRegularEnum SymbolFlags = 1 << 8 // Enum + SymbolFlagsValueModule SymbolFlags = 1 << 9 // Instantiated module + SymbolFlagsNamespaceModule SymbolFlags = 1 << 10 // Uninstantiated module + SymbolFlagsTypeLiteral SymbolFlags = 1 << 11 // Type Literal or mapped type + SymbolFlagsObjectLiteral SymbolFlags = 1 << 12 // Object Literal + SymbolFlagsMethod SymbolFlags = 1 << 13 // Method + SymbolFlagsConstructor SymbolFlags = 1 << 14 // Constructor + SymbolFlagsGetAccessor SymbolFlags = 1 << 15 // Get accessor + SymbolFlagsSetAccessor SymbolFlags = 1 << 16 // Set accessor + SymbolFlagsSignature SymbolFlags = 1 << 17 // Call, construct, or index signature + SymbolFlagsTypeParameter SymbolFlags = 1 << 18 // Type parameter + SymbolFlagsTypeAlias SymbolFlags = 1 << 19 // Type alias + SymbolFlagsExportValue SymbolFlags = 1 << 20 // Exported value marker (see comment in declareModuleMember in binder) + SymbolFlagsAlias SymbolFlags = 1 << 21 // An alias for another symbol (see comment in isAliasSymbolDeclaration in checker) + SymbolFlagsPrototype SymbolFlags = 1 << 22 // Prototype property (no source representation) + SymbolFlagsExportStar SymbolFlags = 1 << 23 // Export * declaration + SymbolFlagsOptional SymbolFlags = 1 << 24 // Optional property + SymbolFlagsTransient SymbolFlags = 1 << 25 // Transient symbol (created during type check) + SymbolFlagsAssignment SymbolFlags = 1 << 26 // Assignment to property on function acting as declaration (eg `func.prop = 1`) + SymbolFlagsModuleExports SymbolFlags = 1 << 27 // Symbol for CommonJS `module` of `module.exports` + SymbolFlagsConstEnumOnlyModule SymbolFlags = 1 << 28 // Module contains only const enums or other modules with only const enums + SymbolFlagsReplaceableByMethod SymbolFlags = 1 << 29 + SymbolFlagsGlobalLookup SymbolFlags = 1 << 30 // Flag to signal this is a global lookup + SymbolFlagsAll SymbolFlags = 1<<30 - 1 // All flags except SymbolFlagsGlobalLookup + + SymbolFlagsEnum = SymbolFlagsRegularEnum | SymbolFlagsConstEnum + SymbolFlagsVariable = SymbolFlagsFunctionScopedVariable | SymbolFlagsBlockScopedVariable + SymbolFlagsValue = SymbolFlagsVariable | SymbolFlagsProperty | SymbolFlagsEnumMember | SymbolFlagsObjectLiteral | SymbolFlagsFunction | SymbolFlagsClass | SymbolFlagsEnum | SymbolFlagsValueModule | SymbolFlagsMethod | SymbolFlagsGetAccessor | SymbolFlagsSetAccessor + SymbolFlagsType = SymbolFlagsClass | SymbolFlagsInterface | SymbolFlagsEnum | SymbolFlagsEnumMember | SymbolFlagsTypeLiteral | SymbolFlagsTypeParameter | SymbolFlagsTypeAlias + SymbolFlagsNamespace = SymbolFlagsValueModule | SymbolFlagsNamespaceModule | SymbolFlagsEnum + SymbolFlagsModule = SymbolFlagsValueModule | SymbolFlagsNamespaceModule + SymbolFlagsAccessor = SymbolFlagsGetAccessor | SymbolFlagsSetAccessor + + // Variables can be redeclared, but can not redeclare a block-scoped declaration with the + // same name, or any other value that is not a variable, e.g. ValueModule or Class + SymbolFlagsFunctionScopedVariableExcludes = SymbolFlagsValue & ^SymbolFlagsFunctionScopedVariable + + // Block-scoped declarations are not allowed to be re-declared + // they can not merge with anything in the value space + SymbolFlagsBlockScopedVariableExcludes = SymbolFlagsValue + + SymbolFlagsParameterExcludes = SymbolFlagsValue + SymbolFlagsPropertyExcludes = SymbolFlagsValue & ^(SymbolFlagsProperty | SymbolFlagsAccessor) + SymbolFlagsEnumMemberExcludes = SymbolFlagsValue | SymbolFlagsType + SymbolFlagsFunctionExcludes = SymbolFlagsValue & ^(SymbolFlagsFunction | SymbolFlagsValueModule | SymbolFlagsClass) + SymbolFlagsClassExcludes = (SymbolFlagsValue | SymbolFlagsType) & ^(SymbolFlagsValueModule | SymbolFlagsInterface | SymbolFlagsFunction) // class-interface mergability done in checker.ts + SymbolFlagsInterfaceExcludes = SymbolFlagsType & ^(SymbolFlagsInterface | SymbolFlagsClass) + SymbolFlagsRegularEnumExcludes = (SymbolFlagsValue | SymbolFlagsType) & ^(SymbolFlagsRegularEnum | SymbolFlagsValueModule) // regular enums merge only with regular enums and modules + SymbolFlagsConstEnumExcludes = (SymbolFlagsValue | SymbolFlagsType) & ^SymbolFlagsConstEnum // const enums merge only with const enums + SymbolFlagsValueModuleExcludes = SymbolFlagsValue & ^(SymbolFlagsFunction | SymbolFlagsClass | SymbolFlagsRegularEnum | SymbolFlagsValueModule) + SymbolFlagsNamespaceModuleExcludes = SymbolFlagsNone + SymbolFlagsMethodExcludes = SymbolFlagsValue & ^SymbolFlagsMethod + SymbolFlagsGetAccessorExcludes = SymbolFlagsValue & ^(SymbolFlagsSetAccessor | SymbolFlagsProperty) + SymbolFlagsSetAccessorExcludes = SymbolFlagsValue & ^(SymbolFlagsGetAccessor | SymbolFlagsProperty) + SymbolFlagsAccessorExcludes = SymbolFlagsValue & ^SymbolFlagsProperty + SymbolFlagsTypeParameterExcludes = SymbolFlagsType & ^SymbolFlagsTypeParameter + SymbolFlagsTypeAliasExcludes = SymbolFlagsType + SymbolFlagsAliasExcludes = SymbolFlagsAlias + SymbolFlagsModuleMember = SymbolFlagsVariable | SymbolFlagsFunction | SymbolFlagsClass | SymbolFlagsInterface | SymbolFlagsEnum | SymbolFlagsModule | SymbolFlagsTypeAlias | SymbolFlagsAlias + SymbolFlagsExportHasLocal = SymbolFlagsFunction | SymbolFlagsClass | SymbolFlagsEnum | SymbolFlagsValueModule + SymbolFlagsBlockScoped = SymbolFlagsBlockScopedVariable | SymbolFlagsClass | SymbolFlagsEnum + SymbolFlagsPropertyOrAccessor = SymbolFlagsProperty | SymbolFlagsAccessor + SymbolFlagsClassMember = SymbolFlagsMethod | SymbolFlagsAccessor | SymbolFlagsProperty + SymbolFlagsExportSupportsDefaultModifier = SymbolFlagsClass | SymbolFlagsFunction | SymbolFlagsInterface + SymbolFlagsExportDoesNotSupportDefaultModifier = ^SymbolFlagsExportSupportsDefaultModifier + // The set of things we consider semantically classifiable. Used to speed up the LS during + // classification. + SymbolFlagsClassifiable = SymbolFlagsClass | SymbolFlagsEnum | SymbolFlagsTypeAlias | SymbolFlagsInterface | SymbolFlagsTypeParameter | SymbolFlagsModule | SymbolFlagsAlias + SymbolFlagsLateBindingContainer = SymbolFlagsClass | SymbolFlagsInterface | SymbolFlagsTypeLiteral | SymbolFlagsObjectLiteral | SymbolFlagsFunction +) diff --git a/tools/tsgo/internal/ast/tokenflags.go b/tools/tsgo/internal/ast/tokenflags.go new file mode 100644 index 00000000..82b64492 --- /dev/null +++ b/tools/tsgo/internal/ast/tokenflags.go @@ -0,0 +1,33 @@ +package ast + +type TokenFlags int32 + +const ( + TokenFlagsNone TokenFlags = 0 + TokenFlagsPrecedingLineBreak TokenFlags = 1 << 0 + TokenFlagsPrecedingJSDocComment TokenFlags = 1 << 1 + TokenFlagsUnterminated TokenFlags = 1 << 2 + TokenFlagsExtendedUnicodeEscape TokenFlags = 1 << 3 // e.g. `\u{10ffff}` + TokenFlagsScientific TokenFlags = 1 << 4 // e.g. `10e2` + TokenFlagsOctal TokenFlags = 1 << 5 // e.g. `0777` + TokenFlagsHexSpecifier TokenFlags = 1 << 6 // e.g. `0x00000000` + TokenFlagsBinarySpecifier TokenFlags = 1 << 7 // e.g. `0b0110010000000000` + TokenFlagsOctalSpecifier TokenFlags = 1 << 8 // e.g. `0o777` + TokenFlagsContainsSeparator TokenFlags = 1 << 9 // e.g. `0b1100_0101` + TokenFlagsUnicodeEscape TokenFlags = 1 << 10 // e.g. `\u00a0` + TokenFlagsContainsInvalidEscape TokenFlags = 1 << 11 // e.g. `\uhello` + TokenFlagsHexEscape TokenFlags = 1 << 12 // e.g. `\xa0` + TokenFlagsContainsLeadingZero TokenFlags = 1 << 13 // e.g. `0888` + TokenFlagsContainsInvalidSeparator TokenFlags = 1 << 14 // e.g. `0_1` + TokenFlagsPrecedingJSDocLeadingAsterisks TokenFlags = 1 << 15 + TokenFlagsSingleQuote TokenFlags = 1 << 16 // e.g. `'abc'` + TokenFlagsPrecedingJSDocWithDeprecated TokenFlags = 1 << 17 // Preceding JSDoc comment contains @deprecated + TokenFlagsPrecedingJSDocWithSeeOrLink TokenFlags = 1 << 18 // Preceding JSDoc comment contains @see or @link + TokenFlagsBinaryOrOctalSpecifier TokenFlags = TokenFlagsBinarySpecifier | TokenFlagsOctalSpecifier + TokenFlagsWithSpecifier TokenFlags = TokenFlagsHexSpecifier | TokenFlagsBinaryOrOctalSpecifier + TokenFlagsStringLiteralFlags TokenFlags = TokenFlagsUnterminated | TokenFlagsHexEscape | TokenFlagsUnicodeEscape | TokenFlagsExtendedUnicodeEscape | TokenFlagsContainsInvalidEscape | TokenFlagsSingleQuote + TokenFlagsNumericLiteralFlags TokenFlags = TokenFlagsScientific | TokenFlagsOctal | TokenFlagsContainsLeadingZero | TokenFlagsWithSpecifier | TokenFlagsContainsSeparator | TokenFlagsContainsInvalidSeparator + TokenFlagsTemplateLiteralLikeFlags TokenFlags = TokenFlagsUnterminated | TokenFlagsHexEscape | TokenFlagsUnicodeEscape | TokenFlagsExtendedUnicodeEscape | TokenFlagsContainsInvalidEscape + TokenFlagsRegularExpressionLiteralFlags TokenFlags = TokenFlagsUnterminated + TokenFlagsIsInvalid TokenFlags = TokenFlagsOctal | TokenFlagsContainsLeadingZero | TokenFlagsContainsInvalidSeparator | TokenFlagsContainsInvalidEscape +) diff --git a/tools/tsgo/internal/ast/utilities.go b/tools/tsgo/internal/ast/utilities.go new file mode 100644 index 00000000..1142bbfb --- /dev/null +++ b/tools/tsgo/internal/ast/utilities.go @@ -0,0 +1,4565 @@ +package ast + +import ( + "fmt" + "slices" + "strings" + "sync" + "sync/atomic" + + "github.com/microsoft/typescript-go/internal/core" + "github.com/microsoft/typescript-go/internal/debug" + "github.com/microsoft/typescript-go/internal/tspath" +) + +// Atomic ids + +var ( + nextNodeId atomic.Uint64 + nextSymbolId atomic.Uint64 +) + +func GetNodeId(node *Node) NodeId { + id := node.id.Load() + if id == 0 { + // Worst case, we burn a few ids if we have to CAS. + id = nextNodeId.Add(1) + if !node.id.CompareAndSwap(0, id) { + id = node.id.Load() + } + } + return NodeId(id) +} + +func GetSymbolId(symbol *Symbol) SymbolId { + id := symbol.id.Load() + if id == 0 { + // Worst case, we burn a few ids if we have to CAS. + id = nextSymbolId.Add(1) + if !symbol.id.CompareAndSwap(0, id) { + id = symbol.id.Load() + } + } + return SymbolId(id) +} + +func GetSymbolTable(data *SymbolTable) SymbolTable { + if *data == nil { + *data = make(SymbolTable) + } + return *data +} + +func GetMembers(symbol *Symbol) SymbolTable { + return GetSymbolTable(&symbol.Members) +} + +func GetExports(symbol *Symbol) SymbolTable { + return GetSymbolTable(&symbol.Exports) +} + +func GetLocals(container *Node) SymbolTable { + return GetSymbolTable(&container.LocalsContainerData().Locals) +} + +// Determines if a node is missing (either `nil` or empty) +func NodeIsMissing(node *Node) bool { + return node == nil || node.Loc.Pos() == node.Loc.End() && node.Loc.Pos() >= 0 && node.Kind != KindEndOfFile +} + +// Determines if a node is present +func NodeIsPresent(node *Node) bool { + return !NodeIsMissing(node) +} + +// Determines if a node contains synthetic positions +func NodeIsSynthesized(node *Node) bool { + return PositionIsSynthesized(node.Loc.Pos()) || PositionIsSynthesized(node.Loc.End()) +} + +func RangeIsSynthesized(loc core.TextRange) bool { + return PositionIsSynthesized(loc.Pos()) || PositionIsSynthesized(loc.End()) +} + +// Determines whether a position is synthetic +func PositionIsSynthesized(pos int) bool { + return pos < 0 +} + +func FindLastVisibleNode(nodes []*Node) *Node { + fromEnd := 1 + for fromEnd <= len(nodes) && nodes[len(nodes)-fromEnd].Flags&NodeFlagsReparsed != 0 { + fromEnd++ + } + if fromEnd <= len(nodes) { + return nodes[len(nodes)-fromEnd] + } + return nil +} + +func NodeKindIs(node *Node, kinds ...Kind) bool { + return slices.Contains(kinds, node.Kind) +} + +func IsModifier(node *Node) bool { + return IsModifierKind(node.Kind) +} + +func IsModifierLike(node *Node) bool { + return IsModifier(node) || IsDecorator(node) +} + +func IsCompoundAssignment(token Kind) bool { + return token >= KindFirstCompoundAssignment && token <= KindLastCompoundAssignment +} + +func IsAssignmentExpression(node *Node, excludeCompoundAssignment bool) bool { + if node.Kind == KindBinaryExpression { + expr := node.AsBinaryExpression() + return (expr.OperatorToken.Kind == KindEqualsToken || !excludeCompoundAssignment && IsAssignmentOperator(expr.OperatorToken.Kind)) && + IsLeftHandSideExpression(expr.Left) + } + return false +} + +func GetRightMostAssignedExpression(node *Node) *Node { + for IsAssignmentExpression(node, false /*excludeCompoundAssignment*/) { + node = node.AsBinaryExpression().Right + } + return node +} + +func IsDestructuringAssignment(node *Node) bool { + if IsAssignmentExpression(node, true /*excludeCompoundAssignment*/) { + kind := node.AsBinaryExpression().Left.Kind + return kind == KindObjectLiteralExpression || kind == KindArrayLiteralExpression + } + return false +} + +func IsObjectBindingOrAssignmentElement(node *Node) bool { + switch node.Kind { + case KindBindingElement, + KindPropertyAssignment, + KindShorthandPropertyAssignment, + KindSpreadAssignment: + return true + } + return false +} + +func IsArrayBindingOrAssignmentElement(node *Node) bool { + switch node.Kind { + case KindBindingElement, + KindOmittedExpression, + KindSpreadElement, + KindArrayLiteralExpression, + KindObjectLiteralExpression, + KindIdentifier, + KindPropertyAccessExpression, + KindElementAccessExpression: + return true + } + return IsAssignmentExpression(node, true /*excludeCompoundAssignment*/) +} + +func IsBindingPattern(node *Node) bool { + return node.Kind == KindObjectBindingPattern || node.Kind == KindArrayBindingPattern +} + +func IsForInOrOfStatement(node *Node) bool { + return node != nil && (node.Kind == KindForInStatement || node.Kind == KindForOfStatement) +} + +// A node is an assignment target if it is on the left hand side of an '=' token, if it is parented by a property +// assignment in an object literal that is an assignment target, or if it is parented by an array literal that is +// an assignment target. Examples include 'a = xxx', '{ p: a } = xxx', '[{ a }] = xxx'. +// (Note that `p` is not a target in the above examples, only `a`.) +func IsAssignmentTarget(node *Node) bool { + return GetAssignmentTarget(node) != nil +} + +// Returns the BinaryExpression, PrefixUnaryExpression, PostfixUnaryExpression, or ForInOrOfStatement that references +// the given node as an assignment target +func GetAssignmentTarget(node *Node) *Node { + for { + parent := node.Parent + switch parent.Kind { + case KindBinaryExpression: + if IsAssignmentOperator(parent.AsBinaryExpression().OperatorToken.Kind) && parent.AsBinaryExpression().Left == node { + return parent + } + return nil + case KindPrefixUnaryExpression: + if parent.AsPrefixUnaryExpression().Operator == KindPlusPlusToken || parent.AsPrefixUnaryExpression().Operator == KindMinusMinusToken { + return parent + } + return nil + case KindPostfixUnaryExpression: + if parent.AsPostfixUnaryExpression().Operator == KindPlusPlusToken || parent.AsPostfixUnaryExpression().Operator == KindMinusMinusToken { + return parent + } + return nil + case KindForInStatement, KindForOfStatement: + if parent.Initializer() == node { + return parent + } + return nil + case KindParenthesizedExpression, KindArrayLiteralExpression, KindSpreadElement, KindNonNullExpression: + node = parent + case KindSpreadAssignment: + node = parent.Parent + case KindShorthandPropertyAssignment: + if parent.AsShorthandPropertyAssignment().Name() != node { + return nil + } + node = parent.Parent + case KindPropertyAssignment: + if parent.AsPropertyAssignment().Name() == node { + return nil + } + node = parent.Parent + default: + return nil + } + } +} + +func IsLogicalBinaryOperator(token Kind) bool { + return token == KindBarBarToken || token == KindAmpersandAmpersandToken +} + +func IsLogicalOrCoalescingBinaryOperator(token Kind) bool { + return IsLogicalBinaryOperator(token) || token == KindQuestionQuestionToken +} + +func IsLogicalOrCoalescingBinaryExpression(expr *Node) bool { + return IsBinaryExpression(expr) && IsLogicalOrCoalescingBinaryOperator(expr.AsBinaryExpression().OperatorToken.Kind) +} + +func IsLogicalOrCoalescingAssignmentExpression(expr *Node) bool { + return IsBinaryExpression(expr) && IsLogicalOrCoalescingAssignmentOperator(expr.AsBinaryExpression().OperatorToken.Kind) +} + +func IsLogicalExpression(node *Node) bool { + for { + if node.Kind == KindParenthesizedExpression { + node = node.Expression() + } else if node.Kind == KindPrefixUnaryExpression && node.AsPrefixUnaryExpression().Operator == KindExclamationToken { + node = node.AsPrefixUnaryExpression().Operand + } else { + return IsLogicalOrCoalescingBinaryExpression(node) + } + } +} + +func IsAccessor(node *Node) bool { + return node.Kind == KindGetAccessor || node.Kind == KindSetAccessor +} + +func IsPropertyNameLiteral(node *Node) bool { + switch node.Kind { + case KindIdentifier, + KindStringLiteral, + KindNoSubstitutionTemplateLiteral, + KindNumericLiteral: + return true + } + return false +} + +func IsMemberName(node *Node) bool { + return node.Kind == KindIdentifier || node.Kind == KindPrivateIdentifier +} + +func IsEntityName(node *Node) bool { + return node.Kind == KindIdentifier || node.Kind == KindQualifiedName +} + +func IsPropertyName(node *Node) bool { + switch node.Kind { + case KindIdentifier, + KindPrivateIdentifier, + KindStringLiteral, + KindNumericLiteral, + KindComputedPropertyName: + return true + } + return false +} + +// Return true if the given identifier is classified as an IdentifierName by inspecting the parent of the node +func IsIdentifierName(node *Node) bool { + parent := node.Parent + switch parent.Kind { + case KindPropertyDeclaration, KindPropertySignature, KindMethodDeclaration, KindMethodSignature, KindGetAccessor, + KindSetAccessor, KindEnumMember, KindPropertyAssignment, KindPropertyAccessExpression: + return parent.Name() == node + case KindQualifiedName: + return parent.AsQualifiedName().Right == node + case KindBindingElement: + return parent.PropertyName() == node + case KindImportSpecifier: + return parent.PropertyName() == node + case KindExportSpecifier, KindJsxAttribute, KindJsxSelfClosingElement, KindJsxOpeningElement, KindJsxClosingElement: + return true + } + return false +} + +func IsPushOrUnshiftIdentifier(node *Node) bool { + text := node.Text() + return text == "push" || text == "unshift" +} + +func IsBooleanLiteral(node *Node) bool { + return node.Kind == KindTrueKeyword || node.Kind == KindFalseKeyword +} + +func IsLiteralExpression(node *Node) bool { + return IsLiteralKind(node.Kind) +} + +func IsStringLiteralLike(node *Node) bool { + switch node.Kind { + case KindStringLiteral, KindNoSubstitutionTemplateLiteral: + return true + } + return false +} + +func IsStringOrNumericLiteralLike(node *Node) bool { + return IsStringLiteralLike(node) || IsNumericLiteral(node) +} + +func IsSignedNumericLiteral(node *Node) bool { + if node.Kind == KindPrefixUnaryExpression { + node := node.AsPrefixUnaryExpression() + return (node.Operator == KindPlusToken || node.Operator == KindMinusToken) && IsNumericLiteral(node.Operand) + } + return false +} + +// Determines if a node is part of an OptionalChain +func IsOptionalChain(node *Node) bool { + if node.Flags&NodeFlagsOptionalChain != 0 { + switch node.Kind { + case KindPropertyAccessExpression, + KindElementAccessExpression, + KindCallExpression, + KindNonNullExpression: + return true + } + } + return false +} + +func getQuestionDotToken(node *Expression) *TokenNode { + return node.QuestionDotToken() +} + +// Determines if node is the root expression of an OptionalChain +func IsOptionalChainRoot(node *Expression) bool { + return IsOptionalChain(node) && !IsNonNullExpression(node) && getQuestionDotToken(node) != nil +} + +// Determines whether a node is the outermost `OptionalChain` in an ECMAScript `OptionalExpression`: +// +// 1. For `a?.b.c`, the outermost chain is `a?.b.c` (`c` is the end of the chain starting at `a?.`) +// 2. For `a?.b!`, the outermost chain is `a?.b` (`b` is the end of the chain starting at `a?.`) +// 3. For `(a?.b.c).d`, the outermost chain is `a?.b.c` (`c` is the end of the chain starting at `a?.` since parens end the chain) +// 4. For `a?.b.c?.d`, both `a?.b.c` and `a?.b.c?.d` are outermost (`c` is the end of the chain starting at `a?.`, and `d` is +// the end of the chain starting at `c?.`) +// 5. For `a?.(b?.c).d`, both `b?.c` and `a?.(b?.c)d` are outermost (`c` is the end of the chain starting at `b`, and `d` is +// the end of the chain starting at `a?.`) +func IsOutermostOptionalChain(node *Expression) bool { + parent := node.Parent + return !IsOptionalChain(parent) || // cases 1, 2, and 3 + IsOptionalChainRoot(parent) || // case 4 + node != parent.Expression() // case 5 +} + +// Determines whether a node is the expression preceding an optional chain (i.e. `a` in `a?.b`). +func IsExpressionOfOptionalChainRoot(node *Node) bool { + return IsOptionalChainRoot(node.Parent) && node.Parent.Expression() == node +} + +func IsNullishCoalesce(node *Node) bool { + return node.Kind == KindBinaryExpression && node.AsBinaryExpression().OperatorToken.Kind == KindQuestionQuestionToken +} + +func IsAssertionExpression(node *Node) bool { + kind := node.Kind + return kind == KindTypeAssertionExpression || kind == KindAsExpression +} + +func isLeftHandSideExpressionKind(kind Kind) bool { + switch kind { + case KindPropertyAccessExpression, KindElementAccessExpression, KindNewExpression, KindCallExpression, + KindJsxElement, KindJsxSelfClosingElement, KindJsxFragment, KindTaggedTemplateExpression, KindArrayLiteralExpression, + KindParenthesizedExpression, KindObjectLiteralExpression, KindClassExpression, KindFunctionExpression, KindIdentifier, + KindPrivateIdentifier, KindRegularExpressionLiteral, KindNumericLiteral, KindBigIntLiteral, KindStringLiteral, + KindNoSubstitutionTemplateLiteral, KindTemplateExpression, KindFalseKeyword, KindNullKeyword, KindThisKeyword, + KindTrueKeyword, KindSuperKeyword, KindNonNullExpression, KindExpressionWithTypeArguments, KindMetaProperty, + KindImportKeyword, KindMissingDeclaration: + return true + } + return false +} + +// Determines whether a node is a LeftHandSideExpression based only on its kind. +func IsLeftHandSideExpression(node *Node) bool { + return isLeftHandSideExpressionKind(SkipPartiallyEmittedExpressions(node).Kind) +} + +func isUnaryExpressionKind(kind Kind) bool { + switch kind { + case KindPrefixUnaryExpression, + KindPostfixUnaryExpression, + KindDeleteExpression, + KindTypeOfExpression, + KindVoidExpression, + KindAwaitExpression, + KindTypeAssertionExpression: + return true + } + return isLeftHandSideExpressionKind(kind) +} + +// Determines whether a node is a UnaryExpression based only on its kind. +func IsUnaryExpression(node *Node) bool { + return isUnaryExpressionKind(SkipPartiallyEmittedExpressions(node).Kind) +} + +func isExpressionKind(kind Kind) bool { + switch kind { + case KindConditionalExpression, + KindYieldExpression, + KindArrowFunction, + KindBinaryExpression, + KindSpreadElement, + KindAsExpression, + KindOmittedExpression, + KindPartiallyEmittedExpression, + KindSatisfiesExpression: + return true + } + return isUnaryExpressionKind(kind) +} + +// Determines whether a node is an expression based only on its kind. +func IsExpression(node *Node) bool { + return isExpressionKind(SkipPartiallyEmittedExpressions(node).Kind) +} + +func IsCommaExpression(node *Node) bool { + return node.Kind == KindBinaryExpression && node.AsBinaryExpression().OperatorToken.Kind == KindCommaToken +} + +func IsCommaSequence(node *Node) bool { + return IsCommaExpression(node) +} + +func IsIterationStatement(node *Node, lookInLabeledStatements bool) bool { + switch node.Kind { + case KindForStatement, + KindForInStatement, + KindForOfStatement, + KindDoStatement, + KindWhileStatement: + return true + case KindLabeledStatement: + return lookInLabeledStatements && IsIterationStatement(node.Statement(), lookInLabeledStatements) + } + + return false +} + +// Determines if a node is a property or element access expression +func IsAccessExpression(node *Node) bool { + return node.Kind == KindPropertyAccessExpression || node.Kind == KindElementAccessExpression +} + +func isFunctionLikeDeclarationKind(kind Kind) bool { + switch kind { + case KindFunctionDeclaration, + KindMethodDeclaration, + KindConstructor, + KindGetAccessor, + KindSetAccessor, + KindFunctionExpression, + KindArrowFunction: + return true + } + return false +} + +// Determines if a node is function-like (but is not a signature declaration) +func IsFunctionLikeDeclaration(node *Node) bool { + // TODO(rbuckton): Move `node != nil` test to call sites + return node != nil && isFunctionLikeDeclarationKind(node.Kind) +} + +func IsFunctionLikeKind(kind Kind) bool { + switch kind { + case KindMethodSignature, + KindCallSignature, + KindJSDocSignature, + KindConstructSignature, + KindIndexSignature, + KindFunctionType, + KindConstructorType: + return true + } + return isFunctionLikeDeclarationKind(kind) +} + +// Determines if a node is function- or signature-like. +func IsFunctionLike(node *Node) bool { + // TODO(rbuckton): Move `node != nil` test to call sites + return node != nil && IsFunctionLikeKind(node.Kind) +} + +func IsFunctionLikeOrClassStaticBlockDeclaration(node *Node) bool { + return node != nil && (IsFunctionLike(node) || IsClassStaticBlockDeclaration(node)) +} + +func IsFunctionOrSourceFile(node *Node) bool { + return IsFunctionLike(node) || IsSourceFile(node) +} + +func IsClassLike(node *Node) bool { + return node.Kind == KindClassDeclaration || node.Kind == KindClassExpression +} + +func IsClassOrInterfaceLike(node *Node) bool { + return node.Kind == KindClassDeclaration || node.Kind == KindClassExpression || node.Kind == KindInterfaceDeclaration +} + +func IsClassElement(node *Node) bool { + switch node.Kind { + case KindConstructor, + KindPropertyDeclaration, + KindMethodDeclaration, + KindGetAccessor, + KindSetAccessor, + KindIndexSignature, + KindClassStaticBlockDeclaration, + KindSemicolonClassElement: + return true + } + return false +} + +func IsMethodOrAccessor(node *Node) bool { + switch node.Kind { + case KindMethodDeclaration, KindGetAccessor, KindSetAccessor: + return true + } + return false +} + +func IsPrivateIdentifierClassElementDeclaration(node *Node) bool { + return (IsPropertyDeclaration(node) || IsMethodOrAccessor(node)) && IsPrivateIdentifier(node.Name()) +} + +func IsObjectLiteralOrClassExpressionMethodOrAccessor(node *Node) bool { + kind := node.Kind + return (kind == KindMethodDeclaration || kind == KindGetAccessor || kind == KindSetAccessor) && + (node.Parent.Kind == KindObjectLiteralExpression || node.Parent.Kind == KindClassExpression) +} + +func IsTypeElement(node *Node) bool { + switch node.Kind { + case KindConstructSignature, + KindCallSignature, + KindPropertySignature, + KindMethodSignature, + KindIndexSignature, + KindGetAccessor, + KindSetAccessor, + KindNotEmittedTypeElement: + return true + } + return false +} + +func IsObjectLiteralElement(node *Node) bool { + switch node.Kind { + case KindPropertyAssignment, + KindShorthandPropertyAssignment, + KindSpreadAssignment, + KindMethodDeclaration, + KindGetAccessor, + KindSetAccessor: + return true + } + return false +} + +func IsObjectLiteralMethod(node *Node) bool { + return node != nil && node.Kind == KindMethodDeclaration && node.Parent.Kind == KindObjectLiteralExpression +} + +func IsAutoAccessorPropertyDeclaration(node *Node) bool { + return IsPropertyDeclaration(node) && HasAccessorModifier(node) +} + +func IsParameterPropertyDeclaration(node *Node, parent *Node) bool { + return IsParameterDeclaration(node) && HasSyntacticModifier(node, ModifierFlagsParameterPropertyModifier) && parent.Kind == KindConstructor +} + +func IsJsxChild(node *Node) bool { + switch node.Kind { + case KindJsxElement, + KindJsxExpression, + KindJsxSelfClosingElement, + KindJsxText, + KindJsxFragment: + return true + } + return false +} + +func IsJsxAttributeLike(node *Node) bool { + return IsJsxAttribute(node) || IsJsxSpreadAttribute(node) +} + +func isDeclarationStatementKind(kind Kind) bool { + switch kind { + case KindFunctionDeclaration, + KindMissingDeclaration, + KindClassDeclaration, + KindInterfaceDeclaration, + KindTypeAliasDeclaration, + KindJSTypeAliasDeclaration, + KindEnumDeclaration, + KindModuleDeclaration, + KindImportDeclaration, + KindJSImportDeclaration, + KindImportEqualsDeclaration, + KindExportDeclaration, + KindExportAssignment, + KindNamespaceExportDeclaration: + return true + } + return false +} + +// Determines whether a node is a DeclarationStatement. Ideally this does not use Parent pointers, but it may use them +// to rule out a Block node that is part of `try` or `catch` or is the Block-like body of a function. +// +// NOTE: ECMA262 would just call this a Declaration +func IsDeclarationStatement(node *Node) bool { + return isDeclarationStatementKind(node.Kind) +} + +func isStatementKindButNotDeclarationKind(kind Kind) bool { + switch kind { + case KindBreakStatement, + KindContinueStatement, + KindDebuggerStatement, + KindDoStatement, + KindExpressionStatement, + KindEmptyStatement, + KindForInStatement, + KindForOfStatement, + KindForStatement, + KindIfStatement, + KindLabeledStatement, + KindReturnStatement, + KindSwitchStatement, + KindThrowStatement, + KindTryStatement, + KindVariableStatement, + KindWhileStatement, + KindWithStatement, + KindNotEmittedStatement: + return true + } + return false +} + +// Determines whether a node is a Statement that is not also a Declaration. Ideally this does not use Parent pointers, +// but it may use them to rule out a Block node that is part of `try` or `catch` or is the Block-like body of a function. +// +// NOTE: ECMA262 would just call this a Statement +func IsStatementButNotDeclaration(node *Node) bool { + return isStatementKindButNotDeclarationKind(node.Kind) +} + +// Determines whether a node is a Statement. Ideally this does not use Parent pointers, but it may use +// them to rule out a Block node that is part of `try` or `catch` or is the Block-like body of a function. +// +// NOTE: ECMA262 would call this either a StatementListItem or ModuleListItem +func IsStatement(node *Node) bool { + kind := node.Kind + return isStatementKindButNotDeclarationKind(kind) || isDeclarationStatementKind(kind) || isBlockStatement(node) +} + +// Determines whether a node is a BlockStatement. If parents are available, this ensures the Block is +// not part of a `try` statement, `catch` clause, or the Block-like body of a function +func isBlockStatement(node *Node) bool { + if node.Kind != KindBlock { + return false + } + if node.Parent != nil && (node.Parent.Kind == KindTryStatement || node.Parent.Kind == KindCatchClause) { + return false + } + return !IsFunctionBlock(node) +} + +// Determines whether a node is the Block-like body of a function by walking the parent of the node +func IsFunctionBlock(node *Node) bool { + return node != nil && node.Kind == KindBlock && node.Parent != nil && IsFunctionLike(node.Parent) +} + +func IsBlockOrCatchScoped(declaration *Node) bool { + return GetCombinedNodeFlags(declaration)&NodeFlagsBlockScoped != 0 || IsCatchClauseVariableDeclarationOrBindingElement(declaration) +} + +func IsCatchClauseVariableDeclarationOrBindingElement(declaration *Node) bool { + node := GetRootDeclaration(declaration) + return node.Kind == KindVariableDeclaration && node.Parent.Kind == KindCatchClause +} + +func IsTypeNodeKind(kind Kind) bool { + switch kind { + case KindAnyKeyword, + KindUnknownKeyword, + KindNumberKeyword, + KindBigIntKeyword, + KindObjectKeyword, + KindBooleanKeyword, + KindStringKeyword, + KindSymbolKeyword, + KindVoidKeyword, + KindUndefinedKeyword, + KindNeverKeyword, + KindIntrinsicKeyword, + KindExpressionWithTypeArguments, + KindJSDocAllType, + KindJSDocNullableType, + KindJSDocNonNullableType, + KindJSDocOptionalType, + KindJSDocVariadicType: + return true + } + return kind >= KindFirstTypeNode && kind <= KindLastTypeNode +} + +func IsTypeNode(node *Node) bool { + return IsTypeNodeKind(node.Kind) +} + +func IsJSDocKind(kind Kind) bool { + return KindFirstJSDocNode <= kind && kind <= KindLastJSDocNode +} + +func IsJSDocTypeAssertion(node *Node) bool { + if node == nil || !IsParenthesizedExpression(node) || !IsInJSFile(node) { + return false + } + expr := node.Expression() + return IsAsExpression(expr) && expr.Type() != nil && expr.Type().Flags&NodeFlagsReparsed != 0 +} + +func IsPrologueDirective(node *Node) bool { + return node.Kind == KindExpressionStatement && + node.Expression().Kind == KindStringLiteral +} + +type OuterExpressionKinds uint16 + +const ( + OEKParentheses OuterExpressionKinds = 1 << 0 + OEKTypeAssertions OuterExpressionKinds = 1 << 1 + OEKNonNullAssertions OuterExpressionKinds = 1 << 2 + OEKPartiallyEmittedExpressions OuterExpressionKinds = 1 << 3 + OEKExpressionsWithTypeArguments OuterExpressionKinds = 1 << 4 + OEKSatisfies OuterExpressionKinds = 1 << 5 + OEKExcludeJSDocTypeAssertion OuterExpressionKinds = 1 << 6 + OEKAssignments OuterExpressionKinds = 1 << 7 + OEKComma OuterExpressionKinds = 1 << 8 + OEKAssertions = OEKTypeAssertions | OEKNonNullAssertions | OEKSatisfies + OEKAll = OEKParentheses | OEKAssertions | OEKPartiallyEmittedExpressions | OEKExpressionsWithTypeArguments + OEKAllExceptAssertionsOrExpressionsWithTypeArguments = OEKAll &^ OEKAssertions &^ OEKExpressionsWithTypeArguments + OEKExpressionTypePassthrough = OEKParentheses | OEKAssignments | OEKComma +) + +// Determines whether node is an "outer expression" of the provided kinds +func IsOuterExpression(node *Expression, kinds OuterExpressionKinds) bool { + switch node.Kind { + case KindParenthesizedExpression: + return kinds&OEKParentheses != 0 && !(kinds&OEKExcludeJSDocTypeAssertion != 0 && IsJSDocTypeAssertion(node)) + case KindTypeAssertionExpression, KindAsExpression: + return kinds&OEKTypeAssertions != 0 + case KindSatisfiesExpression: + return kinds&(OEKExpressionsWithTypeArguments|OEKSatisfies) != 0 + case KindExpressionWithTypeArguments: + return kinds&OEKExpressionsWithTypeArguments != 0 + case KindNonNullExpression: + return kinds&OEKNonNullAssertions != 0 + case KindPartiallyEmittedExpression: + return kinds&OEKPartiallyEmittedExpressions != 0 + case KindBinaryExpression: + switch node.AsBinaryExpression().OperatorToken.Kind { + case KindEqualsToken: + return kinds&OEKAssignments != 0 + case KindCommaToken: + return kinds&OEKComma != 0 + } + } + return false +} + +// Descends into an expression, skipping past "outer expressions" of the provided kinds +func SkipOuterExpressions(node *Expression, kinds OuterExpressionKinds) *Expression { + for IsOuterExpression(node, kinds) { + if IsBinaryExpression(node) { + node = node.AsBinaryExpression().Right + } else { + node = node.Expression() + } + } + return node +} + +// Skips past the parentheses of an expression +func SkipParentheses(node *Expression) *Expression { + return SkipOuterExpressions(node, OEKParentheses) +} + +func SkipTypeParentheses(node *Node) *Node { + for IsParenthesizedTypeNode(node) { + node = node.Type() + } + return node +} + +func SkipPartiallyEmittedExpressions(node *Expression) *Expression { + return SkipOuterExpressions(node, OEKPartiallyEmittedExpressions) +} + +// Walks up the parents of a parenthesized expression to find the containing node +func WalkUpParenthesizedExpressions(node *Expression) *Node { + for node != nil && node.Kind == KindParenthesizedExpression { + node = node.Parent + } + return node +} + +// Walks up the parents of a parenthesized type to find the containing node +func WalkUpParenthesizedTypes(node *TypeNode) *Node { + for node != nil && node.Kind == KindParenthesizedType { + node = node.Parent + } + return node +} + +// Walks up the parents of a node to find the containing SourceFile +func GetSourceFileOfNode(node *Node) *SourceFile { + for node != nil { + if node.Kind == KindSourceFile { + return node.AsSourceFile() + } + node = node.Parent + } + return nil +} + +var setParentInChildrenPool = sync.Pool{ + New: func() any { + return newParentInChildrenSetter() + }, +} + +func newParentInChildrenSetter() func(node *Node) bool { + // Consolidate state into one allocation. + // Similar to https://go.dev/cl/552375. + var state struct { + parent *Node + visit func(*Node) bool + } + + state.visit = func(node *Node) bool { + if state.parent != nil { + node.Parent = state.parent + } + saveParent := state.parent + state.parent = node + node.ForEachChild(state.visit) + state.parent = saveParent + return false + } + + return state.visit +} + +func SetParentInChildren(node *Node) { + fn := setParentInChildrenPool.Get().(func(node *Node) bool) + defer setParentInChildrenPool.Put(fn) + fn(node) +} + +// This should never be called outside the parser +func SetImportsOfSourceFile(node *SourceFile, imports []*LiteralLikeNode) { + node.imports = imports +} + +// Walks up the parents of a node to find the ancestor that matches the callback +func FindAncestor(node *Node, callback func(*Node) bool) *Node { + for node != nil { + if callback(node) { + return node + } + node = node.Parent + } + return nil +} + +// Walks up the parents of a node to find the ancestor that matches the kind +func FindAncestorKind(node *Node, kind Kind) *Node { + for node != nil { + if node.Kind == kind { + return node + } + node = node.Parent + } + return nil +} + +type FindAncestorResult int32 + +const ( + FindAncestorFalse FindAncestorResult = iota + FindAncestorTrue + FindAncestorQuit +) + +func ToFindAncestorResult(b bool) FindAncestorResult { + if b { + return FindAncestorTrue + } + return FindAncestorFalse +} + +// Walks up the parents of a node to find the ancestor that matches the callback +func FindAncestorOrQuit(node *Node, callback func(*Node) FindAncestorResult) *Node { + for node != nil { + switch callback(node) { + case FindAncestorQuit: + return nil + case FindAncestorTrue: + return node + } + node = node.Parent + } + return nil +} + +func IsNodeDescendantOf(node *Node, ancestor *Node) bool { + for node != nil { + if node == ancestor { + return true + } + node = node.Parent + } + return false +} + +func ModifierToFlag(token Kind) ModifierFlags { + switch token { + case KindStaticKeyword: + return ModifierFlagsStatic + case KindPublicKeyword: + return ModifierFlagsPublic + case KindProtectedKeyword: + return ModifierFlagsProtected + case KindPrivateKeyword: + return ModifierFlagsPrivate + case KindAbstractKeyword: + return ModifierFlagsAbstract + case KindAccessorKeyword: + return ModifierFlagsAccessor + case KindExportKeyword: + return ModifierFlagsExport + case KindDeclareKeyword: + return ModifierFlagsAmbient + case KindConstKeyword: + return ModifierFlagsConst + case KindDefaultKeyword: + return ModifierFlagsDefault + case KindAsyncKeyword: + return ModifierFlagsAsync + case KindReadonlyKeyword: + return ModifierFlagsReadonly + case KindOverrideKeyword: + return ModifierFlagsOverride + case KindInKeyword: + return ModifierFlagsIn + case KindOutKeyword: + return ModifierFlagsOut + case KindDecorator: + return ModifierFlagsDecorator + } + return ModifierFlagsNone +} + +func ModifiersToFlags(modifiers []*Node) ModifierFlags { + var flags ModifierFlags + for _, modifier := range modifiers { + flags |= ModifierToFlag(modifier.Kind) + } + return flags +} + +func HasSyntacticModifier(node *Node, flags ModifierFlags) bool { + return node.ModifierFlags()&flags != 0 +} + +func HasAccessorModifier(node *Node) bool { + return HasSyntacticModifier(node, ModifierFlagsAccessor) +} + +func HasStaticModifier(node *Node) bool { + return HasSyntacticModifier(node, ModifierFlagsStatic) +} + +func IsStatic(node *Node) bool { + // https://tc39.es/ecma262/#sec-static-semantics-isstatic + return IsClassElement(node) && HasStaticModifier(node) || IsClassStaticBlockDeclaration(node) +} + +func CanHaveSymbol(node *Node) bool { + switch node.Kind { + case KindArrowFunction, KindBinaryExpression, KindBindingElement, KindCallExpression, KindCallSignature, + KindClassDeclaration, KindClassExpression, KindClassStaticBlockDeclaration, KindConstructor, KindConstructorType, + KindConstructSignature, KindElementAccessExpression, KindEnumDeclaration, KindEnumMember, KindExportAssignment, + KindExportDeclaration, KindExportSpecifier, KindFunctionDeclaration, KindFunctionExpression, KindFunctionType, + KindGetAccessor, KindImportClause, KindImportEqualsDeclaration, KindImportSpecifier, KindIndexSignature, + KindInterfaceDeclaration, KindJSTypeAliasDeclaration, + KindJsxAttribute, KindJsxAttributes, KindJsxSpreadAttribute, KindMappedType, KindMethodDeclaration, + KindMethodSignature, KindModuleDeclaration, KindNamedTupleMember, KindNamespaceExport, KindNamespaceExportDeclaration, + KindNamespaceImport, KindNewExpression, KindNoSubstitutionTemplateLiteral, KindNumericLiteral, KindObjectLiteralExpression, + KindParameter, KindPropertyAccessExpression, KindPropertyAssignment, KindPropertyDeclaration, KindPropertySignature, + KindSetAccessor, KindShorthandPropertyAssignment, KindSourceFile, KindSpreadAssignment, KindStringLiteral, + KindTypeAliasDeclaration, KindTypeLiteral, KindTypeParameter, KindVariableDeclaration: + return true + } + return false +} + +func CanHaveIllegalDecorators(node *Node) bool { + switch node.Kind { + case KindPropertyAssignment, KindShorthandPropertyAssignment, + KindFunctionDeclaration, KindConstructor, + KindIndexSignature, KindClassStaticBlockDeclaration, + KindMissingDeclaration, KindVariableStatement, + KindInterfaceDeclaration, KindTypeAliasDeclaration, + KindEnumDeclaration, KindModuleDeclaration, + KindImportEqualsDeclaration, KindImportDeclaration, KindJSImportDeclaration, + KindNamespaceExportDeclaration, KindExportDeclaration, + KindExportAssignment: + return true + } + return false +} + +func CanHaveIllegalModifiers(node *Node) bool { + switch node.Kind { + case KindClassStaticBlockDeclaration, + KindPropertyAssignment, + KindShorthandPropertyAssignment, + KindMissingDeclaration, + KindNamespaceExportDeclaration: + return true + } + return false +} + +func CanHaveModifiers(node *Node) bool { + switch node.Kind { + case KindTypeParameter, + KindParameter, + KindPropertySignature, + KindPropertyDeclaration, + KindMethodSignature, + KindMethodDeclaration, + KindConstructor, + KindGetAccessor, + KindSetAccessor, + KindIndexSignature, + KindConstructorType, + KindFunctionExpression, + KindArrowFunction, + KindClassExpression, + KindVariableStatement, + KindFunctionDeclaration, + KindClassDeclaration, + KindInterfaceDeclaration, + KindTypeAliasDeclaration, + KindEnumDeclaration, + KindModuleDeclaration, + KindImportEqualsDeclaration, + KindImportDeclaration, + KindJSImportDeclaration, + KindExportAssignment, + KindExportDeclaration: + return true + } + return false +} + +func CanHaveDecorators(node *Node) bool { + switch node.Kind { + case KindParameter, + KindPropertyDeclaration, + KindMethodDeclaration, + KindGetAccessor, + KindSetAccessor, + KindClassExpression, + KindClassDeclaration: + return true + } + return false +} + +func IsFunctionOrModuleBlock(node *Node) bool { + return IsSourceFile(node) || IsModuleBlock(node) || IsBlock(node) && IsFunctionLike(node.Parent) +} + +func IsFunctionExpressionOrArrowFunction(node *Node) bool { + return IsFunctionExpression(node) || IsArrowFunction(node) +} + +// Warning: This has the same semantics as the forEach family of functions in that traversal terminates +// in the event that 'visitor' returns true. +func ForEachReturnStatement(body *Node, visitor func(stmt *Node) bool) bool { + var traverse func(*Node) bool + traverse = func(node *Node) bool { + switch node.Kind { + case KindReturnStatement: + return visitor(node) + case KindCaseBlock, KindBlock, KindIfStatement, KindDoStatement, KindWhileStatement, KindForStatement, KindForInStatement, + KindForOfStatement, KindWithStatement, KindSwitchStatement, KindCaseClause, KindDefaultClause, KindLabeledStatement, + KindTryStatement, KindCatchClause: + return node.ForEachChild(traverse) + } + return false + } + return traverse(body) +} + +func GetRootDeclaration(node *Node) *Node { + for node.Kind == KindBindingElement { + node = node.Parent.Parent + } + return node +} + +func getCombinedFlags[T ~uint32](node *Node, getFlags func(*Node) T) T { + node = GetRootDeclaration(node) + flags := getFlags(node) + if node.Kind == KindVariableDeclaration { + node = node.Parent + } + if node != nil && node.Kind == KindVariableDeclarationList { + flags |= getFlags(node) + node = node.Parent + } + if node != nil && node.Kind == KindVariableStatement { + flags |= getFlags(node) + } + return flags +} + +func GetCombinedModifierFlags(node *Node) ModifierFlags { + return getCombinedFlags(node, (*Node).ModifierFlags) +} + +func GetCombinedNodeFlags(node *Node) NodeFlags { + return getCombinedFlags(node, getNodeFlags) +} + +func getNodeFlags(node *Node) NodeFlags { + return node.Flags +} + +// Gets whether a bound `VariableDeclaration` or `VariableDeclarationList` is part of an `await using` declaration. +func IsVarAwaitUsing(node *Node) bool { + return GetCombinedNodeFlags(node)&NodeFlagsBlockScoped == NodeFlagsAwaitUsing +} + +// Gets whether a bound `VariableDeclaration` or `VariableDeclarationList` is part of a `using` declaration. +func IsVarUsing(node *Node) bool { + return GetCombinedNodeFlags(node)&NodeFlagsBlockScoped == NodeFlagsUsing +} + +// GetJSDocDeprecatedTag returns the first @deprecated JSDoc tag for the given node, or nil if none exists. +func GetJSDocDeprecatedTag(node *Node) *Node { + for _, jsdoc := range node.JSDoc(nil) { + tags := jsdoc.AsJSDoc().Tags + if tags != nil { + for _, tag := range tags.Nodes { + if IsJSDocDeprecatedTag(tag) { + return tag + } + } + } + } + return nil +} + +// IsDeprecatedDeclaration reports whether the given declaration is marked as @deprecated. +// It checks NodeFlagsPossiblyContainsDeprecatedTag on combined node flags, then confirms +// by walking up to find the node with the flag and performing a JSDoc lookup. +func IsDeprecatedDeclaration(declaration *Node) bool { + return IsDeprecatedDeclarationWithCachedFlags(declaration, GetCombinedNodeFlags(declaration)) +} + +// IsDeprecatedDeclarationWithCachedFlags is the core logic for IsDeprecatedDeclaration, +// parameterized on pre-computed combined flags so the checker can supply cached flags. +func IsDeprecatedDeclarationWithCachedFlags(declaration *Node, combinedFlags NodeFlags) bool { + if combinedFlags&NodeFlagsPossiblyContainsDeprecatedTag == 0 { + return false + } + // Walk up to find the node that directly has the flag, since JSDoc is + // attached to that node (e.g. VariableStatement, not VariableDeclaration). + for n := declaration; n != nil; n = n.Parent { + if n.Flags&NodeFlagsPossiblyContainsDeprecatedTag != 0 { + return GetJSDocDeprecatedTag(n) != nil + } + } + return false +} + +// Gets whether a bound `VariableDeclaration` or `VariableDeclarationList` is part of a `const` declaration. +func IsVarConst(node *Node) bool { + return GetCombinedNodeFlags(node)&NodeFlagsBlockScoped == NodeFlagsConst +} + +// Gets whether a bound `VariableDeclaration` or `VariableDeclarationList` is part of a `const`, `using` or `await using` declaration. +func IsVarConstLike(node *Node) bool { + switch GetCombinedNodeFlags(node) & NodeFlagsBlockScoped { + case NodeFlagsConst, NodeFlagsUsing, NodeFlagsAwaitUsing: + return true + } + return false +} + +// Gets whether a bound `VariableDeclaration` or `VariableDeclarationList` is part of a `let` declaration. +func IsVarLet(node *Node) bool { + return GetCombinedNodeFlags(node)&NodeFlagsBlockScoped == NodeFlagsLet +} + +func IsImportMeta(node *Node) bool { + if node.Kind == KindMetaProperty { + return node.AsMetaProperty().KeywordToken == KindImportKeyword && node.AsMetaProperty().Name().Text() == "meta" + } + return false +} + +func WalkUpBindingElementsAndPatterns(binding *Node) *Node { + node := binding.Parent + for IsBindingElement(node.Parent) { + node = node.Parent.Parent + } + return node.Parent +} + +func IsSourceFileJS(file *SourceFile) bool { + return file.ScriptKind == core.ScriptKindJS || file.ScriptKind == core.ScriptKindJSX +} + +func IsInJSFile(node *Node) bool { + return node != nil && node.Flags&NodeFlagsJavaScriptFile != 0 +} + +func IsDeclaration(node *Node) bool { + if node.Kind == KindTypeParameter { + return node.Parent != nil + } + return IsDeclarationNode(node) +} + +// True if `name` is the name of a declaration node +func IsDeclarationName(name *Node) bool { + return !IsSourceFile(name) && !IsBindingPattern(name) && IsDeclaration(name.Parent) && name.Parent.Name() == name +} + +// Like 'isDeclarationName', but returns true for LHS of `import { x as y }` or `export { x as y }`. +func IsDeclarationNameOrImportPropertyName(name *Node) bool { + switch name.Parent.Kind { + case KindImportSpecifier, KindExportSpecifier: + return IsIdentifier(name) || name.Kind == KindStringLiteral + default: + return IsDeclarationName(name) + } +} + +func IsLiteralComputedPropertyDeclarationName(node *Node) bool { + return IsStringOrNumericLiteralLike(node) && + node.Parent.Kind == KindComputedPropertyName && + IsDeclaration(node.Parent.Parent) +} + +func IsExternalModuleImportEqualsDeclaration(node *Node) bool { + return node.Kind == KindImportEqualsDeclaration && node.AsImportEqualsDeclaration().ModuleReference.Kind == KindExternalModuleReference +} + +func IsModuleOrEnumDeclaration(node *Node) bool { + return node.Kind == KindModuleDeclaration || node.Kind == KindEnumDeclaration +} + +func IsLiteralImportTypeNode(node *Node) bool { + return IsImportTypeNode(node) && IsLiteralTypeNode(node.AsImportTypeNode().Argument) && IsStringLiteral(node.AsImportTypeNode().Argument.AsLiteralTypeNode().Literal) +} + +func IsJsxTagName(node *Node) bool { + parent := node.Parent + switch parent.Kind { + case KindJsxOpeningElement, KindJsxClosingElement, KindJsxSelfClosingElement: + return parent.TagName() == node + } + return false +} + +func IsImportOrExportSpecifier(node *Node) bool { + return IsImportSpecifier(node) || IsExportSpecifier(node) +} + +func IsVoidZero(node *Node) bool { + return IsVoidExpression(node) && IsNumericLiteral(node.Expression()) && node.Expression().Text() == "0" +} + +func IsExportsIdentifier(node *Node) bool { + return IsIdentifier(node) && node.Text() == "exports" +} + +func IsModuleIdentifier(node *Node) bool { + return IsIdentifier(node) && node.Text() == "module" +} + +func IsThisIdentifier(node *Node) bool { + return IsIdentifier(node) && node.Text() == "this" +} + +func IsThisParameter(node *Node) bool { + return IsParameterDeclaration(node) && node.Name() != nil && IsThisIdentifier(node.Name()) +} + +func IsBindableStaticAccessExpression(node *Node, excludeThisKeyword bool) bool { + return IsPropertyAccessExpression(node) && + (!excludeThisKeyword && node.Expression().Kind == KindThisKeyword || IsIdentifier(node.Name()) && IsBindableStaticNameExpression(node.Expression(), true /*excludeThisKeyword*/)) || + IsBindableStaticElementAccessExpression(node, excludeThisKeyword) +} + +func IsBindableStaticElementAccessExpression(node *Node, excludeThisKeyword bool) bool { + return IsLiteralLikeElementAccess(node) && + ((!excludeThisKeyword && node.Expression().Kind == KindThisKeyword) || + IsEntityNameExpression(node.Expression()) || + IsBindableStaticAccessExpression(node.Expression(), true /*excludeThisKeyword*/)) +} + +func IsPrototypeAccess(node *Node) bool { + if IsBindableStaticAccessExpression(node, false /*excludeThisKeyword*/) { + if name := GetElementOrPropertyAccessName(node); name != nil { + return name.Text() == "prototype" + } + } + return false +} + +func IsLiteralLikeElementAccess(node *Node) bool { + return IsElementAccessExpression(node) && IsStringOrNumericLiteralLike(node.AsElementAccessExpression().ArgumentExpression) +} + +func IsBindableStaticNameExpression(node *Node, excludeThisKeyword bool) bool { + return IsEntityNameExpression(node) || IsBindableStaticAccessExpression(node, excludeThisKeyword) +} + +// Does not handle signed numeric names like `a[+0]` - handling those would require handling prefix unary expressions +// throughout late binding handling as well, which is awkward (but ultimately probably doable if there is demand) +func GetElementOrPropertyAccessName(node *Node) *Node { + switch node.Kind { + case KindPropertyAccessExpression: + if IsIdentifier(node.Name()) { + return node.Name() + } + return nil + case KindElementAccessExpression: + if arg := SkipParentheses(node.AsElementAccessExpression().ArgumentExpression); IsStringOrNumericLiteralLike(arg) { + return arg + } + return nil + } + panic("Unhandled case in GetElementOrPropertyAccessName") +} + +func GetInitializerOfBinaryExpression(expr *BinaryExpression) *Expression { + for IsBinaryExpression(expr.Right) { + expr = expr.Right.AsBinaryExpression() + } + return expr.Right.Expression() +} + +func IsExpressionWithTypeArgumentsInClassExtendsClause(node *Node) bool { + return TryGetClassExtendingExpressionWithTypeArguments(node) != nil +} + +func TryGetClassExtendingExpressionWithTypeArguments(node *Node) *ClassLikeDeclaration { + cls, isImplements := TryGetClassImplementingOrExtendingExpressionWithTypeArguments(node) + if cls != nil && !isImplements { + return cls + } + return nil +} + +func TryGetClassImplementingOrExtendingExpressionWithTypeArguments(node *Node) (class *ClassLikeDeclaration, isImplements bool) { + if IsExpressionWithTypeArguments(node) { + if IsHeritageClause(node.Parent) && IsClassLike(node.Parent.Parent) { + return node.Parent.Parent, node.Parent.AsHeritageClause().Token == KindImplementsKeyword + } + } + return nil, false +} + +func GetNameOfDeclaration(declaration *Node) *Node { + if declaration == nil { + return nil + } + nonAssignedName := GetNonAssignedNameOfDeclaration(declaration) + if nonAssignedName != nil { + return nonAssignedName + } + if IsFunctionExpression(declaration) || IsArrowFunction(declaration) || IsClassExpression(declaration) { + return GetAssignedName(declaration) + } + return nil +} + +func GetNonAssignedNameOfDeclaration(declaration *Node) *Node { + // !!! + switch declaration.Kind { + case KindBinaryExpression, KindCallExpression: + switch GetAssignmentDeclarationKind(declaration) { + case JSDeclarationKindProperty, JSDeclarationKindThisProperty, JSDeclarationKindExportsProperty: + left := declaration.AsBinaryExpression().Left + if name := GetElementOrPropertyAccessName(left); name != nil { + return name + } + return left + case JSDeclarationKindObjectDefinePropertyValue, JSDeclarationKindObjectDefinePropertyExports: + return declaration.Arguments()[1] + } + return nil + case KindExportAssignment: + expr := declaration.Expression() + if IsIdentifier(expr) { + return expr + } + return nil + } + return declaration.Name() +} + +func GetAssignedName(node *Node) *Node { + parent := node.Parent + if parent != nil { + switch parent.Kind { + case KindPropertyAssignment: + return parent.AsPropertyAssignment().Name() + case KindBindingElement: + return parent.AsBindingElement().Name() + case KindBinaryExpression: + if node == parent.AsBinaryExpression().Right { + left := parent.AsBinaryExpression().Left + switch left.Kind { + case KindIdentifier: + return left + case KindPropertyAccessExpression: + return left.AsPropertyAccessExpression().Name() + case KindElementAccessExpression: + arg := SkipParentheses(left.AsElementAccessExpression().ArgumentExpression) + if IsStringOrNumericLiteralLike(arg) { + return arg + } + } + } + case KindVariableDeclaration: + name := parent.AsVariableDeclaration().Name() + if IsIdentifier(name) { + return name + } + } + } + return nil +} + +type JSDeclarationKind int + +const ( + JSDeclarationKindNone JSDeclarationKind = iota + // module.exports = expr, except for module.exports = exports + JSDeclarationKindModuleExports + // exports.name = expr + // module.exports.name = expr + JSDeclarationKindExportsProperty + // this.name = expr + JSDeclarationKindThisProperty + // F.name = expr, F[name] = expr, in JS or TS file + JSDeclarationKindProperty + // Object.defineProperty(x, 'name', { value: any, writable?: boolean (false by default) }); + // Object.defineProperty(x, 'name', { get: Function, set: Function }); + // Object.defineProperty(x, 'name', { get: Function }); + // Object.defineProperty(x, 'name', { set: Function }); + JSDeclarationKindObjectDefinePropertyValue + // Object.defineProperty(exports || module.exports, 'name', ...); + JSDeclarationKindObjectDefinePropertyExports +) + +func GetAssignmentDeclarationKind(node *Node) JSDeclarationKind { + switch node.Kind { + case KindBinaryExpression: + bin := node.AsBinaryExpression() + if bin.OperatorToken.Kind == KindEqualsToken && IsAccessExpression(bin.Left) { + if IsInJSFile(bin.Left) { + if IsModuleExportsAccessExpression(bin.Left) && !IsExportsIdentifier(bin.Right) { + return JSDeclarationKindModuleExports + } + if (IsModuleExportsAccessExpression(bin.Left.Expression()) || IsExportsIdentifier(bin.Left.Expression())) && + GetElementOrPropertyAccessName(bin.Left) != nil { + return JSDeclarationKindExportsProperty + } + if bin.Left.Expression().Kind == KindThisKeyword { + return JSDeclarationKindThisProperty + } + } + if bin.Left.Kind == KindPropertyAccessExpression && IsEntityNameExpressionEx(bin.Left.Expression(), IsInJSFile(bin.Left)) && IsIdentifier(bin.Left.Name()) || + bin.Left.Kind == KindElementAccessExpression && IsEntityNameExpressionEx(bin.Left.Expression(), IsInJSFile(bin.Left)) { + return JSDeclarationKindProperty + } + } + case KindCallExpression: + if IsInJSFile(node) && IsBindableObjectDefinePropertyCall(node) { + entityName := node.Arguments()[0] + if IsExportsIdentifier(entityName) || IsModuleExportsAccessExpression(entityName) { + return JSDeclarationKindObjectDefinePropertyExports + } + return JSDeclarationKindObjectDefinePropertyValue + } + } + return JSDeclarationKindNone +} + +func IsBindableObjectDefinePropertyCall(node *Node) bool { + if args := node.Arguments(); len(args) == 3 { + if expr := node.Expression(); IsPropertyAccessExpression(expr) && + IsIdentifier(expr.Expression()) && expr.Expression().Text() == "Object" && + expr.Name().Text() == "defineProperty" && + IsStringOrNumericLiteralLike(args[1]) && + IsBindableStaticNameExpression(args[0] /*excludeThisKeyword*/, true) { + return true + } + } + return false +} + +/** + * A declaration has a dynamic name if all of the following are true: + * 1. The declaration has a computed property name. + * 2. The computed name is *not* expressed as a StringLiteral. + * 3. The computed name is *not* expressed as a NumericLiteral. + * 4. The computed name is *not* expressed as a PlusToken or MinusToken + * immediately followed by a NumericLiteral. + */ +func HasDynamicName(declaration *Node) bool { + name := GetNameOfDeclaration(declaration) + return name != nil && IsDynamicName(name) +} + +func IsDynamicName(name *Node) bool { + var expr *Node + switch name.Kind { + case KindComputedPropertyName: + expr = name.Expression() + case KindElementAccessExpression: + expr = SkipParentheses(name.AsElementAccessExpression().ArgumentExpression) + default: + return false + } + return !IsStringOrNumericLiteralLike(expr) && !IsSignedNumericLiteral(expr) +} + +func IsEntityNameExpression(node *Node) bool { + return IsEntityNameExpressionEx(node, false /*allowJS*/) +} + +func IsEntityNameExpressionEx(node *Node, allowJS bool) bool { + return IsIdentifier(node) || + IsPropertyAccessEntityNameExpression(node, allowJS) || + allowJS && (node.Kind == KindThisKeyword || isElementAccessEntityNameExpression(node, allowJS)) +} + +func IsPropertyAccessEntityNameExpression(node *Node, allowJS bool) bool { + return IsPropertyAccessExpression(node) && IsIdentifier(node.Name()) && IsEntityNameExpressionEx(node.Expression(), allowJS) +} + +func isElementAccessEntityNameExpression(node *Node, allowJS bool) bool { + return IsElementAccessExpression(node) && IsStringOrNumericLiteralLike(node.AsElementAccessExpression().ArgumentExpression) && IsEntityNameExpressionEx(node.Expression(), allowJS) +} + +func IsDottedName(node *Node) bool { + switch node.Kind { + case KindIdentifier, KindThisKeyword, KindSuperKeyword, KindMetaProperty: + return true + case KindPropertyAccessExpression, KindParenthesizedExpression: + return IsDottedName(node.Expression()) + } + return false +} + +func HasSamePropertyAccessName(node1, node2 *Node) bool { + if node1.Kind == KindIdentifier && node2.Kind == KindIdentifier { + return node1.Text() == node2.Text() + } else if node1.Kind == KindPropertyAccessExpression && node2.Kind == KindPropertyAccessExpression { + return node1.AsPropertyAccessExpression().Name().Text() == node2.AsPropertyAccessExpression().Name().Text() && + HasSamePropertyAccessName(node1.Expression(), node2.Expression()) + } + return false +} + +func IsAmbientModule(node *Node) bool { + return IsModuleDeclaration(node) && (node.AsModuleDeclaration().Name().Kind == KindStringLiteral || IsGlobalScopeAugmentation(node)) +} + +func IsAmbientModuleSymbolName(s string) bool { + return strings.HasPrefix(s, "\"") && strings.HasSuffix(s, "\"") +} + +func IsExternalModule(file *SourceFile) bool { + return file.ExternalModuleIndicator != nil +} + +func IsExternalOrCommonJSModule(file *SourceFile) bool { + return file.ExternalModuleIndicator != nil || file.CommonJSModuleIndicator != nil +} + +// TODO: Should we deprecate `IsExternalOrCommonJSModule` in favor of this function? +func IsEffectiveExternalModule(node *SourceFile, compilerOptions *core.CompilerOptions) bool { + return IsExternalModule(node) || (isCommonJSContainingModuleKind(compilerOptions.GetEmitModuleKind()) && node.CommonJSModuleIndicator != nil) +} + +func isCommonJSContainingModuleKind(kind core.ModuleKind) bool { + return kind == core.ModuleKindCommonJS || core.ModuleKindNode16 <= kind && kind <= core.ModuleKindNodeNext +} + +func IsExternalModuleIndicator(node *Statement) bool { + // Exported top-level member indicates moduleness + return IsAnyImportOrReExport(node) || IsExportAssignment(node) || HasSyntacticModifier(node, ModifierFlagsExport) +} + +func IsExportNamespaceAsDefaultDeclaration(node *Node) bool { + if IsExportDeclaration(node) { + decl := node.AsExportDeclaration() + return IsNamespaceExport(decl.ExportClause) && ModuleExportNameIsDefault(decl.ExportClause.Name()) + } + return false +} + +func IsGlobalScopeAugmentation(node *Node) bool { + return IsModuleDeclaration(node) && node.AsModuleDeclaration().Keyword == KindGlobalKeyword +} + +func IsModuleAugmentationExternal(node *Node) bool { + // external module augmentation is a ambient module declaration that is either: + // - defined in the top level scope and source file is an external module + // - defined inside ambient module declaration located in the top level scope and source file not an external module + switch node.Parent.Kind { + case KindSourceFile: + return IsExternalModule(node.Parent.AsSourceFile()) + case KindModuleBlock: + grandParent := node.Parent.Parent + return IsAmbientModule(grandParent) && IsSourceFile(grandParent.Parent) && !IsExternalModule(grandParent.Parent.AsSourceFile()) + } + return false +} + +func IsModuleWithStringLiteralName(node *Node) bool { + return IsModuleDeclaration(node) && node.Name().Kind == KindStringLiteral +} + +func GetContainingClass(node *Node) *Node { + return FindAncestor(node.Parent, IsClassLike) +} + +func GetExtendsHeritageClauseElement(node *Node) *ExpressionWithTypeArgumentsNode { + return core.FirstOrNil(GetExtendsHeritageClauseElements(node)) +} + +func GetExtendsHeritageClauseElements(node *Node) []*ExpressionWithTypeArgumentsNode { + return GetHeritageElements(node, KindExtendsKeyword) +} + +func GetImplementsHeritageClauseElements(node *Node) []*ExpressionWithTypeArgumentsNode { + return GetHeritageElements(node, KindImplementsKeyword) +} + +func GetHeritageElements(node *Node, kind Kind) []*Node { + clause := GetHeritageClause(node, kind) + if clause != nil { + return clause.AsHeritageClause().Types.Nodes + } + return nil +} + +func GetHeritageClause(node *Node, kind Kind) *Node { + clauses := getHeritageClauses(node) + if clauses != nil { + for _, clause := range clauses.Nodes { + if clause.AsHeritageClause().Token == kind { + return clause + } + } + } + return nil +} + +func getHeritageClauses(node *Node) *NodeList { + switch node.Kind { + case KindClassDeclaration: + return node.AsClassDeclaration().HeritageClauses + case KindClassExpression: + return node.AsClassExpression().HeritageClauses + case KindInterfaceDeclaration: + return node.AsInterfaceDeclaration().HeritageClauses + } + return nil +} + +func IsPartOfTypeQuery(node *Node) bool { + for node.Kind == KindQualifiedName || node.Kind == KindIdentifier { + node = node.Parent + } + return node.Kind == KindTypeQuery +} + +/** + * This function returns true if the this node's root declaration is a parameter. + * For example, passing a `ParameterDeclaration` will return true, as will passing a + * binding element that is a child of a `ParameterDeclaration`. + * + * If you are looking to test that a `Node` is a `ParameterDeclaration`, use `isParameter`. + */ +func IsPartOfParameterDeclaration(node *Node) bool { + return GetRootDeclaration(node).Kind == KindParameter +} + +func IsInTopLevelContext(node *Node) bool { + // The name of a class or function declaration is a BindingIdentifier in its surrounding scope. + if IsIdentifier(node) { + parent := node.Parent + if (IsClassDeclaration(parent) || IsFunctionDeclaration(parent)) && parent.Name() == node { + node = parent + } + } + container := GetThisContainer(node, true /*includeArrowFunctions*/, false /*includeClassComputedPropertyName*/) + return IsSourceFile(container) +} + +func GetThisContainer(node *Node, includeArrowFunctions bool, includeClassComputedPropertyName bool) *Node { + for { + node = node.Parent + if node == nil { + panic("nil parent in getThisContainer") + } + switch node.Kind { + case KindComputedPropertyName: + if includeClassComputedPropertyName && IsClassLike(node.Parent.Parent) { + return node + } + node = node.Parent.Parent + case KindDecorator: + if node.Parent.Kind == KindParameter && IsClassElement(node.Parent.Parent) { + // If the decorator's parent is a ParameterDeclaration, we resolve the this container from + // the grandparent class declaration. + node = node.Parent.Parent + } else if IsClassElement(node.Parent) { + // If the decorator's parent is a class element, we resolve the 'this' container + // from the parent class declaration. + node = node.Parent + } + case KindArrowFunction: + if includeArrowFunctions { + return node + } + case KindFunctionDeclaration, KindFunctionExpression, KindModuleDeclaration, KindClassStaticBlockDeclaration, + KindPropertyDeclaration, KindPropertySignature, KindMethodDeclaration, KindMethodSignature, KindConstructor, + KindGetAccessor, KindSetAccessor, KindCallSignature, KindConstructSignature, KindIndexSignature, + KindEnumDeclaration, KindSourceFile: + return node + } + } +} + +func GetSuperContainer(node *Node, stopOnFunctions bool) *Node { + for node = node.Parent; node != nil; node = node.Parent { + switch node.Kind { + case KindComputedPropertyName: + node = node.Parent + case KindFunctionDeclaration, KindFunctionExpression, KindArrowFunction: + if !stopOnFunctions { + continue + } + return node + case KindPropertyDeclaration, KindPropertySignature, KindMethodDeclaration, KindMethodSignature, KindConstructor, KindGetAccessor, KindSetAccessor, KindClassStaticBlockDeclaration: + return node + case KindDecorator: + // Decorators are always applied outside of the body of a class or method. + if node.Parent.Kind == KindParameter && IsClassElement(node.Parent.Parent) { + // If the decorator's parent is a ParameterDeclaration, we resolve the this container from + // the grandparent class declaration. + node = node.Parent.Parent + } else if IsClassElement(node.Parent) { + // If the decorator's parent is a class element, we resolve the 'this' container + // from the parent class declaration. + node = node.Parent + } + } + } + return nil +} + +func GetImmediatelyInvokedFunctionExpression(fn *Node) *Node { + if IsFunctionExpressionOrArrowFunction(fn) { + prev := fn + parent := fn.Parent + for IsParenthesizedExpression(parent) { + prev = parent + parent = parent.Parent + } + if IsCallExpression(parent) && parent.Expression() == prev { + return parent + } + } + return nil +} + +func IsEnumConst(node *Node) bool { + return GetCombinedModifierFlags(node)&ModifierFlagsConst != 0 +} + +func ExpressionIsAlias(node *Node) bool { + return IsEntityNameExpression(node) || IsClassExpression(node) +} + +func IsInstanceOfExpression(node *Node) bool { + return IsBinaryExpression(node) && node.AsBinaryExpression().OperatorToken.Kind == KindInstanceOfKeyword +} + +func IsAnyImportOrReExport(node *Node) bool { + return IsImportNode(node) || IsExportDeclaration(node) +} + +func IsImportNode(node *Node) bool { + return IsAnyImportSyntax(node) || NodeKindIs(node, KindJSImportDeclaration) +} + +// Checks if the node is a genuine import declation. In particular the re-parsed KindJSImportDeclaration +// is explicitly excluded because the callers of this function are typically not prepared to handle it properly. +// For more permissive check, use IsImportNode. +func IsAnyImportSyntax(node *Node) bool { + return NodeKindIs(node, KindImportDeclaration, KindImportEqualsDeclaration) +} + +func IsJsonSourceFile(file *SourceFile) bool { + return file.ScriptKind == core.ScriptKindJSON +} + +func IsInJsonFile(node *Node) bool { + return node.Flags&NodeFlagsJsonFile != 0 +} + +func GetExternalModuleName(node *Node) *Expression { + switch node.Kind { + case KindImportDeclaration, KindJSImportDeclaration, KindExportDeclaration: + return node.ModuleSpecifier() + case KindImportEqualsDeclaration: + if node.AsImportEqualsDeclaration().ModuleReference.Kind == KindExternalModuleReference { + return node.AsImportEqualsDeclaration().ModuleReference.Expression() + } + return nil + case KindImportType: + return getImportTypeNodeLiteral(node) + case KindCallExpression: + return core.FirstOrNil(node.Arguments()) + case KindModuleDeclaration: + if IsStringLiteral(node.AsModuleDeclaration().Name()) { + return node.AsModuleDeclaration().Name() + } + return nil + } + panic("Unhandled case in getExternalModuleName") +} + +func GetImportAttributes(node *Node) *Node { + switch node.Kind { + case KindImportDeclaration, KindJSImportDeclaration: + return node.AsImportDeclaration().Attributes + case KindExportDeclaration: + return node.AsExportDeclaration().Attributes + } + panic("Unhandled case in getImportAttributes") +} + +func getImportTypeNodeLiteral(node *Node) *Node { + if IsImportTypeNode(node) { + importTypeNode := node.AsImportTypeNode() + if IsLiteralTypeNode(importTypeNode.Argument) { + literalTypeNode := importTypeNode.Argument.AsLiteralTypeNode() + if IsStringLiteral(literalTypeNode.Literal) { + return literalTypeNode.Literal + } + } + } + return nil +} + +func IsExpressionNode(node *Node) bool { + switch node.Kind { + case KindSuperKeyword, KindNullKeyword, KindTrueKeyword, KindFalseKeyword, KindRegularExpressionLiteral, + KindArrayLiteralExpression, KindObjectLiteralExpression, KindPropertyAccessExpression, KindElementAccessExpression, + KindCallExpression, KindNewExpression, KindTaggedTemplateExpression, KindAsExpression, KindTypeAssertionExpression, + KindSatisfiesExpression, KindNonNullExpression, KindParenthesizedExpression, KindFunctionExpression, + KindClassExpression, KindArrowFunction, KindVoidExpression, KindDeleteExpression, KindTypeOfExpression, + KindPrefixUnaryExpression, KindPostfixUnaryExpression, KindBinaryExpression, KindConditionalExpression, + KindSpreadElement, KindTemplateExpression, KindOmittedExpression, KindJsxElement, KindJsxSelfClosingElement, + KindJsxFragment, KindYieldExpression, KindAwaitExpression: + return true + case KindMetaProperty: + // `import.defer` in `import.defer(...)` is not an expression + return !IsImportCall(node.Parent) || node.Parent.Expression() != node + case KindExpressionWithTypeArguments: + return !IsHeritageClause(node.Parent) + case KindQualifiedName: + for node.Parent.Kind == KindQualifiedName { + node = node.Parent + } + return IsTypeQueryNode(node.Parent) || IsJSDocLinkLike(node.Parent) || IsJSDocNameReference(node.Parent) || IsJsxTagName(node) + case KindPrivateIdentifier: + return IsBinaryExpression(node.Parent) && node.Parent.AsBinaryExpression().Left == node && node.Parent.AsBinaryExpression().OperatorToken.Kind == KindInKeyword + case KindIdentifier: + if IsTypeQueryNode(node.Parent) || IsJSDocLinkLike(node.Parent) || IsJSDocNameReference(node.Parent) || IsJsxTagName(node) { + return true + } + fallthrough + case KindNumericLiteral, KindBigIntLiteral, KindStringLiteral, KindNoSubstitutionTemplateLiteral, KindThisKeyword: + return IsInExpressionContext(node) + default: + return false + } +} + +func IsInExpressionContext(node *Node) bool { + parent := node.Parent + switch parent.Kind { + case KindVariableDeclaration, KindParameter, KindPropertyDeclaration, KindPropertySignature, KindEnumMember, KindPropertyAssignment, KindBindingElement: + return parent.Initializer() == node + case KindExpressionStatement, KindIfStatement, KindDoStatement, KindWhileStatement, KindReturnStatement, KindWithStatement, KindSwitchStatement, + KindCaseClause, KindDefaultClause, KindThrowStatement, KindTypeAssertionExpression, KindAsExpression, KindTemplateSpan, KindComputedPropertyName, + KindSatisfiesExpression: + return parent.Expression() == node + case KindForStatement: + s := parent.AsForStatement() + return s.Initializer == node && s.Initializer.Kind != KindVariableDeclarationList || s.Condition == node || s.Incrementor == node + case KindForInStatement, KindForOfStatement: + s := parent.AsForInOrOfStatement() + return s.Initializer == node && s.Initializer.Kind != KindVariableDeclarationList || s.Expression == node + case KindDecorator, KindJsxExpression, KindJsxSpreadAttribute, KindSpreadAssignment: + return true + case KindExpressionWithTypeArguments: + return parent.Expression() == node && !IsPartOfTypeNode(parent) + case KindShorthandPropertyAssignment: + return parent.AsShorthandPropertyAssignment().ObjectAssignmentInitializer == node + default: + return IsExpressionNode(parent) + } +} + +func IsPartOfTypeNode(node *Node) bool { + kind := node.Kind + if kind >= KindFirstTypeNode && kind <= KindLastTypeNode { + return true + } + switch node.Kind { + case KindAnyKeyword, KindUnknownKeyword, KindNumberKeyword, KindBigIntKeyword, KindStringKeyword, + KindBooleanKeyword, KindSymbolKeyword, KindObjectKeyword, KindUndefinedKeyword, KindNullKeyword, + KindNeverKeyword: + return true + case KindVoidKeyword: + return node.Parent.Kind != KindVoidExpression + case KindExpressionWithTypeArguments: + return isPartOfTypeExpressionWithTypeArguments(node) + case KindTypeParameter: + return node.Parent.Kind == KindMappedType || node.Parent.Kind == KindInferType + case KindIdentifier: + parent := node.Parent + if IsQualifiedName(parent) && parent.AsQualifiedName().Right == node { + return isPartOfTypeNodeInParent(parent) + } + if IsPropertyAccessExpression(parent) && parent.AsPropertyAccessExpression().Name() == node { + return isPartOfTypeNodeInParent(parent) + } + return isPartOfTypeNodeInParent(node) + case KindQualifiedName, KindPropertyAccessExpression, KindThisKeyword: + return isPartOfTypeNodeInParent(node) + } + return false +} + +func isPartOfTypeNodeInParent(node *Node) bool { + parent := node.Parent + if parent.Kind == KindTypeQuery { + return false + } + if parent.Kind == KindImportType { + return !parent.AsImportTypeNode().IsTypeOf + } + + // Do not recursively call isPartOfTypeNode on the parent. In the example: + // + // let a: A.B.C; + // + // Calling isPartOfTypeNode would consider the qualified name A.B a type node. + // Only C and A.B.C are type nodes. + if parent.Kind >= KindFirstTypeNode && parent.Kind <= KindLastTypeNode { + return true + } + switch parent.Kind { + case KindExpressionWithTypeArguments: + return isPartOfTypeExpressionWithTypeArguments(parent) + case KindTypeParameter: + return node == parent.AsTypeParameterDeclaration().Constraint + case KindVariableDeclaration, KindParameter, KindPropertyDeclaration, KindPropertySignature, KindFunctionDeclaration, + KindFunctionExpression, KindArrowFunction, KindConstructor, KindMethodDeclaration, KindMethodSignature, + KindGetAccessor, KindSetAccessor, KindCallSignature, KindConstructSignature, KindIndexSignature, + KindTypeAssertionExpression: + return node == parent.Type() + case KindCallExpression, KindNewExpression, KindTaggedTemplateExpression: + return slices.Contains(parent.TypeArguments(), node) + } + return false +} + +func isPartOfTypeExpressionWithTypeArguments(node *Node) bool { + parent := node.Parent + return IsHeritageClause(parent) && (!IsClassLike(parent.Parent) || parent.AsHeritageClause().Token == KindImplementsKeyword) || + IsJSDocImplementsTag(parent) || + IsJSDocAugmentsTag(parent) +} + +func IsJSDocLinkLike(node *Node) bool { + return NodeKindIs(node, KindJSDocLink, KindJSDocLinkCode, KindJSDocLinkPlain) +} + +func IsJSDocTag(node *Node) bool { + return node.Kind >= KindFirstJSDocTagNode && node.Kind <= KindLastJSDocTagNode +} + +func IsSuperCall(node *Node) bool { + return IsCallExpression(node) && node.Expression().Kind == KindSuperKeyword +} + +func IsImportCall(node *Node) bool { + if !IsCallExpression(node) { + return false + } + e := node.Expression() + return e.Kind == KindImportKeyword || IsMetaProperty(e) && e.AsMetaProperty().KeywordToken == KindImportKeyword && e.Text() == "defer" +} + +func IsComputedNonLiteralName(name *Node) bool { + return IsComputedPropertyName(name) && !IsStringOrNumericLiteralLike(name.Expression()) +} + +func IsQuestionToken(node *Node) bool { + return node != nil && node.Kind == KindQuestionToken +} + +func EntityNameToString(name *Node, getTextOfNode func(*Node) string) string { + switch name.Kind { + case KindThisKeyword: + return "this" + case KindIdentifier, KindPrivateIdentifier: + if NodeIsSynthesized(name) || getTextOfNode == nil { + return name.Text() + } + return getTextOfNode(name) + case KindQualifiedName: + return EntityNameToString(name.AsQualifiedName().Left, getTextOfNode) + "." + EntityNameToString(name.AsQualifiedName().Right, getTextOfNode) + case KindPropertyAccessExpression: + return EntityNameToString(name.Expression(), getTextOfNode) + "." + EntityNameToString(name.AsPropertyAccessExpression().Name(), getTextOfNode) + case KindJsxNamespacedName: + return EntityNameToString(name.AsJsxNamespacedName().Namespace, getTextOfNode) + ":" + EntityNameToString(name.AsJsxNamespacedName().Name(), getTextOfNode) + } + panic("Unhandled case in EntityNameToString") +} + +func GetTextOfPropertyName(name *Node) string { + text, _ := TryGetTextOfPropertyName(name) + return text +} + +func TryGetTextOfPropertyName(name *Node) (string, bool) { + switch name.Kind { + case KindIdentifier, KindPrivateIdentifier, KindStringLiteral, KindNumericLiteral, KindBigIntLiteral, + KindNoSubstitutionTemplateLiteral: + return name.Text(), true + case KindComputedPropertyName: + if IsStringOrNumericLiteralLike(name.Expression()) { + return name.Expression().Text(), true + } + case KindJsxNamespacedName: + return name.AsJsxNamespacedName().Namespace.Text() + ":" + name.Name().Text(), true + } + return "", false +} + +func IsJSDocNode(node *Node) bool { + return node.Kind >= KindFirstJSDocNode && node.Kind <= KindLastJSDocNode +} + +func IsNonWhitespaceToken(node *Node) bool { + return IsTokenKind(node.Kind) && !IsWhitespaceOnlyJsxText(node) +} + +func IsWhitespaceOnlyJsxText(node *Node) bool { + return node.Kind == KindJsxText && node.AsJsxText().ContainsOnlyTriviaWhiteSpaces +} + +func GetNewTargetContainer(node *Node) *Node { + container := GetThisContainer(node, false /*includeArrowFunctions*/, false /*includeClassComputedPropertyName*/) + if container != nil { + switch container.Kind { + case KindConstructor, KindFunctionDeclaration, KindFunctionExpression: + return container + } + } + return nil +} + +func GetEnclosingBlockScopeContainer(node *Node) *Node { + return FindAncestor(node.Parent, func(current *Node) bool { + return IsBlockScope(current, current.Parent) + }) +} + +func IsBlockScope(node *Node, parentNode *Node) bool { + switch node.Kind { + case KindSourceFile, KindCaseBlock, KindCatchClause, KindModuleDeclaration, KindForStatement, KindForInStatement, KindForOfStatement, + KindConstructor, KindMethodDeclaration, KindGetAccessor, KindSetAccessor, KindFunctionDeclaration, KindFunctionExpression, + KindArrowFunction, KindPropertyDeclaration, KindClassStaticBlockDeclaration: + return true + case KindBlock: + // function block is not considered block-scope container + // see comment in binder.ts: bind(...), case for SyntaxKind.Block + return !IsFunctionLikeOrClassStaticBlockDeclaration(parentNode) + } + return false +} + +type SemanticMeaning int32 + +const ( + SemanticMeaningNone SemanticMeaning = 0 + SemanticMeaningValue SemanticMeaning = 1 << 0 + SemanticMeaningType SemanticMeaning = 1 << 1 + SemanticMeaningNamespace SemanticMeaning = 1 << 2 + SemanticMeaningAll SemanticMeaning = SemanticMeaningValue | SemanticMeaningType | SemanticMeaningNamespace +) + +func GetMeaningFromDeclaration(node *Node) SemanticMeaning { + switch node.Kind { + case KindVariableDeclaration: + return SemanticMeaningValue + case KindParameter, + KindBindingElement, + KindPropertyDeclaration, + KindPropertySignature, + KindPropertyAssignment, + KindShorthandPropertyAssignment, + KindMethodDeclaration, + KindMethodSignature, + KindConstructor, + KindGetAccessor, + KindSetAccessor, + KindFunctionDeclaration, + KindFunctionExpression, + KindArrowFunction, + KindCatchClause, + KindJsxAttribute: + return SemanticMeaningValue + + case KindTypeParameter, + KindInterfaceDeclaration, + KindTypeAliasDeclaration, + KindJSTypeAliasDeclaration, + KindTypeLiteral: + return SemanticMeaningType + case KindEnumMember, KindClassDeclaration: + return SemanticMeaningValue | SemanticMeaningType + + case KindModuleDeclaration: + if IsAmbientModule(node) { + return SemanticMeaningNamespace | SemanticMeaningValue + } else if GetModuleInstanceState(node) == ModuleInstanceStateInstantiated { + return SemanticMeaningNamespace | SemanticMeaningValue + } else { + return SemanticMeaningNamespace + } + + case KindEnumDeclaration, + KindNamedImports, + KindImportSpecifier, + KindImportEqualsDeclaration, + KindImportDeclaration, + KindJSImportDeclaration, + KindExportAssignment, + KindExportDeclaration: + return SemanticMeaningAll + + // An external module can be a Value + case KindSourceFile: + return SemanticMeaningNamespace | SemanticMeaningValue + } + + return SemanticMeaningAll +} + +func IsPropertyAccessOrQualifiedName(node *Node) bool { + return node.Kind == KindPropertyAccessExpression || node.Kind == KindQualifiedName +} + +func IsLabelName(node *Node) bool { + return IsLabelOfLabeledStatement(node) || IsJumpStatementTarget(node) +} + +func IsLabelOfLabeledStatement(node *Node) bool { + if !IsIdentifier(node) { + return false + } + if !IsLabeledStatement(node.Parent) { + return false + } + return node == node.Parent.Label() +} + +func IsJumpStatementTarget(node *Node) bool { + if !IsIdentifier(node) { + return false + } + if !IsBreakOrContinueStatement(node.Parent) { + return false + } + return node == node.Parent.Label() +} + +func IsBreakOrContinueStatement(node *Node) bool { + return NodeKindIs(node, KindBreakStatement, KindContinueStatement) +} + +// GetModuleInstanceState is used during binding as well as in transformations and tests, and therefore may be invoked +// with a node that does not yet have its `Parent` pointer set. In this case, an `ancestors` represents a stack of +// virtual `Parent` pointers that can be used to walk up the tree. Since `getModuleInstanceStateForAliasTarget` may +// potentially walk up out of the provided `Node`, merely setting the parent pointers for a given `ModuleDeclaration` +// prior to invoking `GetModuleInstanceState` is not sufficient. It is, however, necessary that the `Parent` pointers +// for all ancestors of the `Node` provided to `GetModuleInstanceState` have been set. + +// Push a virtual parent pointer onto `ancestors` and return it. +func pushAncestor(ancestors []*Node, parent *Node) []*Node { + return append(ancestors, parent) +} + +// If a virtual `Parent` exists on the stack, returns the previous stack entry and the virtual `Parent“. +// Otherwise, we return `nil` and the value of `node.Parent`. +func popAncestor(ancestors []*Node, node *Node) ([]*Node, *Node) { + if len(ancestors) == 0 { + return nil, node.Parent + } + n := len(ancestors) - 1 + return ancestors[:n], ancestors[n] +} + +type ModuleInstanceState int32 + +const ( + ModuleInstanceStateUnknown ModuleInstanceState = iota + ModuleInstanceStateNonInstantiated + ModuleInstanceStateInstantiated + ModuleInstanceStateConstEnumOnly +) + +func GetModuleInstanceState(node *Node) ModuleInstanceState { + return getModuleInstanceState(node, nil, nil) +} + +func getModuleInstanceState(node *Node, ancestors []*Node, visited map[NodeId]ModuleInstanceState) ModuleInstanceState { + module := node.AsModuleDeclaration() + if module.Body != nil { + return getModuleInstanceStateCached(module.Body, pushAncestor(ancestors, node), visited) + } else { + return ModuleInstanceStateInstantiated + } +} + +func getModuleInstanceStateCached(node *Node, ancestors []*Node, visited map[NodeId]ModuleInstanceState) ModuleInstanceState { + if visited == nil { + visited = make(map[NodeId]ModuleInstanceState) + } + nodeId := GetNodeId(node) + if cached, ok := visited[nodeId]; ok { + if cached != ModuleInstanceStateUnknown { + return cached + } + return ModuleInstanceStateNonInstantiated + } + visited[nodeId] = ModuleInstanceStateUnknown + result := getModuleInstanceStateWorker(node, ancestors, visited) + visited[nodeId] = result + return result +} + +func getModuleInstanceStateWorker(node *Node, ancestors []*Node, visited map[NodeId]ModuleInstanceState) ModuleInstanceState { + // A module is uninstantiated if it contains only + switch node.Kind { + case KindInterfaceDeclaration, KindTypeAliasDeclaration, KindJSTypeAliasDeclaration: + return ModuleInstanceStateNonInstantiated + case KindEnumDeclaration: + if IsEnumConst(node) { + return ModuleInstanceStateConstEnumOnly + } + case KindImportDeclaration, KindJSImportDeclaration, KindImportEqualsDeclaration: + if !HasSyntacticModifier(node, ModifierFlagsExport) { + return ModuleInstanceStateNonInstantiated + } + case KindExportDeclaration: + decl := node.AsExportDeclaration() + if decl.ModuleSpecifier == nil && decl.ExportClause != nil && decl.ExportClause.Kind == KindNamedExports { + state := ModuleInstanceStateNonInstantiated + ancestors = pushAncestor(ancestors, node) + ancestors = pushAncestor(ancestors, decl.ExportClause) + for _, specifier := range decl.ExportClause.Elements() { + specifierState := getModuleInstanceStateForAliasTarget(specifier, ancestors, visited) + if specifierState > state { + state = specifierState + } + if state == ModuleInstanceStateInstantiated { + return state + } + } + return state + } + case KindModuleBlock: + state := ModuleInstanceStateNonInstantiated + ancestors = pushAncestor(ancestors, node) + node.ForEachChild(func(n *Node) bool { + childState := getModuleInstanceStateCached(n, ancestors, visited) + switch childState { + case ModuleInstanceStateNonInstantiated: + return false + case ModuleInstanceStateConstEnumOnly: + state = ModuleInstanceStateConstEnumOnly + return false + case ModuleInstanceStateInstantiated: + state = ModuleInstanceStateInstantiated + return true + } + panic("Unhandled case in getModuleInstanceStateWorker") + }) + return state + case KindModuleDeclaration: + return getModuleInstanceState(node, ancestors, visited) + } + return ModuleInstanceStateInstantiated +} + +func getModuleInstanceStateForAliasTarget(node *Node, ancestors []*Node, visited map[NodeId]ModuleInstanceState) ModuleInstanceState { + name := node.PropertyNameOrName() + if name.Kind != KindIdentifier { + // Skip for invalid syntax like this: export { "x" } + return ModuleInstanceStateInstantiated + } + for ancestors, p := popAncestor(ancestors, node); p != nil; ancestors, p = popAncestor(ancestors, p) { + if IsBlock(p) || IsModuleBlock(p) || IsSourceFile(p) { + found := ModuleInstanceStateUnknown + statementsAncestors := pushAncestor(ancestors, p) + for _, statement := range p.Statements() { + if NodeHasName(statement, name) { + state := getModuleInstanceStateCached(statement, statementsAncestors, visited) + if found == ModuleInstanceStateUnknown || state > found { + found = state + } + if found == ModuleInstanceStateInstantiated { + return found + } + if statement.Kind == KindImportEqualsDeclaration { + // Treat re-exports of import aliases as instantiated since they're ambiguous. This is consistent + // with `export import x = mod.x` being treated as instantiated: + // import x = mod.x; + // export { x }; + found = ModuleInstanceStateInstantiated + } + } + } + if found != ModuleInstanceStateUnknown { + return found + } + } + } + // Couldn't locate, assume could refer to a value + return ModuleInstanceStateInstantiated +} + +func IsInstantiatedModule(node *Node, preserveConstEnums bool) bool { + moduleState := GetModuleInstanceState(node) + return moduleState == ModuleInstanceStateInstantiated || + (preserveConstEnums && moduleState == ModuleInstanceStateConstEnumOnly) +} + +func NodeHasName(statement *Node, id *Node) bool { + name := statement.Name() + if name != nil { + return IsIdentifier(name) && name.Text() == id.Text() + } + if IsVariableStatement(statement) { + declarations := statement.AsVariableStatement().DeclarationList.AsVariableDeclarationList().Declarations.Nodes + return core.Some(declarations, func(d *Node) bool { return NodeHasName(d, id) }) + } + return false +} + +func IsInternalModuleImportEqualsDeclaration(node *Node) bool { + return IsImportEqualsDeclaration(node) && node.AsImportEqualsDeclaration().ModuleReference.Kind != KindExternalModuleReference +} + +func IsConstAssertion(node *Node) bool { + switch node.Kind { + case KindAsExpression, KindTypeAssertionExpression: + return IsConstTypeReference(node.Type()) + } + return false +} + +func IsConstTypeReference(node *Node) bool { + return IsTypeReferenceNode(node) && len(node.TypeArguments()) == 0 && IsIdentifier(node.AsTypeReferenceNode().TypeName) && node.AsTypeReferenceNode().TypeName.Text() == "const" +} + +func IsGlobalSourceFile(node *Node) bool { + return node.Kind == KindSourceFile && !IsExternalOrCommonJSModule(node.AsSourceFile()) +} + +func IsParameterLike(node *Node) bool { + switch node.Kind { + case KindParameter, KindTypeParameter: + return true + } + return false +} + +func GetDeclarationOfKind(symbol *Symbol, kind Kind) *Node { + for _, declaration := range symbol.Declarations { + if declaration.Kind == kind { + return declaration + } + } + return nil +} + +func FindConstructorDeclaration(node *ClassLikeDeclaration) *Node { + for _, member := range node.Members() { + if IsConstructorDeclaration(member) && NodeIsPresent(member.Body()) { + return member + } + } + return nil +} + +func GetFirstIdentifier(node *Node) *Node { + switch node.Kind { + case KindIdentifier: + return node + case KindQualifiedName: + return GetFirstIdentifier(node.AsQualifiedName().Left) + case KindPropertyAccessExpression: + return GetFirstIdentifier(node.AsPropertyAccessExpression().Expression) + } + panic("Unhandled case in GetFirstIdentifier") +} + +func GetNamespaceDeclarationNode(node *Node) *Node { + switch node.Kind { + case KindImportDeclaration, KindJSImportDeclaration: + importClause := node.ImportClause() + if importClause != nil && importClause.AsImportClause().NamedBindings != nil && IsNamespaceImport(importClause.AsImportClause().NamedBindings) { + return importClause.AsImportClause().NamedBindings + } + case KindImportEqualsDeclaration: + return node + case KindExportDeclaration: + exportClause := node.AsExportDeclaration().ExportClause + if exportClause != nil && IsNamespaceExport(exportClause) { + return exportClause + } + default: + panic("Unhandled case in getNamespaceDeclarationNode") + } + return nil +} + +func ModuleExportNameIsDefault(node *Node) bool { + return node.Text() == InternalSymbolNameDefault +} + +func IsDefaultImport(node *Node /*ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration*/) bool { + switch node.Kind { + case KindImportDeclaration, KindJSImportDeclaration: + importClause := node.ImportClause() + return importClause != nil && importClause.AsImportClause().name != nil + } + return false +} + +func GetImpliedNodeFormatForFile(path string, packageJsonType string) core.ModuleKind { + impliedNodeFormat := core.ResolutionModeNone + if tspath.FileExtensionIsOneOf(path, []string{tspath.ExtensionDmts, tspath.ExtensionMts, tspath.ExtensionMjs}) { + impliedNodeFormat = core.ResolutionModeESM + } else if tspath.FileExtensionIsOneOf(path, []string{tspath.ExtensionDcts, tspath.ExtensionCts, tspath.ExtensionCjs}) { + impliedNodeFormat = core.ResolutionModeCommonJS + } else if tspath.FileExtensionIsOneOf(path, []string{tspath.ExtensionDts, tspath.ExtensionTs, tspath.ExtensionTsx, tspath.ExtensionJs, tspath.ExtensionJsx}) { + impliedNodeFormat = core.IfElse(packageJsonType == "module", core.ResolutionModeESM, core.ResolutionModeCommonJS) + } + + return impliedNodeFormat +} + +func GetEmitModuleFormatOfFileWorker(fileName string, options *core.CompilerOptions, sourceFileMetaData SourceFileMetaData) core.ModuleKind { + result := GetImpliedNodeFormatForEmitWorker(fileName, options.GetEmitModuleKind(), sourceFileMetaData) + if result != core.ModuleKindNone { + return result + } + return options.GetEmitModuleKind() +} + +func GetImpliedNodeFormatForEmitWorker(fileName string, emitModuleKind core.ModuleKind, sourceFileMetaData SourceFileMetaData) core.ResolutionMode { + if core.ModuleKindNode16 <= emitModuleKind && emitModuleKind <= core.ModuleKindNodeNext { + return sourceFileMetaData.ImpliedNodeFormat + } + if sourceFileMetaData.ImpliedNodeFormat == core.ModuleKindCommonJS && + (sourceFileMetaData.PackageJsonType == "commonjs" || + tspath.FileExtensionIsOneOf(fileName, []string{tspath.ExtensionCjs, tspath.ExtensionCts})) { + return core.ModuleKindCommonJS + } + if sourceFileMetaData.ImpliedNodeFormat == core.ModuleKindESNext && + (sourceFileMetaData.PackageJsonType == "module" || + tspath.FileExtensionIsOneOf(fileName, []string{tspath.ExtensionMjs, tspath.ExtensionMts})) { + return core.ModuleKindESNext + } + return core.ModuleKindNone +} + +func GetDeclarationContainer(node *Node) *Node { + return FindAncestor(GetRootDeclaration(node), func(node *Node) bool { + switch node.Kind { + case KindVariableDeclaration, + KindVariableDeclarationList, + KindImportSpecifier, + KindNamedImports, + KindNamespaceImport, + KindImportClause: + return false + default: + return true + } + }).Parent +} + +// Indicates that a symbol is an alias that does not merge with a local declaration. +// OR Is a JSContainer which may merge an alias with a local declaration +func IsNonLocalAlias(symbol *Symbol, excludes SymbolFlags) bool { + if symbol == nil { + return false + } + return symbol.Flags&(SymbolFlagsAlias|excludes) == SymbolFlagsAlias || + symbol.Flags&SymbolFlagsAlias != 0 && symbol.Flags&SymbolFlagsAssignment != 0 +} + +// An alias symbol is created by one of the following declarations: +// +// import = ... +// const = ... (JS only) +// const { , ... } = ... (JS only) +// import from ... +// import * as from ... +// import { x as } from ... +// export { x as } from ... +// export * as ns from ... +// export = +// export default +// module.exports = (JS only) +// module.exports. = (JS only) +// exports. = (JS only) +func IsAliasSymbolDeclaration(node *Node) bool { + switch node.Kind { + case KindImportEqualsDeclaration, KindNamespaceExportDeclaration, KindNamespaceImport, KindNamespaceExport, + KindImportSpecifier, KindExportSpecifier: + return true + case KindImportClause: + return node.AsImportClause().Name() != nil + case KindExportAssignment: + return ExpressionIsAlias(node.Expression()) + case KindVariableDeclaration, KindBindingElement: + return IsVariableDeclarationInitializedToRequire(node) + case KindBinaryExpression: + switch GetAssignmentDeclarationKind(node) { + case JSDeclarationKindModuleExports, JSDeclarationKindExportsProperty: + return ExpressionIsAlias(node.AsBinaryExpression().Right) + } + } + return false +} + +func IsParseTreeNode(node *Node) bool { + return node.Flags&NodeFlagsSynthesized == 0 +} + +// Returns a token if position is in [start-of-leading-trivia, end), includes JSDoc only if requested +func GetNodeAtPosition(file *SourceFile, position int, includeJSDoc bool) *Node { + current := file.AsNode() + for { + var child *Node + if includeJSDoc { + for _, jsdoc := range current.JSDoc(file) { + if nodeContainsPosition(jsdoc, position) { + child = jsdoc + break + } + } + } + if child == nil { + current.ForEachChild(func(node *Node) bool { + if nodeContainsPosition(node, position) { + child = node + return true + } + return false + }) + } + if child == nil || IsMetaProperty(child) { + return current + } + current = child + } +} + +func nodeContainsPosition(node *Node, position int) bool { + return node.Kind >= KindFirstNode && node.Pos() <= position && (position < node.End() || position == node.End() && node.Kind == KindEndOfFile) +} + +func findImportOrRequire(text string, start int) (index int, size int) { + index = max(start, 0) + n := len(text) + for index < n { + next := strings.IndexAny(text[index:], "ir") + if next < 0 { + break + } + index += next + + var expected string + if text[index] == 'i' { + size = 6 + expected = "import" + } else { + size = 7 + expected = "require" + } + if index+size <= n && text[index:index+size] == expected { + return index, size + } + index++ + } + + return -1, 0 +} + +func ForEachDynamicImportOrRequireCall( + file *SourceFile, + includeTypeSpaceImports bool, + requireStringLiteralLikeArgument bool, + cb func(node *Node, argument *Expression) bool, +) bool { + isJavaScriptFile := IsInJSFile(file.AsNode()) + lastIndex, size := findImportOrRequire(file.Text(), 0) + for lastIndex >= 0 { + node := GetNodeAtPosition(file, lastIndex, isJavaScriptFile && includeTypeSpaceImports) + if isJavaScriptFile && IsRequireCall(node, requireStringLiteralLikeArgument) { + if cb(node, node.Arguments()[0]) { + return true + } + } else if IsImportCall(node) && len(node.Arguments()) > 0 && (!requireStringLiteralLikeArgument || IsStringLiteralLike(node.Arguments()[0])) { + if cb(node, node.Arguments()[0]) { + return true + } + } else if includeTypeSpaceImports && IsLiteralImportTypeNode(node) { + if cb(node, node.AsImportTypeNode().Argument.AsLiteralTypeNode().Literal) { + return true + } + } + // skip past import/require + lastIndex += size + lastIndex, size = findImportOrRequire(file.Text(), lastIndex) + } + return false +} + +// Returns true if the node is a CallExpression to the identifier 'require' with +// exactly one argument (of the form 'require("name")'). +// This function does not test if the node is in a JavaScript file or not. +func IsRequireCall(node *Node, requireStringLiteralLikeArgument bool) bool { + if !IsCallExpression(node) { + return false + } + call := node.AsCallExpression() + if !IsIdentifier(call.Expression) || call.Expression.Text() != "require" { + return false + } + if len(call.Arguments.Nodes) != 1 { + return false + } + return !requireStringLiteralLikeArgument || IsStringLiteralLike(call.Arguments.Nodes[0]) +} + +func IsRequireVariableStatement(node *Node) bool { + if IsVariableStatement(node) { + if declarations := node.AsVariableStatement().DeclarationList.AsVariableDeclarationList().Declarations.Nodes; len(declarations) > 0 { + return core.Every(declarations, IsVariableDeclarationInitializedToRequire) + } + } + return false +} + +func GetJSXImplicitImportBase(compilerOptions *core.CompilerOptions, file *SourceFile) string { + jsxImportSourcePragma := GetPragmaFromSourceFile(file, "jsximportsource") + jsxRuntimePragma := GetPragmaFromSourceFile(file, "jsxruntime") + if GetPragmaArgument(jsxRuntimePragma, "factory") == "classic" { + return "" + } + if compilerOptions.Jsx == core.JsxEmitReactJSX || + compilerOptions.Jsx == core.JsxEmitReactJSXDev || + compilerOptions.JsxImportSource != "" || + jsxImportSourcePragma != nil || + GetPragmaArgument(jsxRuntimePragma, "factory") == "automatic" { + result := GetPragmaArgument(jsxImportSourcePragma, "factory") + if result == "" { + result = compilerOptions.JsxImportSource + } + if result == "" { + result = "react" + } + return result + } + return "" +} + +func GetJSXRuntimeImport(base string, options *core.CompilerOptions) string { + if base == "" { + return base + } + return base + "/" + core.IfElse(options.Jsx == core.JsxEmitReactJSXDev, "jsx-dev-runtime", "jsx-runtime") +} + +func GetPragmaFromSourceFile(file *SourceFile, name string) *Pragma { + var result *Pragma + if file != nil { + for i := range file.Pragmas { + if file.Pragmas[i].Name == name { + result = &file.Pragmas[i] // Last one wins + } + } + } + return result +} + +func GetPragmaArgument(pragma *Pragma, name string) string { + if pragma != nil { + if arg, ok := pragma.Args[name]; ok { + return arg.Value + } + } + return "" +} + +// Of the form: `const x = require("x")` or `const { x } = require("x")` or with `var` or `let` +// The variable must not be exported and must not have a type annotation, even a jsdoc one. +// The initializer must be a call to `require` with a string literal or a string literal-like argument. +func IsVariableDeclarationInitializedToRequire(node *Node) bool { + if node.Kind == KindBindingElement { + node = node.Parent.Parent + } + return isVariableDeclarationInitializedWithRequireHelper(node, false /*allowAccessedRequire*/) +} + +func IsVariableDeclarationInitializedToBareOrAccessedRequire(node *Node) bool { + return isVariableDeclarationInitializedWithRequireHelper(node, true /*allowAccessedRequire*/) +} + +func isVariableDeclarationInitializedWithRequireHelper(node *Node, allowAccessedRequire bool) bool { + if !IsInJSFile(node) { + return false + } + if node.Kind != KindVariableDeclaration { + return false + } + initializer := node.Initializer() + if initializer == nil { + return false + } + if allowAccessedRequire { + initializer = GetLeftmostAccessExpression(initializer) + } + + return node.Parent.Parent.ModifierFlags()&ModifierFlagsExport == 0 && + node.Type() == nil && + IsRequireCall(initializer, true /*requireStringLiteralLikeArgument*/) +} + +func GetModuleSpecifierOfBareOrAccessedRequire(node *Node) *Node { + if isVariableDeclarationInitializedWithRequireHelper(node, false /*allowAccessedRequire*/) { + return node.Initializer().Arguments()[0] + } + if isVariableDeclarationInitializedWithRequireHelper(node, true /*allowAccessedRequire*/) { + leftmost := GetLeftmostAccessExpression(node.Initializer()) + if IsRequireCall(leftmost, true /*requireStringLiteralLikeArgument*/) { + return leftmost.Arguments()[0] + } + } + return nil +} + +func IsModuleExportsAccessExpression(node *Node) bool { + if IsAccessExpression(node) && IsModuleIdentifier(node.Expression()) { + if name := GetElementOrPropertyAccessName(node); name != nil { + return name.Text() == "exports" + } + } + return false +} + +func IsModuleExportsQualifiedName(node *Node) bool { + return IsQualifiedName(node) && IsModuleIdentifier(node.AsQualifiedName().Left) && node.AsQualifiedName().Right.Text() == "exports" +} + +func IsCheckJSEnabledForFile(sourceFile *SourceFile, compilerOptions *core.CompilerOptions) bool { + if sourceFile.CheckJsDirective != nil { + return sourceFile.CheckJsDirective.Enabled + } + return compilerOptions.CheckJs == core.TSTrue +} + +func IsPlainJSFile(file *SourceFile, checkJs core.Tristate) bool { + return file != nil && (file.ScriptKind == core.ScriptKindJS || file.ScriptKind == core.ScriptKindJSX) && file.CheckJsDirective == nil && checkJs == core.TSUnknown +} + +func GetLeftmostAccessExpression(expr *Node) *Node { + for IsAccessExpression(expr) { + expr = expr.Expression() + } + return expr +} + +func IsTypeOnlyImportDeclaration(node *Node) bool { + switch node.Kind { + case KindImportSpecifier: + return node.IsTypeOnly() || node.Parent.Parent.IsTypeOnly() + case KindNamespaceImport: + return node.Parent.IsTypeOnly() + case KindImportClause, KindImportEqualsDeclaration: + return node.IsTypeOnly() + } + return false +} + +func isTypeOnlyExportDeclaration(node *Node) bool { + switch node.Kind { + case KindExportSpecifier: + return node.IsTypeOnly() || node.Parent.Parent.IsTypeOnly() + case KindExportDeclaration: + d := node.AsExportDeclaration() + return d.IsTypeOnly && d.ModuleSpecifier != nil && d.ExportClause == nil + case KindNamespaceExport: + return node.Parent.IsTypeOnly() + } + return false +} + +func IsTypeOnlyImportOrExportDeclaration(node *Node) bool { + return IsTypeOnlyImportDeclaration(node) || isTypeOnlyExportDeclaration(node) +} + +func IsExclusivelyTypeOnlyImportOrExport(node *Node) bool { + switch node.Kind { + case KindExportDeclaration: + return node.IsTypeOnly() + case KindImportDeclaration, KindJSImportDeclaration: + if importClause := node.ImportClause(); importClause != nil { + return importClause.AsImportClause().IsTypeOnly() + } + case KindJSDocImportTag: + if importClause := node.ImportClause(); importClause != nil { + return importClause.AsImportClause().IsTypeOnly() + } + } + return false +} + +func GetClassLikeDeclarationOfSymbol(symbol *Symbol) *Node { + return core.Find(symbol.Declarations, IsClassLike) +} + +func IsCallLikeExpression(node *Node) bool { + switch node.Kind { + case KindJsxOpeningElement, KindJsxSelfClosingElement, KindJsxOpeningFragment, KindCallExpression, KindNewExpression, + KindTaggedTemplateExpression, KindDecorator: + return true + case KindBinaryExpression: + return node.AsBinaryExpression().OperatorToken.Kind == KindInstanceOfKeyword + } + return false +} + +func IsJsxCallLike(node *Node) bool { + switch node.Kind { + case KindJsxOpeningElement, KindJsxSelfClosingElement, KindJsxOpeningFragment: + return true + } + return false +} + +func IsCallLikeOrFunctionLikeExpression(node *Node) bool { + return IsCallLikeExpression(node) || IsFunctionExpressionOrArrowFunction(node) +} + +func NodeHasKind(node *Node, kind Kind) bool { + if node == nil { + return false + } + return node.Kind == kind +} + +func IsContextualKeyword(token Kind) bool { + return KindFirstContextualKeyword <= token && token <= KindLastContextualKeyword +} + +func IsThisInTypeQuery(node *Node) bool { + if !IsThisIdentifier(node) { + return false + } + for IsQualifiedName(node.Parent) && node.Parent.AsQualifiedName().Left == node { + node = node.Parent + } + return node.Parent.Kind == KindTypeQuery +} + +// Gets whether a bound `VariableDeclaration` or `VariableDeclarationList` is part of a `let` declaration. +func IsLet(node *Node) bool { + return GetCombinedNodeFlags(node)&NodeFlagsBlockScoped == NodeFlagsLet +} + +func IsClassMemberModifier(token Kind) bool { + return IsParameterPropertyModifier(token) || token == KindStaticKeyword || + token == KindOverrideKeyword || token == KindAccessorKeyword +} + +func IsParameterPropertyModifier(kind Kind) bool { + return ModifierToFlag(kind)&ModifierFlagsParameterPropertyModifier != 0 +} + +func ForEachChildAndJSDoc(node *Node, sourceFile *SourceFile, v Visitor) bool { + if visitNodes(v, node.JSDoc(sourceFile)) { + return true + } + return node.ForEachChild(v) +} + +func HasTypeArguments(node *Node) bool { + switch node.Kind { + case KindCallExpression, KindNewExpression, KindTaggedTemplateExpression, + KindTypeReference, KindExpressionWithTypeArguments, KindImportType, + KindTypeQuery, KindJsxOpeningElement, KindJsxSelfClosingElement: + return true + } + return false +} + +func IsTypeReferenceType(node *Node) bool { + return node.Kind == KindTypeReference || node.Kind == KindExpressionWithTypeArguments +} + +func IsVariableLike(node *Node) bool { + switch node.Kind { + case KindBindingElement, KindEnumMember, KindParameter, KindPropertyAssignment, KindPropertyDeclaration, + KindPropertySignature, KindShorthandPropertyAssignment, KindVariableDeclaration: + return true + } + return false +} + +func HasInitializer(node *Node) bool { + switch node.Kind { + case KindVariableDeclaration, KindParameter, KindBindingElement, KindPropertyDeclaration, + KindPropertyAssignment, KindEnumMember, KindForStatement, KindForInStatement, KindForOfStatement, + KindJsxAttribute: + return node.Initializer() != nil + default: + return false + } +} + +func IsVariableParameterOrProperty(node *Node) bool { + switch node.Kind { + case KindVariableDeclaration, KindParameter, KindPropertySignature, KindPropertyDeclaration: + return true + default: + return false + } +} + +func GetTypeAnnotationNode(node *Node) *TypeNode { + switch node.Kind { + case KindVariableDeclaration, KindParameter, KindPropertySignature, KindPropertyDeclaration, + KindTypePredicate, KindParenthesizedType, KindTypeOperator, KindMappedType, KindTypeAssertionExpression, + KindAsExpression, KindSatisfiesExpression, KindTypeAliasDeclaration, KindJSTypeAliasDeclaration, + KindNamedTupleMember, KindOptionalType, KindRestType, KindTemplateLiteralTypeSpan, KindJSDocTypeExpression, + KindJSDocPropertyTag, KindJSDocNullableType, KindJSDocNonNullableType, KindJSDocOptionalType: + return node.Type() + default: + funcLike := node.FunctionLikeData() + if funcLike != nil { + return funcLike.Type + } + return nil + } +} + +func IsObjectTypeDeclaration(node *Node) bool { + return IsClassLike(node) || IsInterfaceDeclaration(node) || IsTypeLiteralNode(node) +} + +func IsClassOrTypeElement(node *Node) bool { + return IsClassElement(node) || IsTypeElement(node) +} + +func GetClassExtendsHeritageElement(node *Node) *ExpressionWithTypeArgumentsNode { + heritageElements := GetHeritageElements(node, KindExtendsKeyword) + if len(heritageElements) > 0 { + return heritageElements[0] + } + return nil +} + +func GetImplementsTypeNodes(node *Node) []*ExpressionWithTypeArgumentsNode { + return GetHeritageElements(node, KindImplementsKeyword) +} + +func IsTypeKeywordToken(node *Node) bool { + return node.Kind == KindTypeKeyword +} + +// See `IsJSDocSingleCommentNode`. +func IsJSDocSingleCommentNodeList(nodeList *NodeList) bool { + if nodeList == nil || len(nodeList.Nodes) == 0 { + return false + } + parent := nodeList.Nodes[0].Parent + if parent == nil { + return false + } + return IsJSDocSingleCommentNode(parent) && nodeList == parent.CommentList() +} + +// See `IsJSDocSingleCommentNode`. +func IsJSDocSingleCommentNodeComment(node *Node) bool { + if node == nil || node.Parent == nil { + return false + } + return IsJSDocSingleCommentNode(node.Parent) && node == node.Parent.CommentList().Nodes[0] +} + +// In Strada, if a JSDoc node has a single comment, that comment is represented as a string property +// as a simplification, and therefore that comment is not visited by `forEachChild`. +func IsJSDocSingleCommentNode(node *Node) bool { + return hasComment(node.Kind) && node.CommentList() != nil && len(node.CommentList().Nodes) == 1 +} + +func IsValidTypeOnlyAliasUseSite(useSite *Node) bool { + return useSite.Flags&(NodeFlagsAmbient|NodeFlagsJSDoc) != 0 || + IsPartOfTypeQuery(useSite) || + isIdentifierInNonEmittingHeritageClause(useSite) || + isPartOfPossiblyValidTypeOrAbstractComputedPropertyName(useSite) || + !(IsExpressionNode(useSite) || isShorthandPropertyNameUseSite(useSite)) +} + +func isIdentifierInNonEmittingHeritageClause(node *Node) bool { + if !IsIdentifier(node) { + return false + } + parent := node.Parent + for IsPropertyAccessExpression(parent) || IsExpressionWithTypeArguments(parent) { + parent = parent.Parent + } + return IsHeritageClause(parent) && (parent.AsHeritageClause().Token == KindImplementsKeyword || IsInterfaceDeclaration(parent.Parent)) +} + +func isPartOfPossiblyValidTypeOrAbstractComputedPropertyName(node *Node) bool { + for NodeKindIs(node, KindIdentifier, KindPropertyAccessExpression) { + node = node.Parent + } + if node.Kind != KindComputedPropertyName { + return false + } + if HasSyntacticModifier(node.Parent, ModifierFlagsAbstract) { + return true + } + return NodeKindIs(node.Parent.Parent, KindInterfaceDeclaration, KindTypeLiteral) +} + +func isShorthandPropertyNameUseSite(useSite *Node) bool { + return IsIdentifier(useSite) && IsShorthandPropertyAssignment(useSite.Parent) && useSite.Parent.AsShorthandPropertyAssignment().Name() == useSite +} + +func GetPropertyNameForPropertyNameNode(name *Node) string { + switch name.Kind { + case KindIdentifier, KindPrivateIdentifier, KindStringLiteral, KindNoSubstitutionTemplateLiteral, + KindNumericLiteral, KindBigIntLiteral, KindJsxNamespacedName: + return name.Text() + case KindComputedPropertyName: + nameExpression := name.Expression() + if IsStringOrNumericLiteralLike(nameExpression) { + return nameExpression.Text() + } + if IsSignedNumericLiteral(nameExpression) { + text := nameExpression.AsPrefixUnaryExpression().Operand.Text() + if nameExpression.AsPrefixUnaryExpression().Operator == KindMinusToken { + text = "-" + text + } + return text + } + return InternalSymbolNameMissing + } + panic("Unhandled case in getPropertyNameForPropertyNameNode") +} + +func IsPartOfTypeOnlyImportOrExportDeclaration(node *Node) bool { + return FindAncestor(node, IsTypeOnlyImportOrExportDeclaration) != nil +} + +func IsPartOfExclusivelyTypeOnlyImportOrExportDeclaration(node *Node) bool { + return FindAncestor(node, IsExclusivelyTypeOnlyImportOrExport) != nil +} + +func IsEmittableImport(node *Node) bool { + switch node.Kind { + case KindImportDeclaration: + return node.ImportClause() != nil && !node.ImportClause().IsTypeOnly() + case KindExportDeclaration, KindImportEqualsDeclaration: + return !node.IsTypeOnly() + case KindCallExpression: + return IsImportCall(node) + } + return false +} + +func IsResolutionModeOverrideHost(node *Node) bool { + if node == nil { + return false + } + switch node.Kind { + case KindImportType, KindExportDeclaration, KindImportDeclaration, KindJSImportDeclaration: + return true + } + return false +} + +func HasResolutionModeOverride(node *Node) bool { + if node == nil { + return false + } + var attributes *ImportAttributesNode + switch node.Kind { + case KindImportType: + attributes = node.AsImportTypeNode().Attributes + case KindImportDeclaration, KindJSImportDeclaration: + attributes = node.AsImportDeclaration().Attributes + case KindExportDeclaration: + attributes = node.AsExportDeclaration().Attributes + } + if attributes != nil { + _, ok := attributes.GetResolutionModeOverride() + return ok + } + return false +} + +func IsStringTextContainingNode(node *Node) bool { + return node.Kind == KindStringLiteral || IsTemplateLiteralKind(node.Kind) +} + +func IsTemplateLiteralKind(kind Kind) bool { + return KindFirstTemplateToken <= kind && kind <= KindLastTemplateToken +} + +func IsTemplateLiteralToken(node *Node) bool { + return IsTemplateLiteralKind(node.Kind) +} + +func GetExternalModuleImportEqualsDeclarationExpression(node *Node) *Node { + debug.Assert(IsExternalModuleImportEqualsDeclaration(node)) + return node.AsImportEqualsDeclaration().ModuleReference.Expression() +} + +func CreateModifiersFromModifierFlags(flags ModifierFlags, createModifier func(kind Kind) *Node) []*Node { + var result []*Node + if flags&ModifierFlagsExport != 0 { + result = append(result, createModifier(KindExportKeyword)) + } + if flags&ModifierFlagsAmbient != 0 { + result = append(result, createModifier(KindDeclareKeyword)) + } + if flags&ModifierFlagsDefault != 0 { + result = append(result, createModifier(KindDefaultKeyword)) + } + if flags&ModifierFlagsConst != 0 { + result = append(result, createModifier(KindConstKeyword)) + } + if flags&ModifierFlagsPublic != 0 { + result = append(result, createModifier(KindPublicKeyword)) + } + if flags&ModifierFlagsPrivate != 0 { + result = append(result, createModifier(KindPrivateKeyword)) + } + if flags&ModifierFlagsProtected != 0 { + result = append(result, createModifier(KindProtectedKeyword)) + } + if flags&ModifierFlagsAbstract != 0 { + result = append(result, createModifier(KindAbstractKeyword)) + } + if flags&ModifierFlagsStatic != 0 { + result = append(result, createModifier(KindStaticKeyword)) + } + if flags&ModifierFlagsOverride != 0 { + result = append(result, createModifier(KindOverrideKeyword)) + } + if flags&ModifierFlagsReadonly != 0 { + result = append(result, createModifier(KindReadonlyKeyword)) + } + if flags&ModifierFlagsAccessor != 0 { + result = append(result, createModifier(KindAccessorKeyword)) + } + if flags&ModifierFlagsAsync != 0 { + result = append(result, createModifier(KindAsyncKeyword)) + } + if flags&ModifierFlagsIn != 0 { + result = append(result, createModifier(KindInKeyword)) + } + if flags&ModifierFlagsOut != 0 { + result = append(result, createModifier(KindOutKeyword)) + } + return result +} + +func GetThisParameter(signature *Node) *Node { + // callback tags do not currently support this parameters + if len(signature.Parameters()) != 0 { + thisParameter := signature.Parameters()[0] + if IsThisParameter(thisParameter) { + return thisParameter + } + } + return nil +} + +func ReplaceModifiers(factory *NodeFactory, node *Node, modifierArray *ModifierList) *Node { + switch node.Kind { + case KindTypeParameter: + return factory.UpdateTypeParameterDeclaration( + node.AsTypeParameterDeclaration(), + modifierArray, + node.Name(), + node.AsTypeParameterDeclaration().Constraint, + node.AsTypeParameterDeclaration().Expression, + node.AsTypeParameterDeclaration().DefaultType, + ) + case KindParameter: + return factory.UpdateParameterDeclaration( + node.AsParameterDeclaration(), + modifierArray, + node.AsParameterDeclaration().DotDotDotToken, + node.Name(), + node.QuestionToken(), + node.Type(), + node.Initializer(), + ) + case KindConstructorType: + return factory.UpdateConstructorTypeNode( + node.AsConstructorTypeNode(), + modifierArray, + node.TypeParameterList(), + node.ParameterList(), + node.Type(), + ) + case KindPropertySignature: + return factory.UpdatePropertySignatureDeclaration( + node.AsPropertySignatureDeclaration(), + modifierArray, + node.Name(), + node.PostfixToken(), + node.Type(), + node.Initializer(), + ) + case KindPropertyDeclaration: + return factory.UpdatePropertyDeclaration( + node.AsPropertyDeclaration(), + modifierArray, + node.Name(), + node.PostfixToken(), + node.Type(), + node.Initializer(), + ) + case KindMethodSignature: + return factory.UpdateMethodSignatureDeclaration( + node.AsMethodSignatureDeclaration(), + modifierArray, + node.Name(), + node.PostfixToken(), + node.TypeParameterList(), + node.ParameterList(), + node.Type(), + ) + case KindMethodDeclaration: + return factory.UpdateMethodDeclaration( + node.AsMethodDeclaration(), + modifierArray, + node.AsMethodDeclaration().AsteriskToken, + node.Name(), + node.PostfixToken(), + node.TypeParameterList(), + node.ParameterList(), + node.Type(), + node.AsMethodDeclaration().FullSignature, + node.Body(), + ) + case KindConstructor: + return factory.UpdateConstructorDeclaration( + node.AsConstructorDeclaration(), + modifierArray, + node.TypeParameterList(), + node.ParameterList(), + node.Type(), + node.AsConstructorDeclaration().FullSignature, + node.Body(), + ) + case KindGetAccessor: + return factory.UpdateGetAccessorDeclaration( + node.AsGetAccessorDeclaration(), + modifierArray, + node.Name(), + node.TypeParameterList(), + node.ParameterList(), + node.Type(), + node.AsGetAccessorDeclaration().FullSignature, + node.Body(), + ) + case KindSetAccessor: + return factory.UpdateSetAccessorDeclaration( + node.AsSetAccessorDeclaration(), + modifierArray, + node.Name(), + node.TypeParameterList(), + node.ParameterList(), + node.Type(), + node.AsSetAccessorDeclaration().FullSignature, + node.Body(), + ) + case KindIndexSignature: + return factory.UpdateIndexSignatureDeclaration( + node.AsIndexSignatureDeclaration(), + modifierArray, + node.ParameterList(), + node.Type(), + ) + case KindFunctionExpression: + return factory.UpdateFunctionExpression( + node.AsFunctionExpression(), + modifierArray, + node.AsFunctionExpression().AsteriskToken, + node.Name(), + node.TypeParameterList(), + node.ParameterList(), + node.Type(), + node.AsFunctionExpression().FullSignature, + node.Body(), + ) + case KindArrowFunction: + return factory.UpdateArrowFunction( + node.AsArrowFunction(), + modifierArray, + node.TypeParameterList(), + node.ParameterList(), + node.Type(), + node.AsArrowFunction().FullSignature, + node.AsArrowFunction().EqualsGreaterThanToken, + node.Body(), + ) + case KindClassExpression: + return factory.UpdateClassExpression( + node.AsClassExpression(), + modifierArray, + node.Name(), + node.TypeParameterList(), + node.AsClassExpression().HeritageClauses, + node.MemberList(), + ) + case KindVariableStatement: + return factory.UpdateVariableStatement( + node.AsVariableStatement(), + modifierArray, + node.AsVariableStatement().DeclarationList, + ) + case KindFunctionDeclaration: + return factory.UpdateFunctionDeclaration( + node.AsFunctionDeclaration(), + modifierArray, + node.AsFunctionDeclaration().AsteriskToken, + node.Name(), + node.TypeParameterList(), + node.ParameterList(), + node.Type(), + node.AsFunctionDeclaration().FullSignature, + node.Body(), + ) + case KindClassDeclaration: + return factory.UpdateClassDeclaration( + node.AsClassDeclaration(), + modifierArray, + node.Name(), + node.TypeParameterList(), + node.AsClassDeclaration().HeritageClauses, + node.MemberList(), + ) + case KindInterfaceDeclaration: + return factory.UpdateInterfaceDeclaration( + node.AsInterfaceDeclaration(), + modifierArray, + node.Name(), + node.TypeParameterList(), + node.AsInterfaceDeclaration().HeritageClauses, + node.MemberList(), + ) + case KindTypeAliasDeclaration: + return factory.UpdateTypeAliasDeclaration( + node.AsTypeAliasDeclaration(), + modifierArray, + node.Name(), + node.TypeParameterList(), + node.Type(), + ) + case KindEnumDeclaration: + return factory.UpdateEnumDeclaration( + node.AsEnumDeclaration(), + modifierArray, + node.Name(), + node.MemberList(), + ) + case KindModuleDeclaration: + return factory.UpdateModuleDeclaration( + node.AsModuleDeclaration(), + modifierArray, + node.AsModuleDeclaration().Keyword, + node.Name(), + node.Body(), + ) + case KindImportEqualsDeclaration: + return factory.UpdateImportEqualsDeclaration( + node.AsImportEqualsDeclaration(), + modifierArray, + node.IsTypeOnly(), + node.Name(), + node.AsImportEqualsDeclaration().ModuleReference, + ) + case KindImportDeclaration: + return factory.UpdateImportDeclaration( + node.AsImportDeclaration(), + modifierArray, + node.ImportClause(), + node.ModuleSpecifier(), + node.AsImportDeclaration().Attributes, + ) + case KindExportAssignment: + return factory.UpdateExportAssignment( + node.AsExportAssignment(), + modifierArray, + node.AsExportAssignment().IsExportEquals, + node.Type(), + node.Expression(), + ) + case KindExportDeclaration: + return factory.UpdateExportDeclaration( + node.AsExportDeclaration(), + modifierArray, + node.IsTypeOnly(), + node.AsExportDeclaration().ExportClause, + node.ModuleSpecifier(), + node.AsExportDeclaration().Attributes, + ) + } + panic(fmt.Sprintf("Node that does not have modifiers tried to have modifier replaced: %d", node.Kind)) +} + +func IsLateVisibilityPaintedStatement(node *Node) bool { + switch node.Kind { + case KindImportDeclaration, + KindJSImportDeclaration, + KindImportEqualsDeclaration, + KindVariableStatement, + KindClassDeclaration, + KindFunctionDeclaration, + KindModuleDeclaration, + KindTypeAliasDeclaration, + KindJSTypeAliasDeclaration, + KindInterfaceDeclaration, + KindEnumDeclaration: + return true + default: + return false + } +} + +func IsExternalModuleAugmentation(node *Node) bool { + return IsAmbientModule(node) && IsModuleAugmentationExternal(node) +} + +func GetSourceFileOfModule(module *Symbol) *SourceFile { + declaration := module.ValueDeclaration + if declaration == nil { + declaration = GetNonAugmentationDeclaration(module) + } + return GetSourceFileOfNode(declaration) +} + +func GetNonAugmentationDeclaration(symbol *Symbol) *Node { + return core.Find(symbol.Declarations, func(d *Node) bool { + return !IsExternalModuleAugmentation(d) && !IsGlobalScopeAugmentation(d) + }) +} + +func IsTypeDeclaration(node *Node) bool { + switch node.Kind { + case KindTypeParameter, KindClassDeclaration, KindInterfaceDeclaration, KindTypeAliasDeclaration, KindJSTypeAliasDeclaration, KindEnumDeclaration: + return true + case KindImportClause: + return node.IsTypeOnly() + case KindImportSpecifier, KindExportSpecifier: + return node.Parent.Parent.IsTypeOnly() + default: + return false + } +} + +func IsTypeDeclarationName(name *Node) bool { + return name.Kind == KindIdentifier && + IsTypeDeclaration(name.Parent) && + GetNameOfDeclaration(name.Parent) == name +} + +func IsRightSideOfPropertyAccess(node *Node) bool { + return node.Parent.Kind == KindPropertyAccessExpression && node.Parent.Name() == node +} + +func IsArgumentExpressionOfElementAccess(node *Node) bool { + return node.Parent != nil && node.Parent.Kind == KindElementAccessExpression && node.Parent.AsElementAccessExpression().ArgumentExpression == node +} + +func ClimbPastPropertyAccess(node *Node) *Node { + if IsRightSideOfPropertyAccess(node) { + return node.Parent + } + return node +} + +func climbPastPropertyOrElementAccess(node *Node) *Node { + if IsRightSideOfPropertyAccess(node) || IsArgumentExpressionOfElementAccess(node) { + return node.Parent + } + return node +} + +func selectExpressionOfCallOrNewExpressionOrDecorator(node *Node) *Node { + if IsCallExpression(node) || IsNewExpression(node) || IsDecorator(node) { + return node.Expression() + } + return nil +} + +func selectTagOfTaggedTemplateExpression(node *Node) *Node { + if IsTaggedTemplateExpression(node) { + return node.AsTaggedTemplateExpression().Tag + } + return nil +} + +func selectTagNameOfJsxOpeningLikeElement(node *Node) *Node { + if IsJsxOpeningElement(node) || IsJsxSelfClosingElement(node) { + return node.TagName() + } + return nil +} + +func IsCallExpressionTarget(node *Node, includeElementAccess bool, skipPastOuterExpressions bool) bool { + return isCalleeWorker(node, IsCallExpression, selectExpressionOfCallOrNewExpressionOrDecorator, includeElementAccess, skipPastOuterExpressions) +} + +func IsNewExpressionTarget(node *Node, includeElementAccess bool, skipPastOuterExpressions bool) bool { + return isCalleeWorker(node, IsNewExpression, selectExpressionOfCallOrNewExpressionOrDecorator, includeElementAccess, skipPastOuterExpressions) +} + +func IsCallOrNewExpressionTarget(node *Node, includeElementAccess bool, skipPastOuterExpressions bool) bool { + return isCalleeWorker(node, IsCallOrNewExpression, selectExpressionOfCallOrNewExpressionOrDecorator, includeElementAccess, skipPastOuterExpressions) +} + +func IsTaggedTemplateTag(node *Node, includeElementAccess bool, skipPastOuterExpressions bool) bool { + return isCalleeWorker(node, IsTaggedTemplateExpression, selectTagOfTaggedTemplateExpression, includeElementAccess, skipPastOuterExpressions) +} + +func IsDecoratorTarget(node *Node, includeElementAccess bool, skipPastOuterExpressions bool) bool { + return isCalleeWorker(node, IsDecorator, selectExpressionOfCallOrNewExpressionOrDecorator, includeElementAccess, skipPastOuterExpressions) +} + +func IsJsxOpeningLikeElementTagName(node *Node, includeElementAccess bool, skipPastOuterExpressions bool) bool { + return isCalleeWorker(node, IsJsxOpeningLikeElement, selectTagNameOfJsxOpeningLikeElement, includeElementAccess, skipPastOuterExpressions) +} + +func isCalleeWorker( + node *Node, + pred func(*Node) bool, + calleeSelector func(*Node) *Node, + includeElementAccess bool, + skipPastOuterExpressions bool, +) bool { + var target *Node + if includeElementAccess { + target = climbPastPropertyOrElementAccess(node) + } else { + target = ClimbPastPropertyAccess(node) + } + if skipPastOuterExpressions { + // Only skip outer expressions if the target is actually an expression node + if IsExpression(target) { + target = SkipOuterExpressions(target, OEKAll) + } + } + return target != nil && target.Parent != nil && pred(target.Parent) && calleeSelector(target.Parent) == target +} + +func IsRightSideOfQualifiedNameOrPropertyAccess(node *Node) bool { + parent := node.Parent + switch parent.Kind { + case KindQualifiedName: + return parent.AsQualifiedName().Right == node + case KindPropertyAccessExpression: + return parent.AsPropertyAccessExpression().Name() == node + case KindMetaProperty: + return parent.AsMetaProperty().Name() == node + } + return false +} + +func ShouldTransformImportCall(fileName string, options *core.CompilerOptions, impliedNodeFormatForEmit core.ModuleKind) bool { + moduleKind := options.GetEmitModuleKind() + if core.ModuleKindNode16 <= moduleKind && moduleKind <= core.ModuleKindNodeNext || moduleKind == core.ModuleKindPreserve { + return false + } + return impliedNodeFormatForEmit < core.ModuleKindES2015 +} + +func HasQuestionToken(node *Node) bool { + return IsQuestionToken(node.QuestionToken()) +} + +func IsJsxOpeningLikeElement(node *Node) bool { + return IsJsxOpeningElement(node) || IsJsxSelfClosingElement(node) +} + +func GetInvokedExpression(node *Node) *Node { + switch node.Kind { + case KindTaggedTemplateExpression: + return node.AsTaggedTemplateExpression().Tag + case KindJsxOpeningElement, KindJsxSelfClosingElement: + return node.TagName() + case KindBinaryExpression: + return node.AsBinaryExpression().Right + case KindJsxOpeningFragment: + return node + default: + return node.Expression() + } +} + +func IsCallOrNewExpression(node *Node) bool { + return IsCallExpression(node) || IsNewExpression(node) +} + +func IndexOfNode(nodes []*Node, node *Node) int { + index, ok := slices.BinarySearchFunc(nodes, node, CompareNodePositions) + if ok { + return index + } + return -1 +} + +func CompareNodePositions(n1, n2 *Node) int { + return core.CompareTextRanges(n1.Loc, n2.Loc) +} + +func IsUnterminatedLiteral(node *Node) bool { + return IsLiteralKind(node.Kind) && node.LiteralLikeData().TokenFlags&TokenFlagsUnterminated != 0 || + IsTemplateLiteralKind(node.Kind) && node.TemplateLiteralLikeData().TemplateFlags&TokenFlagsUnterminated != 0 +} + +// Gets a value indicating whether a class element is either a static or an instance property declaration with an initializer. +func IsInitializedProperty(member *ClassElement) bool { + return member.Kind == KindPropertyDeclaration && + member.Initializer() != nil +} + +func IsTrivia(token Kind) bool { + return KindFirstTriviaToken <= token && token <= KindLastTriviaToken +} + +func HasDecorators(node *Node) bool { + return HasSyntacticModifier(node, ModifierFlagsDecorator) +} + +type hasFileNameImpl struct { + fileName string + path tspath.Path +} + +func NewHasFileName(fileName string, path tspath.Path) HasFileName { + return &hasFileNameImpl{ + fileName: fileName, + path: path, + } +} + +func (h *hasFileNameImpl) FileName() string { + return h.fileName +} + +func (h *hasFileNameImpl) Path() tspath.Path { + return h.path +} + +func GetSemanticJsxChildren(children []*JsxChild) []*JsxChild { + return core.Filter(children, func(i *JsxChild) bool { + switch i.Kind { + case KindJsxExpression: + return i.Expression() != nil + case KindJsxText: + return !i.AsJsxText().ContainsOnlyTriviaWhiteSpaces + default: + return true + } + }) +} + +// Returns true if the node kind has a comment property. +func hasComment(kind Kind) bool { + switch kind { + case KindJSDoc, KindJSDocUnknownTag, KindJSDocAugmentsTag, KindJSDocImplementsTag, + KindJSDocDeprecatedTag, KindJSDocPublicTag, KindJSDocPrivateTag, KindJSDocProtectedTag, + KindJSDocReadonlyTag, KindJSDocOverrideTag, KindJSDocCallbackTag, KindJSDocOverloadTag, + KindJSDocParameterTag, KindJSDocPropertyTag, KindJSDocReturnTag, KindJSDocThisTag, + KindJSDocTypeTag, KindJSDocTemplateTag, KindJSDocTypedefTag, KindJSDocSeeTag, + KindJSDocThrowsTag, KindJSDocSatisfiesTag, KindJSDocImportTag: + return true + default: + return false + } +} + +func IsAssignmentPattern(node *Node) bool { + return node.Kind == KindArrayLiteralExpression || node.Kind == KindObjectLiteralExpression +} + +func GetElementsOfBindingOrAssignmentPattern(name *Node) []*Node { + switch name.Kind { + case KindObjectBindingPattern, KindArrayBindingPattern, KindArrayLiteralExpression: + // `a` in `{a}` + // `a` in `[a]` + return name.Elements() + case KindObjectLiteralExpression: + // `a` in `{a}` + return name.Properties() + } + return nil +} + +func IsDeclarationBindingElement(bindingElement *Node) bool { + switch bindingElement.Kind { + case KindVariableDeclaration, KindParameter, KindBindingElement: + return true + default: + return false + } +} + +/** + * Gets the name of an BindingOrAssignmentElement. + */ +func GetTargetOfBindingOrAssignmentElement(bindingElement *Node) *Node { + if IsDeclarationBindingElement(bindingElement) { + // `a` in `let { a } = ...` + // `a` in `let { a = 1 } = ...` + // `b` in `let { a: b } = ...` + // `b` in `let { a: b = 1 } = ...` + // `a` in `let { ...a } = ...` + // `{b}` in `let { a: {b} } = ...` + // `{b}` in `let { a: {b} = 1 } = ...` + // `[b]` in `let { a: [b] } = ...` + // `[b]` in `let { a: [b] = 1 } = ...` + // `a` in `let [a] = ...` + // `a` in `let [a = 1] = ...` + // `a` in `let [...a] = ...` + // `{a}` in `let [{a}] = ...` + // `{a}` in `let [{a} = 1] = ...` + // `[a]` in `let [[a]] = ...` + // `[a]` in `let [[a] = 1] = ...` + return bindingElement.Name() + } + + if IsObjectLiteralElement(bindingElement) { + switch bindingElement.Kind { + case KindPropertyAssignment: + // `b` in `({ a: b } = ...)` + // `b` in `({ a: b = 1 } = ...)` + // `{b}` in `({ a: {b} } = ...)` + // `{b}` in `({ a: {b} = 1 } = ...)` + // `[b]` in `({ a: [b] } = ...)` + // `[b]` in `({ a: [b] = 1 } = ...)` + // `b.c` in `({ a: b.c } = ...)` + // `b.c` in `({ a: b.c = 1 } = ...)` + // `b[0]` in `({ a: b[0] } = ...)` + // `b[0]` in `({ a: b[0] = 1 } = ...)` + return GetTargetOfBindingOrAssignmentElement(bindingElement.Initializer()) + case KindShorthandPropertyAssignment: + // `a` in `({ a } = ...)` + // `a` in `({ a = 1 } = ...)` + return bindingElement.Name() + case KindSpreadAssignment: + // `a` in `({ ...a } = ...)` + return GetTargetOfBindingOrAssignmentElement(bindingElement.Expression()) + } + + // no target + return nil + } + + if IsAssignmentExpression(bindingElement /*excludeCompoundAssignment*/, true) { + // `a` in `[a = 1] = ...` + // `{a}` in `[{a} = 1] = ...` + // `[a]` in `[[a] = 1] = ...` + // `a.b` in `[a.b = 1] = ...` + // `a[0]` in `[a[0] = 1] = ...` + return GetTargetOfBindingOrAssignmentElement(bindingElement.AsBinaryExpression().Left) + } + + if IsSpreadElement(bindingElement) { + // `a` in `[...a] = ...` + return GetTargetOfBindingOrAssignmentElement(bindingElement.Expression()) + } + + // `a` in `[a] = ...` + // `{a}` in `[{a}] = ...` + // `[a]` in `[[a]] = ...` + // `a.b` in `[a.b] = ...` + // `a[0]` in `[a[0]] = ...` + return bindingElement +} + +func TryGetPropertyNameOfBindingOrAssignmentElement(bindingElement *Node) *Node { + switch bindingElement.Kind { + case KindBindingElement: + // `a` in `let { a: b } = ...` + // `[a]` in `let { [a]: b } = ...` + // `"a"` in `let { "a": b } = ...` + // `1` in `let { 1: b } = ...` + if bindingElement.PropertyName() != nil { + propertyName := bindingElement.PropertyName() + // if IsPrivateIdentifier(propertyName) { + // return Debug.failBadSyntaxKind(propertyName) // !!! + // } + if IsComputedPropertyName(propertyName) && IsStringOrNumericLiteralLike(propertyName.Expression()) { + return propertyName.Expression() + } + return propertyName + } + case KindPropertyAssignment: + // `a` in `({ a: b } = ...)` + // `[a]` in `({ [a]: b } = ...)` + // `"a"` in `({ "a": b } = ...)` + // `1` in `({ 1: b } = ...)` + if bindingElement.Name() != nil { + propertyName := bindingElement.Name() + // if IsPrivateIdentifier(propertyName) { + // return Debug.failBadSyntaxKind(propertyName) // !!! + // } + if IsComputedPropertyName(propertyName) && IsStringOrNumericLiteralLike(propertyName.Expression()) { + return propertyName.Expression() + } + return propertyName + } + case KindSpreadAssignment: + // `a` in `({ ...a } = ...)` + // if IsPrivateIdentifier(bindingElement.Name()) { + // return Debug.failBadSyntaxKind(bindingElement.Name()) // !!! + // } + return bindingElement.Name() + } + + target := GetTargetOfBindingOrAssignmentElement(bindingElement) + if target != nil && IsPropertyName(target) { + return target + } + return nil +} + +/** + * Walk an AssignmentPattern to determine if it contains object rest (`...`) syntax. We cannot rely on + * propagation of `TransformFlags.ContainsObjectRestOrSpread` since it isn't propagated by default in + * ObjectLiteralExpression and ArrayLiteralExpression since we do not know whether they belong to an + * AssignmentPattern at the time the nodes are parsed. + */ +func ContainsObjectRestOrSpread(node *Node) bool { + if node.SubtreeFacts()&SubtreeContainsObjectRestOrSpread != 0 { + return true + } + if node.SubtreeFacts()&SubtreeContainsESObjectRestOrSpread != 0 { + // check for nested spread assignments, otherwise '{ x: { a, ...b } = foo } = c' + // will not be correctly interpreted by the rest/spread transformer + for _, element := range GetElementsOfBindingOrAssignmentPattern(node) { + target := GetTargetOfBindingOrAssignmentElement(element) + if target != nil && IsAssignmentPattern(target) { + if target.SubtreeFacts()&SubtreeContainsObjectRestOrSpread != 0 { + return true + } + if target.SubtreeFacts()&SubtreeContainsESObjectRestOrSpread != 0 { + if ContainsObjectRestOrSpread(target) { + return true + } + } + } + } + } + return false +} + +func IsEmptyObjectLiteral(expression *Node) bool { + return IsObjectLiteralExpression(expression) && len(expression.Properties()) == 0 +} + +func IsEmptyArrayLiteral(expression *Node) bool { + return IsArrayLiteralExpression(expression) && len(expression.Elements()) == 0 +} + +func GetRestIndicatorOfBindingOrAssignmentElement(bindingElement *Node) *Node { + switch bindingElement.Kind { + case KindParameter: + return bindingElement.AsParameterDeclaration().DotDotDotToken + case KindBindingElement: + return bindingElement.AsBindingElement().DotDotDotToken + case KindSpreadElement, KindSpreadAssignment: + return bindingElement + } + return nil +} + +func IsJSDocNameReferenceContext(node *Node) bool { + return node.Flags&NodeFlagsJSDoc != 0 && FindAncestor(node, func(node *Node) bool { + return IsJSDocNameReference(node) || IsJSDocLinkLike(node) + }) != nil +} + +// GetJSDocRoot returns the containing JSDoc node for a node inside a JSDoc comment. +func GetJSDocRoot(node *Node) *Node { + return FindAncestor(node.Parent, func(n *Node) bool { + return n.Kind == KindJSDoc + }) +} + +// GetJSDocHost returns the declaration that the JSDoc comment containing the given node is attached to. +func GetJSDocHost(node *Node) *Node { + jsDoc := GetJSDocRoot(node) + if jsDoc == nil { + return nil + } + return jsDoc.Parent +} + +// GetHostSignatureFromJSDoc returns the function-like declaration that hosts the JSDoc comment +// containing the given node. This is used to resolve @link references to parameters. +func GetHostSignatureFromJSDoc(node *Node) *Node { + host := GetJSDocHost(node) + if host == nil { + return nil + } + // !!! Strada's getEffectiveJSDocHost applies JS assignment pattern transforms (getSourceOfAssignment, getSourceOfDefaultedAssignment, etc.) not yet ported + if IsPropertySignatureDeclaration(host) && host.Type() != nil && IsFunctionLike(host.Type()) { + return host.Type() + } + if IsFunctionLike(host) { + return host + } + return nil +} + +// Finds the declaration that owns the JSDoc for a function-like node. +// Keep these hosts aligned with JSDoc parameter reparsing so unmatched @param diagnostics use the same attachment rules. +// Keep in sync with getNextJSDocCommentLocation in the API's src/ast/jsdoc.ts +func GetNextJSDocCommentLocation(node *Node) *Node { + if parent := node.Parent; parent != nil { + switch parent.Kind { + case KindPropertyAssignment, KindExportAssignment, KindPropertyDeclaration, KindVariableDeclaration, + KindSatisfiesExpression, KindReturnStatement, KindVariableStatement, KindExpressionStatement: + return parent + case KindVariableDeclarationList: + if parent.AsVariableDeclarationList().Declarations.Nodes[0] == node { + return parent + } + } + } + return nil +} + +func IsImportOrImportEqualsDeclaration(node *Node) bool { + return IsImportDeclaration(node) || IsImportEqualsDeclaration(node) +} + +func IsPrimitiveLiteralValue(node *Node, includeBigInt bool) bool { + switch node.Kind { + case KindTrueKeyword, + KindFalseKeyword, + KindNumericLiteral, + KindStringLiteral, + KindNoSubstitutionTemplateLiteral: + return true + case KindBigIntLiteral: + return includeBigInt + case KindPrefixUnaryExpression: + if node.AsPrefixUnaryExpression().Operator == KindMinusToken { + return IsNumericLiteral(node.AsPrefixUnaryExpression().Operand) || (includeBigInt && IsBigIntLiteral(node.AsPrefixUnaryExpression().Operand)) + } + if node.AsPrefixUnaryExpression().Operator == KindPlusToken { + return IsNumericLiteral(node.AsPrefixUnaryExpression().Operand) + } + return false + default: + return false + } +} + +func HasInferredType(node *Node) bool { + // Debug.type(node); // !!! + switch node.Kind { + case KindParameter, + KindPropertySignature, + KindPropertyDeclaration, + KindBindingElement, + KindPropertyAccessExpression, + KindElementAccessExpression, + KindBinaryExpression, + KindCallExpression, + KindVariableDeclaration, + KindExportAssignment, + KindPropertyAssignment, + KindShorthandPropertyAssignment, + KindJSDocParameterTag, + KindJSDocPropertyTag: + return true + default: + // assertType(node); // !!! + return false + } +} + +func IsKeyword(token Kind) bool { + return KindFirstKeyword <= token && token <= KindLastKeyword +} + +func IsNonContextualKeyword(token Kind) bool { + return IsKeyword(token) && !IsContextualKeyword(token) +} + +func HasModifier(node *Node, flags ModifierFlags) bool { + return node.ModifierFlags()&flags != 0 +} + +func IsExpandoInitializer(declaration *Node, initializer *Node) bool { + if initializer == nil { + return false + } + if IsFunctionExpressionOrArrowFunction(initializer) { + return true + } + if IsInJSFile(initializer) { + return IsClassExpression(initializer) || (IsObjectLiteralExpression(initializer) && len(initializer.Properties()) == 0 && declaration.Type() == nil) + } + return false +} + +func GetContainingFunction(node *Node) *Node { + return FindAncestor(node.Parent, IsFunctionLike) +} + +func ImportFromModuleSpecifier(node *Node) *Node { + if result := TryGetImportFromModuleSpecifier(node); result != nil { + return result + } + debug.FailBadSyntaxKind(node.Parent) + return nil +} + +func TryGetImportFromModuleSpecifier(node *StringLiteralLike) *Node { + switch node.Parent.Kind { + case KindImportDeclaration, KindJSImportDeclaration, KindExportDeclaration: + return node.Parent + case KindExternalModuleReference: + return node.Parent.Parent + case KindCallExpression: + if IsImportCall(node.Parent) || IsRequireCall(node.Parent, false /*requireStringLiteralLikeArgument*/) { + return node.Parent + } + return nil + case KindLiteralType: + if !IsStringLiteral(node) { + return nil + } + if IsImportTypeNode(node.Parent.Parent) { + return node.Parent.Parent + } + return nil + } + return nil +} + +func IsImplicitlyExportedJSDocDeclaration(node *Node) bool { + if !IsSourceFile(node.Parent) || !IsExternalOrCommonJSModule(node.Parent.AsSourceFile()) { + return false + } + if IsJSTypeAliasDeclaration(node) { + return true + } + // A reparsed ModuleDeclaration synthesized from a JSDoc @typedef/@callback + // dotted name should also be treated as implicitly exported in modules. + return IsModuleDeclaration(node) && node.Flags&NodeFlagsReparsed != 0 +} + +func HasContextSensitiveParameters(node *Node) bool { + // Functions with type parameters are not context sensitive. + if node.TypeParameters() == nil { + // Functions with any parameters that lack type annotations are context sensitive. + if core.Some(node.Parameters(), func(p *Node) bool { return p.Type() == nil }) { + return true + } + if !IsArrowFunction(node) { + // If the first parameter is not an explicit 'this' parameter, then the function has + // an implicit 'this' parameter which is subject to contextual typing. + parameter := core.FirstOrNil(node.Parameters()) + if parameter == nil || !IsThisParameter(parameter) { + return node.Flags&NodeFlagsContainsThis != 0 + } + } + } + return false +} + +func IsInfinityOrNaNString(name string) bool { + return name == "Infinity" || name == "-Infinity" || name == "NaN" +} + +func GetFirstConstructorWithBody(node *Node) *Node { + for _, member := range node.Members() { + if IsConstructorDeclaration(member) && NodeIsPresent(member.Body()) { + return member + } + } + return nil +} + +// Returns true for nodes that are considered executable for the purposes of unreachable code detection. +func IsPotentiallyExecutableNode(node *Node) bool { + if KindFirstStatement <= node.Kind && node.Kind <= KindLastStatement { + if IsVariableStatement(node) { + declarationList := node.AsVariableStatement().DeclarationList + if GetCombinedNodeFlags(declarationList)&NodeFlagsBlockScoped != 0 { + return true + } + declarations := declarationList.AsVariableDeclarationList().Declarations.Nodes + return core.Some(declarations, func(d *Node) bool { + return d.Initializer() != nil + }) + } + return true + } + return IsClassDeclaration(node) || IsEnumDeclaration(node) || IsModuleDeclaration(node) +} + +func HasAbstractModifier(node *Node) bool { + return HasSyntacticModifier(node, ModifierFlagsAbstract) +} + +func HasAmbientModifier(node *Node) bool { + return HasSyntacticModifier(node, ModifierFlagsAmbient) +} + +func NodeCanBeDecorated(useLegacyDecorators bool, node *Node, parent *Node, grandparent *Node) bool { + // private names cannot be used with decorators yet + if useLegacyDecorators && node.Name() != nil && IsPrivateIdentifier(node.Name()) { + return false + } + switch node.Kind { + case KindClassDeclaration: + // class declarations are valid targets + return true + case KindClassExpression: + // class expressions are valid targets for native decorators + return !useLegacyDecorators + case KindPropertyDeclaration: + // property declarations are valid if their parent is a class declaration. + return parent != nil && (useLegacyDecorators && IsClassDeclaration(parent) || + !useLegacyDecorators && IsClassLike(parent) && !HasAbstractModifier(node) && !HasAmbientModifier(node)) + case KindGetAccessor, KindSetAccessor, KindMethodDeclaration: + // if this method has a body and its parent is a class declaration, this is a valid target. + return parent != nil && node.Body() != nil && (useLegacyDecorators && IsClassDeclaration(parent) || + !useLegacyDecorators && IsClassLike(parent)) + case KindParameter: + // TODO(rbuckton): ParameterDeclaration decorator support for ES decorators must wait until it is standardized + if !useLegacyDecorators { + return false + } + // if the parameter's parent has a body and its grandparent is a class declaration, this is a valid target. + return parent != nil && parent.Body() != nil && + (parent.Kind == KindConstructor || parent.Kind == KindMethodDeclaration || parent.Kind == KindSetAccessor) && + GetThisParameter(parent) != node && grandparent != nil && grandparent.Kind == KindClassDeclaration + } + + return false +} + +func ClassOrConstructorParameterIsDecorated(useLegacyDecorators bool, node *Node) bool { + if NodeIsDecorated(useLegacyDecorators, node, nil, nil) { + return true + } + constructor := GetFirstConstructorWithBody(node) + return constructor != nil && ChildIsDecorated(useLegacyDecorators, constructor, node) +} + +func ClassElementOrClassElementParameterIsDecorated(useLegacyDecorators bool, node *Node, parent *Node) bool { + var parameters *NodeList + if IsAccessor(node) { + decls := GetAllAccessorDeclarations(parent.Members(), node) + var firstAccessorWithDecorators *Node + if HasDecorators(decls.FirstAccessor) { + firstAccessorWithDecorators = decls.FirstAccessor + } else if decls.SecondAccessor != nil && HasDecorators(decls.SecondAccessor) { + firstAccessorWithDecorators = decls.SecondAccessor + } + if firstAccessorWithDecorators == nil || node != firstAccessorWithDecorators { + return false + } + if decls.SetAccessor != nil { + parameters = decls.SetAccessor.Parameters + } + } else if IsMethodDeclaration(node) { + parameters = node.ParameterList() + } + if NodeIsDecorated(useLegacyDecorators, node, parent, nil) { + return true + } + if parameters != nil && len(parameters.Nodes) > 0 { + for _, parameter := range parameters.Nodes { + if IsThisParameter(parameter) { + continue + } + if NodeIsDecorated(useLegacyDecorators, parameter, node, parent) { + return true + } + } + } + return false +} + +func NodeIsDecorated(useLegacyDecorators bool, node *Node, parent *Node, grandparent *Node) bool { + return HasDecorators(node) && NodeCanBeDecorated(useLegacyDecorators, node, parent, grandparent) +} + +func NodeOrChildIsDecorated(useLegacyDecorators bool, node *Node, parent *Node, grandparent *Node) bool { + return NodeIsDecorated(useLegacyDecorators, node, parent, grandparent) || ChildIsDecorated(useLegacyDecorators, node, parent) +} + +func ChildIsDecorated(useLegacyDecorators bool, node *Node, parent *Node) bool { + switch node.Kind { + case KindClassDeclaration, KindClassExpression: + return core.Some(node.Members(), func(m *Node) bool { + return NodeOrChildIsDecorated(useLegacyDecorators, m, node, parent) + }) + case KindMethodDeclaration, + KindSetAccessor, + KindConstructor: + return core.Some(node.Parameters(), func(p *Node) bool { + return NodeIsDecorated(useLegacyDecorators, p, node, parent) + }) + default: + return false + } +} + +type AllAccessorDeclarations struct { + FirstAccessor *AccessorDeclaration + SecondAccessor *AccessorDeclaration + SetAccessor *SetAccessorDeclaration + GetAccessor *GetAccessorDeclaration +} + +func GetAllAccessorDeclarationsForDeclaration(accessor *AccessorDeclaration, declarationsOfSymbol []*Node) AllAccessorDeclarations { + var otherKind Kind + if accessor.Kind == KindSetAccessor { + otherKind = KindGetAccessor + } else if accessor.Kind == KindGetAccessor { + otherKind = KindSetAccessor + } else { + panic(fmt.Sprintf("Unexpected node kind %q", accessor.Kind)) + } + // otherAccessor := GetDeclarationOfKind(c.getSymbolOfDeclaration(accessor), otherKind) + var otherAccessor *AccessorDeclaration + for _, d := range declarationsOfSymbol { + if d.Kind == otherKind { + otherAccessor = d + break + } + } + + var firstAccessor *AccessorDeclaration + var secondAccessor *AccessorDeclaration + if otherAccessor != nil && (otherAccessor.Pos() < accessor.Pos()) { + firstAccessor = otherAccessor + secondAccessor = accessor + } else { + firstAccessor = accessor + secondAccessor = otherAccessor + } + + var setAccessor *SetAccessorDeclaration + var getAccessor *GetAccessorDeclaration + if accessor.Kind == KindSetAccessor { + setAccessor = accessor.AsSetAccessorDeclaration() + if otherAccessor != nil { + getAccessor = otherAccessor.AsGetAccessorDeclaration() + } + } else { + getAccessor = accessor.AsGetAccessorDeclaration() + if otherAccessor != nil { + setAccessor = otherAccessor.AsSetAccessorDeclaration() + } + } + + return AllAccessorDeclarations{ + FirstAccessor: firstAccessor, + SecondAccessor: secondAccessor, + SetAccessor: setAccessor, + GetAccessor: getAccessor, + } +} + +func GetAllAccessorDeclarations(parentDeclarations []*Node, accessor *AccessorDeclaration) AllAccessorDeclarations { + if HasDynamicName(accessor) { + // dynamic names can only be match up via checker symbol lookup, just return an object with just this accessor + return GetAllAccessorDeclarationsForDeclaration(accessor, []*Node{accessor}) + } + + accessorName := GetPropertyNameForPropertyNameNode(accessor.Name()) + accessorStatic := IsStatic(accessor) + var matches []*Node + for _, member := range parentDeclarations { + if !IsAccessor(member) || IsStatic(member) != accessorStatic { + continue + } + memberName := GetPropertyNameForPropertyNameNode(member.Name()) + if memberName == accessorName { + matches = append(matches, member) + } + } + return GetAllAccessorDeclarationsForDeclaration(accessor, matches) +} + +func IsAsyncFunction(node *Node) bool { + switch node.Kind { + case KindFunctionDeclaration, KindFunctionExpression, KindArrowFunction, KindMethodDeclaration: + data := node.BodyData() + return data.Body != nil && data.AsteriskToken == nil && HasSyntacticModifier(node, ModifierFlagsAsync) + } + return false +} + +/** + * Gets the most likely element type for a TypeNode. This is not an exhaustive test + * as it assumes a rest argument can only be an array type (either T[], or Array). + * + * @param node The type node. + * + * @internal + */ +func GetRestParameterElementType(node *ParameterDeclarationNode) *Node { + if node == nil { + return node + } + if node.Kind == KindArrayType { + return node.AsArrayTypeNode().ElementType + } + if node.Kind == KindTypeReference && node.AsTypeReferenceNode().TypeArguments != nil { + return core.FirstOrNil(node.AsTypeReferenceNode().TypeArguments.Nodes) + } + return nil +} + +func TagNamesAreEquivalent(lhs *Expression, rhs *Expression) bool { + if lhs.Kind != rhs.Kind { + return false + } + switch lhs.Kind { + case KindIdentifier: + return lhs.Text() == rhs.Text() + case KindThisKeyword: + return true + case KindJsxNamespacedName: + return lhs.AsJsxNamespacedName().Namespace.Text() == rhs.AsJsxNamespacedName().Namespace.Text() && + lhs.AsJsxNamespacedName().Name().Text() == rhs.AsJsxNamespacedName().Name().Text() + case KindPropertyAccessExpression: + return lhs.AsPropertyAccessExpression().Name().Text() == rhs.AsPropertyAccessExpression().Name().Text() && + TagNamesAreEquivalent(lhs.Expression(), rhs.Expression()) + } + panic("Unhandled case in TagNamesAreEquivalent") +} + +func IsTagName(node *Node) bool { + return node.Parent != nil && IsJSDocTag(node.Parent) && node.Parent.TagName() == node +} + +// We want to store any numbers/strings if they were a name that could be +// related to a declaration. So, if we have 'import x = require("something")' +// then we want 'something' to be in the name table. Similarly, if we have +// "a['propname']" then we want to store "propname" in the name table. +func literalIsName(node *Node) bool { + return IsDeclarationName(node) || + node.Parent.Kind == KindExternalModuleReference || + isArgumentOfElementAccessExpression(node) || + IsLiteralComputedPropertyDeclarationName(node) +} + +func isArgumentOfElementAccessExpression(node *Node) bool { + return node != nil && node.Parent != nil && + node.Parent.Kind == KindElementAccessExpression && + node.Parent.AsElementAccessExpression().ArgumentExpression == node +} + +// If the given node is part of a subtree of JSDoc nodes that have been cloned into a reparsed construct, +// return the corresponding reparsed clone in the subtree. Otherwise, just return the node. +func GetReparsedNodeForNode(node *Node) *Node { + if node != nil && node.Flags&NodeFlagsJSDoc != 0 && node.Flags&NodeFlagsReparsed == 0 { + if file := GetSourceFileOfNode(node); file != nil && len(file.ReparsedClones) != 0 { + pos, found := slices.BinarySearchFunc(file.ReparsedClones, node, CompareNodePositions) + if !found && pos > 0 { + pos-- + } + candidate := file.ReparsedClones[pos] + if node.Loc.ContainedBy(candidate.Loc) { + if reparsed := findCloneInNode(candidate, node); reparsed != nil { + return reparsed + } + } + } + } + return node +} + +func findCloneInNode(node *Node, original *Node) *Node { + for { + if node.Kind == original.Kind && node.Loc == original.Loc { + return node + } + foundContainingChild := node.ForEachChild(func(n *Node) bool { + if original.Loc.ContainedBy(n.Loc) { + node = n + return true + } + return false + }) + if !foundContainingChild { + return nil + } + } +} + +func IsExpandoPropertyDeclaration(node *Node) bool { + return node != nil && IsBinaryExpression(node) +} + +// IsSuperProperty checks if a node is super.x or super[x]. +func IsSuperProperty(node *Node) bool { + return (IsPropertyAccessExpression(node) || IsElementAccessExpression(node)) && + node.Expression().Kind == KindSuperKeyword +} + +// Indicates whether a node is a potential source of an assigned name for a class, function, or arrow function. +func IsNamedEvaluationSource(node *Node) bool { + switch node.Kind { + case KindPropertyAssignment: + return !IsProtoSetter(node.AsPropertyAssignment().Name()) + case KindShorthandPropertyAssignment: + return node.AsShorthandPropertyAssignment().ObjectAssignmentInitializer != nil + case KindVariableDeclaration: + return IsIdentifier(node.AsVariableDeclaration().Name()) && node.Initializer() != nil + case KindParameter: + return IsIdentifier(node.AsParameterDeclaration().Name()) && node.Initializer() != nil && node.AsParameterDeclaration().DotDotDotToken == nil + case KindBindingElement: + return IsIdentifier(node.AsBindingElement().Name()) && node.Initializer() != nil && node.AsBindingElement().DotDotDotToken == nil + case KindPropertyDeclaration: + return node.Initializer() != nil + case KindBinaryExpression: + switch node.AsBinaryExpression().OperatorToken.Kind { + case KindEqualsToken, KindAmpersandAmpersandEqualsToken, KindBarBarEqualsToken, KindQuestionQuestionEqualsToken: + return IsIdentifier(node.AsBinaryExpression().Left) + } + case KindExportAssignment: + return true + } + return false +} + +// Indicates whether a property name is the special `__proto__` property. +// Per the ECMA-262 spec, this only matters for property assignments whose name is +// the Identifier `__proto__`, or the string literal `"__proto__"`, but not for +// computed property names. +func IsProtoSetter(node *Node) bool { + return (IsIdentifier(node) || IsStringLiteral(node)) && node.Text() == "__proto__" +} diff --git a/tools/tsgo/internal/ast/visitor.go b/tools/tsgo/internal/ast/visitor.go new file mode 100644 index 00000000..bedcc8b7 --- /dev/null +++ b/tools/tsgo/internal/ast/visitor.go @@ -0,0 +1,278 @@ +package ast + +import ( + "slices" +) + +// NodeVisitor + +type NodeVisitor struct { + Visit func(node *Node) *Node // Required. The callback used to visit a node + Factory *NodeFactory // Required. The NodeFactory used to produce new nodes when passed to VisitEachChild + Hooks NodeVisitorHooks // Hooks to be invoked when visiting a node +} + +// These hooks are used to intercept the default behavior of the visitor +type NodeVisitorHooks struct { + VisitNode func(node *Node, v *NodeVisitor) *Node // Overrides visiting a Node. Only invoked by the VisitEachChild method on a given Node subtype. + VisitToken func(node *TokenNode, v *NodeVisitor) *Node // Overrides visiting a TokenNode. Only invoked by the VisitEachChild method on a given Node subtype. + VisitNodes func(nodes *NodeList, v *NodeVisitor) *NodeList // Overrides visiting a NodeList. Only invoked by the VisitEachChild method on a given Node subtype. + VisitModifiers func(nodes *ModifierList, v *NodeVisitor) *ModifierList // Overrides visiting a ModifierList. Only invoked by the VisitEachChild method on a given Node subtype. + VisitEmbeddedStatement func(node *Statement, v *NodeVisitor) *Statement // Overrides visiting a Node when it is the embedded statement body of an iteration statement, `if` statement, or `with` statement. Only invoked by the VisitEachChild method on a given Node subtype. + VisitIterationBody func(node *Statement, v *NodeVisitor) *Statement // Overrides visiting a Node when it is the embedded statement body of an iteration statement. Only invoked by the VisitEachChild method on a given Node subtype. + VisitParameters func(nodes *ParameterList, v *NodeVisitor) *ParameterList // Overrides visiting a ParameterList. Only invoked by the VisitEachChild method on a given Node subtype. + VisitFunctionBody func(node *BlockOrExpression, v *NodeVisitor) *BlockOrExpression // Overrides visiting a function body. Only invoked by the VisitEachChild method on a given Node subtype. + VisitTopLevelStatements func(nodes *StatementList, v *NodeVisitor) *StatementList // Overrides visiting a variable environment. Only invoked by the VisitEachChild method on a given Node subtype. +} + +func NewNodeVisitor(visit func(node *Node) *Node, factory *NodeFactory, hooks NodeVisitorHooks) *NodeVisitor { + if factory == nil { + factory = &NodeFactory{} + } + return &NodeVisitor{Visit: visit, Factory: factory, Hooks: hooks} +} + +func (v *NodeVisitor) VisitSourceFile(node *SourceFile) *SourceFile { + return v.VisitNode(node.AsNode()).AsSourceFile() +} + +// Visits a Node, possibly returning a new Node in its place. +// +// - If the input node is nil, then the output is nil. +// - If v.Visit is nil, then the output is the input. +// - If v.Visit returns nil, then the output is nil. +// - If v.Visit returns a SyntaxList Node, then the output is the only child of the SyntaxList Node. +func (v *NodeVisitor) VisitNode(node *Node) *Node { + if node == nil || v.Visit == nil { + return node + } + + if v.Visit != nil { + visited := v.Visit(node) + if visited != nil && visited.Kind == KindSyntaxList { + nodes := visited.AsSyntaxList().Children + if len(nodes) != 1 { + panic("Expected only a single node to be written to output") + } + visited = nodes[0] + if visited != nil && visited.Kind == KindSyntaxList { + panic("The result of visiting and lifting a Node may not be SyntaxList") + } + } + return visited + } + + return node +} + +// Visits an embedded Statement (i.e., the single statement body of a loop, `if..else` branch, etc.), possibly returning a new Statement in its place. +// +// - If the input node is nil, then the output is nil. +// - If v.Visit is nil, then the output is the input. +// - If v.Visit returns nil, then the output is nil. +// - If v.Visit returns a SyntaxList Node, then the output is either the only child of the SyntaxList Node, or a Block containing the nodes in the list. +func (v *NodeVisitor) VisitEmbeddedStatement(node *Statement) *Statement { + if node == nil || v.Visit == nil { + return node + } + + visited := v.Visit(node) + if visited == nil { + return nil + } + return v.liftToBlock(visited) +} + +// Visits a NodeList, possibly returning a new NodeList in its place. +// +// - If the input NodeList is nil, the output is nil. +// - If v.Visit is nil, then the output is the input. +// - If v.Visit returns nil, the visited Node will be absent in the output. +// - If v.Visit returns a different Node than the input, a new NodeList will be generated and returned. +// - If v.Visit returns a SyntaxList Node, then the children of that node will be merged into the output and a new NodeList will be returned. +// - If this method returns a new NodeList for any reason, it will have the same Loc as the input NodeList. +func (v *NodeVisitor) VisitNodes(nodes *NodeList) *NodeList { + if nodes == nil || v.Visit == nil { + return nodes + } + + if result, changed := v.VisitSlice(nodes.Nodes); changed { + list := v.Factory.NewNodeList(result) + list.Loc = nodes.Loc + return list + } + + return nodes +} + +// Visits a ModifierList, possibly returning a new ModifierList in its place. +// +// - If the input ModifierList is nil, the output is nil. +// - If v.Visit is nil, then the output is the input. +// - If v.Visit returns nil, the visited Node will be absent in the output. +// - If v.Visit returns a different Node than the input, a new ModifierList will be generated and returned. +// - If v.Visit returns a SyntaxList Node, then the children of that node will be merged into the output and a new NodeList will be returned. +// - If this method returns a new NodeList for any reason, it will have the same Loc as the input NodeList. +func (v *NodeVisitor) VisitModifiers(nodes *ModifierList) *ModifierList { + if nodes == nil || v.Visit == nil { + return nodes + } + + if result, changed := v.VisitSlice(nodes.Nodes); changed { + list := v.Factory.NewModifierList(result) + list.Loc = nodes.Loc + return list + } + + return nodes +} + +// Visits a slice of Nodes, returning the resulting slice and a value indicating whether the slice was changed. +// +// - If the input slice is nil, the output is nil. +// - If v.Visit is nil, then the output is the input. +// - If v.Visit returns nil, the visited Node will be absent in the output. +// - If v.Visit returns a different Node than the input, a new slice will be generated and returned. +// - If v.Visit returns a SyntaxList Node, then the children of that node will be merged into the output and a new slice will be returned. +func (v *NodeVisitor) VisitSlice(nodes []*Node) (result []*Node, changed bool) { + if nodes == nil || v.Visit == nil { + return nodes, false + } + + for i := 0; i < len(nodes); i++ { + node := nodes[i] + if v.Visit == nil { + break + } + + visited := v.Visit(node) + if visited == nil || visited != node { + updated := slices.Clone(nodes[:i]) + + for { + // finish prior loop + switch { + case visited == nil: // do nothing + case visited.Kind == KindSyntaxList: + updated = append(updated, visited.AsSyntaxList().Children...) + default: + updated = append(updated, visited) + } + + i++ + + // loop over remaining elements + if i >= len(nodes) { + break + } + + if v.Visit != nil { + node = nodes[i] + visited = v.Visit(node) + } else { + updated = append(updated, nodes[i:]...) + break + } + } + + return updated, true + } + } + + return nodes, false +} + +// Visits each child of a Node, possibly returning a new Node of the same kind in its place. +func (v *NodeVisitor) VisitEachChild(node *Node) *Node { + if node == nil || v.Visit == nil { + return node + } + + return node.VisitEachChild(v) +} + +func (v *NodeVisitor) visitNode(node *Node) *Node { + if v.Hooks.VisitNode != nil { + return v.Hooks.VisitNode(node, v) + } + return v.VisitNode(node) +} + +func (v *NodeVisitor) visitEmbeddedStatement(node *Node) *Node { + if v.Hooks.VisitEmbeddedStatement != nil { + return v.Hooks.VisitEmbeddedStatement(node, v) + } + if v.Hooks.VisitNode != nil { + return v.liftToBlock(v.Hooks.VisitNode(node, v)) + } + return v.VisitEmbeddedStatement(node) +} + +func (v *NodeVisitor) visitIterationBody(node *Statement) *Statement { + if v.Hooks.VisitIterationBody != nil { + return v.Hooks.VisitIterationBody(node, v) + } + return v.visitEmbeddedStatement(node) +} + +func (v *NodeVisitor) visitFunctionBody(node *BlockOrExpression) *BlockOrExpression { + if v.Hooks.VisitFunctionBody != nil { + return v.Hooks.VisitFunctionBody(node, v) + } + return v.visitNode(node) +} + +func (v *NodeVisitor) visitToken(node *Node) *Node { + if v.Hooks.VisitToken != nil { + return v.Hooks.VisitToken(node, v) + } + return v.VisitNode(node) +} + +func (v *NodeVisitor) visitNodes(nodes *NodeList) *NodeList { + if v.Hooks.VisitNodes != nil { + return v.Hooks.VisitNodes(nodes, v) + } + return v.VisitNodes(nodes) +} + +func (v *NodeVisitor) visitModifiers(nodes *ModifierList) *ModifierList { + if v.Hooks.VisitModifiers != nil { + return v.Hooks.VisitModifiers(nodes, v) + } + return v.VisitModifiers(nodes) +} + +func (v *NodeVisitor) visitParameters(nodes *ParameterList) *ParameterList { + if v.Hooks.VisitParameters != nil { + return v.Hooks.VisitParameters(nodes, v) + } + return v.visitNodes(nodes) +} + +func (v *NodeVisitor) visitTopLevelStatements(nodes *StatementList) *StatementList { + if v.Hooks.VisitTopLevelStatements != nil { + return v.Hooks.VisitTopLevelStatements(nodes, v) + } + return v.visitNodes(nodes) +} + +func (v *NodeVisitor) liftToBlock(node *Statement) *Statement { + var nodes []*Node + if node != nil { + if node.Kind == KindSyntaxList { + nodes = node.AsSyntaxList().Children + } else { + nodes = []*Node{node} + } + } + if len(nodes) == 1 { + node = nodes[0] + } else { + node = v.Factory.NewBlock(v.Factory.NewNodeList(nodes), true /*multiLine*/) + } + if node.Kind == KindSyntaxList { + panic("The result of visiting and lifting a Node may not be SyntaxList") + } + return node +} diff --git a/tools/tsgo/internal/astnav/testmain_test.go b/tools/tsgo/internal/astnav/testmain_test.go new file mode 100644 index 00000000..325d75f4 --- /dev/null +++ b/tools/tsgo/internal/astnav/testmain_test.go @@ -0,0 +1,14 @@ +package astnav_test + +import ( + "testing" + + "github.com/microsoft/typescript-go/internal/core" + "github.com/microsoft/typescript-go/internal/testutil/baseline" +) + +func TestMain(m *testing.M) { + core.ApplyDebugStackLimit() + defer baseline.Track()() + m.Run() +} diff --git a/tools/tsgo/internal/astnav/tokens.go b/tools/tsgo/internal/astnav/tokens.go new file mode 100644 index 00000000..312edac3 --- /dev/null +++ b/tools/tsgo/internal/astnav/tokens.go @@ -0,0 +1,783 @@ +package astnav + +import ( + "fmt" + + "github.com/microsoft/typescript-go/internal/ast" + "github.com/microsoft/typescript-go/internal/core" + "github.com/microsoft/typescript-go/internal/scanner" +) + +func shouldRescanLessThanLessThanToken(s *scanner.Scanner, containingNode *ast.Node, token ast.Kind) bool { + return token == ast.KindLessThanLessThanToken && ast.IsJsxChild(containingNode) +} + +func scanNavigationToken(s *scanner.Scanner, containingNode *ast.Node) ast.Kind { + token := s.Token() + if shouldRescanLessThanLessThanToken(s, containingNode, token) { + return s.ReScanJsxToken(true /*allowMultilineJsxText*/) + } + return token +} + +func GetTouchingPropertyName(sourceFile *ast.SourceFile, position int) *ast.Node { + return getTokenAtPosition(sourceFile, position, false /*allowPositionInLeadingTrivia*/, func(node *ast.Node) bool { + return ast.IsPropertyNameLiteral(node) || ast.IsKeywordKind(node.Kind) || ast.IsPrivateIdentifier(node) + }) +} + +func GetTouchingToken(sourceFile *ast.SourceFile, position int) *ast.Node { + return getTokenAtPosition(sourceFile, position, false /*allowPositionInLeadingTrivia*/, nil) +} + +func GetTokenAtPosition(sourceFile *ast.SourceFile, position int) *ast.Node { + return getTokenAtPosition(sourceFile, position, true /*allowPositionInLeadingTrivia*/, nil) +} + +func getTokenAtPosition( + sourceFile *ast.SourceFile, + position int, + allowPositionInLeadingTrivia bool, + includePrecedingTokenAtEndPosition func(node *ast.Node) bool, +) *ast.Node { + // getTokenAtPosition returns a token at the given position in the source file. + // The token can be a real node in the AST, or a synthesized token constructed + // with information from the scanner. Synthesized tokens are only created when + // needed, and they are stored in the source file's token cache such that multiple + // calls to getTokenAtPosition with the same position will return the same object + // in memory. If there is no token at the given position (possible when + // `allowPositionInLeadingTrivia` is false), the lowest node that encloses the + // position is returned. + + // `next` tracks the node whose children will be visited on the next iteration. + // `prevSubtree` is a node whose end position is equal to the target position, + // only if `includePrecedingTokenAtEndPosition` is provided. Once set, the next + // iteration of the loop will test the rightmost token of `prevSubtree` to see + // if it should be returned. + var next, prevSubtree *ast.Node + current := sourceFile.AsNode() + // `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 + // `nodeAfterLeft` tracks the first node we visit after visiting the node that advances `left`. + // When scanning in between nodes for token, we should only scan up to the start of `nodeAfterLeft`. + var nodeAfterLeft *ast.Node + + testNode := func(node *ast.Node) int { + if node.Kind != ast.KindEndOfFile && node.End() == position && + includePrecedingTokenAtEndPosition != nil && node.Flags&ast.NodeFlagsReparsed == 0 { + prevSubtree = node + } + + // A node "contains" the position if position < end, except nodes at the file end + // treat end as inclusive (there's nowhere else to look). This applies to the EOF + // token itself, and to JSDoc nodes reaching EOF (e.g. unterminated JSDoc comments). + if node.End() < position || node.End() == position && + node.Kind != ast.KindEndOfFile && + (!ast.IsJSDocKind(node.Kind) || node.End() != sourceFile.EndOfFileToken.End()) { + return -1 + } + nodePos := getPosition(node, sourceFile, allowPositionInLeadingTrivia) + if nodePos > position { + return 1 + } + return 0 + } + + // We zero in on the node that contains the target position by visiting each + // child and JSDoc comment of the current node. Node children are walked in + // order, while node lists are binary searched. + visitNode := func(node *ast.Node, _ *ast.NodeVisitor) *ast.Node { + // We can't abort visiting children, so once a match is found, we set `next` + // and do nothing on subsequent visits. + if node == nil || node.Flags&ast.NodeFlagsReparsed != 0 { + return nil + } + if nodeAfterLeft == nil { + nodeAfterLeft = node + } + if next == nil { + result := testNode(node) + switch result { + case -1: + if !ast.IsJSDocKind(node.Kind) { + // We can't move the left boundary into or beyond JSDoc, + // because we may end up returning the token after this JSDoc, + // constructing it with the scanner, and we need to include + // all its leading trivia in its position. + left = node.End() + } + nodeAfterLeft = nil + case 0: + next = node + } + } + return node + } + + visitNodeList := func(nodeList *ast.NodeList, _ *ast.NodeVisitor) *ast.NodeList { + if nodeList == nil || len(nodeList.Nodes) == 0 { + return nodeList + } + if nodeAfterLeft == nil { + for _, node := range nodeList.Nodes { + if node.Flags&ast.NodeFlagsReparsed == 0 { + nodeAfterLeft = node + break + } + } + } + if next == nil { + if nodeList.End() == position && includePrecedingTokenAtEndPosition != nil { + left = nodeList.End() + nodeAfterLeft = nil + for i := len(nodeList.Nodes) - 1; i >= 0; i-- { + if nodeList.Nodes[i].Flags&ast.NodeFlagsReparsed == 0 { + prevSubtree = nodeList.Nodes[i] + break + } + } + } else if nodeList.End() <= position { + left = nodeList.End() + nodeAfterLeft = nil + } else if nodeList.Pos() <= position { + nodes := nodeList.Nodes + index, match := core.BinarySearchUniqueFunc(nodes, func(middle int, node *ast.Node) int { + if node.Flags&ast.NodeFlagsReparsed != 0 { + return 0 + } + cmp := testNode(node) + if cmp < 0 { + left = node.End() + nodeAfterLeft = nil + for i := middle + 1; i < len(nodes); i++ { + if nodes[i].Flags&ast.NodeFlagsReparsed == 0 { + nodeAfterLeft = nodes[i] + break + } + } + } + return cmp + }) + if match && nodes[index].Flags&ast.NodeFlagsReparsed != 0 { + // filter and search again + nodes = core.Filter(nodes, func(node *ast.Node) bool { + return node.Flags&ast.NodeFlagsReparsed == 0 + }) + index, match = core.BinarySearchUniqueFunc(nodes, func(middle int, node *ast.Node) int { + cmp := testNode(node) + if cmp < 0 { + left = node.End() + if middle+1 < len(nodes) { + nodeAfterLeft = nodes[middle+1] + } else { + nodeAfterLeft = nil + } + } + return cmp + }) + } + if match { + next = nodes[index] + } + } + } + return nodeList + } + + for { + VisitEachChildAndJSDoc(current, sourceFile, visitNode, visitNodeList) + // If prevSubtree was set on the last iteration, it ends at the target position. + // Check if the rightmost token of prevSubtree should be returned based on the + // `includePrecedingTokenAtEndPosition` callback. + if prevSubtree != nil { + child := FindPrecedingTokenEx(sourceFile, position, prevSubtree, false /*excludeJSDoc*/) + if child != nil && child.End() == position && includePrecedingTokenAtEndPosition(child) { + // Optimization: includePrecedingTokenAtEndPosition only ever returns true + // for real AST nodes, so we don't run the scanner here. + return child + } + prevSubtree = nil + } + + // No node was found that contains the target position, so we've gone as deep as + // we can in the AST. We've either found a token, or we need to run the scanner + // to construct one that isn't stored in the AST. + if next == nil { + if ast.IsTokenKind(current.Kind) || shouldSkipChild(current) { + return current + } + scanner := scanner.GetScannerForSourceFile(sourceFile, left) + end := current.End() + // We should only scan up to the start of the next node in the AST after the node ending at position `left`. + // It is necessary to enforce this invariant in cases where `position` occurs in between two node/tokens, + // such that we would not find a token in the loop below before we reach the next node. + // We can fall into this case when `allowPositionInLeadingTrivia` is false and `position` is in a leading trivia, + // or when `position` would be in the leading trivia of a node but this node is inside JSDoc: + // ``` + // /** + // * @type {{ + // */*$*/ identifier: boolean; + // * }} + // */ + // ``` + // The position of marker '$' falls in between the asterisk token and the identifier token, but is not + // part of the leading trivia for `identifier`. + if nodeAfterLeft != nil { + end = nodeAfterLeft.Pos() + } + for left < end { + token := scanNavigationToken(scanner, current) + tokenFullStart := scanner.TokenFullStart() + tokenStart := core.IfElse(allowPositionInLeadingTrivia, tokenFullStart, scanner.TokenStart()) + tokenEnd := scanner.TokenEnd() + flags := scanner.TokenFlags() + if tokenEnd > end { + break + } + if tokenStart <= position && (position < tokenEnd) { + if token == ast.KindIdentifier || !ast.IsTokenKind(token) { + if ast.IsJSDocKind(current.Kind) { + return current + } + panic(fmt.Sprintf("did not expect %s to have %s in its trivia", current.Kind.String(), token.String())) + } + return sourceFile.GetOrCreateToken(token, tokenFullStart, tokenEnd, current, flags) + } + if includePrecedingTokenAtEndPosition != nil && tokenEnd == position { + prevToken := sourceFile.GetOrCreateToken(token, tokenFullStart, tokenEnd, current, flags) + if includePrecedingTokenAtEndPosition(prevToken) { + return prevToken + } + } + left = tokenEnd + scanner.Scan() + } + return current + } + current = next + left = current.Pos() + nodeAfterLeft = nil + next = nil + } +} + +func getPosition(node *ast.Node, sourceFile *ast.SourceFile, allowPositionInLeadingTrivia bool) int { + if allowPositionInLeadingTrivia { + return node.Pos() + } + return scanner.GetTokenPosOfNode(node, sourceFile, true /*includeJSDoc*/) +} + +func findRightmostNode(node *ast.Node) *ast.Node { + var next *ast.Node + current := node + visitNode := func(node *ast.Node, _ *ast.NodeVisitor) *ast.Node { + if node != nil { + next = node + } + return node + } + visitNodes := func(nodeList *ast.NodeList, visitor *ast.NodeVisitor) *ast.NodeList { + if nodeList != nil { + if rightmost := ast.FindLastVisibleNode(nodeList.Nodes); rightmost != nil { + next = rightmost + } + } + return nodeList + } + visitor := getNodeVisitor(visitNode, visitNodes) + + for { + current.VisitEachChild(visitor) + if next == nil { + return current + } + current = next + next = nil + } +} + +func VisitEachChildAndJSDoc( + node *ast.Node, + sourceFile *ast.SourceFile, + visitNode func(*ast.Node, *ast.NodeVisitor) *ast.Node, + visitNodes func(*ast.NodeList, *ast.NodeVisitor) *ast.NodeList, +) { + visitor := getNodeVisitor(visitNode, visitNodes) + for _, jsdoc := range node.JSDoc(sourceFile) { + if visitor.Hooks.VisitNode != nil { + visitor.Hooks.VisitNode(jsdoc, visitor) + } else { + visitor.VisitNode(jsdoc) + } + } + node.VisitEachChild(visitor) +} + +const ( + comparisonLessThan = -1 + comparisonEqualTo = 0 + comparisonGreaterThan = 1 +) + +// Finds the leftmost token satisfying `position < token.End()`. +// If the leftmost token satisfying `position < token.End()` is invalid, or if position +// is in the trivia of that leftmost token, +// we will find the rightmost valid token with `token.End() <= position`. +func FindPrecedingToken(sourceFile *ast.SourceFile, position int) *ast.Node { + return FindPrecedingTokenEx(sourceFile, position, nil, false) +} + +func FindPrecedingTokenEx(sourceFile *ast.SourceFile, position int, startNode *ast.Node, excludeJSDoc bool) *ast.Node { + var find func(node *ast.Node) *ast.Node + find = func(n *ast.Node) *ast.Node { + if ast.IsNonWhitespaceToken(n) && n.Kind != ast.KindEndOfFile { + return n + } + + // `foundChild` is the leftmost node that contains the target position. + // `prevChild` is the last visited child of the current node. + var foundChild, prevChild *ast.Node + visitNode := func(node *ast.Node, _ *ast.NodeVisitor) *ast.Node { + // skip synthesized nodes (that will exist now because of jsdoc handling) + if node == nil || node.Flags&ast.NodeFlagsReparsed != 0 { + return node + } + if foundChild != nil { // We cannot abort visiting children, so once the desired child is found, we do nothing. + return node + } + if position < node.End() && (prevChild == nil || prevChild.End() <= position) { + foundChild = node + } else { + prevChild = node + } + return node + } + visitNodes := func(nodeList *ast.NodeList, _ *ast.NodeVisitor) *ast.NodeList { + if foundChild != nil { + return nodeList + } + if nodeList != nil && len(nodeList.Nodes) > 0 { + nodes := nodeList.Nodes + index, match := core.BinarySearchUniqueFunc(nodes, func(middle int, _ *ast.Node) int { + // synthetic jsdoc nodes should have jsdocNode.End() <= n.Pos() + if nodes[middle].Flags&ast.NodeFlagsReparsed != 0 { + return comparisonLessThan + } + if position < nodes[middle].End() { + if middle == 0 || position >= nodes[middle-1].End() { + return comparisonEqualTo + } + return comparisonGreaterThan + } + return comparisonLessThan + }) + + if match { + foundChild = nodes[index] + } + + validLookupIndex := core.IfElse(match, index-1, len(nodes)-1) + for i := validLookupIndex; i >= 0; i-- { + if nodes[i].Flags&ast.NodeFlagsReparsed != 0 { + continue + } + if prevChild == nil { + prevChild = nodes[i] + } + } + } + return nodeList + } + VisitEachChildAndJSDoc(n, sourceFile, visitNode, visitNodes) + + if foundChild != nil { + // Note that the span of a node's tokens is [getStartOfNode(node, ...), node.end). + // Given that `position < child.end` and child has constituent tokens, we distinguish these cases: + // 1) `position` precedes `child`'s tokens or `child` has no tokens (ie: in a comment or whitespace preceding `child`): + // we need to find the last token in a previous child node or child tokens. + // 2) `position` is within the same span: we recurse on `child`. + start := GetStartOfNode(foundChild, sourceFile, !excludeJSDoc /*includeJSDoc*/) + lookInPreviousChild := start >= position || // cursor in the leading trivia or preceding tokens + !isValidPrecedingNode(foundChild, sourceFile) + if lookInPreviousChild { + if position >= foundChild.Pos() { + // Find jsdoc preceding the foundChild. + var jsDoc *ast.Node + nodeJSDoc := n.JSDoc(sourceFile) + for i := len(nodeJSDoc) - 1; i >= 0; i-- { + if nodeJSDoc[i].Pos() >= foundChild.Pos() { + jsDoc = nodeJSDoc[i] + break + } + } + if jsDoc != nil { + if !excludeJSDoc && position < jsDoc.End() { + return find(jsDoc) + } else { + return findRightmostValidToken(jsDoc.End(), sourceFile, n, position, excludeJSDoc) + } + } + return findRightmostValidToken(foundChild.Pos(), sourceFile, n, -1 /*position*/, excludeJSDoc) + } else { // Answer is in tokens between two visited children. + return findRightmostValidToken(foundChild.Pos(), sourceFile, n, position, excludeJSDoc) + } + } else { + // position is in [foundChild.getStart(), foundChild.End): recur. + return find(foundChild) + } + } + + // We have two cases here: either the position is at the end of the file, + // or the desired token is in the unvisited trailing tokens of the current node. + if position >= n.End() { + return findRightmostValidToken(n.End(), sourceFile, n, -1 /*position*/, excludeJSDoc) + } else { + return findRightmostValidToken(n.End(), sourceFile, n, position, excludeJSDoc) + } + } + + var node *ast.Node + if startNode != nil { + node = startNode + } else { + node = sourceFile.AsNode() + } + result := find(node) + if result != nil && ast.IsWhitespaceOnlyJsxText(result) { + panic("Expected result to be a non-whitespace token.") + } + return result +} + +func isValidPrecedingNode(node *ast.Node, sourceFile *ast.SourceFile) bool { + if node.Kind == ast.KindEndOfFile { + return len(node.JSDoc(sourceFile)) > 0 + } + start := GetStartOfNode(node, sourceFile, false /*includeJSDoc*/) + width := node.End() - start + return !(ast.IsWhitespaceOnlyJsxText(node) || width == 0) +} + +func GetStartOfNode(node *ast.Node, file *ast.SourceFile, includeJSDoc bool) int { + return scanner.GetTokenPosOfNode(node, file, includeJSDoc) +} + +// Looks for rightmost valid token in the range [startPos, endPos). +// If position is >= 0, looks for rightmost valid token that precedes or touches that position. +func findRightmostValidToken(endPos int, sourceFile *ast.SourceFile, containingNode *ast.Node, position int, excludeJSDoc bool) *ast.Node { + if position == -1 { + position = containingNode.End() + } + var find func(n *ast.Node, endPos int) *ast.Node + find = func(n *ast.Node, endPos int) *ast.Node { + if n == nil { + return nil + } + if ast.IsNonWhitespaceToken(n) { + return n + } + + var rightmostValidNode *ast.Node + rightmostVisitedNodes := make([]*ast.Node, 0, 1) // Nodes after the last valid node. + hasChildren := false + shouldVisitNode := func(node *ast.Node) bool { + // Node is synthetic or out of the desired range: don't visit it. + return !(node.Flags&ast.NodeFlagsReparsed != 0 || + node.End() > endPos || GetStartOfNode(node, sourceFile, !excludeJSDoc /*includeJSDoc*/) >= position) + } + visitNode := func(node *ast.Node, _ *ast.NodeVisitor) *ast.Node { + if node == nil || node.Flags&ast.NodeFlagsReparsed != 0 { + return node + } + hasChildren = true + if !shouldVisitNode(node) { + return node + } + rightmostVisitedNodes = append(rightmostVisitedNodes, node) + if isValidPrecedingNode(node, sourceFile) { + rightmostValidNode = node + rightmostVisitedNodes = rightmostVisitedNodes[:0] + } + return node + } + visitNodes := func(nodeList *ast.NodeList, _ *ast.NodeVisitor) *ast.NodeList { + if nodeList != nil && len(nodeList.Nodes) > 0 { + hasChildren = true + index, _ := core.BinarySearchUniqueFunc(nodeList.Nodes, func(middle int, node *ast.Node) int { + if node.End() > endPos { + return comparisonGreaterThan + } + return comparisonLessThan + }) + validIndex := -1 + for i := index - 1; i >= 0; i-- { + if !shouldVisitNode(nodeList.Nodes[i]) { + continue + } + if isValidPrecedingNode(nodeList.Nodes[i], sourceFile) { + validIndex = i + rightmostValidNode = nodeList.Nodes[i] + break + } + } + for i := validIndex + 1; i < index; i++ { + if !shouldVisitNode(nodeList.Nodes[i]) { + continue + } + rightmostVisitedNodes = append(rightmostVisitedNodes, nodeList.Nodes[i]) + } + } + return nodeList + } + VisitEachChildAndJSDoc(n, sourceFile, visitNode, visitNodes) + + // Three cases: + // 1. The answer is a token of `rightmostValidNode`. + // 2. The answer is one of the unvisited tokens that occur after the rightmost valid node. + // 3. The current node is a childless, token-less node. The answer is the current node. + + // Case 2: Look at unvisited trailing tokens that occur in between the rightmost visited nodes. + if !shouldSkipChild(n) { // JSDoc nodes don't include trivia tokens as children. + var startPos int + if rightmostValidNode != nil { + startPos = rightmostValidNode.End() + } else { + startPos = n.Pos() + } + scanner := scanner.GetScannerForSourceFile(sourceFile, startPos) + var tokens []*ast.Node + for _, visitedNode := range rightmostVisitedNodes { + // Trailing tokens that occur before this node. + for startPos < min(visitedNode.Pos(), position) { + token := scanNavigationToken(scanner, n) + tokenStart := scanner.TokenStart() + if tokenStart >= position { + break + } + tokenFullStart := scanner.TokenFullStart() + tokenEnd := scanner.TokenEnd() + startPos = tokenEnd + flags := scanner.TokenFlags() + tokens = append(tokens, sourceFile.GetOrCreateToken(token, tokenFullStart, tokenEnd, n, flags)) + scanner.Scan() + } + startPos = visitedNode.End() + scanner.ResetPos(startPos) + scanner.Scan() + } + // Trailing tokens after last visited node. + for startPos < min(endPos, position) { + token := scanNavigationToken(scanner, n) + tokenStart := scanner.TokenStart() + if tokenStart >= position { + break + } + tokenFullStart := scanner.TokenFullStart() + tokenEnd := scanner.TokenEnd() + startPos = tokenEnd + flags := scanner.TokenFlags() + tokens = append(tokens, sourceFile.GetOrCreateToken(token, tokenFullStart, tokenEnd, n, flags)) + scanner.Scan() + } + + lastToken := len(tokens) - 1 + // Find preceding valid token. + for i := lastToken; i >= 0; i-- { + if !ast.IsWhitespaceOnlyJsxText(tokens[i]) { + return tokens[i] + } + } + } + + // Case 3: childless node. + if !hasChildren { + if n != containingNode { + return n + } + return nil + } + // Case 1: recur on rightmostValidNode. + if rightmostValidNode != nil { + endPos = rightmostValidNode.End() + } + return find(rightmostValidNode, endPos) + } + + return find(containingNode, endPos) +} + +func FindNextToken(previousToken *ast.Node, parent *ast.Node, file *ast.SourceFile) *ast.Node { + var find func(n *ast.Node) *ast.Node + find = func(n *ast.Node) *ast.Node { + if ast.IsTokenKind(n.Kind) && n.Pos() == previousToken.End() { + // this is token that starts at the end of previous token - return it + return n + } + // Node that contains `previousToken` or occurs immediately after it. + var foundNode *ast.Node + visitNode := func(node *ast.Node, _ *ast.NodeVisitor) *ast.Node { + if node != nil && node.Flags&ast.NodeFlagsReparsed == 0 && + node.Pos() <= previousToken.End() && node.End() > previousToken.End() { + foundNode = node + } + return node + } + visitNodes := func(nodeList *ast.NodeList, _ *ast.NodeVisitor) *ast.NodeList { + if nodeList != nil && len(nodeList.Nodes) > 0 && foundNode == nil { + nodes := nodeList.Nodes + index, match := core.BinarySearchUniqueFunc(nodes, func(_ int, node *ast.Node) int { + if node.Flags&ast.NodeFlagsReparsed != 0 { + return comparisonLessThan + } + if node.Pos() > previousToken.End() { + return comparisonGreaterThan + } + if node.End() <= previousToken.Pos() { + return comparisonLessThan + } + return comparisonEqualTo + }) + if match { + foundNode = nodes[index] + } + } + return nodeList + } + VisitEachChildAndJSDoc(n, file, visitNode, visitNodes) + // Cases: + // 1. no answer exists + // 2. answer is an unvisited token + // 3. answer is in the visited found node + + // Case 3: look for the next token inside the found node. + if foundNode != nil { + return find(foundNode) + } + startPos := previousToken.End() + // Case 2: look for the next token directly. + if startPos >= n.Pos() && startPos < n.End() { + scanner := scanner.GetScannerForSourceFile(file, startPos) + token := scanner.Token() + tokenFullStart := scanner.TokenFullStart() + tokenEnd := scanner.TokenEnd() + flags := scanner.TokenFlags() + // Use tokenFullStart (which includes leading trivia) to match TS's + // findNextToken behavior where `n.pos === previousToken.end` is checked + // (TS's pos includes trivia, same as Go's Pos()/tokenFullStart). + if tokenFullStart == previousToken.End() { + return file.GetOrCreateToken(token, tokenFullStart, tokenEnd, n, flags) + } + panic(fmt.Sprintf("Expected to find next token at %d, got token %s at %d", previousToken.End(), token, tokenFullStart)) + } + // Case 3: no answer. + return nil + } + return find(parent) +} + +func getNodeVisitor( + visitNode func(*ast.Node, *ast.NodeVisitor) *ast.Node, + visitNodes func(*ast.NodeList, *ast.NodeVisitor) *ast.NodeList, +) *ast.NodeVisitor { + var wrappedVisitNode func(*ast.Node, *ast.NodeVisitor) *ast.Node + var wrappedVisitNodes func(*ast.NodeList, *ast.NodeVisitor) *ast.NodeList + if visitNode != nil { + wrappedVisitNode = func(n *ast.Node, v *ast.NodeVisitor) *ast.Node { + if ast.IsJSDocSingleCommentNodeComment(n) { + return n + } + return visitNode(n, v) + } + } + + if visitNodes != nil { + wrappedVisitNodes = func(n *ast.NodeList, v *ast.NodeVisitor) *ast.NodeList { + if ast.IsJSDocSingleCommentNodeList(n) { + return n + } + return visitNodes(n, v) + } + } + + return ast.NewNodeVisitor(core.Identity, nil, ast.NodeVisitorHooks{ + VisitNode: wrappedVisitNode, + VisitToken: wrappedVisitNode, + VisitNodes: wrappedVisitNodes, + VisitModifiers: func(modifiers *ast.ModifierList, visitor *ast.NodeVisitor) *ast.ModifierList { + if modifiers != nil { + wrappedVisitNodes(&modifiers.NodeList, visitor) + } + return modifiers + }, + }) +} + +func shouldSkipChild(node *ast.Node) bool { + return node.Kind == ast.KindJSDoc || + node.Kind == ast.KindJSDocText || + node.Kind == ast.KindJSDocTypeLiteral || + node.Kind == ast.KindJSDocSignature || + ast.IsJSDocLinkLike(node) || + ast.IsJSDocTag(node) +} + +// FindChildOfKind searches for a child node or token of the specified kind within a containing node. +// This function scans through both AST nodes and intervening tokens to find the first match. +func FindChildOfKind(containingNode *ast.Node, kind ast.Kind, sourceFile *ast.SourceFile) *ast.Node { + lastNodePos := containingNode.Pos() + scan := scanner.GetScannerForSourceFile(sourceFile, lastNodePos) + + var foundChild *ast.Node + visitNode := func(node *ast.Node) bool { + if node == nil || node.Flags&ast.NodeFlagsReparsed != 0 { + return false + } + // Look for child in preceding tokens. + startPos := lastNodePos + for startPos < node.Pos() { + tokenKind := scan.Token() + tokenEnd := scan.TokenEnd() + if tokenKind == kind { + tokenFullStart := scan.TokenFullStart() + flags := scan.TokenFlags() + foundChild = sourceFile.GetOrCreateToken(tokenKind, tokenFullStart, tokenEnd, containingNode, flags) + return true + } + startPos = tokenEnd + scan.Scan() + } + + if node.Kind == kind { + foundChild = node + return true + } + + lastNodePos = node.End() + scan.ResetPos(lastNodePos) + return false + } + + ast.ForEachChildAndJSDoc(containingNode, sourceFile, visitNode) + + if foundChild != nil { + return foundChild + } + + // Look for child in trailing tokens. + startPos := lastNodePos + for startPos < containingNode.End() { + tokenKind := scan.Token() + tokenEnd := scan.TokenEnd() + if tokenKind == kind { + tokenFullStart := scan.TokenFullStart() + flags := scan.TokenFlags() + token := sourceFile.GetOrCreateToken(tokenKind, tokenFullStart, tokenEnd, containingNode, flags) + return token + } + startPos = tokenEnd + scan.Scan() + } + return nil +} diff --git a/tools/tsgo/internal/astnav/tokens_test.go b/tools/tsgo/internal/astnav/tokens_test.go new file mode 100644 index 00000000..896bfc17 --- /dev/null +++ b/tools/tsgo/internal/astnav/tokens_test.go @@ -0,0 +1,626 @@ +package astnav_test + +import ( + "fmt" + "os" + "path/filepath" + "slices" + "strconv" + "strings" + "testing" + + "github.com/microsoft/typescript-go/internal/ast" + "github.com/microsoft/typescript-go/internal/astnav" + "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/testutil/baseline" + "github.com/microsoft/typescript-go/internal/testutil/jstest" + "gotest.tools/v3/assert" +) + +var testFiles = []string{ + filepath.Join(repo.TypeScriptSubmodulePath(), "src/services/mapCode.ts"), +} + +func TestGetTokenAtPosition(t *testing.T) { + t.Parallel() + repo.SkipIfNoTypeScriptSubmodule(t) + jstest.SkipIfNoNodeJS(t) + + t.Run("baseline", func(t *testing.T) { + t.Parallel() + baselineTokens( + t, + "GetTokenAtPosition", + false, /*includeEOF*/ + func(fileText string, positions []int) []*tokenInfo { + return tsGetTokensAtPositions(t, fileText, positions) + }, + func(file *ast.SourceFile, pos int) *tokenInfo { + return toTokenInfo(astnav.GetTokenAtPosition(file, pos)) + }, + ) + }) + + t.Run("go baseline json", func(t *testing.T) { + t.Parallel() + baselineGoTokensJSON(t, "GetTokenAtPosition", func(file *ast.SourceFile, pos int) *tokenInfo { + return toTokenInfo(astnav.GetTokenAtPosition(file, pos)) + }) + }) + + t.Run("JSDoc type assertion", func(t *testing.T) { + t.Parallel() + fileText := `function foo(x) { + const s = /**@type {string}*/(x) +}` + file := parser.ParseSourceFile(ast.SourceFileParseOptions{ + FileName: "/test.js", + Path: "/test.js", + }, fileText, core.ScriptKindJS) + + // Position of 'x' inside the parenthesized expression (position 52) + position := 52 + + // This should not panic - it previously panicked with: + // "did not expect KindParenthesizedExpression to have KindIdentifier in its trivia" + token := astnav.GetTouchingPropertyName(file, position) + if token == nil { + t.Fatal("Expected to get a token, got nil") + } + + // The function may return either the identifier itself or the containing + // parenthesized expression, depending on how the AST is structured + if token.Kind != ast.KindIdentifier && token.Kind != ast.KindParenthesizedExpression { + t.Errorf("Expected identifier or parenthesized expression, got %s", token.Kind) + } + }) + + t.Run("JSDoc type assertion with comment", func(t *testing.T) { + t.Parallel() + // Exact code from the issue report + fileText := `function foo(x) { + const s = /**@type {string}*/(x) // Go-to-definition on x causes panic +}` + file := parser.ParseSourceFile(ast.SourceFileParseOptions{ + FileName: "/test.js", + Path: "/test.js", + }, fileText, core.ScriptKindJS) + + // Find position of 'x' in the type assertion + xPos := 52 // Position of 'x' in (x) + + // This should not panic + token := astnav.GetTouchingPropertyName(file, xPos) + assert.Assert(t, token != nil, "Expected to get a token") + }) + + t.Run("pointer equality", func(t *testing.T) { + t.Parallel() + fileText := ` + function foo() { + return 0; + } + ` + file := parser.ParseSourceFile(ast.SourceFileParseOptions{ + FileName: "/file.ts", + Path: "/file.ts", + }, fileText, core.ScriptKindTS) + assert.Equal(t, astnav.GetTokenAtPosition(file, 0), astnav.GetTokenAtPosition(file, 0)) + }) +} + +func TestGetTouchingPropertyName(t *testing.T) { + t.Parallel() + jstest.SkipIfNoNodeJS(t) + repo.SkipIfNoTypeScriptSubmodule(t) + + baselineTokens( + t, + "GetTouchingPropertyName", + false, /*includeEOF*/ + func(fileText string, positions []int) []*tokenInfo { + return tsGetTouchingPropertyName(t, fileText, positions) + }, + func(file *ast.SourceFile, pos int) *tokenInfo { + return toTokenInfo(astnav.GetTouchingPropertyName(file, pos)) + }, + ) + + t.Run("go baseline json", func(t *testing.T) { + t.Parallel() + baselineGoTokensJSON(t, "GetTouchingPropertyName", func(file *ast.SourceFile, pos int) *tokenInfo { + return toTokenInfo(astnav.GetTouchingPropertyName(file, pos)) + }) + }) +} + +func baselineTokens(t *testing.T, testName string, includeEOF bool, getTSTokens func(fileText string, positions []int) []*tokenInfo, getGoToken func(file *ast.SourceFile, pos int) *tokenInfo) { + for _, fileName := range testFiles { + t.Run(filepath.Base(fileName), func(t *testing.T) { + t.Parallel() + fileText, err := os.ReadFile(fileName) + assert.NilError(t, err) + + positions := make([]int, len(fileText)+core.IfElse(includeEOF, 1, 0)) + for i := range positions { + positions[i] = i + } + tsTokens := getTSTokens(string(fileText), positions) + file := parser.ParseSourceFile(ast.SourceFileParseOptions{ + FileName: "/file.ts", + Path: "/file.ts", + }, string(fileText), core.ScriptKindTS) + + var output strings.Builder + currentRange := core.NewTextRange(0, 0) + currentDiff := tokenDiff{} + + for pos, tsToken := range tsTokens { + goToken := getGoToken(file, pos) + diff := tokenDiff{goToken: goToken, tsToken: tsToken} + + if !diffEqual(currentDiff, diff) { + if !tokensEqual(currentDiff.goToken, currentDiff.tsToken) { + writeRangeDiff(&output, file, currentDiff, currentRange, pos) + } + currentDiff = diff + currentRange = core.NewTextRange(pos, pos) + } + currentRange = currentRange.WithEnd(pos) + } + + if !tokensEqual(currentDiff.goToken, currentDiff.tsToken) { + writeRangeDiff(&output, file, currentDiff, currentRange, len(tsTokens)-1) + } + + baseline.Run( + t, + fmt.Sprintf("%s.%s.baseline.txt", testName, filepath.Base(fileName)), + core.IfElse(output.Len() > 0, output.String(), baseline.NoContent), + baseline.Options{ + Subfolder: "astnav", + }, + ) + }) + } +} + +type tokenRun struct { + StartPos int `json:"startPos"` + EndPos int `json:"endPos"` + Kind string `json:"kind"` + NodePos int `json:"nodePos"` + NodeEnd int `json:"nodeEnd"` +} + +func baselineGoTokensJSON(t *testing.T, testName string, getGoToken func(file *ast.SourceFile, pos int) *tokenInfo) { + for _, fileName := range testFiles { + t.Run(filepath.Base(fileName), func(t *testing.T) { + t.Parallel() + fileText, err := os.ReadFile(fileName) + assert.NilError(t, err) + + file := parser.ParseSourceFile(ast.SourceFileParseOptions{ + FileName: "/file.ts", + Path: "/file.ts", + }, string(fileText), core.ScriptKindTS) + + maxPos := len(fileText) + var runs []tokenRun + var current *tokenRun + + for pos := range maxPos { + token := getGoToken(file, pos) + if current != nil && token != nil && current.Kind == token.Kind && current.NodePos == token.Pos && current.NodeEnd == token.End { + current.EndPos = pos + } else { + if current != nil { + runs = append(runs, *current) + } + if token != nil { + current = &tokenRun{ + StartPos: pos, + EndPos: pos, + Kind: token.Kind, + NodePos: token.Pos, + NodeEnd: token.End, + } + } else { + current = nil + } + } + } + if current != nil { + runs = append(runs, *current) + } + + output := core.Must(core.StringifyJson(runs, "", " ")) + + baseline.Run( + t, + fmt.Sprintf("%s.%s.baseline.json", testName, filepath.Base(fileName)), + output, + baseline.Options{ + Subfolder: "astnav", + }, + ) + }) + } +} + +type tokenDiff struct { + goToken *tokenInfo + tsToken *tokenInfo +} + +type tokenInfo struct { + Kind string `json:"kind"` + Pos int `json:"pos"` + End int `json:"end"` +} + +func toTokenInfo(node *ast.Node) *tokenInfo { + if node == nil { + return nil + } + kind := strings.Replace(node.Kind.String(), "Kind", "", 1) + switch kind { + case "EndOfFile": + kind = "EndOfFileToken" + } + return &tokenInfo{ + Kind: kind, + Pos: node.Pos(), + End: node.End(), + } +} + +func diffEqual(a, b tokenDiff) bool { + return tokensEqual(a.goToken, b.goToken) && tokensEqual(a.tsToken, b.tsToken) +} + +func tokensEqual(t1, t2 *tokenInfo) bool { + if t1 == nil || t2 == nil { + return t1 == t2 + } + return *t1 == *t2 +} + +func tsGetTokensAtPositions(t testing.TB, fileText string, positions []int) []*tokenInfo { + dir := t.TempDir() + err := os.WriteFile(filepath.Join(dir, "file.ts"), []byte(fileText), 0o644) + assert.NilError(t, err) + + err = os.WriteFile(filepath.Join(dir, "positions.json"), []byte(core.Must(core.StringifyJson(positions, "", ""))), 0o644) + assert.NilError(t, err) + + script := ` + import fs from "fs"; + export default (ts) => { + const positions = JSON.parse(fs.readFileSync("positions.json", "utf8")); + const fileText = fs.readFileSync("file.ts", "utf8"); + const file = ts.createSourceFile( + "file.ts", + fileText, + { languageVersion: ts.ScriptTarget.Latest, jsDocParsingMode: ts.JSDocParsingMode.ParseAll }, + /*setParentNodes*/ true + ); + return positions.map(position => { + let token = ts.getTokenAtPosition(file, position); + if (token.kind === ts.SyntaxKind.SyntaxList) { + token = token.parent; + } + return { + kind: ts.Debug.formatSyntaxKind(token.kind), + pos: token.pos, + end: token.end, + }; + }); + };` + + info, err := jstest.EvalNodeScriptWithTS[[]*tokenInfo](t, script, dir, "") + assert.NilError(t, err) + return info +} + +func tsGetTouchingPropertyName(t testing.TB, fileText string, positions []int) []*tokenInfo { + dir := t.TempDir() + err := os.WriteFile(filepath.Join(dir, "file.ts"), []byte(fileText), 0o644) + assert.NilError(t, err) + + err = os.WriteFile(filepath.Join(dir, "positions.json"), []byte(core.Must(core.StringifyJson(positions, "", ""))), 0o644) + assert.NilError(t, err) + + script := ` + import fs from "fs"; + export default (ts) => { + const positions = JSON.parse(fs.readFileSync("positions.json", "utf8")); + const fileText = fs.readFileSync("file.ts", "utf8"); + const file = ts.createSourceFile( + "file.ts", + fileText, + { languageVersion: ts.ScriptTarget.Latest, jsDocParsingMode: ts.JSDocParsingMode.ParseAll }, + /*setParentNodes*/ true + ); + return positions.map(position => { + let token = ts.getTouchingPropertyName(file, position); + if (token.kind === ts.SyntaxKind.SyntaxList) { + token = token.parent; + } + return { + kind: ts.Debug.formatSyntaxKind(token.kind), + pos: token.pos, + end: token.end, + }; + }); + };` + + info, err := jstest.EvalNodeScriptWithTS[[]*tokenInfo](t, script, dir, "") + assert.NilError(t, err) + return info +} + +func writeRangeDiff(output *strings.Builder, file *ast.SourceFile, diff tokenDiff, rng core.TextRange, position int) { + lines := file.ECMALineMap() + + tsTokenPos := position + goTokenPos := position + tsTokenEnd := position + goTokenEnd := position + if diff.tsToken != nil { + tsTokenPos = diff.tsToken.Pos + tsTokenEnd = diff.tsToken.End + } + if diff.goToken != nil { + goTokenPos = diff.goToken.Pos + goTokenEnd = diff.goToken.End + } + tsStartLine, _ := core.PositionToLineAndByteOffset(tsTokenPos, lines) + tsEndLine, _ := core.PositionToLineAndByteOffset(tsTokenEnd, lines) + goStartLine, _ := core.PositionToLineAndByteOffset(goTokenPos, lines) + goEndLine, _ := core.PositionToLineAndByteOffset(goTokenEnd, lines) + + contextLines := 2 + startLine := min(tsStartLine, goStartLine) + endLine := max(tsEndLine, goEndLine) + markerLines := []int{tsStartLine, tsEndLine, goStartLine, goEndLine} + slices.Sort(markerLines) + contextStart := max(0, startLine-contextLines) + contextEnd := min(len(lines)-1, endLine+contextLines) + digits := len(strconv.Itoa(contextEnd)) + + shouldTruncate := func(line int) (result bool, skipTo int) { + index, _ := slices.BinarySearch(markerLines, line) + if index == 0 || index == len(markerLines) { + return false, 0 + } + low := markerLines[index-1] + high := markerLines[index] + if line-low > 5 && high-line > 5 { + return true, high - 5 + } + return false, 0 + } + + if output.Len() > 0 { + output.WriteString("\n\n") + } + + output.WriteString(fmt.Sprintf("〚Positions: [%d, %d]〛\n", rng.Pos(), rng.End())) + if diff.tsToken != nil { + output.WriteString(fmt.Sprintf("【TS: %s [%d, %d)】\n", diff.tsToken.Kind, tsTokenPos, tsTokenEnd)) + } else { + output.WriteString("【TS: nil】\n") + } + if diff.goToken != nil { + output.WriteString(fmt.Sprintf("《Go: %s [%d, %d)》\n", diff.goToken.Kind, goTokenPos, goTokenEnd)) + } else { + output.WriteString("《Go: nil》\n") + } + for line := contextStart; line <= contextEnd; line++ { + if truncate, skipTo := shouldTruncate(line); truncate { + output.WriteString(fmt.Sprintf("%s │........ %d lines omitted ........\n", strings.Repeat(" ", digits), skipTo-line+1)) + line = skipTo + } + output.WriteString(fmt.Sprintf("%*d │", digits, line+1)) + end := len(file.Text()) + 1 + if line < len(lines)-1 { + end = int(lines[line+1]) + } + for pos := int(lines[line]); pos < end; pos++ { + if pos == rng.End()+1 { + output.WriteString("〛") + } + if diff.tsToken != nil && pos == tsTokenEnd { + output.WriteString("】") + } + if diff.goToken != nil && pos == goTokenEnd { + output.WriteString("》") + } + + if diff.goToken != nil && pos == goTokenPos { + output.WriteString("《") + } + if diff.tsToken != nil && pos == tsTokenPos { + output.WriteString("【") + } + if pos == rng.Pos() { + output.WriteString("〚") + } + + if pos < len(file.Text()) { + output.WriteByte(file.Text()[pos]) + } + } + } +} + +func TestFindPrecedingToken(t *testing.T) { + t.Parallel() + repo.SkipIfNoTypeScriptSubmodule(t) + jstest.SkipIfNoNodeJS(t) + + t.Run("baseline", func(t *testing.T) { + t.Parallel() + baselineTokens( + t, + "FindPrecedingToken", + true, /*includeEOF*/ + func(fileText string, positions []int) []*tokenInfo { + return tsFindPrecedingTokens(t, fileText, positions) + }, + func(file *ast.SourceFile, pos int) *tokenInfo { + return toTokenInfo(astnav.FindPrecedingToken(file, pos)) + }, + ) + }) + + t.Run("go baseline json", func(t *testing.T) { + t.Parallel() + baselineGoTokensJSON(t, "FindPrecedingToken", func(file *ast.SourceFile, pos int) *tokenInfo { + return toTokenInfo(astnav.FindPrecedingToken(file, pos)) + }) + }) +} + +func TestFindNextToken(t *testing.T) { + t.Parallel() + repo.SkipIfNoTypeScriptSubmodule(t) + + t.Run("go baseline json", func(t *testing.T) { + t.Parallel() + baselineGoTokensJSON(t, "FindNextToken", func(file *ast.SourceFile, pos int) (result *tokenInfo) { + // FindNextToken panics (like Go's assert) when the scanner finds trivia between + // previousToken.End() and the next syntactic token. Catch those to avoid crashing + // the baseline generator; those positions will be absent from the baseline. + defer func() { + if r := recover(); r != nil { + result = nil + } + }() + token := astnav.GetTokenAtPosition(file, pos) + next := astnav.FindNextToken(token, file.AsNode(), file) + return toTokenInfo(next) + }) + }) +} + +func TestUnitFindPrecedingToken(t *testing.T) { + t.Parallel() + testCases := []struct { + name string + fileContent string + position int + expectedKind ast.Kind + }{ + { + name: "after dot in jsdoc", + fileContent: `import { + CharacterCodes, + compareStringsCaseInsensitive, + compareStringsCaseSensitive, + compareValues, + Comparison, + Debug, + endsWith, + equateStringsCaseInsensitive, + equateStringsCaseSensitive, + GetCanonicalFileName, + getDeclarationFileExtension, + getStringComparer, + identity, + lastOrUndefined, + Path, + some, + startsWith, +} from "./_namespaces/ts.js"; + +/** + * Internally, we represent paths as strings with '/' as the directory separator. + * When we make system calls (eg: LanguageServiceHost.getDirectory()), + * we expect the host to correctly handle paths in our specified format. + * + * @internal + */ +export const directorySeparator = "/"; +/** @internal */ +export const altDirectorySeparator = "\\"; +const urlSchemeSeparator = "://"; +const backslashRegExp = /\\/g; + + +backslashRegExp. + +//Path Tests + +/** + * Determines whether a charCode corresponds to '/' or '\'. + * + * @internal + */ +export function isAnyDirectorySeparator(charCode: number): boolean { + return charCode === CharacterCodes.slash || charCode === CharacterCodes.backslash; +}`, + position: 839, + expectedKind: ast.KindDotToken, + }, + { + name: "after comma in parameter list", + fileContent: `takesCb((n, s, ))`, + position: 15, + expectedKind: ast.KindCommaToken, + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + t.Parallel() + file := parser.ParseSourceFile(ast.SourceFileParseOptions{ + FileName: "/file.ts", + Path: "/file.ts", + }, testCase.fileContent, core.ScriptKindTS) + token := astnav.FindPrecedingToken(file, testCase.position) + assert.Equal(t, token.Kind, testCase.expectedKind) + }) + } +} + +func tsFindPrecedingTokens(t *testing.T, fileText string, positions []int) []*tokenInfo { + dir := t.TempDir() + err := os.WriteFile(filepath.Join(dir, "file.ts"), []byte(fileText), 0o644) + assert.NilError(t, err) + + err = os.WriteFile(filepath.Join(dir, "positions.json"), []byte(core.Must(core.StringifyJson(positions, "", ""))), 0o644) + assert.NilError(t, err) + + script := ` + import fs from "fs"; + export default (ts) => { + const positions = JSON.parse(fs.readFileSync("positions.json", "utf8")); + const fileText = fs.readFileSync("file.ts", "utf8"); + const file = ts.createSourceFile( + "file.ts", + fileText, + { languageVersion: ts.ScriptTarget.Latest, jsDocParsingMode: ts.JSDocParsingMode.ParseAll }, + /*setParentNodes*/ true + ); + return positions.map(position => { + let token = ts.findPrecedingToken(position, file); + if (token === undefined) { + return undefined; + } + if (token.kind === ts.SyntaxKind.SyntaxList) { + token = token.parent; + } + return { + kind: ts.Debug.formatSyntaxKind(token.kind), + pos: token.pos, + end: token.end, + }; + }); + };` + info, err := jstest.EvalNodeScriptWithTS[[]*tokenInfo](t, script, dir, "") + assert.NilError(t, err) + return info +} diff --git a/tools/tsgo/internal/binder/binder.go b/tools/tsgo/internal/binder/binder.go new file mode 100644 index 00000000..c029ef3b --- /dev/null +++ b/tools/tsgo/internal/binder/binder.go @@ -0,0 +1,2795 @@ +package binder + +import ( + "slices" + "strconv" + "sync" + + "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/debug" + "github.com/microsoft/typescript-go/internal/diagnostics" + "github.com/microsoft/typescript-go/internal/scanner" + "github.com/microsoft/typescript-go/internal/tspath" +) + +type ContainerFlags int32 + +const ( + // The current node is not a container, and no container manipulation should happen before + // recursing into it. + ContainerFlagsNone ContainerFlags = 0 + // The current node is a container. It should be set as the current container (and block- + // container) before recursing into it. The current node does not have locals. Examples: + // + // Classes, ObjectLiterals, TypeLiterals, Interfaces... + ContainerFlagsIsContainer ContainerFlags = 1 << 0 + // The current node is a block-scoped-container. It should be set as the current block- + // container before recursing into it. Examples: + // + // Blocks (when not parented by functions), Catch clauses, For/For-in/For-of statements... + ContainerFlagsIsBlockScopedContainer ContainerFlags = 1 << 1 + // The current node is the container of a control flow path. The current control flow should + // be saved and restored, and a new control flow initialized within the container. + ContainerFlagsIsControlFlowContainer ContainerFlags = 1 << 2 + ContainerFlagsIsFunctionLike ContainerFlags = 1 << 3 + ContainerFlagsIsFunctionExpression ContainerFlags = 1 << 4 + ContainerFlagsHasLocals ContainerFlags = 1 << 5 + ContainerFlagsIsInterface ContainerFlags = 1 << 6 + ContainerFlagsIsObjectLiteralOrClassExpressionMethodOrAccessor ContainerFlags = 1 << 7 + ContainerFlagsIsThisContainer ContainerFlags = 1 << 8 + ContainerFlagsPropagatesThisKeyword ContainerFlags = 1 << 9 +) + +type ExpandoAssignmentInfo struct { + node *ast.Node + container *ast.Node + blockScopeContainer *ast.Node +} + +type Binder struct { + file *ast.SourceFile + bindFunc func(*ast.Node) bool + unreachableFlow *ast.FlowNode + + container *ast.Node + thisContainer *ast.Node + blockScopeContainer *ast.Node + lastContainer *ast.Node + currentFlow *ast.FlowNode + currentBreakTarget *ast.FlowLabel + currentContinueTarget *ast.FlowLabel + currentReturnTarget *ast.FlowLabel + currentTrueTarget *ast.FlowLabel + currentFalseTarget *ast.FlowLabel + currentExceptionTarget *ast.FlowLabel + preSwitchCaseFlow *ast.FlowNode + activeLabelList *ActiveLabel + emitFlags ast.NodeFlags + seenThisKeyword bool + hasExplicitReturn bool + hasFlowEffects bool + inAssignmentPattern bool + seenParseError bool + symbolCount int + classifiableNames collections.Set[string] + notConstEnumOnlyModules collections.Set[*ast.Symbol] + symbolArena core.Arena[ast.Symbol] + flowNodeArena core.Arena[ast.FlowNode] + flowListArena core.Arena[ast.FlowList] + singleDeclarationsArena core.Arena[*ast.Node] + expandoAssignments []ExpandoAssignmentInfo +} + +type ActiveLabel struct { + next *ActiveLabel + breakTarget *ast.FlowLabel + continueTarget *ast.FlowLabel + name string + referenced bool +} + +func (label *ActiveLabel) BreakTarget() *ast.FlowNode { return label.breakTarget } +func (label *ActiveLabel) ContinueTarget() *ast.FlowNode { return label.continueTarget } + +func BindSourceFile(file *ast.SourceFile) { + // This is constructed this way to make the compiler "out-line" the function, + // avoiding most work in the common case where the file has already been bound. + if !file.IsBound() { + bindSourceFile(file) + } +} + +var binderPool = sync.Pool{ + New: func() any { + b := &Binder{} + b.bindFunc = b.bind // Allocate closure once + return b + }, +} + +func getBinder() *Binder { + return binderPool.Get().(*Binder) +} + +func putBinder(b *Binder) { + *b = Binder{bindFunc: b.bindFunc} + binderPool.Put(b) +} + +func bindSourceFile(file *ast.SourceFile) { + file.BindOnce(func() { + b := getBinder() + defer putBinder(b) + b.file = file + b.unreachableFlow = b.newFlowNode(ast.FlowFlagsUnreachable) + b.bind(file.AsNode()) + b.bindDeferredExpandoAssignments() + file.SymbolCount = b.symbolCount + file.ClassifiableNames = b.classifiableNames + }) +} + +func (b *Binder) newSymbol(flags ast.SymbolFlags, name string) *ast.Symbol { + b.symbolCount++ + result := b.symbolArena.New() + result.Flags = flags + result.Name = name + return result +} + +/** + * Declares a Symbol for the node and adds it to symbols. Reports errors for conflicting identifier names. + * @param symbolTable - The symbol table which node will be added to. + * @param parent - node's parent declaration. + * @param node - The declaration to be added to the symbol table + * @param includes - The SymbolFlags that node has in addition to its declaration type (eg: export, ambient, etc.) + * @param excludes - The flags which node cannot be declared alongside in a symbol table. Used to report forbidden declarations. + */ +func (b *Binder) declareSymbol(symbolTable ast.SymbolTable, parent *ast.Symbol, node *ast.Node, includes ast.SymbolFlags, excludes ast.SymbolFlags) *ast.Symbol { + return b.declareSymbolEx(symbolTable, parent, node, includes, excludes, false /*isReplaceableByMethod*/, false /*isComputedName*/) +} + +func (b *Binder) declareSymbolEx(symbolTable ast.SymbolTable, parent *ast.Symbol, node *ast.Node, includes ast.SymbolFlags, excludes ast.SymbolFlags, isReplaceableByMethod bool, isComputedName bool) *ast.Symbol { + debug.Assert(isComputedName || !ast.HasDynamicName(node)) + isDefaultExport := ast.HasSyntacticModifier(node, ast.ModifierFlagsDefault) || ast.IsExportSpecifier(node) && ast.ModuleExportNameIsDefault(node.AsExportSpecifier().Name()) + // The exported symbol for an export default function/class node is always named "default" + var name string + switch { + case isComputedName: + name = ast.InternalSymbolNameComputed + case isDefaultExport && parent != nil: + name = ast.InternalSymbolNameDefault + default: + name = b.getDeclarationName(node) + } + var symbol *ast.Symbol + if name == ast.InternalSymbolNameMissing { + symbol = b.newSymbol(ast.SymbolFlagsNone, ast.InternalSymbolNameMissing) + } else { + // Check and see if the symbol table already has a symbol with this name. If not, + // create a new symbol with this name and add it to the table. Note that we don't + // give the new symbol any flags *yet*. This ensures that it will not conflict + // with the 'excludes' flags we pass in. + // + // If we do get an existing symbol, see if it conflicts with the new symbol we're + // creating. For example, a 'var' symbol and a 'class' symbol will conflict within + // the same symbol table. If we have a conflict, report the issue on each + // declaration we have for this symbol, and then create a new symbol for this + // declaration. + // + // Note that when properties declared in Javascript constructors + // (marked by isReplaceableByMethod) conflict with another symbol, the property loses. + // Always. This allows the common Javascript pattern of overwriting a prototype method + // with an bound instance method of the same type: `this.method = this.method.bind(this)` + // + // If we created a new symbol, either because we didn't have a symbol with this name + // in the symbol table, or we conflicted with an existing symbol, then just add this + // node as the sole declaration of the new symbol. + // + // Otherwise, we'll be merging into a compatible existing symbol (for example when + // you have multiple 'vars' with the same name in the same container). In this case + // just add this node into the declarations list of the symbol. + symbol = symbolTable[name] + if includes&ast.SymbolFlagsClassifiable != 0 { + b.classifiableNames.Add(name) + } + if symbol == nil { + symbol = b.newSymbol(ast.SymbolFlagsNone, name) + symbolTable[name] = symbol + if isReplaceableByMethod { + symbol.Flags |= ast.SymbolFlagsReplaceableByMethod + } + } else if isReplaceableByMethod && symbol.Flags&ast.SymbolFlagsReplaceableByMethod == 0 { + // A symbol already exists, so don't add this as a declaration. + return symbol + } else if symbol.Flags&excludes != 0 { + if symbol.Flags&ast.SymbolFlagsReplaceableByMethod != 0 { + // Javascript constructor-declared symbols can be discarded in favor of + // prototype symbols like methods. + symbol = b.newSymbol(ast.SymbolFlagsNone, name) + symbolTable[name] = symbol + } else if !(includes&ast.SymbolFlagsVariable != 0 && symbol.Flags&ast.SymbolFlagsAssignment != 0 || + includes&ast.SymbolFlagsAssignment != 0 && symbol.Flags&ast.SymbolFlagsVariable != 0) { + // Assignment declarations are allowed to merge with variables, no matter what other flags they have. + // Report errors every position with duplicate declaration + // Report errors on previous encountered declarations + var message *diagnostics.Message + if symbol.Flags&ast.SymbolFlagsBlockScopedVariable != 0 { + message = diagnostics.Cannot_redeclare_block_scoped_variable_0 + } else { + message = diagnostics.Duplicate_identifier_0 + } + messageNeedsName := true + if symbol.Flags&ast.SymbolFlagsEnum != 0 || includes&ast.SymbolFlagsEnum != 0 { + message = diagnostics.Enum_declarations_can_only_merge_with_namespace_or_other_enum_declarations + messageNeedsName = false + } + multipleDefaultExports := false + if len(symbol.Declarations) != 0 { + // If the current node is a default export of some sort, then check if + // there are any other default exports that we need to error on. + // We'll know whether we have other default exports depending on if `symbol` already has a declaration list set. + if isDefaultExport { + message = diagnostics.A_module_cannot_have_multiple_default_exports + messageNeedsName = false + multipleDefaultExports = true + } else { + // This is to properly report an error in the case "export default { }" is after export default of class declaration or function declaration. + // Error on multiple export default in the following case: + // 1. multiple export default of class declaration or function declaration by checking NodeFlags.Default + // 2. multiple export default of export assignment. This one doesn't have NodeFlags.Default on (as export default doesn't considered as modifiers) + if len(symbol.Declarations) != 0 && ast.IsExportAssignment(node) && !node.AsExportAssignment().IsExportEquals { + message = diagnostics.A_module_cannot_have_multiple_default_exports + messageNeedsName = false + multipleDefaultExports = true + } + } + } + var declarationName *ast.Node = ast.GetNameOfDeclaration(node) + if declarationName == nil { + declarationName = node + } + var diag *ast.Diagnostic + if messageNeedsName { + diag = b.createDiagnosticForNode(declarationName, message, b.getDisplayName(node)) + } else { + diag = b.createDiagnosticForNode(declarationName, message) + } + if ast.IsTypeAliasDeclaration(node) && ast.NodeIsMissing(node.Type()) && ast.HasSyntacticModifier(node, ast.ModifierFlagsExport) && symbol.Flags&(ast.SymbolFlagsAlias|ast.SymbolFlagsType|ast.SymbolFlagsNamespace) != 0 { + // export type T; - may have meant export type { T }? + diag.AddRelatedInfo(b.createDiagnosticForNode(node, diagnostics.Did_you_mean_0, "export type { "+node.AsTypeAliasDeclaration().Name().Text()+" }")) + } + for index, declaration := range symbol.Declarations { + var decl *ast.Node = ast.GetNameOfDeclaration(declaration) + if decl == nil { + decl = declaration + } + var d *ast.Diagnostic + if messageNeedsName { + d = b.createDiagnosticForNode(decl, message, b.getDisplayName(declaration)) + } else { + d = b.createDiagnosticForNode(decl, message) + } + if multipleDefaultExports { + d.AddRelatedInfo(b.createDiagnosticForNode(declarationName, core.IfElse(index == 0, diagnostics.Another_export_default_is_here, diagnostics.X_and_here))) + } + b.addDiagnostic(d) + if multipleDefaultExports { + diag.AddRelatedInfo(b.createDiagnosticForNode(decl, diagnostics.The_first_export_default_is_here)) + } + } + b.addDiagnostic(diag) + // When get or set accessor conflicts with a non-accessor or an accessor of a different kind, we mark + // the symbol as a full accessor such that all subsequent declarations are considered conflicting. This + // for example ensures that a get accessor followed by a non-accessor followed by a set accessor with the + // same name are all marked as duplicates. + if symbol.Flags&ast.SymbolFlagsAccessor != 0 && symbol.Flags&ast.SymbolFlagsAccessor != includes&ast.SymbolFlagsAccessor { + symbol.Flags |= ast.SymbolFlagsAccessor + } + symbol = b.newSymbol(ast.SymbolFlagsNone, name) + } + } + } + b.addDeclarationToSymbol(symbol, node, includes) + if symbol.Parent == nil { + symbol.Parent = parent + } else if symbol.Parent != parent { + panic("Existing symbol parent should match new one") + } + return symbol +} + +// Should not be called on a declaration with a computed property name, +// unless it is a well known Symbol. +func (b *Binder) getDeclarationName(node *ast.Node) string { + if ast.IsExportAssignment(node) { + return core.IfElse(node.AsExportAssignment().IsExportEquals, ast.InternalSymbolNameExportEquals, ast.InternalSymbolNameDefault) + } + name := ast.GetNameOfDeclaration(node) + if name != nil { + if ast.IsAmbientModule(node) { + moduleName := name.Text() + if ast.IsGlobalScopeAugmentation(node) { + return ast.InternalSymbolNameGlobal + } + return "\"" + moduleName + "\"" + } + if ast.IsPrivateIdentifier(name) { + // containingClass exists because private names only allowed inside classes + containingClass := ast.GetContainingClass(node) + if containingClass == nil { + // we can get here in cases where there is already a parse error. + return ast.InternalSymbolNameMissing + } + return GetSymbolNameForPrivateIdentifier(containingClass.Symbol(), name.Text()) + } + if ast.IsPropertyNameLiteral(name) || ast.IsJsxNamespacedName(name) { + return name.Text() + } + if ast.IsComputedPropertyName(name) { + nameExpression := name.Expression() + // treat computed property names where expression is string/numeric literal as just string/numeric literal + if ast.IsStringOrNumericLiteralLike(nameExpression) { + return nameExpression.Text() + } + if ast.IsSignedNumericLiteral(nameExpression) { + unaryExpression := nameExpression.AsPrefixUnaryExpression() + return scanner.TokenToString(unaryExpression.Operator) + unaryExpression.Operand.Text() + } + panic("Only computed properties with literal names have declaration names") + } + return ast.InternalSymbolNameMissing + } + switch node.Kind { + case ast.KindConstructor: + return ast.InternalSymbolNameConstructor + case ast.KindFunctionType, ast.KindCallSignature: + return ast.InternalSymbolNameCall + case ast.KindConstructorType, ast.KindConstructSignature: + return ast.InternalSymbolNameNew + case ast.KindIndexSignature: + return ast.InternalSymbolNameIndex + case ast.KindExportDeclaration: + return ast.InternalSymbolNameExportStar + case ast.KindSourceFile, ast.KindBinaryExpression: + return ast.InternalSymbolNameExportEquals + } + return ast.InternalSymbolNameMissing +} + +func (b *Binder) getDisplayName(node *ast.Node) string { + nameNode := node.Name() + if nameNode != nil { + return scanner.DeclarationNameToString(nameNode) + } + name := b.getDeclarationName(node) + if name != ast.InternalSymbolNameMissing { + return name + } + return "(Missing)" +} + +func GetSymbolNameForPrivateIdentifier(containingClassSymbol *ast.Symbol, description string) string { + return ast.InternalSymbolNamePrefix + "#" + strconv.Itoa(int(ast.GetSymbolId(containingClassSymbol))) + "@" + description +} + +func (b *Binder) declareModuleMember(node *ast.Node, symbolFlags ast.SymbolFlags, symbolExcludes ast.SymbolFlags) *ast.Symbol { + container := b.container + hasExportModifier := ast.GetCombinedModifierFlags(node)&ast.ModifierFlagsExport != 0 || ast.IsImplicitlyExportedJSDocDeclaration(node) + if symbolFlags&ast.SymbolFlagsAlias != 0 { + if node.Kind == ast.KindExportSpecifier || (node.Kind == ast.KindImportEqualsDeclaration && hasExportModifier) { + return b.declareSymbol(ast.GetExports(container.Symbol()), container.Symbol(), node, symbolFlags, symbolExcludes) + } + return b.declareSymbol(ast.GetLocals(container), nil /*parent*/, node, symbolFlags, symbolExcludes) + } + // Exported module members are given 2 symbols: A local symbol that is classified with an ExportValue flag, + // and an associated export symbol with all the correct flags set on it. There are 2 main reasons: + // + // 1. We treat locals and exports of the same name as mutually exclusive within a container. + // That means the binder will issue a Duplicate Identifier error if you mix locals and exports + // with the same name in the same container. + // TODO: Make this a more specific error and decouple it from the exclusion logic. + // 2. When we checkIdentifier in the checker, we set its resolved symbol to the local symbol, + // but return the export symbol (by calling getExportSymbolOfValueSymbolIfExported). That way + // when the emitter comes back to it, it knows not to qualify the name if it was found in a containing scope. + // + // NOTE: Nested ambient modules always should go to to 'locals' table to prevent their automatic merge + // during global merging in the checker. Why? The only case when ambient module is permitted inside another module is module augmentation + // and this case is specially handled. Module augmentations should only be merged with original module definition + // and should never be merged directly with other augmentation, and the latter case would be possible if automatic merge is allowed. + if !ast.IsAmbientModule(node) && (hasExportModifier || container.Flags&ast.NodeFlagsExportContext != 0) { + if !ast.IsLocalsContainer(container) || (ast.HasSyntacticModifier(node, ast.ModifierFlagsDefault) && b.getDeclarationName(node) == ast.InternalSymbolNameMissing) { + return b.declareSymbol(ast.GetExports(container.Symbol()), container.Symbol(), node, symbolFlags, symbolExcludes) + // No local symbol for an unnamed default! + } + exportKind := ast.SymbolFlagsNone + if symbolFlags&ast.SymbolFlagsValue != 0 { + exportKind = ast.SymbolFlagsExportValue + } + local := b.declareSymbol(ast.GetLocals(container), nil /*parent*/, node, exportKind, symbolExcludes) + local.ExportSymbol = b.declareSymbol(ast.GetExports(container.Symbol()), container.Symbol(), node, symbolFlags, symbolExcludes) + node.ExportableData().LocalSymbol = local + return local + } + return b.declareSymbol(ast.GetLocals(container), nil /*parent*/, node, symbolFlags, symbolExcludes) +} + +func (b *Binder) declareClassMember(node *ast.Node, symbolFlags ast.SymbolFlags, symbolExcludes ast.SymbolFlags) *ast.Symbol { + if ast.IsStatic(node) { + return b.declareSymbol(ast.GetExports(b.container.Symbol()), b.container.Symbol(), node, symbolFlags, symbolExcludes) + } + return b.declareSymbol(ast.GetMembers(b.container.Symbol()), b.container.Symbol(), node, symbolFlags, symbolExcludes) +} + +func (b *Binder) declareSourceFileMember(node *ast.Node, symbolFlags ast.SymbolFlags, symbolExcludes ast.SymbolFlags) *ast.Symbol { + if ast.IsExternalModule(b.file) { + return b.declareModuleMember(node, symbolFlags, symbolExcludes) + } + return b.declareSymbol(ast.GetLocals(b.file.AsNode()), nil /*parent*/, node, symbolFlags, symbolExcludes) +} + +func (b *Binder) declareSymbolAndAddToSymbolTable(node *ast.Node, symbolFlags ast.SymbolFlags, symbolExcludes ast.SymbolFlags) *ast.Symbol { + switch b.container.Kind { + case ast.KindModuleDeclaration: + return b.declareModuleMember(node, symbolFlags, symbolExcludes) + case ast.KindSourceFile: + return b.declareSourceFileMember(node, symbolFlags, symbolExcludes) + case ast.KindClassExpression, ast.KindClassDeclaration: + return b.declareClassMember(node, symbolFlags, symbolExcludes) + case ast.KindEnumDeclaration: + return b.declareSymbol(ast.GetExports(b.container.Symbol()), b.container.Symbol(), node, symbolFlags, symbolExcludes) + case ast.KindTypeLiteral, ast.KindObjectLiteralExpression, ast.KindInterfaceDeclaration, ast.KindJsxAttributes: + return b.declareSymbol(ast.GetMembers(b.container.Symbol()), b.container.Symbol(), node, symbolFlags, symbolExcludes) + case ast.KindFunctionType, ast.KindConstructorType, ast.KindCallSignature, ast.KindConstructSignature, + ast.KindIndexSignature, ast.KindMethodDeclaration, ast.KindMethodSignature, ast.KindConstructor, ast.KindGetAccessor, + ast.KindSetAccessor, ast.KindFunctionDeclaration, ast.KindFunctionExpression, ast.KindArrowFunction, + ast.KindClassStaticBlockDeclaration, ast.KindTypeAliasDeclaration, ast.KindJSTypeAliasDeclaration, ast.KindMappedType: + return b.declareSymbol(ast.GetLocals(b.container), nil /*parent*/, node, symbolFlags, symbolExcludes) + } + panic("Unhandled case in declareSymbolAndAddToSymbolTable") +} + +func (b *Binder) newFlowNode(flags ast.FlowFlags) *ast.FlowNode { + result := b.flowNodeArena.New() + result.Flags = flags + return result +} + +func (b *Binder) newFlowNodeEx(flags ast.FlowFlags, node *ast.Node, antecedent *ast.FlowNode) *ast.FlowNode { + result := b.newFlowNode(flags) + result.Node = node + result.Antecedent = antecedent + return result +} + +func (b *Binder) createLoopLabel() *ast.FlowLabel { + return b.newFlowNode(ast.FlowFlagsLoopLabel) +} + +func (b *Binder) createBranchLabel() *ast.FlowLabel { + return b.newFlowNode(ast.FlowFlagsBranchLabel) +} + +func (b *Binder) createReduceLabel(target *ast.FlowLabel, antecedents *ast.FlowList, antecedent *ast.FlowNode) *ast.FlowNode { + return b.newFlowNodeEx(ast.FlowFlagsReduceLabel, ast.NewFlowReduceLabelData(target, antecedents), antecedent) +} + +func (b *Binder) createFlowCondition(flags ast.FlowFlags, antecedent *ast.FlowNode, expression *ast.Node) *ast.FlowNode { + if antecedent.Flags&ast.FlowFlagsUnreachable != 0 { + return antecedent + } + if expression == nil { + if flags&ast.FlowFlagsTrueCondition != 0 { + return antecedent + } + return b.unreachableFlow + } + if (expression.Kind == ast.KindTrueKeyword && flags&ast.FlowFlagsFalseCondition != 0 || expression.Kind == ast.KindFalseKeyword && flags&ast.FlowFlagsTrueCondition != 0) && !ast.IsExpressionOfOptionalChainRoot(expression) && !ast.IsNullishCoalesce(expression.Parent) { + return b.unreachableFlow + } + if !isNarrowingExpression(expression) { + return antecedent + } + setFlowNodeReferenced(antecedent) + return b.newFlowNodeEx(flags, expression, antecedent) +} + +func (b *Binder) createFlowMutation(flags ast.FlowFlags, antecedent *ast.FlowNode, node *ast.Node) *ast.FlowNode { + setFlowNodeReferenced(antecedent) + b.hasFlowEffects = true + result := b.newFlowNodeEx(flags, node, antecedent) + if b.currentExceptionTarget != nil { + b.addAntecedent(b.currentExceptionTarget, result) + } + return result +} + +func (b *Binder) createFlowSwitchClause(antecedent *ast.FlowNode, switchStatement *ast.Node, clauseStart int, clauseEnd int) *ast.FlowNode { + setFlowNodeReferenced(antecedent) + return b.newFlowNodeEx(ast.FlowFlagsSwitchClause, ast.NewFlowSwitchClauseData(switchStatement, clauseStart, clauseEnd), antecedent) +} + +func (b *Binder) createFlowCall(antecedent *ast.FlowNode, node *ast.Node) *ast.FlowNode { + setFlowNodeReferenced(antecedent) + b.hasFlowEffects = true + return b.newFlowNodeEx(ast.FlowFlagsCall, node, antecedent) +} + +func (b *Binder) newFlowList(head *ast.FlowNode, tail *ast.FlowList) *ast.FlowList { + result := b.flowListArena.New() + result.Flow = head + result.Next = tail + return result +} + +func (b *Binder) combineFlowLists(head *ast.FlowList, tail *ast.FlowList) *ast.FlowList { + if head == nil { + return tail + } + return b.newFlowList(head.Flow, b.combineFlowLists(head.Next, tail)) +} + +func (b *Binder) newSingleDeclaration(declaration *ast.Node) []*ast.Node { + return b.singleDeclarationsArena.NewSlice1(declaration) +} + +func setFlowNodeReferenced(flow *ast.FlowNode) { + // On first reference we set the Referenced flag, thereafter we set the Shared flag + if flow.Flags&ast.FlowFlagsReferenced == 0 { + flow.Flags |= ast.FlowFlagsReferenced + } else { + flow.Flags |= ast.FlowFlagsShared + } +} + +func (b *Binder) addAntecedent(label *ast.FlowLabel, antecedent *ast.FlowNode) { + if antecedent.Flags&ast.FlowFlagsUnreachable != 0 { + return + } + // If antecedent isn't already on the Antecedents list, add it to the end of the list + var last *ast.FlowList + for list := label.Antecedents; list != nil; list = list.Next { + if list.Flow == antecedent { + return + } + last = list + } + if last == nil { + label.Antecedents = b.newFlowList(antecedent, nil) + } else { + last.Next = b.newFlowList(antecedent, nil) + } + setFlowNodeReferenced(antecedent) +} + +func (b *Binder) finishFlowLabel(label *ast.FlowLabel) *ast.FlowNode { + if label.Antecedents == nil { + return b.unreachableFlow + } + if label.Antecedents.Next == nil { + return label.Antecedents.Flow + } + return label +} + +func (b *Binder) bind(node *ast.Node) bool { + if node == nil { + return false + } + // Even though in the AST the jsdoc @typedef node belongs to the current node, + // its symbol might be in the same scope with the current node's symbol. Consider: + // + // /** @typedef {string | number} MyType */ + // function foo(); + // + // Here the current node is "foo", which is a container, but the scope of "MyType" should + // not be inside "foo". Therefore we always bind @typedef before bind the parent node, + // and skip binding this tag later when binding all the other jsdoc tags. + + // First we bind declaration nodes to a symbol if possible. We'll both create a symbol + // and then potentially add the symbol to an appropriate symbol table. Possible + // destination symbol tables are: + // + // 1) The 'exports' table of the current container's symbol. + // 2) The 'members' table of the current container's symbol. + // 3) The 'locals' table of the current container. + // + // However, not all symbols will end up in any of these tables. 'Anonymous' symbols + // (like TypeLiterals for example) will not be put in any table. + switch node.Kind { + case ast.KindIdentifier: + node.AsIdentifier().FlowNode = b.currentFlow + b.checkContextualIdentifier(node) + case ast.KindThisKeyword, ast.KindSuperKeyword: + if node.Kind == ast.KindThisKeyword { + b.seenThisKeyword = true + } + node.AsKeywordExpression().FlowNode = b.currentFlow + case ast.KindQualifiedName: + if b.currentFlow != nil && ast.IsPartOfTypeQuery(node) { + node.AsQualifiedName().FlowNode = b.currentFlow + } + case ast.KindMetaProperty: + node.AsMetaProperty().FlowNode = b.currentFlow + case ast.KindPrivateIdentifier: + b.checkPrivateIdentifier(node) + case ast.KindPropertyAccessExpression, ast.KindElementAccessExpression: + if b.currentFlow != nil && isNarrowableReference(node) { + setFlowNode(node, b.currentFlow) + } + case ast.KindBinaryExpression: + switch ast.GetAssignmentDeclarationKind(node) { + case ast.JSDeclarationKindModuleExports: + b.bindModuleExportsAssignment(node) + case ast.JSDeclarationKindExportsProperty: + b.bindExportsOrObjectDefineProperty(node) + case ast.JSDeclarationKindProperty: + b.bindExpandoPropertyAssignment(node) + case ast.JSDeclarationKindThisProperty: + b.bindThisPropertyAssignment(node) + } + b.checkStrictModeBinaryExpression(node) + case ast.KindCatchClause: + b.checkStrictModeCatchClause(node) + case ast.KindDeleteExpression: + b.checkStrictModeDeleteExpression(node) + case ast.KindPostfixUnaryExpression: + b.checkStrictModePostfixUnaryExpression(node) + case ast.KindPrefixUnaryExpression: + b.checkStrictModePrefixUnaryExpression(node) + case ast.KindWithStatement: + b.checkStrictModeWithStatement(node) + case ast.KindLabeledStatement: + b.checkStrictModeLabeledStatement(node) + case ast.KindThisType: + b.seenThisKeyword = true + case ast.KindTypeParameter: + b.bindTypeParameter(node) + case ast.KindParameter: + b.bindParameter(node) + case ast.KindVariableDeclaration: + b.bindVariableDeclarationOrBindingElement(node) + case ast.KindBindingElement: + node.AsBindingElement().FlowNode = b.currentFlow + b.bindVariableDeclarationOrBindingElement(node) + case ast.KindPropertyDeclaration, ast.KindPropertySignature: + b.bindPropertyWorker(node) + case ast.KindPropertyAssignment, ast.KindShorthandPropertyAssignment: + b.bindPropertyOrMethodOrAccessor(node, ast.SymbolFlagsProperty, ast.SymbolFlagsPropertyExcludes) + case ast.KindEnumMember: + b.bindPropertyOrMethodOrAccessor(node, ast.SymbolFlagsEnumMember, ast.SymbolFlagsEnumMemberExcludes) + case ast.KindCallSignature, ast.KindConstructSignature, ast.KindIndexSignature: + b.declareSymbolAndAddToSymbolTable(node, ast.SymbolFlagsSignature, ast.SymbolFlagsNone) + case ast.KindMethodDeclaration, ast.KindMethodSignature: + b.bindPropertyOrMethodOrAccessor(node, ast.SymbolFlagsMethod|getOptionalSymbolFlagForNode(node), core.IfElse(ast.IsObjectLiteralMethod(node), ast.SymbolFlagsValue, ast.SymbolFlagsMethodExcludes)) + case ast.KindFunctionDeclaration: + b.bindFunctionDeclaration(node) + case ast.KindConstructor: + b.declareSymbolAndAddToSymbolTable(node, ast.SymbolFlagsConstructor, ast.SymbolFlagsNone) + case ast.KindGetAccessor: + b.bindPropertyOrMethodOrAccessor(node, ast.SymbolFlagsGetAccessor, ast.SymbolFlagsGetAccessorExcludes) + case ast.KindSetAccessor: + b.bindPropertyOrMethodOrAccessor(node, ast.SymbolFlagsSetAccessor, ast.SymbolFlagsSetAccessorExcludes) + case ast.KindFunctionType, ast.KindConstructorType: + b.bindFunctionOrConstructorType(node) + case ast.KindTypeLiteral, ast.KindMappedType: + b.bindAnonymousDeclaration(node, ast.SymbolFlagsTypeLiteral, ast.InternalSymbolNameType) + case ast.KindObjectLiteralExpression: + b.bindAnonymousDeclaration(node, ast.SymbolFlagsObjectLiteral, ast.InternalSymbolNameObject) + case ast.KindFunctionExpression, ast.KindArrowFunction: + b.bindFunctionExpression(node) + case ast.KindClassExpression, ast.KindClassDeclaration: + b.bindClassLikeDeclaration(node) + case ast.KindInterfaceDeclaration: + b.bindBlockScopedDeclaration(node, ast.SymbolFlagsInterface, ast.SymbolFlagsInterfaceExcludes) + case ast.KindCallExpression: + switch ast.GetAssignmentDeclarationKind(node) { + case ast.JSDeclarationKindObjectDefinePropertyValue: + b.bindExpandoPropertyAssignment(node) + case ast.JSDeclarationKindObjectDefinePropertyExports: + b.bindExportsOrObjectDefineProperty(node) + } + if ast.IsInJSFile(node) { + b.bindCallExpression(node) + } + case ast.KindTypeAliasDeclaration: + b.bindBlockScopedDeclaration(node, ast.SymbolFlagsTypeAlias, ast.SymbolFlagsTypeAliasExcludes) + case ast.KindJSTypeAliasDeclaration: + // Top-level JSTypeAliasDeclaration nodes are processed in bindContainer + if !ast.IsSourceFile(b.blockScopeContainer) { + b.bindBlockScopedDeclaration(node, ast.SymbolFlagsTypeAlias, ast.SymbolFlagsTypeAliasExcludes) + } + case ast.KindEnumDeclaration: + b.bindEnumDeclaration(node) + case ast.KindModuleDeclaration: + b.bindModuleDeclaration(node) + case ast.KindImportEqualsDeclaration, ast.KindNamespaceImport, ast.KindImportSpecifier, ast.KindExportSpecifier: + b.declareSymbolAndAddToSymbolTable(node, ast.SymbolFlagsAlias, ast.SymbolFlagsAliasExcludes) + case ast.KindNamespaceExportDeclaration: + b.bindNamespaceExportDeclaration(node) + case ast.KindImportClause: + b.bindImportClause(node) + case ast.KindExportDeclaration: + b.bindExportDeclaration(node) + case ast.KindExportAssignment: + b.bindExportAssignment(node) + case ast.KindSourceFile: + b.bindSourceFileIfExternalModule() + case ast.KindJsxAttributes: + b.bindJsxAttributes(node) + case ast.KindJsxAttribute: + b.bindJsxAttribute(node, ast.SymbolFlagsProperty, ast.SymbolFlagsPropertyExcludes) + } + // Then we recurse into the children of the node to bind them as well. For certain + // symbols we do specialized work when we recurse. For example, we'll keep track of + // the current 'container' node when it changes. This helps us know which symbol table + // a local should go into for example. Since terminal nodes are known not to have + // children, as an optimization we don't process those. + thisNodeOrAnySubnodesHasError := node.Flags&ast.NodeFlagsThisNodeHasError != 0 + if node.Kind > ast.KindLastToken { + saveSeenParseError := b.seenParseError + b.seenParseError = false + containerFlags := GetContainerFlags(node) + if containerFlags == ContainerFlagsNone { + b.bindChildren(node) + } else { + b.bindContainer(node, containerFlags) + } + if b.seenParseError { + thisNodeOrAnySubnodesHasError = true + } + b.seenParseError = saveSeenParseError + } + if thisNodeOrAnySubnodesHasError { + node.Flags |= ast.NodeFlagsThisNodeOrAnySubNodesHasError + b.seenParseError = true + } + return false +} + +func (b *Binder) bindPropertyWorker(node *ast.Node) { + isAutoAccessor := ast.IsAutoAccessorPropertyDeclaration(node) + includes := core.IfElse(isAutoAccessor, ast.SymbolFlagsAccessor, ast.SymbolFlagsProperty) + excludes := core.IfElse(isAutoAccessor, ast.SymbolFlagsAccessorExcludes, ast.SymbolFlagsPropertyExcludes) + b.bindPropertyOrMethodOrAccessor(node, includes|getOptionalSymbolFlagForNode(node), excludes) +} + +func (b *Binder) bindSourceFileIfExternalModule() { + b.setExportContextFlag(b.file.AsNode()) + if ast.IsExternalOrCommonJSModule(b.file) { + b.bindSourceFileAsExternalModule() + } else if ast.IsJsonSourceFile(b.file) { + b.bindSourceFileAsExternalModule() + // Create symbol equivalent for the module.exports = {} + originalSymbol := b.file.Symbol + b.declareSymbol(ast.GetSymbolTable(&b.file.Symbol.Exports), b.file.Symbol, b.file.AsNode(), ast.SymbolFlagsProperty, ast.SymbolFlagsAll) + b.file.Symbol = originalSymbol + } +} + +func (b *Binder) bindSourceFileAsExternalModule() { + b.bindAnonymousDeclaration(b.file.AsNode(), ast.SymbolFlagsValueModule, "\""+tspath.RemoveFileExtension(b.file.FileName())+"\"") +} + +func (b *Binder) bindModuleDeclaration(node *ast.Node) { + b.setExportContextFlag(node) + if ast.IsAmbientModule(node) { + if ast.HasSyntacticModifier(node, ast.ModifierFlagsExport) { + b.errorOnFirstToken(node, diagnostics.X_export_modifier_cannot_be_applied_to_ambient_modules_and_module_augmentations_since_they_are_always_visible) + } + if ast.IsModuleAugmentationExternal(node) { + b.declareModuleSymbol(node) + } else { + name := node.AsModuleDeclaration().Name() + symbol := b.declareSymbolAndAddToSymbolTable(node, ast.SymbolFlagsValueModule, ast.SymbolFlagsValueModuleExcludes) + + if ast.IsStringLiteral(name) { + pattern := core.TryParsePattern(name.Text()) + if !pattern.IsValid() { + // An invalid pattern - must have multiple wildcards. + b.errorOnFirstToken(name, diagnostics.Pattern_0_can_have_at_most_one_Asterisk_character, name.Text()) + } else if pattern.StarIndex >= 0 { + b.file.PatternAmbientModules = append(b.file.PatternAmbientModules, &ast.PatternAmbientModule{Pattern: pattern, Symbol: symbol}) + } + } + } + } else { + state := b.declareModuleSymbol(node) + if state != ast.ModuleInstanceStateNonInstantiated { + symbol := node.Symbol() + // if module was already merged with some function, class or non-const enum, treat it as non-const-enum-only + constEnumOnlyModule := (symbol.Flags&(ast.SymbolFlagsFunction|ast.SymbolFlagsClass|ast.SymbolFlagsRegularEnum) == 0) && + // Current must be `const enum` only + state == ast.ModuleInstanceStateConstEnumOnly && + // Can't have been set to 'false' in a previous merged symbol. ('undefined' OK) + !b.notConstEnumOnlyModules.Has(symbol) + if constEnumOnlyModule { + symbol.Flags |= ast.SymbolFlagsConstEnumOnlyModule + } else { + symbol.Flags &^= ast.SymbolFlagsConstEnumOnlyModule + b.notConstEnumOnlyModules.Add(symbol) + } + } + } +} + +func (b *Binder) declareModuleSymbol(node *ast.Node) ast.ModuleInstanceState { + state := ast.GetModuleInstanceState(node) + instantiated := state != ast.ModuleInstanceStateNonInstantiated + b.declareSymbolAndAddToSymbolTable(node, core.IfElse(instantiated, ast.SymbolFlagsValueModule, ast.SymbolFlagsNamespaceModule), core.IfElse(instantiated, ast.SymbolFlagsValueModuleExcludes, ast.SymbolFlagsNamespaceModuleExcludes)) + return state +} + +func (b *Binder) bindNamespaceExportDeclaration(node *ast.Node) { + if node.Modifiers() != nil { + b.errorOnNode(node, diagnostics.Modifiers_cannot_appear_here) + } + switch { + case !ast.IsSourceFile(node.Parent): + b.errorOnNode(node, diagnostics.Global_module_exports_may_only_appear_at_top_level) + case !ast.IsExternalModule(node.Parent.AsSourceFile()): + b.errorOnNode(node, diagnostics.Global_module_exports_may_only_appear_in_module_files) + case !node.Parent.AsSourceFile().IsDeclarationFile: + b.errorOnNode(node, diagnostics.Global_module_exports_may_only_appear_in_declaration_files) + default: + b.declareSymbol(ast.GetSymbolTable(&b.file.GlobalExports), b.file.Symbol, node, ast.SymbolFlagsAlias, ast.SymbolFlagsAliasExcludes) + } +} + +func (b *Binder) bindImportClause(node *ast.Node) { + if node.AsImportClause().Name() != nil { + b.declareSymbolAndAddToSymbolTable(node, ast.SymbolFlagsAlias, ast.SymbolFlagsAliasExcludes) + } +} + +func (b *Binder) bindExportDeclaration(node *ast.Node) { + decl := node.AsExportDeclaration() + if b.container.Symbol() == nil { + // Export * in some sort of block construct + b.bindAnonymousDeclaration(node, ast.SymbolFlagsExportStar, b.getDeclarationName(node)) + } else if decl.ExportClause == nil { + // All export * declarations are collected in an __export symbol + b.declareSymbol(ast.GetExports(b.container.Symbol()), b.container.Symbol(), node, ast.SymbolFlagsExportStar, ast.SymbolFlagsNone) + } else if ast.IsNamespaceExport(decl.ExportClause) { + b.declareSymbol(ast.GetExports(b.container.Symbol()), b.container.Symbol(), decl.ExportClause, ast.SymbolFlagsAlias, ast.SymbolFlagsAliasExcludes) + } +} + +func (b *Binder) bindExportAssignment(node *ast.Node) { + container := b.container + if container.Symbol() == nil && ast.IsExportAssignment(node) { + // Incorrect export assignment in some sort of block construct + b.bindAnonymousDeclaration(node, ast.SymbolFlagsValue, b.getDeclarationName(node)) + } else { + // If there is an `export default x;` alias declaration, can't `export default` anything else. + // (In contrast, you can still have `export default function f() {}` and `export default interface I {}`.) + flags := core.IfElse(ast.ExpressionIsAlias(node.Expression()), ast.SymbolFlagsAlias, ast.SymbolFlagsProperty) + symbol := b.declareSymbol(ast.GetExports(container.Symbol()), container.Symbol(), node, flags, ast.SymbolFlagsAll) + if node.AsExportAssignment().IsExportEquals { + // Ensure export assignments have a ValueDeclaration set. + SetValueDeclaration(symbol, node) + } + } +} + +func (b *Binder) bindJsxAttributes(node *ast.Node) { + b.bindAnonymousDeclaration(node, ast.SymbolFlagsObjectLiteral, ast.InternalSymbolNameJSXAttributes) +} + +func (b *Binder) bindJsxAttribute(node *ast.Node, symbolFlags ast.SymbolFlags, symbolExcludes ast.SymbolFlags) { + b.declareSymbolAndAddToSymbolTable(node, symbolFlags, symbolExcludes) +} + +func (b *Binder) setExportContextFlag(node *ast.Node) { + // A declaration source file or ambient module declaration that contains no export declarations (but possibly regular + // declarations with export modifiers) is an export context in which declarations are implicitly exported. + if node.Flags&ast.NodeFlagsAmbient != 0 && !b.hasExportDeclarations(node) { + node.Flags |= ast.NodeFlagsExportContext + } else { + node.Flags &^= ast.NodeFlagsExportContext + } +} + +func (b *Binder) hasExportDeclarations(node *ast.Node) bool { + var statements []*ast.Node + switch node.Kind { + case ast.KindSourceFile: + statements = node.Statements() + case ast.KindModuleDeclaration: + body := node.Body() + if body != nil && ast.IsModuleBlock(body) { + statements = body.Statements() + } + } + return core.Some(statements, func(s *ast.Node) bool { + return ast.IsExportDeclaration(s) || ast.IsExportAssignment(s) + }) +} + +func (b *Binder) bindFunctionExpression(node *ast.Node) { + if !b.file.IsDeclarationFile && node.Flags&ast.NodeFlagsAmbient == 0 && ast.IsAsyncFunction(node) { + b.emitFlags |= ast.NodeFlagsHasAsyncFunctions + } + setFlowNode(node, b.currentFlow) + bindingName := ast.InternalSymbolNameFunction + if ast.IsFunctionExpression(node) && node.AsFunctionExpression().Name() != nil { + b.checkStrictModeFunctionName(node) + bindingName = node.AsFunctionExpression().Name().Text() + } + b.bindAnonymousDeclaration(node, ast.SymbolFlagsFunction, bindingName) +} + +func (b *Binder) bindCallExpression(node *ast.Node) { + // We're only inspecting call expressions to detect CommonJS modules, so we can skip + // this check if we've already seen the module indicator + if b.file.CommonJSModuleIndicator == nil && ast.IsRequireCall(node, false /*requireStringLiteralLikeArgument*/) { + b.setCommonJSModuleIndicator(node) + } +} + +func (b *Binder) setCommonJSModuleIndicator(node *ast.Node) bool { + if b.file.ExternalModuleIndicator != nil && b.file.ExternalModuleIndicator != b.file.AsNode() { + return false + } + if b.file.CommonJSModuleIndicator == nil { + b.file.CommonJSModuleIndicator = node + if b.file.ExternalModuleIndicator == nil { + b.bindSourceFileAsExternalModule() + } + } + return true +} + +func (b *Binder) bindClassLikeDeclaration(node *ast.Node) { + name := node.Name() + switch node.Kind { + case ast.KindClassDeclaration: + b.bindBlockScopedDeclaration(node, ast.SymbolFlagsClass, ast.SymbolFlagsClassExcludes) + case ast.KindClassExpression: + nameText := ast.InternalSymbolNameClass + if name != nil { + nameText = name.Text() + b.classifiableNames.Add(nameText) + } + b.bindAnonymousDeclaration(node, ast.SymbolFlagsClass, nameText) + } + symbol := node.Symbol() + // TypeScript 1.0 spec (April 2014): 8.4 + // Every class automatically contains a static property member named 'prototype', the + // type of which is an instantiation of the class type with type Any supplied as a type + // argument for each type parameter. It is an error to explicitly declare a static + // property member with the name 'prototype'. + // + // Note: we check for this here because this class may be merging into a module. The + // module might have an exported variable called 'prototype'. We can't allow that as + // that would clash with the built-in 'prototype' for the class. + prototypeSymbol := b.newSymbol(ast.SymbolFlagsProperty|ast.SymbolFlagsPrototype, "prototype") + symbolExport := ast.GetExports(symbol)[prototypeSymbol.Name] + if symbolExport != nil { + b.errorOnNode(symbolExport.Declarations[0], diagnostics.Duplicate_identifier_0, ast.SymbolName(prototypeSymbol)) + } + ast.GetExports(symbol)[prototypeSymbol.Name] = prototypeSymbol + prototypeSymbol.Parent = symbol +} + +func (b *Binder) bindPropertyOrMethodOrAccessor(node *ast.Node, symbolFlags ast.SymbolFlags, symbolExcludes ast.SymbolFlags) { + if !b.file.IsDeclarationFile && node.Flags&ast.NodeFlagsAmbient == 0 && ast.IsAsyncFunction(node) { + b.emitFlags |= ast.NodeFlagsHasAsyncFunctions + } + if b.currentFlow != nil && ast.IsObjectLiteralOrClassExpressionMethodOrAccessor(node) { + setFlowNode(node, b.currentFlow) + } + if ast.HasDynamicName(node) { + b.bindAnonymousDeclaration(node, symbolFlags, ast.InternalSymbolNameComputed) + } else { + b.declareSymbolAndAddToSymbolTable(node, symbolFlags, symbolExcludes) + } +} + +func (b *Binder) bindFunctionOrConstructorType(node *ast.Node) { + // For a given function symbol "<...>(...) => T" we want to generate a symbol identical + // to the one we would get for: { <...>(...): T } + // + // We do that by making an anonymous type literal symbol, and then setting the function + // symbol as its sole member. To the rest of the system, this symbol will be indistinguishable + // from an actual type literal symbol you would have gotten had you used the long form. + symbol := b.newSymbol(ast.SymbolFlagsSignature, b.getDeclarationName(node)) + b.addDeclarationToSymbol(symbol, node, ast.SymbolFlagsSignature) + typeLiteralSymbol := b.newSymbol(ast.SymbolFlagsTypeLiteral, ast.InternalSymbolNameType) + b.addDeclarationToSymbol(typeLiteralSymbol, node, ast.SymbolFlagsTypeLiteral) + typeLiteralSymbol.Members = make(ast.SymbolTable) + typeLiteralSymbol.Members[symbol.Name] = symbol +} + +func (b *Binder) addLateBoundAssignmentDeclarationToSymbol(node *ast.Node, symbol *ast.Symbol) { + exports := ast.GetExports(symbol) + assignmentSymbol := exports[ast.InternalSymbolNameAssignmentDeclaration] + if assignmentSymbol == nil { + assignmentSymbol = b.newSymbol(ast.SymbolFlagsNone, ast.InternalSymbolNameAssignmentDeclaration) + exports[ast.InternalSymbolNameAssignmentDeclaration] = assignmentSymbol + } + assignmentSymbol.Declarations = append(assignmentSymbol.Declarations, node) +} + +func (b *Binder) bindModuleExportsAssignment(node *ast.Node) { + if b.setCommonJSModuleIndicator(node) { + container := b.file.AsNode() + flags := core.IfElse(ast.ExpressionIsAlias(node.AsBinaryExpression().Right), ast.SymbolFlagsAlias, ast.SymbolFlagsProperty) + symbol := b.declareSymbol(ast.GetExports(container.Symbol()), container.Symbol(), node, flags, 0) + SetValueDeclaration(symbol, node) + } +} + +func (b *Binder) bindExpandoPropertyAssignment(node *ast.Node) { + b.expandoAssignments = append(b.expandoAssignments, ExpandoAssignmentInfo{ + node: node, + container: b.container, + blockScopeContainer: b.blockScopeContainer, + }) +} + +func (b *Binder) bindDeferredExpandoAssignments() { + for _, info := range b.expandoAssignments { + b.container = info.container + b.blockScopeContainer = info.blockScopeContainer + b.bindDeferredExpandoAssignment(info.node) + } +} + +// If the given module symbol has an export= symbol, promote exports with a type or namespace meaning +// from the module symbol onto the export= symbol and, if any such exports exist, mark the export= +// symbol as a namespace module. +func (b *Binder) bindCommonJSTypeExports(moduleSymbol *ast.Symbol) { + moduleExports := moduleSymbol.Exports + if exportEquals := moduleExports[ast.InternalSymbolNameExportEquals]; exportEquals != nil { + for _, symbol := range moduleExports { + if symbol.Name != ast.InternalSymbolNameExportEquals && symbol.Flags&(ast.SymbolFlagsType|ast.SymbolFlagsNamespace) != 0 { + ast.GetExports(exportEquals)[symbol.Name] = symbol + exportEquals.Flags |= ast.SymbolFlagsNamespaceModule + } + } + } +} + +func (b *Binder) bindDeferredExpandoAssignment(node *ast.Node) { + parent := getParentOfPropertyAssignment(node) + symbol := b.lookupEntity(parent, b.blockScopeContainer) + if symbol == nil { + symbol = b.lookupEntity(parent, b.container) + } + if symbol = getInitializerSymbol(symbol); symbol != nil { + if ast.HasDynamicName(node) { + b.bindAnonymousDeclaration(node, ast.SymbolFlagsProperty|ast.SymbolFlagsAssignment, ast.InternalSymbolNameComputed) + b.addLateBoundAssignmentDeclarationToSymbol(node, symbol) + } else { + // We declare expandos only when there are no non-expando declarations for that name. + exports := ast.GetExports(symbol) + if existing := exports[b.getDeclarationName(node)]; existing == nil || existing.Flags&ast.SymbolFlagsAssignment != 0 { + b.declareSymbol(exports, symbol, node, ast.SymbolFlagsProperty|ast.SymbolFlagsAssignment, ast.SymbolFlagsPropertyExcludes) + } + } + } +} + +func getParentOfPropertyAssignment(node *ast.Node) *ast.Node { + switch node.Kind { + case ast.KindBinaryExpression: + return node.AsBinaryExpression().Left.Expression() + case ast.KindCallExpression: + return node.Arguments()[0] + } + panic("Unhandled case in getParentOfPropertyAssignment") +} + +func (b *Binder) bindExportsOrObjectDefineProperty(node *ast.Node) { + if b.setCommonJSModuleIndicator(node) { + container := b.file.AsNode() + flags := core.IfElse(ast.IsBinaryExpression(node) && ast.ExpressionIsAlias(node.AsBinaryExpression().Right), ast.SymbolFlagsAlias, ast.SymbolFlagsFunctionScopedVariable) + b.declareSymbol(ast.GetExports(container.Symbol()), container.Symbol(), node, flags, ast.SymbolFlagsFunctionScopedVariableExcludes) + } +} + +func getInitializerSymbol(symbol *ast.Symbol) *ast.Symbol { + if symbol == nil || symbol.ValueDeclaration == nil { + return nil + } + declaration := symbol.ValueDeclaration + // For an assignment 'fn.xxx = ...', where 'fn' is a previously declared function or a previously + // declared const variable initialized with a function expression or arrow function, we add expando + // property declarations to the function's symbol. This also applies to class expressions in JS files, + // and empty object literals in JS files when the declaration doesn't have a type annotation. + switch { + case ast.IsFunctionDeclaration(declaration) || ast.IsInJSFile(declaration) && ast.IsClassDeclaration(declaration): + return symbol + case ast.IsVariableDeclaration(declaration) && + (declaration.Parent.Flags&ast.NodeFlagsConst != 0 || ast.IsInJSFile(declaration)): + initializer := declaration.Initializer() + if ast.IsExpandoInitializer(declaration, initializer) { + return initializer.Symbol() + } + case ast.IsBinaryExpression(declaration) && ast.IsInJSFile(declaration): + initializer := declaration.AsBinaryExpression().Right + if ast.IsExpandoInitializer(declaration, initializer) { + return initializer.Symbol() + } + } + return nil +} + +func (b *Binder) bindThisPropertyAssignment(node *ast.Node) { + if !ast.IsInJSFile(node) { + return + } + bin := node.AsBinaryExpression() + if ast.IsPropertyAccessExpression(bin.Left) && ast.IsPrivateIdentifier(bin.Left.AsPropertyAccessExpression().Name()) || + b.thisContainer == nil { + return + } + if classSymbol, symbolTable := b.getThisClassAndSymbolTable(); symbolTable != nil { + if ast.HasDynamicName(node) { + b.declareSymbolEx(symbolTable, classSymbol, node, ast.SymbolFlagsProperty, ast.SymbolFlagsNone, true /*isReplaceableByMethod*/, true /*isComputedName*/) + b.addLateBoundAssignmentDeclarationToSymbol(node, classSymbol) + } else { + b.declareSymbolEx(symbolTable, classSymbol, node, ast.SymbolFlagsProperty|ast.SymbolFlagsAssignment, ast.SymbolFlagsNone, true /*isReplaceableByMethod*/, false /*isComputedName*/) + } + } else if b.thisContainer.Kind != ast.KindFunctionDeclaration && b.thisContainer.Kind != ast.KindFunctionExpression { + // !!! constructor functions + panic("Unhandled case in bindThisPropertyAssignment: " + b.thisContainer.Kind.String()) + } +} + +func (b *Binder) getThisClassAndSymbolTable() (classSymbol *ast.Symbol, symbolTable ast.SymbolTable) { + if b.thisContainer == nil { + return nil, nil + } + switch b.thisContainer.Kind { + case ast.KindFunctionDeclaration, ast.KindFunctionExpression: + // !!! constructor functions + case ast.KindConstructor, ast.KindPropertyDeclaration, ast.KindMethodDeclaration, ast.KindGetAccessor, ast.KindSetAccessor, ast.KindClassStaticBlockDeclaration: + // this.property assignment in class member -- bind to the containing class + classSymbol = b.thisContainer.Parent.Symbol() + if ast.IsStatic(b.thisContainer) { + symbolTable = ast.GetExports(classSymbol) + } else { + symbolTable = ast.GetMembers(classSymbol) + } + } + return classSymbol, symbolTable +} + +func (b *Binder) bindEnumDeclaration(node *ast.Node) { + if ast.IsEnumConst(node) { + b.bindBlockScopedDeclaration(node, ast.SymbolFlagsConstEnum, ast.SymbolFlagsConstEnumExcludes) + } else { + b.bindBlockScopedDeclaration(node, ast.SymbolFlagsRegularEnum, ast.SymbolFlagsRegularEnumExcludes) + } +} + +func (b *Binder) bindVariableDeclarationOrBindingElement(node *ast.Node) { + b.checkStrictModeEvalOrArguments(node, node.Name()) + if name := node.Name(); name != nil && !ast.IsBindingPattern(name) { + switch { + case ast.IsVariableDeclarationInitializedToRequire(node): + b.declareSymbolAndAddToSymbolTable(node, ast.SymbolFlagsAlias, ast.SymbolFlagsAliasExcludes) + case ast.IsBlockOrCatchScoped(node): + b.bindBlockScopedDeclaration(node, ast.SymbolFlagsBlockScopedVariable, ast.SymbolFlagsBlockScopedVariableExcludes) + case ast.IsPartOfParameterDeclaration(node): + // It is safe to walk up parent chain to find whether the node is a destructuring parameter declaration + // because its parent chain has already been set up, since parents are set before descending into children. + // + // If node is a binding element in parameter declaration, we need to use ParameterExcludes. + // Using ParameterExcludes flag allows the compiler to report an error on duplicate identifiers in Parameter Declaration + // For example: + // function foo([a,a]) {} // Duplicate Identifier error + // function bar(a,a) {} // Duplicate Identifier error, parameter declaration in this case is handled in bindParameter + // // which correctly set excluded symbols + b.declareSymbolAndAddToSymbolTable(node, ast.SymbolFlagsFunctionScopedVariable, ast.SymbolFlagsParameterExcludes) + default: + b.declareSymbolAndAddToSymbolTable(node, ast.SymbolFlagsFunctionScopedVariable, ast.SymbolFlagsFunctionScopedVariableExcludes) + } + } +} + +func (b *Binder) bindParameter(node *ast.Node) { + decl := node.AsParameterDeclaration() + if node.Flags&ast.NodeFlagsAmbient == 0 { + // It is a SyntaxError if the identifier eval or arguments appears within a FormalParameterList of a + // strict mode FunctionLikeDeclaration or FunctionExpression(13.1) + b.checkStrictModeEvalOrArguments(node, decl.Name()) + } + if ast.IsBindingPattern(decl.Name()) { + index := slices.Index(node.Parent.Parameters(), node) + b.bindAnonymousDeclaration(node, ast.SymbolFlagsFunctionScopedVariable, "__"+strconv.Itoa(index)) + } else { + b.declareSymbolAndAddToSymbolTable(node, ast.SymbolFlagsFunctionScopedVariable, ast.SymbolFlagsParameterExcludes) + } + // If this is a property-parameter, then also declare the property symbol into the + // containing class. + if ast.IsParameterPropertyDeclaration(node, node.Parent) { + classDeclaration := node.Parent.Parent + flags := ast.SymbolFlagsProperty | core.IfElse(decl.QuestionToken != nil, ast.SymbolFlagsOptional, ast.SymbolFlagsNone) + b.declareSymbol(ast.GetMembers(classDeclaration.Symbol()), classDeclaration.Symbol(), node, flags, ast.SymbolFlagsPropertyExcludes) + } +} + +func (b *Binder) bindFunctionDeclaration(node *ast.Node) { + if !b.file.IsDeclarationFile && node.Flags&ast.NodeFlagsAmbient == 0 && ast.IsAsyncFunction(node) { + b.emitFlags |= ast.NodeFlagsHasAsyncFunctions + } + b.checkStrictModeFunctionName(node) + b.bindBlockScopedDeclaration(node, ast.SymbolFlagsFunction, ast.SymbolFlagsFunctionExcludes) +} + +func (b *Binder) getInferTypeContainer(node *ast.Node) *ast.Node { + extendsType := ast.FindAncestor(node, func(n *ast.Node) bool { + parent := n.Parent + return parent != nil && ast.IsConditionalTypeNode(parent) && parent.AsConditionalTypeNode().ExtendsType == n + }) + if extendsType != nil { + return extendsType.Parent + } + return nil +} + +func (b *Binder) bindAnonymousDeclaration(node *ast.Node, symbolFlags ast.SymbolFlags, name string) { + symbol := b.newSymbol(symbolFlags, name) + if symbolFlags&(ast.SymbolFlagsEnumMember|ast.SymbolFlagsClassMember) != 0 { + symbol.Parent = b.container.Symbol() + } + b.addDeclarationToSymbol(symbol, node, symbolFlags) +} + +func (b *Binder) bindBlockScopedDeclaration(node *ast.Node, symbolFlags ast.SymbolFlags, symbolExcludes ast.SymbolFlags) { + switch b.blockScopeContainer.Kind { + case ast.KindModuleDeclaration: + b.declareModuleMember(node, symbolFlags, symbolExcludes) + case ast.KindSourceFile: + if ast.IsExternalOrCommonJSModule(b.container.AsSourceFile()) { + b.declareModuleMember(node, symbolFlags, symbolExcludes) + break + } + fallthrough + default: + b.declareSymbol(ast.GetLocals(b.blockScopeContainer), nil /*parent*/, node, symbolFlags, symbolExcludes) + } +} + +func (b *Binder) bindTypeParameter(node *ast.Node) { + if node.Parent.Kind == ast.KindInferType { + container := b.getInferTypeContainer(node.Parent) + if container != nil { + b.declareSymbol(ast.GetLocals(container), nil /*parent*/, node, ast.SymbolFlagsTypeParameter, ast.SymbolFlagsTypeParameterExcludes) + } else { + b.bindAnonymousDeclaration(node, ast.SymbolFlagsTypeParameter, b.getDeclarationName(node)) + } + } else { + b.declareSymbolAndAddToSymbolTable(node, ast.SymbolFlagsTypeParameter, ast.SymbolFlagsTypeParameterExcludes) + } +} + +func (b *Binder) lookupEntity(node *ast.Node, container *ast.Node) *ast.Symbol { + if ast.IsIdentifier(node) { + return b.lookupName(node.Text(), container) + } + if node.Expression().Kind == ast.KindThisKeyword { + if _, symbolTable := b.getThisClassAndSymbolTable(); symbolTable != nil { + if name := ast.GetElementOrPropertyAccessName(node); name != nil { + return symbolTable[name.Text()] + } + } + return nil + } + if symbol := getInitializerSymbol(b.lookupEntity(node.Expression(), container)); symbol != nil && symbol.Exports != nil { + if name := ast.GetElementOrPropertyAccessName(node); name != nil { + return symbol.Exports[name.Text()] + } + } + return nil +} + +func (b *Binder) lookupName(name string, container *ast.Node) *ast.Symbol { + if localsContainer := container.LocalsContainerData(); localsContainer != nil { + if local := localsContainer.Locals[name]; local != nil { + return core.OrElse(local.ExportSymbol, local) + } + } + if declaration := container.DeclarationData(); declaration != nil && declaration.Symbol != nil { + return declaration.Symbol.Exports[name] + } + return nil +} + +// The binder visits every node in the syntax tree so it is a convenient place to perform a single localized +// check for reserved words used as identifiers in strict mode code, as well as `yield` or `await` in +// [Yield] or [Await] contexts, respectively. +func (b *Binder) checkContextualIdentifier(node *ast.Node) { + // Report error only if there are no parse errors in file + if len(b.file.Diagnostics()) == 0 && node.Flags&ast.NodeFlagsAmbient == 0 && node.Flags&ast.NodeFlagsJSDoc == 0 && !ast.IsIdentifierName(node) { + // strict mode identifiers + originalKeywordKind := scanner.GetIdentifierToken(node.Text()) + if originalKeywordKind == ast.KindIdentifier { + return + } + if originalKeywordKind >= ast.KindFirstFutureReservedWord && originalKeywordKind <= ast.KindLastFutureReservedWord { + b.errorOnNode(node, b.getStrictModeIdentifierMessage(node), scanner.DeclarationNameToString(node)) + } else if originalKeywordKind == ast.KindAwaitKeyword { + if ast.IsExternalModule(b.file) && ast.IsInTopLevelContext(node) { + b.errorOnNode(node, diagnostics.Identifier_expected_0_is_a_reserved_word_at_the_top_level_of_a_module, scanner.DeclarationNameToString(node)) + } else if node.Flags&ast.NodeFlagsAwaitContext != 0 { + b.errorOnNode(node, diagnostics.Identifier_expected_0_is_a_reserved_word_that_cannot_be_used_here, scanner.DeclarationNameToString(node)) + } + } else if originalKeywordKind == ast.KindYieldKeyword && node.Flags&ast.NodeFlagsYieldContext != 0 { + b.errorOnNode(node, diagnostics.Identifier_expected_0_is_a_reserved_word_that_cannot_be_used_here, scanner.DeclarationNameToString(node)) + } + } +} + +func (b *Binder) checkPrivateIdentifier(node *ast.Node) { + if node.Text() == "#constructor" { + // Report error only if there are no parse errors in file + if len(b.file.Diagnostics()) == 0 { + b.errorOnNode(node, diagnostics.X_constructor_is_a_reserved_word, scanner.DeclarationNameToString(node)) + } + } +} + +func (b *Binder) getStrictModeIdentifierMessage(node *ast.Node) *diagnostics.Message { + // Provide specialized messages to help the user understand why we think they're in + // strict mode. + if ast.GetContainingClass(node) != nil { + return diagnostics.Identifier_expected_0_is_a_reserved_word_in_strict_mode_Class_definitions_are_automatically_in_strict_mode + } + if b.file.ExternalModuleIndicator != nil { + return diagnostics.Identifier_expected_0_is_a_reserved_word_in_strict_mode_Modules_are_automatically_in_strict_mode + } + return diagnostics.Identifier_expected_0_is_a_reserved_word_in_strict_mode +} + +// Should be called only on prologue directives (ast.IsPrologueDirective(node) should be true) +func isUseStrictPrologueDirective(sourceFile *ast.SourceFile, node *ast.Node) bool { + nodeText := scanner.GetSourceTextOfNodeFromSourceFile(sourceFile, node.Expression(), false /*includeTrivia*/) + // Note: the node text must be exactly "use strict" or 'use strict'. It is not ok for the + // string to contain unicode escapes (as per ES5). + return nodeText == "\"use strict\"" || nodeText == "'use strict'" +} + +func FindUseStrictPrologue(sourceFile *ast.SourceFile, statements []*ast.Node) *ast.Node { + for _, statement := range statements { + if ast.IsPrologueDirective(statement) { + if isUseStrictPrologueDirective(sourceFile, statement) { + return statement + } + } else { + return nil + } + } + + return nil +} + +func (b *Binder) checkStrictModeFunctionName(node *ast.Node) { + if node.Flags&ast.NodeFlagsAmbient == 0 { + // It is a SyntaxError if the identifier eval or arguments appears within a FormalParameterList of a strict mode FunctionDeclaration or FunctionExpression (13.1)) + b.checkStrictModeEvalOrArguments(node, node.Name()) + } +} + +func (b *Binder) getStrictModeBlockScopeFunctionDeclarationMessage(node *ast.Node) *diagnostics.Message { + // Provide specialized messages to help the user understand why we think they're in strict mode. + if ast.GetContainingClass(node) != nil { + return diagnostics.Function_declarations_are_not_allowed_inside_blocks_in_strict_mode_when_targeting_ES5_Class_definitions_are_automatically_in_strict_mode + } + if b.file.ExternalModuleIndicator != nil { + return diagnostics.Function_declarations_are_not_allowed_inside_blocks_in_strict_mode_when_targeting_ES5_Modules_are_automatically_in_strict_mode + } + return diagnostics.Function_declarations_are_not_allowed_inside_blocks_in_strict_mode_when_targeting_ES5 +} + +func (b *Binder) checkStrictModeBinaryExpression(node *ast.Node) { + expr := node.AsBinaryExpression() + if ast.IsLeftHandSideExpression(expr.Left) && ast.IsAssignmentOperator(expr.OperatorToken.Kind) { + // ECMA 262 (Annex C) The identifier eval or arguments may not appear as the LeftHandSideExpression of an + // Assignment operator(11.13) or of a PostfixExpression(11.3) + b.checkStrictModeEvalOrArguments(node, expr.Left) + } +} + +func (b *Binder) checkStrictModeCatchClause(node *ast.Node) { + // It is a SyntaxError if a TryStatement with a Catch occurs within strict code and the Identifier of the + // Catch production is eval or arguments + clause := node.AsCatchClause() + if clause.VariableDeclaration != nil { + b.checkStrictModeEvalOrArguments(node, clause.VariableDeclaration.AsVariableDeclaration().Name()) + } +} + +func (b *Binder) checkStrictModeDeleteExpression(node *ast.Node) { + // Grammar checking + expr := node.AsDeleteExpression() + if expr.Expression.Kind == ast.KindIdentifier { + // When a delete operator occurs within strict mode code, a SyntaxError is thrown if its + // UnaryExpression is a direct reference to a variable, function argument, or function name + b.errorOnNode(expr.Expression, diagnostics.X_delete_cannot_be_called_on_an_identifier_in_strict_mode) + } +} + +func (b *Binder) checkStrictModePostfixUnaryExpression(node *ast.Node) { + // Grammar checking + // The identifier eval or arguments may not appear as the LeftHandSideExpression of an + // Assignment operator(11.13) or of a PostfixExpression(11.3) or as the UnaryExpression + // operated upon by a Prefix Increment(11.4.4) or a Prefix Decrement(11.4.5) operator. + b.checkStrictModeEvalOrArguments(node, node.AsPostfixUnaryExpression().Operand) +} + +func (b *Binder) checkStrictModePrefixUnaryExpression(node *ast.Node) { + // Grammar checking + expr := node.AsPrefixUnaryExpression() + if expr.Operator == ast.KindPlusPlusToken || expr.Operator == ast.KindMinusMinusToken { + b.checkStrictModeEvalOrArguments(node, expr.Operand) + } +} + +func (b *Binder) checkStrictModeWithStatement(node *ast.Node) { + // Grammar checking for withStatement + b.errorOnFirstToken(node, diagnostics.X_with_statements_are_not_allowed_in_strict_mode) +} + +func (b *Binder) checkStrictModeLabeledStatement(node *ast.Node) { + // Grammar checking for labeledStatement + data := node.AsLabeledStatement() + if ast.IsDeclarationStatement(data.Statement) || ast.IsVariableStatement(data.Statement) { + b.errorOnFirstToken(data.Label, diagnostics.A_label_is_not_allowed_here) + } +} + +func isEvalOrArgumentsIdentifier(node *ast.Node) bool { + if ast.IsIdentifier(node) { + text := node.Text() + return text == "eval" || text == "arguments" + } + return false +} + +func (b *Binder) checkStrictModeEvalOrArguments(contextNode *ast.Node, name *ast.Node) { + if name != nil && isEvalOrArgumentsIdentifier(name) { + // We check first if the name is inside class declaration or class expression; if so give explicit message + // otherwise report generic error message. + b.errorOnNode(name, b.getStrictModeEvalOrArgumentsMessage(contextNode), name.Text()) + } +} + +func (b *Binder) getStrictModeEvalOrArgumentsMessage(node *ast.Node) *diagnostics.Message { + // Provide specialized messages to help the user understand why we think they're in strict mode + if ast.GetContainingClass(node) != nil { + return diagnostics.Code_contained_in_a_class_is_evaluated_in_JavaScript_s_strict_mode_which_does_not_allow_this_use_of_0_For_more_information_see_https_Colon_Slash_Slashdeveloper_mozilla_org_Slashen_US_Slashdocs_SlashWeb_SlashJavaScript_SlashReference_SlashStrict_mode + } + if b.file.ExternalModuleIndicator != nil { + return diagnostics.Invalid_use_of_0_Modules_are_automatically_in_strict_mode + } + return diagnostics.Invalid_use_of_0_in_strict_mode +} + +// All container nodes are kept on a linked list in declaration order. This list is used by +// the getLocalNameOfContainer function in the type checker to validate that the local name +// used for a container is unique. +func (b *Binder) bindContainer(node *ast.Node, containerFlags ContainerFlags) { + // Before we recurse into a node's children, we first save the existing parent, container + // and block-container. Then after we pop out of processing the children, we restore + // these saved values. + saveContainer := b.container + saveThisContainer := b.thisContainer + savedBlockScopeContainer := b.blockScopeContainer + // Depending on what kind of node this is, we may have to adjust the current container + // and block-container. If the current node is a container, then it is automatically + // considered the current block-container as well. Also, for containers that we know + // may contain locals, we eagerly initialize the .locals field. We do this because + // it's highly likely that the .locals will be needed to place some child in (for example, + // a parameter, or variable declaration). + // + // However, we do not proactively create the .locals for block-containers because it's + // totally normal and common for block-containers to never actually have a block-scoped + // variable in them. We don't want to end up allocating an object for every 'block' we + // run into when most of them won't be necessary. + // + // Finally, if this is a block-container, then we clear out any existing .locals object + // it may contain within it. This happens in incremental scenarios. Because we can be + // reusing a node from a previous compilation, that node may have had 'locals' created + // for it. We must clear this so we don't accidentally move any stale data forward from + // a previous compilation. + if containerFlags&ContainerFlagsIsContainer != 0 { + b.container = node + b.blockScopeContainer = node + if containerFlags&ContainerFlagsHasLocals != 0 { + // localsContainer := node + // localsContainer.LocalsContainerData().locals = make(SymbolTable) + b.addToContainerChain(node) + } + } else if containerFlags&ContainerFlagsIsBlockScopedContainer != 0 { + b.blockScopeContainer = node + b.addToContainerChain(node) + } + if containerFlags&ContainerFlagsIsThisContainer != 0 { + b.thisContainer = node + } + if containerFlags&ContainerFlagsIsControlFlowContainer != 0 { + saveCurrentFlow := b.currentFlow + saveBreakTarget := b.currentBreakTarget + saveContinueTarget := b.currentContinueTarget + saveReturnTarget := b.currentReturnTarget + saveExceptionTarget := b.currentExceptionTarget + saveActiveLabelList := b.activeLabelList + saveHasExplicitReturn := b.hasExplicitReturn + saveSeenThisKeyword := b.seenThisKeyword + isImmediatelyInvoked := (containerFlags&ContainerFlagsIsFunctionExpression != 0 && + !ast.HasSyntacticModifier(node, ast.ModifierFlagsAsync) && + !isGeneratorFunctionExpression(node) && + ast.GetImmediatelyInvokedFunctionExpression(node) != nil) || node.Kind == ast.KindClassStaticBlockDeclaration + // A non-async, non-generator IIFE is considered part of the containing control flow. Return statements behave + // similarly to break statements that exit to a label just past the statement body. + if !isImmediatelyInvoked { + flowStart := b.newFlowNode(ast.FlowFlagsStart) + b.currentFlow = flowStart + if containerFlags&(ContainerFlagsIsFunctionExpression|ContainerFlagsIsObjectLiteralOrClassExpressionMethodOrAccessor) != 0 { + flowStart.Node = node + } + } + // We create a return control flow graph for IIFEs and constructors. For constructors + // we use the return control flow graph in strict property initialization checks. + if isImmediatelyInvoked || node.Kind == ast.KindConstructor { + b.currentReturnTarget = b.newFlowNode(ast.FlowFlagsBranchLabel) + } else { + b.currentReturnTarget = nil + } + b.currentExceptionTarget = nil + b.currentBreakTarget = nil + b.currentContinueTarget = nil + b.activeLabelList = nil + b.hasExplicitReturn = false + b.seenThisKeyword = false + b.bindChildren(node) + // Reset flags (for incremental scenarios) + node.Flags &^= ast.NodeFlagsReachabilityAndEmitFlags | ast.NodeFlagsContainsThis + if b.currentFlow.Flags&ast.FlowFlagsUnreachable == 0 && containerFlags&ContainerFlagsIsFunctionLike != 0 { + bodyData := node.BodyData() + if bodyData != nil && ast.NodeIsPresent(bodyData.Body) { + node.Flags |= ast.NodeFlagsHasImplicitReturn + if b.hasExplicitReturn { + node.Flags |= ast.NodeFlagsHasExplicitReturn + } + bodyData.EndFlowNode = b.currentFlow + } + } + if b.seenThisKeyword { + node.Flags |= ast.NodeFlagsContainsThis + } + if node.Kind == ast.KindSourceFile { + node.Flags |= b.emitFlags + node.AsSourceFile().EndFlowNode = b.currentFlow + } + if b.currentReturnTarget != nil { + b.addAntecedent(b.currentReturnTarget, b.currentFlow) + b.currentFlow = b.finishFlowLabel(b.currentReturnTarget) + if node.Kind == ast.KindConstructor || node.Kind == ast.KindClassStaticBlockDeclaration { + setReturnFlowNode(node, b.currentFlow) + } + } + if !isImmediatelyInvoked { + b.currentFlow = saveCurrentFlow + } + b.currentBreakTarget = saveBreakTarget + b.currentContinueTarget = saveContinueTarget + b.currentReturnTarget = saveReturnTarget + b.currentExceptionTarget = saveExceptionTarget + b.activeLabelList = saveActiveLabelList + b.hasExplicitReturn = saveHasExplicitReturn + if containerFlags&ContainerFlagsPropagatesThisKeyword != 0 { + b.seenThisKeyword = saveSeenThisKeyword || b.seenThisKeyword + } else { + b.seenThisKeyword = saveSeenThisKeyword + } + } else if containerFlags&ContainerFlagsIsInterface != 0 { + saveSeenThisKeyword := b.seenThisKeyword + b.seenThisKeyword = false + b.bindChildren(node) + // ContainsThis cannot overlap with HasExtendedUnicodeEscape on Identifier + if b.seenThisKeyword { + node.Flags |= ast.NodeFlagsContainsThis + } else { + node.Flags &^= ast.NodeFlagsContainsThis + } + b.seenThisKeyword = saveSeenThisKeyword + } else { + b.bindChildren(node) + } + if ast.IsSourceFile(node) && ast.IsInJSFile(node) { + // Binding of top-level JSTypeAliasDeclaration nodes is deferred to ensure CommonJS module + // indicators, if any, are processed first. + for _, statement := range node.Statements() { + if ast.IsJSTypeAliasDeclaration(statement) { + b.bindBlockScopedDeclaration(statement, ast.SymbolFlagsTypeAlias, ast.SymbolFlagsTypeAliasExcludes) + } + } + if b.file.CommonJSModuleIndicator != nil { + b.declareCommonJSVariable("module") + b.declareCommonJSVariable("exports") + } + } + if ast.IsSourceFile(node) && ast.IsExternalOrCommonJSModule(node.AsSourceFile()) || ast.IsAmbientModule(node) { + b.bindCommonJSTypeExports(node.Symbol()) + } + b.container = saveContainer + b.thisContainer = saveThisContainer + b.blockScopeContainer = savedBlockScopeContainer +} + +func (b *Binder) declareCommonJSVariable(name string) { + locals := ast.GetLocals(b.file.AsNode()) + if locals[name] == nil { + symbol := b.newSymbol(ast.SymbolFlagsFunctionScopedVariable|ast.SymbolFlagsModuleExports, name) + symbol.Declarations = b.newSingleDeclaration(b.file.AsNode()) + symbol.ValueDeclaration = symbol.Declarations[0] + if name == "module" { + exportsProperty := b.newSymbol(ast.SymbolFlagsModuleExports|ast.SymbolFlagsProperty, "exports") + exportsProperty.Declarations = symbol.Declarations + exportsProperty.ValueDeclaration = symbol.ValueDeclaration + exportsProperty.Parent = symbol + symbol.Members = make(ast.SymbolTable, 1) + symbol.Members["exports"] = exportsProperty + } + locals[name] = symbol + } +} + +func (b *Binder) bindChildren(node *ast.Node) { + saveInAssignmentPattern := b.inAssignmentPattern + // Most nodes aren't valid in an assignment pattern, so we clear the value here + // and set it before we descend into nodes that could actually be part of an assignment pattern. + b.inAssignmentPattern = false + + if b.currentFlow == b.unreachableFlow { + if flowNodeData := node.FlowNodeData(); flowNodeData != nil { + flowNodeData.FlowNode = nil + } + if ast.IsPotentiallyExecutableNode(node) { + node.Flags |= ast.NodeFlagsUnreachable + } + b.bindEachChild(node) + b.inAssignmentPattern = saveInAssignmentPattern + return + } + + if ast.KindFirstStatement <= node.Kind && node.Kind <= ast.KindLastStatement { + if flowNodeData := node.FlowNodeData(); flowNodeData != nil { + flowNodeData.FlowNode = b.currentFlow + } + } + + switch node.Kind { + case ast.KindWhileStatement: + b.bindWhileStatement(node) + case ast.KindDoStatement: + b.bindDoStatement(node) + case ast.KindForStatement: + b.bindForStatement(node) + case ast.KindForInStatement, ast.KindForOfStatement: + b.bindForInOrForOfStatement(node) + case ast.KindIfStatement: + b.bindIfStatement(node) + case ast.KindReturnStatement: + b.bindReturnStatement(node) + case ast.KindThrowStatement: + b.bindThrowStatement(node) + case ast.KindBreakStatement: + b.bindBreakStatement(node) + case ast.KindContinueStatement: + b.bindContinueStatement(node) + case ast.KindTryStatement: + b.bindTryStatement(node) + case ast.KindSwitchStatement: + b.bindSwitchStatement(node) + case ast.KindCaseBlock: + b.bindCaseBlock(node) + case ast.KindCaseClause, ast.KindDefaultClause: + b.bindCaseOrDefaultClause(node) + case ast.KindExpressionStatement: + b.bindExpressionStatement(node) + case ast.KindLabeledStatement: + b.bindLabeledStatement(node) + case ast.KindPrefixUnaryExpression: + b.bindPrefixUnaryExpressionFlow(node) + case ast.KindPostfixUnaryExpression: + b.bindPostfixUnaryExpressionFlow(node) + case ast.KindBinaryExpression: + if ast.IsDestructuringAssignment(node) { + // Carry over whether we are in an assignment pattern to + // binary expressions that could actually be an initializer + b.inAssignmentPattern = saveInAssignmentPattern + b.bindDestructuringAssignmentFlow(node) + return + } + b.bindBinaryExpressionFlow(node) + case ast.KindDeleteExpression: + b.bindDeleteExpressionFlow(node) + case ast.KindConditionalExpression: + b.bindConditionalExpressionFlow(node) + case ast.KindVariableDeclaration: + b.bindVariableDeclarationFlow(node) + case ast.KindPropertyAccessExpression, ast.KindElementAccessExpression: + b.bindAccessExpressionFlow(node) + case ast.KindCallExpression: + b.bindCallExpressionFlow(node) + case ast.KindNonNullExpression: + b.bindNonNullExpressionFlow(node) + case ast.KindSourceFile: + sourceFile := node.AsSourceFile() + b.bindEachStatementFunctionsFirst(sourceFile.Statements) + b.bind(sourceFile.EndOfFileToken) + case ast.KindBlock, ast.KindModuleBlock: + b.bindEachStatementFunctionsFirst(node.StatementList()) + case ast.KindBindingElement: + b.bindBindingElementFlow(node) + case ast.KindParameter: + b.bindParameterFlow(node) + case ast.KindObjectLiteralExpression, ast.KindArrayLiteralExpression, ast.KindPropertyAssignment, ast.KindSpreadElement: + b.inAssignmentPattern = saveInAssignmentPattern + b.bindEachChild(node) + default: + b.bindEachChild(node) + } + b.inAssignmentPattern = saveInAssignmentPattern +} + +func (b *Binder) bindEachChild(node *ast.Node) { + node.ForEachChild(b.bindFunc) +} + +func (b *Binder) bindEach(nodes []*ast.Node) { + for _, node := range nodes { + b.bind(node) + } +} + +func (b *Binder) bindNodeList(nodeList *ast.NodeList) { + if nodeList != nil { + b.bindEach(nodeList.Nodes) + } +} + +func (b *Binder) bindModifiers(modifiers *ast.ModifierList) { + if modifiers != nil { + b.bindEach(modifiers.Nodes) + } +} + +func (b *Binder) bindEachStatementFunctionsFirst(statements *ast.NodeList) { + for _, node := range statements.Nodes { + if node.Kind == ast.KindFunctionDeclaration { + b.bind(node) + } + } + for _, node := range statements.Nodes { + if node.Kind != ast.KindFunctionDeclaration { + b.bind(node) + } + } +} + +func (b *Binder) setContinueTarget(node *ast.Node, target *ast.FlowLabel) *ast.FlowLabel { + label := b.activeLabelList + for label != nil && node.Parent.Kind == ast.KindLabeledStatement { + label.continueTarget = target + label = label.next + node = node.Parent + } + return target +} + +func (b *Binder) doWithConditionalBranches(action func(b *Binder, value *ast.Node) bool, value *ast.Node, trueTarget *ast.FlowLabel, falseTarget *ast.FlowLabel) { + savedTrueTarget := b.currentTrueTarget + savedFalseTarget := b.currentFalseTarget + b.currentTrueTarget = trueTarget + b.currentFalseTarget = falseTarget + action(b, value) + b.currentTrueTarget = savedTrueTarget + b.currentFalseTarget = savedFalseTarget +} + +func (b *Binder) bindCondition(node *ast.Node, trueTarget *ast.FlowLabel, falseTarget *ast.FlowLabel) { + b.doWithConditionalBranches((*Binder).bind, node, trueTarget, falseTarget) + if node == nil || !isLogicalAssignmentExpression(node) && !ast.IsLogicalExpression(node) && !(ast.IsOptionalChain(node) && ast.IsOutermostOptionalChain(node)) { + b.addAntecedent(trueTarget, b.createFlowCondition(ast.FlowFlagsTrueCondition, b.currentFlow, node)) + b.addAntecedent(falseTarget, b.createFlowCondition(ast.FlowFlagsFalseCondition, b.currentFlow, node)) + } +} + +func (b *Binder) bindIterativeStatement(node *ast.Node, breakTarget *ast.FlowLabel, continueTarget *ast.FlowLabel) { + saveBreakTarget := b.currentBreakTarget + saveContinueTarget := b.currentContinueTarget + b.currentBreakTarget = breakTarget + b.currentContinueTarget = continueTarget + b.bind(node) + b.currentBreakTarget = saveBreakTarget + b.currentContinueTarget = saveContinueTarget +} + +func isLogicalAssignmentExpression(node *ast.Node) bool { + return ast.IsLogicalOrCoalescingAssignmentExpression(ast.SkipParentheses(node)) +} + +func (b *Binder) bindAssignmentTargetFlow(node *ast.Node) { + switch node.Kind { + case ast.KindArrayLiteralExpression: + for _, e := range node.Elements() { + if e.Kind == ast.KindSpreadElement { + b.bindAssignmentTargetFlow(e.Expression()) + } else { + b.bindDestructuringTargetFlow(e) + } + } + case ast.KindObjectLiteralExpression: + for _, p := range node.Properties() { + switch p.Kind { + case ast.KindPropertyAssignment: + b.bindDestructuringTargetFlow(p.Initializer()) + case ast.KindShorthandPropertyAssignment: + b.bindAssignmentTargetFlow(p.AsShorthandPropertyAssignment().Name()) + case ast.KindSpreadAssignment: + b.bindAssignmentTargetFlow(p.Expression()) + } + } + default: + if isNarrowableReference(node) { + b.currentFlow = b.createFlowMutation(ast.FlowFlagsAssignment, b.currentFlow, node) + } + } +} + +func (b *Binder) bindDestructuringTargetFlow(node *ast.Node) { + if ast.IsBinaryExpression(node) && node.AsBinaryExpression().OperatorToken.Kind == ast.KindEqualsToken { + b.bindAssignmentTargetFlow(node.AsBinaryExpression().Left) + } else { + b.bindAssignmentTargetFlow(node) + } +} + +func (b *Binder) bindWhileStatement(node *ast.Node) { + stmt := node.AsWhileStatement() + preWhileLabel := b.setContinueTarget(node, b.createLoopLabel()) + preBodyLabel := b.createBranchLabel() + postWhileLabel := b.createBranchLabel() + b.addAntecedent(preWhileLabel, b.currentFlow) + b.currentFlow = preWhileLabel + b.bindCondition(stmt.Expression, preBodyLabel, postWhileLabel) + b.currentFlow = b.finishFlowLabel(preBodyLabel) + b.bindIterativeStatement(stmt.Statement, postWhileLabel, preWhileLabel) + b.addAntecedent(preWhileLabel, b.currentFlow) + b.currentFlow = b.finishFlowLabel(postWhileLabel) +} + +func (b *Binder) bindDoStatement(node *ast.Node) { + stmt := node.AsDoStatement() + preDoLabel := b.createLoopLabel() + preConditionLabel := b.setContinueTarget(node, b.createBranchLabel()) + postDoLabel := b.createBranchLabel() + b.addAntecedent(preDoLabel, b.currentFlow) + b.currentFlow = preDoLabel + b.bindIterativeStatement(stmt.Statement, postDoLabel, preConditionLabel) + b.addAntecedent(preConditionLabel, b.currentFlow) + b.currentFlow = b.finishFlowLabel(preConditionLabel) + b.bindCondition(stmt.Expression, preDoLabel, postDoLabel) + b.currentFlow = b.finishFlowLabel(postDoLabel) +} + +func (b *Binder) bindForStatement(node *ast.Node) { + stmt := node.AsForStatement() + preLoopLabel := b.setContinueTarget(node, b.createLoopLabel()) + preBodyLabel := b.createBranchLabel() + preIncrementorLabel := b.createBranchLabel() + postLoopLabel := b.createBranchLabel() + b.bind(stmt.Initializer) + b.addAntecedent(preLoopLabel, b.currentFlow) + b.currentFlow = preLoopLabel + b.bindCondition(stmt.Condition, preBodyLabel, postLoopLabel) + b.currentFlow = b.finishFlowLabel(preBodyLabel) + b.bindIterativeStatement(stmt.Statement, postLoopLabel, preIncrementorLabel) + b.addAntecedent(preIncrementorLabel, b.currentFlow) + b.currentFlow = b.finishFlowLabel(preIncrementorLabel) + b.bind(stmt.Incrementor) + b.addAntecedent(preLoopLabel, b.currentFlow) + b.currentFlow = b.finishFlowLabel(postLoopLabel) +} + +func (b *Binder) bindForInOrForOfStatement(node *ast.Node) { + stmt := node.AsForInOrOfStatement() + preLoopLabel := b.setContinueTarget(node, b.createLoopLabel()) + postLoopLabel := b.createBranchLabel() + b.bind(stmt.Expression) + b.addAntecedent(preLoopLabel, b.currentFlow) + b.currentFlow = preLoopLabel + if node.Kind == ast.KindForOfStatement { + b.bind(stmt.AwaitModifier) + } + b.addAntecedent(postLoopLabel, b.currentFlow) + b.bind(stmt.Initializer) + if stmt.Initializer.Kind != ast.KindVariableDeclarationList { + b.bindAssignmentTargetFlow(stmt.Initializer) + } + b.bindIterativeStatement(stmt.Statement, postLoopLabel, preLoopLabel) + b.addAntecedent(preLoopLabel, b.currentFlow) + b.currentFlow = b.finishFlowLabel(postLoopLabel) +} + +func (b *Binder) bindIfStatement(node *ast.Node) { + stmt := node.AsIfStatement() + thenLabel := b.createBranchLabel() + elseLabel := b.createBranchLabel() + postIfLabel := b.createBranchLabel() + b.bindCondition(stmt.Expression, thenLabel, elseLabel) + b.currentFlow = b.finishFlowLabel(thenLabel) + b.bind(stmt.ThenStatement) + b.addAntecedent(postIfLabel, b.currentFlow) + b.currentFlow = b.finishFlowLabel(elseLabel) + b.bind(stmt.ElseStatement) + b.addAntecedent(postIfLabel, b.currentFlow) + b.currentFlow = b.finishFlowLabel(postIfLabel) +} + +func (b *Binder) bindReturnStatement(node *ast.Node) { + b.bind(node.Expression()) + if b.currentReturnTarget != nil { + b.addAntecedent(b.currentReturnTarget, b.currentFlow) + } + b.currentFlow = b.unreachableFlow + b.hasExplicitReturn = true + b.hasFlowEffects = true +} + +func (b *Binder) bindThrowStatement(node *ast.Node) { + b.bind(node.Expression()) + b.currentFlow = b.unreachableFlow + b.hasFlowEffects = true +} + +func (b *Binder) bindBreakStatement(node *ast.Node) { + b.bindBreakOrContinueStatement(node.Label(), b.currentBreakTarget, (*ActiveLabel).BreakTarget) +} + +func (b *Binder) bindContinueStatement(node *ast.Node) { + b.bindBreakOrContinueStatement(node.Label(), b.currentContinueTarget, (*ActiveLabel).ContinueTarget) +} + +func (b *Binder) bindBreakOrContinueStatement(label *ast.Node, currentTarget *ast.FlowNode, getTarget func(*ActiveLabel) *ast.FlowNode) { + b.bind(label) + if label != nil { + activeLabel := b.findActiveLabel(label.Text()) + if activeLabel != nil { + activeLabel.referenced = true + b.bindBreakOrContinueFlow(getTarget(activeLabel)) + } + } else { + b.bindBreakOrContinueFlow(currentTarget) + } +} + +func (b *Binder) findActiveLabel(name string) *ActiveLabel { + for label := b.activeLabelList; label != nil; label = label.next { + if label.name == name { + return label + } + } + return nil +} + +func (b *Binder) bindBreakOrContinueFlow(flowLabel *ast.FlowLabel) { + if flowLabel != nil { + b.addAntecedent(flowLabel, b.currentFlow) + b.currentFlow = b.unreachableFlow + b.hasFlowEffects = true + } +} + +func (b *Binder) bindTryStatement(node *ast.Node) { + // We conservatively assume that *any* code in the try block can cause an exception, but we only need + // to track code that causes mutations (because only mutations widen the possible control flow type of + // a variable). The exceptionLabel is the target label for control flows that result from exceptions. + // We add all mutation flow nodes as antecedents of this label such that we can analyze them as possible + // antecedents of the start of catch or finally blocks. Furthermore, we add the current control flow to + // represent exceptions that occur before any mutations. + stmt := node.AsTryStatement() + saveReturnTarget := b.currentReturnTarget + saveExceptionTarget := b.currentExceptionTarget + normalExitLabel := b.createBranchLabel() + returnLabel := b.createBranchLabel() + exceptionLabel := b.createBranchLabel() + if stmt.FinallyBlock != nil { + b.currentReturnTarget = returnLabel + } + b.addAntecedent(exceptionLabel, b.currentFlow) + b.currentExceptionTarget = exceptionLabel + b.bind(stmt.TryBlock) + b.addAntecedent(normalExitLabel, b.currentFlow) + if stmt.CatchClause != nil { + // Start of catch clause is the target of exceptions from try block. + b.currentFlow = b.finishFlowLabel(exceptionLabel) + // The currentExceptionTarget now represents control flows from exceptions in the catch clause. + // Effectively, in a try-catch-finally, if an exception occurs in the try block, the catch block + // acts like a second try block. + exceptionLabel = b.createBranchLabel() + b.addAntecedent(exceptionLabel, b.currentFlow) + b.currentExceptionTarget = exceptionLabel + b.bind(stmt.CatchClause) + b.addAntecedent(normalExitLabel, b.currentFlow) + } + b.currentReturnTarget = saveReturnTarget + b.currentExceptionTarget = saveExceptionTarget + if stmt.FinallyBlock != nil { + // Possible ways control can reach the finally block: + // 1) Normal completion of try block of a try-finally or try-catch-finally + // 2) Normal completion of catch block (following exception in try block) of a try-catch-finally + // 3) Return in try or catch block of a try-finally or try-catch-finally + // 4) Exception in try block of a try-finally + // 5) Exception in catch block of a try-catch-finally + // When analyzing a control flow graph that starts inside a finally block we want to consider all + // five possibilities above. However, when analyzing a control flow graph that starts outside (past) + // the finally block, we only want to consider the first two (if we're past a finally block then it + // must have completed normally). Likewise, when analyzing a control flow graph from return statements + // in try or catch blocks in an IIFE, we only want to consider the third. To make this possible, we + // inject a ReduceLabel node into the control flow graph. This node contains an alternate reduced + // set of antecedents for the pre-finally label. As control flow analysis passes by a ReduceLabel + // node, the pre-finally label is temporarily switched to the reduced antecedent set. + finallyLabel := b.createBranchLabel() + finallyLabel.Antecedents = b.combineFlowLists(normalExitLabel.Antecedents, b.combineFlowLists(exceptionLabel.Antecedents, returnLabel.Antecedents)) + b.currentFlow = finallyLabel + b.bind(stmt.FinallyBlock) + if b.currentFlow.Flags&ast.FlowFlagsUnreachable != 0 { + // If the end of the finally block is unreachable, the end of the entire try statement is unreachable. + b.currentFlow = b.unreachableFlow + } else { + // If we have an IIFE return target and return statements in the try or catch blocks, add a control + // flow that goes back through the finally block and back through only the return statements. + if b.currentReturnTarget != nil && returnLabel.Antecedents != nil { + b.addAntecedent(b.currentReturnTarget, b.createReduceLabel(finallyLabel, returnLabel.Antecedents, b.currentFlow)) + } + // If we have an outer exception target (i.e. a containing try-finally or try-catch-finally), add a + // control flow that goes back through the finally block and back through each possible exception source. + if b.currentExceptionTarget != nil && exceptionLabel.Antecedents != nil { + b.addAntecedent(b.currentExceptionTarget, b.createReduceLabel(finallyLabel, exceptionLabel.Antecedents, b.currentFlow)) + } + // If the end of the finally block is reachable, but the end of the try and catch blocks are not, + // convert the current flow to unreachable. For example, 'try { return 1; } finally { ... }' should + // result in an unreachable current control flow. + if normalExitLabel.Antecedents != nil { + b.currentFlow = b.createReduceLabel(finallyLabel, normalExitLabel.Antecedents, b.currentFlow) + } else { + b.currentFlow = b.unreachableFlow + } + } + } else { + b.currentFlow = b.finishFlowLabel(normalExitLabel) + } +} + +func (b *Binder) bindSwitchStatement(node *ast.Node) { + stmt := node.AsSwitchStatement() + postSwitchLabel := b.createBranchLabel() + b.bind(stmt.Expression) + saveBreakTarget := b.currentBreakTarget + savePreSwitchCaseFlow := b.preSwitchCaseFlow + b.currentBreakTarget = postSwitchLabel + b.preSwitchCaseFlow = b.currentFlow + b.bind(stmt.CaseBlock) + b.addAntecedent(postSwitchLabel, b.currentFlow) + hasDefault := core.Some(stmt.CaseBlock.AsCaseBlock().Clauses.Nodes, func(c *ast.Node) bool { + return c.Kind == ast.KindDefaultClause + }) + if !hasDefault { + b.addAntecedent(postSwitchLabel, b.createFlowSwitchClause(b.preSwitchCaseFlow, node, 0, 0)) + } + b.currentBreakTarget = saveBreakTarget + b.preSwitchCaseFlow = savePreSwitchCaseFlow + b.currentFlow = b.finishFlowLabel(postSwitchLabel) +} + +func (b *Binder) bindCaseBlock(node *ast.Node) { + switchStatement := node.Parent + clauses := node.AsCaseBlock().Clauses.Nodes + isNarrowingSwitch := switchStatement.Expression().Kind == ast.KindTrueKeyword || isNarrowingExpression(switchStatement.Expression()) + var fallthroughFlow *ast.FlowNode = b.unreachableFlow + for i := 0; i < len(clauses); i++ { + clauseStart := i + for len(clauses[i].Statements()) == 0 && i+1 < len(clauses) { + if fallthroughFlow == b.unreachableFlow { + b.currentFlow = b.preSwitchCaseFlow + } + b.bind(clauses[i]) + i++ + } + preCaseLabel := b.createBranchLabel() + preCaseFlow := b.preSwitchCaseFlow + if isNarrowingSwitch { + preCaseFlow = b.createFlowSwitchClause(b.preSwitchCaseFlow, switchStatement, clauseStart, i+1) + } + b.addAntecedent(preCaseLabel, preCaseFlow) + b.addAntecedent(preCaseLabel, fallthroughFlow) + b.currentFlow = b.finishFlowLabel(preCaseLabel) + clause := clauses[i] + b.bind(clause) + fallthroughFlow = b.currentFlow + if b.currentFlow.Flags&ast.FlowFlagsUnreachable == 0 && i != len(clauses)-1 { + clause.AsCaseOrDefaultClause().FallthroughFlowNode = b.currentFlow + } + } +} + +func (b *Binder) bindCaseOrDefaultClause(node *ast.Node) { + clause := node.AsCaseOrDefaultClause() + if clause.Expression != nil { + saveCurrentFlow := b.currentFlow + b.currentFlow = b.preSwitchCaseFlow + b.bind(clause.Expression) + b.currentFlow = saveCurrentFlow + } + b.bindEach(clause.Statements.Nodes) +} + +func (b *Binder) bindExpressionStatement(node *ast.Node) { + stmt := node.AsExpressionStatement() + b.bind(stmt.Expression) + b.maybeBindExpressionFlowIfCall(stmt.Expression) +} + +func (b *Binder) maybeBindExpressionFlowIfCall(node *ast.Node) { + // A top level or comma expression call expression with a dotted function name and at least one argument + // is potentially an assertion and is therefore included in the control flow. + if ast.IsCallExpression(node) { + if node.Expression().Kind != ast.KindSuperKeyword && ast.IsDottedName(node.Expression()) { + b.currentFlow = b.createFlowCall(b.currentFlow, node) + } + } +} + +func (b *Binder) bindLabeledStatement(node *ast.Node) { + stmt := node.AsLabeledStatement() + postStatementLabel := b.createBranchLabel() + b.activeLabelList = &ActiveLabel{ + next: b.activeLabelList, + name: stmt.Label.Text(), + breakTarget: postStatementLabel, + continueTarget: nil, + referenced: false, + } + b.bind(stmt.Label) + b.bind(stmt.Statement) + if !b.activeLabelList.referenced { + // Mark the label as unused; the checker will decide whether to report it + stmt.Label.Flags |= ast.NodeFlagsUnreachable + } + b.activeLabelList = b.activeLabelList.next + b.addAntecedent(postStatementLabel, b.currentFlow) + b.currentFlow = b.finishFlowLabel(postStatementLabel) +} + +func (b *Binder) bindPrefixUnaryExpressionFlow(node *ast.Node) { + expr := node.AsPrefixUnaryExpression() + if expr.Operator == ast.KindExclamationToken { + saveTrueTarget := b.currentTrueTarget + b.currentTrueTarget = b.currentFalseTarget + b.currentFalseTarget = saveTrueTarget + b.bindEachChild(node) + b.currentFalseTarget = b.currentTrueTarget + b.currentTrueTarget = saveTrueTarget + } else { + b.bindEachChild(node) + if expr.Operator == ast.KindPlusPlusToken || expr.Operator == ast.KindMinusMinusToken { + b.bindAssignmentTargetFlow(expr.Operand) + } + } +} + +func (b *Binder) bindPostfixUnaryExpressionFlow(node *ast.Node) { + expr := node.AsPostfixUnaryExpression() + b.bindEachChild(node) + if expr.Operator == ast.KindPlusPlusToken || expr.Operator == ast.KindMinusMinusToken { + b.bindAssignmentTargetFlow(expr.Operand) + } +} + +func (b *Binder) bindDestructuringAssignmentFlow(node *ast.Node) { + expr := node.AsBinaryExpression() + if b.inAssignmentPattern { + b.inAssignmentPattern = false + b.bind(expr.OperatorToken) + b.bind(expr.Right) + b.inAssignmentPattern = true + b.bind(expr.Left) + b.bind(expr.Type) + } else { + b.inAssignmentPattern = true + b.bind(expr.Left) + b.bind(expr.Type) + b.inAssignmentPattern = false + b.bind(expr.OperatorToken) + b.bind(expr.Right) + } + b.bindAssignmentTargetFlow(expr.Left) +} + +func (b *Binder) bindBinaryExpressionFlow(node *ast.Node) { + expr := node.AsBinaryExpression() + operator := expr.OperatorToken.Kind + if ast.IsLogicalOrCoalescingBinaryOperator(operator) || ast.IsLogicalOrCoalescingAssignmentOperator(operator) { + if isTopLevelLogicalExpression(node) { + postExpressionLabel := b.createBranchLabel() + saveCurrentFlow := b.currentFlow + saveHasFlowEffects := b.hasFlowEffects + b.hasFlowEffects = false + b.bindLogicalLikeExpression(node, postExpressionLabel, postExpressionLabel) + if b.hasFlowEffects { + b.currentFlow = b.finishFlowLabel(postExpressionLabel) + } else { + b.currentFlow = saveCurrentFlow + } + b.hasFlowEffects = b.hasFlowEffects || saveHasFlowEffects + } else { + b.bindLogicalLikeExpression(node, b.currentTrueTarget, b.currentFalseTarget) + } + } else { + b.bind(expr.Left) + b.bind(expr.Type) + if operator == ast.KindCommaToken { + b.maybeBindExpressionFlowIfCall(expr.Left) + } + b.bind(expr.OperatorToken) + b.bind(expr.Right) + if operator == ast.KindCommaToken { + b.maybeBindExpressionFlowIfCall(expr.Right) + } + if ast.IsAssignmentOperator(operator) && !ast.IsAssignmentTarget(node) { + b.bindAssignmentTargetFlow(expr.Left) + if operator == ast.KindEqualsToken && expr.Left.Kind == ast.KindElementAccessExpression { + elementAccess := expr.Left.AsElementAccessExpression() + if isNarrowableOperand(elementAccess.Expression) { + b.currentFlow = b.createFlowMutation(ast.FlowFlagsArrayMutation, b.currentFlow, node) + } + } + } + } +} + +func (b *Binder) bindLogicalLikeExpression(node *ast.Node, trueTarget *ast.FlowLabel, falseTarget *ast.FlowLabel) { + expr := node.AsBinaryExpression() + preRightLabel := b.createBranchLabel() + if expr.OperatorToken.Kind == ast.KindAmpersandAmpersandToken || expr.OperatorToken.Kind == ast.KindAmpersandAmpersandEqualsToken { + b.bindCondition(expr.Left, preRightLabel, falseTarget) + } else { + b.bindCondition(expr.Left, trueTarget, preRightLabel) + } + b.currentFlow = b.finishFlowLabel(preRightLabel) + b.bind(expr.OperatorToken) + if ast.IsLogicalOrCoalescingAssignmentOperator(expr.OperatorToken.Kind) { + b.doWithConditionalBranches((*Binder).bind, expr.Right, trueTarget, falseTarget) + b.bindAssignmentTargetFlow(expr.Left) + b.addAntecedent(trueTarget, b.createFlowCondition(ast.FlowFlagsTrueCondition, b.currentFlow, node)) + b.addAntecedent(falseTarget, b.createFlowCondition(ast.FlowFlagsFalseCondition, b.currentFlow, node)) + } else { + b.bindCondition(expr.Right, trueTarget, falseTarget) + } +} + +func (b *Binder) bindDeleteExpressionFlow(node *ast.Node) { + expr := node.AsDeleteExpression() + b.bindEachChild(node) + if expr.Expression.Kind == ast.KindPropertyAccessExpression { + b.bindAssignmentTargetFlow(expr.Expression) + } +} + +func (b *Binder) bindConditionalExpressionFlow(node *ast.Node) { + expr := node.AsConditionalExpression() + trueLabel := b.createBranchLabel() + falseLabel := b.createBranchLabel() + postExpressionLabel := b.createBranchLabel() + saveCurrentFlow := b.currentFlow + saveHasFlowEffects := b.hasFlowEffects + b.hasFlowEffects = false + b.bindCondition(expr.Condition, trueLabel, falseLabel) + b.currentFlow = b.finishFlowLabel(trueLabel) + b.bind(expr.QuestionToken) + b.bind(expr.WhenTrue) + b.addAntecedent(postExpressionLabel, b.currentFlow) + b.currentFlow = b.finishFlowLabel(falseLabel) + b.bind(expr.ColonToken) + b.bind(expr.WhenFalse) + b.addAntecedent(postExpressionLabel, b.currentFlow) + if b.hasFlowEffects { + b.currentFlow = b.finishFlowLabel(postExpressionLabel) + } else { + b.currentFlow = saveCurrentFlow + } + b.hasFlowEffects = b.hasFlowEffects || saveHasFlowEffects +} + +func (b *Binder) bindVariableDeclarationFlow(node *ast.Node) { + b.bindEachChild(node) + if node.Initializer() != nil || ast.IsForInOrOfStatement(node.Parent.Parent) { + b.bindInitializedVariableFlow(node) + } +} + +func (b *Binder) bindInitializedVariableFlow(node *ast.Node) { + var name *ast.Node + switch node.Kind { + case ast.KindVariableDeclaration: + name = node.AsVariableDeclaration().Name() + case ast.KindBindingElement: + name = node.AsBindingElement().Name() + } + if name != nil && ast.IsBindingPattern(name) { + for _, child := range name.Elements() { + b.bindInitializedVariableFlow(child) + } + } else { + b.currentFlow = b.createFlowMutation(ast.FlowFlagsAssignment, b.currentFlow, node) + } +} + +func (b *Binder) bindAccessExpressionFlow(node *ast.Node) { + if ast.IsOptionalChain(node) { + b.bindOptionalChainFlow(node) + } else { + b.bindEachChild(node) + } +} + +func (b *Binder) bindOptionalChainFlow(node *ast.Node) { + if isTopLevelLogicalExpression(node) { + postExpressionLabel := b.createBranchLabel() + saveCurrentFlow := b.currentFlow + saveHasFlowEffects := b.hasFlowEffects + b.bindOptionalChain(node, postExpressionLabel, postExpressionLabel) + if b.hasFlowEffects { + b.currentFlow = b.finishFlowLabel(postExpressionLabel) + } else { + b.currentFlow = saveCurrentFlow + } + b.hasFlowEffects = b.hasFlowEffects || saveHasFlowEffects + } else { + b.bindOptionalChain(node, b.currentTrueTarget, b.currentFalseTarget) + } +} + +func (b *Binder) bindOptionalChain(node *ast.Node, trueTarget *ast.FlowLabel, falseTarget *ast.FlowLabel) { + // For an optional chain, we emulate the behavior of a logical expression: + // + // a?.b -> a && a.b + // a?.b.c -> a && a.b.c + // a?.b?.c -> a && a.b && a.b.c + // a?.[x = 1] -> a && a[x = 1] + // + // To do this we descend through the chain until we reach the root of a chain (the expression with a `?.`) + // and build it's CFA graph as if it were the first condition (`a && ...`). Then we bind the rest + // of the node as part of the "true" branch, and continue to do so as we ascend back up to the outermost + // chain node. We then treat the entire node as the right side of the expression. + var preChainLabel *ast.FlowLabel + if ast.IsOptionalChainRoot(node) { + preChainLabel = b.createBranchLabel() + } + b.bindOptionalExpression(node.Expression(), core.IfElse(preChainLabel != nil, preChainLabel, trueTarget), falseTarget) + if preChainLabel != nil { + b.currentFlow = b.finishFlowLabel(preChainLabel) + } + b.doWithConditionalBranches((*Binder).bindOptionalChainRest, node, trueTarget, falseTarget) + if ast.IsOutermostOptionalChain(node) { + b.addAntecedent(trueTarget, b.createFlowCondition(ast.FlowFlagsTrueCondition, b.currentFlow, node)) + b.addAntecedent(falseTarget, b.createFlowCondition(ast.FlowFlagsFalseCondition, b.currentFlow, node)) + } +} + +func (b *Binder) bindOptionalExpression(node *ast.Node, trueTarget *ast.FlowLabel, falseTarget *ast.FlowLabel) { + b.doWithConditionalBranches((*Binder).bind, node, trueTarget, falseTarget) + if !ast.IsOptionalChain(node) || ast.IsOutermostOptionalChain(node) { + b.addAntecedent(trueTarget, b.createFlowCondition(ast.FlowFlagsTrueCondition, b.currentFlow, node)) + b.addAntecedent(falseTarget, b.createFlowCondition(ast.FlowFlagsFalseCondition, b.currentFlow, node)) + } +} + +func (b *Binder) bindOptionalChainRest(node *ast.Node) bool { + switch node.Kind { + case ast.KindPropertyAccessExpression: + b.bind(node.QuestionDotToken()) + b.bind(node.Name()) + case ast.KindElementAccessExpression: + b.bind(node.QuestionDotToken()) + b.bind(node.AsElementAccessExpression().ArgumentExpression) + case ast.KindCallExpression: + b.bind(node.QuestionDotToken()) + b.bindNodeList(node.TypeArgumentList()) + b.bindEach(node.Arguments()) + } + return false +} + +func (b *Binder) bindCallExpressionFlow(node *ast.Node) { + call := node.AsCallExpression() + if ast.IsOptionalChain(node) { + b.bindOptionalChainFlow(node) + } else { + // If the target of the call expression is a function expression or arrow function we have + // an immediately invoked function expression (IIFE). Initialize the flowNode property to + // the current control flow (which includes evaluation of the IIFE arguments). + expr := ast.SkipParentheses(call.Expression) + if expr.Kind == ast.KindFunctionExpression || expr.Kind == ast.KindArrowFunction { + b.bindNodeList(call.TypeArguments) + b.bindEach(call.Arguments.Nodes) + b.bind(call.Expression) + } else { + b.bindEachChild(node) + if call.Expression.Kind == ast.KindSuperKeyword { + b.currentFlow = b.createFlowCall(b.currentFlow, node) + } + } + } + if ast.IsPropertyAccessExpression(call.Expression) { + access := call.Expression.AsPropertyAccessExpression() + if ast.IsIdentifier(access.Name()) && isNarrowableOperand(access.Expression) && ast.IsPushOrUnshiftIdentifier(access.Name()) { + b.currentFlow = b.createFlowMutation(ast.FlowFlagsArrayMutation, b.currentFlow, node) + } + } +} + +func (b *Binder) bindNonNullExpressionFlow(node *ast.Node) { + if ast.IsOptionalChain(node) { + b.bindOptionalChainFlow(node) + } else { + b.bindEachChild(node) + } +} + +func (b *Binder) bindBindingElementFlow(node *ast.Node) { + // When evaluating a binding pattern, the initializer is evaluated before the binding pattern, per: + // - https://tc39.es/ecma262/#sec-destructuring-binding-patterns-runtime-semantics-iteratorbindinginitialization + // - `BindingElement: BindingPattern Initializer?` + // - https://tc39.es/ecma262/#sec-runtime-semantics-keyedbindinginitialization + // - `BindingElement: BindingPattern Initializer?` + elem := node.AsBindingElement() + b.bind(elem.DotDotDotToken) + b.bind(elem.PropertyName) + b.bindInitializer(elem.Initializer) + b.bind(elem.Name()) +} + +func (b *Binder) bindParameterFlow(node *ast.Node) { + param := node.AsParameterDeclaration() + b.bindModifiers(param.Modifiers()) + b.bind(param.DotDotDotToken) + b.bind(param.QuestionToken) + b.bind(param.Type) + b.bindInitializer(param.Initializer) + b.bind(param.Name()) +} + +// a BindingElement/Parameter does not have side effects if initializers are not evaluated and used. (see GH#49759) +func (b *Binder) bindInitializer(node *ast.Node) { + if node == nil { + return + } + entryFlow := b.currentFlow + b.bind(node) + if entryFlow == b.unreachableFlow || entryFlow == b.currentFlow { + return + } + exitFlow := b.createBranchLabel() + b.addAntecedent(exitFlow, entryFlow) + b.addAntecedent(exitFlow, b.currentFlow) + b.currentFlow = b.finishFlowLabel(exitFlow) +} + +func setFlowNode(node *ast.Node, flowNode *ast.FlowNode) { + data := node.FlowNodeData() + if data != nil { + data.FlowNode = flowNode + } +} + +func setReturnFlowNode(node *ast.Node, returnFlowNode *ast.FlowNode) { + switch node.Kind { + case ast.KindConstructor: + node.AsConstructorDeclaration().ReturnFlowNode = returnFlowNode + case ast.KindFunctionDeclaration: + node.AsFunctionDeclaration().ReturnFlowNode = returnFlowNode + case ast.KindFunctionExpression: + node.AsFunctionExpression().ReturnFlowNode = returnFlowNode + case ast.KindClassStaticBlockDeclaration: + node.AsClassStaticBlockDeclaration().ReturnFlowNode = returnFlowNode + } +} + +func isGeneratorFunctionExpression(node *ast.Node) bool { + return ast.IsFunctionExpression(node) && node.AsFunctionExpression().AsteriskToken != nil +} + +func (b *Binder) addToContainerChain(next *ast.Node) { + if b.lastContainer != nil { + b.lastContainer.LocalsContainerData().NextContainer = next + } + b.lastContainer = next +} + +func (b *Binder) addDeclarationToSymbol(symbol *ast.Symbol, node *ast.Node, symbolFlags ast.SymbolFlags) { + symbol.Flags |= symbolFlags + node.DeclarationData().Symbol = symbol + if symbol.Declarations == nil { + symbol.Declarations = b.newSingleDeclaration(node) + } else { + symbol.Declarations = core.AppendIfUnique(symbol.Declarations, node) + } + // On merge of const enum module with class or function, reset const enum only flag (namespaces will already recalculate) + if symbol.Flags&ast.SymbolFlagsConstEnumOnlyModule != 0 && symbol.Flags&(ast.SymbolFlagsFunction|ast.SymbolFlagsClass|ast.SymbolFlagsRegularEnum) != 0 { + symbol.Flags &^= ast.SymbolFlagsConstEnumOnlyModule + b.notConstEnumOnlyModules.Add(symbol) + } + if symbolFlags&ast.SymbolFlagsValue != 0 { + SetValueDeclaration(symbol, node) + } +} + +func SetValueDeclaration(symbol *ast.Symbol, node *ast.Node) { + valueDeclaration := symbol.ValueDeclaration + if valueDeclaration == nil || + isAssignmentDeclaration(valueDeclaration) && !isAssignmentDeclaration(node) || + valueDeclaration.Kind != node.Kind && isEffectiveModuleDeclaration(valueDeclaration) { + // Non-assignment declarations take precedence over assignment declarations and + // non-namespace declarations take precedence over namespace declarations. + symbol.ValueDeclaration = node + } +} + +/** + * Declares a Symbol for the node and adds it to symbols. Reports errors for conflicting identifier names. + * @param symbolTable - The symbol table which node will be added to. + * @param parent - node's parent declaration. + * @param node - The declaration to be added to the symbol table + * @param includes - The SymbolFlags that node has in addition to its declaration type (eg: export, ambient, etc.) + * @param excludes - The flags which node cannot be declared alongside in a symbol table. Used to report forbidden declarations. + */ + +func GetContainerFlags(node *ast.Node) ContainerFlags { + switch node.Kind { + case ast.KindClassExpression, ast.KindClassDeclaration, ast.KindEnumDeclaration, ast.KindObjectLiteralExpression, ast.KindTypeLiteral, + ast.KindJsxAttributes: + return ContainerFlagsIsContainer + case ast.KindInterfaceDeclaration: + return ContainerFlagsIsContainer | ContainerFlagsIsInterface + case ast.KindModuleDeclaration, ast.KindTypeAliasDeclaration, ast.KindJSTypeAliasDeclaration, ast.KindMappedType, ast.KindIndexSignature: + return ContainerFlagsIsContainer | ContainerFlagsHasLocals + case ast.KindSourceFile: + return ContainerFlagsIsContainer | ContainerFlagsIsControlFlowContainer | ContainerFlagsHasLocals + case ast.KindGetAccessor, ast.KindSetAccessor, ast.KindMethodDeclaration: + if ast.IsObjectLiteralOrClassExpressionMethodOrAccessor(node) { + return ContainerFlagsIsContainer | ContainerFlagsIsControlFlowContainer | ContainerFlagsHasLocals | ContainerFlagsIsFunctionLike | ContainerFlagsIsObjectLiteralOrClassExpressionMethodOrAccessor | ContainerFlagsIsThisContainer + } + fallthrough + case ast.KindConstructor, ast.KindFunctionDeclaration, ast.KindClassStaticBlockDeclaration: + return ContainerFlagsIsContainer | ContainerFlagsIsControlFlowContainer | ContainerFlagsHasLocals | ContainerFlagsIsFunctionLike | ContainerFlagsIsThisContainer + case ast.KindMethodSignature, ast.KindCallSignature, ast.KindFunctionType, ast.KindConstructSignature, ast.KindConstructorType: + return ContainerFlagsIsContainer | ContainerFlagsIsControlFlowContainer | ContainerFlagsHasLocals | ContainerFlagsIsFunctionLike | ContainerFlagsPropagatesThisKeyword + case ast.KindFunctionExpression: + return ContainerFlagsIsContainer | ContainerFlagsIsControlFlowContainer | ContainerFlagsHasLocals | ContainerFlagsIsFunctionLike | ContainerFlagsIsFunctionExpression | ContainerFlagsIsThisContainer + case ast.KindArrowFunction: + return ContainerFlagsIsContainer | ContainerFlagsIsControlFlowContainer | ContainerFlagsHasLocals | ContainerFlagsIsFunctionLike | ContainerFlagsIsFunctionExpression | ContainerFlagsPropagatesThisKeyword + case ast.KindModuleBlock: + return ContainerFlagsIsControlFlowContainer + case ast.KindPropertyDeclaration: + if node.Initializer() != nil { + return ContainerFlagsIsControlFlowContainer | ContainerFlagsIsThisContainer + } else { + return ContainerFlagsNone + } + case ast.KindCatchClause, ast.KindForStatement, ast.KindForInStatement, ast.KindForOfStatement, ast.KindCaseBlock: + return ContainerFlagsIsBlockScopedContainer | ContainerFlagsHasLocals + case ast.KindBlock: + if ast.IsFunctionLike(node.Parent) || ast.IsClassStaticBlockDeclaration(node.Parent) { + return ContainerFlagsNone + } else { + return ContainerFlagsIsBlockScopedContainer | ContainerFlagsHasLocals + } + } + return ContainerFlagsNone +} + +func isNarrowingExpression(expr *ast.Node) bool { + switch expr.Kind { + case ast.KindIdentifier, ast.KindThisKeyword: + return true + case ast.KindPropertyAccessExpression, ast.KindElementAccessExpression: + return containsNarrowableReference(expr) + case ast.KindCallExpression: + return hasNarrowableArgument(expr) + case ast.KindParenthesizedExpression, ast.KindNonNullExpression, ast.KindTypeOfExpression: + return isNarrowingExpression(expr.Expression()) + case ast.KindBinaryExpression: + return isNarrowingBinaryExpression(expr.AsBinaryExpression()) + case ast.KindPrefixUnaryExpression: + return expr.AsPrefixUnaryExpression().Operator == ast.KindExclamationToken && isNarrowingExpression(expr.AsPrefixUnaryExpression().Operand) + } + return false +} + +func containsNarrowableReference(expr *ast.Node) bool { + if isNarrowableReference(expr) { + return true + } + if expr.Flags&ast.NodeFlagsOptionalChain != 0 { + switch expr.Kind { + case ast.KindPropertyAccessExpression, ast.KindElementAccessExpression, ast.KindCallExpression, ast.KindNonNullExpression: + return containsNarrowableReference(expr.Expression()) + } + } + return false +} + +func isNarrowableReference(node *ast.Node) bool { + switch node.Kind { + case ast.KindIdentifier, ast.KindThisKeyword, ast.KindSuperKeyword, ast.KindMetaProperty: + return true + case ast.KindPropertyAccessExpression, ast.KindParenthesizedExpression, ast.KindNonNullExpression: + return isNarrowableReference(node.Expression()) + case ast.KindElementAccessExpression: + expr := node.AsElementAccessExpression() + return ast.IsStringOrNumericLiteralLike(expr.ArgumentExpression) || + ast.IsEntityNameExpression(expr.ArgumentExpression) && isNarrowableReference(expr.Expression) + case ast.KindBinaryExpression: + expr := node.AsBinaryExpression() + return expr.OperatorToken.Kind == ast.KindCommaToken && isNarrowableReference(expr.Right) || + ast.IsAssignmentOperator(expr.OperatorToken.Kind) && ast.IsLeftHandSideExpression(expr.Left) + } + return false +} + +func hasNarrowableArgument(expr *ast.Node) bool { + call := expr.AsCallExpression() + for _, argument := range call.Arguments.Nodes { //nolint:modernize + if containsNarrowableReference(argument) { + return true + } + } + if ast.IsPropertyAccessExpression(call.Expression) { + if containsNarrowableReference(call.Expression.Expression()) { + return true + } + } + return false +} + +func isNarrowingBinaryExpression(expr *ast.BinaryExpression) bool { + switch expr.OperatorToken.Kind { + case ast.KindEqualsToken, ast.KindBarBarEqualsToken, ast.KindAmpersandAmpersandEqualsToken, ast.KindQuestionQuestionEqualsToken: + return containsNarrowableReference(expr.Left) + case ast.KindEqualsEqualsToken, ast.KindExclamationEqualsToken, ast.KindEqualsEqualsEqualsToken, ast.KindExclamationEqualsEqualsToken: + left := ast.SkipParentheses(expr.Left) + right := ast.SkipParentheses(expr.Right) + return isNarrowableOperand(left) || isNarrowableOperand(right) || + isNarrowingTypeOfOperands(right, left) || isNarrowingTypeOfOperands(left, right) || + (ast.IsBooleanLiteral(right) && isNarrowingExpression(left) || ast.IsBooleanLiteral(left) && isNarrowingExpression(right)) + case ast.KindInstanceOfKeyword: + return isNarrowableOperand(expr.Left) + case ast.KindInKeyword: + return isNarrowingExpression(expr.Right) + case ast.KindCommaToken: + return isNarrowingExpression(expr.Right) + } + return false +} + +func isNarrowableOperand(expr *ast.Node) bool { + switch expr.Kind { + case ast.KindParenthesizedExpression: + return isNarrowableOperand(expr.Expression()) + case ast.KindBinaryExpression: + binary := expr.AsBinaryExpression() + switch binary.OperatorToken.Kind { + case ast.KindEqualsToken: + return isNarrowableOperand(binary.Left) + case ast.KindCommaToken: + return isNarrowableOperand(binary.Right) + } + } + return containsNarrowableReference(expr) +} + +func isNarrowingTypeOfOperands(expr1 *ast.Node, expr2 *ast.Node) bool { + return ast.IsTypeOfExpression(expr1) && isNarrowableOperand(expr1.Expression()) && ast.IsStringLiteralLike(expr2) +} + +func (b *Binder) errorOnNode(node *ast.Node, message *diagnostics.Message, args ...any) { + b.addDiagnostic(b.createDiagnosticForNode(node, message, args...)) +} + +func (b *Binder) errorOnFirstToken(node *ast.Node, message *diagnostics.Message, args ...any) { + span := scanner.GetRangeOfTokenAtPosition(b.file, node.Pos()) + b.addDiagnostic(ast.NewDiagnostic(b.file, span, message, args...)) +} + +func (b *Binder) errorOrSuggestionOnNode(isError bool, node *ast.Node, message *diagnostics.Message) { + b.errorOrSuggestionOnRange(isError, node, node, message) +} + +func (b *Binder) errorOrSuggestionOnRange(isError bool, startNode *ast.Node, endNode *ast.Node, message *diagnostics.Message) { + textRange := core.NewTextRange(scanner.GetRangeOfTokenAtPosition(b.file, startNode.Pos()).Pos(), endNode.End()) + diagnostic := ast.NewDiagnostic(b.file, textRange, message) + if isError { + b.addDiagnostic(diagnostic) + } else { + diagnostic.SetCategory(diagnostics.CategorySuggestion) + b.file.BindSuggestionDiagnostics = append(b.file.BindSuggestionDiagnostics, diagnostic) + } +} + +// Inside the binder, we may create a diagnostic for an as-yet unbound node (with potentially no parent pointers, implying no accessible source file) +// If so, the node _must_ be in the current file (as that's the only way anything could have traversed to it to yield it as the error node) +// This version of `createDiagnosticForNode` uses the binder's context to account for this, and always yields correct diagnostics even in these situations. +func (b *Binder) createDiagnosticForNode(node *ast.Node, message *diagnostics.Message, args ...any) *ast.Diagnostic { + return ast.NewDiagnostic(b.file, scanner.GetErrorRangeForNode(b.file, node), message, args...) +} + +func (b *Binder) addDiagnostic(diagnostic *ast.Diagnostic) { + b.file.SetBindDiagnostics(append(b.file.BindDiagnostics(), diagnostic)) +} + +func isSignedNumericLiteral(node *ast.Node) bool { + if node.Kind == ast.KindPrefixUnaryExpression { + node := node.AsPrefixUnaryExpression() + return (node.Operator == ast.KindPlusToken || node.Operator == ast.KindMinusToken) && ast.IsNumericLiteral(node.Operand) + } + return false +} + +func getOptionalSymbolFlagForNode(node *ast.Node) ast.SymbolFlags { + postfixToken := node.PostfixToken() + return core.IfElse(postfixToken != nil && postfixToken.Kind == ast.KindQuestionToken, ast.SymbolFlagsOptional, ast.SymbolFlagsNone) +} + +func isFunctionSymbol(symbol *ast.Symbol) bool { + d := symbol.ValueDeclaration + if d != nil { + if ast.IsFunctionDeclaration(d) { + return true + } + if ast.IsVariableDeclaration(d) { + varDecl := d.AsVariableDeclaration() + if varDecl.Initializer != nil { + return ast.IsFunctionLike(varDecl.Initializer) + } + } + } + return false +} + +func isStatementCondition(node *ast.Node) bool { + switch node.Parent.Kind { + case ast.KindIfStatement, ast.KindWhileStatement, ast.KindDoStatement: + return node.Parent.Expression() == node + case ast.KindForStatement: + return node.Parent.AsForStatement().Condition == node + case ast.KindConditionalExpression: + return node.Parent.AsConditionalExpression().Condition == node + } + return false +} + +func isTopLevelLogicalExpression(node *ast.Node) bool { + for ast.IsParenthesizedExpression(node.Parent) || ast.IsPrefixUnaryExpression(node.Parent) && node.Parent.AsPrefixUnaryExpression().Operator == ast.KindExclamationToken { + node = node.Parent + } + return !isStatementCondition(node) && !ast.IsLogicalExpression(node.Parent) && !(ast.IsOptionalChain(node.Parent) && node.Parent.Expression() == node) +} + +func isAssignmentDeclaration(decl *ast.Node) bool { + return ast.IsBinaryExpression(decl) || ast.IsAccessExpression(decl) || ast.IsIdentifier(decl) || ast.IsCallExpression(decl) +} + +func isEffectiveModuleDeclaration(node *ast.Node) bool { + return ast.IsModuleDeclaration(node) || ast.IsIdentifier(node) +} diff --git a/tools/tsgo/internal/binder/binder_test.go b/tools/tsgo/internal/binder/binder_test.go new file mode 100644 index 00000000..3cb495ae --- /dev/null +++ b/tools/tsgo/internal/binder/binder_test.go @@ -0,0 +1,46 @@ +package binder + +import ( + "runtime" + "testing" + + "github.com/microsoft/typescript-go/internal/ast" + "github.com/microsoft/typescript-go/internal/core" + "github.com/microsoft/typescript-go/internal/parser" + "github.com/microsoft/typescript-go/internal/testutil/fixtures" + "github.com/microsoft/typescript-go/internal/tspath" + "github.com/microsoft/typescript-go/internal/vfs/osvfs" +) + +func BenchmarkBind(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) + + parseOptions := ast.SourceFileParseOptions{ + FileName: fileName, + Path: path, + } + scriptKind := core.GetScriptKindFromFileName(fileName) + + sourceFiles := make([]*ast.SourceFile, b.N) + for i := range b.N { + sourceFiles[i] = parser.ParseSourceFile(parseOptions, sourceText, scriptKind) + } + + // The above parses do a lot of work; ensure GC is finished before we start collecting performance data. + // GC must be called twice to allow things to settle. + runtime.GC() + runtime.GC() + + b.ResetTimer() + for i := range b.N { + BindSourceFile(sourceFiles[i]) + } + }) + } +} diff --git a/tools/tsgo/internal/binder/nameresolver.go b/tools/tsgo/internal/binder/nameresolver.go new file mode 100644 index 00000000..2ab0ab5a --- /dev/null +++ b/tools/tsgo/internal/binder/nameresolver.go @@ -0,0 +1,498 @@ +package binder + +import ( + "github.com/microsoft/typescript-go/internal/ast" + "github.com/microsoft/typescript-go/internal/core" + "github.com/microsoft/typescript-go/internal/diagnostics" +) + +type NameResolver struct { + CompilerOptions *core.CompilerOptions + GetSymbolOfDeclaration func(node *ast.Node) *ast.Symbol + Error func(location *ast.Node, message *diagnostics.Message, args ...any) *ast.Diagnostic + Globals ast.SymbolTable + ArgumentsSymbol *ast.Symbol + RequireSymbol *ast.Symbol + Lookup func(symbols ast.SymbolTable, name string, meaning ast.SymbolFlags) *ast.Symbol + SymbolReferenced func(symbol *ast.Symbol, meaning ast.SymbolFlags) + SetRequiresScopeChangeCache func(node *ast.Node, value core.Tristate) + GetRequiresScopeChangeCache func(node *ast.Node) core.Tristate + OnPropertyWithInvalidInitializer func(location *ast.Node, name string, declaration *ast.Node, result *ast.Symbol) bool + OnFailedToResolveSymbol func(location *ast.Node, name string, meaning ast.SymbolFlags, nameNotFoundMessage *diagnostics.Message) + OnSuccessfullyResolvedSymbol func(location *ast.Node, result *ast.Symbol, meaning ast.SymbolFlags, lastLocation *ast.Node, associatedDeclarationForContainingInitializerOrBindingName *ast.Node, withinDeferredContext bool) +} + +func (r *NameResolver) Resolve(location *ast.Node, name string, meaning ast.SymbolFlags, nameNotFoundMessage *diagnostics.Message, isUse bool, excludeGlobals bool) *ast.Symbol { + var result *ast.Symbol + var lastLocation *ast.Node + var lastSelfReferenceLocation *ast.Node + var propertyWithInvalidInitializer *ast.Node + var associatedDeclarationForContainingInitializerOrBindingName *ast.Node + var withinDeferredContext bool + var grandparent *ast.Node + originalLocation := location // needed for did-you-mean error reporting, which gathers candidates starting from the original location + nameIsConst := name == "const" +loop: + for location != nil { + if nameIsConst && ast.IsConstAssertion(location) { + // `const` in an `as const` has no symbol, but issues no error because there is no *actual* lookup of the type + // (it refers to the constant type of the expression instead) + return nil + } + if ast.IsModuleOrEnumDeclaration(location) && lastLocation != nil && location.Name() == lastLocation { + // If lastLocation is the name of a namespace or enum, skip the parent since it will have is own locals that could + // conflict. + lastLocation = location + location = location.Parent + } + locals := location.Locals() + // Locals of a source file are not in scope (because they get merged into the global symbol table) + if locals != nil && !ast.IsGlobalSourceFile(location) { + result = r.lookup(locals, name, meaning) + if result != nil { + useResult := true + if ast.IsFunctionLike(location) && lastLocation != nil && lastLocation != location.Body() { + // symbol lookup restrictions for function-like declarations + // - Type parameters of a function are in scope in the entire function declaration, including the parameter + // list and return type. However, local types are only in scope in the function body. + // - parameters are only in the scope of function body + // This restriction does not apply to JSDoc comment types because they are parented + // at a higher level than type parameters would normally be + if meaning&result.Flags&ast.SymbolFlagsType != 0 && lastLocation.Kind != ast.KindJSDoc { + // type parameters are visible in parameter list, return type and type parameter list. + // Synthetic fake scopes are added for signatures so type parameters are accessible from them. + useResult = result.Flags&ast.SymbolFlagsTypeParameter != 0 && + (lastLocation.Flags&ast.NodeFlagsSynthesized != 0 || + lastLocation == location.Type() || + lastLocation.Kind == ast.KindParameter || + lastLocation.Kind == ast.KindJSDocParameterTag || + lastLocation.Kind == ast.KindJSDocReturnTag || + lastLocation.Kind == ast.KindTypeParameter) + } + if meaning&result.Flags&ast.SymbolFlagsVariable != 0 { + // expression inside parameter will lookup as normal variable scope when targeting es2015+ + if r.useOuterVariableScopeInParameter(result, location, lastLocation) { + useResult = false + } else if result.Flags&ast.SymbolFlagsFunctionScopedVariable != 0 { + // parameters are visible only inside function body, parameter list and return type + // technically for parameter list case here we might mix parameters and variables declared in function, + // however it is detected separately when checking initializers of parameters + // to make sure that they reference no variables declared after them. + useResult = lastLocation.Kind == ast.KindParameter || + lastLocation.Flags&ast.NodeFlagsSynthesized != 0 || + lastLocation == location.Type() && ast.FindAncestor(result.ValueDeclaration, ast.IsParameterDeclaration) != nil + } + } + } else if location.Kind == ast.KindConditionalType { + // A type parameter declared using 'infer T' in a conditional type is visible only in + // the true branch of the conditional type. + useResult = lastLocation == location.AsConditionalTypeNode().TrueType + } + if useResult { + break loop + } + result = nil + } + } + withinDeferredContext = withinDeferredContext || getIsDeferredContext(location, lastLocation) + switch location.Kind { + case ast.KindSourceFile: + if !ast.IsExternalOrCommonJSModule(location.AsSourceFile()) { + break + } + fallthrough + case ast.KindModuleDeclaration: + moduleSymbol := r.getSymbolOfDeclaration(location) + if moduleSymbol == nil { + break + } + moduleExports := moduleSymbol.Exports + if ast.IsSourceFile(location) || (ast.IsModuleDeclaration(location) && location.Flags&ast.NodeFlagsAmbient != 0 && !ast.IsGlobalScopeAugmentation(location)) { + // It's an external module. First see if the module has an export default and if the local + // name of that export default matches. + result = moduleExports[ast.InternalSymbolNameDefault] + if result != nil { + localSymbol := GetLocalSymbolForExportDefault(result) + if localSymbol != nil && result.Flags&meaning != 0 && localSymbol.Name == name { + break loop + } + result = nil + } + // Because of module/namespace merging, a module's exports are in scope, + // yet we never want to treat an export specifier as putting a member in scope. + // Therefore, if the name we find is purely an export specifier, it is not actually considered in scope. + // Two things to note about this: + // 1. We have to check this without calling getSymbol. The problem with calling getSymbol + // on an export specifier is that it might find the export specifier itself, and try to + // resolve it as an alias. This will cause the checker to consider the export specifier + // a circular alias reference when it might not be. + // 2. We check === SymbolFlags.Alias in order to check that the symbol is *purely* + // an alias. If we used &, we'd be throwing out symbols that have non alias aspects, + // which is not the desired behavior. + moduleExport := moduleExports[name] + if moduleExport != nil && moduleExport.Flags == ast.SymbolFlagsAlias && (ast.GetDeclarationOfKind(moduleExport, ast.KindExportSpecifier) != nil || ast.GetDeclarationOfKind(moduleExport, ast.KindNamespaceExport) != nil) { + break + } + } + if name != ast.InternalSymbolNameDefault { + if result = r.lookup(moduleExports, name, meaning&ast.SymbolFlagsModuleMember); result != nil { + if ast.IsSourceFile(location) && location.AsSourceFile().CommonJSModuleIndicator != nil && result.Flags&ast.SymbolFlagsType == 0 { + result = nil + } else { + break loop + } + } + } + case ast.KindEnumDeclaration: + enumSymbol := r.getSymbolOfDeclaration(location) + if enumSymbol == nil { + break + } + result = r.lookup(enumSymbol.Exports, name, meaning&ast.SymbolFlagsEnumMember) + if result != nil { + if nameNotFoundMessage != nil && r.CompilerOptions.GetIsolatedModules() && location.Flags&ast.NodeFlagsAmbient == 0 && ast.GetSourceFileOfNode(location) != ast.GetSourceFileOfNode(result.ValueDeclaration) { + isolatedModulesLikeFlagName := core.IfElse(r.CompilerOptions.VerbatimModuleSyntax == core.TSTrue, "verbatimModuleSyntax", "isolatedModules") + r.error(originalLocation, diagnostics.Cannot_access_0_from_another_file_without_qualification_when_1_is_enabled_Use_2_instead, + name, isolatedModulesLikeFlagName, enumSymbol.Name+"."+name) + } + break loop + } + case ast.KindPropertyDeclaration: + if !ast.IsStatic(location) { + ctor := ast.FindConstructorDeclaration(location.Parent) + if ctor != nil && ctor.Locals() != nil { + if r.lookup(ctor.Locals(), name, meaning&ast.SymbolFlagsValue) != nil { + // Remember the property node, it will be used later to report appropriate error + propertyWithInvalidInitializer = location + } + } + } + case ast.KindClassDeclaration, ast.KindClassExpression, ast.KindInterfaceDeclaration: + result = r.lookup(r.getSymbolOfDeclaration(location).Members, name, meaning&ast.SymbolFlagsType) + if result != nil { + if !isTypeParameterSymbolDeclaredInContainer(result, location) { + // ignore type parameters not declared in this container + result = nil + break + } + if lastLocation != nil && ast.IsStatic(lastLocation) { + // TypeScript 1.0 spec (April 2014): 3.4.1 + // The scope of a type parameter extends over the entire declaration with which the type + // parameter list is associated, with the exception of static member declarations in classes. + if nameNotFoundMessage != nil { + r.error(originalLocation, diagnostics.Static_members_cannot_reference_class_type_parameters) + } + return nil + } + break loop + } + if ast.IsClassExpression(location) && meaning&ast.SymbolFlagsClass != 0 { + className := location.Name() + if className != nil && name == className.Text() { + result = location.Symbol() + break loop + } + } + case ast.KindExpressionWithTypeArguments: + if lastLocation == location.Expression() && ast.IsHeritageClause(location.Parent) && location.Parent.AsHeritageClause().Token == ast.KindExtendsKeyword { + container := location.Parent.Parent + if ast.IsClassLike(container) { + result = r.lookup(r.getSymbolOfDeclaration(container).Members, name, meaning&ast.SymbolFlagsType) + if result != nil { + if nameNotFoundMessage != nil { + r.error(originalLocation, diagnostics.Base_class_expressions_cannot_reference_class_type_parameters) + } + return nil + } + } + } + // It is not legal to reference a class's own type parameters from a computed property name that + // belongs to the class. For example: + // + // function foo() { return '' } + // class C { // <-- Class's own type parameter T + // [foo()]() { } // <-- Reference to T from class's own computed property + // } + case ast.KindComputedPropertyName: + grandparent = location.Parent.Parent + if ast.IsClassLike(grandparent) || ast.IsInterfaceDeclaration(grandparent) { + // A reference to this grandparent's type parameters would be an error + result = r.lookup(r.getSymbolOfDeclaration(grandparent).Members, name, meaning&ast.SymbolFlagsType) + if result != nil { + if nameNotFoundMessage != nil { + r.error(originalLocation, diagnostics.A_computed_property_name_cannot_reference_a_type_parameter_from_its_containing_type) + } + return nil + } + } + case ast.KindMethodDeclaration, ast.KindConstructor, ast.KindGetAccessor, ast.KindSetAccessor, ast.KindFunctionDeclaration: + if meaning&ast.SymbolFlagsVariable != 0 && name == "arguments" { + result = r.argumentsSymbol() + break loop + } + case ast.KindFunctionExpression: + if meaning&ast.SymbolFlagsVariable != 0 && name == "arguments" { + result = r.argumentsSymbol() + break loop + } + if meaning&ast.SymbolFlagsFunction != 0 { + functionName := location.AsFunctionExpression().Name() + if functionName != nil && name == functionName.Text() { + result = location.Symbol() + break loop + } + } + case ast.KindDecorator: + // Decorators are resolved at the class declaration. Resolving at the parameter + // or member would result in looking up locals in the method. + // + // function y() {} + // class C { + // method(@y x, y) {} // <-- decorator y should be resolved at the class declaration, not the parameter. + // } + // + if location.Parent != nil && location.Parent.Kind == ast.KindParameter { + location = location.Parent + } + // function y() {} + // class C { + // @y method(x, y) {} // <-- decorator y should be resolved at the class declaration, not the method. + // } + // + // class Decorators are resolved outside of the class to avoid referencing type parameters of that class. + // + // type T = number; + // declare function y(x: T): any; + // @param(1 as T) // <-- T should resolve to the type alias outside of class C + // class C {} + if location.Parent != nil && (ast.IsClassElement(location.Parent) || location.Parent.Kind == ast.KindClassDeclaration) { + location = location.Parent + } + case ast.KindParameter: + parameterDeclaration := location.AsParameterDeclaration() + if lastLocation != nil && (lastLocation == parameterDeclaration.Initializer || + lastLocation == parameterDeclaration.Name() && ast.IsBindingPattern(lastLocation)) { + if associatedDeclarationForContainingInitializerOrBindingName == nil { + associatedDeclarationForContainingInitializerOrBindingName = location + } + } + case ast.KindBindingElement: + bindingElement := location.AsBindingElement() + if lastLocation != nil && (lastLocation == bindingElement.Initializer || + lastLocation == bindingElement.Name() && ast.IsBindingPattern(lastLocation)) { + if ast.IsPartOfParameterDeclaration(location) && associatedDeclarationForContainingInitializerOrBindingName == nil { + associatedDeclarationForContainingInitializerOrBindingName = location + } + } + case ast.KindInferType: + if meaning&ast.SymbolFlagsTypeParameter != 0 { + parameterName := location.AsInferTypeNode().TypeParameter.AsTypeParameterDeclaration().Name() + if parameterName != nil && name == parameterName.Text() { + result = location.AsInferTypeNode().TypeParameter.Symbol() + break loop + } + } + case ast.KindExportSpecifier: + exportSpecifier := location.AsExportSpecifier() + if lastLocation != nil && lastLocation == exportSpecifier.PropertyName && location.Parent.Parent.ModuleSpecifier() != nil { + location = location.Parent.Parent.Parent + } + } + if isSelfReferenceLocation(location, lastLocation) { + lastSelfReferenceLocation = location + } + lastLocation = location + // !!! In Strada, JSDocTemplateTag/JSDocParameterTag/JSDocReturnTag locations skip to + // getEffectiveContainerForJSDocTemplateTag/getHostSignatureFromJSDoc instead of location.parent. + // This is a no-op currently because JSDoc nodes have no locals and getEffectiveJSDocHost is not + // fully ported for JS assignment patterns. + location = location.Parent + } + // We just climbed up parents looking for the name, meaning that we started in a descendant node of `lastLocation`. + // If `result === lastSelfReferenceLocation.symbol`, that means that we are somewhere inside `lastSelfReferenceLocation` looking up a name, and resolving to `lastLocation` itself. + // That means that this is a self-reference of `lastLocation`, and shouldn't count this when considering whether `lastLocation` is used. + if isUse && result != nil && (lastSelfReferenceLocation == nil || result != lastSelfReferenceLocation.Symbol()) { + if r.SymbolReferenced != nil { + r.SymbolReferenced(result, meaning) + } + } + if result == nil && !excludeGlobals { + result = r.lookup(r.Globals, name, meaning|ast.SymbolFlagsGlobalLookup) + } + if result == nil { + if originalLocation != nil && ast.IsInJSFile(originalLocation) && originalLocation.Parent != nil { + if ast.IsRequireCall(originalLocation.Parent, false /*requireStringLiteralLikeArgument*/) { + return r.RequireSymbol + } + } + } + if nameNotFoundMessage != nil { + if propertyWithInvalidInitializer != nil && r.OnPropertyWithInvalidInitializer != nil && r.OnPropertyWithInvalidInitializer(originalLocation, name, propertyWithInvalidInitializer, result) { + return nil + } + if result == nil { + if r.OnFailedToResolveSymbol != nil { + r.OnFailedToResolveSymbol(originalLocation, name, meaning, nameNotFoundMessage) + } + } else { + if r.OnSuccessfullyResolvedSymbol != nil { + r.OnSuccessfullyResolvedSymbol(originalLocation, result, meaning, lastLocation, associatedDeclarationForContainingInitializerOrBindingName, withinDeferredContext) + } + } + } + return result +} + +func (r *NameResolver) useOuterVariableScopeInParameter(result *ast.Symbol, location *ast.Node, lastLocation *ast.Node) bool { + if ast.IsParameterDeclaration(lastLocation) { + body := location.Body() + if body != nil && result.ValueDeclaration != nil && result.ValueDeclaration.Pos() >= body.Pos() && result.ValueDeclaration.End() <= body.End() { + // check for several cases where we introduce temporaries that require moving the name/initializer of the parameter to the body + // - static field in a class expression + // - optional chaining pre-es2020 + // - nullish coalesce pre-es2020 + // - spread assignment in binding pattern pre-es2017 + functionLocation := location + declarationRequiresScopeChange := core.TSUnknown + if r.GetRequiresScopeChangeCache != nil { + declarationRequiresScopeChange = r.GetRequiresScopeChangeCache(functionLocation) + } + if declarationRequiresScopeChange == core.TSUnknown { + declarationRequiresScopeChange = core.IfElse(core.Some(functionLocation.Parameters(), r.requiresScopeChange), core.TSTrue, core.TSFalse) + if r.SetRequiresScopeChangeCache != nil { + r.SetRequiresScopeChangeCache(functionLocation, declarationRequiresScopeChange) + } + } + return declarationRequiresScopeChange != core.TSTrue + } + } + return false +} + +func (r *NameResolver) requiresScopeChange(node *ast.Node) bool { + d := node.AsParameterDeclaration() + return r.requiresScopeChangeWorker(d.Name()) || d.Initializer != nil && r.requiresScopeChangeWorker(d.Initializer) +} + +func (r *NameResolver) requiresScopeChangeWorker(node *ast.Node) bool { + switch node.Kind { + case ast.KindArrowFunction, ast.KindFunctionExpression, ast.KindFunctionDeclaration, ast.KindConstructor: + return false + case ast.KindMethodDeclaration, ast.KindGetAccessor, ast.KindSetAccessor, ast.KindPropertyAssignment: + return r.requiresScopeChangeWorker(node.Name()) + case ast.KindPropertyDeclaration: + if ast.HasStaticModifier(node) { + return !r.CompilerOptions.GetEmitStandardClassFields() + } + return r.requiresScopeChangeWorker(node.AsPropertyDeclaration().Name()) + default: + if ast.IsNullishCoalesce(node) || ast.IsOptionalChain(node) { + return r.CompilerOptions.GetEmitScriptTarget() < core.ScriptTargetES2020 + } + if ast.IsBindingElement(node) && node.AsBindingElement().DotDotDotToken != nil && ast.IsObjectBindingPattern(node.Parent) { + return r.CompilerOptions.GetEmitScriptTarget() < core.ScriptTargetES2017 + } + if ast.IsTypeNode(node) { + return false + } + return node.ForEachChild(r.requiresScopeChangeWorker) + } +} + +func (r *NameResolver) error(location *ast.Node, message *diagnostics.Message, args ...any) { + if r.Error != nil { + r.Error(location, message, args...) + } + // Default implementation does not report errors +} + +func (r *NameResolver) getSymbolOfDeclaration(node *ast.Node) *ast.Symbol { + if r.GetSymbolOfDeclaration != nil { + return r.GetSymbolOfDeclaration(node) + } + + // Default implementation does not support merged symbols + return node.Symbol() +} + +func (r *NameResolver) lookup(symbols ast.SymbolTable, name string, meaning ast.SymbolFlags) *ast.Symbol { + if r.Lookup != nil { + return r.Lookup(symbols, name, meaning) + } + // Default implementation does not support following aliases or merged symbols + if meaning != 0 { + symbol := symbols[name] + if symbol != nil { + if symbol.Flags&meaning != 0 { + return symbol + } + } + } + return nil +} + +func (r *NameResolver) argumentsSymbol() *ast.Symbol { + if r.ArgumentsSymbol == nil { + // Default implementation synthesizes a transient symbol for `arguments` + r.ArgumentsSymbol = &ast.Symbol{Name: "arguments", Flags: ast.SymbolFlagsProperty | ast.SymbolFlagsTransient} + } + return r.ArgumentsSymbol +} + +func GetLocalSymbolForExportDefault(symbol *ast.Symbol) *ast.Symbol { + if !isExportDefaultSymbol(symbol) || len(symbol.Declarations) == 0 { + return nil + } + for _, decl := range symbol.Declarations { + localSymbol := decl.LocalSymbol() + if localSymbol != nil { + return localSymbol + } + } + return nil +} + +func isExportDefaultSymbol(symbol *ast.Symbol) bool { + return symbol != nil && len(symbol.Declarations) > 0 && ast.HasSyntacticModifier(symbol.Declarations[0], ast.ModifierFlagsDefault) +} + +func getIsDeferredContext(location *ast.Node, lastLocation *ast.Node) bool { + if location.Kind != ast.KindArrowFunction && location.Kind != ast.KindFunctionExpression { + // initializers in instance property declaration of class like entities are executed in constructor and thus deferred + // A name is evaluated within the enclosing scope - so it shouldn't count as deferred + return ast.IsTypeQueryNode(location) || + (ast.IsFunctionLikeDeclaration(location) || location.Kind == ast.KindPropertyDeclaration && !ast.IsStatic(location)) && + (lastLocation == nil || lastLocation != location.Name()) + } + if lastLocation != nil && lastLocation == location.Name() { + return false + } + // generator functions and async functions are not inlined in control flow when immediately invoked + if location.BodyData().AsteriskToken != nil || ast.HasSyntacticModifier(location, ast.ModifierFlagsAsync) { + return true + } + return ast.GetImmediatelyInvokedFunctionExpression(location) == nil +} + +func isTypeParameterSymbolDeclaredInContainer(symbol *ast.Symbol, container *ast.Node) bool { + for _, decl := range symbol.Declarations { + if decl.Kind == ast.KindTypeParameter { + parent := decl.Parent + if parent == container { + return true + } + } + } + return false +} + +func isSelfReferenceLocation(node *ast.Node, lastLocation *ast.Node) bool { + switch node.Kind { + case ast.KindParameter: + return lastLocation != nil && lastLocation == node.Name() + case ast.KindFunctionDeclaration, ast.KindClassDeclaration, ast.KindInterfaceDeclaration, ast.KindEnumDeclaration, + ast.KindTypeAliasDeclaration, ast.KindJSTypeAliasDeclaration, ast.KindModuleDeclaration: // For `namespace N { N; }` + return true + } + return false +} diff --git a/tools/tsgo/internal/binder/referenceresolver.go b/tools/tsgo/internal/binder/referenceresolver.go new file mode 100644 index 00000000..d915378e --- /dev/null +++ b/tools/tsgo/internal/binder/referenceresolver.go @@ -0,0 +1,262 @@ +package binder + +import ( + "github.com/microsoft/typescript-go/internal/ast" + "github.com/microsoft/typescript-go/internal/core" + "github.com/microsoft/typescript-go/internal/diagnostics" +) + +type ReferenceResolver interface { + GetReferencedExportContainer(node *ast.IdentifierNode, prefixLocals bool) *ast.Node + GetReferencedImportDeclaration(node *ast.IdentifierNode) *ast.Declaration + GetReferencedValueDeclaration(node *ast.IdentifierNode) *ast.Declaration + GetReferencedValueDeclarations(node *ast.IdentifierNode) []*ast.Declaration + GetElementAccessExpressionName(expression *ast.ElementAccessExpression) string + GetReferencedMemberValueDeclaration(node *ast.Node) *ast.Declaration +} + +type ReferenceResolverHooks struct { + ResolveName func(location *ast.Node, name string, meaning ast.SymbolFlags, nameNotFoundMessage *diagnostics.Message, isUse bool, excludeGlobals bool) *ast.Symbol + GetResolvedSymbol func(*ast.Node) *ast.Symbol + GetMergedSymbol func(*ast.Symbol) *ast.Symbol + GetParentOfSymbol func(*ast.Symbol) *ast.Symbol + GetSymbolOfDeclaration func(*ast.Declaration) *ast.Symbol + GetTypeOnlyAliasDeclaration func(symbol *ast.Symbol, include ast.SymbolFlags) *ast.Declaration + GetExportSymbolOfValueSymbolIfExported func(*ast.Symbol) *ast.Symbol + GetElementAccessExpressionName func(*ast.ElementAccessExpression) (string, bool) +} + +var _ ReferenceResolver = &referenceResolver{} + +type referenceResolver struct { + resolver *NameResolver + options *core.CompilerOptions + hooks ReferenceResolverHooks +} + +func NewReferenceResolver(options *core.CompilerOptions, hooks ReferenceResolverHooks) ReferenceResolver { + return &referenceResolver{ + options: options, + hooks: hooks, + } +} + +func (r *referenceResolver) getResolvedSymbol(node *ast.Node) *ast.Symbol { + if node != nil { + if r.hooks.GetResolvedSymbol != nil { + return r.hooks.GetResolvedSymbol(node) + } + } + return nil +} + +func (r *referenceResolver) getMergedSymbol(symbol *ast.Symbol) *ast.Symbol { + if symbol != nil { + if r.hooks.GetMergedSymbol != nil { + return r.hooks.GetMergedSymbol(symbol) + } + return symbol + } + return nil +} + +func (r *referenceResolver) getParentOfSymbol(symbol *ast.Symbol) *ast.Symbol { + if symbol != nil { + if r.hooks.GetParentOfSymbol != nil { + return r.hooks.GetParentOfSymbol(symbol) + } + return symbol.Parent + } + return nil +} + +func (r *referenceResolver) getSymbolOfDeclaration(declaration *ast.Declaration) *ast.Symbol { + if declaration != nil { + if r.hooks.GetSymbolOfDeclaration != nil { + return r.hooks.GetSymbolOfDeclaration(declaration) + } + return declaration.Symbol() + } + return nil +} + +func (r *referenceResolver) getReferencedValueSymbol(reference *ast.IdentifierNode, startInDeclarationContainer bool) *ast.Symbol { + resolvedSymbol := r.getResolvedSymbol(reference) + if resolvedSymbol != nil { + return resolvedSymbol + } + + location := reference + if startInDeclarationContainer && reference.Parent != nil && ast.IsDeclaration(reference.Parent) && reference.Parent.Name() == reference { + location = ast.GetDeclarationContainer(reference.Parent) + } + + if r.hooks.ResolveName != nil { + return r.hooks.ResolveName(location, reference.Text(), ast.SymbolFlagsExportValue|ast.SymbolFlagsValue|ast.SymbolFlagsAlias, nil /*nameNotFoundMessage*/, false /*isUse*/, false /*excludeGlobals*/) + } + + if r.resolver == nil { + r.resolver = &NameResolver{ + CompilerOptions: r.options, + } + } + + return r.resolver.Resolve(location, reference.Text(), ast.SymbolFlagsExportValue|ast.SymbolFlagsValue|ast.SymbolFlagsAlias, nil /*nameNotFoundMessage*/, false /*isUse*/, false /*excludeGlobals*/) +} + +func (r *referenceResolver) isTypeOnlyAliasDeclaration(symbol *ast.Symbol) bool { + if symbol != nil { + if r.hooks.GetTypeOnlyAliasDeclaration != nil { + return r.hooks.GetTypeOnlyAliasDeclaration(symbol, ast.SymbolFlagsValue) != nil + } + + node := r.getDeclarationOfAliasSymbol(symbol) + for node != nil { + switch node.Kind { + case ast.KindImportEqualsDeclaration, ast.KindExportDeclaration: + return node.IsTypeOnly() + case ast.KindImportClause, ast.KindImportSpecifier, ast.KindExportSpecifier: + if node.IsTypeOnly() { + return true + } + node = node.Parent + continue + case ast.KindNamedImports, ast.KindNamedExports: + node = node.Parent + continue + } + break + } + } + return false +} + +func (r *referenceResolver) getDeclarationOfAliasSymbol(symbol *ast.Symbol) *ast.Declaration { + return core.FindLast(symbol.Declarations, ast.IsAliasSymbolDeclaration) +} + +func (r *referenceResolver) getExportSymbolOfValueSymbolIfExported(symbol *ast.Symbol) *ast.Symbol { + if symbol != nil { + if r.hooks.GetExportSymbolOfValueSymbolIfExported != nil { + return r.hooks.GetExportSymbolOfValueSymbolIfExported(symbol) + } + if symbol.Flags&ast.SymbolFlagsExportValue != 0 && symbol.ExportSymbol != nil { + symbol = symbol.ExportSymbol + } + return r.getMergedSymbol(symbol) + } + return nil +} + +func (r *referenceResolver) GetReferencedExportContainer(node *ast.IdentifierNode, prefixLocals bool) *ast.Node /*SourceFile|ModuleDeclaration|EnumDeclaration*/ { + // When resolving the export for the name of a module or enum + // declaration, we need to start resolution at the declaration's container. + // Otherwise, we could incorrectly resolve the export as the + // declaration if it contains an exported member with the same name. + startInDeclarationContainer := node.Parent != nil && (node.Parent.Kind == ast.KindModuleDeclaration || node.Parent.Kind == ast.KindEnumDeclaration) && node == node.Parent.Name() + if symbol := r.getReferencedValueSymbol(node, startInDeclarationContainer); symbol != nil { + if symbol.Flags&ast.SymbolFlagsExportValue != 0 { + // If we reference an exported entity within the same module declaration, then whether + // we prefix depends on the kind of entity. SymbolFlags.ExportHasLocal encompasses all the + // kinds that we do NOT prefix. + exportSymbol := r.getMergedSymbol(symbol.ExportSymbol) + if !prefixLocals && exportSymbol.Flags&ast.SymbolFlagsExportHasLocal != 0 && exportSymbol.Flags&ast.SymbolFlagsVariable == 0 { + return nil + } + symbol = exportSymbol + } + parentSymbol := r.getParentOfSymbol(symbol) + if parentSymbol != nil { + if parentSymbol.Flags&ast.SymbolFlagsValueModule != 0 && parentSymbol.ValueDeclaration != nil && parentSymbol.ValueDeclaration.Kind == ast.KindSourceFile { + symbolFile := parentSymbol.ValueDeclaration.AsSourceFile() + referenceFile := ast.GetSourceFileOfNode(node) + // If `node` accesses an export and that export isn't in the same file, then symbol is a namespace export, so return nil. + symbolIsUmdExport := symbolFile != referenceFile + if symbolIsUmdExport { + return nil + } + return symbolFile.AsNode() + } + isMatchingContainer := func(n *ast.Node) bool { + return (n.Kind == ast.KindModuleDeclaration || n.Kind == ast.KindEnumDeclaration) && r.getSymbolOfDeclaration(n) == parentSymbol + } + return ast.FindAncestor(node.Parent, isMatchingContainer) + } + } + + return nil +} + +func (r *referenceResolver) GetReferencedImportDeclaration(node *ast.IdentifierNode) *ast.Declaration { + if symbol := r.getReferencedValueSymbol(node, false /*startInDeclarationContainer*/); symbol != nil { + // We should only get the declaration of an alias if there isn't a local value + // declaration for the symbol + if ast.IsNonLocalAlias(symbol, ast.SymbolFlagsValue /*excludes*/) && !r.isTypeOnlyAliasDeclaration(symbol) { + return r.getDeclarationOfAliasSymbol(symbol) + } + } + + return nil +} + +func (r *referenceResolver) GetReferencedValueDeclaration(node *ast.IdentifierNode) *ast.Declaration { + if symbol := r.getReferencedValueSymbol(node, false /*startInDeclarationContainer*/); symbol != nil { + return r.getExportSymbolOfValueSymbolIfExported(symbol).ValueDeclaration + } + return nil +} + +func (r *referenceResolver) GetReferencedValueDeclarations(node *ast.IdentifierNode) []*ast.Declaration { + var declarations []*ast.Declaration + if symbol := r.getReferencedValueSymbol(node, false /*startInDeclarationContainer*/); symbol != nil { + symbol = r.getExportSymbolOfValueSymbolIfExported(symbol) + for _, declaration := range symbol.Declarations { + switch declaration.Kind { + case ast.KindVariableDeclaration, + ast.KindParameter, + ast.KindBindingElement, + ast.KindPropertyDeclaration, + ast.KindPropertyAssignment, + ast.KindShorthandPropertyAssignment, + ast.KindEnumMember, + ast.KindObjectLiteralExpression, + ast.KindFunctionDeclaration, + ast.KindFunctionExpression, + ast.KindArrowFunction, + ast.KindClassDeclaration, + ast.KindClassExpression, + ast.KindEnumDeclaration, + ast.KindMethodDeclaration, + ast.KindGetAccessor, + ast.KindSetAccessor, + ast.KindModuleDeclaration: + declarations = append(declarations, declaration) + } + } + } + return declarations +} + +func (r *referenceResolver) GetElementAccessExpressionName(expression *ast.ElementAccessExpression) string { + if expression != nil { + if r.hooks.GetElementAccessExpressionName != nil { + if name, ok := r.hooks.GetElementAccessExpressionName(expression); ok { + return name + } + } + } + return "" +} + +func (r *referenceResolver) GetReferencedMemberValueDeclaration(node *ast.Node) *ast.Declaration { + // member references are `this.something` or `this[something]`, so should always simply have a resolved symbol + s := r.getResolvedSymbol(node) + if s == nil && node.Symbol() != nil { + // might be a declaration instead of a ref, get the merged declaration symbol + s = r.getMergedSymbol(node.Symbol()) + } + if s == nil { + return nil + } + return r.getExportSymbolOfValueSymbolIfExported(s).ValueDeclaration +} diff --git a/tools/tsgo/internal/bundled/bundled.go b/tools/tsgo/internal/bundled/bundled.go new file mode 100644 index 00000000..8bc17079 --- /dev/null +++ b/tools/tsgo/internal/bundled/bundled.go @@ -0,0 +1,53 @@ +// Package bundled provides access to files bundled with TypeScript. +package bundled + +import ( + "path/filepath" + "runtime" + "sync" + "testing" + + "github.com/microsoft/typescript-go/internal/tspath" + "github.com/microsoft/typescript-go/internal/vfs" +) + +//go:generate go run generate.go + +// Define the below here to consolidate documentation. + +// Embedded is true if the bundled files are implemented through an embedded FS. +const Embedded = embedded + +// WrapFS returns an FS which redirects embedded paths to the embedded file system. +// If the embedded file system is not available, it returns the original FS. +func WrapFS(fs vfs.FS) vfs.FS { + return wrapFS(fs) +} + +// LibPath returns the path to the directory containing the bundled lib.d.ts files. +// If embedding is not enabled, this is a path on disk, and must be accessed through +// a real OS filesystem. +func LibPath() string { + return libPath() +} + +var bundledSourceDir = sync.OnceValue(func() string { + _, filename, _, ok := runtime.Caller(0) + if !ok { + panic("bundled: could not get current filename") + } + return filepath.Dir(filepath.FromSlash(filename)) +}) + +var testingLibPath = sync.OnceValue(func() string { + if !testing.Testing() { + panic("bundled: TestingLibPath should only be called during tests") + } + return tspath.NormalizeSlashes(filepath.Join(bundledSourceDir(), "libs")) +}) + +// TestingLibPath returns the path to the source bundled libs directory. +// It's only valid to use in tests where the source code is available. +func TestingLibPath() string { + return testingLibPath() +} diff --git a/tools/tsgo/internal/bundled/bundled_test.go b/tools/tsgo/internal/bundled/bundled_test.go new file mode 100644 index 00000000..3abaa410 --- /dev/null +++ b/tools/tsgo/internal/bundled/bundled_test.go @@ -0,0 +1,48 @@ +package bundled_test + +import ( + "os" + "path/filepath" + "testing" + + "github.com/microsoft/typescript-go/internal/bundled" + "github.com/microsoft/typescript-go/internal/tspath" + "github.com/microsoft/typescript-go/internal/vfs" + "github.com/microsoft/typescript-go/internal/vfs/osvfs" + "gotest.tools/v3/assert" +) + +func TestTestingLibPath(t *testing.T) { + t.Parallel() + + p := bundled.TestingLibPath() + + _, err := os.Stat(p) + assert.NilError(t, err) + + libdts := filepath.Join(p, "lib.d.ts") + + _, err = os.Stat(libdts) + assert.NilError(t, err) +} + +func TestEmbeddedLibs(t *testing.T) { + t.Parallel() + + fs := bundled.WrapFS(osvfs.FS()) + + var files []string + + err := fs.WalkDir(bundled.LibPath(), func(path string, d vfs.DirEntry, err error) error { + if err != nil { + return err + } + if !d.IsDir() { + files = append(files, tspath.GetBaseFileName(path)) + } + return nil + }) + assert.NilError(t, err) + + assert.DeepEqual(t, files, bundled.LibNames) +} diff --git a/tools/tsgo/internal/bundled/embed.go b/tools/tsgo/internal/bundled/embed.go new file mode 100644 index 00000000..18e5e103 --- /dev/null +++ b/tools/tsgo/internal/bundled/embed.go @@ -0,0 +1,224 @@ +//go:build !noembed + +package bundled + +import ( + "io/fs" + "strings" + "time" + + "github.com/microsoft/typescript-go/internal/vfs" +) + +const embedded = true + +const scheme = "bundled:///" + +func splitPath(path string) (rest string, ok bool) { + return strings.CutPrefix(path, scheme) +} + +func libPath() string { + return scheme + "libs" +} + +func IsBundled(path string) bool { + _, ok := splitPath(path) + return ok +} + +// wrappedFS is implemented directly rather than going through [io/fs.FS]. +// Our vfs.FS works with file contents in terms of strings, and that's +// what go:embed does under the hood, but going through fs.FS will cause +// copying to []byte and back. + +type wrappedFS struct { + fs vfs.FS +} + +var _ vfs.FS = (*wrappedFS)(nil) + +func wrapFS(fs vfs.FS) vfs.FS { + return &wrappedFS{fs: fs} +} + +func (vfs *wrappedFS) UseCaseSensitiveFileNames() bool { + return vfs.fs.UseCaseSensitiveFileNames() +} + +func (vfs *wrappedFS) FileExists(path string) bool { + if rest, ok := splitPath(path); ok { + _, ok := embeddedContents[rest] + return ok + } + return vfs.fs.FileExists(path) +} + +func (vfs *wrappedFS) ReadFile(path string) (contents string, ok bool) { + if rest, ok := splitPath(path); ok { + contents, ok = embeddedContents[rest] + return contents, ok + } + return vfs.fs.ReadFile(path) +} + +func (vfs *wrappedFS) DirectoryExists(path string) bool { + if rest, ok := splitPath(path); ok { + return rest == "libs" + } + return vfs.fs.DirectoryExists(path) +} + +func (vfs *wrappedFS) GetAccessibleEntries(path string) (result vfs.Entries) { + if rest, ok := splitPath(path); ok { + if rest == "" { + result.Directories = []string{"libs"} + } else if rest == "libs" { + result.Files = LibNames + } + return result + } + return vfs.fs.GetAccessibleEntries(path) +} + +var rootEntries = []fs.DirEntry{ + fs.FileInfoToDirEntry(&fileInfo{name: "libs", mode: fs.ModeDir}), +} + +func (vfs *wrappedFS) Stat(path string) vfs.FileInfo { + if rest, ok := splitPath(path); ok { + if rest == "" || rest == "libs" { + return &fileInfo{name: rest, mode: fs.ModeDir} + } + if lib, ok := embeddedContents[rest]; ok { + libName, _ := strings.CutPrefix(rest, "libs/") + return &fileInfo{name: libName, size: int64(len(lib))} + } + return nil + } + return vfs.fs.Stat(path) +} + +func (vfs *wrappedFS) WalkDir(root string, walkFn vfs.WalkDirFunc) error { + if rest, ok := splitPath(root); ok { + if err := vfs.walkDir(rest, walkFn); err != nil { + if err == fs.SkipAll { //nolint:errorlint + return nil + } + return err + } + return nil + } + return vfs.fs.WalkDir(root, walkFn) +} + +func (vfs *wrappedFS) walkDir(rest string, walkFn vfs.WalkDirFunc) error { + var entries []fs.DirEntry + switch rest { + case "": + entries = rootEntries + case "libs": + entries = libsEntries + default: + return nil + } + + for _, entry := range entries { + name := rest + "/" + entry.Name() + + if err := walkFn(scheme+name, entry, nil); err != nil { + if err == fs.SkipAll { //nolint:errorlint + return fs.SkipAll + } + if err == fs.SkipDir { //nolint:errorlint + continue + } + return err + } + if entry.IsDir() { + if err := vfs.walkDir(strings.TrimPrefix(name, "/"), walkFn); err != nil { + return err + } + } + } + + return nil +} + +func (vfs *wrappedFS) Realpath(path string) string { + if _, ok := splitPath(path); ok { + return path + } + return vfs.fs.Realpath(path) +} + +func (vfs *wrappedFS) WriteFile(path string, data string) error { + if _, ok := splitPath(path); ok { + panic("cannot write to embedded file system") + } + return vfs.fs.WriteFile(path, data) +} + +func (vfs *wrappedFS) AppendFile(path string, data string) error { + if _, ok := splitPath(path); ok { + panic("cannot write to embedded file system") + } + return vfs.fs.AppendFile(path, data) +} + +func (vfs *wrappedFS) Remove(path string) error { + if _, ok := splitPath(path); ok { + panic("cannot remove from embedded file system") + } + return vfs.fs.Remove(path) +} + +func (vfs *wrappedFS) Chtimes(path string, aTime time.Time, mTime time.Time) error { + if _, ok := splitPath(path); ok { + panic("cannot change times on embedded file system") + } + return vfs.fs.Chtimes(path, aTime, mTime) +} + +type fileInfo struct { + mode fs.FileMode + name string + size int64 +} + +var ( + _ fs.FileInfo = (*fileInfo)(nil) + _ fs.DirEntry = (*fileInfo)(nil) +) + +func (fi *fileInfo) IsDir() bool { + return fi.mode.IsDir() +} + +func (fi *fileInfo) ModTime() time.Time { + return time.Time{} +} + +func (fi *fileInfo) Mode() fs.FileMode { + return fi.mode +} + +func (fi *fileInfo) Name() string { + return fi.name +} + +func (fi *fileInfo) Size() int64 { + return fi.size +} + +func (fi *fileInfo) Sys() any { + return nil +} + +func (fi *fileInfo) Info() (fs.FileInfo, error) { + return fi, nil +} + +func (fi *fileInfo) Type() fs.FileMode { + return fi.mode.Type() +} diff --git a/tools/tsgo/internal/bundled/embed_generated.go b/tools/tsgo/internal/bundled/embed_generated.go new file mode 100644 index 00000000..53166f01 --- /dev/null +++ b/tools/tsgo/internal/bundled/embed_generated.go @@ -0,0 +1,452 @@ +//go:build !noembed + +// Code generated by generate.go; DO NOT EDIT. + +package bundled + +import ( + "io/fs" + + _ "embed" +) + +var ( + //go:embed libs/lib.d.ts + libs_lib_d_ts string + //go:embed libs/lib.decorators.d.ts + libs_lib_decorators_d_ts string + //go:embed libs/lib.decorators.legacy.d.ts + libs_lib_decorators_legacy_d_ts string + //go:embed libs/lib.dom.asynciterable.d.ts + libs_lib_dom_asynciterable_d_ts string + //go:embed libs/lib.dom.d.ts + libs_lib_dom_d_ts string + //go:embed libs/lib.dom.iterable.d.ts + libs_lib_dom_iterable_d_ts string + //go:embed libs/lib.es2015.collection.d.ts + libs_lib_es2015_collection_d_ts string + //go:embed libs/lib.es2015.core.d.ts + libs_lib_es2015_core_d_ts string + //go:embed libs/lib.es2015.d.ts + libs_lib_es2015_d_ts string + //go:embed libs/lib.es2015.generator.d.ts + libs_lib_es2015_generator_d_ts string + //go:embed libs/lib.es2015.iterable.d.ts + libs_lib_es2015_iterable_d_ts string + //go:embed libs/lib.es2015.promise.d.ts + libs_lib_es2015_promise_d_ts string + //go:embed libs/lib.es2015.proxy.d.ts + libs_lib_es2015_proxy_d_ts string + //go:embed libs/lib.es2015.reflect.d.ts + libs_lib_es2015_reflect_d_ts string + //go:embed libs/lib.es2015.symbol.d.ts + libs_lib_es2015_symbol_d_ts string + //go:embed libs/lib.es2015.symbol.wellknown.d.ts + libs_lib_es2015_symbol_wellknown_d_ts string + //go:embed libs/lib.es2016.array.include.d.ts + libs_lib_es2016_array_include_d_ts string + //go:embed libs/lib.es2016.d.ts + libs_lib_es2016_d_ts string + //go:embed libs/lib.es2016.full.d.ts + libs_lib_es2016_full_d_ts string + //go:embed libs/lib.es2016.intl.d.ts + libs_lib_es2016_intl_d_ts string + //go:embed libs/lib.es2017.arraybuffer.d.ts + libs_lib_es2017_arraybuffer_d_ts string + //go:embed libs/lib.es2017.d.ts + libs_lib_es2017_d_ts string + //go:embed libs/lib.es2017.date.d.ts + libs_lib_es2017_date_d_ts string + //go:embed libs/lib.es2017.full.d.ts + libs_lib_es2017_full_d_ts string + //go:embed libs/lib.es2017.intl.d.ts + libs_lib_es2017_intl_d_ts string + //go:embed libs/lib.es2017.object.d.ts + libs_lib_es2017_object_d_ts string + //go:embed libs/lib.es2017.sharedmemory.d.ts + libs_lib_es2017_sharedmemory_d_ts string + //go:embed libs/lib.es2017.string.d.ts + libs_lib_es2017_string_d_ts string + //go:embed libs/lib.es2017.typedarrays.d.ts + libs_lib_es2017_typedarrays_d_ts string + //go:embed libs/lib.es2018.asyncgenerator.d.ts + libs_lib_es2018_asyncgenerator_d_ts string + //go:embed libs/lib.es2018.asynciterable.d.ts + libs_lib_es2018_asynciterable_d_ts string + //go:embed libs/lib.es2018.d.ts + libs_lib_es2018_d_ts string + //go:embed libs/lib.es2018.full.d.ts + libs_lib_es2018_full_d_ts string + //go:embed libs/lib.es2018.intl.d.ts + libs_lib_es2018_intl_d_ts string + //go:embed libs/lib.es2018.promise.d.ts + libs_lib_es2018_promise_d_ts string + //go:embed libs/lib.es2018.regexp.d.ts + libs_lib_es2018_regexp_d_ts string + //go:embed libs/lib.es2019.array.d.ts + libs_lib_es2019_array_d_ts string + //go:embed libs/lib.es2019.d.ts + libs_lib_es2019_d_ts string + //go:embed libs/lib.es2019.full.d.ts + libs_lib_es2019_full_d_ts string + //go:embed libs/lib.es2019.intl.d.ts + libs_lib_es2019_intl_d_ts string + //go:embed libs/lib.es2019.object.d.ts + libs_lib_es2019_object_d_ts string + //go:embed libs/lib.es2019.string.d.ts + libs_lib_es2019_string_d_ts string + //go:embed libs/lib.es2019.symbol.d.ts + libs_lib_es2019_symbol_d_ts string + //go:embed libs/lib.es2020.bigint.d.ts + libs_lib_es2020_bigint_d_ts string + //go:embed libs/lib.es2020.d.ts + libs_lib_es2020_d_ts string + //go:embed libs/lib.es2020.date.d.ts + libs_lib_es2020_date_d_ts string + //go:embed libs/lib.es2020.full.d.ts + libs_lib_es2020_full_d_ts string + //go:embed libs/lib.es2020.intl.d.ts + libs_lib_es2020_intl_d_ts string + //go:embed libs/lib.es2020.number.d.ts + libs_lib_es2020_number_d_ts string + //go:embed libs/lib.es2020.promise.d.ts + libs_lib_es2020_promise_d_ts string + //go:embed libs/lib.es2020.sharedmemory.d.ts + libs_lib_es2020_sharedmemory_d_ts string + //go:embed libs/lib.es2020.string.d.ts + libs_lib_es2020_string_d_ts string + //go:embed libs/lib.es2020.symbol.wellknown.d.ts + libs_lib_es2020_symbol_wellknown_d_ts string + //go:embed libs/lib.es2021.d.ts + libs_lib_es2021_d_ts string + //go:embed libs/lib.es2021.full.d.ts + libs_lib_es2021_full_d_ts string + //go:embed libs/lib.es2021.intl.d.ts + libs_lib_es2021_intl_d_ts string + //go:embed libs/lib.es2021.promise.d.ts + libs_lib_es2021_promise_d_ts string + //go:embed libs/lib.es2021.string.d.ts + libs_lib_es2021_string_d_ts string + //go:embed libs/lib.es2021.weakref.d.ts + libs_lib_es2021_weakref_d_ts string + //go:embed libs/lib.es2022.array.d.ts + libs_lib_es2022_array_d_ts string + //go:embed libs/lib.es2022.d.ts + libs_lib_es2022_d_ts string + //go:embed libs/lib.es2022.error.d.ts + libs_lib_es2022_error_d_ts string + //go:embed libs/lib.es2022.full.d.ts + libs_lib_es2022_full_d_ts string + //go:embed libs/lib.es2022.intl.d.ts + libs_lib_es2022_intl_d_ts string + //go:embed libs/lib.es2022.object.d.ts + libs_lib_es2022_object_d_ts string + //go:embed libs/lib.es2022.regexp.d.ts + libs_lib_es2022_regexp_d_ts string + //go:embed libs/lib.es2022.string.d.ts + libs_lib_es2022_string_d_ts string + //go:embed libs/lib.es2023.array.d.ts + libs_lib_es2023_array_d_ts string + //go:embed libs/lib.es2023.collection.d.ts + libs_lib_es2023_collection_d_ts string + //go:embed libs/lib.es2023.d.ts + libs_lib_es2023_d_ts string + //go:embed libs/lib.es2023.full.d.ts + libs_lib_es2023_full_d_ts string + //go:embed libs/lib.es2023.intl.d.ts + libs_lib_es2023_intl_d_ts string + //go:embed libs/lib.es2024.arraybuffer.d.ts + libs_lib_es2024_arraybuffer_d_ts string + //go:embed libs/lib.es2024.collection.d.ts + libs_lib_es2024_collection_d_ts string + //go:embed libs/lib.es2024.d.ts + libs_lib_es2024_d_ts string + //go:embed libs/lib.es2024.full.d.ts + libs_lib_es2024_full_d_ts string + //go:embed libs/lib.es2024.object.d.ts + libs_lib_es2024_object_d_ts string + //go:embed libs/lib.es2024.promise.d.ts + libs_lib_es2024_promise_d_ts string + //go:embed libs/lib.es2024.regexp.d.ts + libs_lib_es2024_regexp_d_ts string + //go:embed libs/lib.es2024.sharedmemory.d.ts + libs_lib_es2024_sharedmemory_d_ts string + //go:embed libs/lib.es2024.string.d.ts + libs_lib_es2024_string_d_ts string + //go:embed libs/lib.es2025.collection.d.ts + libs_lib_es2025_collection_d_ts string + //go:embed libs/lib.es2025.d.ts + libs_lib_es2025_d_ts string + //go:embed libs/lib.es2025.float16.d.ts + libs_lib_es2025_float16_d_ts string + //go:embed libs/lib.es2025.full.d.ts + libs_lib_es2025_full_d_ts string + //go:embed libs/lib.es2025.intl.d.ts + libs_lib_es2025_intl_d_ts string + //go:embed libs/lib.es2025.iterator.d.ts + libs_lib_es2025_iterator_d_ts string + //go:embed libs/lib.es2025.promise.d.ts + libs_lib_es2025_promise_d_ts string + //go:embed libs/lib.es2025.regexp.d.ts + libs_lib_es2025_regexp_d_ts string + //go:embed libs/lib.es5.d.ts + libs_lib_es5_d_ts string + //go:embed libs/lib.es6.d.ts + libs_lib_es6_d_ts string + //go:embed libs/lib.esnext.array.d.ts + libs_lib_esnext_array_d_ts string + //go:embed libs/lib.esnext.collection.d.ts + libs_lib_esnext_collection_d_ts string + //go:embed libs/lib.esnext.d.ts + libs_lib_esnext_d_ts string + //go:embed libs/lib.esnext.date.d.ts + libs_lib_esnext_date_d_ts string + //go:embed libs/lib.esnext.decorators.d.ts + libs_lib_esnext_decorators_d_ts string + //go:embed libs/lib.esnext.disposable.d.ts + libs_lib_esnext_disposable_d_ts string + //go:embed libs/lib.esnext.error.d.ts + libs_lib_esnext_error_d_ts string + //go:embed libs/lib.esnext.full.d.ts + libs_lib_esnext_full_d_ts string + //go:embed libs/lib.esnext.intl.d.ts + libs_lib_esnext_intl_d_ts string + //go:embed libs/lib.esnext.sharedmemory.d.ts + libs_lib_esnext_sharedmemory_d_ts string + //go:embed libs/lib.esnext.temporal.d.ts + libs_lib_esnext_temporal_d_ts string + //go:embed libs/lib.esnext.typedarrays.d.ts + libs_lib_esnext_typedarrays_d_ts string + //go:embed libs/lib.scripthost.d.ts + libs_lib_scripthost_d_ts string + //go:embed libs/lib.webworker.asynciterable.d.ts + libs_lib_webworker_asynciterable_d_ts string + //go:embed libs/lib.webworker.d.ts + libs_lib_webworker_d_ts string + //go:embed libs/lib.webworker.importscripts.d.ts + libs_lib_webworker_importscripts_d_ts string + //go:embed libs/lib.webworker.iterable.d.ts + libs_lib_webworker_iterable_d_ts string +) + +var embeddedContents = map[string]string{ + "libs/lib.d.ts": libs_lib_d_ts, + "libs/lib.decorators.d.ts": libs_lib_decorators_d_ts, + "libs/lib.decorators.legacy.d.ts": libs_lib_decorators_legacy_d_ts, + "libs/lib.dom.asynciterable.d.ts": libs_lib_dom_asynciterable_d_ts, + "libs/lib.dom.d.ts": libs_lib_dom_d_ts, + "libs/lib.dom.iterable.d.ts": libs_lib_dom_iterable_d_ts, + "libs/lib.es2015.collection.d.ts": libs_lib_es2015_collection_d_ts, + "libs/lib.es2015.core.d.ts": libs_lib_es2015_core_d_ts, + "libs/lib.es2015.d.ts": libs_lib_es2015_d_ts, + "libs/lib.es2015.generator.d.ts": libs_lib_es2015_generator_d_ts, + "libs/lib.es2015.iterable.d.ts": libs_lib_es2015_iterable_d_ts, + "libs/lib.es2015.promise.d.ts": libs_lib_es2015_promise_d_ts, + "libs/lib.es2015.proxy.d.ts": libs_lib_es2015_proxy_d_ts, + "libs/lib.es2015.reflect.d.ts": libs_lib_es2015_reflect_d_ts, + "libs/lib.es2015.symbol.d.ts": libs_lib_es2015_symbol_d_ts, + "libs/lib.es2015.symbol.wellknown.d.ts": libs_lib_es2015_symbol_wellknown_d_ts, + "libs/lib.es2016.array.include.d.ts": libs_lib_es2016_array_include_d_ts, + "libs/lib.es2016.d.ts": libs_lib_es2016_d_ts, + "libs/lib.es2016.full.d.ts": libs_lib_es2016_full_d_ts, + "libs/lib.es2016.intl.d.ts": libs_lib_es2016_intl_d_ts, + "libs/lib.es2017.arraybuffer.d.ts": libs_lib_es2017_arraybuffer_d_ts, + "libs/lib.es2017.d.ts": libs_lib_es2017_d_ts, + "libs/lib.es2017.date.d.ts": libs_lib_es2017_date_d_ts, + "libs/lib.es2017.full.d.ts": libs_lib_es2017_full_d_ts, + "libs/lib.es2017.intl.d.ts": libs_lib_es2017_intl_d_ts, + "libs/lib.es2017.object.d.ts": libs_lib_es2017_object_d_ts, + "libs/lib.es2017.sharedmemory.d.ts": libs_lib_es2017_sharedmemory_d_ts, + "libs/lib.es2017.string.d.ts": libs_lib_es2017_string_d_ts, + "libs/lib.es2017.typedarrays.d.ts": libs_lib_es2017_typedarrays_d_ts, + "libs/lib.es2018.asyncgenerator.d.ts": libs_lib_es2018_asyncgenerator_d_ts, + "libs/lib.es2018.asynciterable.d.ts": libs_lib_es2018_asynciterable_d_ts, + "libs/lib.es2018.d.ts": libs_lib_es2018_d_ts, + "libs/lib.es2018.full.d.ts": libs_lib_es2018_full_d_ts, + "libs/lib.es2018.intl.d.ts": libs_lib_es2018_intl_d_ts, + "libs/lib.es2018.promise.d.ts": libs_lib_es2018_promise_d_ts, + "libs/lib.es2018.regexp.d.ts": libs_lib_es2018_regexp_d_ts, + "libs/lib.es2019.array.d.ts": libs_lib_es2019_array_d_ts, + "libs/lib.es2019.d.ts": libs_lib_es2019_d_ts, + "libs/lib.es2019.full.d.ts": libs_lib_es2019_full_d_ts, + "libs/lib.es2019.intl.d.ts": libs_lib_es2019_intl_d_ts, + "libs/lib.es2019.object.d.ts": libs_lib_es2019_object_d_ts, + "libs/lib.es2019.string.d.ts": libs_lib_es2019_string_d_ts, + "libs/lib.es2019.symbol.d.ts": libs_lib_es2019_symbol_d_ts, + "libs/lib.es2020.bigint.d.ts": libs_lib_es2020_bigint_d_ts, + "libs/lib.es2020.d.ts": libs_lib_es2020_d_ts, + "libs/lib.es2020.date.d.ts": libs_lib_es2020_date_d_ts, + "libs/lib.es2020.full.d.ts": libs_lib_es2020_full_d_ts, + "libs/lib.es2020.intl.d.ts": libs_lib_es2020_intl_d_ts, + "libs/lib.es2020.number.d.ts": libs_lib_es2020_number_d_ts, + "libs/lib.es2020.promise.d.ts": libs_lib_es2020_promise_d_ts, + "libs/lib.es2020.sharedmemory.d.ts": libs_lib_es2020_sharedmemory_d_ts, + "libs/lib.es2020.string.d.ts": libs_lib_es2020_string_d_ts, + "libs/lib.es2020.symbol.wellknown.d.ts": libs_lib_es2020_symbol_wellknown_d_ts, + "libs/lib.es2021.d.ts": libs_lib_es2021_d_ts, + "libs/lib.es2021.full.d.ts": libs_lib_es2021_full_d_ts, + "libs/lib.es2021.intl.d.ts": libs_lib_es2021_intl_d_ts, + "libs/lib.es2021.promise.d.ts": libs_lib_es2021_promise_d_ts, + "libs/lib.es2021.string.d.ts": libs_lib_es2021_string_d_ts, + "libs/lib.es2021.weakref.d.ts": libs_lib_es2021_weakref_d_ts, + "libs/lib.es2022.array.d.ts": libs_lib_es2022_array_d_ts, + "libs/lib.es2022.d.ts": libs_lib_es2022_d_ts, + "libs/lib.es2022.error.d.ts": libs_lib_es2022_error_d_ts, + "libs/lib.es2022.full.d.ts": libs_lib_es2022_full_d_ts, + "libs/lib.es2022.intl.d.ts": libs_lib_es2022_intl_d_ts, + "libs/lib.es2022.object.d.ts": libs_lib_es2022_object_d_ts, + "libs/lib.es2022.regexp.d.ts": libs_lib_es2022_regexp_d_ts, + "libs/lib.es2022.string.d.ts": libs_lib_es2022_string_d_ts, + "libs/lib.es2023.array.d.ts": libs_lib_es2023_array_d_ts, + "libs/lib.es2023.collection.d.ts": libs_lib_es2023_collection_d_ts, + "libs/lib.es2023.d.ts": libs_lib_es2023_d_ts, + "libs/lib.es2023.full.d.ts": libs_lib_es2023_full_d_ts, + "libs/lib.es2023.intl.d.ts": libs_lib_es2023_intl_d_ts, + "libs/lib.es2024.arraybuffer.d.ts": libs_lib_es2024_arraybuffer_d_ts, + "libs/lib.es2024.collection.d.ts": libs_lib_es2024_collection_d_ts, + "libs/lib.es2024.d.ts": libs_lib_es2024_d_ts, + "libs/lib.es2024.full.d.ts": libs_lib_es2024_full_d_ts, + "libs/lib.es2024.object.d.ts": libs_lib_es2024_object_d_ts, + "libs/lib.es2024.promise.d.ts": libs_lib_es2024_promise_d_ts, + "libs/lib.es2024.regexp.d.ts": libs_lib_es2024_regexp_d_ts, + "libs/lib.es2024.sharedmemory.d.ts": libs_lib_es2024_sharedmemory_d_ts, + "libs/lib.es2024.string.d.ts": libs_lib_es2024_string_d_ts, + "libs/lib.es2025.collection.d.ts": libs_lib_es2025_collection_d_ts, + "libs/lib.es2025.d.ts": libs_lib_es2025_d_ts, + "libs/lib.es2025.float16.d.ts": libs_lib_es2025_float16_d_ts, + "libs/lib.es2025.full.d.ts": libs_lib_es2025_full_d_ts, + "libs/lib.es2025.intl.d.ts": libs_lib_es2025_intl_d_ts, + "libs/lib.es2025.iterator.d.ts": libs_lib_es2025_iterator_d_ts, + "libs/lib.es2025.promise.d.ts": libs_lib_es2025_promise_d_ts, + "libs/lib.es2025.regexp.d.ts": libs_lib_es2025_regexp_d_ts, + "libs/lib.es5.d.ts": libs_lib_es5_d_ts, + "libs/lib.es6.d.ts": libs_lib_es6_d_ts, + "libs/lib.esnext.array.d.ts": libs_lib_esnext_array_d_ts, + "libs/lib.esnext.collection.d.ts": libs_lib_esnext_collection_d_ts, + "libs/lib.esnext.d.ts": libs_lib_esnext_d_ts, + "libs/lib.esnext.date.d.ts": libs_lib_esnext_date_d_ts, + "libs/lib.esnext.decorators.d.ts": libs_lib_esnext_decorators_d_ts, + "libs/lib.esnext.disposable.d.ts": libs_lib_esnext_disposable_d_ts, + "libs/lib.esnext.error.d.ts": libs_lib_esnext_error_d_ts, + "libs/lib.esnext.full.d.ts": libs_lib_esnext_full_d_ts, + "libs/lib.esnext.intl.d.ts": libs_lib_esnext_intl_d_ts, + "libs/lib.esnext.sharedmemory.d.ts": libs_lib_esnext_sharedmemory_d_ts, + "libs/lib.esnext.temporal.d.ts": libs_lib_esnext_temporal_d_ts, + "libs/lib.esnext.typedarrays.d.ts": libs_lib_esnext_typedarrays_d_ts, + "libs/lib.scripthost.d.ts": libs_lib_scripthost_d_ts, + "libs/lib.webworker.asynciterable.d.ts": libs_lib_webworker_asynciterable_d_ts, + "libs/lib.webworker.d.ts": libs_lib_webworker_d_ts, + "libs/lib.webworker.importscripts.d.ts": libs_lib_webworker_importscripts_d_ts, + "libs/lib.webworker.iterable.d.ts": libs_lib_webworker_iterable_d_ts, +} + +var libsEntries = []fs.DirEntry{ + &fileInfo{name: "lib.d.ts", size: int64(len(libs_lib_d_ts))}, + &fileInfo{name: "lib.decorators.d.ts", size: int64(len(libs_lib_decorators_d_ts))}, + &fileInfo{name: "lib.decorators.legacy.d.ts", size: int64(len(libs_lib_decorators_legacy_d_ts))}, + &fileInfo{name: "lib.dom.asynciterable.d.ts", size: int64(len(libs_lib_dom_asynciterable_d_ts))}, + &fileInfo{name: "lib.dom.d.ts", size: int64(len(libs_lib_dom_d_ts))}, + &fileInfo{name: "lib.dom.iterable.d.ts", size: int64(len(libs_lib_dom_iterable_d_ts))}, + &fileInfo{name: "lib.es2015.collection.d.ts", size: int64(len(libs_lib_es2015_collection_d_ts))}, + &fileInfo{name: "lib.es2015.core.d.ts", size: int64(len(libs_lib_es2015_core_d_ts))}, + &fileInfo{name: "lib.es2015.d.ts", size: int64(len(libs_lib_es2015_d_ts))}, + &fileInfo{name: "lib.es2015.generator.d.ts", size: int64(len(libs_lib_es2015_generator_d_ts))}, + &fileInfo{name: "lib.es2015.iterable.d.ts", size: int64(len(libs_lib_es2015_iterable_d_ts))}, + &fileInfo{name: "lib.es2015.promise.d.ts", size: int64(len(libs_lib_es2015_promise_d_ts))}, + &fileInfo{name: "lib.es2015.proxy.d.ts", size: int64(len(libs_lib_es2015_proxy_d_ts))}, + &fileInfo{name: "lib.es2015.reflect.d.ts", size: int64(len(libs_lib_es2015_reflect_d_ts))}, + &fileInfo{name: "lib.es2015.symbol.d.ts", size: int64(len(libs_lib_es2015_symbol_d_ts))}, + &fileInfo{name: "lib.es2015.symbol.wellknown.d.ts", size: int64(len(libs_lib_es2015_symbol_wellknown_d_ts))}, + &fileInfo{name: "lib.es2016.array.include.d.ts", size: int64(len(libs_lib_es2016_array_include_d_ts))}, + &fileInfo{name: "lib.es2016.d.ts", size: int64(len(libs_lib_es2016_d_ts))}, + &fileInfo{name: "lib.es2016.full.d.ts", size: int64(len(libs_lib_es2016_full_d_ts))}, + &fileInfo{name: "lib.es2016.intl.d.ts", size: int64(len(libs_lib_es2016_intl_d_ts))}, + &fileInfo{name: "lib.es2017.arraybuffer.d.ts", size: int64(len(libs_lib_es2017_arraybuffer_d_ts))}, + &fileInfo{name: "lib.es2017.d.ts", size: int64(len(libs_lib_es2017_d_ts))}, + &fileInfo{name: "lib.es2017.date.d.ts", size: int64(len(libs_lib_es2017_date_d_ts))}, + &fileInfo{name: "lib.es2017.full.d.ts", size: int64(len(libs_lib_es2017_full_d_ts))}, + &fileInfo{name: "lib.es2017.intl.d.ts", size: int64(len(libs_lib_es2017_intl_d_ts))}, + &fileInfo{name: "lib.es2017.object.d.ts", size: int64(len(libs_lib_es2017_object_d_ts))}, + &fileInfo{name: "lib.es2017.sharedmemory.d.ts", size: int64(len(libs_lib_es2017_sharedmemory_d_ts))}, + &fileInfo{name: "lib.es2017.string.d.ts", size: int64(len(libs_lib_es2017_string_d_ts))}, + &fileInfo{name: "lib.es2017.typedarrays.d.ts", size: int64(len(libs_lib_es2017_typedarrays_d_ts))}, + &fileInfo{name: "lib.es2018.asyncgenerator.d.ts", size: int64(len(libs_lib_es2018_asyncgenerator_d_ts))}, + &fileInfo{name: "lib.es2018.asynciterable.d.ts", size: int64(len(libs_lib_es2018_asynciterable_d_ts))}, + &fileInfo{name: "lib.es2018.d.ts", size: int64(len(libs_lib_es2018_d_ts))}, + &fileInfo{name: "lib.es2018.full.d.ts", size: int64(len(libs_lib_es2018_full_d_ts))}, + &fileInfo{name: "lib.es2018.intl.d.ts", size: int64(len(libs_lib_es2018_intl_d_ts))}, + &fileInfo{name: "lib.es2018.promise.d.ts", size: int64(len(libs_lib_es2018_promise_d_ts))}, + &fileInfo{name: "lib.es2018.regexp.d.ts", size: int64(len(libs_lib_es2018_regexp_d_ts))}, + &fileInfo{name: "lib.es2019.array.d.ts", size: int64(len(libs_lib_es2019_array_d_ts))}, + &fileInfo{name: "lib.es2019.d.ts", size: int64(len(libs_lib_es2019_d_ts))}, + &fileInfo{name: "lib.es2019.full.d.ts", size: int64(len(libs_lib_es2019_full_d_ts))}, + &fileInfo{name: "lib.es2019.intl.d.ts", size: int64(len(libs_lib_es2019_intl_d_ts))}, + &fileInfo{name: "lib.es2019.object.d.ts", size: int64(len(libs_lib_es2019_object_d_ts))}, + &fileInfo{name: "lib.es2019.string.d.ts", size: int64(len(libs_lib_es2019_string_d_ts))}, + &fileInfo{name: "lib.es2019.symbol.d.ts", size: int64(len(libs_lib_es2019_symbol_d_ts))}, + &fileInfo{name: "lib.es2020.bigint.d.ts", size: int64(len(libs_lib_es2020_bigint_d_ts))}, + &fileInfo{name: "lib.es2020.d.ts", size: int64(len(libs_lib_es2020_d_ts))}, + &fileInfo{name: "lib.es2020.date.d.ts", size: int64(len(libs_lib_es2020_date_d_ts))}, + &fileInfo{name: "lib.es2020.full.d.ts", size: int64(len(libs_lib_es2020_full_d_ts))}, + &fileInfo{name: "lib.es2020.intl.d.ts", size: int64(len(libs_lib_es2020_intl_d_ts))}, + &fileInfo{name: "lib.es2020.number.d.ts", size: int64(len(libs_lib_es2020_number_d_ts))}, + &fileInfo{name: "lib.es2020.promise.d.ts", size: int64(len(libs_lib_es2020_promise_d_ts))}, + &fileInfo{name: "lib.es2020.sharedmemory.d.ts", size: int64(len(libs_lib_es2020_sharedmemory_d_ts))}, + &fileInfo{name: "lib.es2020.string.d.ts", size: int64(len(libs_lib_es2020_string_d_ts))}, + &fileInfo{name: "lib.es2020.symbol.wellknown.d.ts", size: int64(len(libs_lib_es2020_symbol_wellknown_d_ts))}, + &fileInfo{name: "lib.es2021.d.ts", size: int64(len(libs_lib_es2021_d_ts))}, + &fileInfo{name: "lib.es2021.full.d.ts", size: int64(len(libs_lib_es2021_full_d_ts))}, + &fileInfo{name: "lib.es2021.intl.d.ts", size: int64(len(libs_lib_es2021_intl_d_ts))}, + &fileInfo{name: "lib.es2021.promise.d.ts", size: int64(len(libs_lib_es2021_promise_d_ts))}, + &fileInfo{name: "lib.es2021.string.d.ts", size: int64(len(libs_lib_es2021_string_d_ts))}, + &fileInfo{name: "lib.es2021.weakref.d.ts", size: int64(len(libs_lib_es2021_weakref_d_ts))}, + &fileInfo{name: "lib.es2022.array.d.ts", size: int64(len(libs_lib_es2022_array_d_ts))}, + &fileInfo{name: "lib.es2022.d.ts", size: int64(len(libs_lib_es2022_d_ts))}, + &fileInfo{name: "lib.es2022.error.d.ts", size: int64(len(libs_lib_es2022_error_d_ts))}, + &fileInfo{name: "lib.es2022.full.d.ts", size: int64(len(libs_lib_es2022_full_d_ts))}, + &fileInfo{name: "lib.es2022.intl.d.ts", size: int64(len(libs_lib_es2022_intl_d_ts))}, + &fileInfo{name: "lib.es2022.object.d.ts", size: int64(len(libs_lib_es2022_object_d_ts))}, + &fileInfo{name: "lib.es2022.regexp.d.ts", size: int64(len(libs_lib_es2022_regexp_d_ts))}, + &fileInfo{name: "lib.es2022.string.d.ts", size: int64(len(libs_lib_es2022_string_d_ts))}, + &fileInfo{name: "lib.es2023.array.d.ts", size: int64(len(libs_lib_es2023_array_d_ts))}, + &fileInfo{name: "lib.es2023.collection.d.ts", size: int64(len(libs_lib_es2023_collection_d_ts))}, + &fileInfo{name: "lib.es2023.d.ts", size: int64(len(libs_lib_es2023_d_ts))}, + &fileInfo{name: "lib.es2023.full.d.ts", size: int64(len(libs_lib_es2023_full_d_ts))}, + &fileInfo{name: "lib.es2023.intl.d.ts", size: int64(len(libs_lib_es2023_intl_d_ts))}, + &fileInfo{name: "lib.es2024.arraybuffer.d.ts", size: int64(len(libs_lib_es2024_arraybuffer_d_ts))}, + &fileInfo{name: "lib.es2024.collection.d.ts", size: int64(len(libs_lib_es2024_collection_d_ts))}, + &fileInfo{name: "lib.es2024.d.ts", size: int64(len(libs_lib_es2024_d_ts))}, + &fileInfo{name: "lib.es2024.full.d.ts", size: int64(len(libs_lib_es2024_full_d_ts))}, + &fileInfo{name: "lib.es2024.object.d.ts", size: int64(len(libs_lib_es2024_object_d_ts))}, + &fileInfo{name: "lib.es2024.promise.d.ts", size: int64(len(libs_lib_es2024_promise_d_ts))}, + &fileInfo{name: "lib.es2024.regexp.d.ts", size: int64(len(libs_lib_es2024_regexp_d_ts))}, + &fileInfo{name: "lib.es2024.sharedmemory.d.ts", size: int64(len(libs_lib_es2024_sharedmemory_d_ts))}, + &fileInfo{name: "lib.es2024.string.d.ts", size: int64(len(libs_lib_es2024_string_d_ts))}, + &fileInfo{name: "lib.es2025.collection.d.ts", size: int64(len(libs_lib_es2025_collection_d_ts))}, + &fileInfo{name: "lib.es2025.d.ts", size: int64(len(libs_lib_es2025_d_ts))}, + &fileInfo{name: "lib.es2025.float16.d.ts", size: int64(len(libs_lib_es2025_float16_d_ts))}, + &fileInfo{name: "lib.es2025.full.d.ts", size: int64(len(libs_lib_es2025_full_d_ts))}, + &fileInfo{name: "lib.es2025.intl.d.ts", size: int64(len(libs_lib_es2025_intl_d_ts))}, + &fileInfo{name: "lib.es2025.iterator.d.ts", size: int64(len(libs_lib_es2025_iterator_d_ts))}, + &fileInfo{name: "lib.es2025.promise.d.ts", size: int64(len(libs_lib_es2025_promise_d_ts))}, + &fileInfo{name: "lib.es2025.regexp.d.ts", size: int64(len(libs_lib_es2025_regexp_d_ts))}, + &fileInfo{name: "lib.es5.d.ts", size: int64(len(libs_lib_es5_d_ts))}, + &fileInfo{name: "lib.es6.d.ts", size: int64(len(libs_lib_es6_d_ts))}, + &fileInfo{name: "lib.esnext.array.d.ts", size: int64(len(libs_lib_esnext_array_d_ts))}, + &fileInfo{name: "lib.esnext.collection.d.ts", size: int64(len(libs_lib_esnext_collection_d_ts))}, + &fileInfo{name: "lib.esnext.d.ts", size: int64(len(libs_lib_esnext_d_ts))}, + &fileInfo{name: "lib.esnext.date.d.ts", size: int64(len(libs_lib_esnext_date_d_ts))}, + &fileInfo{name: "lib.esnext.decorators.d.ts", size: int64(len(libs_lib_esnext_decorators_d_ts))}, + &fileInfo{name: "lib.esnext.disposable.d.ts", size: int64(len(libs_lib_esnext_disposable_d_ts))}, + &fileInfo{name: "lib.esnext.error.d.ts", size: int64(len(libs_lib_esnext_error_d_ts))}, + &fileInfo{name: "lib.esnext.full.d.ts", size: int64(len(libs_lib_esnext_full_d_ts))}, + &fileInfo{name: "lib.esnext.intl.d.ts", size: int64(len(libs_lib_esnext_intl_d_ts))}, + &fileInfo{name: "lib.esnext.sharedmemory.d.ts", size: int64(len(libs_lib_esnext_sharedmemory_d_ts))}, + &fileInfo{name: "lib.esnext.temporal.d.ts", size: int64(len(libs_lib_esnext_temporal_d_ts))}, + &fileInfo{name: "lib.esnext.typedarrays.d.ts", size: int64(len(libs_lib_esnext_typedarrays_d_ts))}, + &fileInfo{name: "lib.scripthost.d.ts", size: int64(len(libs_lib_scripthost_d_ts))}, + &fileInfo{name: "lib.webworker.asynciterable.d.ts", size: int64(len(libs_lib_webworker_asynciterable_d_ts))}, + &fileInfo{name: "lib.webworker.d.ts", size: int64(len(libs_lib_webworker_d_ts))}, + &fileInfo{name: "lib.webworker.importscripts.d.ts", size: int64(len(libs_lib_webworker_importscripts_d_ts))}, + &fileInfo{name: "lib.webworker.iterable.d.ts", size: int64(len(libs_lib_webworker_iterable_d_ts))}, +} diff --git a/tools/tsgo/internal/bundled/generate.go b/tools/tsgo/internal/bundled/generate.go new file mode 100644 index 00000000..ed597a69 --- /dev/null +++ b/tools/tsgo/internal/bundled/generate.go @@ -0,0 +1,217 @@ +//go:build ignore + +package main + +import ( + "bytes" + "encoding/json" + "fmt" + "go/format" + "log" + "os" + "path/filepath" + "regexp" + "slices" + "strings" + + "github.com/microsoft/typescript-go/internal/repo" +) + +var ( + libInputDir = filepath.Join(repo.TypeScriptSubmodulePath(), "src", "lib") + copyrightNotice = filepath.Join(repo.TypeScriptSubmodulePath(), "scripts", "CopyrightNotice.txt") +) + +func main() { + log.SetFlags(log.LstdFlags | log.Lshortfile) + + libs := readLibs() + generateLibs(libs) + generateLibList(libs) + generateEmbedded(libs) +} + +type lib struct { + target string // target relative to libs dir + sources []string // sources relative to src/lib dir +} + +func generateLibs(libs []lib) { + const outputDir = "libs" + + copyright := readCopyright() + + if err := os.RemoveAll(outputDir); err != nil { + log.Fatalf("failed to remove libs directory: %v", err) + } + + if err := os.MkdirAll(outputDir, 0o755); err != nil { + log.Fatalf("failed to create libs directory: %v", err) + } + + for _, lib := range libs { + var output bytes.Buffer + output.Write(copyright) + + for _, source := range lib.sources { + sourcePath := filepath.Join(libInputDir, source) + b, err := os.ReadFile(sourcePath) + if err != nil { + log.Fatalf("failed to read %s: %v", sourcePath, err) + } + + output.WriteByte('\n') + output.Write(removeCRLF(b)) + } + + outputPath := filepath.Join(outputDir, lib.target) + if err := os.WriteFile(outputPath, output.Bytes(), 0o644); err != nil { + log.Fatalf("failed to write %s: %v", outputPath, err) + } + } +} + +func generateLibList(libs []lib) { + var code bytes.Buffer + code.WriteString("// Code generated by generate.go; DO NOT EDIT.\n\n") + code.WriteString("package bundled\n\n") + + code.WriteString("// LibNames is the list of all bundled lib files, sorted by name.\n") + code.WriteString("// For the list of libs sorted by load order, use [tsoptions.Libs].\n") + code.WriteString("var LibNames = []string{\n") + for _, lib := range libs { + code.WriteString("\t\"" + lib.target + "\",\n") + } + code.WriteString("}\n") + + writeCode("libs_generated.go", code.Bytes()) +} + +func generateEmbedded(libs []lib) { + libVarNames := make([]string, len(libs)) + for i, lib := range libs { + libVarNames[i] = "libs_" + strings.ReplaceAll(lib.target, ".", "_") + } + + var code bytes.Buffer + code.WriteString("//go:build !noembed\n\n") + code.WriteString("// Code generated by generate.go; DO NOT EDIT.\n\n") + code.WriteString("package bundled\n\n") + code.WriteString("import (\n") + code.WriteString("\"io/fs\"\n\n") + code.WriteString("_ \"embed\"\n") + code.WriteString(")\n\n") + + code.WriteString("var (\n") + for i, lib := range libs { + varName := libVarNames[i] + code.WriteString("//go:embed libs/" + lib.target + "\n") + code.WriteString("" + varName + " string\n") + } + code.WriteString(")\n\n") + + code.WriteString("var embeddedContents = map[string]string{\n") + for i, lib := range libs { + varName := libVarNames[i] + code.WriteString("\t\"libs/" + lib.target + "\": " + varName + ",\n") + } + code.WriteString("}\n\n") + + code.WriteString("var libsEntries = []fs.DirEntry{\n") + for i, lib := range libs { + varName := libVarNames[i] + fmt.Fprintf(&code, "\t&fileInfo{name: %q, size: int64(len(%s))},\n", lib.target, varName) + } + code.WriteString("}\n") + + writeCode("embed_generated.go", code.Bytes()) +} + +var ( + // Match escaped characters, double-quoted strings, single-line comments, and multi-line comments. + reJSONComments = regexp.MustCompile(`\\.|"(?:\\.|[^"])*"|//.*|/\*[\s\S]*?\*/`) + // Match double-quoted strings (to skip) or trailing commas before ] or }. + reTrailingComma = regexp.MustCompile(`"(?:\\.|[^"])*"|,\s*([}\]])`) +) + +// stripJSONC replaces comments and trailing commas with spaces in JSONC content, +// producing valid JSON. The input slice is mutated in place. +func stripJSONC(b []byte) { + for _, loc := range reJSONComments.FindAllIndex(b, -1) { + if b[loc[0]] == '/' { + for i := loc[0]; i < loc[1]; i++ { + if b[i] != '\n' { + b[i] = ' ' + } + } + } + } + for _, loc := range reTrailingComma.FindAllSubmatchIndex(b, -1) { + // loc[2]:loc[3] is the capture group; -1 means this matched a string, not a comma. + if loc[2] < 0 { + continue + } + // Blank the comma (at loc[0]), keep whitespace and closing bracket. + b[loc[0]] = ' ' + } +} + +func readLibs() []lib { + libsFile := filepath.Join(libInputDir, "libs.json") + + b, err := os.ReadFile(libsFile) + if err != nil { + log.Fatalf("failed to open libs.json: %v", err) + } + stripJSONC(b) + + var meta struct { + Libs []string `json:"libs"` + Paths map[string]string `json:"paths"` + } + + if err := json.Unmarshal(b, &meta); err != nil { + log.Fatalf("failed to parse libs.json: %v", err) + } + + var libs []lib + for _, libName := range meta.Libs { + sources := []string{libName + ".d.ts"} + var target string + if path, ok := meta.Paths[libName]; ok { + target = path + } else { + target = "lib." + libName + ".d.ts" + } + libs = append(libs, lib{target: target, sources: sources}) + } + + slices.SortFunc(libs, func(a lib, b lib) int { + return strings.Compare(a.target, b.target) + }) + + return libs +} + +func readCopyright() []byte { + b, err := os.ReadFile(copyrightNotice) + if err != nil { + log.Fatalf("failed to read copyright notice: %v", err) + } + return removeCRLF(b) +} + +func removeCRLF(b []byte) []byte { + return bytes.ReplaceAll(b, []byte("\r\n"), []byte("\n")) +} + +func writeCode(filename string, code []byte) { + formatted, err := format.Source(code) + if err != nil { + log.Fatalf("failed to format source: %v", err) + } + + if err := os.WriteFile(filename, formatted, 0o644); err != nil { + log.Fatalf("failed to write %s: %v", filename, err) + } +} diff --git a/tools/tsgo/internal/bundled/libs/lib.d.ts b/tools/tsgo/internal/bundled/libs/lib.d.ts new file mode 100644 index 00000000..a80f0070 --- /dev/null +++ b/tools/tsgo/internal/bundled/libs/lib.d.ts @@ -0,0 +1,20 @@ +/*! ***************************************************************************** +Copyright (c) Microsoft Corporation. All rights reserved. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the +License at http://www.apache.org/licenses/LICENSE-2.0 + +THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED +WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, +MERCHANTABILITY OR NON-INFRINGEMENT. + +See the Apache Version 2.0 License for specific language governing permissions +and limitations under the License. +***************************************************************************** */ + + +/// +/// +/// +/// diff --git a/tools/tsgo/internal/bundled/libs/lib.decorators.d.ts b/tools/tsgo/internal/bundled/libs/lib.decorators.d.ts new file mode 100644 index 00000000..83d82bef --- /dev/null +++ b/tools/tsgo/internal/bundled/libs/lib.decorators.d.ts @@ -0,0 +1,382 @@ +/*! ***************************************************************************** +Copyright (c) Microsoft Corporation. All rights reserved. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the +License at http://www.apache.org/licenses/LICENSE-2.0 + +THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED +WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, +MERCHANTABILITY OR NON-INFRINGEMENT. + +See the Apache Version 2.0 License for specific language governing permissions +and limitations under the License. +***************************************************************************** */ + + +/** + * The decorator context types provided to class element decorators. + */ +type ClassMemberDecoratorContext = + | ClassMethodDecoratorContext + | ClassGetterDecoratorContext + | ClassSetterDecoratorContext + | ClassFieldDecoratorContext + | ClassAccessorDecoratorContext; + +/** + * The decorator context types provided to any decorator. + */ +type DecoratorContext = + | ClassDecoratorContext + | ClassMemberDecoratorContext; + +type DecoratorMetadataObject = Record & object; + +type DecoratorMetadata = typeof globalThis extends { Symbol: { readonly metadata: symbol; }; } ? DecoratorMetadataObject : DecoratorMetadataObject | undefined; + +/** + * Context provided to a class decorator. + * @template Class The type of the decorated class associated with this context. + */ +interface ClassDecoratorContext< + Class extends abstract new (...args: any) => any = abstract new (...args: any) => any, +> { + /** The kind of element that was decorated. */ + readonly kind: "class"; + + /** The name of the decorated class. */ + readonly name: string | undefined; + + /** + * Adds a callback to be invoked after the class definition has been finalized. + * + * @example + * ```ts + * function customElement(name: string): ClassDecoratorFunction { + * return (target, context) => { + * context.addInitializer(function () { + * customElements.define(name, this); + * }); + * } + * } + * + * @customElement("my-element") + * class MyElement {} + * ``` + */ + addInitializer(initializer: (this: Class) => void): void; + + readonly metadata: DecoratorMetadata; +} + +/** + * Context provided to a class method decorator. + * @template This The type on which the class element will be defined. For a static class element, this will be + * the type of the constructor. For a non-static class element, this will be the type of the instance. + * @template Value The type of the decorated class method. + */ +interface ClassMethodDecoratorContext< + This = unknown, + Value extends (this: This, ...args: any) => any = (this: This, ...args: any) => any, +> { + /** The kind of class element that was decorated. */ + readonly kind: "method"; + + /** The name of the decorated class element. */ + readonly name: string | symbol; + + /** A value indicating whether the class element is a static (`true`) or instance (`false`) element. */ + readonly static: boolean; + + /** A value indicating whether the class element has a private name. */ + readonly private: boolean; + + /** An object that can be used to access the current value of the class element at runtime. */ + readonly access: { + /** + * Determines whether an object has a property with the same name as the decorated element. + */ + has(object: This): boolean; + /** + * Gets the current value of the method from the provided object. + * + * @example + * let fn = context.access.get(instance); + */ + get(object: This): Value; + }; + + /** + * Adds a callback to be invoked either after static methods are defined but before + * static initializers are run (when decorating a `static` element), or before instance + * initializers are run (when decorating a non-`static` element). + * + * @example + * ```ts + * const bound: ClassMethodDecoratorFunction = (value, context) { + * if (context.private) throw new TypeError("Not supported on private methods."); + * context.addInitializer(function () { + * this[context.name] = this[context.name].bind(this); + * }); + * } + * + * class C { + * message = "Hello"; + * + * @bound + * m() { + * console.log(this.message); + * } + * } + * ``` + */ + addInitializer(initializer: (this: This) => void): void; + + readonly metadata: DecoratorMetadata; +} + +/** + * Context provided to a class getter decorator. + * @template This The type on which the class element will be defined. For a static class element, this will be + * the type of the constructor. For a non-static class element, this will be the type of the instance. + * @template Value The property type of the decorated class getter. + */ +interface ClassGetterDecoratorContext< + This = unknown, + Value = unknown, +> { + /** The kind of class element that was decorated. */ + readonly kind: "getter"; + + /** The name of the decorated class element. */ + readonly name: string | symbol; + + /** A value indicating whether the class element is a static (`true`) or instance (`false`) element. */ + readonly static: boolean; + + /** A value indicating whether the class element has a private name. */ + readonly private: boolean; + + /** An object that can be used to access the current value of the class element at runtime. */ + readonly access: { + /** + * Determines whether an object has a property with the same name as the decorated element. + */ + has(object: This): boolean; + /** + * Invokes the getter on the provided object. + * + * @example + * let value = context.access.get(instance); + */ + get(object: This): Value; + }; + + /** + * Adds a callback to be invoked either after static methods are defined but before + * static initializers are run (when decorating a `static` element), or before instance + * initializers are run (when decorating a non-`static` element). + */ + addInitializer(initializer: (this: This) => void): void; + + readonly metadata: DecoratorMetadata; +} + +/** + * Context provided to a class setter decorator. + * @template This The type on which the class element will be defined. For a static class element, this will be + * the type of the constructor. For a non-static class element, this will be the type of the instance. + * @template Value The type of the decorated class setter. + */ +interface ClassSetterDecoratorContext< + This = unknown, + Value = unknown, +> { + /** The kind of class element that was decorated. */ + readonly kind: "setter"; + + /** The name of the decorated class element. */ + readonly name: string | symbol; + + /** A value indicating whether the class element is a static (`true`) or instance (`false`) element. */ + readonly static: boolean; + + /** A value indicating whether the class element has a private name. */ + readonly private: boolean; + + /** An object that can be used to access the current value of the class element at runtime. */ + readonly access: { + /** + * Determines whether an object has a property with the same name as the decorated element. + */ + has(object: This): boolean; + /** + * Invokes the setter on the provided object. + * + * @example + * context.access.set(instance, value); + */ + set(object: This, value: Value): void; + }; + + /** + * Adds a callback to be invoked either after static methods are defined but before + * static initializers are run (when decorating a `static` element), or before instance + * initializers are run (when decorating a non-`static` element). + */ + addInitializer(initializer: (this: This) => void): void; + + readonly metadata: DecoratorMetadata; +} + +/** + * Context provided to a class `accessor` field decorator. + * @template This The type on which the class element will be defined. For a static class element, this will be + * the type of the constructor. For a non-static class element, this will be the type of the instance. + * @template Value The type of decorated class field. + */ +interface ClassAccessorDecoratorContext< + This = unknown, + Value = unknown, +> { + /** The kind of class element that was decorated. */ + readonly kind: "accessor"; + + /** The name of the decorated class element. */ + readonly name: string | symbol; + + /** A value indicating whether the class element is a static (`true`) or instance (`false`) element. */ + readonly static: boolean; + + /** A value indicating whether the class element has a private name. */ + readonly private: boolean; + + /** An object that can be used to access the current value of the class element at runtime. */ + readonly access: { + /** + * Determines whether an object has a property with the same name as the decorated element. + */ + has(object: This): boolean; + + /** + * Invokes the getter on the provided object. + * + * @example + * let value = context.access.get(instance); + */ + get(object: This): Value; + + /** + * Invokes the setter on the provided object. + * + * @example + * context.access.set(instance, value); + */ + set(object: This, value: Value): void; + }; + + /** + * Adds a callback to be invoked immediately after the auto `accessor` being + * decorated is initialized (regardless if the `accessor` is `static` or not). + */ + addInitializer(initializer: (this: This) => void): void; + + readonly metadata: DecoratorMetadata; +} + +/** + * Describes the target provided to class `accessor` field decorators. + * @template This The `this` type to which the target applies. + * @template Value The property type for the class `accessor` field. + */ +interface ClassAccessorDecoratorTarget { + /** + * Invokes the getter that was defined prior to decorator application. + * + * @example + * let value = target.get.call(instance); + */ + get(this: This): Value; + + /** + * Invokes the setter that was defined prior to decorator application. + * + * @example + * target.set.call(instance, value); + */ + set(this: This, value: Value): void; +} + +/** + * Describes the allowed return value from a class `accessor` field decorator. + * @template This The `this` type to which the target applies. + * @template Value The property type for the class `accessor` field. + */ +interface ClassAccessorDecoratorResult { + /** + * An optional replacement getter function. If not provided, the existing getter function is used instead. + */ + get?(this: This): Value; + + /** + * An optional replacement setter function. If not provided, the existing setter function is used instead. + */ + set?(this: This, value: Value): void; + + /** + * An optional initializer mutator that is invoked when the underlying field initializer is evaluated. + * @param value The incoming initializer value. + * @returns The replacement initializer value. + */ + init?(this: This, value: Value): Value; +} + +/** + * Context provided to a class field decorator. + * @template This The type on which the class element will be defined. For a static class element, this will be + * the type of the constructor. For a non-static class element, this will be the type of the instance. + * @template Value The type of the decorated class field. + */ +interface ClassFieldDecoratorContext< + This = unknown, + Value = unknown, +> { + /** The kind of class element that was decorated. */ + readonly kind: "field"; + + /** The name of the decorated class element. */ + readonly name: string | symbol; + + /** A value indicating whether the class element is a static (`true`) or instance (`false`) element. */ + readonly static: boolean; + + /** A value indicating whether the class element has a private name. */ + readonly private: boolean; + + /** An object that can be used to access the current value of the class element at runtime. */ + readonly access: { + /** + * Determines whether an object has a property with the same name as the decorated element. + */ + has(object: This): boolean; + + /** + * Gets the value of the field on the provided object. + */ + get(object: This): Value; + + /** + * Sets the value of the field on the provided object. + */ + set(object: This, value: Value): void; + }; + + /** + * Adds a callback to be invoked immediately after the field being decorated + * is initialized (regardless if the field is `static` or not). + */ + addInitializer(initializer: (this: This) => void): void; + + readonly metadata: DecoratorMetadata; +} diff --git a/tools/tsgo/internal/bundled/libs/lib.decorators.legacy.d.ts b/tools/tsgo/internal/bundled/libs/lib.decorators.legacy.d.ts new file mode 100644 index 00000000..89775167 --- /dev/null +++ b/tools/tsgo/internal/bundled/libs/lib.decorators.legacy.d.ts @@ -0,0 +1,20 @@ +/*! ***************************************************************************** +Copyright (c) Microsoft Corporation. All rights reserved. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the +License at http://www.apache.org/licenses/LICENSE-2.0 + +THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED +WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, +MERCHANTABILITY OR NON-INFRINGEMENT. + +See the Apache Version 2.0 License for specific language governing permissions +and limitations under the License. +***************************************************************************** */ + + +declare type ClassDecorator = (target: TFunction) => TFunction | void; +declare type PropertyDecorator = (target: Object, propertyKey: string | symbol) => void; +declare type MethodDecorator = (target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor) => TypedPropertyDescriptor | void; +declare type ParameterDecorator = (target: Object, propertyKey: string | symbol | undefined, parameterIndex: number) => void; diff --git a/tools/tsgo/internal/bundled/libs/lib.dom.asynciterable.d.ts b/tools/tsgo/internal/bundled/libs/lib.dom.asynciterable.d.ts new file mode 100644 index 00000000..075563ce --- /dev/null +++ b/tools/tsgo/internal/bundled/libs/lib.dom.asynciterable.d.ts @@ -0,0 +1,18 @@ +/*! ***************************************************************************** +Copyright (c) Microsoft Corporation. All rights reserved. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the +License at http://www.apache.org/licenses/LICENSE-2.0 + +THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED +WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, +MERCHANTABILITY OR NON-INFRINGEMENT. + +See the Apache Version 2.0 License for specific language governing permissions +and limitations under the License. +***************************************************************************** */ + + +// This file's contents are now included in the main types file. +// The file has been left for backward compatibility. diff --git a/tools/tsgo/internal/bundled/libs/lib.dom.d.ts b/tools/tsgo/internal/bundled/libs/lib.dom.d.ts new file mode 100644 index 00000000..9e127c3f --- /dev/null +++ b/tools/tsgo/internal/bundled/libs/lib.dom.d.ts @@ -0,0 +1,45125 @@ +/*! ***************************************************************************** +Copyright (c) Microsoft Corporation. All rights reserved. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the +License at http://www.apache.org/licenses/LICENSE-2.0 + +THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED +WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, +MERCHANTABILITY OR NON-INFRINGEMENT. + +See the Apache Version 2.0 License for specific language governing permissions +and limitations under the License. +***************************************************************************** */ + + +/// +/// + +///////////////////////////// +/// Window APIs +///////////////////////////// + +interface AacEncoderConfig { + format?: AacBitstreamFormat; +} + +interface AddEventListenerOptions extends EventListenerOptions { + once?: boolean; + passive?: boolean; + signal?: AbortSignal; +} + +interface AddressErrors { + addressLine?: string; + city?: string; + country?: string; + dependentLocality?: string; + organization?: string; + phone?: string; + postalCode?: string; + recipient?: string; + region?: string; + sortingCode?: string; +} + +interface AesCbcParams extends Algorithm { + iv: BufferSource; +} + +interface AesCtrParams extends Algorithm { + counter: BufferSource; + length: number; +} + +interface AesDerivedKeyParams extends Algorithm { + length: number; +} + +interface AesGcmParams extends Algorithm { + additionalData?: BufferSource; + iv: BufferSource; + tagLength?: number; +} + +interface AesKeyAlgorithm extends KeyAlgorithm { + length: number; +} + +interface AesKeyGenParams extends Algorithm { + length: number; +} + +interface Algorithm { + name: string; +} + +interface AllAcceptedCredentialsOptions { + allAcceptedCredentialIds: Base64URLString[]; + rpId: string; + userId: Base64URLString; +} + +interface AnalyserOptions extends AudioNodeOptions { + fftSize?: number; + maxDecibels?: number; + minDecibels?: number; + smoothingTimeConstant?: number; +} + +interface AnimationEventInit extends EventInit { + animationName?: string; + elapsedTime?: number; + pseudoElement?: string; +} + +interface AnimationPlaybackEventInit extends EventInit { + currentTime?: CSSNumberish | null; + timelineTime?: CSSNumberish | null; +} + +interface AssignedNodesOptions { + flatten?: boolean; +} + +interface AudioBufferOptions { + length: number; + numberOfChannels?: number; + sampleRate: number; +} + +interface AudioBufferSourceOptions { + buffer?: AudioBuffer | null; + detune?: number; + loop?: boolean; + loopEnd?: number; + loopStart?: number; + playbackRate?: number; +} + +interface AudioConfiguration { + bitrate?: number; + channels?: string; + contentType: string; + samplerate?: number; + spatialRendering?: boolean; +} + +interface AudioContextOptions { + latencyHint?: AudioContextLatencyCategory | number; + sampleRate?: number; +} + +interface AudioDataCopyToOptions { + format?: AudioSampleFormat; + frameCount?: number; + frameOffset?: number; + planeIndex: number; +} + +interface AudioDataInit { + data: BufferSource; + format: AudioSampleFormat; + numberOfChannels: number; + numberOfFrames: number; + sampleRate: number; + timestamp: number; + transfer?: ArrayBuffer[]; +} + +interface AudioDecoderConfig { + codec: string; + description?: AllowSharedBufferSource; + numberOfChannels: number; + sampleRate: number; +} + +interface AudioDecoderInit { + error: WebCodecsErrorCallback; + output: AudioDataOutputCallback; +} + +interface AudioDecoderSupport { + config?: AudioDecoderConfig; + supported?: boolean; +} + +interface AudioEncoderConfig { + aac?: AacEncoderConfig; + bitrate?: number; + bitrateMode?: BitrateMode; + codec: string; + numberOfChannels: number; + opus?: OpusEncoderConfig; + sampleRate: number; +} + +interface AudioEncoderInit { + error: WebCodecsErrorCallback; + output: EncodedAudioChunkOutputCallback; +} + +interface AudioEncoderSupport { + config?: AudioEncoderConfig; + supported?: boolean; +} + +interface AudioNodeOptions { + channelCount?: number; + channelCountMode?: ChannelCountMode; + channelInterpretation?: ChannelInterpretation; +} + +interface AudioProcessingEventInit extends EventInit { + inputBuffer: AudioBuffer; + outputBuffer: AudioBuffer; + playbackTime: number; +} + +interface AudioTimestamp { + contextTime?: number; + performanceTime?: DOMHighResTimeStamp; +} + +interface AudioWorkletNodeOptions extends AudioNodeOptions { + numberOfInputs?: number; + numberOfOutputs?: number; + outputChannelCount?: number[]; + parameterData?: Record; + processorOptions?: any; +} + +interface AuthenticationExtensionsClientInputs { + appid?: string; + credProps?: boolean; + credentialProtectionPolicy?: string; + enforceCredentialProtectionPolicy?: boolean; + hmacCreateSecret?: boolean; + largeBlob?: AuthenticationExtensionsLargeBlobInputs; + minPinLength?: boolean; + prf?: AuthenticationExtensionsPRFInputs; +} + +interface AuthenticationExtensionsClientInputsJSON { + appid?: string; + credProps?: boolean; + largeBlob?: AuthenticationExtensionsLargeBlobInputsJSON; + prf?: AuthenticationExtensionsPRFInputsJSON; +} + +interface AuthenticationExtensionsClientOutputs { + appid?: boolean; + credProps?: CredentialPropertiesOutput; + hmacCreateSecret?: boolean; + largeBlob?: AuthenticationExtensionsLargeBlobOutputs; + prf?: AuthenticationExtensionsPRFOutputs; +} + +interface AuthenticationExtensionsClientOutputsJSON { + appid?: boolean; + credProps?: CredentialPropertiesOutput; + largeBlob?: AuthenticationExtensionsLargeBlobOutputsJSON; + prf?: AuthenticationExtensionsPRFOutputsJSON; +} + +interface AuthenticationExtensionsLargeBlobInputs { + read?: boolean; + support?: string; + write?: BufferSource; +} + +interface AuthenticationExtensionsLargeBlobInputsJSON { + read?: boolean; + support?: string; + write?: Base64URLString; +} + +interface AuthenticationExtensionsLargeBlobOutputs { + blob?: ArrayBuffer; + supported?: boolean; + written?: boolean; +} + +interface AuthenticationExtensionsLargeBlobOutputsJSON { + blob?: Base64URLString; + supported?: boolean; + written?: boolean; +} + +interface AuthenticationExtensionsPRFInputs { + eval?: AuthenticationExtensionsPRFValues; + evalByCredential?: Record; +} + +interface AuthenticationExtensionsPRFInputsJSON { + eval?: AuthenticationExtensionsPRFValuesJSON; + evalByCredential?: Record; +} + +interface AuthenticationExtensionsPRFOutputs { + enabled?: boolean; + results?: AuthenticationExtensionsPRFValues; +} + +interface AuthenticationExtensionsPRFOutputsJSON { + enabled?: boolean; + results?: AuthenticationExtensionsPRFValuesJSON; +} + +interface AuthenticationExtensionsPRFValues { + first: BufferSource; + second?: BufferSource; +} + +interface AuthenticationExtensionsPRFValuesJSON { + first: Base64URLString; + second?: Base64URLString; +} + +interface AuthenticationResponseJSON { + authenticatorAttachment?: string; + clientExtensionResults: AuthenticationExtensionsClientOutputsJSON; + id: string; + rawId: Base64URLString; + response: AuthenticatorAssertionResponseJSON; + type: string; +} + +interface AuthenticatorAssertionResponseJSON { + authenticatorData: Base64URLString; + clientDataJSON: Base64URLString; + signature: Base64URLString; + userHandle?: Base64URLString; +} + +interface AuthenticatorAttestationResponseJSON { + attestationObject: Base64URLString; + authenticatorData: Base64URLString; + clientDataJSON: Base64URLString; + publicKey?: Base64URLString; + publicKeyAlgorithm: COSEAlgorithmIdentifier; + transports: string[]; +} + +interface AuthenticatorSelectionCriteria { + authenticatorAttachment?: AuthenticatorAttachment; + requireResidentKey?: boolean; + residentKey?: ResidentKeyRequirement; + userVerification?: UserVerificationRequirement; +} + +interface AvcEncoderConfig { + format?: AvcBitstreamFormat; +} + +interface BiquadFilterOptions extends AudioNodeOptions { + Q?: number; + detune?: number; + frequency?: number; + gain?: number; + type?: BiquadFilterType; +} + +interface BlobEventInit extends EventInit { + data: Blob; + timecode?: DOMHighResTimeStamp; +} + +interface BlobPropertyBag { + endings?: EndingType; + type?: string; +} + +interface CSSMatrixComponentOptions { + is2D?: boolean; +} + +interface CSSNumericType { + angle?: number; + flex?: number; + frequency?: number; + length?: number; + percent?: number; + percentHint?: CSSNumericBaseType; + resolution?: number; + time?: number; +} + +interface CSSStyleSheetInit { + baseURL?: string; + disabled?: boolean; + media?: MediaList | string; +} + +interface CacheQueryOptions { + ignoreMethod?: boolean; + ignoreSearch?: boolean; + ignoreVary?: boolean; +} + +interface CanvasRenderingContext2DSettings { + alpha?: boolean; + colorSpace?: PredefinedColorSpace; + desynchronized?: boolean; + willReadFrequently?: boolean; +} + +interface CaretPositionFromPointOptions { + shadowRoots?: ShadowRoot[]; +} + +interface ChannelMergerOptions extends AudioNodeOptions { + numberOfInputs?: number; +} + +interface ChannelSplitterOptions extends AudioNodeOptions { + numberOfOutputs?: number; +} + +interface CheckVisibilityOptions { + checkOpacity?: boolean; + checkVisibilityCSS?: boolean; + contentVisibilityAuto?: boolean; + opacityProperty?: boolean; + visibilityProperty?: boolean; +} + +interface ClientQueryOptions { + includeUncontrolled?: boolean; + type?: ClientTypes; +} + +interface ClipboardEventInit extends EventInit { + clipboardData?: DataTransfer | null; +} + +interface ClipboardItemOptions { + presentationStyle?: PresentationStyle; +} + +interface CloseEventInit extends EventInit { + code?: number; + reason?: string; + wasClean?: boolean; +} + +interface CommandEventInit extends EventInit { + command?: string; + source?: Element | null; +} + +interface CompositionEventInit extends UIEventInit { + data?: string; +} + +interface ComputedEffectTiming extends EffectTiming { + activeDuration?: CSSNumberish; + currentIteration?: number | null; + endTime?: CSSNumberish; + localTime?: CSSNumberish | null; + progress?: number | null; + startTime?: CSSNumberish; +} + +interface ComputedKeyframe { + composite: CompositeOperationOrAuto; + computedOffset: number; + easing: string; + offset: number | null; + [property: string]: string | number | null | undefined; +} + +interface ConstantSourceOptions { + offset?: number; +} + +interface ConstrainBooleanOrDOMStringParameters { + exact?: boolean | string; + ideal?: boolean | string; +} + +interface ConstrainBooleanParameters { + exact?: boolean; + ideal?: boolean; +} + +interface ConstrainDOMStringParameters { + exact?: string | string[]; + ideal?: string | string[]; +} + +interface ConstrainDoubleRange extends DoubleRange { + exact?: number; + ideal?: number; +} + +interface ConstrainULongRange extends ULongRange { + exact?: number; + ideal?: number; +} + +interface ContentVisibilityAutoStateChangeEventInit extends EventInit { + skipped?: boolean; +} + +interface ConvolverOptions extends AudioNodeOptions { + buffer?: AudioBuffer | null; + disableNormalization?: boolean; +} + +interface CookieChangeEventInit extends EventInit { + changed?: CookieList; + deleted?: CookieList; +} + +interface CookieInit { + domain?: string | null; + expires?: DOMHighResTimeStamp | null; + name: string; + partitioned?: boolean; + path?: string; + sameSite?: CookieSameSite; + value: string; +} + +interface CookieListItem { + name?: string; + value?: string; +} + +interface CookieStoreDeleteOptions { + domain?: string | null; + name: string; + partitioned?: boolean; + path?: string; +} + +interface CookieStoreGetOptions { + name?: string; + url?: string; +} + +interface CredentialCreationOptions { + publicKey?: PublicKeyCredentialCreationOptions; + signal?: AbortSignal; +} + +interface CredentialPropertiesOutput { + rk?: boolean; +} + +interface CredentialRequestOptions { + mediation?: CredentialMediationRequirement; + publicKey?: PublicKeyCredentialRequestOptions; + signal?: AbortSignal; +} + +interface CryptoKeyPair { + privateKey: CryptoKey; + publicKey: CryptoKey; +} + +interface CurrentUserDetailsOptions { + displayName: string; + name: string; + rpId: string; + userId: Base64URLString; +} + +interface CustomEventInit extends EventInit { + detail?: T; +} + +interface DOMMatrix2DInit { + a?: number; + b?: number; + c?: number; + d?: number; + e?: number; + f?: number; + m11?: number; + m12?: number; + m21?: number; + m22?: number; + m41?: number; + m42?: number; +} + +interface DOMMatrixInit extends DOMMatrix2DInit { + is2D?: boolean; + m13?: number; + m14?: number; + m23?: number; + m24?: number; + m31?: number; + m32?: number; + m33?: number; + m34?: number; + m43?: number; + m44?: number; +} + +interface DOMPointInit { + w?: number; + x?: number; + y?: number; + z?: number; +} + +interface DOMQuadInit { + p1?: DOMPointInit; + p2?: DOMPointInit; + p3?: DOMPointInit; + p4?: DOMPointInit; +} + +interface DOMRectInit { + height?: number; + width?: number; + x?: number; + y?: number; +} + +interface DelayOptions extends AudioNodeOptions { + delayTime?: number; + maxDelayTime?: number; +} + +interface DeviceMotionEventAccelerationInit { + x?: number | null; + y?: number | null; + z?: number | null; +} + +interface DeviceMotionEventInit extends EventInit { + acceleration?: DeviceMotionEventAccelerationInit; + accelerationIncludingGravity?: DeviceMotionEventAccelerationInit; + interval?: number; + rotationRate?: DeviceMotionEventRotationRateInit; +} + +interface DeviceMotionEventRotationRateInit { + alpha?: number | null; + beta?: number | null; + gamma?: number | null; +} + +interface DeviceOrientationEventInit extends EventInit { + absolute?: boolean; + alpha?: number | null; + beta?: number | null; + gamma?: number | null; +} + +interface DisplayMediaStreamOptions { + audio?: boolean | MediaTrackConstraints; + video?: boolean | MediaTrackConstraints; +} + +interface DocumentTimelineOptions { + originTime?: DOMHighResTimeStamp; +} + +interface DoubleRange { + max?: number; + min?: number; +} + +interface DragEventInit extends MouseEventInit { + dataTransfer?: DataTransfer | null; +} + +interface DynamicsCompressorOptions extends AudioNodeOptions { + attack?: number; + knee?: number; + ratio?: number; + release?: number; + threshold?: number; +} + +interface EcKeyAlgorithm extends KeyAlgorithm { + namedCurve: NamedCurve; +} + +interface EcKeyGenParams extends Algorithm { + namedCurve: NamedCurve; +} + +interface EcKeyImportParams extends Algorithm { + namedCurve: NamedCurve; +} + +interface EcdhKeyDeriveParams extends Algorithm { + public: CryptoKey; +} + +interface EcdsaParams extends Algorithm { + hash: HashAlgorithmIdentifier; +} + +interface EffectTiming { + delay?: number; + direction?: PlaybackDirection; + duration?: number | CSSNumericValue | string; + easing?: string; + endDelay?: number; + fill?: FillMode; + iterationStart?: number; + iterations?: number; + playbackRate?: number; +} + +interface ElementCreationOptions { + customElementRegistry?: CustomElementRegistry | null; + is?: string; +} + +interface ElementDefinitionOptions { + extends?: string; +} + +interface EncodedAudioChunkInit { + data: AllowSharedBufferSource; + duration?: number; + timestamp: number; + transfer?: ArrayBuffer[]; + type: EncodedAudioChunkType; +} + +interface EncodedAudioChunkMetadata { + decoderConfig?: AudioDecoderConfig; +} + +interface EncodedVideoChunkInit { + data: AllowSharedBufferSource; + duration?: number; + timestamp: number; + type: EncodedVideoChunkType; +} + +interface EncodedVideoChunkMetadata { + decoderConfig?: VideoDecoderConfig; + svc?: SvcOutputMetadata; +} + +interface ErrorEventInit extends EventInit { + colno?: number; + error?: any; + filename?: string; + lineno?: number; + message?: string; +} + +interface EventInit { + bubbles?: boolean; + cancelable?: boolean; + composed?: boolean; +} + +interface EventListenerOptions { + capture?: boolean; +} + +interface EventModifierInit extends UIEventInit { + altKey?: boolean; + ctrlKey?: boolean; + metaKey?: boolean; + modifierAltGraph?: boolean; + modifierCapsLock?: boolean; + modifierFn?: boolean; + modifierFnLock?: boolean; + modifierHyper?: boolean; + modifierNumLock?: boolean; + modifierScrollLock?: boolean; + modifierSuper?: boolean; + modifierSymbol?: boolean; + modifierSymbolLock?: boolean; + shiftKey?: boolean; +} + +interface EventSourceInit { + withCredentials?: boolean; +} + +interface FilePropertyBag extends BlobPropertyBag { + lastModified?: number; +} + +interface FileSystemCreateWritableOptions { + keepExistingData?: boolean; +} + +interface FileSystemFlags { + create?: boolean; + exclusive?: boolean; +} + +interface FileSystemGetDirectoryOptions { + create?: boolean; +} + +interface FileSystemGetFileOptions { + create?: boolean; +} + +interface FileSystemRemoveOptions { + recursive?: boolean; +} + +interface FocusEventInit extends UIEventInit { + relatedTarget?: EventTarget | null; +} + +interface FocusOptions { + focusVisible?: boolean; + preventScroll?: boolean; +} + +interface FontFaceDescriptors { + ascentOverride?: string; + descentOverride?: string; + display?: FontDisplay; + featureSettings?: string; + lineGapOverride?: string; + stretch?: string; + style?: string; + unicodeRange?: string; + variationSettings?: string; + weight?: string; +} + +interface FontFaceSetLoadEventInit extends EventInit { + fontfaces?: FontFace[]; +} + +interface FormDataEventInit extends EventInit { + formData: FormData; +} + +interface FullscreenOptions { + navigationUI?: FullscreenNavigationUI; +} + +interface GPUBindGroupDescriptor extends GPUObjectDescriptorBase { + entries: GPUBindGroupEntry[]; + layout: GPUBindGroupLayout; +} + +interface GPUBindGroupEntry { + binding: GPUIndex32; + resource: GPUBindingResource; +} + +interface GPUBindGroupLayoutDescriptor extends GPUObjectDescriptorBase { + entries: GPUBindGroupLayoutEntry[]; +} + +interface GPUBindGroupLayoutEntry { + binding: GPUIndex32; + buffer?: GPUBufferBindingLayout; + externalTexture?: GPUExternalTextureBindingLayout; + sampler?: GPUSamplerBindingLayout; + storageTexture?: GPUStorageTextureBindingLayout; + texture?: GPUTextureBindingLayout; + visibility: GPUShaderStageFlags; +} + +interface GPUBlendComponent { + dstFactor?: GPUBlendFactor; + operation?: GPUBlendOperation; + srcFactor?: GPUBlendFactor; +} + +interface GPUBlendState { + alpha: GPUBlendComponent; + color: GPUBlendComponent; +} + +interface GPUBufferBinding { + buffer: GPUBuffer; + offset?: GPUSize64; + size?: GPUSize64; +} + +interface GPUBufferBindingLayout { + hasDynamicOffset?: boolean; + minBindingSize?: GPUSize64; + type?: GPUBufferBindingType; +} + +interface GPUBufferDescriptor extends GPUObjectDescriptorBase { + mappedAtCreation?: boolean; + size: GPUSize64; + usage: GPUBufferUsageFlags; +} + +interface GPUCanvasConfiguration { + alphaMode?: GPUCanvasAlphaMode; + colorSpace?: PredefinedColorSpace; + device: GPUDevice; + format: GPUTextureFormat; + toneMapping?: GPUCanvasToneMapping; + usage?: GPUTextureUsageFlags; + viewFormats?: GPUTextureFormat[]; +} + +interface GPUCanvasToneMapping { + mode?: GPUCanvasToneMappingMode; +} + +interface GPUColorDict { + a: number; + b: number; + g: number; + r: number; +} + +interface GPUColorTargetState { + blend?: GPUBlendState; + format: GPUTextureFormat; + writeMask?: GPUColorWriteFlags; +} + +interface GPUCommandBufferDescriptor extends GPUObjectDescriptorBase { +} + +interface GPUCommandEncoderDescriptor extends GPUObjectDescriptorBase { +} + +interface GPUComputePassDescriptor extends GPUObjectDescriptorBase { + timestampWrites?: GPUComputePassTimestampWrites; +} + +interface GPUComputePassTimestampWrites { + beginningOfPassWriteIndex?: GPUSize32; + endOfPassWriteIndex?: GPUSize32; + querySet: GPUQuerySet; +} + +interface GPUComputePipelineDescriptor extends GPUPipelineDescriptorBase { + compute: GPUProgrammableStage; +} + +interface GPUCopyExternalImageDestInfo extends GPUTexelCopyTextureInfo { + colorSpace?: PredefinedColorSpace; + premultipliedAlpha?: boolean; +} + +interface GPUCopyExternalImageSourceInfo { + flipY?: boolean; + origin?: GPUOrigin2D; + source: GPUCopyExternalImageSource; +} + +interface GPUDepthStencilState { + depthBias?: GPUDepthBias; + depthBiasClamp?: number; + depthBiasSlopeScale?: number; + depthCompare?: GPUCompareFunction; + depthWriteEnabled?: boolean; + format: GPUTextureFormat; + stencilBack?: GPUStencilFaceState; + stencilFront?: GPUStencilFaceState; + stencilReadMask?: GPUStencilValue; + stencilWriteMask?: GPUStencilValue; +} + +interface GPUDeviceDescriptor extends GPUObjectDescriptorBase { + defaultQueue?: GPUQueueDescriptor; + requiredFeatures?: GPUFeatureName[]; + requiredLimits?: Record; +} + +interface GPUExtent3DDict { + depthOrArrayLayers?: GPUIntegerCoordinate; + height?: GPUIntegerCoordinate; + width: GPUIntegerCoordinate; +} + +interface GPUExternalTextureBindingLayout { +} + +interface GPUExternalTextureDescriptor extends GPUObjectDescriptorBase { + colorSpace?: PredefinedColorSpace; + source: HTMLVideoElement | VideoFrame; +} + +interface GPUFragmentState extends GPUProgrammableStage { + targets: (GPUColorTargetState | null)[]; +} + +interface GPUMultisampleState { + alphaToCoverageEnabled?: boolean; + count?: GPUSize32; + mask?: GPUSampleMask; +} + +interface GPUObjectDescriptorBase { + label?: string; +} + +interface GPUOrigin2DDict { + x?: GPUIntegerCoordinate; + y?: GPUIntegerCoordinate; +} + +interface GPUOrigin3DDict { + x?: GPUIntegerCoordinate; + y?: GPUIntegerCoordinate; + z?: GPUIntegerCoordinate; +} + +interface GPUPipelineDescriptorBase extends GPUObjectDescriptorBase { + layout: GPUPipelineLayout | GPUAutoLayoutMode; +} + +interface GPUPipelineErrorInit { + reason: GPUPipelineErrorReason; +} + +interface GPUPipelineLayoutDescriptor extends GPUObjectDescriptorBase { + bindGroupLayouts: (GPUBindGroupLayout | null)[]; +} + +interface GPUPrimitiveState { + cullMode?: GPUCullMode; + frontFace?: GPUFrontFace; + stripIndexFormat?: GPUIndexFormat; + topology?: GPUPrimitiveTopology; + unclippedDepth?: boolean; +} + +interface GPUProgrammableStage { + constants?: Record; + entryPoint?: string; + module: GPUShaderModule; +} + +interface GPUQuerySetDescriptor extends GPUObjectDescriptorBase { + count: GPUSize32; + type: GPUQueryType; +} + +interface GPUQueueDescriptor extends GPUObjectDescriptorBase { +} + +interface GPURenderBundleDescriptor extends GPUObjectDescriptorBase { +} + +interface GPURenderBundleEncoderDescriptor extends GPURenderPassLayout { + depthReadOnly?: boolean; + stencilReadOnly?: boolean; +} + +interface GPURenderPassColorAttachment { + clearValue?: GPUColor; + depthSlice?: GPUIntegerCoordinate; + loadOp: GPULoadOp; + resolveTarget?: GPUTexture | GPUTextureView; + storeOp: GPUStoreOp; + view: GPUTexture | GPUTextureView; +} + +interface GPURenderPassDepthStencilAttachment { + depthClearValue?: number; + depthLoadOp?: GPULoadOp; + depthReadOnly?: boolean; + depthStoreOp?: GPUStoreOp; + stencilClearValue?: GPUStencilValue; + stencilLoadOp?: GPULoadOp; + stencilReadOnly?: boolean; + stencilStoreOp?: GPUStoreOp; + view: GPUTexture | GPUTextureView; +} + +interface GPURenderPassDescriptor extends GPUObjectDescriptorBase { + colorAttachments: (GPURenderPassColorAttachment | null)[]; + depthStencilAttachment?: GPURenderPassDepthStencilAttachment; + maxDrawCount?: GPUSize64; + occlusionQuerySet?: GPUQuerySet; + timestampWrites?: GPURenderPassTimestampWrites; +} + +interface GPURenderPassLayout extends GPUObjectDescriptorBase { + colorFormats: (GPUTextureFormat | null)[]; + depthStencilFormat?: GPUTextureFormat; + sampleCount?: GPUSize32; +} + +interface GPURenderPassTimestampWrites { + beginningOfPassWriteIndex?: GPUSize32; + endOfPassWriteIndex?: GPUSize32; + querySet: GPUQuerySet; +} + +interface GPURenderPipelineDescriptor extends GPUPipelineDescriptorBase { + depthStencil?: GPUDepthStencilState; + fragment?: GPUFragmentState; + multisample?: GPUMultisampleState; + primitive?: GPUPrimitiveState; + vertex: GPUVertexState; +} + +interface GPURequestAdapterOptions { + forceFallbackAdapter?: boolean; + powerPreference?: GPUPowerPreference; +} + +interface GPUSamplerBindingLayout { + type?: GPUSamplerBindingType; +} + +interface GPUSamplerDescriptor extends GPUObjectDescriptorBase { + addressModeU?: GPUAddressMode; + addressModeV?: GPUAddressMode; + addressModeW?: GPUAddressMode; + compare?: GPUCompareFunction; + lodMaxClamp?: number; + lodMinClamp?: number; + magFilter?: GPUFilterMode; + maxAnisotropy?: number; + minFilter?: GPUFilterMode; + mipmapFilter?: GPUMipmapFilterMode; +} + +interface GPUShaderModuleDescriptor extends GPUObjectDescriptorBase { + code: string; +} + +interface GPUStencilFaceState { + compare?: GPUCompareFunction; + depthFailOp?: GPUStencilOperation; + failOp?: GPUStencilOperation; + passOp?: GPUStencilOperation; +} + +interface GPUStorageTextureBindingLayout { + access?: GPUStorageTextureAccess; + format: GPUTextureFormat; + viewDimension?: GPUTextureViewDimension; +} + +interface GPUTexelCopyBufferInfo extends GPUTexelCopyBufferLayout { + buffer: GPUBuffer; +} + +interface GPUTexelCopyBufferLayout { + bytesPerRow?: GPUSize32; + offset?: GPUSize64; + rowsPerImage?: GPUSize32; +} + +interface GPUTexelCopyTextureInfo { + aspect?: GPUTextureAspect; + mipLevel?: GPUIntegerCoordinate; + origin?: GPUOrigin3D; + texture: GPUTexture; +} + +interface GPUTextureBindingLayout { + multisampled?: boolean; + sampleType?: GPUTextureSampleType; + viewDimension?: GPUTextureViewDimension; +} + +interface GPUTextureDescriptor extends GPUObjectDescriptorBase { + dimension?: GPUTextureDimension; + format: GPUTextureFormat; + mipLevelCount?: GPUIntegerCoordinate; + sampleCount?: GPUSize32; + size: GPUExtent3D; + usage: GPUTextureUsageFlags; + viewFormats?: GPUTextureFormat[]; +} + +interface GPUTextureViewDescriptor extends GPUObjectDescriptorBase { + arrayLayerCount?: GPUIntegerCoordinate; + aspect?: GPUTextureAspect; + baseArrayLayer?: GPUIntegerCoordinate; + baseMipLevel?: GPUIntegerCoordinate; + dimension?: GPUTextureViewDimension; + format?: GPUTextureFormat; + mipLevelCount?: GPUIntegerCoordinate; + usage?: GPUTextureUsageFlags; +} + +interface GPUUncapturedErrorEventInit extends EventInit { + error: GPUError; +} + +interface GPUVertexAttribute { + format: GPUVertexFormat; + offset: GPUSize64; + shaderLocation: GPUIndex32; +} + +interface GPUVertexBufferLayout { + arrayStride: GPUSize64; + attributes: GPUVertexAttribute[]; + stepMode?: GPUVertexStepMode; +} + +interface GPUVertexState extends GPUProgrammableStage { + buffers?: (GPUVertexBufferLayout | null)[]; +} + +interface GainOptions extends AudioNodeOptions { + gain?: number; +} + +interface GamepadEffectParameters { + duration?: number; + leftTrigger?: number; + rightTrigger?: number; + startDelay?: number; + strongMagnitude?: number; + weakMagnitude?: number; +} + +interface GamepadEventInit extends EventInit { + gamepad?: Gamepad | null; +} + +interface GetAnimationsOptions { + subtree?: boolean; +} + +interface GetComposedRangesOptions { + shadowRoots?: ShadowRoot[]; +} + +interface GetHTMLOptions { + serializableShadowRoots?: boolean; + shadowRoots?: ShadowRoot[]; +} + +interface GetNotificationOptions { + tag?: string; +} + +interface GetRootNodeOptions { + composed?: boolean; +} + +interface HashChangeEventInit extends EventInit { + newURL?: string; + oldURL?: string; +} + +interface HkdfParams extends Algorithm { + hash: HashAlgorithmIdentifier; + info: BufferSource; + salt: BufferSource; +} + +interface HmacImportParams extends Algorithm { + hash: HashAlgorithmIdentifier; + length?: number; +} + +interface HmacKeyAlgorithm extends KeyAlgorithm { + hash: KeyAlgorithm; + length: number; +} + +interface HmacKeyGenParams extends Algorithm { + hash: HashAlgorithmIdentifier; + length?: number; +} + +interface IDBDatabaseInfo { + name?: string; + version?: number; +} + +interface IDBIndexParameters { + multiEntry?: boolean; + unique?: boolean; +} + +interface IDBObjectStoreParameters { + autoIncrement?: boolean; + keyPath?: string | string[] | null; +} + +interface IDBTransactionOptions { + durability?: IDBTransactionDurability; +} + +interface IDBVersionChangeEventInit extends EventInit { + newVersion?: number | null; + oldVersion?: number; +} + +interface IIRFilterOptions extends AudioNodeOptions { + feedback: number[]; + feedforward: number[]; +} + +interface IdleRequestOptions { + timeout?: number; +} + +interface ImageBitmapOptions { + colorSpaceConversion?: ColorSpaceConversion; + imageOrientation?: ImageOrientation; + premultiplyAlpha?: PremultiplyAlpha; + resizeHeight?: number; + resizeQuality?: ResizeQuality; + resizeWidth?: number; +} + +interface ImageBitmapRenderingContextSettings { + alpha?: boolean; +} + +interface ImageDataSettings { + colorSpace?: PredefinedColorSpace; + pixelFormat?: ImageDataPixelFormat; +} + +interface ImageDecodeOptions { + completeFramesOnly?: boolean; + frameIndex?: number; +} + +interface ImageDecodeResult { + complete: boolean; + image: VideoFrame; +} + +interface ImageDecoderInit { + colorSpaceConversion?: ColorSpaceConversion; + data: ImageBufferSource; + desiredHeight?: number; + desiredWidth?: number; + preferAnimation?: boolean; + transfer?: ArrayBuffer[]; + type: string; +} + +interface ImageEncodeOptions { + quality?: number; + type?: string; +} + +interface ImportNodeOptions { + customElementRegistry?: CustomElementRegistry; + selfOnly?: boolean; +} + +interface InputEventInit extends UIEventInit { + data?: string | null; + dataTransfer?: DataTransfer | null; + inputType?: string; + isComposing?: boolean; + targetRanges?: StaticRange[]; +} + +interface IntersectionObserverInit { + root?: Element | Document | null; + rootMargin?: string; + scrollMargin?: string; + threshold?: number | number[]; +} + +interface JsonWebKey { + alg?: string; + crv?: string; + d?: string; + dp?: string; + dq?: string; + e?: string; + ext?: boolean; + k?: string; + key_ops?: string[]; + kty?: string; + n?: string; + oth?: RsaOtherPrimesInfo[]; + p?: string; + q?: string; + qi?: string; + use?: string; + x?: string; + y?: string; +} + +interface KeyAlgorithm { + name: string; +} + +interface KeySystemTrackConfiguration { + robustness?: string; +} + +interface KeyboardEventInit extends EventModifierInit { + /** @deprecated `charCode` is inconsistent across environments, consider using `key` instead. */ + charCode?: number; + code?: string; + isComposing?: boolean; + key?: string; + /** @deprecated `keyCode` is inconsistent across environments, consider using `key` instead. */ + keyCode?: number; + location?: number; + repeat?: boolean; +} + +interface Keyframe { + composite?: CompositeOperationOrAuto; + easing?: string; + offset?: number | null; + [property: string]: string | number | null | undefined; +} + +interface KeyframeAnimationOptions extends KeyframeEffectOptions { + id?: string; + rangeEnd?: TimelineRangeOffset | CSSNumericValue | CSSKeywordValue | string; + rangeStart?: TimelineRangeOffset | CSSNumericValue | CSSKeywordValue | string; + timeline?: AnimationTimeline | null; +} + +interface KeyframeEffectOptions extends EffectTiming { + composite?: CompositeOperation; + iterationComposite?: IterationCompositeOperation; + pseudoElement?: string | null; +} + +interface LockInfo { + clientId?: string; + mode?: LockMode; + name?: string; +} + +interface LockManagerSnapshot { + held?: LockInfo[]; + pending?: LockInfo[]; +} + +interface LockOptions { + ifAvailable?: boolean; + mode?: LockMode; + signal?: AbortSignal; + steal?: boolean; +} + +interface MIDIConnectionEventInit extends EventInit { + port?: MIDIPort; +} + +interface MIDIMessageEventInit extends EventInit { + data?: Uint8Array; +} + +interface MIDIOptions { + software?: boolean; + sysex?: boolean; +} + +interface MediaCapabilitiesDecodingInfo extends MediaCapabilitiesInfo { + keySystemAccess: MediaKeySystemAccess | null; +} + +interface MediaCapabilitiesEncodingInfo extends MediaCapabilitiesInfo { +} + +interface MediaCapabilitiesInfo { + powerEfficient: boolean; + smooth: boolean; + supported: boolean; +} + +interface MediaCapabilitiesKeySystemConfiguration { + audio?: KeySystemTrackConfiguration; + distinctiveIdentifier?: MediaKeysRequirement; + initDataType?: string; + keySystem: string; + persistentState?: MediaKeysRequirement; + sessionTypes?: string[]; + video?: KeySystemTrackConfiguration; +} + +interface MediaConfiguration { + audio?: AudioConfiguration; + video?: VideoConfiguration; +} + +interface MediaDecodingConfiguration extends MediaConfiguration { + keySystemConfiguration?: MediaCapabilitiesKeySystemConfiguration; + type: MediaDecodingType; +} + +interface MediaElementAudioSourceOptions { + mediaElement: HTMLMediaElement; +} + +interface MediaEncodingConfiguration extends MediaConfiguration { + type: MediaEncodingType; +} + +interface MediaEncryptedEventInit extends EventInit { + initData?: ArrayBuffer | null; + initDataType?: string; +} + +interface MediaImage { + sizes?: string; + src: string; + type?: string; +} + +interface MediaKeyMessageEventInit extends EventInit { + message: ArrayBuffer; + messageType: MediaKeyMessageType; +} + +interface MediaKeySystemConfiguration { + audioCapabilities?: MediaKeySystemMediaCapability[]; + distinctiveIdentifier?: MediaKeysRequirement; + initDataTypes?: string[]; + label?: string; + persistentState?: MediaKeysRequirement; + sessionTypes?: string[]; + videoCapabilities?: MediaKeySystemMediaCapability[]; +} + +interface MediaKeySystemMediaCapability { + contentType?: string; + encryptionScheme?: string | null; + robustness?: string; +} + +interface MediaKeysPolicy { + minHdcpVersion?: string; +} + +interface MediaMetadataInit { + album?: string; + artist?: string; + artwork?: MediaImage[]; + title?: string; +} + +interface MediaPositionState { + duration?: number; + playbackRate?: number; + position?: number; +} + +interface MediaQueryListEventInit extends EventInit { + matches?: boolean; + media?: string; +} + +interface MediaRecorderOptions { + audioBitsPerSecond?: number; + bitsPerSecond?: number; + mimeType?: string; + videoBitsPerSecond?: number; +} + +interface MediaSessionActionDetails { + action: MediaSessionAction; + fastSeek?: boolean; + seekOffset?: number; + seekTime?: number; +} + +interface MediaSettingsRange { + max?: number; + min?: number; + step?: number; +} + +interface MediaStreamAudioSourceOptions { + mediaStream: MediaStream; +} + +interface MediaStreamConstraints { + audio?: boolean | MediaTrackConstraints; + peerIdentity?: string; + preferCurrentTab?: boolean; + video?: boolean | MediaTrackConstraints; +} + +interface MediaStreamTrackEventInit extends EventInit { + track: MediaStreamTrack; +} + +interface MediaTrackCapabilities { + aspectRatio?: DoubleRange; + autoGainControl?: boolean[]; + backgroundBlur?: boolean[]; + channelCount?: ULongRange; + deviceId?: string; + displaySurface?: string; + echoCancellation?: (boolean | string)[]; + facingMode?: string[]; + frameRate?: DoubleRange; + groupId?: string; + height?: ULongRange; + noiseSuppression?: boolean[]; + sampleRate?: ULongRange; + sampleSize?: ULongRange; + width?: ULongRange; +} + +interface MediaTrackConstraintSet { + aspectRatio?: ConstrainDouble; + autoGainControl?: ConstrainBoolean; + backgroundBlur?: ConstrainBoolean; + channelCount?: ConstrainULong; + deviceId?: ConstrainDOMString; + displaySurface?: ConstrainDOMString; + echoCancellation?: ConstrainBooleanOrDOMString; + facingMode?: ConstrainDOMString; + frameRate?: ConstrainDouble; + groupId?: ConstrainDOMString; + height?: ConstrainULong; + noiseSuppression?: ConstrainBoolean; + sampleRate?: ConstrainULong; + sampleSize?: ConstrainULong; + width?: ConstrainULong; +} + +interface MediaTrackConstraints extends MediaTrackConstraintSet { + advanced?: MediaTrackConstraintSet[]; +} + +interface MediaTrackSettings { + aspectRatio?: number; + autoGainControl?: boolean; + backgroundBlur?: boolean; + channelCount?: number; + deviceId?: string; + displaySurface?: string; + echoCancellation?: boolean | string; + facingMode?: string; + frameRate?: number; + groupId?: string; + height?: number; + noiseSuppression?: boolean; + sampleRate?: number; + sampleSize?: number; + torch?: boolean; + whiteBalanceMode?: string; + width?: number; + zoom?: number; +} + +interface MediaTrackSupportedConstraints { + aspectRatio?: boolean; + autoGainControl?: boolean; + backgroundBlur?: boolean; + channelCount?: boolean; + deviceId?: boolean; + displaySurface?: boolean; + echoCancellation?: boolean; + facingMode?: boolean; + frameRate?: boolean; + groupId?: boolean; + height?: boolean; + noiseSuppression?: boolean; + sampleRate?: boolean; + sampleSize?: boolean; + width?: boolean; +} + +interface MessageEventInit extends EventInit { + data?: T; + lastEventId?: string; + origin?: string; + ports?: MessagePort[]; + source?: MessageEventSource | null; +} + +interface MouseEventInit extends EventModifierInit { + button?: number; + buttons?: number; + clientX?: number; + clientY?: number; + movementX?: number; + movementY?: number; + relatedTarget?: EventTarget | null; + screenX?: number; + screenY?: number; +} + +interface MultiCacheQueryOptions extends CacheQueryOptions { + cacheName?: string; +} + +interface MutationObserverInit { + /** Set to a list of attribute local names (without namespace) if not all attribute mutations need to be observed and attributes is true or omitted. */ + attributeFilter?: string[]; + /** Set to true if attributes is true or omitted and target's attribute value before the mutation needs to be recorded. */ + attributeOldValue?: boolean; + /** Set to true if mutations to target's attributes are to be observed. Can be omitted if attributeOldValue or attributeFilter is specified. */ + attributes?: boolean; + /** Set to true if mutations to target's data are to be observed. Can be omitted if characterDataOldValue is specified. */ + characterData?: boolean; + /** Set to true if characterData is set to true or omitted and target's data before the mutation needs to be recorded. */ + characterDataOldValue?: boolean; + /** Set to true if mutations to target's children are to be observed. */ + childList?: boolean; + /** Set to true if mutations to not just target, but also target's descendants are to be observed. */ + subtree?: boolean; +} + +interface NavigateEventInit extends EventInit { + canIntercept?: boolean; + destination: NavigationDestination; + downloadRequest?: string | null; + formData?: FormData | null; + hasUAVisualTransition?: boolean; + hashChange?: boolean; + info?: any; + navigationType?: NavigationType; + signal: AbortSignal; + sourceElement?: Element | null; + userInitiated?: boolean; +} + +interface NavigationCurrentEntryChangeEventInit extends EventInit { + from: NavigationHistoryEntry; + navigationType?: NavigationType | null; +} + +interface NavigationInterceptOptions { + focusReset?: NavigationFocusReset; + handler?: NavigationInterceptHandler; + precommitHandler?: NavigationPrecommitHandler; + scroll?: NavigationScrollBehavior; +} + +interface NavigationNavigateOptions extends NavigationOptions { + history?: NavigationHistoryBehavior; + state?: any; +} + +interface NavigationOptions { + info?: any; +} + +interface NavigationPreloadState { + enabled?: boolean; + headerValue?: string; +} + +interface NavigationReloadOptions extends NavigationOptions { + state?: any; +} + +interface NavigationResult { + committed?: Promise; + finished?: Promise; +} + +interface NavigationUpdateCurrentEntryOptions { + state: any; +} + +interface NotificationOptions { + badge?: string; + body?: string; + data?: any; + dir?: NotificationDirection; + icon?: string; + lang?: string; + requireInteraction?: boolean; + silent?: boolean | null; + tag?: string; +} + +interface OfflineAudioCompletionEventInit extends EventInit { + renderedBuffer: AudioBuffer; +} + +interface OfflineAudioContextOptions { + length: number; + numberOfChannels?: number; + sampleRate: number; +} + +interface OptionalEffectTiming { + delay?: number; + direction?: PlaybackDirection; + duration?: number | string; + easing?: string; + endDelay?: number; + fill?: FillMode; + iterationStart?: number; + iterations?: number; + playbackRate?: number; +} + +interface OpusEncoderConfig { + complexity?: number; + format?: OpusBitstreamFormat; + frameDuration?: number; + packetlossperc?: number; + usedtx?: boolean; + useinbandfec?: boolean; +} + +interface OscillatorOptions extends AudioNodeOptions { + detune?: number; + frequency?: number; + periodicWave?: PeriodicWave; + type?: OscillatorType; +} + +interface PageRevealEventInit extends EventInit { + viewTransition?: ViewTransition | null; +} + +interface PageSwapEventInit extends EventInit { + activation?: NavigationActivation | null; + viewTransition?: ViewTransition | null; +} + +interface PageTransitionEventInit extends EventInit { + persisted?: boolean; +} + +interface PannerOptions extends AudioNodeOptions { + coneInnerAngle?: number; + coneOuterAngle?: number; + coneOuterGain?: number; + distanceModel?: DistanceModelType; + maxDistance?: number; + orientationX?: number; + orientationY?: number; + orientationZ?: number; + panningModel?: PanningModelType; + positionX?: number; + positionY?: number; + positionZ?: number; + refDistance?: number; + rolloffFactor?: number; +} + +interface PayerErrors { + email?: string; + name?: string; + phone?: string; +} + +interface PaymentCurrencyAmount { + currency: string; + value: string; +} + +interface PaymentDetailsBase { + displayItems?: PaymentItem[]; + modifiers?: PaymentDetailsModifier[]; + shippingOptions?: PaymentShippingOption[]; +} + +interface PaymentDetailsInit extends PaymentDetailsBase { + id?: string; + total: PaymentItem; +} + +interface PaymentDetailsModifier { + additionalDisplayItems?: PaymentItem[]; + data?: any; + supportedMethods: string; + total?: PaymentItem; +} + +interface PaymentDetailsUpdate extends PaymentDetailsBase { + error?: string; + paymentMethodErrors?: any; + shippingAddressErrors?: AddressErrors; + total?: PaymentItem; +} + +interface PaymentItem { + amount: PaymentCurrencyAmount; + label: string; + pending?: boolean; +} + +interface PaymentMethodChangeEventInit extends PaymentRequestUpdateEventInit { + methodDetails?: any; + methodName?: string; +} + +interface PaymentMethodData { + data?: any; + supportedMethods: string; +} + +interface PaymentOptions { + requestPayerEmail?: boolean; + requestPayerName?: boolean; + requestPayerPhone?: boolean; + requestShipping?: boolean; + shippingType?: PaymentShippingType; +} + +interface PaymentRequestUpdateEventInit extends EventInit { +} + +interface PaymentShippingOption { + amount: PaymentCurrencyAmount; + id: string; + label: string; + selected?: boolean; +} + +interface PaymentValidationErrors { + error?: string; + payer?: PayerErrors; + shippingAddress?: AddressErrors; +} + +interface Pbkdf2Params extends Algorithm { + hash: HashAlgorithmIdentifier; + iterations: number; + salt: BufferSource; +} + +interface PerformanceMarkOptions { + detail?: any; + startTime?: DOMHighResTimeStamp; +} + +interface PerformanceMeasureOptions { + detail?: any; + duration?: DOMHighResTimeStamp; + end?: string | DOMHighResTimeStamp; + start?: string | DOMHighResTimeStamp; +} + +interface PerformanceObserverInit { + buffered?: boolean; + entryTypes?: string[]; + type?: string; +} + +interface PeriodicWaveConstraints { + disableNormalization?: boolean; +} + +interface PeriodicWaveOptions extends PeriodicWaveConstraints { + imag?: number[] | Float32Array; + real?: number[] | Float32Array; +} + +interface PermissionDescriptor { + name: PermissionName; +} + +interface PhotoCapabilities { + fillLightMode?: FillLightMode[]; + imageHeight?: MediaSettingsRange; + imageWidth?: MediaSettingsRange; + redEyeReduction?: RedEyeReduction; +} + +interface PhotoSettings { + fillLightMode?: FillLightMode; + imageHeight?: number; + imageWidth?: number; + redEyeReduction?: boolean; +} + +interface PictureInPictureEventInit extends EventInit { + pictureInPictureWindow: PictureInPictureWindow; +} + +interface PlaneLayout { + offset: number; + stride: number; +} + +interface PointerEventInit extends MouseEventInit { + altitudeAngle?: number; + azimuthAngle?: number; + coalescedEvents?: PointerEvent[]; + height?: number; + isPrimary?: boolean; + pointerId?: number; + pointerType?: string; + predictedEvents?: PointerEvent[]; + pressure?: number; + tangentialPressure?: number; + tiltX?: number; + tiltY?: number; + twist?: number; + width?: number; +} + +interface PointerLockOptions { + unadjustedMovement?: boolean; +} + +interface PopStateEventInit extends EventInit { + hasUAVisualTransition?: boolean; + state?: any; +} + +interface PositionOptions { + enableHighAccuracy?: boolean; + maximumAge?: number; + timeout?: number; +} + +interface ProgressEventInit extends EventInit { + lengthComputable?: boolean; + loaded?: number; + total?: number; +} + +interface PromiseRejectionEventInit extends EventInit { + promise: Promise; + reason?: any; +} + +interface PropertyDefinition { + inherits: boolean; + initialValue?: string; + name: string; + syntax?: string; +} + +interface PropertyIndexedKeyframes { + composite?: CompositeOperationOrAuto | CompositeOperationOrAuto[]; + easing?: string | string[]; + offset?: number | (number | null)[]; + [property: string]: string | string[] | number | null | (number | null)[] | undefined; +} + +interface PublicKeyCredentialCreationOptions { + attestation?: AttestationConveyancePreference; + authenticatorSelection?: AuthenticatorSelectionCriteria; + challenge: BufferSource; + excludeCredentials?: PublicKeyCredentialDescriptor[]; + extensions?: AuthenticationExtensionsClientInputs; + pubKeyCredParams: PublicKeyCredentialParameters[]; + rp: PublicKeyCredentialRpEntity; + timeout?: number; + user: PublicKeyCredentialUserEntity; +} + +interface PublicKeyCredentialCreationOptionsJSON { + attestation?: string; + authenticatorSelection?: AuthenticatorSelectionCriteria; + challenge: Base64URLString; + excludeCredentials?: PublicKeyCredentialDescriptorJSON[]; + extensions?: AuthenticationExtensionsClientInputsJSON; + hints?: string[]; + pubKeyCredParams: PublicKeyCredentialParameters[]; + rp: PublicKeyCredentialRpEntity; + timeout?: number; + user: PublicKeyCredentialUserEntityJSON; +} + +interface PublicKeyCredentialDescriptor { + id: BufferSource; + transports?: AuthenticatorTransport[]; + type: PublicKeyCredentialType; +} + +interface PublicKeyCredentialDescriptorJSON { + id: Base64URLString; + transports?: string[]; + type: string; +} + +interface PublicKeyCredentialEntity { + name: string; +} + +interface PublicKeyCredentialParameters { + alg: COSEAlgorithmIdentifier; + type: PublicKeyCredentialType; +} + +interface PublicKeyCredentialRequestOptions { + allowCredentials?: PublicKeyCredentialDescriptor[]; + challenge: BufferSource; + extensions?: AuthenticationExtensionsClientInputs; + rpId?: string; + timeout?: number; + userVerification?: UserVerificationRequirement; +} + +interface PublicKeyCredentialRequestOptionsJSON { + allowCredentials?: PublicKeyCredentialDescriptorJSON[]; + challenge: Base64URLString; + extensions?: AuthenticationExtensionsClientInputsJSON; + hints?: string[]; + rpId?: string; + timeout?: number; + userVerification?: string; +} + +interface PublicKeyCredentialRpEntity extends PublicKeyCredentialEntity { + id?: string; +} + +interface PublicKeyCredentialUserEntity extends PublicKeyCredentialEntity { + displayName: string; + id: BufferSource; +} + +interface PublicKeyCredentialUserEntityJSON { + displayName: string; + id: Base64URLString; + name: string; +} + +interface PushSubscriptionJSON { + endpoint?: string; + expirationTime?: EpochTimeStamp | null; + keys?: Record; +} + +interface PushSubscriptionOptionsInit { + applicationServerKey?: BufferSource | string | null; + userVisibleOnly?: boolean; +} + +interface QueuingStrategy { + highWaterMark?: number; + size?: QueuingStrategySize; +} + +interface QueuingStrategyInit { + /** + * Creates a new ByteLengthQueuingStrategy with the provided high water mark. + * + * Note that the provided high water mark will not be validated ahead of time. Instead, if it is negative, NaN, or not a number, the resulting ByteLengthQueuingStrategy will cause the corresponding stream constructor to throw. + */ + highWaterMark: number; +} + +interface RTCAnswerOptions extends RTCOfferAnswerOptions { +} + +interface RTCCertificateExpiration { + expires?: number; +} + +interface RTCConfiguration { + bundlePolicy?: RTCBundlePolicy; + certificates?: RTCCertificate[]; + iceCandidatePoolSize?: number; + iceServers?: RTCIceServer[]; + iceTransportPolicy?: RTCIceTransportPolicy; + rtcpMuxPolicy?: RTCRtcpMuxPolicy; +} + +interface RTCDTMFToneChangeEventInit extends EventInit { + tone?: string; +} + +interface RTCDataChannelEventInit extends EventInit { + channel: RTCDataChannel; +} + +interface RTCDataChannelInit { + id?: number; + maxPacketLifeTime?: number; + maxRetransmits?: number; + negotiated?: boolean; + ordered?: boolean; + protocol?: string; +} + +interface RTCDtlsFingerprint { + algorithm?: string; + value?: string; +} + +interface RTCEncodedAudioFrameMetadata extends RTCEncodedFrameMetadata { + sequenceNumber?: number; +} + +interface RTCEncodedFrameMetadata { + contributingSources?: number[]; + mimeType?: string; + payloadType?: number; + rtpTimestamp?: number; + synchronizationSource?: number; +} + +interface RTCEncodedVideoFrameMetadata extends RTCEncodedFrameMetadata { + dependencies?: number[]; + frameId?: number; + height?: number; + spatialIndex?: number; + temporalIndex?: number; + timestamp?: number; + width?: number; +} + +interface RTCErrorEventInit extends EventInit { + error: RTCError; +} + +interface RTCErrorInit { + errorDetail: RTCErrorDetailType; + httpRequestStatusCode?: number; + receivedAlert?: number; + sctpCauseCode?: number; + sdpLineNumber?: number; + sentAlert?: number; +} + +interface RTCIceCandidateInit { + candidate?: string; + sdpMLineIndex?: number | null; + sdpMid?: string | null; + usernameFragment?: string | null; +} + +interface RTCIceCandidatePairStats extends RTCStats { + availableIncomingBitrate?: number; + availableOutgoingBitrate?: number; + bytesDiscardedOnSend?: number; + bytesReceived?: number; + bytesSent?: number; + consentRequestsSent?: number; + currentRoundTripTime?: number; + lastPacketReceivedTimestamp?: DOMHighResTimeStamp; + lastPacketSentTimestamp?: DOMHighResTimeStamp; + localCandidateId: string; + nominated?: boolean; + packetsDiscardedOnSend?: number; + packetsReceived?: number; + packetsSent?: number; + remoteCandidateId: string; + requestsReceived?: number; + requestsSent?: number; + responsesReceived?: number; + responsesSent?: number; + state: RTCStatsIceCandidatePairState; + totalRoundTripTime?: number; + transportId: string; +} + +interface RTCIceServer { + credential?: string; + urls: string | string[]; + username?: string; +} + +interface RTCInboundRtpStreamStats extends RTCReceivedRtpStreamStats { + audioLevel?: number; + bytesReceived?: number; + concealedSamples?: number; + concealmentEvents?: number; + decoderImplementation?: string; + estimatedPlayoutTimestamp?: DOMHighResTimeStamp; + fecBytesReceived?: number; + fecPacketsDiscarded?: number; + fecPacketsReceived?: number; + fecSsrc?: number; + firCount?: number; + frameHeight?: number; + frameWidth?: number; + framesAssembledFromMultiplePackets?: number; + framesDecoded?: number; + framesDropped?: number; + framesPerSecond?: number; + framesReceived?: number; + framesRendered?: number; + freezeCount?: number; + headerBytesReceived?: number; + insertedSamplesForDeceleration?: number; + jitterBufferDelay?: number; + jitterBufferEmittedCount?: number; + jitterBufferMinimumDelay?: number; + jitterBufferTargetDelay?: number; + keyFramesDecoded?: number; + lastPacketReceivedTimestamp?: DOMHighResTimeStamp; + mid?: string; + nackCount?: number; + packetsDiscarded?: number; + pauseCount?: number; + playoutId?: string; + pliCount?: number; + qpSum?: number; + remoteId?: string; + removedSamplesForAcceleration?: number; + retransmittedBytesReceived?: number; + retransmittedPacketsReceived?: number; + rtxSsrc?: number; + silentConcealedSamples?: number; + totalAssemblyTime?: number; + totalAudioEnergy?: number; + totalDecodeTime?: number; + totalFreezesDuration?: number; + totalInterFrameDelay?: number; + totalPausesDuration?: number; + totalProcessingDelay?: number; + totalSamplesDuration?: number; + totalSamplesReceived?: number; + totalSquaredInterFrameDelay?: number; + trackIdentifier: string; +} + +interface RTCLocalIceCandidateInit extends RTCIceCandidateInit { +} + +interface RTCLocalSessionDescriptionInit { + sdp?: string; + type?: RTCSdpType; +} + +interface RTCOfferAnswerOptions { +} + +interface RTCOfferOptions extends RTCOfferAnswerOptions { + iceRestart?: boolean; + offerToReceiveAudio?: boolean; + offerToReceiveVideo?: boolean; +} + +interface RTCOutboundRtpStreamStats extends RTCSentRtpStreamStats { + active?: boolean; + firCount?: number; + frameHeight?: number; + frameWidth?: number; + framesEncoded?: number; + framesPerSecond?: number; + framesSent?: number; + headerBytesSent?: number; + hugeFramesSent?: number; + keyFramesEncoded?: number; + mediaSourceId?: string; + mid?: string; + nackCount?: number; + pliCount?: number; + qpSum?: number; + qualityLimitationDurations?: Record; + qualityLimitationReason?: RTCQualityLimitationReason; + qualityLimitationResolutionChanges?: number; + remoteId?: string; + retransmittedBytesSent?: number; + retransmittedPacketsSent?: number; + rid?: string; + rtxSsrc?: number; + scalabilityMode?: string; + targetBitrate?: number; + totalEncodeTime?: number; + totalEncodedBytesTarget?: number; + totalPacketSendDelay?: number; +} + +interface RTCPeerConnectionIceErrorEventInit extends EventInit { + address?: string | null; + errorCode: number; + errorText?: string; + port?: number | null; + url?: string; +} + +interface RTCPeerConnectionIceEventInit extends EventInit { + candidate?: RTCIceCandidate | null; +} + +interface RTCReceivedRtpStreamStats extends RTCRtpStreamStats { + jitter?: number; + packetsLost?: number; + packetsReceived?: number; +} + +interface RTCRtcpParameters { + cname?: string; + reducedSize?: boolean; +} + +interface RTCRtpCapabilities { + codecs: RTCRtpCodec[]; + headerExtensions: RTCRtpHeaderExtensionCapability[]; +} + +interface RTCRtpCodec { + channels?: number; + clockRate: number; + mimeType: string; + sdpFmtpLine?: string; +} + +interface RTCRtpCodecParameters extends RTCRtpCodec { + payloadType: number; +} + +interface RTCRtpCodingParameters { + rid?: string; +} + +interface RTCRtpContributingSource { + audioLevel?: number; + rtpTimestamp: number; + source: number; + timestamp: DOMHighResTimeStamp; +} + +interface RTCRtpEncodingParameters extends RTCRtpCodingParameters { + active?: boolean; + maxBitrate?: number; + maxFramerate?: number; + networkPriority?: RTCPriorityType; + priority?: RTCPriorityType; + scaleResolutionDownBy?: number; +} + +interface RTCRtpHeaderExtensionCapability { + uri: string; +} + +interface RTCRtpHeaderExtensionParameters { + encrypted?: boolean; + id: number; + uri: string; +} + +interface RTCRtpParameters { + codecs: RTCRtpCodecParameters[]; + headerExtensions: RTCRtpHeaderExtensionParameters[]; + rtcp: RTCRtcpParameters; +} + +interface RTCRtpReceiveParameters extends RTCRtpParameters { +} + +interface RTCRtpSendParameters extends RTCRtpParameters { + degradationPreference?: RTCDegradationPreference; + encodings: RTCRtpEncodingParameters[]; + transactionId: string; +} + +interface RTCRtpStreamStats extends RTCStats { + codecId?: string; + kind: string; + ssrc: number; + transportId?: string; +} + +interface RTCRtpSynchronizationSource extends RTCRtpContributingSource { +} + +interface RTCRtpTransceiverInit { + direction?: RTCRtpTransceiverDirection; + sendEncodings?: RTCRtpEncodingParameters[]; + streams?: MediaStream[]; +} + +interface RTCSentRtpStreamStats extends RTCRtpStreamStats { + bytesSent?: number; + packetsSent?: number; +} + +interface RTCSessionDescriptionInit { + sdp?: string; + type: RTCSdpType; +} + +interface RTCSetParameterOptions { +} + +interface RTCStats { + id: string; + timestamp: DOMHighResTimeStamp; + type: RTCStatsType; +} + +interface RTCTrackEventInit extends EventInit { + receiver: RTCRtpReceiver; + streams?: MediaStream[]; + track: MediaStreamTrack; + transceiver: RTCRtpTransceiver; +} + +interface RTCTransportStats extends RTCStats { + bytesReceived?: number; + bytesSent?: number; + dtlsCipher?: string; + dtlsRole?: RTCDtlsRole; + dtlsState: RTCDtlsTransportState; + iceLocalUsernameFragment?: string; + iceRole?: RTCIceRole; + iceState?: RTCIceTransportState; + localCertificateId?: string; + packetsReceived?: number; + packetsSent?: number; + remoteCertificateId?: string; + selectedCandidatePairChanges?: number; + selectedCandidatePairId?: string; + srtpCipher?: string; + tlsVersion?: string; +} + +interface ReadableStreamBYOBReaderReadOptions { + min?: number; +} + +interface ReadableStreamGetReaderOptions { + /** + * Creates a ReadableStreamBYOBReader and locks the stream to the new reader. + * + * This call behaves the same way as the no-argument variant, except that it only works on readable byte streams, i.e. streams which were constructed specifically with the ability to handle "bring your own buffer" reading. The returned BYOB reader provides the ability to directly read individual chunks from the stream via its read() method, into developer-supplied buffers, allowing more precise control over allocation. + */ + mode?: ReadableStreamReaderMode; +} + +interface ReadableStreamIteratorOptions { + /** + * Asynchronously iterates over the chunks in the stream's internal queue. + * + * Asynchronously iterating over the stream will lock it, preventing any other consumer from acquiring a reader. The lock will be released if the async iterator's return() method is called, e.g. by breaking out of the loop. + * + * By default, calling the async iterator's return() method will also cancel the stream. To prevent this, use the stream's values() method, passing true for the preventCancel option. + */ + preventCancel?: boolean; +} + +interface ReadableStreamReadDoneResult { + done: true; + value: T | undefined; +} + +interface ReadableStreamReadValueResult { + done: false; + value: T; +} + +interface ReadableWritablePair { + readable: ReadableStream; + /** + * Provides a convenient, chainable way of piping this readable stream through a transform stream (or any other { writable, readable } pair). It simply pipes the stream into the writable side of the supplied pair, and returns the readable side for further use. + * + * Piping a stream will lock it for the duration of the pipe, preventing any other consumer from acquiring a reader. + */ + writable: WritableStream; +} + +interface RegistrationOptions { + scope?: string; + type?: WorkerType; + updateViaCache?: ServiceWorkerUpdateViaCache; +} + +interface RegistrationResponseJSON { + authenticatorAttachment?: string; + clientExtensionResults: AuthenticationExtensionsClientOutputsJSON; + id: string; + rawId: Base64URLString; + response: AuthenticatorAttestationResponseJSON; + type: string; +} + +interface Report { + body?: ReportBody | null; + type?: string; + url?: string; +} + +interface ReportBody { +} + +interface ReportingObserverOptions { + buffered?: boolean; + types?: string[]; +} + +interface RequestInit { + /** A BodyInit object or null to set request's body. */ + body?: BodyInit | null; + /** A string indicating how the request will interact with the browser's cache to set request's cache. */ + cache?: RequestCache; + /** A string indicating whether credentials will be sent with the request always, never, or only when sent to a same-origin URL. Sets request's credentials. */ + credentials?: RequestCredentials; + /** A Headers object, an object literal, or an array of two-item arrays to set request's headers. */ + headers?: HeadersInit; + /** A cryptographic hash of the resource to be fetched by request. Sets request's integrity. */ + integrity?: string; + /** A boolean to set request's keepalive. */ + keepalive?: boolean; + /** A string to set request's method. */ + method?: string; + /** A string to indicate whether the request will use CORS, or will be restricted to same-origin URLs. Sets request's mode. */ + mode?: RequestMode; + priority?: RequestPriority; + /** A string indicating whether request follows redirects, results in an error upon encountering a redirect, or returns the redirect (in an opaque fashion). Sets request's redirect. */ + redirect?: RequestRedirect; + /** A string whose value is a same-origin URL, "about:client", or the empty string, to set request's referrer. */ + referrer?: string; + /** A referrer policy to set request's referrerPolicy. */ + referrerPolicy?: ReferrerPolicy; + /** An AbortSignal to set request's signal. */ + signal?: AbortSignal | null; + /** Can only be null. Used to disassociate request from any Window. */ + window?: null; +} + +interface ResizeObserverOptions { + box?: ResizeObserverBoxOptions; +} + +interface ResponseInit { + headers?: HeadersInit; + status?: number; + statusText?: string; +} + +interface RsaHashedImportParams extends Algorithm { + hash: HashAlgorithmIdentifier; +} + +interface RsaHashedKeyAlgorithm extends RsaKeyAlgorithm { + hash: KeyAlgorithm; +} + +interface RsaHashedKeyGenParams extends RsaKeyGenParams { + hash: HashAlgorithmIdentifier; +} + +interface RsaKeyAlgorithm extends KeyAlgorithm { + modulusLength: number; + publicExponent: BigInteger; +} + +interface RsaKeyGenParams extends Algorithm { + modulusLength: number; + publicExponent: BigInteger; +} + +interface RsaOaepParams extends Algorithm { + label?: BufferSource; +} + +interface RsaOtherPrimesInfo { + d?: string; + r?: string; + t?: string; +} + +interface RsaPssParams extends Algorithm { + saltLength: number; +} + +interface SVGBoundingBoxOptions { + clipped?: boolean; + fill?: boolean; + markers?: boolean; + stroke?: boolean; +} + +interface SanitizerAttributeNamespace { + name: string; + namespace?: string | null; +} + +interface SanitizerConfig { + attributes?: SanitizerAttribute[]; + comments?: boolean; + dataAttributes?: boolean; + elements?: SanitizerElementWithAttributes[]; + removeAttributes?: SanitizerAttribute[]; + removeElements?: SanitizerElement[]; + replaceWithChildrenElements?: SanitizerElement[]; +} + +interface SanitizerElementNamespace { + name: string; + namespace?: string | null; +} + +interface SanitizerElementNamespaceWithAttributes extends SanitizerElementNamespace { + attributes?: SanitizerAttribute[]; + removeAttributes?: SanitizerAttribute[]; +} + +interface SchedulerPostTaskOptions { + delay?: number; + priority?: TaskPriority; + signal?: AbortSignal; +} + +interface ScrollIntoViewOptions extends ScrollOptions { + block?: ScrollLogicalPosition; + inline?: ScrollLogicalPosition; +} + +interface ScrollOptions { + behavior?: ScrollBehavior; +} + +interface ScrollTimelineOptions { + axis?: ScrollAxis; + source?: Element | null; +} + +interface ScrollToOptions extends ScrollOptions { + left?: number; + top?: number; +} + +interface SecurityPolicyViolationEventInit extends EventInit { + blockedURI?: string; + columnNumber?: number; + disposition?: SecurityPolicyViolationEventDisposition; + documentURI?: string; + effectiveDirective?: string; + lineNumber?: number; + originalPolicy?: string; + referrer?: string; + sample?: string; + sourceFile?: string; + statusCode?: number; + violatedDirective?: string; +} + +interface ShadowRootInit { + clonable?: boolean; + customElementRegistry?: CustomElementRegistry | null; + delegatesFocus?: boolean; + mode: ShadowRootMode; + serializable?: boolean; + slotAssignment?: SlotAssignmentMode; +} + +interface ShareData { + files?: File[]; + text?: string; + title?: string; + url?: string; +} + +interface ShowPopoverOptions { + source?: HTMLElement; +} + +interface SpeechRecognitionErrorEventInit extends EventInit { + error: SpeechRecognitionErrorCode; + message?: string; +} + +interface SpeechRecognitionEventInit extends EventInit { + resultIndex?: number; + results: SpeechRecognitionResultList; +} + +interface SpeechSynthesisErrorEventInit extends SpeechSynthesisEventInit { + error: SpeechSynthesisErrorCode; +} + +interface SpeechSynthesisEventInit extends EventInit { + charIndex?: number; + charLength?: number; + elapsedTime?: number; + name?: string; + utterance: SpeechSynthesisUtterance; +} + +interface StartViewTransitionOptions { + types?: string[] | null; + update?: ViewTransitionUpdateCallback | null; +} + +interface StaticRangeInit { + endContainer: Node; + endOffset: number; + startContainer: Node; + startOffset: number; +} + +interface StereoPannerOptions extends AudioNodeOptions { + pan?: number; +} + +interface StorageEstimate { + quota?: number; + usage?: number; +} + +interface StorageEventInit extends EventInit { + key?: string | null; + newValue?: string | null; + oldValue?: string | null; + storageArea?: Storage | null; + url?: string; +} + +interface StreamPipeOptions { + preventAbort?: boolean; + preventCancel?: boolean; + /** + * Pipes this readable stream to a given writable stream destination. The way in which the piping process behaves under various error conditions can be customized with a number of passed options. It returns a promise that fulfills when the piping process completes successfully, or rejects if any errors were encountered. + * + * Piping a stream will lock it for the duration of the pipe, preventing any other consumer from acquiring a reader. + * + * Errors and closures of the source and destination streams propagate as follows: + * + * An error in this source readable stream will abort destination, unless preventAbort is truthy. The returned promise will be rejected with the source's error, or with any error that occurs during aborting the destination. + * + * An error in destination will cancel this source readable stream, unless preventCancel is truthy. The returned promise will be rejected with the destination's error, or with any error that occurs during canceling the source. + * + * When this source readable stream closes, destination will be closed, unless preventClose is truthy. The returned promise will be fulfilled once this process completes, unless an error is encountered while closing the destination, in which case it will be rejected with that error. + * + * If destination starts out closed or closing, this source readable stream will be canceled, unless preventCancel is true. The returned promise will be rejected with an error indicating piping to a closed stream failed, or with any error that occurs during canceling the source. + * + * The signal option can be set to an AbortSignal to allow aborting an ongoing pipe operation via the corresponding AbortController. In this case, this source readable stream will be canceled, and destination aborted, unless the respective options preventCancel or preventAbort are set. + */ + preventClose?: boolean; + signal?: AbortSignal; +} + +interface StructuredSerializeOptions { + transfer?: Transferable[]; +} + +interface SubmitEventInit extends EventInit { + submitter?: HTMLElement | null; +} + +interface SvcOutputMetadata { + temporalLayerId?: number; +} + +interface TaskControllerInit { + priority?: TaskPriority; +} + +interface TaskPriorityChangeEventInit extends EventInit { + previousPriority: TaskPriority; +} + +interface TaskSignalAnyInit { + priority?: TaskPriority | TaskSignal; +} + +interface TextDecodeOptions { + stream?: boolean; +} + +interface TextDecoderOptions { + fatal?: boolean; + ignoreBOM?: boolean; +} + +interface TextEncoderEncodeIntoResult { + read: number; + written: number; +} + +interface TimelineRangeOffset { + offset?: CSSNumericValue; + rangeName?: string | null; +} + +interface ToggleEventInit extends EventInit { + newState?: string; + oldState?: string; + source?: Element | null; +} + +interface TogglePopoverOptions extends ShowPopoverOptions { + force?: boolean; +} + +interface TouchEventInit extends EventModifierInit { + changedTouches?: Touch[]; + targetTouches?: Touch[]; + touches?: Touch[]; +} + +interface TouchInit { + altitudeAngle?: number; + azimuthAngle?: number; + clientX?: number; + clientY?: number; + force?: number; + identifier: number; + pageX?: number; + pageY?: number; + radiusX?: number; + radiusY?: number; + rotationAngle?: number; + screenX?: number; + screenY?: number; + target: EventTarget; + touchType?: TouchType; +} + +interface TrackEventInit extends EventInit { + track?: TextTrack | null; +} + +interface Transformer { + flush?: TransformerFlushCallback; + readableType?: undefined; + start?: TransformerStartCallback; + transform?: TransformerTransformCallback; + writableType?: undefined; +} + +interface TransitionEventInit extends EventInit { + elapsedTime?: number; + propertyName?: string; + pseudoElement?: string; +} + +interface UIEventInit extends EventInit { + detail?: number; + view?: Window | null; + /** @deprecated */ + which?: number; +} + +interface ULongRange { + max?: number; + min?: number; +} + +interface URLPatternComponentResult { + groups: Record; + input: string; +} + +interface URLPatternInit { + baseURL?: string; + hash?: string; + hostname?: string; + password?: string; + pathname?: string; + port?: string; + protocol?: string; + search?: string; + username?: string; +} + +interface URLPatternOptions { + ignoreCase?: boolean; +} + +interface URLPatternResult { + hash: URLPatternComponentResult; + hostname: URLPatternComponentResult; + inputs: URLPatternInput[]; + password: URLPatternComponentResult; + pathname: URLPatternComponentResult; + port: URLPatternComponentResult; + protocol: URLPatternComponentResult; + search: URLPatternComponentResult; + username: URLPatternComponentResult; +} + +interface UnderlyingByteSource { + autoAllocateChunkSize?: number; + cancel?: UnderlyingSourceCancelCallback; + pull?: (controller: ReadableByteStreamController) => void | PromiseLike; + start?: (controller: ReadableByteStreamController) => any; + type: "bytes"; +} + +interface UnderlyingDefaultSource { + cancel?: UnderlyingSourceCancelCallback; + pull?: (controller: ReadableStreamDefaultController) => void | PromiseLike; + start?: (controller: ReadableStreamDefaultController) => any; + type?: undefined; +} + +interface UnderlyingSink { + abort?: UnderlyingSinkAbortCallback; + close?: UnderlyingSinkCloseCallback; + start?: UnderlyingSinkStartCallback; + type?: undefined; + write?: UnderlyingSinkWriteCallback; +} + +interface UnderlyingSource { + autoAllocateChunkSize?: number; + cancel?: UnderlyingSourceCancelCallback; + pull?: UnderlyingSourcePullCallback; + start?: UnderlyingSourceStartCallback; + type?: ReadableStreamType; +} + +interface UnknownCredentialOptions { + credentialId: Base64URLString; + rpId: string; +} + +interface ValidityStateFlags { + badInput?: boolean; + customError?: boolean; + patternMismatch?: boolean; + rangeOverflow?: boolean; + rangeUnderflow?: boolean; + stepMismatch?: boolean; + tooLong?: boolean; + tooShort?: boolean; + typeMismatch?: boolean; + valueMissing?: boolean; +} + +interface VideoColorSpaceInit { + fullRange?: boolean | null; + matrix?: VideoMatrixCoefficients | null; + primaries?: VideoColorPrimaries | null; + transfer?: VideoTransferCharacteristics | null; +} + +interface VideoConfiguration { + bitrate: number; + colorGamut?: ColorGamut; + contentType: string; + framerate: number; + hasAlphaChannel?: boolean; + hdrMetadataType?: HdrMetadataType; + height: number; + scalabilityMode?: string; + transferFunction?: TransferFunction; + width: number; +} + +interface VideoDecoderConfig { + codec: string; + codedHeight?: number; + codedWidth?: number; + colorSpace?: VideoColorSpaceInit; + description?: AllowSharedBufferSource; + displayAspectHeight?: number; + displayAspectWidth?: number; + hardwareAcceleration?: HardwareAcceleration; + optimizeForLatency?: boolean; +} + +interface VideoDecoderInit { + error: WebCodecsErrorCallback; + output: VideoFrameOutputCallback; +} + +interface VideoDecoderSupport { + config?: VideoDecoderConfig; + supported?: boolean; +} + +interface VideoEncoderConfig { + alpha?: AlphaOption; + avc?: AvcEncoderConfig; + bitrate?: number; + bitrateMode?: VideoEncoderBitrateMode; + codec: string; + contentHint?: string; + displayHeight?: number; + displayWidth?: number; + framerate?: number; + hardwareAcceleration?: HardwareAcceleration; + height: number; + latencyMode?: LatencyMode; + scalabilityMode?: string; + width: number; +} + +interface VideoEncoderEncodeOptions { + avc?: VideoEncoderEncodeOptionsForAvc; + keyFrame?: boolean; +} + +interface VideoEncoderEncodeOptionsForAvc { + quantizer?: number | null; +} + +interface VideoEncoderInit { + error: WebCodecsErrorCallback; + output: EncodedVideoChunkOutputCallback; +} + +interface VideoEncoderSupport { + config?: VideoEncoderConfig; + supported?: boolean; +} + +interface VideoFrameBufferInit { + codedHeight: number; + codedWidth: number; + colorSpace?: VideoColorSpaceInit; + displayHeight?: number; + displayWidth?: number; + duration?: number; + format: VideoPixelFormat; + layout?: PlaneLayout[]; + timestamp: number; + visibleRect?: DOMRectInit; +} + +interface VideoFrameCallbackMetadata { + captureTime?: DOMHighResTimeStamp; + expectedDisplayTime: DOMHighResTimeStamp; + height: number; + mediaTime: number; + presentationTime: DOMHighResTimeStamp; + presentedFrames: number; + processingDuration?: number; + receiveTime?: DOMHighResTimeStamp; + rtpTimestamp?: number; + width: number; +} + +interface VideoFrameCopyToOptions { + colorSpace?: PredefinedColorSpace; + format?: VideoPixelFormat; + layout?: PlaneLayout[]; + rect?: DOMRectInit; +} + +interface VideoFrameInit { + alpha?: AlphaOption; + displayHeight?: number; + displayWidth?: number; + duration?: number; + timestamp?: number; + visibleRect?: DOMRectInit; +} + +interface ViewTimelineOptions { + axis?: ScrollAxis; + inset?: string | (CSSNumericValue | CSSKeywordValue)[]; + subject?: Element; +} + +interface WaveShaperOptions extends AudioNodeOptions { + curve?: number[] | Float32Array; + oversample?: OverSampleType; +} + +interface WebGLContextAttributes { + alpha?: boolean; + antialias?: boolean; + depth?: boolean; + desynchronized?: boolean; + failIfMajorPerformanceCaveat?: boolean; + powerPreference?: WebGLPowerPreference; + premultipliedAlpha?: boolean; + preserveDrawingBuffer?: boolean; + stencil?: boolean; + xrCompatible?: boolean; +} + +interface WebGLContextEventInit extends EventInit { + statusMessage?: string; +} + +interface WebTransportCloseInfo { + closeCode?: number; + reason?: string; +} + +interface WebTransportErrorOptions { + source?: WebTransportErrorSource; + streamErrorCode?: number | null; +} + +interface WebTransportHash { + algorithm: string; + value: BufferSource; +} + +interface WebTransportOptions { + allowPooling?: boolean; + congestionControl?: WebTransportCongestionControl; + protocols?: string[]; + requireUnreliable?: boolean; + serverCertificateHashes?: WebTransportHash[]; +} + +interface WebTransportSendOptions { + sendOrder?: number; +} + +interface WebTransportSendStreamOptions extends WebTransportSendOptions { +} + +interface WheelEventInit extends MouseEventInit { + deltaMode?: number; + deltaX?: number; + deltaY?: number; + deltaZ?: number; +} + +interface WindowPostMessageOptions extends StructuredSerializeOptions { + targetOrigin?: string; +} + +interface WorkerOptions { + credentials?: RequestCredentials; + name?: string; + type?: WorkerType; +} + +interface WorkletOptions { + credentials?: RequestCredentials; +} + +interface WriteParams { + data?: BufferSource | Blob | string | null; + position?: number | null; + size?: number | null; + type: WriteCommandType; +} + +type NodeFilter = ((node: Node) => number) | { acceptNode(node: Node): number; }; + +declare var NodeFilter: { + readonly FILTER_ACCEPT: 1; + readonly FILTER_REJECT: 2; + readonly FILTER_SKIP: 3; + readonly SHOW_ALL: 0xFFFFFFFF; + readonly SHOW_ELEMENT: 0x1; + readonly SHOW_ATTRIBUTE: 0x2; + readonly SHOW_TEXT: 0x4; + readonly SHOW_CDATA_SECTION: 0x8; + readonly SHOW_ENTITY_REFERENCE: 0x10; + readonly SHOW_ENTITY: 0x20; + readonly SHOW_PROCESSING_INSTRUCTION: 0x40; + readonly SHOW_COMMENT: 0x80; + readonly SHOW_DOCUMENT: 0x100; + readonly SHOW_DOCUMENT_TYPE: 0x200; + readonly SHOW_DOCUMENT_FRAGMENT: 0x400; + readonly SHOW_NOTATION: 0x800; +}; + +type XPathNSResolver = ((prefix: string | null) => string | null) | { lookupNamespaceURI(prefix: string | null): string | null; }; + +/** + * The **`ANGLE_instanced_arrays`** extension is part of the WebGL API and allows to draw the same object, or groups of similar objects multiple times, if they share the same vertex data, primitive count and type. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ANGLE_instanced_arrays) + */ +interface ANGLE_instanced_arrays { + /** + * The **`ANGLE_instanced_arrays.drawArraysInstancedANGLE()`** method of the WebGL API renders primitives from array data like the gl.drawArrays() method. In addition, it can execute multiple instances of the range of elements. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ANGLE_instanced_arrays/drawArraysInstancedANGLE) + */ + drawArraysInstancedANGLE(mode: GLenum, first: GLint, count: GLsizei, primcount: GLsizei): void; + /** + * The **`ANGLE_instanced_arrays.drawElementsInstancedANGLE()`** method of the WebGL API renders primitives from array data like the gl.drawElements() method. In addition, it can execute multiple instances of a set of elements. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ANGLE_instanced_arrays/drawElementsInstancedANGLE) + */ + drawElementsInstancedANGLE(mode: GLenum, count: GLsizei, type: GLenum, offset: GLintptr, primcount: GLsizei): void; + /** + * The **`ANGLE_instanced_arrays.vertexAttribDivisorANGLE()`** method of the WebGL API modifies the rate at which generic vertex attributes advance when rendering multiple instances of primitives with ext.drawArraysInstancedANGLE() and ext.drawElementsInstancedANGLE(). + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ANGLE_instanced_arrays/vertexAttribDivisorANGLE) + */ + vertexAttribDivisorANGLE(index: GLuint, divisor: GLuint): void; + readonly VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE: 0x88FE; +} + +interface ARIAMixin { + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaActiveDescendantElement) */ + ariaActiveDescendantElement: Element | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaAtomic) */ + ariaAtomic: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaAutoComplete) */ + ariaAutoComplete: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaBrailleLabel) */ + ariaBrailleLabel: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaBrailleRoleDescription) */ + ariaBrailleRoleDescription: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaBusy) */ + ariaBusy: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaChecked) */ + ariaChecked: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaColCount) */ + ariaColCount: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaColIndex) */ + ariaColIndex: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaColIndexText) */ + ariaColIndexText: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaColSpan) */ + ariaColSpan: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaControlsElements) */ + ariaControlsElements: ReadonlyArray | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaCurrent) */ + ariaCurrent: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaDescribedByElements) */ + ariaDescribedByElements: ReadonlyArray | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaDescription) */ + ariaDescription: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaDetailsElements) */ + ariaDetailsElements: ReadonlyArray | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaDisabled) */ + ariaDisabled: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaErrorMessageElements) */ + ariaErrorMessageElements: ReadonlyArray | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaExpanded) */ + ariaExpanded: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaFlowToElements) */ + ariaFlowToElements: ReadonlyArray | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaHasPopup) */ + ariaHasPopup: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaHidden) */ + ariaHidden: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaInvalid) */ + ariaInvalid: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaKeyShortcuts) */ + ariaKeyShortcuts: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaLabel) */ + ariaLabel: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaLabelledByElements) */ + ariaLabelledByElements: ReadonlyArray | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaLevel) */ + ariaLevel: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaLive) */ + ariaLive: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaModal) */ + ariaModal: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaMultiLine) */ + ariaMultiLine: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaMultiSelectable) */ + ariaMultiSelectable: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaOrientation) */ + ariaOrientation: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaOwnsElements) */ + ariaOwnsElements: ReadonlyArray | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaPlaceholder) */ + ariaPlaceholder: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaPosInSet) */ + ariaPosInSet: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaPressed) */ + ariaPressed: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaReadOnly) */ + ariaReadOnly: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaRelevant) */ + ariaRelevant: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaRequired) */ + ariaRequired: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaRoleDescription) */ + ariaRoleDescription: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaRowCount) */ + ariaRowCount: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaRowIndex) */ + ariaRowIndex: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaRowIndexText) */ + ariaRowIndexText: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaRowSpan) */ + ariaRowSpan: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaSelected) */ + ariaSelected: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaSetSize) */ + ariaSetSize: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaSort) */ + ariaSort: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaValueMax) */ + ariaValueMax: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaValueMin) */ + ariaValueMin: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaValueNow) */ + ariaValueNow: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaValueText) */ + ariaValueText: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/role) */ + role: string | null; +} + +/** + * The **`AbortController`** interface represents a controller object that allows you to abort one or more Web requests as and when desired. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortController) + */ +interface AbortController { + /** + * The **`signal`** read-only property of the AbortController interface returns an AbortSignal object instance, which can be used to communicate with/abort an asynchronous operation as desired. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortController/signal) + */ + readonly signal: AbortSignal; + /** + * The **`abort()`** method of the AbortController interface aborts an asynchronous operation before it has completed. This is able to abort fetch requests, the consumption of any response bodies, or streams. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortController/abort) + */ + abort(reason?: any): void; +} + +declare var AbortController: { + prototype: AbortController; + new(): AbortController; +}; + +interface AbortSignalEventMap { + "abort": Event; +} + +/** + * The **`AbortSignal`** interface represents a signal object that allows you to communicate with an asynchronous operation (such as a fetch request) and abort it if required via an AbortController object. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal) + */ +interface AbortSignal extends EventTarget { + /** + * The **`aborted`** read-only property returns a value that indicates whether the asynchronous operations the signal is communicating with are aborted (true) or not (false). + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/aborted) + */ + readonly aborted: boolean; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/abort_event) */ + onabort: ((this: AbortSignal, ev: Event) => any) | null; + /** + * The **`reason`** read-only property returns a JavaScript value that indicates the abort reason. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/reason) + */ + readonly reason: any; + /** + * The **`throwIfAborted()`** method throws the signal's abort reason if the signal has been aborted; otherwise it does nothing. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/throwIfAborted) + */ + throwIfAborted(): void; + addEventListener(type: K, listener: (this: AbortSignal, ev: AbortSignalEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; + addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void; + removeEventListener(type: K, listener: (this: AbortSignal, ev: AbortSignalEventMap[K]) => any, options?: boolean | EventListenerOptions): void; + removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void; +} + +declare var AbortSignal: { + prototype: AbortSignal; + new(): AbortSignal; + /** + * The **`AbortSignal.abort()`** static method returns an AbortSignal that is already set as aborted (and which does not trigger an abort event). + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/abort_static) + */ + abort(reason?: any): AbortSignal; + /** + * The **`AbortSignal.any()`** static method takes an iterable of abort signals and returns an AbortSignal. The returned abort signal is aborted when any of the input iterable abort signals are aborted. The abort reason will be set to the reason of the first signal that is aborted. If any of the given abort signals are already aborted then so will be the returned AbortSignal. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/any_static) + */ + any(signals: AbortSignal[]): AbortSignal; + /** + * The **`AbortSignal.timeout()`** static method returns an AbortSignal that will automatically abort after a specified time. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/timeout_static) + */ + timeout(milliseconds: number): AbortSignal; +}; + +/** + * The **`AbstractRange`** abstract interface is the base class upon which all DOM range types are defined. A range is an object that indicates the start and end points of a section of content within the document. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbstractRange) + */ +interface AbstractRange { + /** + * The read-only **`collapsed`** property of the AbstractRange interface returns true if the range's start position and end position are the same. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbstractRange/collapsed) + */ + readonly collapsed: boolean; + /** + * The read-only **`endContainer`** property of the AbstractRange interface returns the Node in which the end of the range is located. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbstractRange/endContainer) + */ + readonly endContainer: Node; + /** + * The **`endOffset`** property of the AbstractRange interface returns the offset into the end node of the range's end position. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbstractRange/endOffset) + */ + readonly endOffset: number; + /** + * The read-only **`startContainer`** property of the AbstractRange interface returns the Node in which the start of the range is located. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbstractRange/startContainer) + */ + readonly startContainer: Node; + /** + * The read-only **`startOffset`** property of the AbstractRange interface returns the offset into the start node of the range's start position. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbstractRange/startOffset) + */ + readonly startOffset: number; +} + +declare var AbstractRange: { + prototype: AbstractRange; + new(): AbstractRange; +}; + +interface AbstractWorkerEventMap { + "error": ErrorEvent; +} + +interface AbstractWorker { + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/ServiceWorker/error_event) */ + onerror: ((this: AbstractWorker, ev: ErrorEvent) => any) | null; + addEventListener(type: K, listener: (this: AbstractWorker, ev: AbstractWorkerEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; + addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void; + removeEventListener(type: K, listener: (this: AbstractWorker, ev: AbstractWorkerEventMap[K]) => any, options?: boolean | EventListenerOptions): void; + removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void; +} + +/** + * The **`AnalyserNode`** interface represents a node able to provide real-time frequency and time-domain analysis information. It is an AudioNode that passes the audio stream unchanged from the input to the output, but allows you to take the generated data, process it, and create audio visualizations. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AnalyserNode) + */ +interface AnalyserNode extends AudioNode { + /** + * The **`fftSize`** property of the AnalyserNode interface is an unsigned long value and represents the window size in samples that is used when performing a Fast Fourier Transform (FFT) to get frequency domain data. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AnalyserNode/fftSize) + */ + fftSize: number; + /** + * The **`frequencyBinCount`** read-only property of the AnalyserNode interface contains the total number of data points available to AudioContext sampleRate. This is half of the value of the AnalyserNode.fftSize. The two methods' indices have a linear relationship with the frequencies they represent, between 0 and the Nyquist frequency. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AnalyserNode/frequencyBinCount) + */ + readonly frequencyBinCount: number; + /** + * The **`maxDecibels`** property of the AnalyserNode interface is a double value representing the maximum power value in the scaling range for the FFT analysis data, for conversion to unsigned byte values — basically, this specifies the maximum value for the range of results when using getByteFrequencyData(). + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AnalyserNode/maxDecibels) + */ + maxDecibels: number; + /** + * The **`minDecibels`** property of the AnalyserNode interface is a double value representing the minimum power value in the scaling range for the FFT analysis data, for conversion to unsigned byte values — basically, this specifies the minimum value for the range of results when using getByteFrequencyData(). + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AnalyserNode/minDecibels) + */ + minDecibels: number; + /** + * The **`smoothingTimeConstant`** property of the AnalyserNode interface is a double value representing the averaging constant with the last analysis frame. It's basically an average between the current buffer and the last buffer the AnalyserNode processed, and results in a much smoother set of value changes over time. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AnalyserNode/smoothingTimeConstant) + */ + smoothingTimeConstant: number; + /** + * The **`getByteFrequencyData()`** method of the AnalyserNode interface copies the current frequency data into a Uint8Array (unsigned byte array) passed into it. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AnalyserNode/getByteFrequencyData) + */ + getByteFrequencyData(array: Uint8Array): void; + /** + * The **`getByteTimeDomainData()`** method of the AnalyserNode Interface copies the current waveform, or time-domain, data into a Uint8Array (unsigned byte array) passed into it. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AnalyserNode/getByteTimeDomainData) + */ + getByteTimeDomainData(array: Uint8Array): void; + /** + * The **`getFloatFrequencyData()`** method of the AnalyserNode Interface copies the current frequency data into a Float32Array array passed into it. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AnalyserNode/getFloatFrequencyData) + */ + getFloatFrequencyData(array: Float32Array): void; + /** + * The **`getFloatTimeDomainData()`** method of the AnalyserNode Interface copies the current waveform, or time-domain, data into a Float32Array array passed into it. Each array value is a sample, the magnitude of the signal at a particular time. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AnalyserNode/getFloatTimeDomainData) + */ + getFloatTimeDomainData(array: Float32Array): void; +} + +declare var AnalyserNode: { + prototype: AnalyserNode; + new(context: BaseAudioContext, options?: AnalyserOptions): AnalyserNode; +}; + +interface Animatable { + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/animate) */ + animate(keyframes: Keyframe[] | PropertyIndexedKeyframes | null, options?: number | KeyframeAnimationOptions): Animation; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/getAnimations) */ + getAnimations(options?: GetAnimationsOptions): Animation[]; +} + +interface AnimationEventMap { + "cancel": AnimationPlaybackEvent; + "finish": AnimationPlaybackEvent; + "remove": AnimationPlaybackEvent; +} + +/** + * The **`Animation`** interface of the Web Animations API represents a single animation player and provides playback controls and a timeline for an animation node or source. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Animation) + */ +interface Animation extends EventTarget { + /** + * The **`Animation.currentTime`** property of the Web Animations API returns and sets the current time value of the animation in milliseconds, whether running or paused. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Animation/currentTime) + */ + currentTime: CSSNumberish | null; + /** + * The **`Animation.effect`** property of the Web Animations API gets and sets the target effect of an animation. The target effect may be either an effect object of a type based on AnimationEffect, such as KeyframeEffect, or null. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Animation/effect) + */ + effect: AnimationEffect | null; + /** + * The **`Animation.finished`** read-only property of the Web Animations API returns a Promise which resolves once the animation has finished playing. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Animation/finished) + */ + readonly finished: Promise; + /** + * The **`Animation.id`** property of the Web Animations API returns or sets a string used to identify the animation. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Animation/id) + */ + id: string; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Animation/cancel_event) */ + oncancel: ((this: Animation, ev: AnimationPlaybackEvent) => any) | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Animation/finish_event) */ + onfinish: ((this: Animation, ev: AnimationPlaybackEvent) => any) | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Animation/remove_event) */ + onremove: ((this: Animation, ev: AnimationPlaybackEvent) => any) | null; + /** + * The **`overallProgress`** read-only property of the Animation interface returns a number between 0 and 1 indicating the animation's overall progress towards its finished state. This is the overall progress across all of the animation's iterations, not each individual iteration. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Animation/overallProgress) + */ + readonly overallProgress: number | null; + /** + * The read-only **`Animation.pending`** property of the Web Animations API indicates whether the animation is currently waiting for an asynchronous operation such as initiating playback or pausing a running animation. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Animation/pending) + */ + readonly pending: boolean; + /** + * The read-only **`Animation.playState`** property of the Web Animations API returns an enumerated value describing the playback state of an animation. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Animation/playState) + */ + readonly playState: AnimationPlayState; + /** + * The **`Animation.playbackRate`** property of the Web Animations API returns or sets the playback rate of the animation. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Animation/playbackRate) + */ + playbackRate: number; + /** + * The read-only **`Animation.ready`** property of the Web Animations API returns a Promise which resolves when the animation is ready to play. A new promise is created every time the animation enters the "pending" play state as well as when the animation is canceled, since in both of those scenarios, the animation is ready to be started again. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Animation/ready) + */ + readonly ready: Promise; + /** + * The read-only **`Animation.replaceState`** property of the Web Animations API indicates whether the animation has been removed by the browser automatically after being replaced by another animation. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Animation/replaceState) + */ + readonly replaceState: AnimationReplaceState; + /** + * The **`Animation.startTime`** property of the Animation interface is a double-precision floating-point value which indicates the scheduled time when an animation's playback should begin. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Animation/startTime) + */ + startTime: CSSNumberish | null; + /** + * The **`Animation.timeline`** property of the Animation interface returns or sets the timeline associated with this animation. A timeline is a source of time values for synchronization purposes, and is an AnimationTimeline-based object. By default, the animation's timeline and the Document's timeline are the same. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Animation/timeline) + */ + timeline: AnimationTimeline | null; + /** + * The Web Animations API's **`cancel()`** method of the Animation interface clears all KeyframeEffects caused by this animation and aborts its playback. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Animation/cancel) + */ + cancel(): void; + /** + * The **`commitStyles()`** method of the Web Animations API's Animation interface writes the computed values of the animation's current styles into its target element's style attribute. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Animation/commitStyles) + */ + commitStyles(): void; + /** + * The **`finish()`** method of the Web Animations API's Animation Interface sets the current playback time to the end of the animation corresponding to the current playback direction. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Animation/finish) + */ + finish(): void; + /** + * The **`pause()`** method of the Web Animations API's Animation interface suspends playback of the animation. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Animation/pause) + */ + pause(): void; + /** + * The **`persist()`** method of the Web Animations API's Animation interface explicitly persists an animation, preventing it from being automatically removed when it is replaced by another animation. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Animation/persist) + */ + persist(): void; + /** + * The **`play()`** method of the Web Animations API's Animation Interface starts or resumes playing of an animation. If the animation is finished, calling play() restarts the animation, playing it from the beginning. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Animation/play) + */ + play(): void; + /** + * The **`Animation.reverse()`** method of the Animation Interface reverses the playback direction, meaning the animation ends at its beginning. If called on an unplayed animation, the whole animation is played backwards. If called on a paused animation, the animation will continue in reverse. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Animation/reverse) + */ + reverse(): void; + /** + * The **`updatePlaybackRate()`** method of the Web Animations API's Animation Interface sets the speed of an animation after first synchronizing its playback position. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Animation/updatePlaybackRate) + */ + updatePlaybackRate(playbackRate: number): void; + addEventListener(type: K, listener: (this: Animation, ev: AnimationEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; + addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void; + removeEventListener(type: K, listener: (this: Animation, ev: AnimationEventMap[K]) => any, options?: boolean | EventListenerOptions): void; + removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void; +} + +declare var Animation: { + prototype: Animation; + new(effect?: AnimationEffect | null, timeline?: AnimationTimeline | null): Animation; +}; + +/** + * The **`AnimationEffect`** interface of the Web Animations API is an interface representing animation effects. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AnimationEffect) + */ +interface AnimationEffect { + /** + * The **`getComputedTiming()`** method of the AnimationEffect interface returns the calculated timing properties for this animation effect. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AnimationEffect/getComputedTiming) + */ + getComputedTiming(): ComputedEffectTiming; + /** + * The **`AnimationEffect.getTiming()`** method of the AnimationEffect interface returns an object containing the timing properties for the Animation Effect. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AnimationEffect/getTiming) + */ + getTiming(): EffectTiming; + /** + * The **`updateTiming()`** method of the AnimationEffect interface updates the specified timing properties for an animation effect. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AnimationEffect/updateTiming) + */ + updateTiming(timing?: OptionalEffectTiming): void; +} + +declare var AnimationEffect: { + prototype: AnimationEffect; + new(): AnimationEffect; +}; + +/** + * The **`AnimationEvent`** interface represents events providing information related to animations. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AnimationEvent) + */ +interface AnimationEvent extends Event { + /** + * The **`AnimationEvent.animationName`** read-only property is a string containing the value of the animation-name CSS property associated with the transition. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AnimationEvent/animationName) + */ + readonly animationName: string; + /** + * The **`AnimationEvent.elapsedTime`** read-only property is a float giving the amount of time the animation has been running, in seconds, when this event fired, excluding any time the animation was paused. For an animationstart event, elapsedTime is 0.0 unless there was a negative value for animation-delay, in which case the event will be fired with elapsedTime containing (-1 * delay). + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AnimationEvent/elapsedTime) + */ + readonly elapsedTime: number; + /** + * The **`AnimationEvent.pseudoElement`** read-only property is a string, starting with '::', containing the name of the pseudo-element the animation runs on. If the animation doesn't run on a pseudo-element but on the element, an empty string: ''. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AnimationEvent/pseudoElement) + */ + readonly pseudoElement: string; +} + +declare var AnimationEvent: { + prototype: AnimationEvent; + new(type: string, animationEventInitDict?: AnimationEventInit): AnimationEvent; +}; + +interface AnimationFrameProvider { + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/DedicatedWorkerGlobalScope/cancelAnimationFrame) */ + cancelAnimationFrame(handle: number): void; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/DedicatedWorkerGlobalScope/requestAnimationFrame) */ + requestAnimationFrame(callback: FrameRequestCallback): number; +} + +/** + * The **`AnimationPlaybackEvent`** interface of the Web Animations API represents animation events. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AnimationPlaybackEvent) + */ +interface AnimationPlaybackEvent extends Event { + /** + * The **`currentTime`** read-only property of the AnimationPlaybackEvent interface represents the current time of the animation that generated the event at the moment the event is queued. This will be unresolved if the animation was idle at the time the event was generated. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AnimationPlaybackEvent/currentTime) + */ + readonly currentTime: CSSNumberish | null; + /** + * The **`timelineTime`** read-only property of the AnimationPlaybackEvent interface represents the time value of the animation's timeline at the moment the event is queued. This will be unresolved if the animation was not associated with a timeline at the time the event was generated or if the associated timeline was inactive. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AnimationPlaybackEvent/timelineTime) + */ + readonly timelineTime: CSSNumberish | null; +} + +declare var AnimationPlaybackEvent: { + prototype: AnimationPlaybackEvent; + new(type: string, eventInitDict?: AnimationPlaybackEventInit): AnimationPlaybackEvent; +}; + +/** + * The **`AnimationTimeline`** interface of the Web Animations API represents the timeline of an animation. This interface exists to define timeline features, inherited by other timeline types: + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AnimationTimeline) + */ +interface AnimationTimeline { + /** + * The **`currentTime`** read-only property of the Web Animations API's AnimationTimeline interface returns the timeline's current time in milliseconds, or null if the timeline is inactive. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AnimationTimeline/currentTime) + */ + readonly currentTime: CSSNumberish | null; + /** + * The **`duration`** read-only property of the Web Animations API's AnimationTimeline interface returns the maximum value for this timeline or null. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AnimationTimeline/duration) + */ + readonly duration: CSSNumberish | null; +} + +declare var AnimationTimeline: { + prototype: AnimationTimeline; + new(): AnimationTimeline; +}; + +/** + * The **`Attr`** interface represents one of an element's attributes as an object. In most situations, you will directly retrieve the attribute value as a string (e.g., Element.getAttribute()), but some cases may require interacting with Attr instances (e.g., Element.getAttributeNode()). + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Attr) + */ +interface Attr extends Node { + /** + * The read-only **`localName`** property of the Attr interface returns the local part of the qualified name of an attribute, that is the name of the attribute, stripped from any namespace in front of it. For example, if the qualified name is xml:lang, the returned local name is lang, if the element supports that namespace. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Attr/localName) + */ + readonly localName: string; + /** + * The read-only **`name`** property of the Attr interface returns the qualified name of an attribute, that is the name of the attribute, with the namespace prefix, if any, in front of it. For example, if the local name is lang and the namespace prefix is xml, the returned qualified name is xml:lang. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Attr/name) + */ + readonly name: string; + /** + * The read-only **`namespaceURI`** property of the Attr interface returns the namespace URI of the attribute, or null if the element is not in a namespace. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Attr/namespaceURI) + */ + readonly namespaceURI: string | null; + readonly ownerDocument: Document; + /** + * The read-only **`ownerElement`** property of the Attr interface returns the Element the attribute belongs to. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Attr/ownerElement) + */ + readonly ownerElement: Element | null; + /** + * The read-only **`prefix`** property of the Attr returns the namespace prefix of the attribute, or null if no prefix is specified. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Attr/prefix) + */ + readonly prefix: string | null; + /** + * The read-only **`specified`** property of the Attr interface always returns true. + * @deprecated + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Attr/specified) + */ + readonly specified: boolean; + /** + * The **`value`** property of the Attr interface contains the value of the attribute. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Attr/value) + */ + value: string; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Node/textContent) */ + get textContent(): string; + set textContent(value: string | null); +} + +declare var Attr: { + prototype: Attr; + new(): Attr; +}; + +/** + * The **`AudioBuffer`** interface represents a short audio asset residing in memory, created from an audio file using the AudioContext.decodeAudioData() method, or from raw data using AudioContext.createBuffer(). Once put into an AudioBuffer, the audio can then be played by being passed into an AudioBufferSourceNode. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AudioBuffer) + */ +interface AudioBuffer { + /** + * The **`duration`** property of the AudioBuffer interface returns a double representing the duration, in seconds, of the PCM data stored in the buffer. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AudioBuffer/duration) + */ + readonly duration: number; + /** + * The **`length`** property of the AudioBuffer interface returns an integer representing the length, in sample-frames, of the PCM data stored in the buffer. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AudioBuffer/length) + */ + readonly length: number; + /** + * The **`numberOfChannels`** property of the AudioBuffer interface returns an integer representing the number of discrete audio channels described by the PCM data stored in the buffer. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AudioBuffer/numberOfChannels) + */ + readonly numberOfChannels: number; + /** + * The **`sampleRate`** property of the AudioBuffer interface returns a float representing the sample rate, in samples per second, of the PCM data stored in the buffer. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AudioBuffer/sampleRate) + */ + readonly sampleRate: number; + /** + * The **`copyFromChannel()`** method of the AudioBuffer interface copies the audio sample data from the specified channel of the AudioBuffer to a specified Float32Array. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AudioBuffer/copyFromChannel) + */ + copyFromChannel(destination: Float32Array, channelNumber: number, bufferOffset?: number): void; + /** + * The **`copyToChannel()`** method of the AudioBuffer interface copies the samples to the specified channel of the AudioBuffer, from the source array. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AudioBuffer/copyToChannel) + */ + copyToChannel(source: Float32Array, channelNumber: number, bufferOffset?: number): void; + /** + * The **`getChannelData()`** method of the AudioBuffer Interface returns a Float32Array containing the PCM data associated with the channel, defined by the channel parameter (with 0 representing the first channel). + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AudioBuffer/getChannelData) + */ + getChannelData(channel: number): Float32Array; +} + +declare var AudioBuffer: { + prototype: AudioBuffer; + new(options: AudioBufferOptions): AudioBuffer; +}; + +/** + * The **`AudioBufferSourceNode`** interface is an AudioScheduledSourceNode which represents an audio source consisting of in-memory audio data, stored in an AudioBuffer. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AudioBufferSourceNode) + */ +interface AudioBufferSourceNode extends AudioScheduledSourceNode { + /** + * The **`buffer`** property of the AudioBufferSourceNode interface provides the ability to play back audio using an AudioBuffer as the source of the sound data. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AudioBufferSourceNode/buffer) + */ + buffer: AudioBuffer | null; + /** + * The **`detune`** property of the AudioBufferSourceNode interface is a k-rate AudioParam representing detuning of oscillation in cents. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AudioBufferSourceNode/detune) + */ + readonly detune: AudioParam; + /** + * The **`loop`** property of the AudioBufferSourceNode interface is a Boolean indicating if the audio asset must be replayed when the end of the AudioBuffer is reached. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AudioBufferSourceNode/loop) + */ + loop: boolean; + /** + * The **`loopEnd`** property of the AudioBufferSourceNode interface specifies is a floating point number specifying, in seconds, at what offset into playing the AudioBuffer playback should loop back to the time indicated by the loopStart property. This is only used if the loop property is true. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AudioBufferSourceNode/loopEnd) + */ + loopEnd: number; + /** + * The **`loopStart`** property of the AudioBufferSourceNode interface is a floating-point value indicating, in seconds, where in the AudioBuffer the restart of the play must happen. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AudioBufferSourceNode/loopStart) + */ + loopStart: number; + /** + * The **`playbackRate`** property of the AudioBufferSourceNode interface Is a k-rate AudioParam that defines the speed at which the audio asset will be played. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AudioBufferSourceNode/playbackRate) + */ + readonly playbackRate: AudioParam; + /** + * The **`start()`** method of the AudioBufferSourceNode Interface is used to schedule playback of the audio data contained in the buffer, or to begin playback immediately. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AudioBufferSourceNode/start) + */ + start(when?: number, offset?: number, duration?: number): void; + addEventListener(type: K, listener: (this: AudioBufferSourceNode, ev: AudioScheduledSourceNodeEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; + addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void; + removeEventListener(type: K, listener: (this: AudioBufferSourceNode, ev: AudioScheduledSourceNodeEventMap[K]) => any, options?: boolean | EventListenerOptions): void; + removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void; +} + +declare var AudioBufferSourceNode: { + prototype: AudioBufferSourceNode; + new(context: BaseAudioContext, options?: AudioBufferSourceOptions): AudioBufferSourceNode; +}; + +/** + * The **`AudioContext`** interface represents an audio-processing graph built from audio modules linked together, each represented by an AudioNode. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AudioContext) + */ +interface AudioContext extends BaseAudioContext { + /** + * The **`baseLatency`** read-only property of the AudioContext interface returns a double that represents the number of seconds of processing latency incurred by the AudioContext passing an audio buffer from the AudioDestinationNode — i.e., the end of the audio graph — into the host system's audio subsystem ready for playing. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AudioContext/baseLatency) + */ + readonly baseLatency: number; + /** + * The **`outputLatency`** read-only property of the AudioContext Interface provides an estimation of the output latency of the current audio context. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AudioContext/outputLatency) + */ + readonly outputLatency: number; + /** + * The **`close()`** method of the AudioContext Interface closes the audio context, releasing any system audio resources that it uses. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AudioContext/close) + */ + close(): Promise; + /** + * The **`createMediaElementSource()`** method of the AudioContext Interface is used to create a new MediaElementAudioSourceNode object, given an existing HTML