220 lines
9.7 KiB
Go
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
|
|
}
|