1113 lines
43 KiB
Go
1113 lines
43 KiB
Go
package checker
|
|
|
|
import (
|
|
"maps"
|
|
"slices"
|
|
"sync"
|
|
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/binder"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/core"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/debug"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/evaluator"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/jsnum"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/nodebuilder"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/printer"
|
|
)
|
|
|
|
var _ printer.EmitResolver = (*emitResolver)(nil)
|
|
|
|
// Links for jsx
|
|
type JSXLinks struct {
|
|
importRef *ast.Node
|
|
}
|
|
|
|
// Links for declarations
|
|
|
|
type DeclarationLinks struct {
|
|
isVisible core.Tristate // if declaration is depended upon by exported declarations
|
|
}
|
|
|
|
type DeclarationFileLinks struct {
|
|
aliasesMarked bool // if file has had alias visibility marked
|
|
}
|
|
|
|
type emitResolver struct {
|
|
checker *Checker
|
|
checkerMu sync.Mutex
|
|
isValueAliasDeclaration func(node *ast.Node) bool
|
|
aliasMarkingVisitor func(node *ast.Node) bool
|
|
referenceResolver binder.ReferenceResolver
|
|
jsxLinks core.LinkStore[*ast.Node, JSXLinks]
|
|
declarationLinks core.LinkStore[*ast.Node, DeclarationLinks]
|
|
declarationFileLinks core.LinkStore[*ast.Node, DeclarationFileLinks]
|
|
}
|
|
|
|
func newEmitResolver(checker *Checker) *emitResolver {
|
|
e := &emitResolver{checker: checker}
|
|
e.isValueAliasDeclaration = e.isValueAliasDeclarationWorker
|
|
e.aliasMarkingVisitor = e.aliasMarkingVisitorWorker
|
|
return e
|
|
}
|
|
|
|
func (r *emitResolver) GetJsxFactoryEntity(location *ast.Node) *ast.Node {
|
|
r.checkerMu.Lock()
|
|
defer r.checkerMu.Unlock()
|
|
return r.checker.getJsxFactoryEntity(location)
|
|
}
|
|
|
|
func (r *emitResolver) GetJsxFragmentFactoryEntity(location *ast.Node) *ast.Node {
|
|
r.checkerMu.Lock()
|
|
defer r.checkerMu.Unlock()
|
|
return r.checker.getJsxFragmentFactoryEntity(location)
|
|
}
|
|
|
|
func (r *emitResolver) IsOptionalParameter(node *ast.Node) bool {
|
|
r.checkerMu.Lock()
|
|
defer r.checkerMu.Unlock()
|
|
return r.isOptionalParameter(node)
|
|
}
|
|
|
|
func (r *emitResolver) IsLateBound(node *ast.Node) bool {
|
|
// TODO: Require an emitContext to construct an EmitResolver, remove all emitContext arguments
|
|
// node = r.emitContext.ParseNode(node)
|
|
if node == nil {
|
|
return false
|
|
}
|
|
if !ast.IsParseTreeNode(node) {
|
|
return false
|
|
}
|
|
r.checkerMu.Lock()
|
|
defer r.checkerMu.Unlock()
|
|
symbol := r.checker.getSymbolOfDeclaration(node)
|
|
if symbol == nil {
|
|
return false
|
|
}
|
|
return symbol.CheckFlags&ast.CheckFlagsLate != 0
|
|
}
|
|
|
|
func (r *emitResolver) GetEnumMemberValue(node *ast.Node) evaluator.Result {
|
|
// node = r.emitContext.ParseNode(node)
|
|
if !ast.IsParseTreeNode(node) {
|
|
return evaluator.NewResult(nil, false, false, false)
|
|
}
|
|
r.checkerMu.Lock()
|
|
defer r.checkerMu.Unlock()
|
|
|
|
r.checker.computeEnumMemberValues(node.Parent)
|
|
if !r.checker.enumMemberLinks.Has(node) {
|
|
return evaluator.NewResult(nil, false, false, false)
|
|
}
|
|
return r.checker.enumMemberLinks.Get(node).value
|
|
}
|
|
|
|
func (r *emitResolver) IsDeclarationVisible(node *ast.Node) bool {
|
|
// Only lock on external API func to prevent deadlocks
|
|
r.checkerMu.Lock()
|
|
defer r.checkerMu.Unlock()
|
|
return r.isDeclarationVisible(node)
|
|
}
|
|
|
|
func (r *emitResolver) isDeclarationVisible(node *ast.Node) bool {
|
|
// node = r.emitContext.ParseNode(node)
|
|
if !ast.IsParseTreeNode(node) {
|
|
return false
|
|
}
|
|
if node == nil {
|
|
return false
|
|
}
|
|
|
|
links := r.declarationLinks.Get(node)
|
|
if links.isVisible == core.TSUnknown {
|
|
if r.determineIfDeclarationIsVisible(node) {
|
|
links.isVisible = core.TSTrue
|
|
} else {
|
|
links.isVisible = core.TSFalse
|
|
}
|
|
}
|
|
return links.isVisible == core.TSTrue
|
|
}
|
|
|
|
func (r *emitResolver) determineIfDeclarationIsVisible(node *ast.Node) bool {
|
|
switch node.Kind {
|
|
case ast.KindJSDocCallbackTag,
|
|
// ast.KindJSDocEnumTag, // !!! TODO: JSDoc @enum support?
|
|
ast.KindJSDocTypedefTag:
|
|
// Top-level jsdoc type aliases are considered exported
|
|
// First parent is comment node, second is hosting declaration or token; we only care about those tokens or declarations whose parent is a source file
|
|
return node.Parent != nil && node.Parent.Parent != nil && node.Parent.Parent.Parent != nil && ast.IsSourceFile(node.Parent.Parent.Parent)
|
|
case ast.KindBindingElement:
|
|
return r.isDeclarationVisible(node.Parent.Parent)
|
|
case ast.KindVariableDeclaration,
|
|
ast.KindModuleDeclaration,
|
|
ast.KindClassDeclaration,
|
|
ast.KindInterfaceDeclaration,
|
|
ast.KindTypeAliasDeclaration,
|
|
ast.KindJSTypeAliasDeclaration,
|
|
ast.KindFunctionDeclaration,
|
|
ast.KindEnumDeclaration,
|
|
ast.KindImportEqualsDeclaration:
|
|
if ast.IsVariableDeclaration(node) {
|
|
if ast.IsBindingPattern(node.Name()) &&
|
|
len(node.Name().AsBindingPattern().Elements.Nodes) == 0 {
|
|
// If the binding pattern is empty, this variable declaration is not visible
|
|
return false
|
|
}
|
|
// falls through
|
|
}
|
|
// external module augmentation is always visible
|
|
if ast.IsExternalModuleAugmentation(node) {
|
|
return true
|
|
}
|
|
parent := ast.GetDeclarationContainer(node)
|
|
// If the node is not exported or it is not ambient module element (except import declaration)
|
|
if r.checker.getCombinedModifierFlagsCached(node)&ast.ModifierFlagsExport == 0 &&
|
|
!(node.Kind != ast.KindImportEqualsDeclaration && parent.Kind != ast.KindSourceFile && parent.Flags&ast.NodeFlagsAmbient != 0) {
|
|
return ast.IsGlobalSourceFile(parent)
|
|
}
|
|
// Exported members/ambient module elements (exception import declaration) are visible if parent is visible
|
|
return r.isDeclarationVisible(parent)
|
|
|
|
case ast.KindPropertyDeclaration,
|
|
ast.KindPropertySignature,
|
|
ast.KindGetAccessor,
|
|
ast.KindSetAccessor,
|
|
ast.KindMethodDeclaration,
|
|
ast.KindMethodSignature:
|
|
if r.checker.GetEffectiveDeclarationFlags(node, ast.ModifierFlagsPrivate|ast.ModifierFlagsProtected) != 0 {
|
|
// Private/protected properties/methods are not visible
|
|
return false
|
|
}
|
|
// Public properties/methods are visible if its parents are visible, so:
|
|
return r.isDeclarationVisible(node.Parent)
|
|
|
|
case ast.KindConstructor,
|
|
ast.KindConstructSignature,
|
|
ast.KindCallSignature,
|
|
ast.KindIndexSignature,
|
|
ast.KindParameter,
|
|
ast.KindModuleBlock,
|
|
ast.KindFunctionType,
|
|
ast.KindConstructorType,
|
|
ast.KindTypeLiteral,
|
|
ast.KindTypeReference,
|
|
ast.KindArrayType,
|
|
ast.KindTupleType,
|
|
ast.KindUnionType,
|
|
ast.KindIntersectionType,
|
|
ast.KindParenthesizedType,
|
|
ast.KindNamedTupleMember:
|
|
return r.isDeclarationVisible(node.Parent)
|
|
|
|
// Default binding, import specifier and namespace import is visible
|
|
// only on demand so by default it is not visible
|
|
case ast.KindImportClause,
|
|
ast.KindNamespaceImport,
|
|
ast.KindImportSpecifier:
|
|
return false
|
|
|
|
// Type parameters are always visible
|
|
case ast.KindTypeParameter:
|
|
return true
|
|
// Source file and namespace export are always visible
|
|
case ast.KindSourceFile,
|
|
ast.KindNamespaceExportDeclaration:
|
|
return true
|
|
|
|
// Export assignments do not create name bindings outside the module
|
|
case ast.KindExportAssignment, ast.KindJSExportAssignment:
|
|
return false
|
|
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func (r *emitResolver) PrecalculateDeclarationEmitVisibility(file *ast.SourceFile) {
|
|
r.checkerMu.Lock()
|
|
defer r.checkerMu.Unlock()
|
|
if r.declarationFileLinks.Get(file.AsNode()).aliasesMarked {
|
|
return
|
|
}
|
|
r.declarationFileLinks.Get(file.AsNode()).aliasesMarked = true
|
|
// TODO: Does this even *have* to be an upfront walk? If it's not possible for a
|
|
// import a = a.b.c statement to chain into exposing a statement in a sibling scope,
|
|
// it could at least be pushed into scope entry - then it wouldn't need to be recursive.
|
|
file.AsNode().ForEachChild(r.aliasMarkingVisitor)
|
|
}
|
|
|
|
func (r *emitResolver) aliasMarkingVisitorWorker(node *ast.Node) bool {
|
|
switch node.Kind {
|
|
case ast.KindExportAssignment, ast.KindJSExportAssignment:
|
|
if node.AsExportAssignment().Expression.Kind == ast.KindIdentifier {
|
|
r.markLinkedAliases(node.Expression())
|
|
}
|
|
case ast.KindExportSpecifier:
|
|
r.markLinkedAliases(node.PropertyNameOrName())
|
|
}
|
|
return node.ForEachChild(r.aliasMarkingVisitor)
|
|
}
|
|
|
|
// Sets the isVisible link on statements the Identifier or ExportName node points at
|
|
// Follows chains of import d = a.b.c
|
|
func (r *emitResolver) markLinkedAliases(node *ast.Node) {
|
|
var exportSymbol *ast.Symbol
|
|
if node.Kind != ast.KindStringLiteral && node.Parent != nil && node.Parent.Kind == ast.KindExportAssignment {
|
|
exportSymbol = r.checker.resolveName(node, node.AsIdentifier().Text, ast.SymbolFlagsValue|ast.SymbolFlagsType|ast.SymbolFlagsNamespace|ast.SymbolFlagsAlias /*nameNotFoundMessage*/, nil /*isUse*/, false, false)
|
|
} else if node.Parent.Kind == ast.KindExportSpecifier {
|
|
exportSymbol = r.checker.getTargetOfExportSpecifier(node.Parent, ast.SymbolFlagsValue|ast.SymbolFlagsType|ast.SymbolFlagsNamespace|ast.SymbolFlagsAlias, false)
|
|
}
|
|
|
|
visited := make(map[ast.SymbolId]struct{}, 2) // guard against circular imports
|
|
for exportSymbol != nil {
|
|
_, seen := visited[ast.GetSymbolId(exportSymbol)]
|
|
if seen {
|
|
break
|
|
}
|
|
visited[ast.GetSymbolId(exportSymbol)] = struct{}{}
|
|
|
|
var nextSymbol *ast.Symbol
|
|
for _, declaration := range exportSymbol.Declarations {
|
|
r.declarationLinks.Get(declaration).isVisible = core.TSTrue
|
|
|
|
if ast.IsInternalModuleImportEqualsDeclaration(declaration) {
|
|
// Add the referenced top container visible
|
|
internalModuleReference := declaration.AsImportEqualsDeclaration().ModuleReference
|
|
firstIdentifier := ast.GetFirstIdentifier(internalModuleReference)
|
|
importSymbol := r.checker.resolveName(declaration, firstIdentifier.AsIdentifier().Text, ast.SymbolFlagsValue|ast.SymbolFlagsType|ast.SymbolFlagsNamespace|ast.SymbolFlagsAlias /*nameNotFoundMessage*/, nil /*isUse*/, false, false)
|
|
nextSymbol = importSymbol
|
|
}
|
|
}
|
|
|
|
exportSymbol = nextSymbol
|
|
}
|
|
}
|
|
|
|
func getMeaningOfEntityNameReference(entityName *ast.Node) ast.SymbolFlags {
|
|
// get symbol of the first identifier of the entityName
|
|
if entityName.Parent.Kind == ast.KindTypeQuery ||
|
|
entityName.Parent.Kind == ast.KindExpressionWithTypeArguments && !ast.IsPartOfTypeNode(entityName.Parent) ||
|
|
entityName.Parent.Kind == ast.KindComputedPropertyName ||
|
|
entityName.Parent.Kind == ast.KindTypePredicate && entityName.Parent.AsTypePredicateNode().ParameterName == entityName {
|
|
// Typeof value
|
|
return ast.SymbolFlagsValue | ast.SymbolFlagsExportValue
|
|
}
|
|
if entityName.Kind == ast.KindQualifiedName || entityName.Kind == ast.KindPropertyAccessExpression ||
|
|
entityName.Parent.Kind == ast.KindImportEqualsDeclaration ||
|
|
(entityName.Parent.Kind == ast.KindQualifiedName && entityName.Parent.AsQualifiedName().Left == entityName) ||
|
|
(entityName.Parent.Kind == ast.KindPropertyAccessExpression && (entityName.Parent.AsPropertyAccessExpression()).Expression == entityName) ||
|
|
(entityName.Parent.Kind == ast.KindElementAccessExpression && (entityName.Parent.AsElementAccessExpression()).Expression == entityName) {
|
|
// Left identifier from type reference or TypeAlias
|
|
// Entity name of the import declaration
|
|
return ast.SymbolFlagsNamespace
|
|
}
|
|
// Type Reference or TypeAlias entity = Identifier
|
|
return ast.SymbolFlagsType
|
|
}
|
|
|
|
func (r *emitResolver) IsEntityNameVisible(entityName *ast.Node, enclosingDeclaration *ast.Node) printer.SymbolAccessibilityResult {
|
|
r.checkerMu.Lock()
|
|
defer r.checkerMu.Unlock()
|
|
return r.isEntityNameVisible(entityName, enclosingDeclaration, true)
|
|
}
|
|
|
|
func (r *emitResolver) isEntityNameVisible(entityName *ast.Node, enclosingDeclaration *ast.Node, shouldComputeAliasToMakeVisible bool) printer.SymbolAccessibilityResult {
|
|
// node = r.emitContext.ParseNode(entityName)
|
|
if !ast.IsParseTreeNode(entityName) {
|
|
return printer.SymbolAccessibilityResult{Accessibility: printer.SymbolAccessibilityNotAccessible}
|
|
}
|
|
|
|
meaning := getMeaningOfEntityNameReference(entityName)
|
|
firstIdentifier := ast.GetFirstIdentifier(entityName)
|
|
|
|
symbol := r.checker.resolveName(enclosingDeclaration, firstIdentifier.Text(), meaning, nil, false, false)
|
|
|
|
if symbol != nil && symbol.Flags&ast.SymbolFlagsTypeParameter != 0 && meaning&ast.SymbolFlagsType != 0 {
|
|
return printer.SymbolAccessibilityResult{Accessibility: printer.SymbolAccessibilityAccessible}
|
|
}
|
|
|
|
if symbol == nil && ast.IsThisIdentifier(firstIdentifier) {
|
|
sym := r.checker.getSymbolOfDeclaration(r.checker.getThisContainer(firstIdentifier, false, false))
|
|
if r.isSymbolAccessible(sym, enclosingDeclaration, meaning, false).Accessibility == printer.SymbolAccessibilityAccessible {
|
|
return printer.SymbolAccessibilityResult{Accessibility: printer.SymbolAccessibilityAccessible}
|
|
}
|
|
}
|
|
|
|
if symbol == nil {
|
|
return printer.SymbolAccessibilityResult{
|
|
Accessibility: printer.SymbolAccessibilityNotResolved,
|
|
ErrorSymbolName: firstIdentifier.Text(),
|
|
ErrorNode: firstIdentifier,
|
|
}
|
|
}
|
|
|
|
visible := r.hasVisibleDeclarations(symbol, shouldComputeAliasToMakeVisible)
|
|
if visible != nil {
|
|
return *visible
|
|
}
|
|
|
|
return printer.SymbolAccessibilityResult{
|
|
Accessibility: printer.SymbolAccessibilityNotAccessible,
|
|
ErrorSymbolName: firstIdentifier.Text(),
|
|
ErrorNode: firstIdentifier,
|
|
}
|
|
}
|
|
|
|
func noopAddVisibleAlias(declaration *ast.Node, aliasingStatement *ast.Node) {}
|
|
|
|
func (r *emitResolver) hasVisibleDeclarations(symbol *ast.Symbol, shouldComputeAliasToMakeVisible bool) *printer.SymbolAccessibilityResult {
|
|
var aliasesToMakeVisibleSet map[ast.NodeId]*ast.Node
|
|
|
|
var addVisibleAlias func(declaration *ast.Node, aliasingStatement *ast.Node)
|
|
if shouldComputeAliasToMakeVisible {
|
|
addVisibleAlias = func(declaration *ast.Node, aliasingStatement *ast.Node) {
|
|
r.declarationLinks.Get(declaration).isVisible = core.TSTrue
|
|
if aliasesToMakeVisibleSet == nil {
|
|
aliasesToMakeVisibleSet = make(map[ast.NodeId]*ast.Node)
|
|
}
|
|
aliasesToMakeVisibleSet[ast.GetNodeId(declaration)] = aliasingStatement
|
|
}
|
|
} else {
|
|
addVisibleAlias = noopAddVisibleAlias
|
|
}
|
|
|
|
for _, declaration := range symbol.Declarations {
|
|
if ast.IsIdentifier(declaration) {
|
|
continue
|
|
}
|
|
|
|
if !r.isDeclarationVisible(declaration) {
|
|
// Mark the unexported alias as visible if its parent is visible
|
|
// because these kind of aliases can be used to name types in declaration file
|
|
anyImportSyntax := getAnyImportSyntax(declaration)
|
|
if anyImportSyntax != nil &&
|
|
!ast.HasSyntacticModifier(anyImportSyntax, ast.ModifierFlagsExport) && // import clause without export
|
|
r.isDeclarationVisible(anyImportSyntax.Parent) {
|
|
addVisibleAlias(declaration, anyImportSyntax)
|
|
continue
|
|
}
|
|
if ast.IsVariableDeclaration(declaration) && ast.IsVariableStatement(declaration.Parent.Parent) &&
|
|
!ast.HasSyntacticModifier(declaration.Parent.Parent, ast.ModifierFlagsExport) && // unexported variable statement
|
|
r.isDeclarationVisible(declaration.Parent.Parent.Parent) {
|
|
addVisibleAlias(declaration, declaration.Parent.Parent)
|
|
continue
|
|
}
|
|
if ast.IsLateVisibilityPaintedStatement(declaration) && // unexported top-level statement
|
|
!ast.HasSyntacticModifier(declaration, ast.ModifierFlagsExport) &&
|
|
r.isDeclarationVisible(declaration.Parent) {
|
|
addVisibleAlias(declaration, declaration)
|
|
continue
|
|
}
|
|
if ast.IsBindingElement(declaration) {
|
|
if symbol.Flags&ast.SymbolFlagsAlias != 0 && ast.IsInJSFile(declaration) && declaration.Parent != nil && declaration.Parent.Parent != nil && // exported import-like top-level JS require statement
|
|
ast.IsVariableDeclaration(declaration.Parent.Parent) &&
|
|
declaration.Parent.Parent.Parent.Parent != nil && ast.IsVariableStatement(declaration.Parent.Parent.Parent.Parent) &&
|
|
!ast.HasSyntacticModifier(declaration.Parent.Parent.Parent.Parent, ast.ModifierFlagsExport) &&
|
|
declaration.Parent.Parent.Parent.Parent.Parent != nil && // check if the thing containing the variable statement is visible (ie, the file)
|
|
r.isDeclarationVisible(declaration.Parent.Parent.Parent.Parent.Parent) {
|
|
addVisibleAlias(declaration, declaration.Parent.Parent.Parent.Parent)
|
|
continue
|
|
}
|
|
if symbol.Flags&ast.SymbolFlagsBlockScopedVariable != 0 {
|
|
rootDeclaration := ast.WalkUpBindingElementsAndPatterns(declaration)
|
|
if ast.IsParameter(rootDeclaration) {
|
|
return nil
|
|
}
|
|
variableStatement := rootDeclaration.Parent.Parent
|
|
if !ast.IsVariableStatement(variableStatement) {
|
|
return nil
|
|
}
|
|
if ast.HasSyntacticModifier(variableStatement, ast.ModifierFlagsExport) {
|
|
continue // no alias to add, already exported
|
|
}
|
|
if !r.isDeclarationVisible(variableStatement.Parent) {
|
|
return nil // not visible
|
|
}
|
|
addVisibleAlias(declaration, variableStatement)
|
|
continue
|
|
}
|
|
}
|
|
|
|
// Declaration is not visible
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return &printer.SymbolAccessibilityResult{
|
|
Accessibility: printer.SymbolAccessibilityAccessible,
|
|
AliasesToMakeVisible: slices.Collect(maps.Values(aliasesToMakeVisibleSet)),
|
|
}
|
|
}
|
|
|
|
func (r *emitResolver) IsImplementationOfOverload(node *ast.SignatureDeclaration) bool {
|
|
// node = r.emitContext.ParseNode(node)
|
|
if !ast.IsParseTreeNode(node) {
|
|
return false
|
|
}
|
|
if ast.NodeIsPresent(node.Body()) {
|
|
if ast.IsGetAccessorDeclaration(node) || ast.IsSetAccessorDeclaration(node) {
|
|
return false // Get or set accessors can never be overload implementations, but can have up to 2 signatures
|
|
}
|
|
r.checkerMu.Lock()
|
|
defer r.checkerMu.Unlock()
|
|
symbol := r.checker.getSymbolOfDeclaration(node)
|
|
signaturesOfSymbol := r.checker.getSignaturesOfSymbol(symbol)
|
|
// If this function body corresponds to function with multiple signature, it is implementation of overload
|
|
// e.g.: function foo(a: string): string;
|
|
// function foo(a: number): number;
|
|
// function foo(a: any) { // This is implementation of the overloads
|
|
// return a;
|
|
// }
|
|
return len(signaturesOfSymbol) > 1 ||
|
|
// If there is single signature for the symbol, it is overload if that signature isn't coming from the node
|
|
// e.g.: function foo(a: string): string;
|
|
// function foo(a: any) { // This is implementation of the overloads
|
|
// return a;
|
|
// }
|
|
(len(signaturesOfSymbol) == 1 && signaturesOfSymbol[0].declaration != node)
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (r *emitResolver) IsImportRequiredByAugmentation(decl *ast.ImportDeclaration) bool {
|
|
// node = r.emitContext.ParseNode(node)
|
|
if !ast.IsParseTreeNode(decl.AsNode()) {
|
|
return false
|
|
}
|
|
file := ast.GetSourceFileOfNode(decl.AsNode())
|
|
if file.Symbol == nil {
|
|
// script file
|
|
return false
|
|
}
|
|
importTarget := r.GetExternalModuleFileFromDeclaration(decl.AsNode())
|
|
if importTarget == nil {
|
|
return false
|
|
}
|
|
if importTarget == file {
|
|
return false
|
|
}
|
|
r.checkerMu.Lock()
|
|
defer r.checkerMu.Unlock()
|
|
exports := r.checker.getExportsOfModule(file.Symbol)
|
|
for s := range maps.Values(exports) {
|
|
merged := r.checker.getMergedSymbol(s)
|
|
if merged != s {
|
|
if len(merged.Declarations) > 0 {
|
|
for _, d := range merged.Declarations {
|
|
declFile := ast.GetSourceFileOfNode(d)
|
|
if declFile == importTarget {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (r *emitResolver) IsDefinitelyReferenceToGlobalSymbolObject(node *ast.Node) bool {
|
|
if !ast.IsPropertyAccessExpression(node) ||
|
|
!ast.IsIdentifier(node.Name()) ||
|
|
!ast.IsPropertyAccessExpression(node.Expression()) && !ast.IsIdentifier(node.Expression()) {
|
|
return false
|
|
}
|
|
if node.Expression().Kind == ast.KindIdentifier {
|
|
if node.Expression().AsIdentifier().Text != "Symbol" {
|
|
return false
|
|
}
|
|
r.checkerMu.Lock()
|
|
defer r.checkerMu.Unlock()
|
|
// Exactly `Symbol.something` and `Symbol` either does not resolve or definitely resolves to the global Symbol
|
|
return r.checker.getResolvedSymbol(node.Expression()) == r.checker.getGlobalSymbol("Symbol", ast.SymbolFlagsValue|ast.SymbolFlagsExportValue, nil /*diagnostic*/)
|
|
}
|
|
if node.Expression().Expression().Kind != ast.KindIdentifier || node.Expression().Expression().AsIdentifier().Text != "globalThis" || node.Expression().Name().Text() != "Symbol" {
|
|
return false
|
|
}
|
|
r.checkerMu.Lock()
|
|
defer r.checkerMu.Unlock()
|
|
// Exactly `globalThis.Symbol.something` and `globalThis` resolves to the global `globalThis`
|
|
return r.checker.getResolvedSymbol(node.Expression().Expression()) == r.checker.globalThisSymbol
|
|
}
|
|
|
|
func (r *emitResolver) RequiresAddingImplicitUndefined(declaration *ast.Node, symbol *ast.Symbol, enclosingDeclaration *ast.Node) bool {
|
|
if !ast.IsParseTreeNode(declaration) {
|
|
return false
|
|
}
|
|
r.checkerMu.Lock()
|
|
defer r.checkerMu.Unlock()
|
|
return r.requiresAddingImplicitUndefined(declaration, symbol, enclosingDeclaration)
|
|
}
|
|
|
|
func (r *emitResolver) requiresAddingImplicitUndefined(declaration *ast.Node, symbol *ast.Symbol, enclosingDeclaration *ast.Node) bool {
|
|
// node = r.emitContext.ParseNode(node)
|
|
if !ast.IsParseTreeNode(declaration) {
|
|
return false
|
|
}
|
|
switch declaration.Kind {
|
|
case ast.KindPropertyDeclaration, ast.KindPropertySignature, ast.KindJSDocPropertyTag:
|
|
if symbol == nil {
|
|
symbol = r.checker.getSymbolOfDeclaration(declaration)
|
|
}
|
|
t := r.checker.getTypeOfSymbol(symbol)
|
|
r.checker.mappedSymbolLinks.Has(symbol)
|
|
return (symbol.Flags&ast.SymbolFlagsProperty != 0) && (symbol.Flags&ast.SymbolFlagsOptional != 0) && isOptionalDeclaration(declaration) && r.checker.ReverseMappedSymbolLinks.Has(symbol) && r.checker.ReverseMappedSymbolLinks.Get(symbol).mappedType != nil && containsNonMissingUndefinedType(r.checker, t)
|
|
case ast.KindParameter, ast.KindJSDocParameterTag:
|
|
return r.requiresAddingImplicitUndefinedWorker(declaration, enclosingDeclaration)
|
|
default:
|
|
panic("Node cannot possibly require adding undefined")
|
|
}
|
|
}
|
|
|
|
func (r *emitResolver) requiresAddingImplicitUndefinedWorker(parameter *ast.Node, enclosingDeclaration *ast.Node) bool {
|
|
return (r.isRequiredInitializedParameter(parameter, enclosingDeclaration) || r.isOptionalUninitializedParameterProperty(parameter)) && !r.declaredParameterTypeContainsUndefined(parameter)
|
|
}
|
|
|
|
func (r *emitResolver) declaredParameterTypeContainsUndefined(parameter *ast.Node) bool {
|
|
// typeNode := getNonlocalEffectiveTypeAnnotationNode(parameter); // !!! JSDoc Support
|
|
typeNode := parameter.Type()
|
|
if typeNode == nil {
|
|
return false
|
|
}
|
|
t := r.checker.getTypeFromTypeNode(typeNode)
|
|
// allow error type here to avoid confusing errors that the annotation has to contain undefined when it does in cases like this:
|
|
//
|
|
// export function fn(x?: Unresolved | undefined): void {}
|
|
return r.checker.isErrorType(t) || r.checker.containsUndefinedType(t)
|
|
}
|
|
|
|
func (r *emitResolver) isOptionalUninitializedParameterProperty(parameter *ast.Node) bool {
|
|
return r.checker.strictNullChecks &&
|
|
r.isOptionalParameter(parameter) &&
|
|
( /*isJSDocParameterTag(parameter) ||*/ parameter.Initializer() == nil) && // !!! TODO: JSDoc support
|
|
ast.HasSyntacticModifier(parameter, ast.ModifierFlagsParameterPropertyModifier)
|
|
}
|
|
|
|
func (r *emitResolver) isRequiredInitializedParameter(parameter *ast.Node, enclosingDeclaration *ast.Node) bool {
|
|
if !r.checker.strictNullChecks || r.isOptionalParameter(parameter) || /*isJSDocParameterTag(parameter) ||*/ parameter.Initializer() == nil { // !!! TODO: JSDoc Support
|
|
return false
|
|
}
|
|
if ast.HasSyntacticModifier(parameter, ast.ModifierFlagsParameterPropertyModifier) {
|
|
return enclosingDeclaration != nil && ast.IsFunctionLikeDeclaration(enclosingDeclaration)
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (r *emitResolver) isOptionalParameter(node *ast.Node) bool {
|
|
// !!! TODO: JSDoc support
|
|
// if (hasEffectiveQuestionToken(node)) {
|
|
// return true;
|
|
// }
|
|
if ast.IsParameter(node) && node.AsParameterDeclaration().QuestionToken != nil {
|
|
return true
|
|
}
|
|
if !ast.IsParameter(node) {
|
|
return false
|
|
}
|
|
if node.Initializer() != nil {
|
|
signature := r.checker.getSignatureFromDeclaration(node.Parent)
|
|
parameterIndex := core.FindIndex(node.Parent.Parameters(), func(p *ast.ParameterDeclarationNode) bool { return p == node })
|
|
debug.Assert(parameterIndex >= 0)
|
|
// Only consider syntactic or instantiated parameters as optional, not `void` parameters as this function is used
|
|
// in grammar checks and checking for `void` too early results in parameter types widening too early
|
|
// and causes some noImplicitAny errors to be lost.
|
|
return parameterIndex >= r.checker.getMinArgumentCountEx(signature, MinArgumentCountFlagsStrongArityForUntypedJS|MinArgumentCountFlagsVoidIsNonOptional)
|
|
}
|
|
iife := ast.GetImmediatelyInvokedFunctionExpression(node.Parent)
|
|
if iife != nil {
|
|
parameterIndex := core.FindIndex(node.Parent.Parameters(), func(p *ast.ParameterDeclarationNode) bool { return p == node })
|
|
return node.Type() == nil &&
|
|
node.AsParameterDeclaration().DotDotDotToken == nil &&
|
|
parameterIndex >= len(r.checker.getEffectiveCallArguments(iife))
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func (r *emitResolver) IsLiteralConstDeclaration(node *ast.Node) bool {
|
|
// node = r.emitContext.ParseNode(node)
|
|
if !ast.IsParseTreeNode(node) {
|
|
return false
|
|
}
|
|
if isDeclarationReadonly(node) || ast.IsVariableDeclaration(node) && ast.IsVarConst(node) {
|
|
r.checkerMu.Lock()
|
|
defer r.checkerMu.Unlock()
|
|
return isFreshLiteralType(r.checker.getTypeOfSymbol(r.checker.getSymbolOfDeclaration(node)))
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (r *emitResolver) IsExpandoFunctionDeclaration(node *ast.Node) bool {
|
|
// node = r.emitContext.ParseNode(node)
|
|
// !!! TODO: expando function support
|
|
return false
|
|
}
|
|
|
|
func (r *emitResolver) isSymbolAccessible(symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags, shouldComputeAliasToMarkVisible bool) printer.SymbolAccessibilityResult {
|
|
return r.checker.IsSymbolAccessible(symbol, enclosingDeclaration, meaning, shouldComputeAliasToMarkVisible)
|
|
}
|
|
|
|
func (r *emitResolver) IsSymbolAccessible(symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags, shouldComputeAliasToMarkVisible bool) printer.SymbolAccessibilityResult {
|
|
// TODO: Split into locking and non-locking API methods - only current usage is the symbol tracker, which is non-locking,
|
|
// as all tracker calls happen within a CreateX call below, which already holds a lock
|
|
// r.checkerMu.Lock()
|
|
// defer r.checkerMu.Unlock()
|
|
return r.isSymbolAccessible(symbol, enclosingDeclaration, meaning, shouldComputeAliasToMarkVisible)
|
|
}
|
|
|
|
func isConstEnumOrConstEnumOnlyModule(s *ast.Symbol) bool {
|
|
return isConstEnumSymbol(s) || s.Flags&ast.SymbolFlagsConstEnumOnlyModule != 0
|
|
}
|
|
|
|
func (r *emitResolver) IsReferencedAliasDeclaration(node *ast.Node) bool {
|
|
c := r.checker
|
|
if !c.canCollectSymbolAliasAccessibilityData || !ast.IsParseTreeNode(node) {
|
|
return true
|
|
}
|
|
|
|
r.checkerMu.Lock()
|
|
defer r.checkerMu.Unlock()
|
|
|
|
if ast.IsAliasSymbolDeclaration(node) {
|
|
if symbol := c.getSymbolOfDeclaration(node); symbol != nil {
|
|
aliasLinks := c.aliasSymbolLinks.Get(symbol)
|
|
if aliasLinks.referenced {
|
|
return true
|
|
}
|
|
target := aliasLinks.aliasTarget
|
|
if target != nil && node.ModifierFlags()&ast.ModifierFlagsExport != 0 &&
|
|
c.getSymbolFlags(target)&ast.SymbolFlagsValue != 0 &&
|
|
(c.compilerOptions.ShouldPreserveConstEnums() || !isConstEnumOrConstEnumOnlyModule(target)) {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (r *emitResolver) IsValueAliasDeclaration(node *ast.Node) bool {
|
|
c := r.checker
|
|
if !c.canCollectSymbolAliasAccessibilityData || !ast.IsParseTreeNode(node) {
|
|
return true
|
|
}
|
|
|
|
r.checkerMu.Lock()
|
|
defer r.checkerMu.Unlock()
|
|
|
|
return r.isValueAliasDeclarationWorker(node)
|
|
}
|
|
|
|
func (r *emitResolver) isValueAliasDeclarationWorker(node *ast.Node) bool {
|
|
c := r.checker
|
|
|
|
switch node.Kind {
|
|
case ast.KindImportEqualsDeclaration:
|
|
return r.isAliasResolvedToValue(c.getSymbolOfDeclaration(node), false /*excludeTypeOnlyValues*/)
|
|
case ast.KindImportClause,
|
|
ast.KindNamespaceImport,
|
|
ast.KindImportSpecifier,
|
|
ast.KindExportSpecifier:
|
|
symbol := c.getSymbolOfDeclaration(node)
|
|
return symbol != nil && r.isAliasResolvedToValue(symbol, true /*excludeTypeOnlyValues*/)
|
|
case ast.KindExportDeclaration:
|
|
exportClause := node.AsExportDeclaration().ExportClause
|
|
return exportClause != nil && (ast.IsNamespaceExport(exportClause) ||
|
|
core.Some(exportClause.AsNamedExports().Elements.Nodes, r.isValueAliasDeclaration))
|
|
case ast.KindExportAssignment, ast.KindJSExportAssignment:
|
|
if node.AsExportAssignment().Expression != nil && node.AsExportAssignment().Expression.Kind == ast.KindIdentifier {
|
|
return r.isAliasResolvedToValue(c.getSymbolOfDeclaration(node), true /*excludeTypeOnlyValues*/)
|
|
}
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (r *emitResolver) isAliasResolvedToValue(symbol *ast.Symbol, excludeTypeOnlyValues bool) bool {
|
|
c := r.checker
|
|
if symbol == nil {
|
|
return false
|
|
}
|
|
if symbol.ValueDeclaration != nil {
|
|
if container := ast.GetSourceFileOfNode(symbol.ValueDeclaration); container != nil {
|
|
fileSymbol := c.getSymbolOfDeclaration(container.AsNode())
|
|
// Ensures cjs export assignment is setup, since this symbol may point at, and merge with, the file itself.
|
|
// If we don't, the merge may not have yet occurred, and the flags check below will be missing flags that
|
|
// are added as a result of the merge.
|
|
c.resolveExternalModuleSymbol(fileSymbol, false /*dontResolveAlias*/)
|
|
}
|
|
}
|
|
target := c.getExportSymbolOfValueSymbolIfExported(c.resolveAlias(symbol))
|
|
if target == c.unknownSymbol {
|
|
return !excludeTypeOnlyValues || c.getTypeOnlyAliasDeclaration(symbol) == nil
|
|
}
|
|
// const enums and modules that contain only const enums are not considered values from the emit perspective
|
|
// unless 'preserveConstEnums' option is set to true
|
|
return c.getSymbolFlagsEx(symbol, excludeTypeOnlyValues, true /*excludeLocalMeanings*/)&ast.SymbolFlagsValue != 0 &&
|
|
(c.compilerOptions.ShouldPreserveConstEnums() ||
|
|
!isConstEnumOrConstEnumOnlyModule(target))
|
|
}
|
|
|
|
func (r *emitResolver) IsTopLevelValueImportEqualsWithEntityName(node *ast.Node) bool {
|
|
c := r.checker
|
|
if !c.canCollectSymbolAliasAccessibilityData {
|
|
return true
|
|
}
|
|
if !ast.IsParseTreeNode(node) || node.Kind != ast.KindImportEqualsDeclaration || node.Parent.Kind != ast.KindSourceFile {
|
|
return false
|
|
}
|
|
if ast.IsImportEqualsDeclaration(node) &&
|
|
(ast.NodeIsMissing(node.AsImportEqualsDeclaration().ModuleReference) || node.AsImportEqualsDeclaration().ModuleReference.Kind != ast.KindExternalModuleReference) {
|
|
return false
|
|
}
|
|
|
|
r.checkerMu.Lock()
|
|
defer r.checkerMu.Unlock()
|
|
|
|
return r.isAliasResolvedToValue(c.getSymbolOfDeclaration(node), false /*excludeTypeOnlyValues*/)
|
|
}
|
|
|
|
func (r *emitResolver) MarkLinkedReferencesRecursively(file *ast.SourceFile) {
|
|
r.checkerMu.Lock()
|
|
defer r.checkerMu.Unlock()
|
|
|
|
if file != nil {
|
|
var visit ast.Visitor
|
|
visit = func(n *ast.Node) bool {
|
|
if ast.IsImportEqualsDeclaration(n) && n.ModifierFlags()&ast.ModifierFlagsExport == 0 {
|
|
return false // These are deferred and marked in a chain when referenced
|
|
}
|
|
if ast.IsJSExportAssignment(n) {
|
|
return false
|
|
}
|
|
if ast.IsImportDeclaration(n) {
|
|
return false // likewise, these are ultimately what get marked by calls on other nodes - we want to skip them
|
|
}
|
|
r.checker.markLinkedReferences(n, ReferenceHintUnspecified, nil /*propSymbol*/, nil /*parentType*/)
|
|
n.ForEachChild(visit)
|
|
return false
|
|
}
|
|
file.ForEachChild(visit)
|
|
}
|
|
}
|
|
|
|
func (r *emitResolver) GetExternalModuleFileFromDeclaration(declaration *ast.Node) *ast.SourceFile {
|
|
if !ast.IsParseTreeNode(declaration) {
|
|
return nil
|
|
}
|
|
|
|
var specifier *ast.Node
|
|
if declaration.Kind == ast.KindModuleDeclaration {
|
|
if ast.IsStringLiteral(declaration.Name()) {
|
|
specifier = declaration.Name()
|
|
}
|
|
} else {
|
|
specifier = ast.GetExternalModuleName(declaration)
|
|
}
|
|
r.checkerMu.Lock()
|
|
defer r.checkerMu.Unlock()
|
|
moduleSymbol := r.checker.resolveExternalModuleNameWorker(specifier, specifier /*moduleNotFoundError*/, nil, false, false) // TODO: GH#18217
|
|
if moduleSymbol == nil {
|
|
return nil
|
|
}
|
|
decl := ast.GetDeclarationOfKind(moduleSymbol, ast.KindSourceFile)
|
|
if decl == nil {
|
|
return nil
|
|
}
|
|
return decl.AsSourceFile()
|
|
}
|
|
|
|
func (r *emitResolver) getReferenceResolver() binder.ReferenceResolver {
|
|
if r.referenceResolver == nil {
|
|
r.referenceResolver = binder.NewReferenceResolver(r.checker.compilerOptions, binder.ReferenceResolverHooks{
|
|
ResolveName: r.checker.resolveName,
|
|
GetResolvedSymbol: r.checker.getResolvedSymbol,
|
|
GetMergedSymbol: r.checker.getMergedSymbol,
|
|
GetParentOfSymbol: r.checker.getParentOfSymbol,
|
|
GetSymbolOfDeclaration: r.checker.getSymbolOfDeclaration,
|
|
GetTypeOnlyAliasDeclaration: r.checker.getTypeOnlyAliasDeclarationEx,
|
|
GetExportSymbolOfValueSymbolIfExported: r.checker.getExportSymbolOfValueSymbolIfExported,
|
|
GetElementAccessExpressionName: r.checker.tryGetElementAccessExpressionName,
|
|
})
|
|
}
|
|
return r.referenceResolver
|
|
}
|
|
|
|
func (r *emitResolver) GetReferencedExportContainer(node *ast.IdentifierNode, prefixLocals bool) *ast.Node /*SourceFile|ModuleDeclaration|EnumDeclaration*/ {
|
|
if !ast.IsParseTreeNode(node) {
|
|
return nil
|
|
}
|
|
|
|
r.checkerMu.Lock()
|
|
defer r.checkerMu.Unlock()
|
|
|
|
return r.getReferenceResolver().GetReferencedExportContainer(node, prefixLocals)
|
|
}
|
|
|
|
func (r *emitResolver) SetReferencedImportDeclaration(node *ast.IdentifierNode, ref *ast.Declaration) {
|
|
r.checkerMu.Lock()
|
|
defer r.checkerMu.Unlock()
|
|
r.jsxLinks.Get(node).importRef = ref
|
|
}
|
|
|
|
func (r *emitResolver) GetReferencedImportDeclaration(node *ast.IdentifierNode) *ast.Declaration {
|
|
r.checkerMu.Lock()
|
|
defer r.checkerMu.Unlock()
|
|
if !ast.IsParseTreeNode(node) {
|
|
return r.jsxLinks.Get(node).importRef
|
|
}
|
|
|
|
return r.getReferenceResolver().GetReferencedImportDeclaration(node)
|
|
}
|
|
|
|
func (r *emitResolver) GetReferencedValueDeclaration(node *ast.IdentifierNode) *ast.Declaration {
|
|
if !ast.IsParseTreeNode(node) {
|
|
return nil
|
|
}
|
|
|
|
r.checkerMu.Lock()
|
|
defer r.checkerMu.Unlock()
|
|
|
|
return r.getReferenceResolver().GetReferencedValueDeclaration(node)
|
|
}
|
|
|
|
func (r *emitResolver) GetReferencedValueDeclarations(node *ast.IdentifierNode) []*ast.Declaration {
|
|
if !ast.IsParseTreeNode(node) {
|
|
return nil
|
|
}
|
|
|
|
r.checkerMu.Lock()
|
|
defer r.checkerMu.Unlock()
|
|
|
|
return r.getReferenceResolver().GetReferencedValueDeclarations(node)
|
|
}
|
|
|
|
func (r *emitResolver) GetElementAccessExpressionName(expression *ast.ElementAccessExpression) string {
|
|
if !ast.IsParseTreeNode(expression.AsNode()) {
|
|
return ""
|
|
}
|
|
|
|
r.checkerMu.Lock()
|
|
defer r.checkerMu.Unlock()
|
|
|
|
return r.getReferenceResolver().GetElementAccessExpressionName(expression)
|
|
}
|
|
|
|
// TODO: the emit resolver being responsible for some amount of node construction is a very leaky abstraction,
|
|
// and requires giving it access to a lot of context it's otherwise not required to have, which also further complicates the API
|
|
// and likely reduces performance. There's probably some refactoring that could be done here to simplify this.
|
|
|
|
func (r *emitResolver) CreateReturnTypeOfSignatureDeclaration(emitContext *printer.EmitContext, signatureDeclaration *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node {
|
|
original := emitContext.ParseNode(signatureDeclaration)
|
|
if original == nil {
|
|
return emitContext.Factory.NewKeywordTypeNode(ast.KindAnyKeyword)
|
|
}
|
|
|
|
r.checkerMu.Lock()
|
|
defer r.checkerMu.Unlock()
|
|
requestNodeBuilder := NewNodeBuilder(r.checker, emitContext) // TODO: cache per-context
|
|
return requestNodeBuilder.SerializeReturnTypeForSignature(original, enclosingDeclaration, flags, internalFlags, tracker)
|
|
}
|
|
|
|
func (r *emitResolver) CreateTypeParametersOfSignatureDeclaration(emitContext *printer.EmitContext, signatureDeclaration *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) []*ast.Node {
|
|
original := emitContext.ParseNode(signatureDeclaration)
|
|
if original == nil {
|
|
return nil
|
|
}
|
|
|
|
r.checkerMu.Lock()
|
|
defer r.checkerMu.Unlock()
|
|
requestNodeBuilder := NewNodeBuilder(r.checker, emitContext) // TODO: cache per-context
|
|
return requestNodeBuilder.SerializeTypeParametersForSignature(original, enclosingDeclaration, flags, internalFlags, tracker)
|
|
}
|
|
|
|
func (r *emitResolver) CreateTypeOfDeclaration(emitContext *printer.EmitContext, declaration *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node {
|
|
original := emitContext.ParseNode(declaration)
|
|
if original == nil {
|
|
return emitContext.Factory.NewKeywordTypeNode(ast.KindAnyKeyword)
|
|
}
|
|
|
|
r.checkerMu.Lock()
|
|
defer r.checkerMu.Unlock()
|
|
requestNodeBuilder := NewNodeBuilder(r.checker, emitContext) // TODO: cache per-context
|
|
// // Get type of the symbol if this is the valid symbol otherwise get type at location
|
|
symbol := r.checker.getSymbolOfDeclaration(declaration)
|
|
return requestNodeBuilder.SerializeTypeForDeclaration(declaration, symbol, enclosingDeclaration, flags|nodebuilder.FlagsMultilineObjectLiterals, internalFlags, tracker)
|
|
}
|
|
|
|
func (r *emitResolver) CreateLiteralConstValue(emitContext *printer.EmitContext, node *ast.Node, tracker nodebuilder.SymbolTracker) *ast.Node {
|
|
node = emitContext.ParseNode(node)
|
|
r.checkerMu.Lock()
|
|
t := r.checker.getTypeOfSymbol(r.checker.getSymbolOfDeclaration(node))
|
|
r.checkerMu.Unlock()
|
|
if t == nil {
|
|
return nil // TODO: How!? Maybe this should be a panic. All symbols should have a type.
|
|
}
|
|
|
|
var enumResult *ast.Node
|
|
if t.flags&TypeFlagsEnumLike != 0 {
|
|
r.checkerMu.Lock()
|
|
defer r.checkerMu.Unlock()
|
|
requestNodeBuilder := NewNodeBuilder(r.checker, emitContext) // TODO: cache per-context
|
|
enumResult = requestNodeBuilder.SymbolToExpression(t.symbol, ast.SymbolFlagsValue, node, nodebuilder.FlagsNone, nodebuilder.InternalFlagsNone, tracker)
|
|
// What about regularTrueType/regularFalseType - since those aren't fresh, we never make initializers from them
|
|
// TODO: handle those if this function is ever used for more than initializers in declaration emit
|
|
} else if t == r.checker.trueType {
|
|
enumResult = emitContext.Factory.NewKeywordExpression(ast.KindTrueKeyword)
|
|
} else if t == r.checker.falseType {
|
|
enumResult = emitContext.Factory.NewKeywordExpression(ast.KindFalseKeyword)
|
|
}
|
|
if enumResult != nil {
|
|
return enumResult
|
|
}
|
|
if t.flags&TypeFlagsLiteral == 0 {
|
|
return nil // non-literal type
|
|
}
|
|
switch value := t.AsLiteralType().value.(type) {
|
|
case string:
|
|
return emitContext.Factory.NewStringLiteral(value)
|
|
case jsnum.Number:
|
|
if value.Abs() != value {
|
|
// negative
|
|
return emitContext.Factory.NewPrefixUnaryExpression(
|
|
ast.KindMinusToken,
|
|
emitContext.Factory.NewNumericLiteral(value.String()[1:]),
|
|
)
|
|
}
|
|
return emitContext.Factory.NewNumericLiteral(value.String())
|
|
case jsnum.PseudoBigInt:
|
|
return emitContext.Factory.NewBigIntLiteral(pseudoBigIntToString(value) + "n")
|
|
case bool:
|
|
kind := ast.KindFalseKeyword
|
|
if value {
|
|
kind = ast.KindTrueKeyword
|
|
}
|
|
return emitContext.Factory.NewKeywordExpression(kind)
|
|
}
|
|
panic("unhandled literal const value kind")
|
|
}
|
|
|
|
func (r *emitResolver) CreateTypeOfExpression(emitContext *printer.EmitContext, expression *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node {
|
|
expression = emitContext.ParseNode(expression)
|
|
if expression == nil {
|
|
return emitContext.Factory.NewKeywordTypeNode(ast.KindAnyKeyword)
|
|
}
|
|
|
|
r.checkerMu.Lock()
|
|
defer r.checkerMu.Unlock()
|
|
requestNodeBuilder := NewNodeBuilder(r.checker, emitContext) // TODO: cache per-context
|
|
return requestNodeBuilder.SerializeTypeForExpression(expression, enclosingDeclaration, flags|nodebuilder.FlagsMultilineObjectLiterals, internalFlags, tracker)
|
|
}
|
|
|
|
func (r *emitResolver) CreateLateBoundIndexSignatures(emitContext *printer.EmitContext, container *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) []*ast.Node {
|
|
container = emitContext.ParseNode(container)
|
|
r.checkerMu.Lock()
|
|
defer r.checkerMu.Unlock()
|
|
|
|
sym := container.Symbol()
|
|
staticInfos := r.checker.getIndexInfosOfType(r.checker.getTypeOfSymbol(sym))
|
|
instanceIndexSymbol := r.checker.getIndexSymbol(sym)
|
|
var instanceInfos []*IndexInfo
|
|
if instanceIndexSymbol != nil {
|
|
siblingSymbols := slices.Collect(maps.Values(r.checker.getMembersOfSymbol(sym)))
|
|
instanceInfos = r.checker.getIndexInfosOfIndexSymbol(instanceIndexSymbol, siblingSymbols)
|
|
}
|
|
|
|
requestNodeBuilder := NewNodeBuilder(r.checker, emitContext) // TODO: cache per-context
|
|
|
|
var result []*ast.Node
|
|
for i, infoList := range [][]*IndexInfo{staticInfos, instanceInfos} {
|
|
isStatic := true
|
|
if i > 0 {
|
|
isStatic = false
|
|
}
|
|
if len(infoList) == 0 {
|
|
continue
|
|
}
|
|
for _, info := range infoList {
|
|
if info.declaration != nil {
|
|
continue
|
|
}
|
|
if info == r.checker.anyBaseTypeIndexInfo {
|
|
continue // inherited, but looks like a late-bound signature because it has no declarations
|
|
}
|
|
if len(info.components) != 0 {
|
|
// !!! TODO: Complete late-bound index info support - getObjectLiteralIndexInfo does not yet add late bound components to index signatures
|
|
allComponentComputedNamesSerializable := enclosingDeclaration != nil && core.Every(info.components, func(c *ast.Node) bool {
|
|
return c.Name() != nil &&
|
|
ast.IsComputedPropertyName(c.Name()) &&
|
|
ast.IsEntityNameExpression(c.Name().AsComputedPropertyName().Expression) &&
|
|
r.isEntityNameVisible(c.Name().AsComputedPropertyName().Expression, enclosingDeclaration, false).Accessibility == printer.SymbolAccessibilityAccessible
|
|
})
|
|
if allComponentComputedNamesSerializable {
|
|
for _, c := range info.components {
|
|
if r.checker.hasLateBindableName(c) {
|
|
// skip late bound props that contribute to the index signature - they'll be preserved via other means
|
|
continue
|
|
}
|
|
|
|
firstIdentifier := ast.GetFirstIdentifier(c.Name().Expression())
|
|
name := r.checker.resolveName(firstIdentifier, firstIdentifier.Text(), ast.SymbolFlagsValue|ast.SymbolFlagsExportValue, nil /*nameNotFoundMessage*/, true /*isUse*/, false /*excludeGlobals*/)
|
|
if name != nil {
|
|
tracker.TrackSymbol(name, enclosingDeclaration, ast.SymbolFlagsValue)
|
|
}
|
|
|
|
mods := core.IfElse(isStatic, []*ast.Node{emitContext.Factory.NewModifier(ast.KindStaticKeyword)}, nil)
|
|
if info.isReadonly {
|
|
mods = append(mods, emitContext.Factory.NewModifier(ast.KindReadonlyKeyword))
|
|
}
|
|
|
|
decl := emitContext.Factory.NewPropertyDeclaration(
|
|
core.IfElse(mods != nil, emitContext.Factory.NewModifierList(mods), nil),
|
|
c.Name(),
|
|
c.QuestionToken(),
|
|
requestNodeBuilder.TypeToTypeNode(r.checker.getTypeOfSymbol(c.Symbol()), enclosingDeclaration, flags, internalFlags, tracker),
|
|
nil,
|
|
)
|
|
result = append(result, decl)
|
|
}
|
|
continue
|
|
}
|
|
}
|
|
node := requestNodeBuilder.IndexInfoToIndexSignatureDeclaration(info, enclosingDeclaration, flags, internalFlags, tracker)
|
|
if node != nil && isStatic {
|
|
modNodes := []*ast.Node{emitContext.Factory.NewModifier(ast.KindStaticKeyword)}
|
|
mods := node.Modifiers()
|
|
if mods != nil {
|
|
modNodes = append(modNodes, mods.Nodes...)
|
|
}
|
|
mods = emitContext.Factory.NewModifierList(modNodes)
|
|
node = emitContext.Factory.UpdateIndexSignatureDeclaration(
|
|
node.AsIndexSignatureDeclaration(),
|
|
mods,
|
|
node.ParameterList(),
|
|
node.Type(),
|
|
)
|
|
}
|
|
if node != nil {
|
|
result = append(result, node)
|
|
}
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
func (r *emitResolver) GetEffectiveDeclarationFlags(node *ast.Node, flags ast.ModifierFlags) ast.ModifierFlags {
|
|
// node = emitContext.ParseNode(node)
|
|
r.checkerMu.Lock()
|
|
defer r.checkerMu.Unlock()
|
|
return r.checker.GetEffectiveDeclarationFlags(node, flags)
|
|
}
|
|
|
|
func (r *emitResolver) GetResolutionModeOverride(node *ast.Node) core.ResolutionMode {
|
|
// node = emitContext.ParseNode(node)
|
|
r.checkerMu.Lock()
|
|
defer r.checkerMu.Unlock()
|
|
return r.checker.GetResolutionModeOverride(node.AsImportAttributes(), false)
|
|
}
|
|
|
|
func (r *emitResolver) GetConstantValue(node *ast.Node) any {
|
|
// node = emitContext.ParseNode(node)
|
|
r.checkerMu.Lock()
|
|
defer r.checkerMu.Unlock()
|
|
return r.checker.GetConstantValue(node)
|
|
}
|