vendor tsgo
This commit is contained in:
362
tools/tsgo/internal/ast/diagnostic.go
Normal file
362
tools/tsgo/internal/ast/diagnostic.go
Normal file
@@ -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())
|
||||
}
|
||||
Reference in New Issue
Block a user