2025-10-15 10:12:44 +03:00

220 lines
9.7 KiB
Go

package declarations
import (
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/checker"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/core"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/diagnostics"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/modulespecifiers"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/printer"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/scanner"
)
type SymbolTrackerImpl struct {
resolver printer.EmitResolver
state *SymbolTrackerSharedState
host DeclarationEmitHost
fallbackStack []*ast.Node
}
// GetModuleSpecifierGenerationHost implements checker.SymbolTracker.
func (s *SymbolTrackerImpl) GetModuleSpecifierGenerationHost() modulespecifiers.ModuleSpecifierGenerationHost {
return s.host
}
// PopErrorFallbackNode implements checker.SymbolTracker.
func (s *SymbolTrackerImpl) PopErrorFallbackNode() {
s.fallbackStack = s.fallbackStack[:len(s.fallbackStack)-1]
}
// PushErrorFallbackNode implements checker.SymbolTracker.
func (s *SymbolTrackerImpl) PushErrorFallbackNode(node *ast.Node) {
s.fallbackStack = append(s.fallbackStack, node)
}
// ReportCyclicStructureError implements checker.SymbolTracker.
func (s *SymbolTrackerImpl) ReportCyclicStructureError() {
location := s.errorLocation()
if location != nil {
s.state.addDiagnostic(createDiagnosticForNode(location, diagnostics.The_inferred_type_of_0_references_a_type_with_a_cyclic_structure_which_cannot_be_trivially_serialized_A_type_annotation_is_necessary, s.errorDeclarationNameWithFallback()))
}
}
// ReportInaccessibleThisError implements checker.SymbolTracker.
func (s *SymbolTrackerImpl) ReportInaccessibleThisError() {
location := s.errorLocation()
if location != nil {
s.state.addDiagnostic(createDiagnosticForNode(location, diagnostics.The_inferred_type_of_0_references_an_inaccessible_1_type_A_type_annotation_is_necessary, s.errorDeclarationNameWithFallback(), "this"))
}
}
// ReportInaccessibleUniqueSymbolError implements checker.SymbolTracker.
func (s *SymbolTrackerImpl) ReportInaccessibleUniqueSymbolError() {
location := s.errorLocation()
if location != nil {
s.state.addDiagnostic(createDiagnosticForNode(location, diagnostics.The_inferred_type_of_0_references_an_inaccessible_1_type_A_type_annotation_is_necessary, s.errorDeclarationNameWithFallback(), "unique symbol"))
}
}
// ReportInferenceFallback implements checker.SymbolTracker.
func (s *SymbolTrackerImpl) ReportInferenceFallback(node *ast.Node) {
if s.state.isolatedDeclarations || ast.IsSourceFileJS(s.state.currentSourceFile) {
return
}
if ast.GetSourceFileOfNode(node) != s.state.currentSourceFile {
return // Nested error on a declaration in another file - ignore, will be reemitted if file is in the output file set
}
if ast.IsVariableDeclaration(node) && s.state.resolver.IsExpandoFunctionDeclaration(node) {
s.state.reportExpandoFunctionErrors(node)
} else {
// !!! isolatedDeclaration support
// s.state.addDiagnostic(getIsolatedDeclarationError(node))
}
}
// ReportLikelyUnsafeImportRequiredError implements checker.SymbolTracker.
func (s *SymbolTrackerImpl) ReportLikelyUnsafeImportRequiredError(specifier string) {
location := s.errorLocation()
if location != nil {
s.state.addDiagnostic(createDiagnosticForNode(location, diagnostics.The_inferred_type_of_0_cannot_be_named_without_a_reference_to_1_This_is_likely_not_portable_A_type_annotation_is_necessary, s.errorDeclarationNameWithFallback(), specifier))
}
}
// ReportNonSerializableProperty implements checker.SymbolTracker.
func (s *SymbolTrackerImpl) ReportNonSerializableProperty(propertyName string) {
location := s.errorLocation()
if location != nil {
s.state.addDiagnostic(createDiagnosticForNode(location, diagnostics.The_type_of_this_node_cannot_be_serialized_because_its_property_0_cannot_be_serialized, propertyName))
}
}
// ReportNonlocalAugmentation implements checker.SymbolTracker.
func (s *SymbolTrackerImpl) ReportNonlocalAugmentation(containingFile *ast.SourceFile, parentSymbol *ast.Symbol, augmentingSymbol *ast.Symbol) {
primaryDeclaration := core.Find(parentSymbol.Declarations, func(d *ast.Node) bool { return ast.GetSourceFileOfNode(d) == containingFile })
augmentingDeclarations := core.Filter(augmentingSymbol.Declarations, func(d *ast.Node) bool { return ast.GetSourceFileOfNode(d) != containingFile })
if primaryDeclaration != nil && len(augmentingDeclarations) > 0 {
for _, augmentations := range augmentingDeclarations {
diag := createDiagnosticForNode(augmentations, diagnostics.Declaration_augments_declaration_in_another_file_This_cannot_be_serialized)
related := createDiagnosticForNode(primaryDeclaration, diagnostics.This_is_the_declaration_being_augmented_Consider_moving_the_augmenting_declaration_into_the_same_file)
diag.AddRelatedInfo(related)
s.state.addDiagnostic(diag)
}
}
}
// ReportPrivateInBaseOfClassExpression implements checker.SymbolTracker.
func (s *SymbolTrackerImpl) ReportPrivateInBaseOfClassExpression(propertyName string) {
location := s.errorLocation()
if location != nil {
diag := createDiagnosticForNode(location, diagnostics.Property_0_of_exported_anonymous_class_type_may_not_be_private_or_protected, propertyName)
if ast.IsVariableDeclaration(location.Parent) {
related := createDiagnosticForNode(location, diagnostics.Add_a_type_annotation_to_the_variable_0, s.errorDeclarationNameWithFallback())
diag.AddRelatedInfo(related)
}
s.state.addDiagnostic(diag)
}
}
// ReportTruncationError implements checker.SymbolTracker.
func (s *SymbolTrackerImpl) ReportTruncationError() {
location := s.errorLocation()
if location != nil {
s.state.addDiagnostic(createDiagnosticForNode(location, diagnostics.The_inferred_type_of_this_node_exceeds_the_maximum_length_the_compiler_will_serialize_An_explicit_type_annotation_is_needed))
}
}
func (s *SymbolTrackerImpl) errorFallbackNode() *ast.Node {
if len(s.fallbackStack) >= 1 {
return s.fallbackStack[len(s.fallbackStack)-1]
}
return nil
}
func (s *SymbolTrackerImpl) errorLocation() *ast.Node {
location := s.state.errorNameNode
if location == nil {
location = s.errorFallbackNode()
}
return location
}
func (s *SymbolTrackerImpl) errorDeclarationNameWithFallback() string {
if s.state.errorNameNode != nil {
return scanner.DeclarationNameToString(s.state.errorNameNode)
}
if s.errorFallbackNode() != nil && ast.GetNameOfDeclaration(s.errorFallbackNode()) != nil {
return scanner.DeclarationNameToString(ast.GetNameOfDeclaration(s.errorFallbackNode()))
}
if s.errorFallbackNode() != nil && ast.IsExportAssignment(s.errorFallbackNode()) {
if s.errorFallbackNode().AsExportAssignment().IsExportEquals {
return "export="
}
return "default"
}
return "(Missing)" // same fallback declarationNameToString uses when node is zero-width (ie, nameless)
}
// TrackSymbol implements checker.SymbolTracker.
func (s *SymbolTrackerImpl) TrackSymbol(symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags) bool {
if symbol.Flags&ast.SymbolFlagsTypeParameter != 0 {
return false
}
issuedDiagnostic := s.handleSymbolAccessibilityError(s.resolver.IsSymbolAccessible(symbol, enclosingDeclaration, meaning /*shouldComputeAliasToMarkVisible*/, true))
return issuedDiagnostic
}
func (s *SymbolTrackerImpl) handleSymbolAccessibilityError(symbolAccessibilityResult printer.SymbolAccessibilityResult) bool {
if symbolAccessibilityResult.Accessibility == printer.SymbolAccessibilityAccessible {
// Add aliases back onto the possible imports list if they're not there so we can try them again with updated visibility info
if len(symbolAccessibilityResult.AliasesToMakeVisible) > 0 {
for _, ref := range symbolAccessibilityResult.AliasesToMakeVisible {
s.state.lateMarkedStatements = core.AppendIfUnique(s.state.lateMarkedStatements, ref)
}
}
// TODO: Do all these accessibility checks inside/after the first pass in the checker when declarations are enabled, if possible
// The checker should issue errors on unresolvable names, skip the declaration emit error for using a private/unreachable name for those
} else if symbolAccessibilityResult.Accessibility != printer.SymbolAccessibilityNotResolved {
// Report error
errorInfo := s.state.getSymbolAccessibilityDiagnostic(symbolAccessibilityResult)
if errorInfo != nil {
info := *errorInfo
diagNode := symbolAccessibilityResult.ErrorNode
if diagNode == nil {
diagNode = errorInfo.errorNode
}
if info.typeName != nil {
s.state.addDiagnostic(createDiagnosticForNode(diagNode, info.diagnosticMessage, scanner.GetTextOfNode(info.typeName), symbolAccessibilityResult.ErrorSymbolName, symbolAccessibilityResult.ErrorModuleName))
} else {
s.state.addDiagnostic(createDiagnosticForNode(diagNode, info.diagnosticMessage, symbolAccessibilityResult.ErrorSymbolName, symbolAccessibilityResult.ErrorModuleName))
}
return true
}
}
return false
}
func createDiagnosticForNode(node *ast.Node, message *diagnostics.Message, args ...any) *ast.Diagnostic {
return checker.NewDiagnosticForNode(node, message, args...)
}
type SymbolTrackerSharedState struct {
lateMarkedStatements []*ast.Node
diagnostics []*ast.Diagnostic
getSymbolAccessibilityDiagnostic GetSymbolAccessibilityDiagnostic
errorNameNode *ast.Node
isolatedDeclarations bool
currentSourceFile *ast.SourceFile
resolver printer.EmitResolver
reportExpandoFunctionErrors func(node *ast.Node)
}
func (s *SymbolTrackerSharedState) addDiagnostic(diag *ast.Diagnostic) {
s.diagnostics = append(s.diagnostics, diag)
}
func NewSymbolTracker(host DeclarationEmitHost, resolver printer.EmitResolver, state *SymbolTrackerSharedState) *SymbolTrackerImpl {
tracker := &SymbolTrackerImpl{host: host, resolver: resolver, state: state}
return tracker
}