diff --git a/internal/ast/ast.go b/internal/ast/ast.go index 7d77dbff0b..1cb960a3a8 100644 --- a/internal/ast/ast.go +++ b/internal/ast/ast.go @@ -1338,6 +1338,10 @@ func (n *Node) AsNotEmittedStatement() *NotEmittedStatement { return n.data.(*NotEmittedStatement) } +func (n *Node) AsNotEmittedTypeElement() *NotEmittedTypeElement { + return n.data.(*NotEmittedTypeElement) +} + func (n *Node) AsJSDoc() *JSDoc { return n.data.(*JSDoc) } @@ -3915,6 +3919,28 @@ func IsNotEmittedStatement(node *Node) bool { return node.Kind == KindNotEmittedStatement } +// NotEmittedStatement + +// Represents a type element that is elided as part of a transformation to emit comments on a +// not-emitted node. +type NotEmittedTypeElement struct { + NodeBase + TypeElementBase +} + +func (f *NodeFactory) NewNotEmittedTypeElement() *Node { + data := &NotEmittedTypeElement{} + return newNode(KindNotEmittedTypeElement, data, f.hooks) +} + +func (node *NotEmittedTypeElement) Clone(f *NodeFactory) *Node { + return cloneNode(f.NewNotEmittedTypeElement(), node.AsNode(), f.hooks) +} + +func IsNotEmittedTypeElement(node *Node) bool { + return node.Kind == KindNotEmittedTypeElement +} + // ImportEqualsDeclaration type ImportEqualsDeclaration struct { diff --git a/internal/ast/kind.go b/internal/ast/kind.go index 228c440c58..f377ffcde0 100644 --- a/internal/ast/kind.go +++ b/internal/ast/kind.go @@ -384,6 +384,7 @@ const ( KindPartiallyEmittedExpression KindCommaListExpression KindSyntheticReferenceExpression + KindNotEmittedTypeElement // Enum value count KindCount // Markers diff --git a/internal/ast/kind_stringer_generated.go b/internal/ast/kind_stringer_generated.go index 62972bad69..d081993851 100644 --- a/internal/ast/kind_stringer_generated.go +++ b/internal/ast/kind_stringer_generated.go @@ -358,12 +358,13 @@ func _() { _ = x[KindPartiallyEmittedExpression-347] _ = x[KindCommaListExpression-348] _ = x[KindSyntheticReferenceExpression-349] - _ = x[KindCount-350] + _ = x[KindNotEmittedTypeElement-350] + _ = x[KindCount-351] } -const _Kind_name = "KindUnknownKindEndOfFileKindSingleLineCommentTriviaKindMultiLineCommentTriviaKindNewLineTriviaKindWhitespaceTriviaKindConflictMarkerTriviaKindNonTextFileMarkerTriviaKindNumericLiteralKindBigIntLiteralKindStringLiteralKindJsxTextKindJsxTextAllWhiteSpacesKindRegularExpressionLiteralKindNoSubstitutionTemplateLiteralKindTemplateHeadKindTemplateMiddleKindTemplateTailKindOpenBraceTokenKindCloseBraceTokenKindOpenParenTokenKindCloseParenTokenKindOpenBracketTokenKindCloseBracketTokenKindDotTokenKindDotDotDotTokenKindSemicolonTokenKindCommaTokenKindQuestionDotTokenKindLessThanTokenKindLessThanSlashTokenKindGreaterThanTokenKindLessThanEqualsTokenKindGreaterThanEqualsTokenKindEqualsEqualsTokenKindExclamationEqualsTokenKindEqualsEqualsEqualsTokenKindExclamationEqualsEqualsTokenKindEqualsGreaterThanTokenKindPlusTokenKindMinusTokenKindAsteriskTokenKindAsteriskAsteriskTokenKindSlashTokenKindPercentTokenKindPlusPlusTokenKindMinusMinusTokenKindLessThanLessThanTokenKindGreaterThanGreaterThanTokenKindGreaterThanGreaterThanGreaterThanTokenKindAmpersandTokenKindBarTokenKindCaretTokenKindExclamationTokenKindTildeTokenKindAmpersandAmpersandTokenKindBarBarTokenKindQuestionTokenKindColonTokenKindAtTokenKindQuestionQuestionTokenKindBacktickTokenKindHashTokenKindEqualsTokenKindPlusEqualsTokenKindMinusEqualsTokenKindAsteriskEqualsTokenKindAsteriskAsteriskEqualsTokenKindSlashEqualsTokenKindPercentEqualsTokenKindLessThanLessThanEqualsTokenKindGreaterThanGreaterThanEqualsTokenKindGreaterThanGreaterThanGreaterThanEqualsTokenKindAmpersandEqualsTokenKindBarEqualsTokenKindBarBarEqualsTokenKindAmpersandAmpersandEqualsTokenKindQuestionQuestionEqualsTokenKindCaretEqualsTokenKindIdentifierKindPrivateIdentifierKindJSDocCommentTextTokenKindBreakKeywordKindCaseKeywordKindCatchKeywordKindClassKeywordKindConstKeywordKindContinueKeywordKindDebuggerKeywordKindDefaultKeywordKindDeleteKeywordKindDoKeywordKindElseKeywordKindEnumKeywordKindExportKeywordKindExtendsKeywordKindFalseKeywordKindFinallyKeywordKindForKeywordKindFunctionKeywordKindIfKeywordKindImportKeywordKindInKeywordKindInstanceOfKeywordKindNewKeywordKindNullKeywordKindReturnKeywordKindSuperKeywordKindSwitchKeywordKindThisKeywordKindThrowKeywordKindTrueKeywordKindTryKeywordKindTypeOfKeywordKindVarKeywordKindVoidKeywordKindWhileKeywordKindWithKeywordKindImplementsKeywordKindInterfaceKeywordKindLetKeywordKindPackageKeywordKindPrivateKeywordKindProtectedKeywordKindPublicKeywordKindStaticKeywordKindYieldKeywordKindAbstractKeywordKindAccessorKeywordKindAsKeywordKindAssertsKeywordKindAssertKeywordKindAnyKeywordKindAsyncKeywordKindAwaitKeywordKindBooleanKeywordKindConstructorKeywordKindDeclareKeywordKindGetKeywordKindImmediateKeywordKindInferKeywordKindIntrinsicKeywordKindIsKeywordKindKeyOfKeywordKindModuleKeywordKindNamespaceKeywordKindNeverKeywordKindOutKeywordKindReadonlyKeywordKindRequireKeywordKindNumberKeywordKindObjectKeywordKindSatisfiesKeywordKindSetKeywordKindStringKeywordKindSymbolKeywordKindTypeKeywordKindUndefinedKeywordKindUniqueKeywordKindUnknownKeywordKindUsingKeywordKindFromKeywordKindGlobalKeywordKindBigIntKeywordKindOverrideKeywordKindOfKeywordKindQualifiedNameKindComputedPropertyNameKindTypeParameterKindParameterKindDecoratorKindPropertySignatureKindPropertyDeclarationKindMethodSignatureKindMethodDeclarationKindClassStaticBlockDeclarationKindConstructorKindGetAccessorKindSetAccessorKindCallSignatureKindConstructSignatureKindIndexSignatureKindTypePredicateKindTypeReferenceKindFunctionTypeKindConstructorTypeKindTypeQueryKindTypeLiteralKindArrayTypeKindTupleTypeKindOptionalTypeKindRestTypeKindUnionTypeKindIntersectionTypeKindConditionalTypeKindInferTypeKindParenthesizedTypeKindThisTypeKindTypeOperatorKindIndexedAccessTypeKindMappedTypeKindLiteralTypeKindNamedTupleMemberKindTemplateLiteralTypeKindTemplateLiteralTypeSpanKindImportTypeKindObjectBindingPatternKindArrayBindingPatternKindBindingElementKindArrayLiteralExpressionKindObjectLiteralExpressionKindPropertyAccessExpressionKindElementAccessExpressionKindCallExpressionKindNewExpressionKindTaggedTemplateExpressionKindTypeAssertionExpressionKindParenthesizedExpressionKindFunctionExpressionKindArrowFunctionKindDeleteExpressionKindTypeOfExpressionKindVoidExpressionKindAwaitExpressionKindPrefixUnaryExpressionKindPostfixUnaryExpressionKindBinaryExpressionKindConditionalExpressionKindTemplateExpressionKindYieldExpressionKindSpreadElementKindClassExpressionKindOmittedExpressionKindExpressionWithTypeArgumentsKindAsExpressionKindNonNullExpressionKindMetaPropertyKindSyntheticExpressionKindSatisfiesExpressionKindTemplateSpanKindSemicolonClassElementKindBlockKindEmptyStatementKindVariableStatementKindExpressionStatementKindIfStatementKindDoStatementKindWhileStatementKindForStatementKindForInStatementKindForOfStatementKindContinueStatementKindBreakStatementKindReturnStatementKindWithStatementKindSwitchStatementKindLabeledStatementKindThrowStatementKindTryStatementKindDebuggerStatementKindVariableDeclarationKindVariableDeclarationListKindFunctionDeclarationKindClassDeclarationKindInterfaceDeclarationKindTypeAliasDeclarationKindEnumDeclarationKindModuleDeclarationKindModuleBlockKindCaseBlockKindNamespaceExportDeclarationKindImportEqualsDeclarationKindImportDeclarationKindImportClauseKindNamespaceImportKindNamedImportsKindImportSpecifierKindExportAssignmentKindExportDeclarationKindNamedExportsKindNamespaceExportKindExportSpecifierKindMissingDeclarationKindExternalModuleReferenceKindJsxElementKindJsxSelfClosingElementKindJsxOpeningElementKindJsxClosingElementKindJsxFragmentKindJsxOpeningFragmentKindJsxClosingFragmentKindJsxAttributeKindJsxAttributesKindJsxSpreadAttributeKindJsxExpressionKindJsxNamespacedNameKindCaseClauseKindDefaultClauseKindHeritageClauseKindCatchClauseKindImportAttributesKindImportAttributeKindPropertyAssignmentKindShorthandPropertyAssignmentKindSpreadAssignmentKindEnumMemberKindSourceFileKindBundleKindJSDocTypeExpressionKindJSDocNameReferenceKindJSDocMemberNameKindJSDocAllTypeKindJSDocNullableTypeKindJSDocNonNullableTypeKindJSDocOptionalTypeKindJSDocVariadicTypeKindJSDocKindJSDocTextKindJSDocTypeLiteralKindJSDocSignatureKindJSDocLinkKindJSDocLinkCodeKindJSDocLinkPlainKindJSDocTagKindJSDocAugmentsTagKindJSDocImplementsTagKindJSDocDeprecatedTagKindJSDocPublicTagKindJSDocPrivateTagKindJSDocProtectedTagKindJSDocReadonlyTagKindJSDocOverrideTagKindJSDocCallbackTagKindJSDocOverloadTagKindJSDocParameterTagKindJSDocReturnTagKindJSDocThisTagKindJSDocTypeTagKindJSDocTemplateTagKindJSDocTypedefTagKindJSDocSeeTagKindJSDocPropertyTagKindJSDocSatisfiesTagKindJSDocImportTagKindSyntaxListKindJSTypeAliasDeclarationKindNotEmittedStatementKindPartiallyEmittedExpressionKindCommaListExpressionKindSyntheticReferenceExpressionKindCount" +const _Kind_name = "KindUnknownKindEndOfFileKindSingleLineCommentTriviaKindMultiLineCommentTriviaKindNewLineTriviaKindWhitespaceTriviaKindConflictMarkerTriviaKindNonTextFileMarkerTriviaKindNumericLiteralKindBigIntLiteralKindStringLiteralKindJsxTextKindJsxTextAllWhiteSpacesKindRegularExpressionLiteralKindNoSubstitutionTemplateLiteralKindTemplateHeadKindTemplateMiddleKindTemplateTailKindOpenBraceTokenKindCloseBraceTokenKindOpenParenTokenKindCloseParenTokenKindOpenBracketTokenKindCloseBracketTokenKindDotTokenKindDotDotDotTokenKindSemicolonTokenKindCommaTokenKindQuestionDotTokenKindLessThanTokenKindLessThanSlashTokenKindGreaterThanTokenKindLessThanEqualsTokenKindGreaterThanEqualsTokenKindEqualsEqualsTokenKindExclamationEqualsTokenKindEqualsEqualsEqualsTokenKindExclamationEqualsEqualsTokenKindEqualsGreaterThanTokenKindPlusTokenKindMinusTokenKindAsteriskTokenKindAsteriskAsteriskTokenKindSlashTokenKindPercentTokenKindPlusPlusTokenKindMinusMinusTokenKindLessThanLessThanTokenKindGreaterThanGreaterThanTokenKindGreaterThanGreaterThanGreaterThanTokenKindAmpersandTokenKindBarTokenKindCaretTokenKindExclamationTokenKindTildeTokenKindAmpersandAmpersandTokenKindBarBarTokenKindQuestionTokenKindColonTokenKindAtTokenKindQuestionQuestionTokenKindBacktickTokenKindHashTokenKindEqualsTokenKindPlusEqualsTokenKindMinusEqualsTokenKindAsteriskEqualsTokenKindAsteriskAsteriskEqualsTokenKindSlashEqualsTokenKindPercentEqualsTokenKindLessThanLessThanEqualsTokenKindGreaterThanGreaterThanEqualsTokenKindGreaterThanGreaterThanGreaterThanEqualsTokenKindAmpersandEqualsTokenKindBarEqualsTokenKindBarBarEqualsTokenKindAmpersandAmpersandEqualsTokenKindQuestionQuestionEqualsTokenKindCaretEqualsTokenKindIdentifierKindPrivateIdentifierKindJSDocCommentTextTokenKindBreakKeywordKindCaseKeywordKindCatchKeywordKindClassKeywordKindConstKeywordKindContinueKeywordKindDebuggerKeywordKindDefaultKeywordKindDeleteKeywordKindDoKeywordKindElseKeywordKindEnumKeywordKindExportKeywordKindExtendsKeywordKindFalseKeywordKindFinallyKeywordKindForKeywordKindFunctionKeywordKindIfKeywordKindImportKeywordKindInKeywordKindInstanceOfKeywordKindNewKeywordKindNullKeywordKindReturnKeywordKindSuperKeywordKindSwitchKeywordKindThisKeywordKindThrowKeywordKindTrueKeywordKindTryKeywordKindTypeOfKeywordKindVarKeywordKindVoidKeywordKindWhileKeywordKindWithKeywordKindImplementsKeywordKindInterfaceKeywordKindLetKeywordKindPackageKeywordKindPrivateKeywordKindProtectedKeywordKindPublicKeywordKindStaticKeywordKindYieldKeywordKindAbstractKeywordKindAccessorKeywordKindAsKeywordKindAssertsKeywordKindAssertKeywordKindAnyKeywordKindAsyncKeywordKindAwaitKeywordKindBooleanKeywordKindConstructorKeywordKindDeclareKeywordKindGetKeywordKindImmediateKeywordKindInferKeywordKindIntrinsicKeywordKindIsKeywordKindKeyOfKeywordKindModuleKeywordKindNamespaceKeywordKindNeverKeywordKindOutKeywordKindReadonlyKeywordKindRequireKeywordKindNumberKeywordKindObjectKeywordKindSatisfiesKeywordKindSetKeywordKindStringKeywordKindSymbolKeywordKindTypeKeywordKindUndefinedKeywordKindUniqueKeywordKindUnknownKeywordKindUsingKeywordKindFromKeywordKindGlobalKeywordKindBigIntKeywordKindOverrideKeywordKindOfKeywordKindQualifiedNameKindComputedPropertyNameKindTypeParameterKindParameterKindDecoratorKindPropertySignatureKindPropertyDeclarationKindMethodSignatureKindMethodDeclarationKindClassStaticBlockDeclarationKindConstructorKindGetAccessorKindSetAccessorKindCallSignatureKindConstructSignatureKindIndexSignatureKindTypePredicateKindTypeReferenceKindFunctionTypeKindConstructorTypeKindTypeQueryKindTypeLiteralKindArrayTypeKindTupleTypeKindOptionalTypeKindRestTypeKindUnionTypeKindIntersectionTypeKindConditionalTypeKindInferTypeKindParenthesizedTypeKindThisTypeKindTypeOperatorKindIndexedAccessTypeKindMappedTypeKindLiteralTypeKindNamedTupleMemberKindTemplateLiteralTypeKindTemplateLiteralTypeSpanKindImportTypeKindObjectBindingPatternKindArrayBindingPatternKindBindingElementKindArrayLiteralExpressionKindObjectLiteralExpressionKindPropertyAccessExpressionKindElementAccessExpressionKindCallExpressionKindNewExpressionKindTaggedTemplateExpressionKindTypeAssertionExpressionKindParenthesizedExpressionKindFunctionExpressionKindArrowFunctionKindDeleteExpressionKindTypeOfExpressionKindVoidExpressionKindAwaitExpressionKindPrefixUnaryExpressionKindPostfixUnaryExpressionKindBinaryExpressionKindConditionalExpressionKindTemplateExpressionKindYieldExpressionKindSpreadElementKindClassExpressionKindOmittedExpressionKindExpressionWithTypeArgumentsKindAsExpressionKindNonNullExpressionKindMetaPropertyKindSyntheticExpressionKindSatisfiesExpressionKindTemplateSpanKindSemicolonClassElementKindBlockKindEmptyStatementKindVariableStatementKindExpressionStatementKindIfStatementKindDoStatementKindWhileStatementKindForStatementKindForInStatementKindForOfStatementKindContinueStatementKindBreakStatementKindReturnStatementKindWithStatementKindSwitchStatementKindLabeledStatementKindThrowStatementKindTryStatementKindDebuggerStatementKindVariableDeclarationKindVariableDeclarationListKindFunctionDeclarationKindClassDeclarationKindInterfaceDeclarationKindTypeAliasDeclarationKindEnumDeclarationKindModuleDeclarationKindModuleBlockKindCaseBlockKindNamespaceExportDeclarationKindImportEqualsDeclarationKindImportDeclarationKindImportClauseKindNamespaceImportKindNamedImportsKindImportSpecifierKindExportAssignmentKindExportDeclarationKindNamedExportsKindNamespaceExportKindExportSpecifierKindMissingDeclarationKindExternalModuleReferenceKindJsxElementKindJsxSelfClosingElementKindJsxOpeningElementKindJsxClosingElementKindJsxFragmentKindJsxOpeningFragmentKindJsxClosingFragmentKindJsxAttributeKindJsxAttributesKindJsxSpreadAttributeKindJsxExpressionKindJsxNamespacedNameKindCaseClauseKindDefaultClauseKindHeritageClauseKindCatchClauseKindImportAttributesKindImportAttributeKindPropertyAssignmentKindShorthandPropertyAssignmentKindSpreadAssignmentKindEnumMemberKindSourceFileKindBundleKindJSDocTypeExpressionKindJSDocNameReferenceKindJSDocMemberNameKindJSDocAllTypeKindJSDocNullableTypeKindJSDocNonNullableTypeKindJSDocOptionalTypeKindJSDocVariadicTypeKindJSDocKindJSDocTextKindJSDocTypeLiteralKindJSDocSignatureKindJSDocLinkKindJSDocLinkCodeKindJSDocLinkPlainKindJSDocTagKindJSDocAugmentsTagKindJSDocImplementsTagKindJSDocDeprecatedTagKindJSDocPublicTagKindJSDocPrivateTagKindJSDocProtectedTagKindJSDocReadonlyTagKindJSDocOverrideTagKindJSDocCallbackTagKindJSDocOverloadTagKindJSDocParameterTagKindJSDocReturnTagKindJSDocThisTagKindJSDocTypeTagKindJSDocTemplateTagKindJSDocTypedefTagKindJSDocSeeTagKindJSDocPropertyTagKindJSDocSatisfiesTagKindJSDocImportTagKindSyntaxListKindJSTypeAliasDeclarationKindNotEmittedStatementKindPartiallyEmittedExpressionKindCommaListExpressionKindSyntheticReferenceExpressionKindNotEmittedTypeElementKindCount" -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, 3141, 3165, 3182, 3195, 3208, 3229, 3252, 3271, 3292, 3323, 3338, 3353, 3368, 3385, 3407, 3425, 3442, 3459, 3475, 3494, 3507, 3522, 3535, 3548, 3564, 3576, 3589, 3609, 3628, 3641, 3662, 3674, 3690, 3711, 3725, 3740, 3760, 3783, 3810, 3824, 3848, 3871, 3889, 3915, 3942, 3970, 3997, 4015, 4032, 4060, 4087, 4114, 4136, 4153, 4173, 4193, 4211, 4230, 4255, 4281, 4301, 4326, 4348, 4367, 4384, 4403, 4424, 4455, 4471, 4492, 4508, 4531, 4554, 4570, 4595, 4604, 4622, 4643, 4666, 4681, 4696, 4714, 4730, 4748, 4766, 4787, 4805, 4824, 4841, 4860, 4880, 4898, 4914, 4935, 4958, 4985, 5008, 5028, 5052, 5076, 5095, 5116, 5131, 5144, 5174, 5201, 5222, 5238, 5257, 5273, 5292, 5312, 5333, 5349, 5368, 5387, 5409, 5436, 5450, 5475, 5496, 5517, 5532, 5554, 5576, 5592, 5609, 5631, 5648, 5669, 5683, 5700, 5718, 5733, 5753, 5772, 5794, 5825, 5845, 5859, 5873, 5883, 5906, 5928, 5947, 5963, 5984, 6008, 6029, 6050, 6059, 6072, 6092, 6110, 6123, 6140, 6158, 6170, 6190, 6212, 6234, 6252, 6271, 6292, 6312, 6332, 6352, 6372, 6393, 6411, 6427, 6443, 6463, 6482, 6497, 6517, 6538, 6556, 6570, 6596, 6619, 6649, 6672, 6704, 6713} +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, 3141, 3165, 3182, 3195, 3208, 3229, 3252, 3271, 3292, 3323, 3338, 3353, 3368, 3385, 3407, 3425, 3442, 3459, 3475, 3494, 3507, 3522, 3535, 3548, 3564, 3576, 3589, 3609, 3628, 3641, 3662, 3674, 3690, 3711, 3725, 3740, 3760, 3783, 3810, 3824, 3848, 3871, 3889, 3915, 3942, 3970, 3997, 4015, 4032, 4060, 4087, 4114, 4136, 4153, 4173, 4193, 4211, 4230, 4255, 4281, 4301, 4326, 4348, 4367, 4384, 4403, 4424, 4455, 4471, 4492, 4508, 4531, 4554, 4570, 4595, 4604, 4622, 4643, 4666, 4681, 4696, 4714, 4730, 4748, 4766, 4787, 4805, 4824, 4841, 4860, 4880, 4898, 4914, 4935, 4958, 4985, 5008, 5028, 5052, 5076, 5095, 5116, 5131, 5144, 5174, 5201, 5222, 5238, 5257, 5273, 5292, 5312, 5333, 5349, 5368, 5387, 5409, 5436, 5450, 5475, 5496, 5517, 5532, 5554, 5576, 5592, 5609, 5631, 5648, 5669, 5683, 5700, 5718, 5733, 5753, 5772, 5794, 5825, 5845, 5859, 5873, 5883, 5906, 5928, 5947, 5963, 5984, 6008, 6029, 6050, 6059, 6072, 6092, 6110, 6123, 6140, 6158, 6170, 6190, 6212, 6234, 6252, 6271, 6292, 6312, 6332, 6352, 6372, 6393, 6411, 6427, 6443, 6463, 6482, 6497, 6517, 6538, 6556, 6570, 6596, 6619, 6649, 6672, 6704, 6729, 6738} func (i Kind) String() string { if i < 0 || i >= Kind(len(_Kind_index)-1) { diff --git a/internal/ast/utilities.go b/internal/ast/utilities.go index 30a4d350b2..76e0281370 100644 --- a/internal/ast/utilities.go +++ b/internal/ast/utilities.go @@ -1,6 +1,7 @@ package ast import ( + "fmt" "slices" "strings" "sync" @@ -583,8 +584,8 @@ func IsTypeElement(node *Node) bool { KindMethodSignature, KindIndexSignature, KindGetAccessor, - KindSetAccessor: - // !!! KindNotEmittedTypeElement + KindSetAccessor, + KindNotEmittedTypeElement: return true } return false @@ -1435,10 +1436,10 @@ func IsDynamicName(name *Node) bool { } func IsEntityNameExpression(node *Node) bool { - return node.Kind == KindIdentifier || isPropertyAccessEntityNameExpression(node) + return node.Kind == KindIdentifier || IsPropertyAccessEntityNameExpression(node) } -func isPropertyAccessEntityNameExpression(node *Node) bool { +func IsPropertyAccessEntityNameExpression(node *Node) bool { if node.Kind == KindPropertyAccessExpression { expr := node.AsPropertyAccessExpression() return expr.Name().Kind == KindIdentifier && IsEntityNameExpression(expr.Expression) @@ -1530,14 +1531,14 @@ func GetImplementsHeritageClauseElements(node *Node) []*ExpressionWithTypeArgume } func getHeritageElements(node *Node, kind Kind) []*Node { - clause := getHeritageClause(node, kind) + clause := GetHeritageClause(node, kind) if clause != nil { return clause.AsHeritageClause().Types.Nodes } return nil } -func getHeritageClause(node *Node, kind Kind) *Node { +func GetHeritageClause(node *Node, kind Kind) *Node { clauses := getHeritageClauses(node) if clauses != nil { for _, clause := range clauses.Nodes { @@ -2639,3 +2640,313 @@ func getPragmaArgument(pragma *Pragma, name string) string { } return "" } + +func GetExternalModuleImportEqualsDeclarationExpression(node *Node) *Node { + // Debug.assert(isExternalModuleImportEqualsDeclaration(node)) + return node.AsImportEqualsDeclaration().ModuleReference.AsExternalModuleReference().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.AsTypeParameter(), + modifierArray, + node.Name(), + node.AsTypeParameter().Constraint, + node.AsTypeParameter().DefaultType, + ) + case KindParameter: + return factory.UpdateParameterDeclaration( + node.AsParameterDeclaration(), + modifierArray, + node.AsParameterDeclaration().DotDotDotToken, + node.Name(), + node.AsParameterDeclaration().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.AsPropertySignatureDeclaration().PostfixToken, + node.Type(), + node.Initializer(), + ) + case KindPropertyDeclaration: + return factory.UpdatePropertyDeclaration( + node.AsPropertyDeclaration(), + modifierArray, + node.Name(), + node.AsPropertyDeclaration().PostfixToken, + node.Type(), + node.Initializer(), + ) + case KindMethodSignature: + return factory.UpdateMethodSignatureDeclaration( + node.AsMethodSignatureDeclaration(), + modifierArray, + node.Name(), + node.AsMethodSignatureDeclaration().PostfixToken, + node.TypeParameterList(), + node.ParameterList(), + node.Type(), + ) + case KindMethodDeclaration: + return factory.UpdateMethodDeclaration( + node.AsMethodDeclaration(), + modifierArray, + node.AsMethodDeclaration().AsteriskToken, + node.Name(), + node.AsMethodDeclaration().PostfixToken, + node.TypeParameterList(), + node.ParameterList(), + node.Type(), + node.Body(), + ) + case KindConstructor: + return factory.UpdateConstructorDeclaration( + node.AsConstructorDeclaration(), + modifierArray, + node.TypeParameterList(), + node.ParameterList(), + node.Type(), + node.Body(), + ) + case KindGetAccessor: + return factory.UpdateGetAccessorDeclaration( + node.AsGetAccessorDeclaration(), + modifierArray, + node.Name(), + node.TypeParameterList(), + node.ParameterList(), + node.Type(), + node.Body(), + ) + case KindSetAccessor: + return factory.UpdateSetAccessorDeclaration( + node.AsSetAccessorDeclaration(), + modifierArray, + node.Name(), + node.TypeParameterList(), + node.ParameterList(), + node.Type(), + 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.Body(), + ) + case KindArrowFunction: + return factory.UpdateArrowFunction( + node.AsArrowFunction(), + modifierArray, + node.TypeParameterList(), + node.ParameterList(), + node.Type(), + 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.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.AsImportDeclaration().ImportClause, + node.AsImportDeclaration().ModuleSpecifier, + node.AsImportDeclaration().Attributes, + ) + case KindExportAssignment: + return factory.UpdateExportAssignment( + node.AsExportAssignment(), + modifierArray, + node.Expression(), + ) + case KindExportDeclaration: + return factory.UpdateExportDeclaration( + node.AsExportDeclaration(), + modifierArray, + node.IsTypeOnly(), + node.AsExportDeclaration().ExportClause, + node.AsExportDeclaration().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, + KindImportEqualsDeclaration, + KindVariableStatement, + KindClassDeclaration, + KindFunctionDeclaration, + KindModuleDeclaration, + KindTypeAliasDeclaration, + KindInterfaceDeclaration, + KindEnumDeclaration: + return true + default: + return false + } +} diff --git a/internal/checker/checker.go b/internal/checker/checker.go index 8bbc51ba9e..cf8c556151 100644 --- a/internal/checker/checker.go +++ b/internal/checker/checker.go @@ -619,6 +619,7 @@ type Checker struct { typeNodeLinks core.LinkStore[*ast.Node, TypeNodeLinks] enumMemberLinks core.LinkStore[*ast.Node, EnumMemberLinks] assertionLinks core.LinkStore[*ast.Node, AssertionLinks] + declarationLinks core.LinkStore[*ast.Node, DeclarationLinks] // TODO: Move into EmitResolver if `collectLinkedAliases` also can move arrayLiteralLinks core.LinkStore[*ast.Node, ArrayLiteralLinks] switchStatementLinks core.LinkStore[*ast.Node, SwitchStatementLinks] jsxElementLinks core.LinkStore[*ast.Node, JsxElementLinks] @@ -638,6 +639,7 @@ type Checker struct { indexSymbolLinks core.LinkStore[*ast.Symbol, IndexSymbolLinks] ReverseMappedSymbolLinks core.LinkStore[*ast.Symbol, ReverseMappedSymbolLinks] markedAssignmentSymbolLinks core.LinkStore[*ast.Symbol, MarkedAssignmentSymbolLinks] + symbolContainerLinks core.LinkStore[*ast.Symbol, ContainingSymbolLinks] sourceFileLinks core.LinkStore[*ast.SourceFile, SourceFileLinks] patternForType map[*Type]*ast.Node contextFreeTypes map[*ast.Node]*Type @@ -705,6 +707,7 @@ type Checker struct { resolvingSignature *Signature silentNeverSignature *Signature enumNumberIndexInfo *IndexInfo + anyBaseTypeIndexInfo *IndexInfo patternAmbientModules []*ast.PatternAmbientModule patternAmbientModuleAugmentations ast.SymbolTable globalObjectType *Type @@ -821,6 +824,8 @@ type Checker struct { markNodeAssignments func(*ast.Node) bool emitResolver *emitResolver emitResolverOnce sync.Once + diagnosticConstructionContext *printer.EmitContext + nodeBuilder NodeBuilderInterface _jsxNamespace string _jsxFactoryEntity *ast.Node } @@ -966,6 +971,7 @@ func NewChecker(program Program) *Checker { c.resolvingSignature = c.newSignature(SignatureFlagsNone, nil, nil, nil, nil, c.anyType, nil, 0) c.silentNeverSignature = c.newSignature(SignatureFlagsNone, nil, nil, nil, nil, c.silentNeverType, nil, 0) c.enumNumberIndexInfo = &IndexInfo{keyType: c.numberType, valueType: c.stringType, isReadonly: true} + c.anyBaseTypeIndexInfo = &IndexInfo{keyType: c.stringType, valueType: c.anyType, isReadonly: false} c.emptyStringType = c.getStringLiteralType("") c.zeroType = c.getNumberLiteralType(0) c.zeroBigIntType = c.getBigIntLiteralType(jsnum.PseudoBigInt{}) @@ -1027,6 +1033,8 @@ func NewChecker(program Program) *Checker { c.getGlobalClassAccessorDecoratorTargetType = c.getGlobalTypeResolver("ClassAccessorDecoratorTarget", 2 /*arity*/, true /*reportErrors*/) c.getGlobalClassAccessorDecoratorResultType = c.getGlobalTypeResolver("ClassAccessorDecoratorResult", 2 /*arity*/, true /*reportErrors*/) c.getGlobalClassFieldDecoratorContextType = c.getGlobalTypeResolver("ClassFieldDecoratorContext", 2 /*arity*/, true /*reportErrors*/) + c.diagnosticConstructionContext = printer.NewEmitContext() + c.nodeBuilder = NewNodeBuilderAPI(c, c.diagnosticConstructionContext) c.initializeClosures() c.initializeIterationResolvers() c.initializeChecker() @@ -3020,12 +3028,12 @@ func (c *Checker) checkTemplateLiteralType(node *ast.Node) { func (c *Checker) checkImportType(node *ast.Node) { c.checkSourceElement(node.AsImportTypeNode().Argument) if attributes := node.AsImportTypeNode().Attributes; attributes != nil { - c.getResolutionModeOverride(attributes.AsImportAttributes(), true /*reportErrors*/) + c.GetResolutionModeOverride(attributes.AsImportAttributes(), true /*reportErrors*/) } c.checkTypeReferenceOrImport(node) } -func (c *Checker) getResolutionModeOverride(node *ast.ImportAttributes, reportErrors bool) core.ResolutionMode { +func (c *Checker) GetResolutionModeOverride(node *ast.ImportAttributes, reportErrors bool) core.ResolutionMode { if len(node.Attributes.Nodes) != 1 { if reportErrors { c.grammarErrorOnNode(node.AsNode(), core.IfElse(node.Token == ast.KindWithKeyword, @@ -3191,17 +3199,17 @@ func (c *Checker) checkFunctionOrConstructorSymbol(symbol *ast.Symbol) { // deviations, we XOR someOverloadFlags with allOverloadFlags someButNotAllOverloadFlags := someOverloadFlags ^ allOverloadFlags if someButNotAllOverloadFlags != 0 { - canonicalFlags := c.getEffectiveDeclarationFlags(getCanonicalOverload(overloads, implementation), flagsToCheck) + canonicalFlags := c.GetEffectiveDeclarationFlags(getCanonicalOverload(overloads, implementation), flagsToCheck) groups := make(map[*ast.SourceFile][]*ast.Node) for _, overload := range overloads { sourceFile := ast.GetSourceFileOfNode(overload) groups[sourceFile] = append(groups[sourceFile], overload) } for _, overloadsInFile := range groups { - canonicalFlagsForFile := c.getEffectiveDeclarationFlags(getCanonicalOverload(overloadsInFile, implementation), flagsToCheck) + canonicalFlagsForFile := c.GetEffectiveDeclarationFlags(getCanonicalOverload(overloadsInFile, implementation), flagsToCheck) for _, overload := range overloadsInFile { - deviation := c.getEffectiveDeclarationFlags(overload, flagsToCheck) ^ canonicalFlags - deviationInFile := c.getEffectiveDeclarationFlags(overload, flagsToCheck) ^ canonicalFlagsForFile + deviation := c.GetEffectiveDeclarationFlags(overload, flagsToCheck) ^ canonicalFlags + deviationInFile := c.GetEffectiveDeclarationFlags(overload, flagsToCheck) ^ canonicalFlagsForFile switch { case deviationInFile&ast.ModifierFlagsExport != 0: // Overloads in different files need not all have export modifiers. This is ok: @@ -3311,7 +3319,7 @@ func (c *Checker) checkFunctionOrConstructorSymbol(symbol *ast.Symbol) { } if ast.IsFunctionDeclaration(node) || ast.IsMethodDeclaration(node) || ast.IsMethodSignatureDeclaration(node) || ast.IsConstructorDeclaration(node) { functionDeclarations = append(functionDeclarations, node) - currentNodeFlags := c.getEffectiveDeclarationFlags(node, flagsToCheck) + currentNodeFlags := c.GetEffectiveDeclarationFlags(node, flagsToCheck) someNodeFlags |= currentNodeFlags allNodeFlags &= currentNodeFlags someHaveQuestionToken = someHaveQuestionToken || isOptionalDeclaration(node) @@ -3390,7 +3398,7 @@ func (c *Checker) checkFunctionOrConstructorSymbol(symbol *ast.Symbol) { } } -func (c *Checker) getEffectiveDeclarationFlags(n *ast.Node, flagsToCheck ast.ModifierFlags) ast.ModifierFlags { +func (c *Checker) GetEffectiveDeclarationFlags(n *ast.Node, flagsToCheck ast.ModifierFlags) ast.ModifierFlags { flags := c.getCombinedModifierFlagsCached(n) // children of classes (even ambient classes) should not be marked as ambient or export // because those flags have no useful semantics there. @@ -5002,7 +5010,7 @@ func (c *Checker) checkImportAttributes(declaration *ast.Node) { c.checkTypeAssignableTo(c.getTypeFromImportAttributes(node), c.getNullableType(importAttributesType, TypeFlagsUndefined), node, nil) } isTypeOnly := isTypeOnlyImportOrExportDeclaration(declaration) - override := c.getResolutionModeOverride(node.AsImportAttributes(), isTypeOnly) + override := c.GetResolutionModeOverride(node.AsImportAttributes(), isTypeOnly) isImportAttributes := node.AsImportAttributes().Token == ast.KindWithKeyword if isTypeOnly && override != core.ResolutionModeNone { return // Other grammar checks do not apply to type-only imports with resolution mode assertions @@ -6382,7 +6390,7 @@ func (c *Checker) checkExportsOnMergedDeclarations(node *ast.Node) { defaultExportedDeclarationSpaces := DeclarationSpacesNone for _, d := range symbol.Declarations { declarationSpaces := c.getDeclarationSpaces(d) - effectiveDeclarationFlags := c.getEffectiveDeclarationFlags(d, ast.ModifierFlagsExport|ast.ModifierFlagsDefault) + effectiveDeclarationFlags := c.GetEffectiveDeclarationFlags(d, ast.ModifierFlagsExport|ast.ModifierFlagsDefault) if effectiveDeclarationFlags&ast.ModifierFlagsExport != 0 { if effectiveDeclarationFlags&ast.ModifierFlagsDefault != 0 { defaultExportedDeclarationSpaces |= declarationSpaces @@ -11294,7 +11302,7 @@ func (c *Checker) getEnclosingClassFromThisParameter(node *ast.Node) *Type { func getThisParameterFromNodeContext(node *ast.Node) *ast.Node { thisContainer := ast.GetThisContainer(node, false /*includeArrowFunctions*/, false /*includeClassComputedPropertyName*/) if thisContainer != nil && ast.IsFunctionLike(thisContainer) { - return getThisParameter(thisContainer) + return ast.GetThisParameter(thisContainer) } return nil } @@ -11408,7 +11416,7 @@ func (c *Checker) tryGetThisTypeAt(node *ast.Node) *Type { } func (c *Checker) tryGetThisTypeAtEx(node *ast.Node, includeGlobalThis bool, container *ast.Node) *Type { - if ast.IsFunctionLike(container) && (!c.isInParameterInitializerBeforeContainingFunction(node) || getThisParameter(container) != nil) { + if ast.IsFunctionLike(container) && (!c.isInParameterInitializerBeforeContainingFunction(node) || ast.GetThisParameter(container) != nil) { thisType := c.getThisTypeOfDeclaration(container) // Note: a parameter initializer should refer to class-this unless function-this is explicitly annotated. // If this is a function in a JS file, it might be a class method. @@ -12967,7 +12975,7 @@ func (c *Checker) getNarrowedTypeOfSymbol(symbol *ast.Symbol, location *ast.Node restType := c.getReducedApparentType(c.instantiateType(c.getTypeOfSymbol(contextualSignature.parameters[0]), mapper)) if restType.flags&TypeFlagsUnion != 0 && everyType(restType, isTupleType) && !core.Some(fn.Parameters(), c.isSomeSymbolAssigned) { narrowedType := c.getFlowTypeOfReferenceEx(fn, restType, restType, nil /*flowContainer*/, getFlowNodeOfNode(location)) - index := slices.Index(fn.Parameters(), declaration) - (core.IfElse(getThisParameter(fn) != nil, 1, 0)) + index := slices.Index(fn.Parameters(), declaration) - (core.IfElse(ast.GetThisParameter(fn) != nil, 1, 0)) return c.getIndexedAccessType(narrowedType, c.getNumberLiteralType(jsnum.Number(index))) } } @@ -13559,7 +13567,7 @@ func (c *Checker) getTargetOfImportEqualsDeclaration(node *ast.Node, dontResolve if ast.IsVariableDeclaration(node) || node.AsImportEqualsDeclaration().ModuleReference.Kind == ast.KindExternalModuleReference { moduleReference := getExternalModuleRequireArgument(node) if moduleReference == nil { - moduleReference = getExternalModuleImportEqualsDeclarationExpression(node) + moduleReference = ast.GetExternalModuleImportEqualsDeclarationExpression(node) } immediate := c.resolveExternalModuleName(node, moduleReference, false /*ignoreErrors*/) resolved := c.resolveExternalModuleSymbol(immediate, false /*dontResolveAlias*/) @@ -17536,7 +17544,7 @@ func (c *Checker) resolveObjectTypeMembers(t *Type, source *Type, typeParameters if instantiatedBaseType != c.anyType { inheritedIndexInfos = c.getIndexInfosOfType(instantiatedBaseType) } else { - inheritedIndexInfos = []*IndexInfo{{keyType: c.stringType, valueType: c.anyType}} + inheritedIndexInfos = []*IndexInfo{c.anyBaseTypeIndexInfo} } indexInfos = core.Concatenate(indexInfos, core.Filter(inheritedIndexInfos, func(info *IndexInfo) bool { return findIndexInfo(indexInfos, info.keyType) == nil @@ -17923,7 +17931,7 @@ func (c *Checker) areAllOuterTypeParametersApplied(t *Type) bool { } func (c *Checker) reportCircularBaseType(node *ast.Node, t *Type) { - c.error(node, diagnostics.Type_0_recursively_references_itself_as_a_base_type, c.typeToStringEx(t, nil, TypeFormatFlagsWriteArrayAsGenericType)) + c.error(node, diagnostics.Type_0_recursively_references_itself_as_a_base_type, c.typeToStringEx(t, nil, TypeFormatFlagsWriteArrayAsGenericType, nil)) } // A valid base type is `any`, an object type or intersection of object types. @@ -18295,7 +18303,7 @@ func (c *Checker) getAnnotatedAccessorThisParameter(accessor *ast.Node) *ast.Sym func (c *Checker) getAccessorThisParameter(accessor *ast.Node) *ast.Node { if len(accessor.Parameters()) == core.IfElse(ast.IsGetAccessorDeclaration(accessor), 1, 2) { - return getThisParameter(accessor) + return ast.GetThisParameter(accessor) } return nil } @@ -19028,7 +19036,7 @@ func (c *Checker) resolveAnonymousTypeMembers(t *Type) { c.addInheritedMembers(members, c.getPropertiesOfType(baseConstructorType)) c.setStructuredTypeMembers(t, members, nil, nil, nil) } else if baseConstructorType == c.anyType { - baseConstructorIndexInfo = &IndexInfo{keyType: c.stringType, valueType: c.anyType} + baseConstructorIndexInfo = c.anyBaseTypeIndexInfo } } indexSymbol := members[ast.InternalSymbolNameIndex] @@ -20196,11 +20204,11 @@ func (c *Checker) elaborateNeverIntersection(chain *ast.Diagnostic, node *ast.No if t.flags&TypeFlagsIntersection != 0 && t.objectFlags&ObjectFlagsIsNeverIntersection != 0 { neverProp := core.Find(c.getPropertiesOfUnionOrIntersectionType(t), c.isDiscriminantWithNeverType) if neverProp != nil { - return NewDiagnosticChainForNode(chain, node, diagnostics.The_intersection_0_was_reduced_to_never_because_property_1_has_conflicting_types_in_some_constituents, c.typeToStringEx(t, nil, TypeFormatFlagsNoTypeReduction), c.symbolToString(neverProp)) + return NewDiagnosticChainForNode(chain, node, diagnostics.The_intersection_0_was_reduced_to_never_because_property_1_has_conflicting_types_in_some_constituents, c.typeToStringEx(t, nil, TypeFormatFlagsNoTypeReduction, nil), c.symbolToString(neverProp)) } privateProp := core.Find(c.getPropertiesOfUnionOrIntersectionType(t), isConflictingPrivateProperty) if privateProp != nil { - return NewDiagnosticChainForNode(chain, node, diagnostics.The_intersection_0_was_reduced_to_never_because_property_1_exists_in_multiple_constituents_and_is_private_in_some, c.typeToStringEx(t, nil, TypeFormatFlagsNoTypeReduction), c.symbolToString(privateProp)) + return NewDiagnosticChainForNode(chain, node, diagnostics.The_intersection_0_was_reduced_to_never_because_property_1_exists_in_multiple_constituents_and_is_private_in_some, c.typeToStringEx(t, nil, TypeFormatFlagsNoTypeReduction, nil), c.symbolToString(privateProp)) } } return chain @@ -27230,7 +27238,7 @@ func (c *Checker) getContextuallyTypedParameterType(parameter *ast.Node) *Type { } contextualSignature := c.getContextualSignature(fn) if contextualSignature != nil { - index := slices.Index(fn.Parameters(), parameter) - core.IfElse(getThisParameter(fn) != nil, 1, 0) + index := slices.Index(fn.Parameters(), parameter) - core.IfElse(ast.GetThisParameter(fn) != nil, 1, 0) if hasDotDotDotToken(parameter) && core.LastOrNil(fn.Parameters()) == parameter { return c.getRestTypeAtPosition(contextualSignature, index, false) } @@ -27865,10 +27873,10 @@ func (c *Checker) getLegacyDecoratorCallSignature(decorator *ast.Node) *Signatur if !ast.IsConstructorDeclaration(node.Parent) && !(ast.IsMethodDeclaration(node.Parent) || ast.IsSetAccessorDeclaration(node.Parent) && ast.IsClassLike(node.Parent.Parent)) { break } - if getThisParameter(node.Parent) == node { + if ast.GetThisParameter(node.Parent) == node { break } - index := slices.Index(node.Parent.Parameters(), node) - core.IfElse(getThisParameter(node.Parent) != nil, 1, 0) + index := slices.Index(node.Parent.Parameters(), node) - core.IfElse(ast.GetThisParameter(node.Parent) != nil, 1, 0) // Debug.assert(index >= 0) // A parameter declaration decorator will have three arguments (see `ParameterDecorator` in // core.d.ts). @@ -29356,7 +29364,7 @@ func (c *Checker) getSymbolAtLocation(node *ast.Node, ignoreErrors bool) *ast.Sy // 1). import x = require("./mo/*gotToDefinitionHere*/d") // 2). External module name in an import declaration // 4). type A = import("./f/*gotToDefinitionHere*/oo") - if (ast.IsExternalModuleImportEqualsDeclaration(grandParent) && getExternalModuleImportEqualsDeclarationExpression(grandParent) == node) || + if (ast.IsExternalModuleImportEqualsDeclaration(grandParent) && ast.GetExternalModuleImportEqualsDeclarationExpression(grandParent) == node) || ((parent.Kind == ast.KindImportDeclaration || parent.Kind == ast.KindExportDeclaration) && parent.AsImportDeclaration().ModuleSpecifier == node) || (ast.IsLiteralTypeNode(parent) && ast.IsLiteralImportTypeNode(grandParent) && grandParent.AsImportTypeNode().Argument == parent) { return c.resolveExternalModuleName(node, node, ignoreErrors) @@ -29759,7 +29767,7 @@ func (c *Checker) GetTypeAtLocation(node *ast.Node) *Type { return c.getTypeOfNode(node) } -func (c *Checker) GetEmitResolver(file *ast.SourceFile, skipDiagnostics bool) printer.EmitResolver { +func (c *Checker) GetEmitResolver(file *ast.SourceFile, skipDiagnostics bool) *emitResolver { c.emitResolverOnce.Do(func() { c.emitResolver = &emitResolver{checker: c} }) diff --git a/internal/checker/emitresolver.go b/internal/checker/emitresolver.go index 1b2bca67c0..6bb4cab8f4 100644 --- a/internal/checker/emitresolver.go +++ b/internal/checker/emitresolver.go @@ -1,11 +1,16 @@ package checker import ( + "maps" + "slices" "sync" "github.com/microsoft/typescript-go/internal/ast" "github.com/microsoft/typescript-go/internal/binder" "github.com/microsoft/typescript-go/internal/core" + "github.com/microsoft/typescript-go/internal/evaluator" + "github.com/microsoft/typescript-go/internal/jsnum" + "github.com/microsoft/typescript-go/internal/nodebuilder" "github.com/microsoft/typescript-go/internal/printer" ) @@ -18,6 +23,497 @@ type emitResolver struct { referenceResolver binder.ReferenceResolver } +func (r *emitResolver) IsOptionalParameter(node *ast.Node) bool { + 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.checker.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.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 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: + return false + + default: + return false + } +} + +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 { + 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) + + r.checkerMu.Lock() + symbol := r.checker.resolveName(enclosingDeclaration, firstIdentifier.Text(), meaning, nil, false, false) + r.checkerMu.Unlock() + + if symbol != nil && symbol.Flags&ast.SymbolFlagsTypeParameter != 0 && meaning&ast.SymbolFlagsType != 0 { + return printer.SymbolAccessibilityResult{Accessibility: printer.SymbolAccessibilityAccessible} + } + + if symbol == nil && ast.IsThisIdentifier(firstIdentifier) { + r.checkerMu.Lock() + sym := r.checker.getSymbolOfDeclaration(r.checker.getThisContainer(firstIdentifier, false, false)) + r.checkerMu.Unlock() + 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) { + // Only lock as we edit links, so the IsDeclarationVisible calls don't trip over the lock + // TODO: does this need to lock? but multiple already-locking resolver entrypoints reach here... + r.checker.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 { + variableStatement := ast.FindAncestor(declaration, ast.IsVariableStatement) + 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) 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: + r.checkerMu.Lock() + defer r.checkerMu.Unlock() + if symbol == nil { + symbol = r.checker.getSymbolOfDeclaration(declaration) + } + type_ := 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, type_)) + case ast.KindParameter, ast.KindJSDocParameterTag: + return r.requiresAddingImplicitUndefined(declaration, enclosingDeclaration) + default: + panic("Node cannot possibly require adding undefined") + } +} + +func (r *emitResolver) requiresAddingImplicitUndefined(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 + } + r.checkerMu.Lock() + defer r.checkerMu.Unlock() + type_ := 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(type_) || r.checker.containsUndefinedType(type_) +} + +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 { + r.checkerMu.Lock() + defer r.checkerMu.Unlock() + return r.checker.IsSymbolAccessible(symbol, enclosingDeclaration, meaning, shouldComputeAliasToMarkVisible) +} + func isConstEnumOrConstEnumOnlyModule(s *ast.Symbol) bool { return isConstEnumSymbol(s) || s.Flags&ast.SymbolFlagsConstEnumOnlyModule != 0 } @@ -153,15 +649,30 @@ func (r *emitResolver) MarkLinkedReferencesRecursively(file *ast.SourceFile) { } } -func (r *emitResolver) GetExternalModuleFileFromDeclaration(node *ast.Node) *ast.SourceFile { +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() - - if ast.IsParseTreeNode(node) { - // !!! - // return r.checker.getExternalModuleFileFromDeclaration(node) + moduleSymbol := r.checker.resolveExternalModuleNameWorker(specifier, specifier /*moduleNotFoundError*/, nil, false, false) // TODO: GH#18217 + if moduleSymbol == nil { + return nil } - return nil + decl := ast.GetDeclarationOfKind(moduleSymbol, ast.KindSourceFile) + if decl == nil { + return nil + } + return decl.AsSourceFile() } func (r *emitResolver) getReferenceResolver() binder.ReferenceResolver { @@ -222,3 +733,150 @@ func (r *emitResolver) GetReferencedValueDeclarations(node *ast.IdentifierNode) return r.getReferenceResolver().GetReferencedValueDeclarations(node) } + +// TODO: the emit resolver being respoinsible 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) + } + requestNodeBuilder := NewNodeBuilderAPI(r.checker, emitContext) // TODO: cache per-context + return requestNodeBuilder.SerializeReturnTypeForSignature(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) + } + requestNodeBuilder := NewNodeBuilderAPI(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() + type_ := r.checker.getTypeOfSymbol(r.checker.getSymbolOfDeclaration(node)) + r.checkerMu.Unlock() + if type_ == nil { + return nil // TODO: How!? Maybe this should be a panic. All symbols should have a type. + } + + var enumResult *ast.Node + if type_.flags&TypeFlagsEnumLike != 0 { + requestNodeBuilder := NewNodeBuilderAPI(r.checker, emitContext) // TODO: cache per-context + enumResult = requestNodeBuilder.SymbolToExpression(type_.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 type_ == r.checker.trueType { + enumResult = emitContext.Factory.NewKeywordExpression(ast.KindTrueKeyword) + } else if type_ == r.checker.falseType { + enumResult = emitContext.Factory.NewKeywordExpression(ast.KindFalseKeyword) + } + if enumResult != nil { + return enumResult + } + if type_.flags&TypeFlagsLiteral == 0 { + return nil // non-literal type + } + literalValue := type_.AsLiteralType().value + switch literalValue.(type) { + case string: + return emitContext.Factory.NewStringLiteral(literalValue.(string)) + case jsnum.Number: + value := literalValue.(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: + value := literalValue.(jsnum.PseudoBigInt) + if value.Negative { + // negative + return emitContext.Factory.NewPrefixUnaryExpression( + ast.KindMinusToken, + emitContext.Factory.NewBigIntLiteral(value.Base10Value), + ) + } + return emitContext.Factory.NewNumericLiteral(value.Base10Value) + case bool: + if literalValue.(bool) { + return emitContext.Factory.NewKeywordExpression(ast.KindTrueKeyword) + } + return emitContext.Factory.NewKeywordExpression(ast.KindFalseKeyword) + } + return nil /// !!! TODO: panic? impossible? +} + +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) + } + + requestNodeBuilder := NewNodeBuilderAPI(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() + + 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) + } + r.checkerMu.Unlock() + + requestNodeBuilder := NewNodeBuilderAPI(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 info.components { + // !!! TODO: Complete late-bound index info support - getObjectLiteralIndexInfo does not yet add late bound components to index signatures + // } + node := requestNodeBuilder.IndexInfoToIndexSignatureDeclaration(info, enclosingDeclaration, flags, internalFlags, tracker) + if node != nil && isStatic { + mods := node.Modifiers() + mods = emitContext.Factory.NewModifierList(append([]*ast.Node{emitContext.Factory.NewModifier(ast.KindStaticKeyword)}, mods.Nodes...)) + node = emitContext.Factory.UpdateIndexSignatureDeclaration( + node.AsIndexSignatureDeclaration(), + mods, + node.ParameterList(), + node.Type(), + ) + } + if node != nil { + result = append(result, node) + } + } + } + return result +} diff --git a/internal/checker/nodebuilder.go b/internal/checker/nodebuilder.go new file mode 100644 index 0000000000..fd6adecaaf --- /dev/null +++ b/internal/checker/nodebuilder.go @@ -0,0 +1,2961 @@ +package checker + +import ( + "fmt" + "slices" + "strings" + + "github.com/microsoft/typescript-go/internal/ast" + "github.com/microsoft/typescript-go/internal/compiler/module" + "github.com/microsoft/typescript-go/internal/core" + "github.com/microsoft/typescript-go/internal/jsnum" + "github.com/microsoft/typescript-go/internal/modulespecifiers" + "github.com/microsoft/typescript-go/internal/nodebuilder" + "github.com/microsoft/typescript-go/internal/printer" + "github.com/microsoft/typescript-go/internal/scanner" + "github.com/microsoft/typescript-go/internal/tspath" +) + +type CompositeSymbolIdentity struct { + isConstructorNode bool + symbolId ast.SymbolId + nodeId ast.NodeId +} + +type TrackedSymbolArgs struct { + symbol *ast.Symbol + enclosingDeclaration *ast.Node + meaning ast.SymbolFlags +} + +type SerializedTypeEntry struct { + node *ast.Node + truncating bool + addedLength int + trackedSymbols []*TrackedSymbolArgs +} + +type CompositeTypeCacheIdentity struct { + typeId TypeId + flags nodebuilder.Flags + internalFlags nodebuilder.InternalFlags +} + +type NodeBuilderLinks struct { + serializedTypes map[CompositeTypeCacheIdentity]*SerializedTypeEntry // Collection of types serialized at this location + fakeScopeForSignatureDeclaration *string // If present, this is a fake scope injected into an enclosing declaration chain. +} + +type NodeBuilderSymbolLinks struct { + specifierCache module.ModeAwareCache[string] +} +type NodeBuilderContext struct { + tracker nodebuilder.SymbolTracker + approximateLength int + encounteredError bool + truncating bool + reportedDiagnostic bool + flags nodebuilder.Flags + internalFlags nodebuilder.InternalFlags + depth int + enclosingDeclaration *ast.Node + enclosingFile *ast.SourceFile + inferTypeParameters []*Type + visitedTypes map[TypeId]bool + symbolDepth map[CompositeSymbolIdentity]int + trackedSymbols []*TrackedSymbolArgs + mapper *TypeMapper + reverseMappedStack []*ast.Symbol + enclosingSymbolTypes map[ast.SymbolId]*Type + suppressReportInferenceFallback bool + remappedSymbolReferences map[ast.SymbolId]*ast.Symbol + + // per signature scope state + hasCreatedTypeParameterSymbolList bool + hasCreatedTypeParametersNamesLookups bool + typeParameterNames map[TypeId]*ast.Identifier + typeParameterNamesByText map[string]struct{} + typeParameterNamesByTextNextNameCount map[string]int + typeParameterSymbolList map[ast.SymbolId]struct{} +} + +type NodeBuilder struct { + // host members + f *ast.NodeFactory + ch *Checker + e *printer.EmitContext + + // cache + links core.LinkStore[*ast.Node, NodeBuilderLinks] + symbolLinks core.LinkStore[*ast.Symbol, NodeBuilderSymbolLinks] + + // closures + typeToTypeNodeClosure func(t *Type) *ast.TypeNode + typeReferenceToTypeNodeClosure func(t *Type) *ast.TypeNode + conditionalTypeToTypeNodeClosure func(t *Type) *ast.TypeNode + createTypeNodeFromObjectTypeClosure func(t *Type) *ast.TypeNode + isStringNamedClosure func(d *ast.Declaration) bool + isSingleQuotedStringNamedClosure func(d *ast.Declaration) bool + + // state + ctx *NodeBuilderContext +} + +const defaultMaximumTruncationLength = 160 +const noTruncationMaximumTruncationLength = 1_000_000 + +// Node builder utility functions + +// You probably don't mean to use this - use `NewNodeBuilderAPI` instead +func NewNodeBuilder(ch *Checker, e *printer.EmitContext) NodeBuilder { + result := NodeBuilder{f: e.Factory, ch: ch, e: e, typeToTypeNodeClosure: nil, typeReferenceToTypeNodeClosure: nil, conditionalTypeToTypeNodeClosure: nil, ctx: nil} + result.initializeClosures() + return result +} + +func (b *NodeBuilder) initializeClosures() { + b.typeToTypeNodeClosure = b.typeToTypeNode + b.typeReferenceToTypeNodeClosure = b.typeReferenceToTypeNode + b.conditionalTypeToTypeNodeClosure = b.conditionalTypeToTypeNode + b.createTypeNodeFromObjectTypeClosure = b.createTypeNodeFromObjectType + b.isStringNamedClosure = b.isStringNamed + b.isSingleQuotedStringNamedClosure = b.isSingleQuotedStringNamed +} + +func (b *NodeBuilder) saveRestoreFlags() func() { + flags := b.ctx.flags + internalFlags := b.ctx.internalFlags + depth := b.ctx.depth + + return func() { + b.ctx.flags = flags + b.ctx.internalFlags = internalFlags + b.ctx.depth = depth + } +} + +func (b *NodeBuilder) checkTruncationLength() bool { + if b.ctx.truncating { + return b.ctx.truncating + } + b.ctx.truncating = b.ctx.approximateLength > (core.IfElse((b.ctx.flags&nodebuilder.FlagsNoTruncation != 0), noTruncationMaximumTruncationLength, defaultMaximumTruncationLength)) + return b.ctx.truncating +} + +func (b *NodeBuilder) appendReferenceToType(root *ast.TypeNode, ref *ast.TypeNode) *ast.TypeNode { + if ast.IsImportTypeNode(root) { + // first shift type arguments + + // !!! In the old emitter, an Identifier could have type arguments for use with quickinfo: + // typeArguments := root.TypeArguments + // qualifier := root.AsImportTypeNode().Qualifier + // if qualifier != nil { + // if ast.IsIdentifier(qualifier) { + // if typeArguments != getIdentifierTypeArguments(qualifier) { + // qualifier = setIdentifierTypeArguments(b.f.CloneNode(qualifier), typeArguments) + // } + // } else { + // if typeArguments != getIdentifierTypeArguments(qualifier.Right) { + // qualifier = b.f.UpdateQualifiedName(qualifier, qualifier.Left, setIdentifierTypeArguments(b.f.cloneNode(qualifier.Right), typeArguments)) + // } + // } + // } + // !!! Without the above, nested type args are silently elided + imprt := root.AsImportTypeNode() + // then move qualifiers + ids := getAccessStack(ref) + var qualifier *ast.Node + for _, id := range ids { + if qualifier != nil { + qualifier = b.f.NewQualifiedName(qualifier, id) + } else { + qualifier = id + } + } + return b.f.UpdateImportTypeNode(imprt, imprt.IsTypeOf, imprt.Argument, imprt.Attributes, qualifier, ref.AsTypeReferenceNode().TypeArguments) + } else { + // first shift type arguments + // !!! In the old emitter, an Identifier could have type arguments for use with quickinfo: + // typeArguments := root.TypeArguments + // typeName := root.AsTypeReferenceNode().TypeName + // if ast.IsIdentifier(typeName) { + // if typeArguments != getIdentifierTypeArguments(typeName) { + // typeName = setIdentifierTypeArguments(b.f.cloneNode(typeName), typeArguments) + // } + // } else { + // if typeArguments != getIdentifierTypeArguments(typeName.Right) { + // typeName = b.f.UpdateQualifiedName(typeName, typeName.Left, setIdentifierTypeArguments(b.f.cloneNode(typeName.Right), typeArguments)) + // } + // } + // !!! Without the above, nested type args are silently elided + // then move qualifiers + ids := getAccessStack(ref) + var typeName *ast.Node = root.AsTypeReferenceNode().TypeName + for _, id := range ids { + typeName = b.f.NewQualifiedName(typeName, id) + } + return b.f.UpdateTypeReferenceNode(root.AsTypeReferenceNode(), typeName, ref.AsTypeReferenceNode().TypeArguments) + } +} + +func getAccessStack(ref *ast.Node) []*ast.Node { + var state *ast.Node = ref.AsTypeReferenceNode().TypeName + ids := []*ast.Node{} + for !ast.IsIdentifier(state) { + entity := state.AsQualifiedName() + ids = append([]*ast.Node{entity.Right}, ids...) + state = entity.Left + } + ids = append([]*ast.Node{state}, ids...) + return ids +} + +func isClassInstanceSide(c *Checker, t *Type) bool { + return t.symbol != nil && t.symbol.Flags&ast.SymbolFlagsClass != 0 && (t == c.getDeclaredTypeOfClassOrInterface(t.symbol) || (t.flags&TypeFlagsObject != 0 && t.objectFlags&ObjectFlagsIsClassInstanceClone != 0)) +} + +func (b *NodeBuilder) createElidedInformationPlaceholder() *ast.TypeNode { + b.ctx.approximateLength += 3 + if b.ctx.flags&nodebuilder.FlagsNoTruncation == 0 { + return b.f.NewTypeReferenceNode(b.f.NewIdentifier("..."), nil /*typeArguments*/) + } + // addSyntheticLeadingComment(b.f.NewKeywordTypeNode(ast.KindAnyKeyword), ast.KindMultiLineCommentTrivia, "elided") // !!! + return b.f.NewKeywordTypeNode(ast.KindAnyKeyword) +} + +func (b *NodeBuilder) mapToTypeNodes(list []*Type) *ast.NodeList { + if len(list) == 0 { + return nil + } + contents := core.Map(list, b.typeToTypeNodeClosure) + return b.f.NewNodeList(contents) +} + +func (b *NodeBuilder) setCommentRange(node *ast.Node, range_ *ast.Node) { + if range_ != nil && b.ctx.enclosingFile != nil && b.ctx.enclosingFile == ast.GetSourceFileOfNode(range_) { + // Copy comments to node for declaration emit + b.e.AssignCommentRange(node, range_) + } +} + +func (b *NodeBuilder) tryReuseExistingTypeNodeHelper(existing *ast.TypeNode) *ast.TypeNode { + return nil // !!! +} + +func (b *NodeBuilder) tryReuseExistingTypeNode(typeNode *ast.TypeNode, t *Type, host *ast.Node, addUndefined bool) *ast.TypeNode { + originalType := t + if addUndefined { + t = b.ch.getOptionalType(t, !ast.IsParameter(host)) + } + clone := b.tryReuseExistingNonParameterTypeNode(typeNode, t, host, nil) + if clone != nil { + // explicitly add `| undefined` if it's missing from the input type nodes and the type contains `undefined` (and not the missing type) + if addUndefined && containsNonMissingUndefinedType(b.ch, t) && !someType(b.getTypeFromTypeNode(typeNode, false), func(t *Type) bool { + return t.flags&TypeFlagsUndefined != 0 + }) { + return b.f.NewUnionTypeNode(b.f.NewNodeList([]*ast.TypeNode{clone, b.f.NewKeywordTypeNode(ast.KindUndefinedKeyword)})) + } + return clone + } + if addUndefined && originalType != t { + cloneMissingUndefined := b.tryReuseExistingNonParameterTypeNode(typeNode, originalType, host, nil) + if cloneMissingUndefined != nil { + return b.f.NewUnionTypeNode(b.f.NewNodeList([]*ast.TypeNode{cloneMissingUndefined, b.f.NewKeywordTypeNode(ast.KindUndefinedKeyword)})) + } + } + return nil +} + +func (b *NodeBuilder) typeNodeIsEquivalentToType(annotatedDeclaration *ast.Node, t *Type, typeFromTypeNode *Type) bool { + if typeFromTypeNode == t { + return true + } + if annotatedDeclaration == nil { + return false + } + // !!! + // used to be hasEffectiveQuestionToken for JSDoc + if isOptionalDeclaration(annotatedDeclaration) { + return b.ch.getTypeWithFacts(t, TypeFactsNEUndefined) == typeFromTypeNode + } + return false +} + +func (b *NodeBuilder) existingTypeNodeIsNotReferenceOrIsReferenceWithCompatibleTypeArgumentCount(existing *ast.TypeNode, t *Type) bool { + // In JS, you can say something like `Foo` and get a `Foo` implicitly - we don't want to preserve that original `Foo` in these cases, though. + if t.objectFlags&ObjectFlagsReference == 0 { + return true + } + if !ast.IsTypeReferenceNode(existing) { + return true + } + // `type` is a reference type, and `existing` is a type reference node, but we still need to make sure they refer to the _same_ target type + // before we go comparing their type argument counts. + b.ch.getTypeFromTypeReference(existing) + // call to ensure symbol is resolved + links := b.ch.symbolNodeLinks.TryGet(existing) + if links == nil { + return true + } + symbol := links.resolvedSymbol + if symbol == nil { + return true + } + existingTarget := b.ch.getDeclaredTypeOfSymbol(symbol) + if existingTarget == nil || existingTarget != t.AsTypeReference().target { + return true + } + return len(existing.TypeArguments()) >= b.ch.getMinTypeArgumentCount(t.AsTypeReference().target.AsInterfaceType().TypeParameters()) +} + +func (b *NodeBuilder) tryReuseExistingNonParameterTypeNode(existing *ast.TypeNode, t *Type, host *ast.Node, annotationType *Type) *ast.TypeNode { + if host == nil { + host = b.ctx.enclosingDeclaration + } + if annotationType == nil { + annotationType = b.getTypeFromTypeNode(existing, true) + } + if annotationType != nil && b.typeNodeIsEquivalentToType(host, t, annotationType) && b.existingTypeNodeIsNotReferenceOrIsReferenceWithCompatibleTypeArgumentCount(existing, t) { + result := b.tryReuseExistingTypeNodeHelper(existing) + if result != nil { + return result + } + } + return nil +} + +func (b *NodeBuilder) getResolvedTypeWithoutAbstractConstructSignatures(t *StructuredType) *Type { + if len(t.ConstructSignatures()) == 0 { + return t.AsType() + } + if t.objectTypeWithoutAbstractConstructSignatures != nil { + return t.objectTypeWithoutAbstractConstructSignatures + } + constructSignatures := core.Filter(t.ConstructSignatures(), func(signature *Signature) bool { + return signature.flags&SignatureFlagsAbstract == 0 + }) + if len(constructSignatures) == len(t.ConstructSignatures()) { + t.objectTypeWithoutAbstractConstructSignatures = t.AsType() + return t.AsType() + } + typeCopy := b.ch.newAnonymousType(t.symbol, t.members, t.CallSignatures(), core.IfElse(len(constructSignatures) > 0, constructSignatures, []*Signature{}), t.indexInfos) + t.objectTypeWithoutAbstractConstructSignatures = typeCopy + typeCopy.AsStructuredType().objectTypeWithoutAbstractConstructSignatures = typeCopy + return typeCopy +} + +func (b *NodeBuilder) symbolToNode(symbol *ast.Symbol, meaning ast.SymbolFlags) *ast.Node { + if b.ctx.internalFlags&nodebuilder.InternalFlagsWriteComputedProps != 0 { + if symbol.ValueDeclaration != nil { + name := ast.GetNameOfDeclaration(symbol.ValueDeclaration) + if name != nil && ast.IsComputedPropertyName(name) { + return name + } + if b.ch.valueSymbolLinks.Has(symbol) { + nameType := b.ch.valueSymbolLinks.Get(symbol).nameType + if nameType != nil && nameType.flags&(TypeFlagsEnumLiteral|TypeFlagsUniqueESSymbol) != 0 { + oldEnclosing := b.ctx.enclosingDeclaration + b.ctx.enclosingDeclaration = nameType.symbol.ValueDeclaration + result := b.f.NewComputedPropertyName(b.symbolToExpression(nameType.symbol, meaning)) + b.ctx.enclosingDeclaration = oldEnclosing + return result + } + } + } + } + return b.symbolToExpression(symbol, meaning) +} + +func (b *NodeBuilder) symbolToName(symbol *ast.Symbol, meaning ast.SymbolFlags, expectsIdentifier bool) *ast.Node { + chain := b.lookupSymbolChain(symbol, meaning, false) + if expectsIdentifier && len(chain) != 1 && !b.ctx.encounteredError && (b.ctx.flags&nodebuilder.FlagsAllowQualifiedNameInPlaceOfIdentifier != 0) { + b.ctx.encounteredError = true + } + return b.createEntityNameFromSymbolChain(chain, len(chain)-1) +} + +func (b *NodeBuilder) createEntityNameFromSymbolChain(chain []*ast.Symbol, index int) *ast.Node { + // typeParameterNodes := b.lookupTypeParameterNodes(chain, index) + symbol := chain[index] + + if index == 0 { + b.ctx.flags |= nodebuilder.FlagsInInitialEntityName + } + symbolName := b.getNameOfSymbolAsWritten(symbol) + if index == 0 { + b.ctx.flags ^= nodebuilder.FlagsInInitialEntityName + } + + identifier := b.f.NewIdentifier(symbolName) + b.e.AddEmitFlags(identifier, printer.EFNoAsciiEscaping) + // !!! TODO: smuggle type arguments out + // if (typeParameterNodes) setIdentifierTypeArguments(identifier, factory.createNodeArray(typeParameterNodes)); + // identifier.symbol = symbol; + // expression = identifier; + if index > 0 { + return b.f.NewQualifiedName( + b.createEntityNameFromSymbolChain(chain, index-1), + identifier, + ) + } + return identifier +} + +// TODO: Audit usages of symbolToEntityNameNode - they should probably all be symbolToName +func (b *NodeBuilder) symbolToEntityNameNode(symbol *ast.Symbol) *ast.EntityName { + identifier := b.f.NewIdentifier(symbol.Name) + if symbol.Parent != nil { + return b.f.NewQualifiedName(b.symbolToEntityNameNode(symbol.Parent), identifier) + } + return identifier +} + +func (b *NodeBuilder) symbolToTypeNode(symbol *ast.Symbol, mask ast.SymbolFlags, typeArguments *ast.NodeList) *ast.TypeNode { + chain := b.lookupSymbolChain(symbol, mask, (b.ctx.flags&nodebuilder.FlagsUseAliasDefinedOutsideCurrentScope == 0)) // If we're using aliases outside the current scope, dont bother with the module + if len(chain) == 0 { + return nil // TODO: shouldn't be possible, `lookupSymbolChain` should always at least return the input symbol and issue an error + } + isTypeOf := mask == ast.SymbolFlagsValue + if core.Some(chain[0].Declarations, hasNonGlobalAugmentationExternalModuleSymbol) { + // module is root, must use `ImportTypeNode` + var nonRootParts *ast.Node + if len(chain) > 1 { + nonRootParts = b.createAccessFromSymbolChain(chain, len(chain)-1, 1, typeArguments) + } + typeParameterNodes := typeArguments + if typeParameterNodes == nil { + typeParameterNodes = b.lookupTypeParameterNodes(chain, 0) + } + contextFile := ast.GetSourceFileOfNode(b.e.MostOriginal(b.ctx.enclosingDeclaration)) // TODO: Just use b.ctx.enclosingFile ? Or is the delayed lookup important for context moves? + targetFile := getSourceFileOfModule(chain[0]) + var specifier string + var attributes *ast.Node + if b.ch.compilerOptions.GetModuleResolutionKind() == core.ModuleResolutionKindNode16 || b.ch.compilerOptions.GetModuleResolutionKind() == core.ModuleResolutionKindNodeNext { + // An `import` type directed at an esm format file is only going to resolve in esm mode - set the esm mode assertion + if targetFile != nil && contextFile != nil && b.ch.program.GetEmitModuleFormatOfFile(targetFile) == core.ModuleKindESNext && b.ch.program.GetEmitModuleFormatOfFile(targetFile) != b.ch.program.GetEmitModuleFormatOfFile(contextFile) { + specifier = b.getSpecifierForModuleSymbol(chain[0], core.ModuleKindESNext) + attributes = b.f.NewImportAttributes( + ast.KindWithKeyword, + b.f.NewNodeList([]*ast.Node{b.f.NewImportAttribute(b.f.NewStringLiteral("resolution-mode"), b.f.NewStringLiteral("import"))}), + false, + ) + } + } + if len(specifier) == 0 { + specifier = b.getSpecifierForModuleSymbol(chain[0], core.ResolutionModeNone) + } + if (b.ctx.flags&nodebuilder.FlagsAllowNodeModulesRelativePaths == 0) /* && b.ch.compilerOptions.GetModuleResolutionKind() != core.ModuleResolutionKindClassic */ && strings.Contains(specifier, "/node_modules/") { + oldSpecifier := specifier + + if b.ch.compilerOptions.GetModuleResolutionKind() == core.ModuleResolutionKindNode16 || b.ch.compilerOptions.GetModuleResolutionKind() == core.ModuleResolutionKindNodeNext { + // We might be able to write a portable import type using a mode override; try specifier generation again, but with a different mode set + swappedMode := core.ModuleKindESNext + if b.ch.program.GetEmitModuleFormatOfFile(contextFile) == core.ModuleKindESNext { + swappedMode = core.ModuleKindCommonJS + } + specifier = b.getSpecifierForModuleSymbol(chain[0], swappedMode) + + if strings.Contains(specifier, "/node_modules/") { + // Still unreachable :( + specifier = oldSpecifier + } else { + modeStr := "require" + if swappedMode == core.ModuleKindESNext { + modeStr = "import" + } + attributes = b.f.NewImportAttributes( + ast.KindWithKeyword, + b.f.NewNodeList([]*ast.Node{b.f.NewImportAttribute(b.f.NewStringLiteral("resolution-mode"), b.f.NewStringLiteral(modeStr))}), + false, + ) + } + } + + if attributes == nil { + // If ultimately we can only name the symbol with a reference that dives into a `node_modules` folder, we should error + // since declaration files with these kinds of references are liable to fail when published :( + b.ctx.encounteredError = true + b.ctx.tracker.ReportLikelyUnsafeImportRequiredError(oldSpecifier) + } + } + + lit := b.f.NewLiteralTypeNode(b.f.NewStringLiteral(specifier)) + b.ctx.approximateLength += len(specifier) + 10 // specifier + import("") + if nonRootParts == nil || ast.IsEntityName(nonRootParts) { + if nonRootParts != nil { + // !!! TODO: smuggle type arguments out + // const lastId = isIdentifier(nonRootParts) ? nonRootParts : nonRootParts.right; + // setIdentifierTypeArguments(lastId, /*typeArguments*/ undefined); + } + return b.f.NewImportTypeNode(isTypeOf, lit, attributes, nonRootParts, typeParameterNodes) + } + + splitNode := getTopmostIndexedAccessType(nonRootParts.AsIndexedAccessTypeNode()) + qualifier := splitNode.ObjectType.AsTypeReference().TypeName + return b.f.NewIndexedAccessTypeNode( + b.f.NewImportTypeNode(isTypeOf, lit, attributes, qualifier, typeParameterNodes), + splitNode.IndexType, + ) + + } + + entityName := b.createAccessFromSymbolChain(chain, len(chain)-1, 0, typeArguments) + if ast.IsIndexedAccessTypeNode(entityName) { + return entityName // Indexed accesses can never be `typeof` + } + if isTypeOf { + return b.f.NewTypeQueryNode(entityName, nil) + } + // !!! TODO: smuggle type arguments out + // Move type arguments from last identifier on chain to type reference + // const lastId = isIdentifier(entityName) ? entityName : entityName.right; + // const lastTypeArgs = getIdentifierTypeArguments(lastId); + // setIdentifierTypeArguments(lastId, /*typeArguments*/ undefined); + return b.f.NewTypeReferenceNode(entityName, typeArguments) +} + +func getTopmostIndexedAccessType(node *ast.IndexedAccessTypeNode) *ast.IndexedAccessTypeNode { + if ast.IsIndexedAccessTypeNode(node.ObjectType) { + return getTopmostIndexedAccessType(node.ObjectType.AsIndexedAccessTypeNode()) + } + return node +} + +func (b *NodeBuilder) createAccessFromSymbolChain(chain []*ast.Symbol, index int, stopper int, overrideTypeArguments *ast.NodeList) *ast.Node { + // !!! TODO: smuggle type arguments out + typeParameterNodes := overrideTypeArguments + if index != (len(chain) - 1) { + typeParameterNodes = b.lookupTypeParameterNodes(chain, index) + } + symbol := chain[index] + var parent *ast.Symbol + if index > 0 { + parent = chain[index-1] + } + + var symbolName string + if index == 0 { + b.ctx.flags |= nodebuilder.FlagsInInitialEntityName + symbolName = b.getNameOfSymbolAsWritten(symbol) + b.ctx.approximateLength += len(symbolName) + 1 + b.ctx.flags ^= nodebuilder.FlagsInInitialEntityName + } else { + // lookup a ref to symbol within parent to handle export aliases + if parent != nil { + exports := b.ch.getExportsOfSymbol(parent) + if exports != nil { + for name, ex := range exports { + if b.ch.getSymbolIfSameReference(ex, symbol) != nil && !isLateBoundName(name) && name != ast.InternalSymbolNameExportEquals { + symbolName = name + break + } + } + } + } + } + + if len(symbolName) == 0 { + var name *ast.Node + for _, d := range symbol.Declarations { + name = ast.GetNameOfDeclaration(d) + if name != nil { + break + } + } + if name != nil && ast.IsComputedPropertyName(name) && ast.IsEntityName(name.AsComputedPropertyName().Expression) { + lhs := b.createAccessFromSymbolChain(chain, index-1, stopper, overrideTypeArguments) + if ast.IsEntityName(lhs) { + return b.f.NewIndexedAccessTypeNode( + b.f.NewParenthesizedTypeNode(b.f.NewTypeQueryNode(lhs, nil)), + b.f.NewTypeQueryNode(name.Expression(), nil), + ) + } + return lhs + } + symbolName = b.getNameOfSymbolAsWritten(symbol) + } + b.ctx.approximateLength += len(symbolName) + 1 + + if (b.ctx.flags&nodebuilder.FlagsForbidIndexedAccessSymbolReferences == 0) && parent != nil && + b.ch.getMembersOfSymbol(parent) != nil && b.ch.getMembersOfSymbol(parent)[symbol.Name] != nil && + b.ch.getSymbolIfSameReference(b.ch.getMembersOfSymbol(parent)[symbol.Name], symbol) != nil { + // Should use an indexed access + lhs := b.createAccessFromSymbolChain(chain, index-1, stopper, overrideTypeArguments) + if ast.IsIndexedAccessTypeNode(lhs) { + return b.f.NewIndexedAccessTypeNode( + lhs, + b.f.NewLiteralTypeNode(b.f.NewStringLiteral(symbolName)), + ) + } + return b.f.NewIndexedAccessTypeNode( + b.f.NewTypeReferenceNode(lhs, typeParameterNodes), + b.f.NewLiteralTypeNode(b.f.NewStringLiteral(symbolName)), + ) + } + + identifier := b.f.NewIdentifier(symbolName) + b.e.AddEmitFlags(identifier, printer.EFNoAsciiEscaping) + // !!! TODO: smuggle type arguments out + // if (typeParameterNodes) setIdentifierTypeArguments(identifier, factory.createNodeArray(typeParameterNodes)); + // identifier.symbol = symbol; + + if index > stopper { + lhs := b.createAccessFromSymbolChain(chain, index-1, stopper, overrideTypeArguments) + if !ast.IsEntityName(lhs) { + panic("Impossible construct - an export of an indexed access cannot be reachable") + } + return b.f.NewQualifiedName(lhs, identifier) + } + + return identifier +} + +func (b *NodeBuilder) symbolToExpression(symbol *ast.Symbol, mask ast.SymbolFlags) *ast.Expression { + chain := b.lookupSymbolChain(symbol, mask, false) + return b.createExpressionFromSymbolChain(chain, len(chain)-1) +} + +func (b *NodeBuilder) createExpressionFromSymbolChain(chain []*ast.Symbol, index int) *ast.Expression { + // typeParameterNodes := b.lookupTypeParameterNodes(chain, index) + symbol := chain[index] + + if index == 0 { + b.ctx.flags |= nodebuilder.FlagsInInitialEntityName + } + symbolName := b.getNameOfSymbolAsWritten(symbol) + if index == 0 { + b.ctx.flags ^= nodebuilder.FlagsInInitialEntityName + } + + if startsWithSingleOrDoubleQuote(symbolName) && core.Some(symbol.Declarations, hasNonGlobalAugmentationExternalModuleSymbol) { + return b.f.NewStringLiteral(b.getSpecifierForModuleSymbol(symbol, core.ResolutionModeNone)) + } + + if index == 0 || canUsePropertyAccess(symbolName, b.ch.languageVersion) { + identifier := b.f.NewIdentifier(symbolName) + b.e.AddEmitFlags(identifier, printer.EFNoAsciiEscaping) + // !!! TODO: smuggle type arguments out + // if (typeParameterNodes) setIdentifierTypeArguments(identifier, factory.createNodeArray(typeParameterNodes)); + // identifier.symbol = symbol; + if index > 0 { + return b.f.NewPropertyAccessExpression(b.createExpressionFromSymbolChain(chain, index-1), nil, identifier, ast.NodeFlagsNone) + } + return identifier + } + + if startsWithSquareBracket(symbolName) { + symbolName = symbolName[1 : len(symbolName)-1] + } + + var expression *ast.Expression + if startsWithSingleOrDoubleQuote(symbolName) && symbol.Flags&ast.SymbolFlagsEnumMember == 0 { + expression = b.f.NewStringLiteral(unquoteString(symbolName)) + } else if jsnum.FromString(symbolName).String() == symbolName { + // TODO: the follwing in strada would assert if the number is negative, but no such assertion exists here + // Moreover, what's even guaranteeing the name *isn't* -1 here anyway? Needs double-checking. + expression = b.f.NewNumericLiteral(symbolName) + } + if expression == nil { + expression = b.f.NewIdentifier(symbolName) + b.e.AddEmitFlags(expression, printer.EFNoAsciiEscaping) + // !!! TODO: smuggle type arguments out + // if (typeParameterNodes) setIdentifierTypeArguments(identifier, factory.createNodeArray(typeParameterNodes)); + // identifier.symbol = symbol; + // expression = identifier; + } + return b.f.NewElementAccessExpression(b.createExpressionFromSymbolChain(chain, index-1), nil, expression, ast.NodeFlagsNone) +} + +func canUsePropertyAccess(name string, languageVersion core.ScriptTarget) bool { + if len(name) == 0 { + return false + } + // TODO: in strada, this only used `isIdentifierStart` on the first character, while this checks the whole string for validity + // - possible strada bug? + if strings.HasPrefix(name, "#") { + return len(name) > 1 && scanner.IsIdentifierText(name[1:], languageVersion) + } + return scanner.IsIdentifierText(name, languageVersion) +} + +func unquoteString(str string) string { + // strconv.Unquote is insufficient as that only handles a single character inside single quotes, as those are character literals in go + inner := stripQuotes(str) + // In strada we do str.replace(/\\./g, s => s.substring(1)) - which is to say, replace all backslash-something with just something + // That's replicated here faithfully, but it seems wrong! This should probably be an actual unquote operation? + return strings.ReplaceAll(inner, "\\", "") +} + +/** + * Strip off existed surrounding single quotes, double quotes, or backticks from a given string + * + * @return non-quoted string + * + * @internal + */ +func stripQuotes(name string) string { + length := len(name) + if length >= 2 && startsWithSingleOrDoubleQuote(name) && name[0] == name[length-1] { // TODO: in TS this also handles backtick quoted things + return name[1 : len(name)-1] + } + return name +} + +func startsWithSingleOrDoubleQuote(str string) bool { + return strings.HasPrefix(str, "'") || strings.HasPrefix(str, "\"") +} + +func startsWithSquareBracket(str string) bool { + return strings.HasPrefix(str, "[") +} + +func isDefaultBindingContext(location *ast.Node) bool { + return location.Kind == ast.KindSourceFile || ast.IsAmbientModule(location) +} + +func (b *NodeBuilder) getNameOfSymbolFromNameType(symbol *ast.Symbol) string { + if b.ch.valueSymbolLinks.Has(symbol) { + nameType := b.ch.valueSymbolLinks.Get(symbol).nameType + if nameType == nil { + return "" + } + if nameType.flags&TypeFlagsStringOrNumberLiteral != 0 { + var name string + switch v := nameType.AsLiteralType().value.(type) { + case string: + name = v + case jsnum.Number: + name = v.String() + } + if !scanner.IsIdentifierText(name, b.ch.compilerOptions.GetEmitScriptTarget()) && !isNumericLiteralName(name) { + return b.ch.valueToString(nameType.AsLiteralType().value) + } + if isNumericLiteralName(name) && strings.HasPrefix(name, "-") { + return fmt.Sprintf("[%s]", name) + } + return name + } + if nameType.flags&TypeFlagsUniqueESSymbol != 0 { + text := b.getNameOfSymbolAsWritten(nameType.AsUniqueESSymbolType().symbol) + return fmt.Sprintf("[%s]", text) + } + } + return "" +} + +/** +* Gets a human-readable name for a symbol. +* Should *not* be used for the right-hand side of a `.` -- use `symbolName(symbol)` for that instead. +* +* Unlike `symbolName(symbol)`, this will include quotes if the name is from a string literal. +* It will also use a representation of a number as written instead of a decimal form, e.g. `0o11` instead of `9`. + */ +func (b *NodeBuilder) getNameOfSymbolAsWritten(symbol *ast.Symbol) string { + result, ok := b.ctx.remappedSymbolReferences[ast.GetSymbolId(symbol)] + if ok { + symbol = result + } + if symbol.Name == ast.InternalSymbolNameDefault && (b.ctx.flags&nodebuilder.FlagsUseAliasDefinedOutsideCurrentScope == 0) && + // If it's not the first part of an entity name, it must print as `default` + ((b.ctx.flags&nodebuilder.FlagsInInitialEntityName == 0) || + // if the symbol is synthesized, it will only be referenced externally it must print as `default` + len(symbol.Declarations) == 0 || + // if not in the same binding context (source file, module declaration), it must print as `default` + (b.ctx.enclosingDeclaration != nil && ast.FindAncestor(symbol.Declarations[0], isDefaultBindingContext) != ast.FindAncestor(b.ctx.enclosingDeclaration, isDefaultBindingContext))) { + return "default" + } + if len(symbol.Declarations) > 0 { + name := core.FirstNonNil(symbol.Declarations, ast.GetNameOfDeclaration) // Try using a declaration with a name, first + if name != nil { + // !!! TODO: JS Object.defineProperty declarations + // if ast.IsCallExpression(declaration) && ast.IsBindableObjectDefinePropertyCall(declaration) { + // return symbol.Name + // } + if ast.IsComputedPropertyName(name) && symbol.CheckFlags&ast.CheckFlagsLate == 0 { + if b.ch.valueSymbolLinks.Has(symbol) && b.ch.valueSymbolLinks.Get(symbol).nameType != nil && b.ch.valueSymbolLinks.Get(symbol).nameType.flags&TypeFlagsStringOrNumberLiteral != 0 { + result := b.getNameOfSymbolFromNameType(symbol) + if len(result) > 0 { + return result + } + } + } + return scanner.DeclarationNameToString(name) + } + declaration := symbol.Declarations[0] // Declaration may be nameless, but we'll try anyway + if declaration.Parent != nil && declaration.Parent.Kind == ast.KindVariableDeclaration { + return scanner.DeclarationNameToString(declaration.Parent.AsVariableDeclaration().Name()) + } + if ast.IsClassExpression(declaration) || ast.IsFunctionExpression(declaration) || ast.IsArrowFunction(declaration) { + if b.ctx != nil && !b.ctx.encounteredError && b.ctx.flags&nodebuilder.FlagsAllowAnonymousIdentifier == 0 { + b.ctx.encounteredError = true + } + switch declaration.Kind { + case ast.KindClassExpression: + return "(Anonymous class)" + case ast.KindFunctionExpression, ast.KindArrowFunction: + return "(Anonymous function)" + } + } + } + name := b.getNameOfSymbolFromNameType(symbol) + if len(name) > 0 { + return name + } + return symbol.Name +} + +// The full set of type parameters for a generic class or interface type consists of its outer type parameters plus +// its locally declared type parameters. +func (b *NodeBuilder) getTypeParametersOfClassOrInterface(symbol *ast.Symbol) []*Type { + result := make([]*Type, 0) + result = append(result, b.ch.getOuterTypeParametersOfClassOrInterface(symbol)...) + result = append(result, b.ch.getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol)...) + return result +} + +func (b *NodeBuilder) lookupTypeParameterNodes(chain []*ast.Symbol, index int) *ast.TypeParameterList { + // Debug.assert(chain && 0 <= index && index < chain.length); // !!! + symbol := chain[index] + symbolId := ast.GetSymbolId(symbol) + if !b.ctx.hasCreatedTypeParameterSymbolList { + b.ctx.hasCreatedTypeParameterSymbolList = true + b.ctx.typeParameterSymbolList = make(map[ast.SymbolId]struct{}) + } + _, ok := b.ctx.typeParameterSymbolList[symbolId] + if ok { + return nil + } + b.ctx.typeParameterSymbolList[symbolId] = struct{}{} + + if b.ctx.flags&nodebuilder.FlagsWriteTypeParametersInQualifiedName != 0 && index < (len(chain)-1) { + parentSymbol := symbol + nextSymbol := chain[index+1] + + if nextSymbol.CheckFlags&ast.CheckFlagsInstantiated != 0 { + targetSymbol := parentSymbol + if parentSymbol.Flags&ast.SymbolFlagsAlias != 0 { + targetSymbol = b.ch.resolveAlias(parentSymbol) + } + params := b.getTypeParametersOfClassOrInterface(targetSymbol) + targetMapper := b.ch.valueSymbolLinks.Get(nextSymbol).mapper + if targetMapper != nil { + params = core.Map(params, targetMapper.Map) + } + return b.mapToTypeNodes(params) + } else { + typeParameterNodes := b.typeParametersToTypeParameterDeclarations(symbol) + if len(typeParameterNodes) > 0 { + return b.f.NewNodeList(typeParameterNodes) + } + return nil + } + } + + return nil +} + +// TODO: move `lookupSymbolChain` and co to `symbolaccessibility.go` (but getSpecifierForModuleSymbol uses much context which makes that hard?) +func (b *NodeBuilder) lookupSymbolChain(symbol *ast.Symbol, meaning ast.SymbolFlags, yieldModuleSymbol bool) []*ast.Symbol { + b.ctx.tracker.TrackSymbol(symbol, b.ctx.enclosingDeclaration, meaning) + return b.lookupSymbolChainWorker(symbol, meaning, yieldModuleSymbol) +} + +func (b *NodeBuilder) lookupSymbolChainWorker(symbol *ast.Symbol, meaning ast.SymbolFlags, yieldModuleSymbol bool) []*ast.Symbol { + // Try to get qualified name if the symbol is not a type parameter and there is an enclosing declaration. + var chain []*ast.Symbol + isTypeParameter := symbol.Flags&ast.SymbolFlagsTypeParameter != 0 + if !isTypeParameter && (b.ctx.enclosingDeclaration != nil || b.ctx.flags&nodebuilder.FlagsUseFullyQualifiedType != 0) && (b.ctx.internalFlags&nodebuilder.InternalFlagsDoNotIncludeSymbolChain == 0) { + res := b.getSymbolChain(symbol, meaning /*endOfChain*/, true, yieldModuleSymbol) + chain = res + // Debug.checkDefined(chain) // !!! + // Debug.assert(chain && chain.length > 0); // !!! + } else { + chain = append(chain, symbol) + } + return chain +} + +type sortedSymbolNamePair struct { + sym *ast.Symbol + name string +} + +/** @param endOfChain Set to false for recursive calls; non-recursive calls should always output something. */ +func (b *NodeBuilder) getSymbolChain(symbol *ast.Symbol, meaning ast.SymbolFlags, endOfChain bool, yieldModuleSymbol bool) []*ast.Symbol { + accessibleSymbolChain := b.ch.getAccessibleSymbolChain(symbol, b.ctx.enclosingDeclaration, meaning, b.ctx.flags&nodebuilder.FlagsUseOnlyExternalAliasing != 0) + qualifierMeaning := meaning + if len(accessibleSymbolChain) > 0 { + qualifierMeaning = getQualifiedLeftMeaning(meaning) + } + if len(accessibleSymbolChain) == 0 || + b.ch.needsQualification(accessibleSymbolChain[0], b.ctx.enclosingDeclaration, qualifierMeaning) { + // Go up and add our parent. + root := symbol + if len(accessibleSymbolChain) > 0 { + root = accessibleSymbolChain[0] + } + parents := b.ch.getContainersOfSymbol(root, b.ctx.enclosingDeclaration, meaning) + if len(parents) > 0 { + parentSpecifiers := core.Map(parents, func(symbol *ast.Symbol) sortedSymbolNamePair { + if core.Some(symbol.Declarations, hasNonGlobalAugmentationExternalModuleSymbol) { + return sortedSymbolNamePair{symbol, b.getSpecifierForModuleSymbol(symbol, core.ResolutionModeNone)} + } + return sortedSymbolNamePair{symbol, ""} + }) + slices.SortStableFunc(parentSpecifiers, sortByBestName) + for _, pair := range parentSpecifiers { + parent := pair.sym + parentChain := b.getSymbolChain(parent, getQualifiedLeftMeaning(meaning), false, false) + if len(parentChain) > 0 { + if parent.Exports != nil { + exported, ok := parent.Exports[ast.InternalSymbolNameExportEquals] + if ok && b.ch.getSymbolIfSameReference(exported, symbol) != nil { + // parentChain root _is_ symbol - symbol is a module export=, so it kinda looks like it's own parent + // No need to lookup an alias for the symbol in itself + accessibleSymbolChain = parentChain + } + } + nextSyms := accessibleSymbolChain + if len(nextSyms) == 0 { + fallback := b.ch.getAliasForSymbolInContainer(parent, symbol) + if fallback == nil { + fallback = symbol + } + nextSyms = append(nextSyms, fallback) + } + accessibleSymbolChain = append(parentChain, nextSyms...) + break + } + } + } + } + if len(accessibleSymbolChain) > 0 { + return accessibleSymbolChain + } + if + // If this is the last part of outputting the symbol, always output. The cases apply only to parent symbols. + endOfChain || + // If a parent symbol is an anonymous type, don't write it. + (symbol.Flags&(ast.SymbolFlagsTypeLiteral|ast.SymbolFlagsObjectLiteral) == 0) { + // If a parent symbol is an external module, don't write it. (We prefer just `x` vs `"foo/bar".x`.) + if !endOfChain && !yieldModuleSymbol && !!core.Some(symbol.Declarations, hasNonGlobalAugmentationExternalModuleSymbol) { + return nil + } + return []*ast.Symbol{symbol} + } + return nil +} + +func sortByBestName(a sortedSymbolNamePair, b sortedSymbolNamePair) int { + specifierA := a.name + specifierB := b.name + if len(specifierA) > 0 && len(specifierB) > 0 { + isBRelative := tspath.PathIsRelative(specifierB) + if tspath.PathIsRelative(specifierA) == isBRelative { + // Both relative or both non-relative, sort by number of parts + return modulespecifiers.CountPathComponents(specifierA) - modulespecifiers.CountPathComponents(specifierB) + } + if isBRelative { + // A is non-relative, B is relative: prefer A + return -1 + } + // A is relative, B is non-relative: prefer B + return 1 + } + return 0 +} + +func isAmbientModuleSymbolName(s string) bool { + return strings.HasPrefix(s, "\"") && strings.HasSuffix(s, "\"") +} + +func canHaveModuleSpecifier(node *ast.Node) bool { + if node == nil { + return false + } + switch node.Kind { + case ast.KindVariableDeclaration, + ast.KindBindingElement, + ast.KindImportDeclaration, + ast.KindExportDeclaration, + ast.KindImportEqualsDeclaration, + ast.KindImportClause, + ast.KindNamespaceExport, + ast.KindNamespaceImport, + ast.KindExportSpecifier, + ast.KindImportSpecifier, + ast.KindImportType: + return true + } + return false +} + +func tryGetModuleSpecifierFromDeclaration(node *ast.Node) *ast.Node { + res := tryGetModuleSpecifierFromDeclarationWorker(node) + if !ast.IsStringLiteral(res) { + return nil + } + return res +} + +func tryGetModuleSpecifierFromDeclarationWorker(node *ast.Node) *ast.Node { + switch node.Kind { + case ast.KindVariableDeclaration, ast.KindBindingElement: + requireCall := ast.FindAncestor(node.Initializer(), func(node *ast.Node) bool { + return ast.IsRequireCall(node /*requireStringLiteralLikeArgument*/, true) + }) + if requireCall == nil { + return nil + } + return requireCall.AsCallExpression().Arguments.Nodes[0] + case ast.KindImportDeclaration: + return node.AsImportDeclaration().ModuleSpecifier + case ast.KindExportDeclaration: + return node.AsExportDeclaration().ModuleSpecifier + case ast.KindJSDocImportTag: + return node.AsJSDocImportTag().ModuleSpecifier + case ast.KindImportEqualsDeclaration: + ref := node.AsImportEqualsDeclaration().ModuleReference + if ref.Kind != ast.KindExternalModuleReference { + return nil + } + return ref.AsExternalModuleReference().Expression + case ast.KindImportClause: + if ast.IsImportDeclaration(node.Parent.Parent) { + return node.Parent.AsImportDeclaration().ModuleSpecifier + } + return node.Parent.AsJSDocImportTag().ModuleSpecifier + case ast.KindNamespaceExport: + return node.Parent.AsExportDeclaration().ModuleSpecifier + case ast.KindNamespaceImport: + if ast.IsImportDeclaration(node.Parent.Parent) { + return node.Parent.Parent.AsImportDeclaration().ModuleSpecifier + } + return node.Parent.Parent.AsJSDocImportTag().ModuleSpecifier + case ast.KindExportSpecifier: + return node.Parent.Parent.AsExportDeclaration().ModuleSpecifier + case ast.KindImportSpecifier: + if ast.IsImportDeclaration(node.Parent.Parent.Parent) { + return node.Parent.Parent.Parent.AsImportDeclaration().ModuleSpecifier + } + return node.Parent.Parent.Parent.AsJSDocImportTag().ModuleSpecifier + case ast.KindImportType: + if ast.IsLiteralImportTypeNode(node) { + return node.AsImportTypeNode().Argument.AsLiteralTypeNode().Literal + } + return nil + default: + // Debug.assertNever(node); // !!! + return nil + } +} + +func (b *NodeBuilder) getSpecifierForModuleSymbol(symbol *ast.Symbol, overrideImportMode core.ResolutionMode) string { + file := ast.GetDeclarationOfKind(symbol, ast.KindSourceFile) + if file == nil { + equivalentSymbol := core.FirstNonNil(symbol.Declarations, func(d *ast.Node) *ast.Symbol { + return b.ch.getFileSymbolIfFileSymbolExportEqualsContainer(d, symbol) + }) + if equivalentSymbol != nil { + file = ast.GetDeclarationOfKind(equivalentSymbol, ast.KindSourceFile) + } + } + + if file == nil { + if isAmbientModuleSymbolName(symbol.Name) { + return stripQuotes(symbol.Name) + } + } + if b.ctx.enclosingFile == nil || b.ctx.tracker.GetModuleSpecifierGenerationHost() == nil { + if isAmbientModuleSymbolName(symbol.Name) { + return stripQuotes(symbol.Name) + } + return ast.GetSourceFileOfNode(getNonAugmentationDeclaration(symbol)).FileName() + } + + enclosingDeclaration := b.e.MostOriginal(b.ctx.enclosingDeclaration) + var originalModuleSpecifier *ast.Node + if canHaveModuleSpecifier(enclosingDeclaration) { + originalModuleSpecifier = tryGetModuleSpecifierFromDeclaration(enclosingDeclaration) + } + contextFile := b.ctx.enclosingFile + resolutionMode := core.ResolutionModeNone + if overrideImportMode != core.ResolutionModeNone || originalModuleSpecifier != nil { + // !!! import resolution mode support + //resolutionMode = b.ch.host.GetModeForUsageLocation(contextFile, originalModuleSpecifier) + } else if contextFile != nil { + // !!! import resolution mode support + //resolutionMode = b.ch.host.GetDefaultResolutionModeForFile(contextFile) + } + cacheKey := module.ModeAwareCacheKey{Name: string(contextFile.Path()), Mode: resolutionMode} + links := b.symbolLinks.Get(symbol) + if links.specifierCache == nil { + links.specifierCache = make(module.ModeAwareCache[string]) + } + result, ok := links.specifierCache[cacheKey] + if ok { + return result + } + isBundle := len(b.ch.compilerOptions.OutFile) > 0 + // For declaration bundles, we need to generate absolute paths relative to the common source dir for imports, + // just like how the declaration emitter does for the ambient module declarations - we can easily accomplish this + // using the `baseUrl` compiler option (which we would otherwise never use in declaration emit) and a non-relative + // specifier preference + host := b.ctx.tracker.GetModuleSpecifierGenerationHost() + specifierCompilerOptions := b.ch.compilerOptions + if isBundle { + // !!! relies on option cloning and specifier host implementation + // specifierCompilerOptions = &core.CompilerOptions{BaseUrl: host.GetCommonSourceDirectory()} + // TODO: merge with b.ch.compilerOptions + } + allSpecifiers := modulespecifiers.GetModuleSpecifiers( + symbol, + b.ch, + specifierCompilerOptions, + contextFile, + host, + nil, + nil, + // !!! pass options when unstubbed + // { + // importModuleSpecifierPreference: isBundle ? "non-relative" : "project-relative", + // importModuleSpecifierEnding: isBundle ? "minimal" + // : resolutionMode === ModuleKind.ESNext ? "js" + // : undefined, + // }, + // { overrideImportMode }, + ) + specifier := allSpecifiers[0] + links.specifierCache[cacheKey] = specifier + return specifier +} + +func (b *NodeBuilder) typeParameterToDeclarationWithConstraint(typeParameter *Type, constraintNode *ast.TypeNode) *ast.TypeParameterDeclarationNode { + restoreFlags := b.saveRestoreFlags() + b.ctx.flags &= ^nodebuilder.FlagsWriteTypeParametersInQualifiedName // Avoids potential infinite loop when building for a claimspace with a generic + modifiers := ast.CreateModifiersFromModifierFlags(b.ch.getTypeParameterModifiers(typeParameter), b.f.NewModifier) + var modifiersList *ast.ModifierList + if len(modifiers) > 0 { + modifiersList = b.f.NewModifierList(modifiers) + } + name := b.typeParameterToName(typeParameter) + defaultParameter := b.ch.getDefaultFromTypeParameter(typeParameter) + var defaultParameterNode *ast.Node + if defaultParameter != nil { + defaultParameterNode = b.typeToTypeNodeClosure(defaultParameter) + } + restoreFlags() + return b.f.NewTypeParameterDeclaration( + modifiersList, + name.AsNode(), + constraintNode, + defaultParameterNode, + ) +} + +/** +* Unlike the utilities `setTextRange`, this checks if the `location` we're trying to set on `range` is within the +* same file as the active context. If not, the range is not applied. This prevents us from copying ranges across files, +* which will confuse the node printer (as it assumes all node ranges are within the current file). +* Additionally, if `range` _isn't synthetic_, or isn't in the current file, it will _copy_ it to _remove_ its' position +* information. +* +* It also calls `setOriginalNode` to setup a `.original` pointer, since you basically *always* want these in the node builder. + */ +func (b *NodeBuilder) setTextRange(range_ *ast.Node, location *ast.Node) *ast.Node { + if range_ == nil { + return range_ + } + if !ast.NodeIsSynthesized(range_) || (range_.Flags&ast.NodeFlagsSynthesized == 0) || b.ctx.enclosingFile == nil || b.ctx.enclosingFile != ast.GetSourceFileOfNode(b.e.MostOriginal(range_)) { + range_ = range_.Clone(b.f) // if `range` is synthesized or originates in another file, copy it so it definitely has synthetic positions + } + if range_ == location || location == nil { + return range_ + } + // Don't overwrite the original node if `range` has an `original` node that points either directly or indirectly to `location` + original := b.e.Original(range_) + for original != nil && original != location { + original = b.e.Original(original) + } + if original == nil { + b.e.SetOriginalEx(range_, location, true) + } + + // only set positions if range comes from the same file since copying text across files isn't supported by the emitter + if b.ctx.enclosingFile != nil && b.ctx.enclosingFile == ast.GetSourceFileOfNode(b.e.MostOriginal(range_)) { + range_.Loc = location.Loc + return range_ + } + return range_ +} + +func (b *NodeBuilder) typeParameterShadowsOtherTypeParameterInScope(name string, typeParameter *Type) bool { + result := b.ch.resolveName(b.ctx.enclosingDeclaration, name, ast.SymbolFlagsType, nil, false, false) + if result != nil && result.Flags&ast.SymbolFlagsTypeParameter != 0 { + return result != typeParameter.symbol + } + return false +} + +func (b *NodeBuilder) typeParameterToName(typeParameter *Type) *ast.Identifier { + if b.ctx.flags&nodebuilder.FlagsGenerateNamesForShadowedTypeParams != 0 && b.ctx.typeParameterNames != nil { + cached, ok := b.ctx.typeParameterNames[typeParameter.id] + if ok { + return cached + } + } + result := b.symbolToName(typeParameter.symbol, ast.SymbolFlagsType /*expectsIdentifier*/, true) + if !ast.IsIdentifier(result) { + return b.f.NewIdentifier("(Missing type parameter)").AsIdentifier() + } + if typeParameter.symbol != nil && len(typeParameter.symbol.Declarations) > 0 { + decl := typeParameter.symbol.Declarations[0] + if decl != nil && ast.IsTypeParameterDeclaration(decl) { + result = b.setTextRange(result, decl.Name()) + } + } + if b.ctx.flags&nodebuilder.FlagsGenerateNamesForShadowedTypeParams != 0 { + if !b.ctx.hasCreatedTypeParametersNamesLookups { + b.ctx.hasCreatedTypeParametersNamesLookups = true + b.ctx.typeParameterNames = make(map[TypeId]*ast.Identifier) + b.ctx.typeParameterNamesByText = make(map[string]struct{}) + b.ctx.typeParameterNamesByTextNextNameCount = make(map[string]int) + } + + rawText := result.AsIdentifier().Text + i := 0 + cached, ok := b.ctx.typeParameterNamesByTextNextNameCount[rawText] + if ok { + i = cached + } + text := rawText + + for true { + _, present := b.ctx.typeParameterNamesByText[text] + if !present && !b.typeParameterShadowsOtherTypeParameterInScope(text, typeParameter) { + break + } + i++ + text = fmt.Sprintf("%s_%d", rawText, i) + } + + if text != rawText { + // !!! TODO: smuggle type arguments out + // const typeArguments = getIdentifierTypeArguments(result); + result = b.f.NewIdentifier(text) + // setIdentifierTypeArguments(result, typeArguments); + } + + // avoiding iterations of the above loop turns out to be worth it when `i` starts to get large, so we cache the max + // `i` we've used thus far, to save work later + b.ctx.typeParameterNamesByTextNextNameCount[rawText] = i + b.ctx.typeParameterNames[typeParameter.id] = result.AsIdentifier() + b.ctx.typeParameterNamesByText[text] = struct{}{} + } + + return result.AsIdentifier() +} + +func (b *NodeBuilder) isMappedTypeHomomorphic(mapped *Type) bool { + return b.ch.getHomomorphicTypeVariable(mapped) != nil +} + +func (b *NodeBuilder) isHomomorphicMappedTypeWithNonHomomorphicInstantiation(mapped *MappedType) bool { + return mapped.target != nil && !b.isMappedTypeHomomorphic(mapped.AsType()) && b.isMappedTypeHomomorphic(mapped.target) +} + +func (b *NodeBuilder) createMappedTypeNodeFromType(type_ *Type) *ast.TypeNode { + // Debug.assert(!!(type.flags & TypeFlags.Object)); // !!! + mapped := type_.AsMappedType() + var readonlyToken *ast.Node + if mapped.declaration.ReadonlyToken != nil { + readonlyToken = b.f.NewToken(ast.KindReadonlyKeyword) + } + var questionToken *ast.Node + if mapped.declaration.QuestionToken != nil { + readonlyToken = b.f.NewToken(ast.KindQuestionToken) + } + var appropriateConstraintTypeNode *ast.Node + var newTypeVariable *ast.Node + + // If the mapped type isn't `keyof` constraint-declared, _but_ still has modifiers preserved, and its naive instantiation won't preserve modifiers because its constraint isn't `keyof` constrained, we have work to do + needsModifierPreservingWrapper := !b.ch.isMappedTypeWithKeyofConstraintDeclaration(type_) && + b.ch.getModifiersTypeFromMappedType(type_).flags&TypeFlagsUnknown == 0 && + b.ctx.flags&nodebuilder.FlagsGenerateNamesForShadowedTypeParams != 0 && + !(b.ch.getConstraintTypeFromMappedType(type_).flags&TypeFlagsTypeParameter != 0 && b.ch.getConstraintOfTypeParameter(b.ch.getConstraintTypeFromMappedType(type_)).flags&TypeFlagsIndex != 0) + + if b.ch.isMappedTypeWithKeyofConstraintDeclaration(type_) { + // We have a { [P in keyof T]: X } + // We do this to ensure we retain the toplevel keyof-ness of the type which may be lost due to keyof distribution during `getConstraintTypeFromMappedType` + if b.ctx.flags&nodebuilder.FlagsGenerateNamesForShadowedTypeParams != 0 && b.isHomomorphicMappedTypeWithNonHomomorphicInstantiation(mapped) { + newParam := b.ch.newTypeParameter( + b.ch.newSymbol(ast.SymbolFlagsTypeParameter, "T"), + ) + name := b.typeParameterToName(newParam) + newTypeVariable = b.f.NewTypeReferenceNode(name.AsNode(), nil) + } + indexTarget := newTypeVariable + if indexTarget == nil { + indexTarget = b.typeToTypeNodeClosure(b.ch.getModifiersTypeFromMappedType(type_)) + } + appropriateConstraintTypeNode = b.f.NewTypeOperatorNode(ast.KindKeyOfKeyword, indexTarget) + } else if needsModifierPreservingWrapper { + // So, step 1: new type variable + newParam := b.ch.newTypeParameter( + b.ch.newSymbol(ast.SymbolFlagsTypeParameter, "T"), + ) + name := b.typeParameterToName(newParam) + newTypeVariable = b.f.NewTypeReferenceNode(name.AsNode(), nil) + // step 2: make that new type variable itself the constraint node, making the mapped type `{[K in T_1]: Template}` + appropriateConstraintTypeNode = newTypeVariable + } else { + appropriateConstraintTypeNode = b.typeToTypeNodeClosure(b.ch.getConstraintTypeFromMappedType(type_)) + } + + typeParameterNode := b.typeParameterToDeclarationWithConstraint(b.ch.getTypeParameterFromMappedType(type_), appropriateConstraintTypeNode) + var nameTypeNode *ast.Node + if mapped.declaration.NameType != nil { + nameTypeNode = b.typeToTypeNodeClosure(b.ch.getNameTypeFromMappedType(type_)) + } + templateTypeNode := b.typeToTypeNodeClosure(b.ch.removeMissingType( + b.ch.getTemplateTypeFromMappedType(type_), + getMappedTypeModifiers(type_)&MappedTypeModifiersIncludeOptional != 0, + )) + result := b.f.NewMappedTypeNode( + readonlyToken, + typeParameterNode, + nameTypeNode, + questionToken, + templateTypeNode, + nil, + ) + b.e.AddEmitFlags(result, printer.EFSingleLine) + + if b.ctx.flags&nodebuilder.FlagsGenerateNamesForShadowedTypeParams != 0 && b.isHomomorphicMappedTypeWithNonHomomorphicInstantiation(mapped) { + // homomorphic mapped type with a non-homomorphic naive inlining + // wrap it with a conditional like `SomeModifiersType extends infer U ? {..the mapped type...} : never` to ensure the resulting + // type stays homomorphic + + rawConstraintTypeFromDeclaration := b.getTypeFromTypeNode(mapped.declaration.TypeParameter.AsTypeParameter().Constraint.AsTypeOperatorNode().Type, false) + if rawConstraintTypeFromDeclaration != nil { + rawConstraintTypeFromDeclaration = b.ch.getConstraintOfTypeParameter(rawConstraintTypeFromDeclaration) + } + if rawConstraintTypeFromDeclaration == nil { + rawConstraintTypeFromDeclaration = b.ch.unknownType + } + originalConstraint := b.ch.instantiateType(rawConstraintTypeFromDeclaration, mapped.mapper) + + var originalConstraintNode *ast.Node + if originalConstraint.flags&TypeFlagsUnknown != 0 { + originalConstraintNode = b.typeToTypeNodeClosure(originalConstraint) + } + + return b.f.NewConditionalTypeNode( + b.typeToTypeNodeClosure(b.ch.getModifiersTypeFromMappedType(type_)), + b.f.NewInferTypeNode(b.f.NewTypeParameterDeclaration(nil, newTypeVariable.AsTypeReference().TypeName.Clone(b.f), originalConstraintNode, nil)), + result, + b.f.NewKeywordTypeNode(ast.KindNeverKeyword), + ) + } else if needsModifierPreservingWrapper { + // and step 3: once the mapped type is reconstructed, create a `ConstraintType extends infer T_1 extends keyof ModifiersType ? {[K in T_1]: Template} : never` + // subtly different from the `keyof` constraint case, by including the `keyof` constraint on the `infer` type parameter, it doesn't rely on the constraint type being itself + // constrained to a `keyof` type to preserve its modifier-preserving behavior. This is all basically because we preserve modifiers for a wider set of mapped types than + // just homomorphic ones. + return b.f.NewConditionalTypeNode( + b.typeToTypeNodeClosure(b.ch.getConstraintTypeFromMappedType(type_)), + b.f.NewInferTypeNode(b.f.NewTypeParameterDeclaration(nil, newTypeVariable.AsTypeReference().TypeName.Clone(b.f), b.f.NewTypeOperatorNode(ast.KindKeyOfKeyword, b.typeToTypeNodeClosure(b.ch.getModifiersTypeFromMappedType(type_))), nil)), + result, + b.f.NewKeywordTypeNode(ast.KindNeverKeyword), + ) + } + + return result +} + +func (b *NodeBuilder) typePredicateToTypePredicateNode(predicate *TypePredicate) *ast.Node { + var assertsModifier *ast.Node + if predicate.kind == TypePredicateKindAssertsIdentifier || predicate.kind == TypePredicateKindAssertsThis { + assertsModifier = b.f.NewToken(ast.KindAssertsKeyword) + } + var parameterName *ast.Node + if predicate.kind == TypePredicateKindIdentifier || predicate.kind == TypePredicateKindAssertsIdentifier { + parameterName = b.f.NewIdentifier(predicate.parameterName) + b.e.AddEmitFlags(parameterName, printer.EFNoAsciiEscaping) + } else { + parameterName = b.f.NewThisTypeNode() + } + var typeNode *ast.Node + if predicate.t != nil { + typeNode = b.typeToTypeNodeClosure(predicate.t) + } + return b.f.NewTypePredicateNode( + assertsModifier, + parameterName, + typeNode, + ) +} + +func (b *NodeBuilder) typeToTypeNodeHelperWithPossibleReusableTypeNode(type_ *Type, typeNode *ast.TypeNode) *ast.TypeNode { + if type_ == nil { + return b.f.NewKeywordTypeNode(ast.KindAnyKeyword) + } + if typeNode != nil && b.getTypeFromTypeNode(typeNode, false) == type_ { + reused := b.tryReuseExistingTypeNodeHelper(typeNode) + if reused != nil { + return reused + } + } + return b.typeToTypeNodeClosure(type_) +} + +func (b *NodeBuilder) typeParameterToDeclaration(parameter *Type) *ast.Node { + constraint := b.ch.getConstraintOfTypeParameter(parameter) + var constraintNode *ast.Node + if constraint != nil { + constraintNode = b.typeToTypeNodeHelperWithPossibleReusableTypeNode(constraint, b.ch.getConstraintDeclaration(parameter)) + } + return b.typeParameterToDeclarationWithConstraint(parameter, constraintNode) +} + +func (b *NodeBuilder) symbolToTypeParameterDeclarations(symbol *ast.Symbol) []*ast.Node { + return b.typeParametersToTypeParameterDeclarations(symbol) +} + +func (b *NodeBuilder) typeParametersToTypeParameterDeclarations(symbol *ast.Symbol) []*ast.Node { + targetSymbol := b.ch.getTargetSymbol(symbol) + if targetSymbol.Flags&(ast.SymbolFlagsClass|ast.SymbolFlagsInterface|ast.SymbolFlagsAlias) != 0 { + var results []*ast.Node + params := b.ch.getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol) + for _, param := range params { + results = append(results, b.typeParameterToDeclaration(param)) + } + return results + } + return nil +} + +func getEffectiveParameterDeclaration(symbol *ast.Symbol) *ast.Node { + parameterDeclaration := ast.GetDeclarationOfKind(symbol, ast.KindParameter) + if parameterDeclaration != nil { + return parameterDeclaration + } + if symbol.Flags&ast.SymbolFlagsTransient == 0 { + return ast.GetDeclarationOfKind(symbol, ast.KindJSDocParameterTag) + } + return nil +} + +func (b *NodeBuilder) symbolToParameterDeclaration(parameterSymbol *ast.Symbol, preserveModifierFlags bool) *ast.Node { + parameterDeclaration := getEffectiveParameterDeclaration(parameterSymbol) + + parameterType := b.ch.getTypeOfSymbol(parameterSymbol) + parameterTypeNode := b.serializeTypeForDeclaration(parameterDeclaration, parameterType, parameterSymbol) + var modifiers *ast.ModifierList + if b.ctx.flags&nodebuilder.FlagsOmitParameterModifiers == 0 && preserveModifierFlags && parameterDeclaration != nil && ast.CanHaveModifiers(parameterDeclaration) { + originals := core.Filter(parameterDeclaration.Modifiers().Nodes, ast.IsModifier) + clones := core.Map(originals, func(node *ast.Node) *ast.Node { return node.Clone(b.f) }) + if len(clones) > 0 { + modifiers = b.f.NewModifierList(clones) + } + } + isRest := parameterDeclaration != nil && isRestParameter(parameterDeclaration) || parameterSymbol.CheckFlags&ast.CheckFlagsRestParameter != 0 + var dotDotDotToken *ast.Node + if isRest { + dotDotDotToken = b.f.NewToken(ast.KindDotDotDotToken) + } + name := b.parameterToParameterDeclarationName(parameterSymbol, parameterDeclaration) + // TODO: isOptionalParameter on emit resolver here is silly - hoist to checker and reexpose on emit resolver? + isOptional := parameterDeclaration != nil && b.ch.GetEmitResolver(nil, true).isOptionalParameter(parameterDeclaration) || parameterSymbol.CheckFlags&ast.CheckFlagsOptionalParameter != 0 + var questionToken *ast.Node + if isOptional { + questionToken = b.f.NewToken(ast.KindQuestionToken) + } + + parameterNode := b.f.NewParameterDeclaration( + modifiers, + dotDotDotToken, + name, + questionToken, + parameterTypeNode, + /*initializer*/ nil, + ) + b.ctx.approximateLength += len(parameterSymbol.Name) + 3 + return parameterNode +} + +func (b *NodeBuilder) parameterToParameterDeclarationName(parameterSymbol *ast.Symbol, parameterDeclaration *ast.Node) *ast.Node { + if parameterDeclaration == nil || parameterDeclaration.Name() == nil { + return b.f.NewIdentifier(parameterSymbol.Name) + } + // !!! TODO - symbol tracking of computed names in cloned binding patterns, set singleline emit flags + return b.f.DeepCloneNode(parameterDeclaration.Name()) +} + +func (b *NodeBuilder) symbolTableToDeclarationStatements(symbolTable *ast.SymbolTable) []*ast.Node { + panic("unimplemented") // !!! +} + +func (b *NodeBuilder) serializeTypeForExpression(expr *ast.Node) *ast.Node { + // !!! TODO: shim, add node reuse + type_ := b.ch.instantiateType(b.ch.getWidenedType(b.ch.getRegularTypeOfExpression(expr)), b.ctx.mapper) + return b.typeToTypeNodeClosure(type_) +} + +func (b *NodeBuilder) serializeInferredReturnTypeForSignature(signature *Signature, returnType *Type) *ast.Node { + oldSuppressReportInferenceFallback := b.ctx.suppressReportInferenceFallback + b.ctx.suppressReportInferenceFallback = true + typePredicate := b.ch.getTypePredicateOfSignature(signature) + var returnTypeNode *ast.Node + if typePredicate != nil { + var predicate *TypePredicate + if b.ctx.mapper != nil { + predicate = b.ch.instantiateTypePredicate(typePredicate, b.ctx.mapper) + } else { + predicate = typePredicate + } + returnTypeNode = b.typePredicateToTypePredicateNodeHelper(predicate) + } else { + returnTypeNode = b.typeToTypeNodeClosure(returnType) + } + b.ctx.suppressReportInferenceFallback = oldSuppressReportInferenceFallback + return returnTypeNode +} + +func (b *NodeBuilder) typePredicateToTypePredicateNodeHelper(typePredicate *TypePredicate) *ast.Node { + var assertsModifier *ast.Node + if typePredicate.kind == TypePredicateKindAssertsThis || typePredicate.kind == TypePredicateKindAssertsIdentifier { + assertsModifier = b.f.NewToken(ast.KindAssertsKeyword) + } else { + assertsModifier = nil + } + var parameterName *ast.Node + if typePredicate.kind == TypePredicateKindIdentifier || typePredicate.kind == TypePredicateKindAssertsIdentifier { + parameterName = b.f.NewIdentifier(typePredicate.parameterName) + b.e.SetEmitFlags(parameterName, printer.EFNoAsciiEscaping) + } else { + parameterName = b.f.NewThisTypeNode() + } + var typeNode *ast.Node + if typePredicate.t != nil { + typeNode = b.typeToTypeNodeClosure(typePredicate.t) + } + return b.f.NewTypePredicateNode(assertsModifier, parameterName, typeNode) +} + +type SignatureToSignatureDeclarationOptions struct { + modifiers []*ast.Node + name *ast.PropertyName + questionToken *ast.Node +} + +func (b *NodeBuilder) signatureToSignatureDeclarationHelper(signature *Signature, kind ast.Kind, options *SignatureToSignatureDeclarationOptions) *ast.Node { + var typeParameters *[]*ast.Node + var typeArguments *[]*ast.Node + + expandedParams := b.ch.getExpandedParameters(signature, true /*skipUnionExpanding*/)[0] + cleanup := b.enterNewScope(signature.declaration, &expandedParams, &signature.typeParameters, &signature.parameters, signature.mapper) + b.ctx.approximateLength += 3 + // Usually a signature contributes a few more characters than this, but 3 is the minimum + + if b.ctx.flags&nodebuilder.FlagsWriteTypeArgumentsOfSignature != 0 && signature.target != nil && signature.mapper != nil && signature.target.typeParameters != nil { + for _, parameter := range signature.target.typeParameters { + node := b.typeToTypeNodeClosure(b.ch.instantiateType(parameter, signature.mapper)) + if typeArguments == nil { + typeArguments = &[]*ast.Node{} + } + args := append(*typeArguments, node) + typeArguments = &args + } + } else if signature.typeParameters != nil { + for _, parameter := range signature.typeParameters { + node := b.typeParameterToDeclaration(parameter) + if typeParameters == nil { + typeParameters = &[]*ast.Node{} + } + args := append(*typeParameters, node) + typeParameters = &args + } + } + + restoreFlags := b.saveRestoreFlags() + b.ctx.flags &^= nodebuilder.FlagsSuppressAnyReturnType + // If the expanded parameter list had a variadic in a non-trailing position, don't expand it + parameters := core.Map(core.IfElse(core.Some(expandedParams, func(p *ast.Symbol) bool { + return p != expandedParams[len(expandedParams)-1] && p.CheckFlags&ast.CheckFlagsRestParameter != 0 + }), signature.parameters, expandedParams), func(parameter *ast.Symbol) *ast.Node { + return b.symbolToParameterDeclaration(parameter, kind == ast.KindConstructor) + }) + var thisParameter *ast.Node + if b.ctx.flags&nodebuilder.FlagsOmitThisParameter != 0 { + thisParameter = nil + } else { + thisParameter = b.tryGetThisParameterDeclaration(signature) + } + if thisParameter != nil { + parameters = append([]*ast.Node{thisParameter}, parameters...) + } + restoreFlags() + + returnTypeNode := b.serializeReturnTypeForSignature(signature) + + var modifiers []*ast.Node + if options != nil { + modifiers = options.modifiers + } + if (kind == ast.KindConstructorType) && signature.flags&SignatureFlagsAbstract != 0 { + flags := ast.ModifiersToFlags(modifiers) + modifiers = ast.CreateModifiersFromModifierFlags(flags|ast.ModifierFlagsAbstract, b.f.NewModifier) + } + + paramList := b.f.NewNodeList(parameters) + var typeParamList *ast.NodeList + if typeParameters != nil { + typeParamList = b.f.NewNodeList(*typeParameters) + } + var modifierList *ast.ModifierList + if modifiers != nil && len(modifiers) > 0 { + modifierList = b.f.NewModifierList(modifiers) + } + var name *ast.Node + if options != nil { + name = options.name + } + if name == nil { + name = b.f.NewIdentifier("") + } + + var node *ast.Node + switch { + case kind == ast.KindCallSignature: + node = b.f.NewCallSignatureDeclaration(typeParamList, paramList, returnTypeNode) + case kind == ast.KindConstructSignature: + node = b.f.NewConstructSignatureDeclaration(typeParamList, paramList, returnTypeNode) + case kind == ast.KindMethodSignature: + var questionToken *ast.Node + if options != nil { + questionToken = options.questionToken + } + node = b.f.NewMethodSignatureDeclaration(modifierList, name, questionToken, typeParamList, paramList, returnTypeNode) + case kind == ast.KindMethodDeclaration: + node = b.f.NewMethodDeclaration(modifierList, nil /*asteriskToken*/, name, nil /*questionToken*/, typeParamList, paramList, returnTypeNode, nil /*body*/) + case kind == ast.KindConstructor: + node = b.f.NewConstructorDeclaration(modifierList, nil /*typeParamList*/, paramList, nil /*returnTypeNode*/, nil /*body*/) + case kind == ast.KindGetAccessor: + node = b.f.NewGetAccessorDeclaration(modifierList, name, nil /*typeParamList*/, paramList, returnTypeNode, nil /*body*/) + case kind == ast.KindSetAccessor: + node = b.f.NewSetAccessorDeclaration(modifierList, name, nil /*typeParamList*/, paramList, nil /*returnTypeNode*/, nil /*body*/) + case kind == ast.KindIndexSignature: + node = b.f.NewIndexSignatureDeclaration(modifierList, paramList, returnTypeNode) + // !!! JSDoc Support + // case kind == ast.KindJSDocFunctionType: + // node = b.f.NewJSDocFunctionType(parameters, returnTypeNode) + case kind == ast.KindFunctionType: + if returnTypeNode == nil { + returnTypeNode = b.f.NewTypeReferenceNode(b.f.NewIdentifier(""), nil) + } + node = b.f.NewFunctionTypeNode(typeParamList, paramList, returnTypeNode) + case kind == ast.KindConstructorType: + if returnTypeNode == nil { + returnTypeNode = b.f.NewTypeReferenceNode(b.f.NewIdentifier(""), nil) + } + node = b.f.NewConstructorTypeNode(modifierList, typeParamList, paramList, returnTypeNode) + case kind == ast.KindFunctionDeclaration: + // TODO: assert name is Identifier + node = b.f.NewFunctionDeclaration(modifierList, nil /*asteriskToken*/, name, typeParamList, paramList, returnTypeNode, nil /*body*/) + case kind == ast.KindFunctionExpression: + // TODO: assert name is Identifier + node = b.f.NewFunctionExpression(modifierList, nil /*asteriskToken*/, name, typeParamList, paramList, returnTypeNode, b.f.NewBlock(b.f.NewNodeList([]*ast.Node{}), false)) + case kind == ast.KindArrowFunction: + node = b.f.NewArrowFunction(modifierList, typeParamList, paramList, returnTypeNode, nil /*equalsGreaterThanToken*/, b.f.NewBlock(b.f.NewNodeList([]*ast.Node{}), false)) + default: + panic("Unhandled kind in signatureToSignatureDeclarationHelper") + } + + // !!! TODO: Smuggle type arguments of signatures out for quickinfo + // if typeArguments != nil { + // node.TypeArguments = b.f.NewNodeList(typeArguments) + // } + // !!! TODO: synthetic comment support + // if signature.declaration. /* ? */ kind == ast.KindJSDocSignature && signature.declaration.Parent.Kind == ast.KindJSDocOverloadTag { + // comment := getTextOfNode(signature.declaration.Parent.Parent, true /*includeTrivia*/).slice(2, -2).split(regexp.MustParse(`\r\n|\n|\r`)).map_(func(line string) string { + // return line.replace(regexp.MustParse(`^\s+`), " ") + // }).join("\n") + // addSyntheticLeadingComment(node, ast.KindMultiLineCommentTrivia, comment, true /*hasTrailingNewLine*/) + // } + + cleanup() + return node +} + +func (c *Checker) getExpandedParameters(sig *Signature, skipUnionExpanding bool) [][]*ast.Symbol { + if signatureHasRestParameter(sig) { + restIndex := len(sig.parameters) - 1 + restSymbol := sig.parameters[restIndex] + restType := c.getTypeOfSymbol(restSymbol) + getUniqAssociatedNamesFromTupleType := func(t *Type, restSymbol *ast.Symbol) []string { + names := core.MapIndex(t.Target().AsTupleType().elementInfos, func(info TupleElementInfo, i int) string { + return c.getTupleElementLabel(info, restSymbol, i) + }) + if len(names) > 0 { + duplicates := []int{} + uniqueNames := make(map[string]bool) + for i, name := range names { + _, ok := uniqueNames[name] + if ok { + duplicates = append(duplicates, i) + } else { + uniqueNames[name] = true + } + } + counters := make(map[string]int) + for _, i := range duplicates { + counter, ok := counters[names[i]] + if !ok { + counter = 1 + } + var name string + for true { + name = fmt.Sprintf("%s_%d", names[i], counter) + _, ok := uniqueNames[name] + if ok { + counter++ + continue + } else { + uniqueNames[name] = true + break + } + } + names[i] = name + counters[names[i]] = counter + 1 + } + } + return names + } + expandSignatureParametersWithTupleMembers := func(restType *Type, restIndex int, restSymbol *ast.Symbol) []*ast.Symbol { + elementTypes := c.getTypeArguments(restType) + associatedNames := getUniqAssociatedNamesFromTupleType(restType, restSymbol) + restParams := core.MapIndex(elementTypes, func(t *Type, i int) *ast.Symbol { + // Lookup the label from the individual tuple passed in before falling back to the signature `rest` parameter name + // TODO: getTupleElementLabel can no longer fail, investigate if this lack of falliability meaningfully changes output + // var name *string + // if associatedNames != nil && associatedNames[i] != nil { + // name = associatedNames[i] + // } else { + // name = c.getParameterNameAtPosition(sig, restIndex+i, restType) + // } + name := associatedNames[i] + flags := restType.Target().AsTupleType().elementInfos[i].flags + var checkFlags ast.CheckFlags + switch { + case flags&ElementFlagsVariable != 0: + checkFlags = ast.CheckFlagsRestParameter + case flags&ElementFlagsOptional != 0: + checkFlags = ast.CheckFlagsOptionalParameter + default: + checkFlags = 0 + } + symbol := c.newSymbolEx(ast.SymbolFlagsFunctionScopedVariable, name, checkFlags) + links := c.valueSymbolLinks.Get(symbol) + if flags&ElementFlagsRest != 0 { + links.resolvedType = c.createArrayType(t) + } else { + links.resolvedType = t + } + return symbol + }) + return core.Concatenate(sig.parameters[0:restIndex], restParams) + } + + if isTupleType(restType) { + return [][]*ast.Symbol{expandSignatureParametersWithTupleMembers(restType, restIndex, restSymbol)} + } else if !skipUnionExpanding && restType.flags&TypeFlagsUnion != 0 && core.Every(restType.AsUnionType().types, isTupleType) { + return core.Map(restType.AsUnionType().types, func(t *Type) []*ast.Symbol { + return expandSignatureParametersWithTupleMembers(t, restIndex, restSymbol) + }) + } + } + return [][]*ast.Symbol{sig.parameters} + +} + +func (b *NodeBuilder) tryGetThisParameterDeclaration(signature *Signature) *ast.Node { + if signature.thisParameter != nil { + return b.symbolToParameterDeclaration(signature.thisParameter, false) + } + if signature.declaration != nil && ast.IsInJSFile(signature.declaration) { + // !!! JSDoc Support + // thisTag := getJSDocThisTag(signature.declaration) + // if (thisTag && thisTag.typeExpression) { + // return factory.createParameterDeclaration( + // /*modifiers*/ undefined, + // /*dotDotDotToken*/ undefined, + // "this", + // /*questionToken*/ undefined, + // typeToTypeNodeHelper(getTypeFromTypeNode(context, thisTag.typeExpression), context), + // ); + // } + } + return nil +} + +/** +* Serializes the return type of the signature by first trying to use the syntactic printer if possible and falling back to the checker type if not. + */ +func (b *NodeBuilder) serializeReturnTypeForSignature(signature *Signature) *ast.Node { + suppressAny := b.ctx.flags&nodebuilder.FlagsSuppressAnyReturnType != 0 + restoreFlags := b.saveRestoreFlags() + if suppressAny { + b.ctx.flags &= ^nodebuilder.FlagsSuppressAnyReturnType // suppress only toplevel `any`s + } + var returnTypeNode *ast.Node + + returnType := b.ch.getReturnTypeOfSignature(signature) + if !(suppressAny && IsTypeAny(returnType)) { + // !!! IsolatedDeclaration support + // if signature.declaration != nil && !ast.NodeIsSynthesized(signature.declaration) { + // declarationSymbol := b.ch.getSymbolOfDeclaration(signature.declaration) + // restore := addSymbolTypeToContext(declarationSymbol, returnType) + // returnTypeNode = syntacticNodeBuilder.serializeReturnTypeForSignature(signature.declaration, declarationSymbol) + // restore() + // } + if returnTypeNode == nil { + returnTypeNode = b.serializeInferredReturnTypeForSignature(signature, returnType) + } + } + + if returnTypeNode == nil && !suppressAny { + returnTypeNode = b.f.NewKeywordTypeNode(ast.KindAnyKeyword) + } + restoreFlags() + return returnTypeNode +} + +func (b *NodeBuilder) indexInfoToIndexSignatureDeclarationHelper(indexInfo *IndexInfo, typeNode *ast.TypeNode) *ast.Node { + name := getNameFromIndexInfo(indexInfo) + indexerTypeNode := b.typeToTypeNodeClosure(indexInfo.keyType) + + indexingParameter := b.f.NewParameterDeclaration(nil, nil, b.f.NewIdentifier(name), nil, indexerTypeNode, nil) + if typeNode == nil { + if indexInfo.valueType == nil { + typeNode = b.f.NewKeywordTypeNode(ast.KindAnyKeyword) + } else { + typeNode = b.typeToTypeNodeClosure(indexInfo.valueType) + } + } + if indexInfo.valueType == nil && b.ctx.flags&nodebuilder.FlagsAllowEmptyIndexInfoType == 0 { + b.ctx.encounteredError = true + } + b.ctx.approximateLength += len(name) + 4 + var modifiers *ast.ModifierList + if indexInfo.isReadonly { + b.ctx.approximateLength += 9 + modifiers = b.f.NewModifierList([]*ast.Node{b.f.NewModifier(ast.KindReadonlyKeyword)}) + } + return b.f.NewIndexSignatureDeclaration(modifiers, b.f.NewNodeList([]*ast.Node{indexingParameter}), typeNode) +} + +/** +* Unlike `typeToTypeNodeHelper`, this handles setting up the `AllowUniqueESSymbolType` flag +* so a `unique symbol` is returned when appropriate for the input symbol, rather than `typeof sym` +* @param declaration - The preferred declaration to pull existing type nodes from (the symbol will be used as a fallback to find any annotated declaration) +* @param type - The type to write; an existing annotation must match this type if it's used, otherwise this is the type serialized as a new type node +* @param symbol - The symbol is used both to find an existing annotation if declaration is not provided, and to determine if `unique symbol` should be printed + */ +func (b *NodeBuilder) serializeTypeForDeclaration(declaration *ast.Declaration, t *Type, symbol *ast.Symbol) *ast.Node { + // !!! node reuse logic + if symbol == nil { + symbol = b.ch.getSymbolOfDeclaration(declaration) + } + if t == nil { + t = b.ctx.enclosingSymbolTypes[ast.GetSymbolId(symbol)] + if t == nil { + if symbol.Flags&ast.SymbolFlagsAccessor != 0 && declaration.Kind == ast.KindSetAccessor { + t = b.ch.instantiateType(b.ch.getWriteTypeOfSymbol(symbol), b.ctx.mapper) + } else if symbol != nil && (symbol.Flags&(ast.SymbolFlagsTypeLiteral|ast.SymbolFlagsSignature) == 0) { + t = b.ch.instantiateType(b.ch.getWidenedLiteralType(b.ch.getTypeOfSymbol(symbol)), b.ctx.mapper) + } else { + t = b.ch.errorType + } + } + // !!! TODO: JSDoc, getEmitResolver call is unfortunate layering for the helper - hoist it into checker + addUndefinedForParameter := declaration != nil && (ast.IsParameter(declaration) /*|| ast.IsJSDocParameterTag(declaration)*/) && b.ch.GetEmitResolver(nil, true).RequiresAddingImplicitUndefined(declaration, symbol, b.ctx.enclosingDeclaration) + if addUndefinedForParameter { + t = b.ch.getOptionalType(t, false) + } + } + + restoreFlags := b.saveRestoreFlags() + if t.flags&TypeFlagsUniqueESSymbol != 0 && t.symbol == symbol && (b.ctx.enclosingDeclaration == nil || core.Some(symbol.Declarations, func(d *ast.Declaration) bool { + return ast.GetSourceFileOfNode(d) == b.ctx.enclosingFile + })) { + b.ctx.flags |= nodebuilder.FlagsAllowUniqueESSymbolType + } + result := b.typeToTypeNodeClosure(t) // !!! expressionOrTypeToTypeNode + restoreFlags() + return result +} + +const MAX_REVERSE_MAPPED_NESTING_INSPECTION_DEPTH = 3 + +func (b *NodeBuilder) shouldUsePlaceholderForProperty(propertySymbol *ast.Symbol) bool { + // Use placeholders for reverse mapped types we've either + // (1) already descended into, or + // (2) are nested reverse mappings within a mapping over a non-anonymous type, or + // (3) are deeply nested properties that originate from the same mapped type. + // Condition (2) is a restriction mostly just to + // reduce the blowup in printback size from doing, eg, a deep reverse mapping over `Window`. + // Since anonymous types usually come from expressions, this allows us to preserve the output + // for deep mappings which likely come from expressions, while truncating those parts which + // come from mappings over library functions. + // Condition (3) limits printing of possibly infinitely deep reverse mapped types. + if propertySymbol.CheckFlags&ast.CheckFlagsReverseMapped == 0 { + return false + } + // (1) + for _, elem := range b.ctx.reverseMappedStack { + if elem == propertySymbol { + return true + } + } + // (2) + if len(b.ctx.reverseMappedStack) > 0 { + last := b.ctx.reverseMappedStack[len(b.ctx.reverseMappedStack)-1] + if b.ch.ReverseMappedSymbolLinks.Has(last) { + links := b.ch.ReverseMappedSymbolLinks.TryGet(last) + propertyType := links.propertyType + if propertyType != nil && propertyType.objectFlags&ObjectFlagsAnonymous == 0 { + return true + } + } + } + // (3) - we only inspect the last MAX_REVERSE_MAPPED_NESTING_INSPECTION_DEPTH elements of the + // stack for approximate matches to catch tight infinite loops + // TODO: Why? Reasoning lost to time. this could probably stand to be improved? + if len(b.ctx.reverseMappedStack) < MAX_REVERSE_MAPPED_NESTING_INSPECTION_DEPTH { + return false + } + if !b.ch.ReverseMappedSymbolLinks.Has(propertySymbol) { + return false + } + propertyLinks := b.ch.ReverseMappedSymbolLinks.TryGet(propertySymbol) + propMappedType := propertyLinks.mappedType + if propMappedType == nil || propMappedType.symbol == nil { + return false + } + for i := range b.ctx.reverseMappedStack { + if i > MAX_REVERSE_MAPPED_NESTING_INSPECTION_DEPTH { + break + } + prop := b.ctx.reverseMappedStack[len(b.ctx.reverseMappedStack)-1-i] + if b.ch.ReverseMappedSymbolLinks.Has(prop) { + links := b.ch.ReverseMappedSymbolLinks.TryGet(prop) + mappedType := links.mappedType + if mappedType != nil && mappedType.symbol == propMappedType.symbol { + return true + } + } + } + return false +} + +func (b *NodeBuilder) trackComputedName(accessExpression *ast.Node, enclosingDeclaration *ast.Node) { + // get symbol of the first identifier of the entityName + firstIdentifier := ast.GetFirstIdentifier(accessExpression) + name := b.ch.resolveName(firstIdentifier, firstIdentifier.Text(), ast.SymbolFlagsValue|ast.SymbolFlagsExportValue, nil /*nameNotFoundMessage*/, true /*isUse*/, false) + if name != nil { + b.ctx.tracker.TrackSymbol(name, enclosingDeclaration, ast.SymbolFlagsValue) + } +} + +func (b *NodeBuilder) createPropertyNameNodeForIdentifierOrLiteral(name string, target core.ScriptTarget, _singleQuote bool, stringNamed bool, isMethod bool) *ast.Node { + isMethodNamedNew := isMethod && name == "new" + if !isMethodNamedNew && scanner.IsIdentifierText(name, target) { + return b.f.NewIdentifier(name) + } + if !stringNamed && !isMethodNamedNew && isNumericLiteralName(name) && jsnum.FromString(name) >= 0 { + return b.f.NewNumericLiteral(name) + } + result := b.f.NewStringLiteral(name) + // !!! TODO: set singleQuote + return result +} + +func (b *NodeBuilder) isStringNamed(d *ast.Declaration) bool { + name := ast.GetNameOfDeclaration(d) + if name == nil { + return false + } + if ast.IsComputedPropertyName(name) { + t := b.ch.checkExpression(name.AsComputedPropertyName().Expression) + return t.flags&TypeFlagsStringLike != 0 + } + if ast.IsElementAccessExpression(name) { + t := b.ch.checkExpression(name.AsElementAccessExpression().ArgumentExpression) + return t.flags&TypeFlagsStringLike != 0 + } + return ast.IsStringLiteral(name) +} + +func (b *NodeBuilder) isSingleQuotedStringNamed(d *ast.Declaration) bool { + return false // !!! + // TODO: actually support single-quote-style-maintenance + // name := ast.GetNameOfDeclaration(d) + // return name != nil && ast.IsStringLiteral(name) && (name.AsStringLiteral().SingleQuote || !nodeIsSynthesized(name) && startsWith(getTextOfNode(name, false /*includeTrivia*/), "'")) +} + +func (b *NodeBuilder) getPropertyNameNodeForSymbol(symbol *ast.Symbol) *ast.Node { + stringNamed := len(symbol.Declarations) != 0 && core.Every(symbol.Declarations, b.isStringNamedClosure) + singleQuote := len(symbol.Declarations) != 0 && core.Every(symbol.Declarations, b.isSingleQuotedStringNamedClosure) + isMethod := symbol.Flags&ast.SymbolFlagsMethod != 0 + fromNameType := b.getPropertyNameNodeForSymbolFromNameType(symbol, singleQuote, stringNamed, isMethod) + if fromNameType != nil { + return fromNameType + } + return b.createPropertyNameNodeForIdentifierOrLiteral(symbol.Name, b.ch.compilerOptions.GetEmitScriptTarget(), singleQuote, stringNamed, isMethod) +} + +// See getNameForSymbolFromNameType for a stringy equivalent +func (b *NodeBuilder) getPropertyNameNodeForSymbolFromNameType(symbol *ast.Symbol, singleQuote bool, stringNamed bool, isMethod bool) *ast.Node { + if !b.ch.valueSymbolLinks.Has(symbol) { + return nil + } + nameType := b.ch.valueSymbolLinks.TryGet(symbol).nameType + if nameType == nil { + return nil + } + if nameType.flags&TypeFlagsStringOrNumberLiteral != 0 { + var name string + switch nameType.AsLiteralType().value.(type) { + case jsnum.Number: + name = nameType.AsLiteralType().value.(jsnum.Number).String() + case string: + name = nameType.AsLiteralType().value.(string) + } + if !scanner.IsIdentifierText(name, b.ch.compilerOptions.GetEmitScriptTarget()) && (stringNamed || !isNumericLiteralName(name)) { + // !!! TODO: set singleQuote + return b.f.NewStringLiteral(name) + } + if isNumericLiteralName(name) && name[0] == '-' { + return b.f.NewComputedPropertyName(b.f.NewPrefixUnaryExpression(ast.KindMinusToken, b.f.NewNumericLiteral(name[1:]))) + } + return b.createPropertyNameNodeForIdentifierOrLiteral(name, b.ch.compilerOptions.GetEmitScriptTarget(), singleQuote, stringNamed, isMethod) + } + if nameType.flags&TypeFlagsUniqueESSymbol != 0 { + return b.f.NewComputedPropertyName(b.symbolToExpression(nameType.AsUniqueESSymbolType().symbol, ast.SymbolFlagsValue)) + } + return nil +} + +func (b *NodeBuilder) addPropertyToElementList(propertySymbol *ast.Symbol, typeElements []*ast.TypeElement) []*ast.TypeElement { + propertyIsReverseMapped := propertySymbol.CheckFlags&ast.CheckFlagsReverseMapped != 0 + var propertyType *Type + if b.shouldUsePlaceholderForProperty(propertySymbol) { + propertyType = b.ch.anyType + } else { + propertyType = b.ch.getNonMissingTypeOfSymbol(propertySymbol) + } + saveEnclosingDeclaration := b.ctx.enclosingDeclaration + b.ctx.enclosingDeclaration = nil + if isLateBoundName(propertySymbol.Name) { + if len(propertySymbol.Declarations) > 0 { + decl := propertySymbol.Declarations[0] + if b.ch.hasLateBindableName(decl) { + if ast.IsBinaryExpression(decl) { + name := ast.GetNameOfDeclaration(decl) + if name != nil && ast.IsElementAccessExpression(name) && ast.IsPropertyAccessEntityNameExpression(name.AsElementAccessExpression().ArgumentExpression) { + b.trackComputedName(name.AsElementAccessExpression().ArgumentExpression, saveEnclosingDeclaration) + } + } else { + b.trackComputedName(decl.Name().Expression(), saveEnclosingDeclaration) + } + } + } else { + b.ctx.tracker.ReportNonSerializableProperty(b.ch.symbolToString(propertySymbol)) + } + } + if propertySymbol.ValueDeclaration != nil { + b.ctx.enclosingDeclaration = propertySymbol.ValueDeclaration + } else if len(propertySymbol.Declarations) > 0 && propertySymbol.Declarations[0] != nil { + b.ctx.enclosingDeclaration = propertySymbol.Declarations[0] + } else { + b.ctx.enclosingDeclaration = saveEnclosingDeclaration + } + propertyName := b.getPropertyNameNodeForSymbol(propertySymbol) + b.ctx.enclosingDeclaration = saveEnclosingDeclaration + b.ctx.approximateLength += len(ast.SymbolName(propertySymbol)) + 1 + + if propertySymbol.Flags&ast.SymbolFlagsAccessor != 0 { + writeType := b.ch.getWriteTypeOfSymbol(propertySymbol) + if propertyType != writeType && !b.ch.isErrorType(propertyType) && !b.ch.isErrorType(writeType) { + getterDeclaration := ast.GetDeclarationOfKind(propertySymbol, ast.KindGetAccessor) + getterSignature := b.ch.getSignatureFromDeclaration(getterDeclaration) + getter := b.signatureToSignatureDeclarationHelper(getterSignature, ast.KindGetAccessor, &SignatureToSignatureDeclarationOptions{ + name: propertyName, + }) + b.setCommentRange(getter, getterDeclaration) + typeElements = append(typeElements, getter) + setterDeclaration := ast.GetDeclarationOfKind(propertySymbol, ast.KindSetAccessor) + setterSignature := b.ch.getSignatureFromDeclaration(setterDeclaration) + setter := b.signatureToSignatureDeclarationHelper(setterSignature, ast.KindSetAccessor, &SignatureToSignatureDeclarationOptions{ + name: propertyName, + }) + b.setCommentRange(setter, setterDeclaration) + typeElements = append(typeElements, setter) + return typeElements + } + } + + var optionalToken *ast.Node + if propertySymbol.Flags&ast.SymbolFlagsOptional != 0 { + optionalToken = b.f.NewToken(ast.KindQuestionToken) + } else { + optionalToken = nil + } + if propertySymbol.Flags&(ast.SymbolFlagsFunction|ast.SymbolFlagsMethod) != 0 && len(b.ch.getPropertiesOfObjectType(propertyType)) == 0 && !b.ch.isReadonlySymbol(propertySymbol) { + signatures := b.ch.getSignaturesOfType(b.ch.filterType(propertyType, func(t *Type) bool { + return t.flags&TypeFlagsUndefined == 0 + }), SignatureKindCall) + for _, signature := range signatures { + methodDeclaration := b.signatureToSignatureDeclarationHelper(signature, ast.KindMethodSignature, &SignatureToSignatureDeclarationOptions{ + name: propertyName, + questionToken: optionalToken, + }) + b.setCommentRange(methodDeclaration, propertySymbol.ValueDeclaration) // !!! missing JSDoc support formerly provided by preserveCommentsOn + typeElements = append(typeElements, methodDeclaration) + } + if len(signatures) != 0 || optionalToken == nil { + return typeElements + } + } + var propertyTypeNode *ast.TypeNode + if b.shouldUsePlaceholderForProperty(propertySymbol) { + propertyTypeNode = b.createElidedInformationPlaceholder() + } else { + if propertyIsReverseMapped { + b.ctx.reverseMappedStack = append(b.ctx.reverseMappedStack, propertySymbol) + } + if propertyType != nil { + propertyTypeNode = b.serializeTypeForDeclaration(nil /*declaration*/, propertyType, propertySymbol) + } else { + propertyTypeNode = b.f.NewKeywordTypeNode(ast.KindAnyKeyword) + } + if propertyIsReverseMapped { + b.ctx.reverseMappedStack = b.ctx.reverseMappedStack[:len(b.ctx.reverseMappedStack)-1] + } + } + + var modifiers *ast.ModifierList + if b.ch.isReadonlySymbol(propertySymbol) { + modifiers = b.f.NewModifierList([]*ast.Node{b.f.NewModifier(ast.KindReadonlyKeyword)}) + b.ctx.approximateLength += 9 + } + propertySignature := b.f.NewPropertySignatureDeclaration(modifiers, propertyName, optionalToken, propertyTypeNode, nil) + + b.setCommentRange(propertySignature, propertySymbol.ValueDeclaration) // !!! missing JSDoc support formerly provided by preserveCommentsOn + typeElements = append(typeElements, propertySignature) + + return typeElements +} + +func (b *NodeBuilder) createTypeNodesFromResolvedType(resolvedType *StructuredType) *ast.NodeList { + if b.checkTruncationLength() { + if b.ctx.flags&nodebuilder.FlagsNoTruncation != 0 { + elem := b.f.NewNotEmittedTypeElement() + // TODO: attach synthetic comment + // b.e.addSyntheticTrailingComment(elem, ast.KindMultiLineCommentTrivia, "elided") + return b.f.NewNodeList([]*ast.TypeElement{elem}) + } + return b.f.NewNodeList([]*ast.Node{b.f.NewPropertySignatureDeclaration(nil, b.f.NewIdentifier("..."), nil, nil, nil)}) + } + var typeElements []*ast.TypeElement + for _, signature := range resolvedType.CallSignatures() { + typeElements = append(typeElements, b.signatureToSignatureDeclarationHelper(signature, ast.KindCallSignature, nil)) + } + for _, signature := range resolvedType.ConstructSignatures() { + if signature.flags&SignatureFlagsAbstract != 0 { + continue + } + typeElements = append(typeElements, b.signatureToSignatureDeclarationHelper(signature, ast.KindConstructSignature, nil)) + } + for _, info := range resolvedType.indexInfos { + typeElements = append(typeElements, b.indexInfoToIndexSignatureDeclarationHelper(info, core.IfElse(resolvedType.objectFlags&ObjectFlagsReverseMapped != 0, b.createElidedInformationPlaceholder(), nil))) + } + + properties := resolvedType.properties + if len(properties) == 0 { + return b.f.NewNodeList(typeElements) + } + + i := 0 + for _, propertySymbol := range properties { + i++ + if b.ctx.flags&nodebuilder.FlagsWriteClassExpressionAsTypeLiteral != 0 { + if propertySymbol.Flags&ast.SymbolFlagsPrototype != 0 { + continue + } + if getDeclarationModifierFlagsFromSymbol(propertySymbol)&(ast.ModifierFlagsPrivate|ast.ModifierFlagsProtected) != 0 { + b.ctx.tracker.ReportPrivateInBaseOfClassExpression(propertySymbol.Name) + } + } + if b.checkTruncationLength() && (i+2 < len(properties)-1) { + if b.ctx.flags&nodebuilder.FlagsNoTruncation != 0 { + // !!! synthetic comment support - missing middle silently elided without + // typeElement := typeElements[len(typeElements) - 1].Clone() + // typeElements = typeElements[0:len(typeElements)-1] + // b.e.addSyntheticTrailingComment(typeElement, ast.KindMultiLineCommentTrivia, __TEMPLATE__("... ", properties.length-i, " more elided ...")) + // typeElements = append(typeElements, typeElement) + } else { + text := fmt.Sprintf("... %d more ...", len(properties)-i) + typeElements = append(typeElements, b.f.NewPropertySignatureDeclaration(nil, b.f.NewIdentifier(text), nil, nil, nil)) + } + typeElements = b.addPropertyToElementList(properties[len(properties)-1], typeElements) + break + } + typeElements = b.addPropertyToElementList(propertySymbol, typeElements) + } + if len(typeElements) != 0 { + return b.f.NewNodeList(typeElements) + } else { + return nil + } +} + +func (b *NodeBuilder) createTypeNodeFromObjectType(t *Type) *ast.TypeNode { + if b.ch.isGenericMappedType(t) || (t.objectFlags&ObjectFlagsMapped != 0 && t.AsMappedType().containsError) { + return b.createMappedTypeNodeFromType(t) + } + + resolved := b.ch.resolveStructuredTypeMembers(t) + callSigs := resolved.CallSignatures() + ctorSigs := resolved.ConstructSignatures() + if len(resolved.properties) == 0 && len(resolved.indexInfos) == 0 { + if len(callSigs) == 0 && len(ctorSigs) == 0 { + b.ctx.approximateLength += 2 + result := b.f.NewTypeLiteralNode(b.f.NewNodeList([]*ast.Node{})) + b.e.SetEmitFlags(result, printer.EFSingleLine) + return result + } + + if len(callSigs) == 1 && len(ctorSigs) == 0 { + signature := callSigs[0] + signatureNode := b.signatureToSignatureDeclarationHelper(signature, ast.KindFunctionType, nil) + return signatureNode + } + + if len(ctorSigs) == 1 && len(callSigs) == 0 { + signature := ctorSigs[0] + signatureNode := b.signatureToSignatureDeclarationHelper(signature, ast.KindConstructorType, nil) + return signatureNode + } + } + + abstractSignatures := core.Filter(ctorSigs, func(signature *Signature) bool { + return signature.flags&SignatureFlagsAbstract != 0 + }) + if len(abstractSignatures) > 0 { + types := core.Map(abstractSignatures, func(s *Signature) *Type { + return b.ch.getOrCreateTypeFromSignature(s, nil) + }) + // count the number of type elements excluding abstract constructors + typeElementCount := len(callSigs) + (len(ctorSigs) - len(abstractSignatures)) + len(resolved.indexInfos) + (core.IfElse(b.ctx.flags&nodebuilder.FlagsWriteClassExpressionAsTypeLiteral != 0, core.CountWhere(resolved.properties, func(p *ast.Symbol) bool { + return p.Flags&ast.SymbolFlagsPrototype == 0 + }), len(resolved.properties))) + // don't include an empty object literal if there were no other static-side + // properties to write, i.e. `abstract class C { }` becomes `abstract new () => {}` + // and not `(abstract new () => {}) & {}` + if typeElementCount != 0 { + // create a copy of the object type without any abstract construct signatures. + types = append(types, b.getResolvedTypeWithoutAbstractConstructSignatures(resolved)) + } + return b.typeToTypeNodeClosure(b.ch.getIntersectionType(types)) + } + + restoreFlags := b.saveRestoreFlags() + b.ctx.flags |= nodebuilder.FlagsInObjectTypeLiteral + members := b.createTypeNodesFromResolvedType(resolved) + restoreFlags() + typeLiteralNode := b.f.NewTypeLiteralNode(members) + b.ctx.approximateLength += 2 + b.e.SetEmitFlags(typeLiteralNode, core.IfElse((b.ctx.flags&nodebuilder.FlagsMultilineObjectLiterals != 0), 0, printer.EFSingleLine)) + return typeLiteralNode +} + +func getTypeAliasForTypeLiteral(c *Checker, t *Type) *ast.Symbol { + if t.symbol != nil && t.symbol.Flags&ast.SymbolFlagsTypeLiteral != 0 && t.symbol.Declarations != nil { + node := ast.WalkUpParenthesizedTypes(t.symbol.Declarations[0].Parent) + if ast.IsTypeAliasDeclaration(node) { + return c.getSymbolOfDeclaration(node) + } + } + return nil +} + +func (b *NodeBuilder) shouldWriteTypeOfFunctionSymbol(symbol *ast.Symbol, typeId TypeId) bool { + isStaticMethodSymbol := symbol.Flags&ast.SymbolFlagsMethod != 0 && core.Some(symbol.Declarations, func(declaration *ast.Node) bool { + return ast.IsStatic(declaration) + }) + isNonLocalFunctionSymbol := false + if symbol.Flags&ast.SymbolFlagsFunction != 0 { + if symbol.Parent != nil { + isNonLocalFunctionSymbol = true + } else { + for _, declaration := range symbol.Declarations { + if declaration.Parent.Kind == ast.KindSourceFile || declaration.Parent.Kind == ast.KindModuleBlock { + isNonLocalFunctionSymbol = true + break + } + } + } + } + if isStaticMethodSymbol || isNonLocalFunctionSymbol { + // typeof is allowed only for static/non local functions + _, visited := b.ctx.visitedTypes[typeId] + return (b.ctx.flags&nodebuilder.FlagsUseTypeOfFunction != 0 || visited) && (b.ctx.flags&nodebuilder.FlagsUseStructuralFallback == 0 || b.ch.IsValueSymbolAccessible(symbol, b.ctx.enclosingDeclaration)) + // And the build is going to succeed without visibility error or there is no structural fallback allowed + } + return false +} + +func (b *NodeBuilder) createAnonymousTypeNode(t *Type) *ast.TypeNode { + typeId := t.id + symbol := t.symbol + if symbol != nil { + isInstantiationExpressionType := t.objectFlags&ObjectFlagsInstantiationExpressionType != 0 + if isInstantiationExpressionType { + instantiationExpressionType := t.AsInstantiationExpressionType() + existing := instantiationExpressionType.node + if ast.IsTypeQueryNode(existing) { + typeNode := b.tryReuseExistingNonParameterTypeNode(existing, t, nil, nil) + if typeNode != nil { + return typeNode + } + } + if _, ok := b.ctx.visitedTypes[typeId]; ok { + return b.createElidedInformationPlaceholder() + } + return b.visitAndTransformType(t, b.createTypeNodeFromObjectTypeClosure) + } + var isInstanceType ast.SymbolFlags + if isClassInstanceSide(b.ch, t) { + isInstanceType = ast.SymbolFlagsType + } else { + isInstanceType = ast.SymbolFlagsValue + } + + // !!! JS support + // if c.isJSConstructor(symbol.ValueDeclaration) { + // // Instance and static types share the same symbol; only add 'typeof' for the static side. + // return b.symbolToTypeNode(symbol, isInstanceType, nil) + // } else + if symbol.Flags&ast.SymbolFlagsClass != 0 && b.ch.getBaseTypeVariableOfClass(symbol) == nil && !(symbol.ValueDeclaration != nil && ast.IsClassLike(symbol.ValueDeclaration) && b.ctx.flags&nodebuilder.FlagsWriteClassExpressionAsTypeLiteral != 0 && (!ast.IsClassDeclaration(symbol.ValueDeclaration) || b.ch.IsSymbolAccessible(symbol, b.ctx.enclosingDeclaration, isInstanceType, false /*shouldComputeAliasesToMakeVisible*/).Accessibility != printer.SymbolAccessibilityAccessible)) || symbol.Flags&(ast.SymbolFlagsEnum|ast.SymbolFlagsValueModule) != 0 || b.shouldWriteTypeOfFunctionSymbol(symbol, typeId) { + return b.symbolToTypeNode(symbol, isInstanceType, nil) + } else if _, ok := b.ctx.visitedTypes[typeId]; ok { + // If type is an anonymous type literal in a type alias declaration, use type alias name + typeAlias := getTypeAliasForTypeLiteral(b.ch, t) + if typeAlias != nil { + // The specified symbol flags need to be reinterpreted as type flags + return b.symbolToTypeNode(typeAlias, ast.SymbolFlagsType, nil) + } else { + return b.createElidedInformationPlaceholder() + } + } else { + return b.visitAndTransformType(t, b.createTypeNodeFromObjectTypeClosure) + } + } else { + // Anonymous types without a symbol are never circular. + return b.createTypeNodeFromObjectTypeClosure(t) + } + +} + +func (b *NodeBuilder) getTypeFromTypeNode(node *ast.TypeNode, noMappedTypes bool) *Type { + // !!! noMappedTypes optional param support + t := b.ch.getTypeFromTypeNode(node) + if b.ctx.mapper == nil { + return t + } + + instantiated := b.ch.instantiateType(t, b.ctx.mapper) + if noMappedTypes && instantiated != t { + return nil + } + return instantiated +} + +func (b *NodeBuilder) typeToTypeNodeOrCircularityElision(t *Type) *ast.TypeNode { + if t.flags&TypeFlagsUnion != 0 { + if _, ok := b.ctx.visitedTypes[t.id]; ok { + if b.ctx.flags&nodebuilder.FlagsAllowAnonymousIdentifier == 0 { + b.ctx.encounteredError = true + b.ctx.tracker.ReportCyclicStructureError() + } + return b.createElidedInformationPlaceholder() + } + return b.visitAndTransformType(t, b.typeToTypeNodeClosure) + } + return b.typeToTypeNodeClosure(t) +} + +func (b *NodeBuilder) conditionalTypeToTypeNode(_t *Type) *ast.TypeNode { + t := _t.AsConditionalType() + checkTypeNode := b.typeToTypeNodeClosure(t.checkType) + b.ctx.approximateLength += 15 + if b.ctx.flags&nodebuilder.FlagsGenerateNamesForShadowedTypeParams != 0 && t.root.isDistributive && t.checkType.flags&TypeFlagsTypeParameter == 0 { + newParam := b.ch.newTypeParameter(b.ch.newSymbol(ast.SymbolFlagsTypeParameter, "T" /* as __String */)) + name := b.typeParameterToName(newParam) + newTypeVariable := b.f.NewTypeReferenceNode(name.AsNode(), nil) + b.ctx.approximateLength += 37 + // 15 each for two added conditionals, 7 for an added infer type + newMapper := prependTypeMapping(t.root.checkType, newParam, t.mapper) + saveInferTypeParameters := b.ctx.inferTypeParameters + b.ctx.inferTypeParameters = t.root.inferTypeParameters + extendsTypeNode := b.typeToTypeNodeClosure(b.ch.instantiateType(t.root.extendsType, newMapper)) + b.ctx.inferTypeParameters = saveInferTypeParameters + trueTypeNode := b.typeToTypeNodeOrCircularityElision(b.ch.instantiateType(b.getTypeFromTypeNode(t.root.node.TrueType, false), newMapper)) + falseTypeNode := b.typeToTypeNodeOrCircularityElision(b.ch.instantiateType(b.getTypeFromTypeNode(t.root.node.FalseType, false), newMapper)) + + // outermost conditional makes `T` a type parameter, allowing the inner conditionals to be distributive + // second conditional makes `T` have `T & checkType` substitution, so it is correctly usable as the checkType + // inner conditional runs the check the user provided on the check type (distributively) and returns the result + // checkType extends infer T ? T extends checkType ? T extends extendsType ? trueType : falseType : never : never; + // this is potentially simplifiable to + // checkType extends infer T ? T extends checkType & extendsType ? trueType : falseType : never; + // but that may confuse users who read the output more. + // On the other hand, + // checkType extends infer T extends checkType ? T extends extendsType ? trueType : falseType : never; + // may also work with `infer ... extends ...` in, but would produce declarations only compatible with the latest TS. + newId := newTypeVariable.AsTypeReferenceNode().TypeName.AsIdentifier().Clone(b.f) + syntheticExtendsNode := b.f.NewInferTypeNode(b.f.NewTypeParameterDeclaration(nil, newId, nil, nil)) + innerCheckConditionalNode := b.f.NewConditionalTypeNode(newTypeVariable, extendsTypeNode, trueTypeNode, falseTypeNode) + syntheticTrueNode := b.f.NewConditionalTypeNode(b.f.NewTypeReferenceNode(name.Clone(b.f), nil), b.f.DeepCloneNode(checkTypeNode), innerCheckConditionalNode, b.f.NewKeywordTypeNode(ast.KindNeverKeyword)) + return b.f.NewConditionalTypeNode(checkTypeNode, syntheticExtendsNode, syntheticTrueNode, b.f.NewKeywordTypeNode(ast.KindNeverKeyword)) + } + saveInferTypeParameters := b.ctx.inferTypeParameters + b.ctx.inferTypeParameters = t.root.inferTypeParameters + extendsTypeNode := b.typeToTypeNodeClosure(t.extendsType) + b.ctx.inferTypeParameters = saveInferTypeParameters + trueTypeNode := b.typeToTypeNodeOrCircularityElision(b.ch.getTrueTypeFromConditionalType(_t)) + falseTypeNode := b.typeToTypeNodeOrCircularityElision(b.ch.getFalseTypeFromConditionalType(_t)) + return b.f.NewConditionalTypeNode(checkTypeNode, extendsTypeNode, trueTypeNode, falseTypeNode) +} + +func (b *NodeBuilder) getParentSymbolOfTypeParameter(typeParameter *TypeParameter) *ast.Symbol { + tp := ast.GetDeclarationOfKind(typeParameter.symbol, ast.KindTypeParameter) + var host *ast.Node + // !!! JSDoc support + // if ast.IsJSDocTemplateTag(tp.Parent) { + // host = getEffectiveContainerForJSDocTemplateTag(tp.Parent) + // } else { + host = tp.Parent + // } + if host == nil { + return nil + } + return b.ch.getSymbolOfNode(host) +} + +func (b *NodeBuilder) typeReferenceToTypeNode(t *Type) *ast.TypeNode { + var typeArguments []*Type = b.ch.getTypeArguments(t) + if t.Target() == b.ch.globalArrayType || t.Target() == b.ch.globalReadonlyArrayType { + if b.ctx.flags&nodebuilder.FlagsWriteArrayAsGenericType != 0 { + typeArgumentNode := b.typeToTypeNodeClosure(typeArguments[0]) + return b.f.NewTypeReferenceNode(b.f.NewIdentifier(core.IfElse(t.Target() == b.ch.globalArrayType, "Array", "ReadonlyArray")), b.f.NewNodeList([]*ast.TypeNode{typeArgumentNode})) + } + elementType := b.typeToTypeNodeClosure(typeArguments[0]) + arrayType := b.f.NewArrayTypeNode(elementType) + if t.Target() == b.ch.globalArrayType { + return arrayType + } else { + return b.f.NewTypeOperatorNode(ast.KindReadonlyKeyword, arrayType) + } + } else if t.Target().objectFlags&ObjectFlagsTuple != 0 { + typeArguments = core.SameMapIndex(typeArguments, func(arg *Type, i int) *Type { + isOptional := false + if i < len(t.Target().AsTupleType().elementInfos) { + isOptional = t.Target().AsTupleType().elementInfos[i].flags&ElementFlagsOptional != 0 + } + return b.ch.removeMissingType(arg, isOptional) + }) + if len(typeArguments) > 0 { + arity := b.ch.getTypeReferenceArity(t) + tupleConstituentNodes := b.mapToTypeNodes(typeArguments[0:arity]) + if tupleConstituentNodes != nil { + for i := 0; i < len(tupleConstituentNodes.Nodes); i++ { + flags := t.Target().AsTupleType().elementInfos[i].flags + labeledElementDeclaration := t.Target().AsTupleType().elementInfos[i].labeledDeclaration + + if labeledElementDeclaration != nil { + tupleConstituentNodes.Nodes[i] = b.f.NewNamedTupleMember(core.IfElse(flags&ElementFlagsVariable != 0, b.f.NewToken(ast.KindDotDotDotToken), nil), b.f.NewIdentifier(b.ch.getTupleElementLabel(t.Target().AsTupleType().elementInfos[i], nil, i)), core.IfElse(flags&ElementFlagsOptional != 0, b.f.NewToken(ast.KindQuestionToken), nil), core.IfElse(flags&ElementFlagsRest != 0, b.f.NewArrayTypeNode(tupleConstituentNodes.Nodes[i]), tupleConstituentNodes.Nodes[i])) + } else { + switch { + case flags&ElementFlagsVariable != 0: + tupleConstituentNodes.Nodes[i] = b.f.NewRestTypeNode(core.IfElse(flags&ElementFlagsRest != 0, b.f.NewArrayTypeNode(tupleConstituentNodes.Nodes[i]), tupleConstituentNodes.Nodes[i])) + case flags&ElementFlagsOptional != 0: + tupleConstituentNodes.Nodes[i] = b.f.NewOptionalTypeNode(tupleConstituentNodes.Nodes[i]) + } + } + } + tupleTypeNode := b.f.NewTupleTypeNode(tupleConstituentNodes) + b.e.SetEmitFlags(tupleTypeNode, printer.EFSingleLine) + if t.Target().AsTupleType().readonly { + return b.f.NewTypeOperatorNode(ast.KindReadonlyKeyword, tupleTypeNode) + } else { + return tupleTypeNode + } + } + } + if b.ctx.encounteredError || (b.ctx.flags&nodebuilder.FlagsAllowEmptyTuple != 0) { + tupleTypeNode := b.f.NewTupleTypeNode(b.f.NewNodeList([]*ast.TypeNode{})) + b.e.SetEmitFlags(tupleTypeNode, printer.EFSingleLine) + if t.Target().AsTupleType().readonly { + return b.f.NewTypeOperatorNode(ast.KindReadonlyKeyword, tupleTypeNode) + } else { + return tupleTypeNode + } + } + b.ctx.encounteredError = true + return nil + // TODO: GH#18217 + } else if b.ctx.flags&nodebuilder.FlagsWriteClassExpressionAsTypeLiteral != 0 && t.symbol.ValueDeclaration != nil && ast.IsClassLike(t.symbol.ValueDeclaration) && !b.ch.IsValueSymbolAccessible(t.symbol, b.ctx.enclosingDeclaration) { + return b.createAnonymousTypeNode(t) + } else { + outerTypeParameters := t.Target().AsInterfaceType().OuterTypeParameters() + i := 0 + var resultType *ast.TypeNode + if outerTypeParameters != nil { + length := len(outerTypeParameters) + for i < length { + // Find group of type arguments for type parameters with the same declaring container. + start := i + parent := b.getParentSymbolOfTypeParameter(outerTypeParameters[i].AsTypeParameter()) + for ok := true; ok; ok = i < length && b.getParentSymbolOfTypeParameter(outerTypeParameters[i].AsTypeParameter()) == parent { // do-while loop + i++ + } + // When type parameters are their own type arguments for the whole group (i.e. we have + // the default outer type arguments), we don't show the group. + + if !slices.Equal(outerTypeParameters[start:i], typeArguments[start:i]) { + typeArgumentSlice := b.mapToTypeNodes(typeArguments[start:i]) + restoreFlags := b.saveRestoreFlags() + b.ctx.flags |= nodebuilder.FlagsForbidIndexedAccessSymbolReferences + ref := b.symbolToTypeNode(parent, ast.SymbolFlagsType, typeArgumentSlice) + restoreFlags() + if resultType == nil { + resultType = ref + } else { + resultType = b.appendReferenceToType(resultType, ref) + } + } + } + } + var typeArgumentNodes *ast.NodeList + if len(typeArguments) > 0 { + typeParameterCount := 0 + typeParams := t.Target().AsInterfaceType().TypeParameters() + if typeParams != nil { + typeParameterCount = min(len(typeParams), len(typeArguments)) + + // Maybe we should do this for more types, but for now we only elide type arguments that are + // identical to their associated type parameters' defaults for `Iterable`, `IterableIterator`, + // `AsyncIterable`, and `AsyncIterableIterator` to provide backwards-compatible .d.ts emit due + // to each now having three type parameters instead of only one. + if b.ch.isReferenceToType(t, b.ch.getGlobalIterableType()) || b.ch.isReferenceToType(t, b.ch.getGlobalIterableIteratorType()) || b.ch.isReferenceToType(t, b.ch.getGlobalAsyncIterableType()) || b.ch.isReferenceToType(t, b.ch.getGlobalAsyncIterableIteratorType()) { + if t.AsTypeReference().node == nil || !ast.IsTypeReferenceNode(t.AsTypeReference().node) || t.AsTypeReference().node.TypeArguments() == nil || len(t.AsTypeReference().node.TypeArguments()) < typeParameterCount { + for typeParameterCount > 0 { + typeArgument := typeArguments[typeParameterCount-1] + typeParameter := t.Target().AsInterfaceType().TypeParameters()[typeParameterCount-1] + defaultType := b.ch.getDefaultFromTypeParameter(typeParameter) + if defaultType == nil || !b.ch.isTypeIdenticalTo(typeArgument, defaultType) { + break + } + typeParameterCount-- + } + } + } + } + + typeArgumentNodes = b.mapToTypeNodes(typeArguments[i:typeParameterCount]) + } + restoreFlags := b.saveRestoreFlags() + b.ctx.flags |= nodebuilder.FlagsForbidIndexedAccessSymbolReferences + finalRef := b.symbolToTypeNode(t.symbol, ast.SymbolFlagsType, typeArgumentNodes) + restoreFlags() + if resultType == nil { + return finalRef + } else { + return b.appendReferenceToType(resultType, finalRef) + } + } +} + +func (b *NodeBuilder) visitAndTransformType(t *Type, transform func(t *Type) *ast.TypeNode) *ast.TypeNode { + typeId := t.id + isConstructorObject := t.objectFlags&ObjectFlagsAnonymous != 0 && t.symbol != nil && t.symbol.Flags&ast.SymbolFlagsClass != 0 + var id *CompositeSymbolIdentity + switch { + case t.objectFlags&ObjectFlagsReference != 0 && t.AsTypeReference().node != nil: + id = &CompositeSymbolIdentity{false, 0, ast.GetNodeId(t.AsTypeReference().node)} + case t.flags&TypeFlagsConditional != 0: + id = &CompositeSymbolIdentity{false, 0, ast.GetNodeId(t.AsConditionalType().root.node.AsNode())} + case t.symbol != nil: + id = &CompositeSymbolIdentity{isConstructorObject, ast.GetSymbolId(t.symbol), 0} + default: + id = nil + } + // Since instantiations of the same anonymous type have the same symbol, tracking symbols instead + // of types allows us to catch circular references to instantiations of the same anonymous type + + key := CompositeTypeCacheIdentity{typeId, b.ctx.flags, b.ctx.internalFlags} + if b.links.Has(b.ctx.enclosingDeclaration) { + links := b.links.Get(b.ctx.enclosingDeclaration) + cachedResult, ok := links.serializedTypes[key] + if ok { + // TODO:: check if we instead store late painted statements associated with this? + for _, arg := range cachedResult.trackedSymbols { + b.ctx.tracker.TrackSymbol(arg.symbol, arg.enclosingDeclaration, arg.meaning) + } + if cachedResult.truncating { + b.ctx.truncating = true + } + b.ctx.approximateLength += cachedResult.addedLength + return b.f.DeepCloneNode(cachedResult.node) + } + } + + var depth int + if id != nil { + depth = b.ctx.symbolDepth[*id] + if depth > 10 { + return b.createElidedInformationPlaceholder() + } + b.ctx.symbolDepth[*id] = depth + 1 + } + b.ctx.visitedTypes[typeId] = true + prevTrackedSymbols := b.ctx.trackedSymbols + b.ctx.trackedSymbols = nil + startLength := b.ctx.approximateLength + result := transform(t) + addedLength := b.ctx.approximateLength - startLength + if !b.ctx.reportedDiagnostic && !b.ctx.encounteredError { + links := b.links.Get(b.ctx.enclosingDeclaration) + if links.serializedTypes == nil { + links.serializedTypes = make(map[CompositeTypeCacheIdentity]*SerializedTypeEntry) + } + links.serializedTypes[key] = &SerializedTypeEntry{ + node: result, + truncating: b.ctx.truncating, + addedLength: addedLength, + trackedSymbols: b.ctx.trackedSymbols, + } + } + delete(b.ctx.visitedTypes, typeId) + if id != nil { + b.ctx.symbolDepth[*id] = depth + } + b.ctx.trackedSymbols = prevTrackedSymbols + return result + + // !!! TODO: Attempt node reuse or parse nodes to minimize copying once text range setting is set up + // deepCloneOrReuseNode := func(node T) T { + // if !nodeIsSynthesized(node) && getParseTreeNode(node) == node { + // return node + // } + // return setTextRange(b.ctx, b.f.cloneNode(visitEachChildWorker(node, deepCloneOrReuseNode, nil /*b.ctx*/, deepCloneOrReuseNodes, deepCloneOrReuseNode)), node) + // } + + // deepCloneOrReuseNodes := func(nodes *NodeArray[*ast.Node], visitor Visitor, test func(node *ast.Node) bool, start number, count number) *NodeArray[*ast.Node] { + // if nodes != nil && nodes.length == 0 { + // // Ensure we explicitly make a copy of an empty array; visitNodes will not do this unless the array has elements, + // // which can lead to us reusing the same empty NodeArray more than once within the same AST during type noding. + // return setTextRangeWorker(b.f.NewNodeArray(nil, nodes.hasTrailingComma), nodes) + // } + // return visitNodes(nodes, visitor, test, start, count) + // } +} + +func (b *NodeBuilder) typeToTypeNode(t *Type) *ast.TypeNode { + + inTypeAlias := b.ctx.flags & nodebuilder.FlagsInTypeAlias + b.ctx.flags &^= nodebuilder.FlagsInTypeAlias + + if t == nil { + if b.ctx.flags&nodebuilder.FlagsAllowEmptyUnionOrIntersection == 0 { + b.ctx.encounteredError = true + return nil + // TODO: GH#18217 + } + b.ctx.approximateLength += 3 + return b.f.NewKeywordTypeNode(ast.KindAnyKeyword) + } + + if b.ctx.flags&nodebuilder.FlagsNoTypeReduction == 0 { + t = b.ch.getReducedType(t) + } + + if t.flags&TypeFlagsAny != 0 { + if t.alias != nil { + return t.alias.ToTypeReferenceNode(b) + } + // !!! TODO: add comment once synthetic comment additions to nodes are supported + // if t == b.ch.unresolvedType { + // return e.AddSyntheticLeadingComment(b.f.NewKeywordTypeNode(ast.KindAnyKeyword), ast.KindMultiLineCommentTrivia, "unresolved") + // } + b.ctx.approximateLength += 3 + return b.f.NewKeywordTypeNode(core.IfElse(t == b.ch.intrinsicMarkerType, ast.KindIntrinsicKeyword, ast.KindAnyKeyword)) + } + if t.flags&TypeFlagsUnknown != 0 { + return b.f.NewKeywordTypeNode(ast.KindUnknownKeyword) + } + if t.flags&TypeFlagsString != 0 { + b.ctx.approximateLength += 6 + return b.f.NewKeywordTypeNode(ast.KindStringKeyword) + } + if t.flags&TypeFlagsNumber != 0 { + b.ctx.approximateLength += 6 + return b.f.NewKeywordTypeNode(ast.KindNumberKeyword) + } + if t.flags&TypeFlagsBigInt != 0 { + b.ctx.approximateLength += 6 + return b.f.NewKeywordTypeNode(ast.KindBigIntKeyword) + } + if t.flags&TypeFlagsBoolean != 0 && t.alias == nil { + b.ctx.approximateLength += 7 + return b.f.NewKeywordTypeNode(ast.KindBooleanKeyword) + } + if t.flags&TypeFlagsEnumLike != 0 { + if t.symbol.Flags&ast.SymbolFlagsEnumMember != 0 { + parentSymbol := b.ch.getParentOfSymbol(t.symbol) + parentName := b.symbolToTypeNode(parentSymbol, ast.SymbolFlagsType, nil) + if b.ch.getDeclaredTypeOfSymbol(parentSymbol) == t { + return parentName + } + memberName := ast.SymbolName(t.symbol) + if scanner.IsIdentifierText(memberName, core.ScriptTargetES5) { + return b.appendReferenceToType(parentName /* as TypeReferenceNode | ImportTypeNode */, b.f.NewTypeReferenceNode(b.f.NewIdentifier(memberName), nil /*typeArguments*/)) + } + if ast.IsImportTypeNode(parentName) { + parentName.AsImportTypeNode().IsTypeOf = true + // mutably update, node is freshly manufactured anyhow + return b.f.NewIndexedAccessTypeNode(parentName, b.f.NewLiteralTypeNode(b.f.NewStringLiteral(memberName))) + } else if ast.IsTypeReferenceNode(parentName) { + return b.f.NewIndexedAccessTypeNode(b.f.NewTypeQueryNode(parentName.AsTypeReferenceNode().TypeName, nil), b.f.NewLiteralTypeNode(b.f.NewStringLiteral(memberName))) + } else { + panic("Unhandled type node kind returned from `symbolToTypeNode`.") + } + } + return b.symbolToTypeNode(t.symbol, ast.SymbolFlagsType, nil) + } + if t.flags&TypeFlagsStringLiteral != 0 { + b.ctx.approximateLength += len(t.AsLiteralType().value.(string)) + 2 + return b.f.NewLiteralTypeNode(b.f.NewStringLiteral(t.AsLiteralType().value.(string) /*, b.flags&nodebuilder.FlagsUseSingleQuotesForStringLiteralType != 0*/)) + } + if t.flags&TypeFlagsNumberLiteral != 0 { + value := t.AsLiteralType().value.(jsnum.Number) + b.ctx.approximateLength += len(value.String()) + return b.f.NewLiteralTypeNode(core.IfElse(value < 0, b.f.NewPrefixUnaryExpression(ast.KindMinusToken, b.f.NewNumericLiteral("-"+value.String())), b.f.NewNumericLiteral(value.String()))) + } + if t.flags&TypeFlagsBigIntLiteral != 0 { + b.ctx.approximateLength += len(pseudoBigIntToString(getBigIntLiteralValue(t))) + 1 + return b.f.NewLiteralTypeNode(b.f.NewBigIntLiteral(pseudoBigIntToString(getBigIntLiteralValue(t)))) + } + if t.flags&TypeFlagsBooleanLiteral != 0 { + if t.AsLiteralType().value.(bool) { + b.ctx.approximateLength += 4 + return b.f.NewLiteralTypeNode(b.f.NewKeywordExpression(ast.KindTrueKeyword)) + } else { + b.ctx.approximateLength += 5 + return b.f.NewLiteralTypeNode(b.f.NewKeywordExpression(ast.KindFalseKeyword)) + } + } + if t.flags&TypeFlagsUniqueESSymbol != 0 { + if b.ctx.flags&nodebuilder.FlagsAllowUniqueESSymbolType == 0 { + if b.ch.IsValueSymbolAccessible(t.symbol, b.ctx.enclosingDeclaration) { + b.ctx.approximateLength += 6 + return b.symbolToTypeNode(t.symbol, ast.SymbolFlagsValue, nil) + } + b.ctx.tracker.ReportInaccessibleUniqueSymbolError() + } + b.ctx.approximateLength += 13 + return b.f.NewTypeOperatorNode(ast.KindUniqueKeyword, b.f.NewKeywordTypeNode(ast.KindSymbolKeyword)) + } + if t.flags&TypeFlagsVoid != 0 { + b.ctx.approximateLength += 4 + return b.f.NewKeywordTypeNode(ast.KindVoidKeyword) + } + if t.flags&TypeFlagsUndefined != 0 { + b.ctx.approximateLength += 9 + return b.f.NewKeywordTypeNode(ast.KindUndefinedKeyword) + } + if t.flags&TypeFlagsNull != 0 { + b.ctx.approximateLength += 4 + return b.f.NewLiteralTypeNode(b.f.NewKeywordExpression(ast.KindNullKeyword)) + } + if t.flags&TypeFlagsNever != 0 { + b.ctx.approximateLength += 5 + return b.f.NewKeywordTypeNode(ast.KindNeverKeyword) + } + if t.flags&TypeFlagsESSymbol != 0 { + b.ctx.approximateLength += 6 + return b.f.NewKeywordTypeNode(ast.KindSymbolKeyword) + } + if t.flags&TypeFlagsNonPrimitive != 0 { + b.ctx.approximateLength += 6 + return b.f.NewKeywordTypeNode(ast.KindObjectKeyword) + } + if isThisTypeParameter(t) { + if b.ctx.flags&nodebuilder.FlagsInObjectTypeLiteral != 0 { + if !b.ctx.encounteredError && b.ctx.flags&nodebuilder.FlagsAllowThisInObjectLiteral == 0 { + b.ctx.encounteredError = true + } + b.ctx.tracker.ReportInaccessibleThisError() + } + b.ctx.approximateLength += 4 + return b.f.NewThisTypeNode() + } + + if inTypeAlias == 0 && t.alias != nil && (b.ctx.flags&nodebuilder.FlagsUseAliasDefinedOutsideCurrentScope != 0 || b.ch.IsTypeSymbolAccessible(t.alias.Symbol(), b.ctx.enclosingDeclaration)) { + sym := t.alias.Symbol() + typeArgumentNodes := b.mapToTypeNodes(t.alias.TypeArguments()) + if isReservedMemberName(sym.Name) && sym.Flags&ast.SymbolFlagsClass == 0 { + return b.f.NewTypeReferenceNode(b.f.NewIdentifier(""), typeArgumentNodes) + } + if typeArgumentNodes != nil && len(typeArgumentNodes.Nodes) == 1 && sym == b.ch.globalArrayType.symbol { + return b.f.NewArrayTypeNode(typeArgumentNodes.Nodes[0]) + } + return b.symbolToTypeNode(sym, ast.SymbolFlagsType, typeArgumentNodes) + } + + objectFlags := t.objectFlags + + if objectFlags&ObjectFlagsReference != 0 { + // Debug.assert(t.flags&TypeFlagsObject != 0) // !!! + if t.AsTypeReference().node != nil { + return b.visitAndTransformType(t, b.typeReferenceToTypeNodeClosure) + } else { + return b.typeReferenceToTypeNodeClosure(t) + } + } + if t.flags&TypeFlagsTypeParameter != 0 || objectFlags&ObjectFlagsClassOrInterface != 0 { + if t.flags&TypeFlagsTypeParameter != 0 && slices.Contains(b.ctx.inferTypeParameters, t) { + b.ctx.approximateLength += len(ast.SymbolName(t.symbol)) + 6 + var constraintNode *ast.TypeNode + constraint := b.ch.getConstraintOfTypeParameter(t) + if constraint != nil { + // If the infer type has a constraint that is not the same as the constraint + // we would have normally inferred based on b, we emit the constraint + // using `infer T extends ?`. We omit inferred constraints from type references + // as they may be elided. + inferredConstraint := b.ch.getInferredTypeParameterConstraint(t, true /*omitTypeReferences*/) + if !(inferredConstraint != nil && b.ch.isTypeIdenticalTo(constraint, inferredConstraint)) { + b.ctx.approximateLength += 9 + constraintNode = b.typeToTypeNodeClosure(constraint) + } + } + return b.f.NewInferTypeNode(b.typeParameterToDeclarationWithConstraint(t, constraintNode)) + } + if b.ctx.flags&nodebuilder.FlagsGenerateNamesForShadowedTypeParams != 0 && t.flags&TypeFlagsTypeParameter != 0 { + name := b.typeParameterToName(t) + b.ctx.approximateLength += len(name.Text) + return b.f.NewTypeReferenceNode(b.f.NewIdentifier(name.Text), nil /*typeArguments*/) + } + // Ignore constraint/default when creating a usage (as opposed to declaration) of a type parameter. + if t.symbol != nil { + return b.symbolToTypeNode(t.symbol, ast.SymbolFlagsType, nil) + } + var name string + if (t == b.ch.markerSuperTypeForCheck || t == b.ch.markerSubTypeForCheck) && b.ch.varianceTypeParameter != nil && b.ch.varianceTypeParameter.symbol != nil { + name = (core.IfElse(t == b.ch.markerSubTypeForCheck, "sub-", "super-")) + ast.SymbolName(b.ch.varianceTypeParameter.symbol) + } else { + name = "?" + } + return b.f.NewTypeReferenceNode(b.f.NewIdentifier(name), nil /*typeArguments*/) + } + if t.flags&TypeFlagsUnion != 0 && t.AsUnionType().origin != nil { + t = t.AsUnionType().origin + } + if t.flags&(TypeFlagsUnion|TypeFlagsIntersection) != 0 { + var types []*Type + if t.flags&TypeFlagsUnion != 0 { + types = b.ch.formatUnionTypes(t.AsUnionType().types) + } else { + types = t.AsIntersectionType().types + } + if len(types) == 1 { + return b.typeToTypeNodeClosure(types[0]) + } + typeNodes := b.mapToTypeNodes(types) + if typeNodes != nil && len(typeNodes.Nodes) > 0 { + if t.flags&TypeFlagsUnion != 0 { + return b.f.NewUnionTypeNode(typeNodes) + } else { + return b.f.NewIntersectionTypeNode(typeNodes) + } + } else { + if !b.ctx.encounteredError && b.ctx.flags&nodebuilder.FlagsAllowEmptyUnionOrIntersection == 0 { + b.ctx.encounteredError = true + } + return nil + // TODO: GH#18217 + } + } + if objectFlags&(ObjectFlagsAnonymous|ObjectFlagsMapped) != 0 { + // Debug.assert(t.flags&TypeFlagsObject != 0) // !!! + // The type is an object literal type. + return b.createAnonymousTypeNode(t) + } + if t.flags&TypeFlagsIndex != 0 { + indexedType := t.Target() + b.ctx.approximateLength += 6 + indexTypeNode := b.typeToTypeNodeClosure(indexedType) + return b.f.NewTypeOperatorNode(ast.KindKeyOfKeyword, indexTypeNode) + } + if t.flags&TypeFlagsTemplateLiteral != 0 { + texts := t.AsTemplateLiteralType().texts + types := t.AsTemplateLiteralType().types + templateHead := b.f.NewTemplateHead(texts[0], texts[0], ast.TokenFlagsNone) + templateSpans := b.f.NewNodeList(core.MapIndex(types, func(t *Type, i int) *ast.Node { + var res *ast.TemplateMiddleOrTail + if i < len(types)-1 { + res = b.f.NewTemplateMiddle(texts[i+1], texts[i+1], ast.TokenFlagsNone) + } else { + res = b.f.NewTemplateTail(texts[i+1], texts[i+1], ast.TokenFlagsNone) + } + return b.f.NewTemplateLiteralTypeSpan(b.typeToTypeNodeClosure(t), res) + })) + b.ctx.approximateLength += 2 + return b.f.NewTemplateLiteralTypeNode(templateHead, templateSpans) + } + if t.flags&TypeFlagsStringMapping != 0 { + typeNode := b.typeToTypeNodeClosure(t.Target()) + return b.symbolToTypeNode(t.AsStringMappingType().symbol, ast.SymbolFlagsType, b.f.NewNodeList([]*ast.Node{typeNode})) + } + if t.flags&TypeFlagsIndexedAccess != 0 { + objectTypeNode := b.typeToTypeNodeClosure(t.AsIndexedAccessType().objectType) + indexTypeNode := b.typeToTypeNodeClosure(t.AsIndexedAccessType().indexType) + b.ctx.approximateLength += 2 + return b.f.NewIndexedAccessTypeNode(objectTypeNode, indexTypeNode) + } + if t.flags&TypeFlagsConditional != 0 { + return b.visitAndTransformType(t, b.conditionalTypeToTypeNodeClosure) + } + if t.flags&TypeFlagsSubstitution != 0 { + typeNode := b.typeToTypeNodeClosure(t.AsSubstitutionType().baseType) + if !b.ch.isNoInferType(t) { + return typeNode + } + noInferSymbol := b.ch.getGlobalTypeAliasSymbol("NoInfer", 1, false) + if noInferSymbol != nil { + return b.symbolToTypeNode(noInferSymbol, ast.SymbolFlagsType, b.f.NewNodeList([]*ast.Node{typeNode})) + } else { + return typeNode + } + } + + panic("Should be unreachable.") +} + +// Direct serialization core functions for types, type aliases, and symbols + +func (t *TypeAlias) ToTypeReferenceNode(b *NodeBuilder) *ast.Node { + return b.f.NewTypeReferenceNode(b.symbolToEntityNameNode(t.Symbol()), b.mapToTypeNodes(t.TypeArguments())) +} diff --git a/internal/checker/nodebuilderapi.go b/internal/checker/nodebuilderapi.go new file mode 100644 index 0000000000..b8c0913ab3 --- /dev/null +++ b/internal/checker/nodebuilderapi.go @@ -0,0 +1,198 @@ +package checker + +import ( + "github.com/microsoft/typescript-go/internal/ast" + "github.com/microsoft/typescript-go/internal/nodebuilder" + "github.com/microsoft/typescript-go/internal/printer" +) + +type NodeBuilderInterface interface { + EmitContext() *printer.EmitContext + + TypeToTypeNode(typ *Type, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node + TypePredicateToTypePredicateNode(predicate *TypePredicate, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node + SerializeTypeForDeclaration(declaration *ast.Node, symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node + SerializeReturnTypeForSignature(signatureDeclaration *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node + SerializeTypeForExpression(expr *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node + IndexInfoToIndexSignatureDeclaration(info *IndexInfo, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node + SignatureToSignatureDeclaration(signature *Signature, kind ast.Kind, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node + SymbolToEntityName(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node + SymbolToExpression(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node + SymbolToTypeParameterDeclarations(symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) []*ast.Node + SymbolToParameterDeclaration(symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node + TypeParameterToDeclaration(parameter *Type, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node + SymbolTableToDeclarationStatements(symbolTable *ast.SymbolTable, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) []*ast.Node + SymbolToNode(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node +} + +type NodeBuilderAPI struct { + ctxStack []*NodeBuilderContext + impl *NodeBuilder +} + +// EmitContext implements NodeBuilderInterface. +func (b *NodeBuilderAPI) EmitContext() *printer.EmitContext { + return b.impl.e +} + +func (b *NodeBuilderAPI) enterContext(enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) { + b.impl.ctx = &NodeBuilderContext{ + tracker: tracker, + approximateLength: 0, + encounteredError: false, + truncating: false, + reportedDiagnostic: false, + flags: flags, + internalFlags: internalFlags, + depth: 0, + enclosingDeclaration: enclosingDeclaration, + enclosingFile: ast.GetSourceFileOfNode(enclosingDeclaration), + inferTypeParameters: make([]*Type, 0), + visitedTypes: make(map[TypeId]bool), + symbolDepth: make(map[CompositeSymbolIdentity]int), + trackedSymbols: make([]*TrackedSymbolArgs, 0), + mapper: nil, + reverseMappedStack: make([]*ast.Symbol, 0), + enclosingSymbolTypes: make(map[ast.SymbolId]*Type), + remappedSymbolReferences: make(map[ast.SymbolId]*ast.Symbol), + } + if tracker == nil { + tracker = NewSymbolTrackerImpl(b.impl.ctx, nil) + b.impl.ctx.tracker = tracker + } + b.impl.initializeClosures() // recapture ctx + b.ctxStack = append(b.ctxStack, b.impl.ctx) +} + +func (b *NodeBuilderAPI) popContext() { + b.impl.ctx = nil + if len(b.ctxStack) > 1 { + b.impl.ctx = b.ctxStack[len(b.ctxStack)-1] + } + b.ctxStack = b.ctxStack[0 : len(b.ctxStack)-1] +} + +func (b *NodeBuilderAPI) exitContext(result *ast.Node) *ast.Node { + b.exitContextCheck() + defer b.popContext() + if b.impl.ctx.encounteredError { + return nil + } + return result +} + +func (b *NodeBuilderAPI) exitContextSlice(result []*ast.Node) []*ast.Node { + b.exitContextCheck() + defer b.popContext() + if b.impl.ctx.encounteredError { + return nil + } + return result +} + +func (b *NodeBuilderAPI) exitContextCheck() { + if b.impl.ctx.truncating && b.impl.ctx.flags&nodebuilder.FlagsNoTruncation != 0 { + b.impl.ctx.tracker.ReportTruncationError() + } +} + +// IndexInfoToIndexSignatureDeclaration implements NodeBuilderInterface. +func (b *NodeBuilderAPI) IndexInfoToIndexSignatureDeclaration(info *IndexInfo, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { + b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) + return b.exitContext(b.impl.indexInfoToIndexSignatureDeclarationHelper(info, nil)) +} + +// SerializeReturnTypeForSignature implements NodeBuilderInterface. +func (b *NodeBuilderAPI) SerializeReturnTypeForSignature(signatureDeclaration *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { + b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) + signature := b.impl.ch.getSignatureFromDeclaration(signatureDeclaration) + symbol := b.impl.ch.getSymbolOfDeclaration(signatureDeclaration) + returnType, ok := b.impl.ctx.enclosingSymbolTypes[ast.GetSymbolId(symbol)] + if !ok || returnType == nil { + returnType = b.impl.ch.instantiateType(b.impl.ch.getReturnTypeOfSignature(signature), b.impl.ctx.mapper) + } + return b.exitContext(b.impl.serializeInferredReturnTypeForSignature(signature, returnType)) +} + +// SerializeTypeForDeclaration implements NodeBuilderInterface. +func (b *NodeBuilderAPI) SerializeTypeForDeclaration(declaration *ast.Node, symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { + b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) + return b.exitContext(b.impl.serializeTypeForDeclaration(declaration, nil, symbol)) +} + +// SerializeTypeForExpression implements NodeBuilderInterface. +func (b *NodeBuilderAPI) SerializeTypeForExpression(expr *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { + b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) + return b.exitContext(b.impl.serializeTypeForExpression(expr)) +} + +// SignatureToSignatureDeclaration implements NodeBuilderInterface. +func (b *NodeBuilderAPI) SignatureToSignatureDeclaration(signature *Signature, kind ast.Kind, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { + b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) + return b.exitContext(b.impl.signatureToSignatureDeclarationHelper(signature, kind, nil)) +} + +// SymbolTableToDeclarationStatements implements NodeBuilderInterface. +func (b *NodeBuilderAPI) SymbolTableToDeclarationStatements(symbolTable *ast.SymbolTable, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) []*ast.Node { + b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) + return b.exitContextSlice(b.impl.symbolTableToDeclarationStatements(symbolTable)) +} + +// SymbolToEntityName implements NodeBuilderInterface. +func (b *NodeBuilderAPI) SymbolToEntityName(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { + b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) + return b.exitContext(b.impl.symbolToName(symbol, meaning, false)) +} + +// SymbolToExpression implements NodeBuilderInterface. +func (b *NodeBuilderAPI) SymbolToExpression(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { + b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) + return b.exitContext(b.impl.symbolToExpression(symbol, meaning)) +} + +// SymbolToNode implements NodeBuilderInterface. +func (b *NodeBuilderAPI) SymbolToNode(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { + b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) + return b.exitContext(b.impl.symbolToNode(symbol, meaning)) +} + +// SymbolToParameterDeclaration implements NodeBuilderInterface. +func (b NodeBuilderAPI) SymbolToParameterDeclaration(symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { + b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) + return b.exitContext(b.impl.symbolToParameterDeclaration(symbol, false)) +} + +// SymbolToTypeParameterDeclarations implements NodeBuilderInterface. +func (b *NodeBuilderAPI) SymbolToTypeParameterDeclarations(symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) []*ast.Node { + b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) + return b.exitContextSlice(b.impl.symbolToTypeParameterDeclarations(symbol)) +} + +// TypeParameterToDeclaration implements NodeBuilderInterface. +func (b *NodeBuilderAPI) TypeParameterToDeclaration(parameter *Type, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { + b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) + return b.exitContext(b.impl.typeParameterToDeclaration(parameter)) +} + +// TypePredicateToTypePredicateNode implements NodeBuilderInterface. +func (b *NodeBuilderAPI) TypePredicateToTypePredicateNode(predicate *TypePredicate, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { + b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) + return b.exitContext(b.impl.typePredicateToTypePredicateNode(predicate)) +} + +// TypeToTypeNode implements NodeBuilderInterface. +func (b *NodeBuilderAPI) TypeToTypeNode(typ *Type, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { + b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) + return b.exitContext(b.impl.typeToTypeNode(typ)) +} + +// var _ NodeBuilderInterface = NewNodeBuilderAPI(nil, nil) + +func NewNodeBuilderAPI(ch *Checker, e *printer.EmitContext) *NodeBuilderAPI { + impl := NewNodeBuilder(ch, e) + return &NodeBuilderAPI{impl: &impl, ctxStack: make([]*NodeBuilderContext, 0, 1)} +} + +func (c *Checker) GetDiagnosticNodeBuilder() NodeBuilderInterface { + return c.nodeBuilder +} diff --git a/internal/checker/nodebuilderscopes.go b/internal/checker/nodebuilderscopes.go new file mode 100644 index 0000000000..7403c4c5ff --- /dev/null +++ b/internal/checker/nodebuilderscopes.go @@ -0,0 +1,238 @@ +package checker + +import ( + "github.com/microsoft/typescript-go/internal/ast" + "github.com/microsoft/typescript-go/internal/core" + "github.com/microsoft/typescript-go/internal/nodebuilder" +) + +func cloneNodeBuilderContext(context *NodeBuilderContext) func() { + // Make type parameters created within this context not consume the name outside this context + // The symbol serializer ends up creating many sibling scopes that all need "separate" contexts when + // it comes to naming things - within a normal `typeToTypeNode` call, the node builder only ever descends + // through the type tree, so the only cases where we could have used distinct sibling scopes was when there + // were multiple generic overloads with similar generated type parameter names + // The effect: + // When we write out + // export const x: (x: T) => T + // export const y: (x: T) => T + // we write it out like that, rather than as + // export const x: (x: T) => T + // export const y: (x: T_1) => T_1 + oldMustCreateTypeParameterSymbolList := context.hasCreatedTypeParameterSymbolList + oldMustCreateTypeParametersNamesLookups := context.hasCreatedTypeParametersNamesLookups + context.hasCreatedTypeParameterSymbolList = false + context.hasCreatedTypeParametersNamesLookups = false + oldTypeParameterNames := context.typeParameterNames + oldTypeParameterNamesByText := context.typeParameterNamesByText + oldTypeParameterNamesByTextNextNameCount := context.typeParameterNamesByTextNextNameCount + oldTypeParameterSymbolList := context.typeParameterSymbolList + return func() { + context.typeParameterNames = oldTypeParameterNames + context.typeParameterNamesByText = oldTypeParameterNamesByText + context.typeParameterNamesByTextNextNameCount = oldTypeParameterNamesByTextNextNameCount + context.typeParameterSymbolList = oldTypeParameterSymbolList + context.hasCreatedTypeParameterSymbolList = oldMustCreateTypeParameterSymbolList + context.hasCreatedTypeParametersNamesLookups = oldMustCreateTypeParametersNamesLookups + } +} + +type localsRecord struct { + name string + oldSymbol *ast.Symbol +} + +func (b *NodeBuilder) enterNewScope(declaration *ast.Node, expandedParams *[]*ast.Symbol, typeParameters *[]*Type, originalParameters *[]*ast.Symbol, mapper *TypeMapper) func() { + cleanupContext := cloneNodeBuilderContext(b.ctx) + // For regular function/method declarations, the enclosing declaration will already be signature.declaration, + // so this is a no-op, but for arrow functions and function expressions, the enclosing declaration will be + // the declaration that the arrow function / function expression is assigned to. + // + // If the parameters or return type include "typeof globalThis.paramName", using the wrong scope will lead + // us to believe that we can emit "typeof paramName" instead, even though that would refer to the parameter, + // not the global. Make sure we are in the right scope by changing the enclosingDeclaration to the function. + // + // We can't use the declaration directly; it may be in another file and so we may lose access to symbols + // accessible to the current enclosing declaration, or gain access to symbols not accessible to the current + // enclosing declaration. To keep this chain accurate, insert a fake scope into the chain which makes the + // function's parameters visible. + var cleanupParams *func() + var cleanupTypeParams *func() + oldEnclosingDecl := b.ctx.enclosingDeclaration + oldMapper := b.ctx.mapper + if mapper != nil { + b.ctx.mapper = mapper + } + if b.ctx.enclosingDeclaration != nil && declaration != nil { + // As a performance optimization, reuse the same fake scope within this chain. + // This is especially needed when we are working on an excessively deep type; + // if we don't do this, then we spend all of our time adding more and more + // scopes that need to be searched in isSymbolAccessible later. Since all we + // really want to do is to mark certain names as unavailable, we can just keep + // all of the names we're introducing in one large table and push/pop from it as + // needed; isSymbolAccessible will walk upward and find the closest "fake" scope, + // which will conveniently report on any and all faked scopes in the chain. + // + // It'd likely be better to store this somewhere else for isSymbolAccessible, but + // since that API _only_ uses the enclosing declaration (and its parents), this is + // seems like the best way to inject names into that search process. + // + // Note that we only check the most immediate enclosingDeclaration; the only place we + // could potentially add another fake scope into the chain is right here, so we don't + // traverse all ancestors. + pushFakeScope := func(kind string, addAll func(addSymbol func(name string, symbol *ast.Symbol))) *func() { + // We only ever need to look two declarations upward. + // Debug.assert(context.enclosingDeclaration) // !!! + var existingFakeScope *ast.Node + if b.links.Has(b.ctx.enclosingDeclaration) { + links := b.links.Get(b.ctx.enclosingDeclaration) + if links.fakeScopeForSignatureDeclaration != nil && *links.fakeScopeForSignatureDeclaration == kind { + existingFakeScope = b.ctx.enclosingDeclaration + } + } + if existingFakeScope == nil && b.ctx.enclosingDeclaration.Parent != nil { + if b.links.Has(b.ctx.enclosingDeclaration.Parent) { + links := b.links.Get(b.ctx.enclosingDeclaration.Parent) + if links.fakeScopeForSignatureDeclaration != nil && *links.fakeScopeForSignatureDeclaration == kind { + existingFakeScope = b.ctx.enclosingDeclaration.Parent + } + } + } + // Debug.assertOptionalNode(existingFakeScope, isBlock) // !!! + + var locals ast.SymbolTable + if existingFakeScope != nil { + locals = existingFakeScope.Locals() + } + if locals == nil { + locals = make(ast.SymbolTable) + } + newLocals := []string{} + oldLocals := []localsRecord{} + addAll(func(name string, symbol *ast.Symbol) { + // Add cleanup information only if we don't own the fake scope + if existingFakeScope != nil { + oldSymbol, ok := locals[name] + if !ok || oldSymbol == nil { + newLocals = append(newLocals, name) + } else { + oldLocals = append(oldLocals, localsRecord{name, oldSymbol}) + } + } + locals[name] = symbol + }) + + if existingFakeScope == nil { + // Use a Block for this; the type of the node doesn't matter so long as it + // has locals, and this is cheaper/easier than using a function-ish Node. + fakeScope := b.f.NewBlock(b.f.NewNodeList([]*ast.Node{}), false) + b.links.Get(fakeScope).fakeScopeForSignatureDeclaration = &kind + data := fakeScope.LocalsContainerData() + data.Locals = locals + fakeScope.Parent = b.ctx.enclosingDeclaration + b.ctx.enclosingDeclaration = fakeScope + return nil + } else { + // We did not create the current scope, so we have to clean it up + undo := func() { + for _, s := range newLocals { + delete(locals, s) + } + for _, s := range oldLocals { + locals[s.name] = s.oldSymbol + } + } + return &undo + } + } + + if expandedParams == nil || !core.Some(*expandedParams, func(p *ast.Symbol) bool { return p != nil }) { + cleanupParams = nil + } else { + cleanupParams = pushFakeScope("params", func(add func(name string, symbol *ast.Symbol)) { + if expandedParams == nil { + return + } + for pIndex, param := range *expandedParams { + var originalParam *ast.Symbol + if pIndex < len(*originalParameters) { + originalParam = (*originalParameters)[pIndex] + } + if originalParameters != nil && originalParam != param { + // Can't reference parameters that come from an expansion + add(param.Name, b.ch.unknownSymbol) + // Can't reference the original expanded parameter either + if originalParam != nil { + add(originalParam.Name, b.ch.unknownSymbol) + } + } else if !core.Some(param.Declarations, func(d *ast.Node) bool { + var bindElement *(func(e *ast.BindingElement)) + var bindPattern *(func(e *ast.BindingPattern)) + + bindPatternWorker := func(p *ast.BindingPattern) { + for _, e := range p.Elements.Nodes { + switch e.Kind { + case ast.KindOmittedExpression: + return + case ast.KindBindingElement: + (*bindElement)(e.AsBindingElement()) + return + default: + panic("Unhandled binding element kind") + } + } + } + + bindElementWorker := func(e *ast.BindingElement) { + if ast.IsBindingPattern(e.Name()) { + (*bindPattern)(e.Name().AsBindingPattern()) + return + } + symbol := b.ch.getSymbolOfDeclaration(e.AsNode()) + add(symbol.Name, symbol) + } + bindElement = &bindElementWorker + bindPattern = &bindPatternWorker + + if ast.IsParameter(d) && ast.IsBindingPattern(d.Name()) { + (*bindPattern)(d.Name().AsBindingPattern()) + return true + } + return false + + }) { + add(param.Name, param) + } + } + }) + } + + if b.ctx.flags&nodebuilder.FlagsGenerateNamesForShadowedTypeParams != 0 && typeParameters != nil && core.Some(*typeParameters, func(p *Type) bool { return p != nil }) { + cleanupTypeParams = pushFakeScope("typeParams", func(add func(name string, symbol *ast.Symbol)) { + if typeParameters == nil { + return + } + for _, typeParam := range *typeParameters { + if typeParam == nil { + continue + } + typeParamName := b.typeParameterToName(typeParam).Text + add(typeParamName, typeParam.symbol) + } + }) + } + + } + + return func() { + if cleanupParams != nil { + (*cleanupParams)() + } + if cleanupTypeParams != nil { + (*cleanupTypeParams)() + } + cleanupContext() + b.ctx.enclosingDeclaration = oldEnclosingDecl + b.ctx.mapper = oldMapper + } +} diff --git a/internal/checker/printer.go b/internal/checker/printer.go index 454b8680c5..7bbde96c13 100644 --- a/internal/checker/printer.go +++ b/internal/checker/printer.go @@ -6,642 +6,261 @@ import ( "github.com/microsoft/typescript-go/internal/ast" "github.com/microsoft/typescript-go/internal/core" "github.com/microsoft/typescript-go/internal/jsnum" + "github.com/microsoft/typescript-go/internal/nodebuilder" "github.com/microsoft/typescript-go/internal/printer" "github.com/microsoft/typescript-go/internal/scanner" ) -func (c *Checker) getTypePrecedence(t *Type) ast.TypePrecedence { - if t.alias == nil { - switch { - case t.flags&TypeFlagsConditional != 0: - return ast.TypePrecedenceConditional - case t.flags&TypeFlagsIntersection != 0: - return ast.TypePrecedenceIntersection - case t.flags&TypeFlagsUnion != 0 && t.flags&TypeFlagsBoolean == 0: - return ast.TypePrecedenceUnion - case t.flags&TypeFlagsIndex != 0 || isInferTypeParameter(t): - return ast.TypePrecedenceTypeOperator - case c.isArrayType(t): - return ast.TypePrecedencePostfix - case t.objectFlags&ObjectFlagsClassOrInterface == 0 && c.getSingleCallOrConstructSignature(t) != nil: - return ast.TypePrecedenceFunction - } - } - return ast.TypePrecedenceNonArray -} - -func (c *Checker) SymbolToString(s *ast.Symbol) string { - return c.symbolToString(s) -} - -func (c *Checker) symbolToString(s *ast.Symbol) string { - if scanner.IsValidIdentifier(s.Name, c.languageVersion) { - return s.Name - } - if s.ValueDeclaration != nil { - if ast.IsJsxAttribute(s.ValueDeclaration) { - return "\"" + s.Name + "\"" - } - name := ast.GetNameOfDeclaration(s.ValueDeclaration) - if name != nil { - return scanner.GetTextOfNode(name) - } - } - if len(s.Name) == 0 || s.Name[0] != '\xFE' { - return s.Name // !!! Implement escaping - } - switch s.Name { - case ast.InternalSymbolNameClass: - return "(Anonymous class)" - case ast.InternalSymbolNameFunction: - return "(Anonymous function)" - case ast.InternalSymbolNameType, ast.InternalSymbolNameObject: - return "(Anonymous type)" - case ast.InternalSymbolNameComputed: - return "(Computed name)" - } - if len(s.Name) >= 2 && s.Name[1] == '@' { - return "(Unique symbol)" - } - return "(Missing)" -} - -func (c *Checker) TypeToString(t *Type) string { - return c.typeToStringEx(t, nil, TypeFormatFlagsNone) -} - -func (c *Checker) typeToStringEx(t *Type, enclosingDeclaration *ast.Node, flags TypeFormatFlags) string { - p := c.newPrinter(flags) - if flags&TypeFormatFlagsNoTypeReduction == 0 { - t = c.getReducedType(t) - } - p.printType(t) - return p.string() -} - -func (c *Checker) SourceFileWithTypes(sourceFile *ast.SourceFile) string { - p := c.newPrinter(TypeFormatFlagsInTypeAlias) - p.printSourceFileWithTypes(sourceFile) - return p.string() -} - -func (c *Checker) signatureToString(s *Signature) string { - p := c.newPrinter(TypeFormatFlagsNone) - if s.flags&SignatureFlagsConstruct != 0 { - p.print("new ") - } - p.printSignature(s, ": ") - return p.string() -} - -func (c *Checker) typePredicateToString(t *TypePredicate) string { - p := c.newPrinter(TypeFormatFlagsNone) - p.printTypePredicate(t) - return p.string() +// TODO: Memoize once per checker to retain threadsafety +func createPrinterWithDefaults(emitContext *printer.EmitContext) *printer.Printer { + return printer.NewPrinter(printer.PrinterOptions{}, printer.PrintHandlers{}, emitContext) } -func (c *Checker) valueToString(value any) string { - p := c.newPrinter(TypeFormatFlagsNone) - p.printValue(value) - return p.string() +func createPrinterWithRemoveComments(emitContext *printer.EmitContext) *printer.Printer { + return printer.NewPrinter(printer.PrinterOptions{RemoveComments: true}, printer.PrintHandlers{}, emitContext) } -type Printer struct { - c *Checker - flags TypeFormatFlags - sb strings.Builder - printing core.Set[*Type] - depth int32 - extendsTypeDepth int32 +func createPrinterWithRemoveCommentsOmitTrailingSemicolon(emitContext *printer.EmitContext) *printer.Printer { + // TODO: OmitTrailingSemicolon support + return printer.NewPrinter(printer.PrinterOptions{RemoveComments: true}, printer.PrintHandlers{}, emitContext) } -func (c *Checker) newPrinter(flags TypeFormatFlags) *Printer { - return &Printer{c: c, flags: flags} +func createPrinterWithRemoveCommentsNeverAsciiEscape(emitContext *printer.EmitContext) *printer.Printer { + // TODO: NeverAsciiEscape support + return printer.NewPrinter(printer.PrinterOptions{RemoveComments: true}, printer.PrintHandlers{}, emitContext) } -func (p *Printer) string() string { - return p.sb.String() +func getTrailingSemicolonDeferringWriter(writer printer.EmitTextWriter) printer.EmitTextWriter { + // TODO: wrap arbitrary writer with writer that only commits semicolon writes on following write operations (is OmitTrailingSemicolon printer option redundant?) + return writer } -func (p *Printer) print(s string) { - p.sb.WriteString(s) +func (c *Checker) TypeToString(type_ *Type) string { + return c.typeToStringEx(type_, nil, TypeFormatFlagsNone, nil) } -func (p *Printer) printName(symbol *ast.Symbol) { - p.print(p.c.symbolToString(symbol)) +func toNodeBuilderFlags(flags TypeFormatFlags) nodebuilder.Flags { + return nodebuilder.Flags(flags & TypeFormatFlagsNodeBuilderFlagsMask) } -func (p *Printer) printQualifiedName(symbol *ast.Symbol) { - if p.flags&TypeFormatFlagsUseFullyQualifiedType != 0 && symbol.Parent != nil { - p.printQualifiedName(symbol.Parent) - p.print(".") - } - if symbol.Flags&ast.SymbolFlagsModule != 0 && strings.HasPrefix(symbol.Name, "\"") { - p.print("import(") - p.print(symbol.Name) - p.print(")") - return - } - p.printName(symbol) -} - -func (p *Printer) printTypeEx(t *Type, precedence ast.TypePrecedence) { - if p.c.getTypePrecedence(t) < precedence { - p.print("(") - p.printType(t) - p.print(")") - } else { - p.printType(t) +func (c *Checker) typeToStringEx(type_ *Type, enclosingDeclaration *ast.Node, flags TypeFormatFlags, writer printer.EmitTextWriter) string { + if writer == nil { + writer = printer.NewTextWriter("") } -} - -func (p *Printer) printType(t *Type) { - if p.sb.Len() > 1_000_000 { - p.print("...") - return + noTruncation := (c.compilerOptions.NoErrorTruncation == core.TSTrue) || (flags&TypeFormatFlagsNoTruncation != 0) + combinedFlags := toNodeBuilderFlags(flags) | nodebuilder.FlagsIgnoreErrors + if noTruncation { + combinedFlags = combinedFlags | nodebuilder.FlagsNoTruncation } - - if t.alias != nil && (p.flags&TypeFormatFlagsInTypeAlias == 0 || p.depth > 0) { - p.printQualifiedName(t.alias.symbol) - p.printTypeArguments(t.alias.typeArguments) - } else { - p.printTypeNoAlias(t) + typeNode := c.nodeBuilder.TypeToTypeNode(type_, enclosingDeclaration, combinedFlags, nodebuilder.InternalFlagsNone, nil) + if typeNode == nil { + panic("should always get typenode") } -} - -func (p *Printer) printTypeNoAlias(t *Type) { - p.depth++ - switch { - case t.flags&TypeFlagsIntrinsic != 0: - p.print(t.AsIntrinsicType().intrinsicName) - case t.flags&(TypeFlagsLiteral|TypeFlagsEnum) != 0: - p.printLiteralType(t) - case t.flags&TypeFlagsUniqueESSymbol != 0: - p.printUniqueESSymbolType(t) - case t.flags&TypeFlagsUnion != 0: - p.printUnionType(t) - case t.flags&TypeFlagsIntersection != 0: - p.printIntersectionType(t) - case t.flags&TypeFlagsTypeParameter != 0: - p.printTypeParameter(t) - case t.flags&TypeFlagsObject != 0: - p.printRecursive(t, (*Printer).printObjectType) - case t.flags&TypeFlagsIndex != 0: - p.printRecursive(t, (*Printer).printIndexType) - case t.flags&TypeFlagsIndexedAccess != 0: - p.printRecursive(t, (*Printer).printIndexedAccessType) - case t.flags&TypeFlagsConditional != 0: - p.printRecursive(t, (*Printer).printConditionalType) - case t.flags&TypeFlagsTemplateLiteral != 0: - p.printTemplateLiteralType(t) - case t.flags&TypeFlagsStringMapping != 0: - p.printStringMappingType(t) - case t.flags&TypeFlagsSubstitution != 0: - if p.c.isNoInferType(t) { - if noInferSymbol := p.c.getGlobalNoInferSymbolOrNil(); noInferSymbol != nil { - p.printQualifiedName(noInferSymbol) - p.printTypeArguments([]*Type{t.AsSubstitutionType().baseType}) - break - } - } - p.printType(t.AsSubstitutionType().baseType) - } - p.depth-- -} - -func (p *Printer) printRecursive(t *Type, f func(*Printer, *Type)) { - if !p.printing.Has(t) && p.depth < 10 { - p.printing.Add(t) - f(p, t) - p.printing.Delete(t) + // The unresolved type gets a synthesized comment on `any` to hint to users that it's not a plain `any`. + // Otherwise, we always strip comments out. + var printer *printer.Printer + if type_ == c.unresolvedType { + printer = createPrinterWithDefaults(c.diagnosticConstructionContext) } else { - p.print("???") + printer = createPrinterWithRemoveComments(c.diagnosticConstructionContext) } -} - -func (p *Printer) printLiteralType(t *Type) { - if t.flags&(TypeFlagsEnumLiteral|TypeFlagsEnum) != 0 { - p.printEnumLiteral(t) - } else { - p.printValue(t.AsLiteralType().value) + var sourceFile *ast.SourceFile + if enclosingDeclaration != nil { + sourceFile = ast.GetSourceFileOfNode(enclosingDeclaration) } -} + printer.Write(typeNode /*sourceFile*/, sourceFile, writer, nil) + result := writer.String() -func (p *Printer) printValue(value any) { - switch value := value.(type) { - case string: - p.printStringLiteral(value) - case jsnum.Number: - p.printNumberLiteral(value) - case bool: - p.printBooleanLiteral(value) - case jsnum.PseudoBigInt: - p.printBigIntLiteral(value) + maxLength := defaultMaximumTruncationLength * 2 + if noTruncation { + maxLength = noTruncationMaximumTruncationLength * 2 } + if maxLength > 0 && result != "" && len(result) >= maxLength { + return result[0:maxLength-len("...")] + "..." + } + return result } -func (p *Printer) printStringLiteral(s string) { - p.print("\"") - p.print(printer.EscapeString(s, '"')) - p.print("\"") -} - -func (p *Printer) printNumberLiteral(f jsnum.Number) { - p.print(f.String()) -} - -func (p *Printer) printBooleanLiteral(b bool) { - p.print(core.IfElse(b, "true", "false")) -} - -func (p *Printer) printBigIntLiteral(b jsnum.PseudoBigInt) { - p.print(b.String() + "n") -} - -func (p *Printer) printUniqueESSymbolType(t *Type) { - p.print("unique symbol") -} - -func (p *Printer) printTemplateLiteralType(t *Type) { - texts := t.AsTemplateLiteralType().texts - types := t.AsTemplateLiteralType().types - p.print("`") - p.print(texts[0]) - for i, t := range types { - p.print("${") - p.printType(t) - p.print("}") - p.print(texts[i+1]) - } - p.print("`") +func (c *Checker) SymbolToString(s *ast.Symbol) string { + return c.symbolToString(s) } -func (p *Printer) printStringMappingType(t *Type) { - p.printName(t.symbol) - p.print("<") - p.printType(t.AsStringMappingType().target) - p.print(">") +func (c *Checker) symbolToString(symbol *ast.Symbol) string { + return c.symbolToStringEx(symbol, nil, ast.SymbolFlagsAll, SymbolFormatFlagsAllowAnyNodeKind, nil) } -func (p *Printer) printEnumLiteral(t *Type) { - if parent := p.c.getParentOfSymbol(t.symbol); parent != nil { - p.printQualifiedName(parent) - if p.c.getDeclaredTypeOfSymbol(parent) != t { - p.print(".") - p.printName(t.symbol) - } - return +func (c *Checker) symbolToStringEx(symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags, flags SymbolFormatFlags, writer printer.EmitTextWriter) string { + if writer == nil { + writer = printer.SingleLineStringWriterPool.Get().(printer.EmitTextWriter) } - p.printQualifiedName(t.symbol) -} -func (p *Printer) printObjectType(t *Type) { - switch { - case t.objectFlags&ObjectFlagsReference != 0: - p.printParameterizedType(t) - case t.objectFlags&ObjectFlagsClassOrInterface != 0: - p.printQualifiedName(t.symbol) - case p.c.isGenericMappedType(t) || t.objectFlags&ObjectFlagsMapped != 0 && t.AsMappedType().containsError: - p.printMappedType(t) - default: - p.printAnonymousType(t) + nodeFlags := nodebuilder.FlagsIgnoreErrors + internalNodeFlags := nodebuilder.InternalFlagsNone + if flags&SymbolFormatFlagsUseOnlyExternalAliasing != 0 { + nodeFlags |= nodebuilder.FlagsUseOnlyExternalAliasing } -} - -func (p *Printer) printParameterizedType(t *Type) { - switch { - case p.c.isArrayType(t) && p.flags&TypeFormatFlagsWriteArrayAsGenericType == 0: - p.printArrayType(t) - case isTupleType(t): - p.printTupleType(t) - default: - p.printTypeReference(t) + if flags&SymbolFormatFlagsWriteTypeParametersOrArguments != 0 { + nodeFlags |= nodebuilder.FlagsWriteTypeParametersInQualifiedName + } + if flags&SymbolFormatFlagsUseAliasDefinedOutsideCurrentScope != 0 { + nodeFlags |= nodebuilder.FlagsUseAliasDefinedOutsideCurrentScope + } + if flags&SymbolFormatFlagsDoNotIncludeSymbolChain != 0 { + internalNodeFlags |= nodebuilder.InternalFlagsDoNotIncludeSymbolChain + } + if flags&SymbolFormatFlagsWriteComputedProps != 0 { + internalNodeFlags |= nodebuilder.InternalFlagsWriteComputedProps } -} - -func (p *Printer) printTypeReference(t *Type) { - p.printQualifiedName(t.symbol) - p.printTypeArguments(p.c.getTypeArguments(t)[:p.c.getTypeReferenceArity(t)]) -} -func (p *Printer) printTypeArguments(typeArguments []*Type) { - if len(typeArguments) != 0 { - p.print("<") - var tail bool - for _, t := range typeArguments { - if tail { - p.print(", ") - } - p.printType(t) - tail = true + var sourceFile *ast.SourceFile + if enclosingDeclaration != nil { + sourceFile = ast.GetSourceFileOfNode(enclosingDeclaration) + } + if writer == printer.SingleLineStringWriterPool.Get().(printer.EmitTextWriter) { + // handle uses of the single-line writer during an ongoing write + existing := writer.String() + defer writer.Clear() + if existing != "" { + defer writer.WriteKeyword(existing) } - p.print(">") } -} + var printer_ *printer.Printer + if enclosingDeclaration != nil && enclosingDeclaration.Kind == ast.KindSourceFile { + printer_ = createPrinterWithRemoveCommentsNeverAsciiEscape(c.diagnosticConstructionContext) + } else { + printer_ = createPrinterWithRemoveComments(c.diagnosticConstructionContext) + } -func (p *Printer) printArrayType(t *Type) { - d := t.AsTypeReference() - if d.target != p.c.globalArrayType { - p.print("readonly ") + var builder func(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node + if flags&SymbolFormatFlagsAllowAnyNodeKind != 0 { + builder = c.nodeBuilder.SymbolToNode + } else { + builder = c.nodeBuilder.SymbolToEntityName } - p.printTypeEx(p.c.getTypeArguments(t)[0], ast.TypePrecedencePostfix) - p.print("[]") + entity := builder(symbol, meaning, enclosingDeclaration, nodeFlags, internalNodeFlags, nil) // TODO: GH#18217 + printer_.Write(entity /*sourceFile*/, sourceFile, getTrailingSemicolonDeferringWriter(writer), nil) // TODO: GH#18217 + return writer.String() } -func (p *Printer) printTupleType(t *Type) { - if t.TargetTupleType().readonly { - p.print("readonly ") - } - p.print("[") - elementInfos := t.TargetTupleType().elementInfos - typeArguments := p.c.getTypeArguments(t) - var tail bool - for i, info := range elementInfos { - t := typeArguments[i] - if tail { - p.print(", ") - } - if info.flags&ElementFlagsVariable != 0 { - p.print("...") - } - if info.labeledDeclaration != nil { - p.print(info.labeledDeclaration.Name().Text()) - if info.flags&ElementFlagsOptional != 0 { - p.print("?: ") - p.printType(p.c.removeMissingType(t, true)) - } else { - p.print(": ") - if info.flags&ElementFlagsRest != 0 { - p.printTypeEx(t, ast.TypePrecedencePostfix) - p.print("[]") - } else { - p.printType(t) - } - } - } else { - if info.flags&ElementFlagsOptional != 0 { - p.printTypeEx(p.c.removeMissingType(t, true), ast.TypePrecedencePostfix) - p.print("?") - } else if info.flags&ElementFlagsRest != 0 { - p.printTypeEx(t, ast.TypePrecedencePostfix) - p.print("[]") - } else { - p.printType(t) - } - } - tail = true - } - p.print("]") +func (c *Checker) signatureToString(signature *Signature) string { + return c.signatureToStringEx(signature, nil, TypeFormatFlagsNone, nil, nil) } -func (p *Printer) printAnonymousType(t *Type) { - if t.symbol != nil && len(t.symbol.Name) != 0 { - if t.symbol.Flags&(ast.SymbolFlagsClass|ast.SymbolFlagsEnum|ast.SymbolFlagsValueModule) != 0 { - if t == p.c.getTypeOfSymbol(t.symbol) { - p.print("typeof ") - p.printQualifiedName(t.symbol) - return - } - } - } - props := p.c.getPropertiesOfObjectType(t) - callSignatures := p.c.getSignaturesOfType(t, SignatureKindCall) - constructSignatures := p.c.getSignaturesOfType(t, SignatureKindConstruct) - if len(props) == 0 { - if len(callSignatures) == 1 && len(constructSignatures) == 0 { - p.printSignature(callSignatures[0], " => ") - return +func (c *Checker) signatureToStringEx(signature *Signature, enclosingDeclaration *ast.Node, flags TypeFormatFlags, kind *SignatureKind, writer printer.EmitTextWriter) string { + var sigOutput ast.Kind + if flags&TypeFormatFlagsWriteArrowStyleSignature != 0 { + if kind != nil && *kind == SignatureKindConstruct { + sigOutput = ast.KindConstructorType + } else { + sigOutput = ast.KindFunctionType } - if len(callSignatures) == 0 && len(constructSignatures) == 1 { - p.print("new ") - p.printSignature(constructSignatures[0], " => ") - return + } else { + if kind != nil && *kind == SignatureKindConstruct { + sigOutput = ast.KindConstructSignature + } else { + sigOutput = ast.KindCallSignature } } - p.print("{") - hasMembers := false - for _, sig := range callSignatures { - p.print(" ") - p.printSignature(sig, ": ") - p.print(";") - hasMembers = true - } - for _, sig := range constructSignatures { - p.print(" new ") - p.printSignature(sig, ": ") - p.print(";") - hasMembers = true - } - for _, info := range p.c.getIndexInfosOfType(t) { - if info.isReadonly { - p.print(" readonly") - } - p.print(" [") - p.print(getNameFromIndexInfo(info)) - p.print(": ") - p.printType(info.keyType) - p.print("]: ") - p.printType(info.valueType) - p.print(";") - hasMembers = true - } - for _, prop := range props { - if p.c.isReadonlySymbol(prop) { - p.print(" readonly") - } - p.print(" ") - p.printName(prop) - if prop.Flags&ast.SymbolFlagsOptional != 0 { - p.print("?") + if writer == nil { + writer = printer.SingleLineStringWriterPool.Get().(printer.EmitTextWriter) + } + combinedFlags := toNodeBuilderFlags(flags) | nodebuilder.FlagsIgnoreErrors | nodebuilder.FlagsWriteTypeParametersInQualifiedName + sig := c.nodeBuilder.SignatureToSignatureDeclaration(signature, sigOutput, enclosingDeclaration, combinedFlags, nodebuilder.InternalFlagsNone, nil) + printer_ := createPrinterWithRemoveCommentsOmitTrailingSemicolon(c.diagnosticConstructionContext) + var sourceFile *ast.SourceFile + if enclosingDeclaration != nil { + sourceFile = ast.GetSourceFileOfNode(enclosingDeclaration) + } + if writer == printer.SingleLineStringWriterPool.Get().(printer.EmitTextWriter) { + // handle uses of the single-line writer during an ongoing write + existing := writer.String() + defer writer.Clear() + if existing != "" { + defer writer.WriteKeyword(existing) } - p.print(": ") - p.printType(p.c.getNonMissingTypeOfSymbol(prop)) - p.print(";") - hasMembers = true } - if hasMembers { - p.print(" ") - } - p.print("}") + printer_.Write(sig /*sourceFile*/, sourceFile, getTrailingSemicolonDeferringWriter(writer), nil) // TODO: GH#18217 + return writer.String() } -func (p *Printer) printSignature(sig *Signature, returnSeparator string) { - if len(sig.typeParameters) != 0 { - p.print("<") - var tail bool - for _, tp := range sig.typeParameters { - if tail { - p.print(", ") - } - p.printTypeParameterAndConstraint(tp) - tail = true - } - p.print(">") - } - p.print("(") - var tail bool - if sig.thisParameter != nil { - p.print("this: ") - p.printType(p.c.getTypeOfSymbol(sig.thisParameter)) - tail = true - } - expandedParameters := p.c.GetExpandedParameters(sig) - // If the expanded parameter list had a variadic in a non-trailing position, don't expand it - parameters := core.IfElse(core.Some(expandedParameters, func(s *ast.Symbol) bool { - return s != expandedParameters[len(expandedParameters)-1] && s.CheckFlags&ast.CheckFlagsRestParameter != 0 - }), sig.parameters, expandedParameters) - for i, param := range parameters { - if tail { - p.print(", ") - } - if param.ValueDeclaration != nil && isRestParameter(param.ValueDeclaration) || param.CheckFlags&ast.CheckFlagsRestParameter != 0 { - p.print("...") - p.printName(param) - } else { - p.printName(param) - if i >= p.c.getMinArgumentCountEx(sig, MinArgumentCountFlagsVoidIsNonOptional) { - p.print("?") - } - } - p.print(": ") - p.printType(p.c.getTypeOfSymbol(param)) - tail = true - } - p.print(")") - p.print(returnSeparator) - if pred := p.c.getTypePredicateOfSignature(sig); pred != nil { - p.printTypePredicate(pred) - } else { - p.printType(p.c.getReturnTypeOfSignature(sig)) - } +func (c *Checker) typePredicateToString(typePredicate *TypePredicate) string { + return c.typePredicateToStringEx(typePredicate, nil, TypeFormatFlagsUseAliasDefinedOutsideCurrentScope, nil) } -func (p *Printer) printTypePredicate(pred *TypePredicate) { - if pred.kind == TypePredicateKindAssertsThis || pred.kind == TypePredicateKindAssertsIdentifier { - p.print("asserts ") - } - if pred.kind == TypePredicateKindThis || pred.kind == TypePredicateKindAssertsThis { - p.print("this") - } else { - p.print(pred.parameterName) +func (c *Checker) typePredicateToStringEx(typePredicate *TypePredicate, enclosingDeclaration *ast.Node, flags TypeFormatFlags, writer printer.EmitTextWriter) string { + if writer == nil { + writer = printer.SingleLineStringWriterPool.Get().(printer.EmitTextWriter) } - if pred.t != nil { - p.print(" is ") - p.printType(pred.t) + combinedFlags := toNodeBuilderFlags(flags) | nodebuilder.FlagsIgnoreErrors | nodebuilder.FlagsWriteTypeParametersInQualifiedName + predicate := c.nodeBuilder.TypePredicateToTypePredicateNode(typePredicate, enclosingDeclaration, combinedFlags, nodebuilder.InternalFlagsNone, nil) // TODO: GH#18217 + printer_ := createPrinterWithRemoveComments(c.diagnosticConstructionContext) + var sourceFile *ast.SourceFile + if enclosingDeclaration != nil { + sourceFile = ast.GetSourceFileOfNode(enclosingDeclaration) } -} - -func (p *Printer) printTypeParameter(t *Type) { - switch { - case t.AsTypeParameter().isThisType: - p.print("this") - case p.extendsTypeDepth > 0 && isInferTypeParameter(t): - p.print("infer ") - p.printTypeParameterAndConstraint(t) - case t.symbol != nil: - p.printName(t.symbol) - default: - p.print("???") + if writer == printer.SingleLineStringWriterPool.Get().(printer.EmitTextWriter) { + // handle uses of the single-line writer during an ongoing write + existing := writer.String() + defer writer.Clear() + if existing != "" { + defer writer.WriteKeyword(existing) + } } + printer_.Write(predicate /*sourceFile*/, sourceFile, writer, nil) + return writer.String() } -func (p *Printer) printTypeParameterAndConstraint(t *Type) { - p.printName(t.symbol) - if constraint := p.c.getConstraintOfTypeParameter(t); constraint != nil { - p.print(" extends ") - p.printType(constraint) +func (c *Checker) valueToString(value any) string { + switch value := value.(type) { + case string: + return "\"" + printer.EscapeString(value, '"') + "\"" + case jsnum.Number: + return value.String() + case bool: + return core.IfElse(value, "true", "false") + case jsnum.PseudoBigInt: + return value.String() + "n" } + panic("unhandled value type in valueToString") } -func (p *Printer) printUnionType(t *Type) { - switch { - case t.flags&TypeFlagsBoolean != 0: - p.print("boolean") - case t.flags&TypeFlagsEnumLiteral != 0: - p.printQualifiedName(t.symbol) - default: - u := t.AsUnionType() - if u.origin != nil { - p.printType(u.origin) - } else { - var tail bool - for _, t := range p.c.formatUnionTypes(u.types) { - if tail { - p.print(" | ") +func (c *Checker) formatUnionTypes(types []*Type) []*Type { + var result []*Type + var flags TypeFlags + for i := 0; i < len(types); i++ { + t := types[i] + flags |= t.flags + if t.flags&TypeFlagsNullable == 0 { + if t.flags&(TypeFlagsBooleanLiteral|TypeFlagsEnumLike) != 0 { + var baseType *Type + if t.flags&TypeFlagsBooleanLiteral != 0 { + baseType = c.booleanType + } else { + baseType = c.getBaseTypeOfEnumLikeType(t) + } + if baseType.flags&TypeFlagsUnion != 0 { + count := len(baseType.AsUnionType().types) + if i+count <= len(types) && c.getRegularTypeOfLiteralType(types[i+count-1]) == c.getRegularTypeOfLiteralType(baseType.AsUnionType().types[count-1]) { + result = append(result, baseType) + i += count - 1 + continue + } } - p.printTypeEx(t, ast.TypePrecedenceUnion) - tail = true } + result = append(result, t) } } -} - -func (p *Printer) printIntersectionType(t *Type) { - var tail bool - for _, t := range t.AsIntersectionType().types { - if tail { - p.print(" & ") - } - p.printTypeEx(t, ast.TypePrecedenceIntersection) - tail = true + if flags&TypeFlagsNull != 0 { + result = append(result, c.nullType) } -} - -func (p *Printer) printIndexType(t *Type) { - p.print("keyof ") - p.printTypeEx(t.AsIndexType().target, ast.TypePrecedenceTypeOperator) -} - -func (p *Printer) printIndexedAccessType(t *Type) { - p.printType(t.AsIndexedAccessType().objectType) - p.print("[") - p.printType(t.AsIndexedAccessType().indexType) - p.print("]") -} - -func (p *Printer) printConditionalType(t *Type) { - p.printType(t.AsConditionalType().checkType) - p.print(" extends ") - p.extendsTypeDepth++ - p.printType(t.AsConditionalType().extendsType) - p.extendsTypeDepth-- - p.print(" ? ") - p.printType(p.c.getTrueTypeFromConditionalType(t)) - p.print(" : ") - p.printType(p.c.getFalseTypeFromConditionalType(t)) -} - -func (p *Printer) printMappedType(t *Type) { - d := t.AsMappedType().declaration - p.print("{ ") - if d.ReadonlyToken != nil { - if d.ReadonlyToken.Kind != ast.KindReadonlyKeyword { - p.print(scanner.TokenToString(d.ReadonlyToken.Kind)) - } - p.print("readonly ") - } - p.print("[") - p.printName(p.c.getTypeParameterFromMappedType(t).symbol) - p.print(" in ") - p.printType(p.c.getConstraintTypeFromMappedType(t)) - nameType := p.c.getNameTypeFromMappedType(t) - if nameType != nil { - p.print(" as ") - p.printType(nameType) - } - p.print("]") - if d.QuestionToken != nil { - if d.QuestionToken.Kind != ast.KindQuestionToken { - p.print(scanner.TokenToString(d.QuestionToken.Kind)) - } - p.print("?") + if flags&TypeFlagsUndefined != 0 { + result = append(result, c.undefinedType) } - p.print(": ") - p.printType(p.c.getTemplateTypeFromMappedType(t)) - p.print("; }") + return result } -func (p *Printer) printSourceFileWithTypes(sourceFile *ast.SourceFile) { +func (c *Checker) SourceFileWithTypes(sourceFile *ast.SourceFile) string { + writer := printer.NewTextWriter("\n") var pos int var visit func(*ast.Node) bool var typesPrinted bool @@ -656,32 +275,33 @@ func (p *Printer) printSourceFileWithTypes(sourceFile *ast.SourceFile) { } if pos < nextLineStart { if typesPrinted { - p.print("\n") + writer.WriteLine() } - p.print(sourceFile.Text()[pos:nextLineStart]) + writer.Write(sourceFile.Text()[pos:nextLineStart]) pos = nextLineStart typesPrinted = false } } visit = func(node *ast.Node) bool { - text, t, isDeclaration := p.c.getTextAndTypeOfNode(node) + text, t, isDeclaration := c.getTextAndTypeOfNode(node) if text != "" && !strings.Contains(text, "\n") { printLinesBefore(node) - p.print(">") - p.print(text) - p.print(" : ") - p.printType(t) + writer.Write(">") + writer.Write(text) + writer.Write(" : ") + c.typeToStringEx(t, nil, TypeFormatFlagsNone, writer) if isDeclaration && t.flags&TypeFlagsEnumLiteral != 0 && t.flags&(TypeFlagsStringLiteral|TypeFlagsNumberLiteral) != 0 { - p.print(" = ") - p.printValue(t.AsLiteralType().value) + writer.Write(" = ") + writer.Write(c.valueToString(t.AsLiteralType().value)) } - p.print("\n") + writer.WriteLine() typesPrinted = true } return node.ForEachChild(visit) } visit(sourceFile.AsNode()) - p.print(sourceFile.Text()[pos:sourceFile.End()]) + writer.Write(sourceFile.Text()[pos:sourceFile.End()]) + return writer.String() } func (c *Checker) getTextAndTypeOfNode(node *ast.Node) (string, *Type, bool) { @@ -701,42 +321,3 @@ func (c *Checker) getTextAndTypeOfNode(node *ast.Node) (string, *Type, bool) { } return "", nil, false } - -func (c *Checker) formatUnionTypes(types []*Type) []*Type { - var result []*Type - var flags TypeFlags - for i := 0; i < len(types); i++ { - t := types[i] - flags |= t.flags - if t.flags&TypeFlagsNullable == 0 { - if t.flags&(TypeFlagsBooleanLiteral|TypeFlagsEnumLike) != 0 { - var baseType *Type - if t.flags&TypeFlagsBooleanLiteral != 0 { - baseType = c.booleanType - } else { - baseType = c.getBaseTypeOfEnumLikeType(t) - } - if baseType.flags&TypeFlagsUnion != 0 { - count := len(baseType.AsUnionType().types) - if i+count <= len(types) && c.getRegularTypeOfLiteralType(types[i+count-1]) == c.getRegularTypeOfLiteralType(baseType.AsUnionType().types[count-1]) { - result = append(result, baseType) - i += count - 1 - continue - } - } - } - result = append(result, t) - } - } - if flags&TypeFlagsNull != 0 { - result = append(result, c.nullType) - } - if flags&TypeFlagsUndefined != 0 { - result = append(result, c.undefinedType) - } - return result -} - -func isInferTypeParameter(t *Type) bool { - return t.flags&TypeFlagsTypeParameter != 0 && t.symbol != nil && core.Some(t.symbol.Declarations, func(d *ast.Node) bool { return ast.IsInferTypeNode(d.Parent) }) -} diff --git a/internal/checker/relater.go b/internal/checker/relater.go index 4cca82aa2c..c4ca45a5ea 100644 --- a/internal/checker/relater.go +++ b/internal/checker/relater.go @@ -1267,13 +1267,13 @@ func isNonPrimitiveType(t *Type) bool { func (c *Checker) getTypeNamesForErrorDisplay(left *Type, right *Type) (string, string) { var leftStr string if c.symbolValueDeclarationIsContextSensitive(left.symbol) { - leftStr = c.typeToStringEx(left, left.symbol.ValueDeclaration, TypeFormatFlagsNone) + leftStr = c.typeToStringEx(left, left.symbol.ValueDeclaration, TypeFormatFlagsNone, nil) } else { leftStr = c.TypeToString(left) } var rightStr string if c.symbolValueDeclarationIsContextSensitive(right.symbol) { - rightStr = c.typeToStringEx(right, right.symbol.ValueDeclaration, TypeFormatFlagsNone) + rightStr = c.typeToStringEx(right, right.symbol.ValueDeclaration, TypeFormatFlagsNone, nil) } else { rightStr = c.TypeToString(right) } @@ -1285,7 +1285,7 @@ func (c *Checker) getTypeNamesForErrorDisplay(left *Type, right *Type) (string, } func (c *Checker) getTypeNameForErrorDisplay(t *Type) string { - return c.typeToStringEx(t, nil /*enclosingDeclaration*/, TypeFormatFlagsUseFullyQualifiedType) + return c.typeToStringEx(t, nil /*enclosingDeclaration*/, TypeFormatFlagsUseFullyQualifiedType, nil) } func (c *Checker) symbolValueDeclarationIsContextSensitive(symbol *ast.Symbol) bool { @@ -4645,7 +4645,7 @@ func (r *Relater) reportErrorResults(originalSource *Type, originalTarget *Type, prop = core.Find(r.c.getPropertiesOfUnionOrIntersectionType(originalTarget), isConflictingPrivateProperty) } if prop != nil { - r.reportError(message, r.c.typeToStringEx(originalTarget, nil /*enclosingDeclaration*/, TypeFormatFlagsNoTypeReduction), r.c.symbolToString(prop)) + r.reportError(message, r.c.typeToStringEx(originalTarget, nil /*enclosingDeclaration*/, TypeFormatFlagsNoTypeReduction, nil), r.c.symbolToString(prop)) } } r.reportRelationError(headMessage, source, target) diff --git a/internal/checker/symbolaccessibility.go b/internal/checker/symbolaccessibility.go new file mode 100644 index 0000000000..3d45673c76 --- /dev/null +++ b/internal/checker/symbolaccessibility.go @@ -0,0 +1,720 @@ +package checker + +import ( + "reflect" + + "github.com/microsoft/typescript-go/internal/ast" + "github.com/microsoft/typescript-go/internal/core" + "github.com/microsoft/typescript-go/internal/printer" +) + +func (ch *Checker) IsTypeSymbolAccessible(typeSymbol *ast.Symbol, enclosingDeclaration *ast.Node) bool { + access := ch.isSymbolAccessibleWorker(typeSymbol, enclosingDeclaration, ast.SymbolFlagsType /*shouldComputeAliasesToMakeVisible*/, false /*allowModules*/, true) + return access.Accessibility == printer.SymbolAccessibilityAccessible +} + +func (ch *Checker) IsValueSymbolAccessible(symbol *ast.Symbol, enclosingDeclaration *ast.Node) bool { + access := ch.isSymbolAccessibleWorker(symbol, enclosingDeclaration, ast.SymbolFlagsValue /*shouldComputeAliasesToMakeVisible*/, false /*allowModules*/, true) + return access.Accessibility == printer.SymbolAccessibilityAccessible +} + +func (ch *Checker) IsSymbolAccessibleByFlags(symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags ast.SymbolFlags) bool { + access := ch.isSymbolAccessibleWorker(symbol, enclosingDeclaration, flags /*shouldComputeAliasesToMakeVisible*/, false /*allowModules*/, false) // TODO: Strada bug? Why is this allowModules: false? + return access.Accessibility == printer.SymbolAccessibilityAccessible +} + +func (ch *Checker) IsAnySymbolAccessible(symbols []*ast.Symbol, enclosingDeclaration *ast.Node, initialSymbol *ast.Symbol, meaning ast.SymbolFlags, shouldComputeAliasesToMakeVisible bool, allowModules bool) *printer.SymbolAccessibilityResult { + if len(symbols) == 0 { + return nil + } + + var hadAccessibleChain *ast.Symbol + earlyModuleBail := false + for _, symbol := range symbols { + // Symbol is accessible if it by itself is accessible + accessibleSymbolChain := ch.getAccessibleSymbolChain(symbol, enclosingDeclaration, meaning /*useOnlyExternalAliasing*/, false) + if len(accessibleSymbolChain) > 0 { + hadAccessibleChain = symbol + // TODO: going through emit resolver here is weird. Relayer these APIs. + hasAccessibleDeclarations := ch.GetEmitResolver(nil, true).hasVisibleDeclarations(accessibleSymbolChain[0], shouldComputeAliasesToMakeVisible) + if hasAccessibleDeclarations != nil { + return hasAccessibleDeclarations + } + } + if allowModules { + if core.Some(symbol.Declarations, hasNonGlobalAugmentationExternalModuleSymbol) { + if shouldComputeAliasesToMakeVisible { + earlyModuleBail = true + // Generally speaking, we want to use the aliases that already exist to refer to a module, if present + // In order to do so, we need to find those aliases in order to retain them in declaration emit; so + // if we are in declaration emit, we cannot use the fast path for module visibility until we've exhausted + // all other visibility options (in order to capture the possible aliases used to reference the module) + continue + } + // Any meaning of a module symbol is always accessible via an `import` type + return &printer.SymbolAccessibilityResult{ + Accessibility: printer.SymbolAccessibilityAccessible, + } + } + } + + // If we haven't got the accessible symbol, it doesn't mean the symbol is actually inaccessible. + // It could be a qualified symbol and hence verify the path + // e.g.: + // module m { + // export class c { + // } + // } + // const x: typeof m.c + // In the above example when we start with checking if typeof m.c symbol is accessible, + // we are going to see if c can be accessed in scope directly. + // But it can't, hence the accessible is going to be undefined, but that doesn't mean m.c is inaccessible + // It is accessible if the parent m is accessible because then m.c can be accessed through qualification + + containers := ch.getContainersOfSymbol(symbol, enclosingDeclaration, meaning) + nextMeaning := meaning + if initialSymbol == symbol { + nextMeaning = getQualifiedLeftMeaning(meaning) + } + parentResult := ch.IsAnySymbolAccessible(containers, enclosingDeclaration, initialSymbol, nextMeaning, shouldComputeAliasesToMakeVisible, allowModules) + if parentResult != nil { + return parentResult + } + } + + if earlyModuleBail { + return &printer.SymbolAccessibilityResult{ + Accessibility: printer.SymbolAccessibilityAccessible, + } + } + + if hadAccessibleChain != nil { + var moduleName string + if hadAccessibleChain != initialSymbol { + moduleName = ch.symbolToStringEx(hadAccessibleChain, enclosingDeclaration, ast.SymbolFlagsNamespace, SymbolFormatFlagsAllowAnyNodeKind, nil) + } + return &printer.SymbolAccessibilityResult{ + Accessibility: printer.SymbolAccessibilityNotAccessible, + ErrorSymbolName: ch.symbolToStringEx(initialSymbol, enclosingDeclaration, meaning, SymbolFormatFlagsAllowAnyNodeKind, nil), + ErrorModuleName: moduleName, + } + } + return nil +} + +func hasNonGlobalAugmentationExternalModuleSymbol(declaration *ast.Node) bool { + return isModuleWithStringLiteralName(declaration) || (declaration.Kind == ast.KindSourceFile && ast.IsExternalOrCommonJSModule(declaration.AsSourceFile())) +} + +func isModuleWithStringLiteralName(node *ast.Node) bool { + return ast.IsModuleDeclaration(node) && node.Name().Kind == ast.KindStringLiteral +} + +func getQualifiedLeftMeaning(rightMeaning ast.SymbolFlags) ast.SymbolFlags { + // If we are looking in value space, the parent meaning is value, other wise it is namespace + if rightMeaning == ast.SymbolFlagsValue { + return ast.SymbolFlagsValue + } + return ast.SymbolFlagsNamespace +} + +func (ch *Checker) getWithAlternativeContainers(container *ast.Symbol, symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags) []*ast.Symbol { + additionalContainers := core.MapNonNil(container.Declarations, func(d *ast.Node) *ast.Symbol { + return ch.getFileSymbolIfFileSymbolExportEqualsContainer(d, container) + }) + var reexportContainers []*ast.Symbol + if enclosingDeclaration != nil { + reexportContainers = ch.getAlternativeContainingModules(symbol, enclosingDeclaration) + } + objectLiteralContainer := ch.getVariableDeclarationOfObjectLiteral(container, meaning) + leftMeaning := getQualifiedLeftMeaning(meaning) + if enclosingDeclaration != nil && + container.Flags&leftMeaning != 0 && + len(ch.getAccessibleSymbolChain(container, enclosingDeclaration, ast.SymbolFlagsNamespace /*useOnlyExternalAliasing*/, false)) > 0 { + // This order expresses a preference for the real container if it is in scope + res := append(append([]*ast.Symbol{container}, additionalContainers...), reexportContainers...) + if objectLiteralContainer != nil { + res = append(res, objectLiteralContainer) + } + return res + } + // we potentially have a symbol which is a member of the instance side of something - look for a variable in scope with the container's type + // which may be acting like a namespace (eg, `Symbol` acts like a namespace when looking up `Symbol.toStringTag`) + var firstVariableMatch *ast.Symbol + if (meaning == ast.SymbolFlagsValue && + container.Flags&leftMeaning == 0) && + container.Flags&ast.SymbolFlagsType != 0 && + ch.getDeclaredTypeOfSymbol(container).flags&TypeFlagsObject != 0 { + ch.someSymbolTableInScope(enclosingDeclaration, func(t ast.SymbolTable, _ bool, _ bool, _ *ast.Node) bool { + for _, s := range t { + if s.Flags&leftMeaning != 0 && ch.getTypeOfSymbol(s) == ch.getDeclaredTypeOfSymbol(container) { + firstVariableMatch = s + return true + } + } + return false + }) + } + + var res []*ast.Symbol + if firstVariableMatch != nil { + res = append(res, firstVariableMatch) + } + res = append(res, additionalContainers...) + res = append(res, container) + if objectLiteralContainer != nil { + res = append(res, objectLiteralContainer) + } + res = append(res, reexportContainers...) + return res +} + +func (ch *Checker) getAlternativeContainingModules(symbol *ast.Symbol, enclosingDeclaration *ast.Node) []*ast.Symbol { + if enclosingDeclaration == nil { + return nil + } + containingFile := ast.GetSourceFileOfNode(enclosingDeclaration) + id := ast.GetNodeId(containingFile.AsNode()) + links := ch.symbolContainerLinks.Get(symbol) + if links.extendedContainersByFile == nil { + links.extendedContainersByFile = make(map[ast.NodeId][]*ast.Symbol) + } + existing, ok := links.extendedContainersByFile[id] + if ok && existing != nil { + return existing + } + var results []*ast.Symbol + if len(containingFile.Imports) > 0 { + // Try to make an import using an import already in the enclosing file, if possible + for _, importRef := range containingFile.Imports { + if ast.NodeIsSynthesized(importRef) { + // Synthetic names can't be resolved by `resolveExternalModuleName` - they'll cause a debug assert if they error + continue + } + resolvedModule := ch.resolveExternalModuleName(enclosingDeclaration, importRef /*ignoreErrors*/, true) + if resolvedModule == nil { + continue + } + ref := ch.getAliasForSymbolInContainer(resolvedModule, symbol) + if ref == nil { + continue + } + results = append(results, resolvedModule) + } + if len(results) > 0 { + links.extendedContainersByFile[id] = results + return results + } + } + + if links.extendedContainers != nil { + return *links.extendedContainers + } + // No results from files already being imported by this file - expand search (expensive, but not location-specific, so cached) + otherFiles := ch.program.SourceFiles() + for _, file := range otherFiles { + if !ast.IsExternalModule(file) { + continue + } + sym := ch.getSymbolOfDeclaration(file.AsNode()) + ref := ch.getAliasForSymbolInContainer(sym, symbol) + if ref == nil { + continue + } + results = append(results, sym) + } + links.extendedContainers = &results + return results +} + +func (ch *Checker) getVariableDeclarationOfObjectLiteral(symbol *ast.Symbol, meaning ast.SymbolFlags) *ast.Symbol { + // If we're trying to reference some object literal in, eg `var a = { x: 1 }`, the symbol for the literal, `__object`, is distinct + // from the symbol of the declaration it is being assigned to. Since we can use the declaration to refer to the literal, however, + // we'd like to make that connection here - potentially causing us to paint the declaration's visibility, and therefore the literal. + if meaning&ast.SymbolFlagsValue == 0 { + return nil + } + if len(symbol.Declarations) == 0 { + return nil + } + firstDecl := symbol.Declarations[0] + if firstDecl.Parent == nil { + return nil + } + if !ast.IsVariableDeclaration(firstDecl.Parent) { + return nil + } + if ast.IsObjectLiteralExpression(firstDecl) && firstDecl == firstDecl.Parent.Initializer() || ast.IsTypeLiteralNode(firstDecl) && firstDecl == firstDecl.Parent.Type() { + return ch.getSymbolOfDeclaration(firstDecl.Parent) + } + return nil +} + +func hasExternalModuleSymbol(declaration *ast.Node) bool { + return ast.IsAmbientModule(declaration) || (declaration.Kind == ast.KindSourceFile && ast.IsExternalOrCommonJSModule(declaration.AsSourceFile())) +} + +func (ch *Checker) getExternalModuleContainer(declaration *ast.Node) *ast.Symbol { + node := ast.FindAncestor(declaration, hasExternalModuleSymbol) + if node == nil { + return nil + } + return ch.getSymbolOfDeclaration(node) +} + +func (ch *Checker) getFileSymbolIfFileSymbolExportEqualsContainer(d *ast.Node, container *ast.Symbol) *ast.Symbol { + fileSymbol := ch.getExternalModuleContainer(d) + if fileSymbol == nil || fileSymbol.Exports == nil { + return nil + } + exported, ok := fileSymbol.Exports[ast.InternalSymbolNameExportEquals] + if !ok || exported == nil { + return nil + } + if ch.getSymbolIfSameReference(exported, container) != nil { + return fileSymbol + } + return nil +} + +/** +* Attempts to find the symbol corresponding to the container a symbol is in - usually this +* is just its' `.parent`, but for locals, this value is `undefined` + */ +func (ch *Checker) getContainersOfSymbol(symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags) []*ast.Symbol { + container := ch.getParentOfSymbol(symbol) + // Type parameters end up in the `members` lists but are not externally visible + if container != nil && (symbol.Flags&ast.SymbolFlagsTypeParameter == 0) { + return ch.getWithAlternativeContainers(container, symbol, enclosingDeclaration, meaning) + } + var candidates []*ast.Symbol + for _, d := range symbol.Declarations { + if !ast.IsAmbientModule(d) && d.Parent != nil { + // direct children of a module + if hasNonGlobalAugmentationExternalModuleSymbol(d.Parent) { + sym := ch.getSymbolOfDeclaration(d.Parent) + if sym != nil { + candidates = append(candidates, sym) + } + continue + } + // export ='d member of an ambient module + if ast.IsModuleBlock(d.Parent) && d.Parent.Parent != nil && ch.resolveExternalModuleSymbol(ch.getSymbolOfDeclaration(d.Parent.Parent), false) == symbol { + sym := ch.getSymbolOfDeclaration(d.Parent.Parent) + if sym != nil { + candidates = append(candidates, sym) + } + continue + } + } + if ast.IsClassExpression(d) && ast.IsBinaryExpression(d.Parent) && d.Parent.AsBinaryExpression().OperatorToken.Kind == ast.KindEqualsToken && ast.IsAccessExpression(d.Parent.AsBinaryExpression().Left) && ast.IsEntityNameExpression(d.Parent.AsBinaryExpression().Left.Expression()) { + if isModuleExportsAccessExpression(d.Parent.AsBinaryExpression().Left) || ast.IsExportsIdentifier(d.Parent.AsBinaryExpression().Left.Expression()) { + sym := ch.getSymbolOfDeclaration(ast.GetSourceFileOfNode(d).AsNode()) + if sym != nil { + candidates = append(candidates, sym) + } + continue + } + ch.checkExpressionCached(d.Parent.AsBinaryExpression().Left.Expression()) + sym := ch.symbolNodeLinks.Get(d.Parent.AsBinaryExpression().Left.Expression()).resolvedSymbol + if sym != nil { + candidates = append(candidates, sym) + } + continue + } + } + if len(candidates) == 0 { + return nil + } + + var bestContainers []*ast.Symbol + var alternativeContainers []*ast.Symbol + for _, container := range candidates { + if ch.getAliasForSymbolInContainer(container, symbol) == nil { + continue + } + allAlts := ch.getWithAlternativeContainers(container, symbol, enclosingDeclaration, meaning) + if len(allAlts) == 0 { + continue + } + bestContainers = append(bestContainers, allAlts[0]) + alternativeContainers = append(alternativeContainers, allAlts[1:]...) + } + return append(bestContainers, alternativeContainers...) +} + +func (ch *Checker) getAliasForSymbolInContainer(container *ast.Symbol, symbol *ast.Symbol) *ast.Symbol { + if container == ch.getParentOfSymbol(symbol) { + // fast path, `symbol` is either already the alias or isn't aliased + return symbol + } + // Check if container is a thing with an `export=` which points directly at `symbol`, and if so, return + // the container itself as the alias for the symbol + if container.Exports != nil { + exportEquals, ok := container.Exports[ast.InternalSymbolNameExportEquals] + if ok && exportEquals != nil && ch.getSymbolIfSameReference(exportEquals, symbol) != nil { + return container + } + } + exports := ch.getExportsOfSymbol(container) + quick, ok := exports[symbol.Name] + if ok && quick != nil && ch.getSymbolIfSameReference(quick, symbol) != nil { + return quick + } + for _, exported := range exports { + if ch.getSymbolIfSameReference(exported, symbol) != nil { + return exported + } + } + return nil +} + +func (ch *Checker) getAccessibleSymbolChain( + symbol *ast.Symbol, + enclosingDeclaration *ast.Node, + meaning ast.SymbolFlags, + useOnlyExternalAliasing bool) []*ast.Symbol { + return ch.getAccessibleSymbolChainEx(accessibleSymbolChainContext{symbol, enclosingDeclaration, meaning, useOnlyExternalAliasing, make(map[ast.SymbolId]map[uintptr]struct{})}) +} + +type accessibleSymbolChainContext struct { + symbol *ast.Symbol + enclosingDeclaration *ast.Node + meaning ast.SymbolFlags + useOnlyExternalAliasing bool + visitedSymbolTablesMap map[ast.SymbolId]map[uintptr]struct{} +} + +func (ch *Checker) getAccessibleSymbolChainEx(ctx accessibleSymbolChainContext) []*ast.Symbol { + if ctx.symbol == nil { + return nil + } + if isPropertyOrMethodDeclarationSymbol(ctx.symbol) { + return nil + } + // Go from enclosingDeclaration to the first scope we check, so the cache is keyed off the scope and thus shared more + var firstRelevantLocation *ast.Node + ch.someSymbolTableInScope(ctx.enclosingDeclaration, func(_ ast.SymbolTable, _ bool, _ bool, node *ast.Node) bool { + firstRelevantLocation = node + return true + }) + links := ch.symbolContainerLinks.Get(ctx.symbol) + linkKey := accessibleChainCacheKey{ctx.useOnlyExternalAliasing, firstRelevantLocation, ctx.meaning} + if links.accessibleChainCache == nil { + links.accessibleChainCache = make(map[accessibleChainCacheKey][]*ast.Symbol) + } + existing, ok := links.accessibleChainCache[linkKey] + if ok { + return existing + } + + var result []*ast.Symbol + + ch.someSymbolTableInScope(ctx.enclosingDeclaration, func(t ast.SymbolTable, ignoreQualification bool, isLocalNameLookup bool, _ *ast.Node) bool { + res := ch.getAccessibleSymbolChainFromSymbolTable(ctx, t, ignoreQualification, isLocalNameLookup) + if len(res) > 0 { + result = res + return true + } + return false + }) + links.accessibleChainCache[linkKey] = result + return result +} + +/** +* @param {ignoreQualification} boolean Set when a symbol is being looked for through the exports of another symbol (meaning we have a route to qualify it already) + */ +func (ch *Checker) getAccessibleSymbolChainFromSymbolTable(ctx accessibleSymbolChainContext, t ast.SymbolTable, ignoreQualification bool, isLocalNameLookup bool) []*ast.Symbol { + symId := ast.GetSymbolId(ctx.symbol) + visitedSymbolTables, ok := ctx.visitedSymbolTablesMap[symId] + if !ok { + visitedSymbolTables = make(map[uintptr]struct{}) + ctx.visitedSymbolTablesMap[symId] = visitedSymbolTables + } + + id := reflect.ValueOf(t).Pointer() // TODO: Is this seriously the only way to check reference equality of maps? + _, present := visitedSymbolTables[id] + if present { + return nil + } + visitedSymbolTables[id] = struct{}{} + + res := ch.trySymbolTable(ctx, t, ignoreQualification, isLocalNameLookup) + + delete(visitedSymbolTables, id) + return res +} + +func (ch *Checker) trySymbolTable( + ctx accessibleSymbolChainContext, + symbols ast.SymbolTable, + ignoreQualification bool, + isLocalNameLookup bool, +) []*ast.Symbol { + // If symbol is directly available by its name in the symbol table + res, ok := symbols[ctx.symbol.Name] + if ok && res != nil && ch.isAccessible(ctx, res /*resolvedAliasSymbol*/, nil, ignoreQualification) { + return []*ast.Symbol{ctx.symbol} + } + + for _, symbolFromSymbolTable := range symbols { + if symbolFromSymbolTable.Flags&ast.SymbolFlagsAlias != 0 && + symbolFromSymbolTable.Name != ast.InternalSymbolNameExportEquals && + symbolFromSymbolTable.Name != ast.InternalSymbolNameDefault && + !(isUMDExportSymbol(symbolFromSymbolTable) && ctx.enclosingDeclaration != nil && ast.IsExternalModule(ast.GetSourceFileOfNode(ctx.enclosingDeclaration))) && + // If `!useOnlyExternalAliasing`, we can use any type of alias to get the name + (!ctx.useOnlyExternalAliasing || core.Some(symbolFromSymbolTable.Declarations, ast.IsExternalModuleImportEqualsDeclaration)) && + // If we're looking up a local name to reference directly, omit namespace reexports, otherwise when we're trawling through an export list to make a dotted name, we can keep it + (isLocalNameLookup && !core.Some(symbolFromSymbolTable.Declarations, isNamespaceReexportDeclaration) || !isLocalNameLookup) && + // While exports are generally considered to be in scope, export-specifier declared symbols are _not_ + // See similar comment in `resolveName` for details + (ignoreQualification || len(getDeclarationsOfKind(symbolFromSymbolTable, ast.KindExportSpecifier)) == 0) { + resolvedImportedSymbol := ch.resolveAlias(symbolFromSymbolTable) + candidate := ch.getCandidateListForSymbol(ctx, symbolFromSymbolTable, resolvedImportedSymbol, ignoreQualification) + if len(candidate) > 0 { + return candidate + } + } + if symbolFromSymbolTable.Name == ctx.symbol.Name && symbolFromSymbolTable.ExportSymbol != nil { + if ch.isAccessible(ctx, ch.getMergedSymbol(symbolFromSymbolTable.ExportSymbol) /*resolvedAliasSymbol*/, nil, ignoreQualification) { + return []*ast.Symbol{ctx.symbol} + } + } + } + + // If there's no result and we're looking at the global symbol table, treat `globalThis` like an alias and try to lookup thru that + if reflect.ValueOf(ch.globals).Pointer() == reflect.ValueOf(symbols).Pointer() { + return ch.getCandidateListForSymbol(ctx, ch.globalThisSymbol, ch.globalThisSymbol, ignoreQualification) + } + return nil +} + +func isUMDExportSymbol(symbol *ast.Symbol) bool { + return symbol != nil && len(symbol.Declarations) > 0 && symbol.Declarations[0] != nil && ast.IsNamespaceExportDeclaration(symbol.Declarations[0]) +} + +func isNamespaceReexportDeclaration(node *ast.Node) bool { + return ast.IsNamespaceExport(node) && node.Parent.AsExportDeclaration().ModuleSpecifier != nil +} + +func (ch *Checker) getCandidateListForSymbol( + ctx accessibleSymbolChainContext, + symbolFromSymbolTable *ast.Symbol, + resolvedImportedSymbol *ast.Symbol, + ignoreQualification bool, +) []*ast.Symbol { + if ch.isAccessible(ctx, symbolFromSymbolTable, resolvedImportedSymbol, ignoreQualification) { + return []*ast.Symbol{symbolFromSymbolTable} + } + + // Look in the exported members, if we can find accessibleSymbolChain, symbol is accessible using this chain + // but only if the symbolFromSymbolTable can be qualified + candidateTable := ch.getExportsOfSymbol(resolvedImportedSymbol) + if candidateTable == nil { + return nil + } + accessibleSymbolsFromExports := ch.getAccessibleSymbolChainFromSymbolTable(ctx, candidateTable /*ignoreQualification*/, true, false) + if len(accessibleSymbolsFromExports) == 0 { + return nil + } + if !ch.canQualifySymbol(ctx, symbolFromSymbolTable, getQualifiedLeftMeaning(ctx.meaning)) { + return nil + } + return append([]*ast.Symbol{symbolFromSymbolTable}, accessibleSymbolsFromExports...) +} + +func (ch *Checker) isAccessible( + ctx accessibleSymbolChainContext, + symbolFromSymbolTable *ast.Symbol, + resolvedAliasSymbol *ast.Symbol, + ignoreQualification bool, +) bool { + likeSymbols := false + if ctx.symbol == resolvedAliasSymbol { + likeSymbols = true + } + if ctx.symbol == symbolFromSymbolTable { + likeSymbols = true + } + symbol := ch.getMergedSymbol(ctx.symbol) + if symbol == ch.getMergedSymbol(resolvedAliasSymbol) { + likeSymbols = true + } + if symbol == ch.getMergedSymbol(symbolFromSymbolTable) { + likeSymbols = true + } + if !likeSymbols { + return false + } + // if the symbolFromSymbolTable is not external module (it could be if it was determined as ambient external module and would be in globals table) + // and if symbolFromSymbolTable or alias resolution matches the symbol, + // check the symbol can be qualified, it is only then this symbol is accessible + return !core.Some(symbolFromSymbolTable.Declarations, hasNonGlobalAugmentationExternalModuleSymbol) && + (ignoreQualification || ch.canQualifySymbol(ctx, ch.getMergedSymbol(symbolFromSymbolTable), ctx.meaning)) +} + +func (ch *Checker) canQualifySymbol( + ctx accessibleSymbolChainContext, + symbolFromSymbolTable *ast.Symbol, + meaning ast.SymbolFlags, +) bool { + // If the symbol is equivalent and doesn't need further qualification, this symbol is accessible + return !ch.needsQualification(symbolFromSymbolTable, ctx.enclosingDeclaration, meaning) || + // If symbol needs qualification, make sure that parent is accessible, if it is then this symbol is accessible too + len(ch.getAccessibleSymbolChainEx(accessibleSymbolChainContext{ctx.symbol, ctx.enclosingDeclaration, getQualifiedLeftMeaning(meaning), ctx.useOnlyExternalAliasing, ctx.visitedSymbolTablesMap})) > 0 +} + +func (ch *Checker) needsQualification(symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags) bool { + qualify := false + ch.someSymbolTableInScope(enclosingDeclaration, func(symbolTable ast.SymbolTable, _ bool, _ bool, _ *ast.Node) bool { + // If symbol of this name is not available in the symbol table we are ok + res, ok := symbolTable[symbol.Name] + if !ok || res == nil { + return false + } + symbolFromSymbolTable := ch.getMergedSymbol(res) + if symbolFromSymbolTable == nil { + // Continue to the next symbol table + return false + } + // If the symbol with this name is present it should refer to the symbol + if symbolFromSymbolTable == symbol { + // No need to qualify + return true + } + + // Qualify if the symbol from symbol table has same meaning as expected + shouldResolveAlias := symbolFromSymbolTable.Flags&ast.SymbolFlagsAlias != 0 && ast.GetDeclarationOfKind(symbolFromSymbolTable, ast.KindExportSpecifier) == nil + if shouldResolveAlias { + symbolFromSymbolTable = ch.resolveAlias(symbolFromSymbolTable) + } + flags := symbolFromSymbolTable.Flags + if shouldResolveAlias { + flags = ch.getSymbolFlags(symbolFromSymbolTable) + } + if flags&meaning != 0 { + qualify = true + return true + } + + // Continue to the next symbol table + return false + }) + + return qualify +} + +func isPropertyOrMethodDeclarationSymbol(symbol *ast.Symbol) bool { + if len(symbol.Declarations) > 0 { + for _, declaration := range symbol.Declarations { + switch declaration.Kind { + case ast.KindPropertyDeclaration, + ast.KindMethodDeclaration, + ast.KindGetAccessor, + ast.KindSetAccessor: + continue + default: + return false + } + } + return true + } + return false +} + +func (ch *Checker) someSymbolTableInScope( + enclosingDeclaration *ast.Node, + callback func(symbolTable ast.SymbolTable, ignoreQualification bool, isLocalNameLookup bool, scopeNode *ast.Node) bool, +) bool { + for location := enclosingDeclaration; location != nil; location = location.Parent { + // Locals of a source file are not in scope (because they get merged into the global symbol table) + if canHaveLocals(location) && location.Locals() != nil && !ast.IsGlobalSourceFile(location) { + if callback(location.Locals(), false, true, location) { + return true + } + } + switch location.Kind { + case ast.KindSourceFile, ast.KindModuleDeclaration: + if ast.IsSourceFile(location) && !ast.IsExternalOrCommonJSModule(location.AsSourceFile()) { + break + } + sym := ch.getSymbolOfDeclaration(location) + if callback(sym.Exports, false, true, location) { + return true + } + case ast.KindClassDeclaration, ast.KindClassExpression, ast.KindInterfaceDeclaration: + // Type parameters are bound into `members` lists so they can merge across declarations + // This is troublesome, since in all other respects, they behave like locals :cries: + // TODO: the below is shared with similar code in `resolveName` - in fact, rephrasing all this symbol + // lookup logic in terms of `resolveName` would be nice + // The below is used to lookup type parameters within a class or interface, as they are added to the class/interface locals + // These can never be latebound, so the symbol's raw members are sufficient. `getMembersOfNode` cannot be used, as it would + // trigger resolving late-bound names, which we may already be in the process of doing while we're here! + var table ast.SymbolTable + sym := ch.getSymbolOfDeclaration(location) + // TODO: Should this filtered table be cached in some way? + for key, memberSymbol := range sym.Members { + if memberSymbol.Flags&(ast.SymbolFlagsType & ^ast.SymbolFlagsAssignment) != 0 { + if table == nil { + table = make(ast.SymbolTable) + } + table[key] = memberSymbol + } + } + if table != nil && callback(table, false, false, location) { + return true + } + } + } + + return callback(ch.globals, false, true, nil) +} + +/** + * Check if the given symbol in given enclosing declaration is accessible and mark all associated alias to be visible if requested + * + * @param symbol a Symbol to check if accessible + * @param enclosingDeclaration a Node containing reference to the symbol + * @param meaning a SymbolFlags to check if such meaning of the symbol is accessible + * @param shouldComputeAliasToMakeVisible a boolean value to indicate whether to return aliases to be mark visible in case the symbol is accessible + */ + +func (c *Checker) IsSymbolAccessible(symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags, shouldComputeAliasesToMakeVisible bool) printer.SymbolAccessibilityResult { + return c.isSymbolAccessibleWorker(symbol, enclosingDeclaration, meaning, shouldComputeAliasesToMakeVisible, true /*allowModules*/) +} + +func (c *Checker) isSymbolAccessibleWorker(symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags, shouldComputeAliasesToMakeVisible bool, allowModules bool) printer.SymbolAccessibilityResult { + if symbol != nil && enclosingDeclaration != nil { + result := c.IsAnySymbolAccessible([]*ast.Symbol{symbol}, enclosingDeclaration, symbol, meaning, shouldComputeAliasesToMakeVisible, allowModules) + if result != nil { + return *result + } + + // This could be a symbol that is not exported in the external module + // or it could be a symbol from different external module that is not aliased and hence cannot be named + symbolExternalModule := core.FirstNonNil(symbol.Declarations, c.getExternalModuleContainer) + if symbolExternalModule != nil { + enclosingExternalModule := c.getExternalModuleContainer(enclosingDeclaration) + if symbolExternalModule != enclosingExternalModule { + // name from different external module that is not visible + return printer.SymbolAccessibilityResult{ + Accessibility: printer.SymbolAccessibilityCannotBeNamed, + ErrorSymbolName: c.symbolToStringEx(symbol, enclosingDeclaration, meaning, SymbolFormatFlagsAllowAnyNodeKind, nil), + ErrorModuleName: c.symbolToString(symbolExternalModule), + ErrorNode: core.IfElse(ast.IsInJSFile(enclosingDeclaration), enclosingDeclaration, nil), + } + } + } + + // Just a local name that is not accessible + return printer.SymbolAccessibilityResult{ + Accessibility: printer.SymbolAccessibilityNotAccessible, + ErrorSymbolName: c.symbolToStringEx(symbol, enclosingDeclaration, meaning, SymbolFormatFlagsAllowAnyNodeKind, nil), + } + } + + return printer.SymbolAccessibilityResult{ + Accessibility: printer.SymbolAccessibilityAccessible, + } +} diff --git a/internal/checker/symboltracker.go b/internal/checker/symboltracker.go new file mode 100644 index 0000000000..7abd4a3625 --- /dev/null +++ b/internal/checker/symboltracker.go @@ -0,0 +1,138 @@ +package checker + +import ( + "github.com/microsoft/typescript-go/internal/ast" + "github.com/microsoft/typescript-go/internal/nodebuilder" +) + +type SymbolTrackerImpl struct { + context *NodeBuilderContext + inner nodebuilder.SymbolTracker + DisableTrackSymbol bool +} + +func NewSymbolTrackerImpl(context *NodeBuilderContext, tracker nodebuilder.SymbolTracker) *SymbolTrackerImpl { + var inner nodebuilder.SymbolTracker + if tracker != nil { + inner = tracker.GetInnerSymbolTracker() + if inner == nil { + inner = tracker + } + } + + return &SymbolTrackerImpl{context, inner, false} +} + +func (this *SymbolTrackerImpl) GetModuleSpecifierGenerationHost() any { + if this.inner == nil { + return nil + } + return this.inner.GetModuleSpecifierGenerationHost() +} + +func (this *SymbolTrackerImpl) GetInnerSymbolTracker() nodebuilder.SymbolTracker { + return this.inner +} + +func (this *SymbolTrackerImpl) TrackSymbol(symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags) bool { + if !this.DisableTrackSymbol { + if this.inner != nil && this.inner.TrackSymbol(symbol, enclosingDeclaration, meaning) { + this.onDiagnosticReported() + return true + } + // Skip recording type parameters as they dont contribute to late painted statements + if symbol.Flags&ast.SymbolFlagsTypeParameter == 0 { + this.context.trackedSymbols = append(this.context.trackedSymbols, &TrackedSymbolArgs{symbol, enclosingDeclaration, meaning}) + } + } + return false +} + +func (this *SymbolTrackerImpl) ReportInaccessibleThisError() { + this.onDiagnosticReported() + if this.inner == nil { + return + } + this.inner.ReportInaccessibleThisError() +} + +func (this *SymbolTrackerImpl) ReportPrivateInBaseOfClassExpression(propertyName string) { + this.onDiagnosticReported() + if this.inner == nil { + return + } + this.inner.ReportPrivateInBaseOfClassExpression(propertyName) +} + +func (this *SymbolTrackerImpl) ReportInaccessibleUniqueSymbolError() { + this.onDiagnosticReported() + if this.inner == nil { + return + } + this.inner.ReportInaccessibleUniqueSymbolError() +} + +func (this *SymbolTrackerImpl) ReportCyclicStructureError() { + this.onDiagnosticReported() + if this.inner == nil { + return + } + this.inner.ReportCyclicStructureError() +} + +func (this *SymbolTrackerImpl) ReportLikelyUnsafeImportRequiredError(specifier string) { + this.onDiagnosticReported() + if this.inner == nil { + return + } + this.inner.ReportLikelyUnsafeImportRequiredError(specifier) +} + +func (this *SymbolTrackerImpl) ReportTruncationError() { + this.onDiagnosticReported() + if this.inner == nil { + return + } + this.inner.ReportTruncationError() +} + +func (this *SymbolTrackerImpl) ReportNonlocalAugmentation(containingFile *ast.SourceFile, parentSymbol *ast.Symbol, augmentingSymbol *ast.Symbol) { + this.onDiagnosticReported() + if this.inner == nil { + return + } + this.inner.ReportNonlocalAugmentation(containingFile, parentSymbol, augmentingSymbol) +} + +func (this *SymbolTrackerImpl) ReportNonSerializableProperty(propertyName string) { + this.onDiagnosticReported() + if this.inner == nil { + return + } + this.inner.ReportNonSerializableProperty(propertyName) +} + +func (this *SymbolTrackerImpl) onDiagnosticReported() { + this.context.reportedDiagnostic = true +} + +func (this *SymbolTrackerImpl) ReportInferenceFallback(node *ast.Node) { + if this.inner == nil { + return + } + this.inner.ReportInferenceFallback(node) +} + +func (this *SymbolTrackerImpl) PushErrorFallbackNode(node *ast.Node) { + if this.inner == nil { + return + } + this.inner.PushErrorFallbackNode(node) +} + +func (this *SymbolTrackerImpl) PopErrorFallbackNode() { + if this.inner == nil { + return + } + this.inner.PopErrorFallbackNode() +} diff --git a/internal/checker/types.go b/internal/checker/types.go index fa77c35cce..4afd5e2b22 100644 --- a/internal/checker/types.go +++ b/internal/checker/types.go @@ -75,6 +75,37 @@ const ( TypeFormatFlagsInTypeAlias TypeFormatFlags = 1 << 23 // Writing type in type alias declaration ) +const TypeFormatFlagsNodeBuilderFlagsMask = TypeFormatFlagsNoTruncation | TypeFormatFlagsWriteArrayAsGenericType | TypeFormatFlagsGenerateNamesForShadowedTypeParams | TypeFormatFlagsUseStructuralFallback | TypeFormatFlagsWriteTypeArgumentsOfSignature | + TypeFormatFlagsUseFullyQualifiedType | TypeFormatFlagsSuppressAnyReturnType | TypeFormatFlagsMultilineObjectLiterals | TypeFormatFlagsWriteClassExpressionAsTypeLiteral | + TypeFormatFlagsUseTypeOfFunction | TypeFormatFlagsOmitParameterModifiers | TypeFormatFlagsUseAliasDefinedOutsideCurrentScope | TypeFormatFlagsAllowUniqueESSymbolType | TypeFormatFlagsInTypeAlias | + TypeFormatFlagsUseSingleQuotesForStringLiteralType | TypeFormatFlagsNoTypeReduction | TypeFormatFlagsOmitThisParameter + +// dprint-ignore +type SymbolFormatFlags int32 + +const ( + SymbolFormatFlagsNone SymbolFormatFlags = 0 + // Write symbols's type argument if it is instantiated symbol + // eg. class C { p: T } <-- Show p as C.p here + // var a: C; + // var p = a.p; <--- Here p is property of C so show it as C.p instead of just C.p + SymbolFormatFlagsWriteTypeParametersOrArguments SymbolFormatFlags = 1 << 0 + // Use only external alias information to get the symbol name in the given context + // eg. module m { export class c { } } import x = m.c; + // When this flag is specified m.c will be used to refer to the class instead of alias symbol x + SymbolFormatFlagsUseOnlyExternalAliasing SymbolFormatFlags = 1 << 1 + // Build symbol name using any nodes needed, instead of just components of an entity name + SymbolFormatFlagsAllowAnyNodeKind SymbolFormatFlags = 1 << 2 + // Prefer aliases which are not directly visible + SymbolFormatFlagsUseAliasDefinedOutsideCurrentScope SymbolFormatFlags = 1 << 3 + // { [E.A]: 1 } + /** @internal */ + SymbolFormatFlagsWriteComputedProps SymbolFormatFlags = 1 << 4 + // Skip building an accessible symbol chain + /** @internal */ + SymbolFormatFlagsDoNotIncludeSymbolChain SymbolFormatFlags = 1 << 5 +) + // Ids type TypeId uint32 @@ -237,6 +268,18 @@ type MarkedAssignmentSymbolLinks struct { hasDefiniteAssignment bool // Symbol is definitely assigned somewhere } +type accessibleChainCacheKey struct { + useOnlyExternalAliasing bool + location *ast.Node + meaning ast.SymbolFlags +} + +type ContainingSymbolLinks struct { + extendedContainersByFile map[ast.NodeId][]*ast.Symbol // Symbols of nodes which which logically contain this one, cached by file the request is made within + extendedContainers *[]*ast.Symbol // Containers (other than the parent) which this symbol is aliased in + accessibleChainCache map[accessibleChainCacheKey][]*ast.Symbol +} + type AccessFlags uint32 const ( @@ -341,6 +384,12 @@ type AssertionLinks struct { exprType *Type // Assertion expression type } +// Links for declarations + +type DeclarationLinks struct { + isVisible core.Tristate // if declaration is depended upon by exported declarations +} + // SourceFile links type SourceFileLinks struct { @@ -594,6 +643,8 @@ func (t *Type) Target() *Type { return t.AsIndexType().target case t.flags&TypeFlagsStringMapping != 0: return t.AsStringMappingType().target + case t.flags&TypeFlagsObject != 0 && t.objectFlags&ObjectFlagsMapped != 0: + return t.AsMappedType().target } panic("Unhandled case in Type.Target") } @@ -697,6 +748,8 @@ type StructuredType struct { signatures []*Signature // Signatures (call + construct) callSignatureCount int // Count of call signatures indexInfos []*IndexInfo + + objectTypeWithoutAbstractConstructSignatures *Type } func (t *StructuredType) AsStructuredType() *StructuredType { return t } diff --git a/internal/checker/utilities.go b/internal/checker/utilities.go index e2231126f5..f52f197e89 100644 --- a/internal/checker/utilities.go +++ b/internal/checker/utilities.go @@ -349,7 +349,7 @@ func nodeCanBeDecorated(useLegacyDecorators bool, node *ast.Node, parent *ast.No 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.BodyData() != nil && (parent.BodyData()).Body != nil && (parent.Kind == ast.KindConstructor || parent.Kind == ast.KindMethodDeclaration || parent.Kind == ast.KindSetAccessor) && getThisParameter(parent) != node && grandparent != nil && grandparent.Kind == ast.KindClassDeclaration + return parent != nil && parent.BodyData() != nil && (parent.BodyData()).Body != nil && (parent.Kind == ast.KindConstructor || parent.Kind == ast.KindMethodDeclaration || parent.Kind == ast.KindSetAccessor) && ast.GetThisParameter(parent) != node && grandparent != nil && grandparent.Kind == ast.KindClassDeclaration } return false @@ -465,11 +465,6 @@ func getExternalModuleRequireArgument(node *ast.Node) *ast.Node { return nil } -func getExternalModuleImportEqualsDeclarationExpression(node *ast.Node) *ast.Node { - // Debug.assert(isExternalModuleImportEqualsDeclaration(node)) - return node.AsImportEqualsDeclaration().ModuleReference.AsExternalModuleReference().Expression -} - func isRightSideOfQualifiedNameOrPropertyAccess(node *ast.Node) bool { parent := node.Parent switch parent.Kind { @@ -1256,17 +1251,6 @@ func isLateBoundName(name string) bool { return len(name) >= 2 && name[0] == '\xfe' && name[1] == '@' } -func getThisParameter(signature *ast.Node) *ast.Node { - // callback tags do not currently support this parameters - if len(signature.Parameters()) != 0 { - thisParameter := signature.Parameters()[0] - if ast.IsThisParameter(thisParameter) { - return thisParameter - } - } - return nil -} - func isObjectOrArrayLiteralType(t *Type) bool { return t.objectFlags&(ObjectFlagsObjectLiteral|ObjectFlagsArrayLiteral) != 0 } @@ -2157,3 +2141,28 @@ func allDeclarationsInSameSourceFile(symbol *ast.Symbol) bool { } return true } + +func containsNonMissingUndefinedType(c *Checker, t *Type) bool { + var candidate *Type + if t.flags&TypeFlagsUnion != 0 { + candidate = t.AsUnionType().types[0] + } else { + candidate = t + } + return candidate.flags&TypeFlagsUndefined != 0 && candidate != c.missingType +} + +func getAnyImportSyntax(node *ast.Node) *ast.Node { + switch node.Kind { + case ast.KindImportEqualsDeclaration: + return node + case ast.KindImportClause: + return node.Parent + case ast.KindNamespaceImport: + return node.Parent.Parent + case ast.KindImportSpecifier: + return node.Parent.Parent.Parent + default: + return nil + } +} diff --git a/internal/compiler/emitHost.go b/internal/compiler/emitHost.go index 01445eeb10..592039ca38 100644 --- a/internal/compiler/emitHost.go +++ b/internal/compiler/emitHost.go @@ -3,6 +3,7 @@ package compiler import ( "github.com/microsoft/typescript-go/internal/ast" "github.com/microsoft/typescript-go/internal/core" + "github.com/microsoft/typescript-go/internal/declarations" "github.com/microsoft/typescript-go/internal/printer" "github.com/microsoft/typescript-go/internal/tspath" ) @@ -17,6 +18,7 @@ type WriteFileData struct { // NOTE: EmitHost operations must be thread-safe type EmitHost interface { + declarations.DeclarationEmitHost Options() *core.CompilerOptions SourceFiles() []*ast.SourceFile UseCaseSensitiveFileNames() bool @@ -35,6 +37,29 @@ type emitHost struct { program *Program } +func (host *emitHost) GetEffectiveDeclarationFlags(node *ast.Node, flags ast.ModifierFlags) ast.ModifierFlags { + // TODO: EmitContext().ParseNode(node) - can only find a checker for parse nodes! + ch := host.program.GetTypeCheckerForFile(ast.GetSourceFileOfNode(node)) + if ch == nil { + return ast.ModifierFlagsNone // TODO: Should this be a panic? + } + return ch.GetEffectiveDeclarationFlags(node, flags) +} + +func (host *emitHost) GetOutputPathsFor(file *ast.SourceFile, forceDtsPaths bool) declarations.OutputPaths { + // TODO: cache + return getOutputPathsFor(file, host, forceDtsPaths) +} + +func (host *emitHost) GetResolutionModeOverride(node *ast.Node) core.ResolutionMode { + ch := host.program.GetTypeCheckerForFile(ast.GetSourceFileOfNode(node)) + return ch.GetResolutionModeOverride(node.AsImportAttributes(), false) +} + +func (host *emitHost) GetSourceFileFromReference(origin *ast.SourceFile, ref *ast.FileReference) *ast.SourceFile { + return host.program.GetSourceFileFromReference(origin, ref) +} + func (host *emitHost) Options() *core.CompilerOptions { return host.program.Options() } func (host *emitHost) SourceFiles() []*ast.SourceFile { return host.program.SourceFiles() } func (host *emitHost) GetCurrentDirectory() string { return host.program.host.GetCurrentDirectory() } diff --git a/internal/compiler/emitter.go b/internal/compiler/emitter.go index 2970a179bf..f305ac63ca 100644 --- a/internal/compiler/emitter.go +++ b/internal/compiler/emitter.go @@ -8,6 +8,7 @@ import ( "github.com/microsoft/typescript-go/internal/binder" "github.com/microsoft/typescript-go/internal/compiler/diagnostics" "github.com/microsoft/typescript-go/internal/core" + "github.com/microsoft/typescript-go/internal/declarations" "github.com/microsoft/typescript-go/internal/printer" "github.com/microsoft/typescript-go/internal/sourcemap" "github.com/microsoft/typescript-go/internal/stringutil" @@ -98,6 +99,12 @@ func (e *emitter) getScriptTransformers(emitContext *printer.EmitContext, source return tx } +func (e *emitter) getDeclarationTransformers(emitContext *printer.EmitContext, sourceFile *ast.SourceFile, declarationFilePath string, declarationMapPath string) []*declarations.DeclarationTransformer { + emitResolver := e.host.GetEmitResolver(sourceFile, false /*skipDiagnostics*/) // !!! conditionally skip diagnostics + transform := declarations.NewDeclarationTransformer(e.host, emitResolver, emitContext, e.host.Options(), declarationFilePath, declarationMapPath) + return []*declarations.DeclarationTransformer{transform} +} + func (e *emitter) emitJSFile(sourceFile *ast.SourceFile, jsFilePath string, sourceMapFilePath string) { options := e.host.Options() @@ -129,7 +136,7 @@ func (e *emitter) emitJSFile(sourceFile *ast.SourceFile, jsFilePath string, sour // !!! }, emitContext) - e.printSourceFile(jsFilePath, sourceMapFilePath, sourceFile, printer) + e.printSourceFile(jsFilePath, sourceMapFilePath, sourceFile, printer, shouldEmitSourceMaps(options, sourceFile)) if e.emittedFilesList != nil { e.emittedFilesList = append(e.emittedFilesList, jsFilePath) @@ -140,22 +147,59 @@ func (e *emitter) emitJSFile(sourceFile *ast.SourceFile, jsFilePath string, sour } func (e *emitter) emitDeclarationFile(sourceFile *ast.SourceFile, declarationFilePath string, declarationMapPath string) { - // !!! + options := e.host.Options() + + if sourceFile == nil || e.emitOnly != emitAll && e.emitOnly != emitOnlyDts || len(declarationFilePath) == 0 { + return + } + + if options.NoEmit == core.TSTrue || e.host.IsEmitBlocked(declarationFilePath) { + return + } + + var diags []*ast.Diagnostic + emitContext := printer.NewEmitContext() + for _, transformer := range e.getDeclarationTransformers(emitContext, sourceFile, declarationFilePath, declarationMapPath) { + sourceFile = transformer.TransformSourceFile(sourceFile) + diags = append(diags, transformer.GetDiagnostics()...) + } + + printerOptions := printer.PrinterOptions{ + RemoveComments: options.RemoveComments.IsTrue(), + NewLine: options.NewLine, + NoEmitHelpers: options.NoEmitHelpers.IsTrue(), + SourceMap: options.DeclarationMap.IsTrue(), + InlineSourceMap: options.InlineSourceMap.IsTrue(), + InlineSources: options.InlineSources.IsTrue(), + // !!! + } + + // create a printer to print the nodes + printer := printer.NewPrinter(printerOptions, printer.PrintHandlers{ + // !!! + }, emitContext) + + e.printSourceFile(declarationFilePath, declarationMapPath, sourceFile, printer, shouldEmitDeclarationSourceMaps(options, sourceFile)) + + for _, elem := range diags { + // Add declaration transform diagnostics to emit diagnostics + e.emitterDiagnostics.Add(elem) + } } func (e *emitter) emitBuildInfo(buildInfoPath string) { // !!! } -func (e *emitter) printSourceFile(jsFilePath string, sourceMapFilePath string, sourceFile *ast.SourceFile, printer *printer.Printer) bool { +func (e *emitter) printSourceFile(outFilePath string, sourceMapFilePath string, sourceFile *ast.SourceFile, printer *printer.Printer, shouldEmitSourceMaps bool) bool { // !!! sourceMapGenerator options := e.host.Options() var sourceMapGenerator *sourcemap.Generator - if shouldEmitSourceMaps(options, sourceFile) { + if shouldEmitSourceMaps { sourceMapGenerator = sourcemap.NewGenerator( - tspath.GetBaseFileName(tspath.NormalizeSlashes(jsFilePath)), + tspath.GetBaseFileName(tspath.NormalizeSlashes(outFilePath)), getSourceRoot(options), - e.getSourceMapDirectory(options, jsFilePath, sourceFile), + e.getSourceMapDirectory(options, outFilePath, sourceFile), tspath.ComparePathsOptions{ UseCaseSensitiveFileNames: e.host.UseCaseSensitiveFileNames(), CurrentDirectory: e.host.GetCurrentDirectory(), @@ -174,14 +218,14 @@ func (e *emitter) printSourceFile(jsFilePath string, sourceMapFilePath string, s e.sourceMapDataList = append(e.sourceMapDataList, &SourceMapEmitResult{ InputSourceFileNames: sourceMapGenerator.Sources(), SourceMap: sourceMapGenerator.RawSourceMap(), - GeneratedFile: jsFilePath, + GeneratedFile: outFilePath, }) } sourceMappingURL := e.getSourceMappingURL( options, sourceMapGenerator, - jsFilePath, + outFilePath, sourceMapFilePath, sourceFile, ) @@ -199,7 +243,7 @@ func (e *emitter) printSourceFile(jsFilePath string, sourceMapFilePath string, s sourceMap := sourceMapGenerator.String() err := e.host.WriteFile(sourceMapFilePath, sourceMap, false /*writeByteOrderMark*/, sourceFiles, nil /*data*/) if err != nil { - e.emitterDiagnostics.Add(ast.NewCompilerDiagnostic(diagnostics.Could_not_write_file_0_Colon_1, jsFilePath, err.Error())) + e.emitterDiagnostics.Add(ast.NewCompilerDiagnostic(diagnostics.Could_not_write_file_0_Colon_1, outFilePath, err.Error())) } } } else { @@ -209,9 +253,9 @@ func (e *emitter) printSourceFile(jsFilePath string, sourceMapFilePath string, s // Write the output file text := e.writer.String() data := &WriteFileData{SourceMapUrlPos: sourceMapUrlPos} // !!! transform diagnostics - err := e.host.WriteFile(jsFilePath, text, e.host.Options().EmitBOM.IsTrue(), sourceFiles, data) + err := e.host.WriteFile(outFilePath, text, e.host.Options().EmitBOM.IsTrue(), sourceFiles, data) if err != nil { - e.emitterDiagnostics.Add(ast.NewCompilerDiagnostic(diagnostics.Could_not_write_file_0_Colon_1, jsFilePath, err.Error())) + e.emitterDiagnostics.Add(ast.NewCompilerDiagnostic(diagnostics.Could_not_write_file_0_Colon_1, outFilePath, err.Error())) } // Reset state @@ -262,6 +306,11 @@ func shouldEmitSourceMaps(mapOptions *core.CompilerOptions, sourceFile *ast.Sour !tspath.FileExtensionIs(sourceFile.FileName(), tspath.ExtensionJson) } +func shouldEmitDeclarationSourceMaps(mapOptions *core.CompilerOptions, sourceFile *ast.SourceFile) bool { + return mapOptions.DeclarationMap.IsTrue() && + !tspath.FileExtensionIs(sourceFile.FileName(), tspath.ExtensionJson) +} + func getSourceRoot(mapOptions *core.CompilerOptions) string { // Normalize source root and make sure it has trailing "/" so that it can be used to combine paths with the // relative paths of the sources list in the sourcemap @@ -342,8 +391,33 @@ func (e *emitter) getSourceMappingURL(mapOptions *core.CompilerOptions, sourceMa } func getDeclarationEmitOutputFilePath(file string, host EmitHost) string { - // !!! - return "" + options := host.Options() + var outputDir *string + if len(options.DeclarationDir) > 0 { + outputDir = &options.DeclarationDir + } else if len(options.OutDir) > 0 { + outputDir = &options.OutDir + } + + var path string + if outputDir != nil { + path = getSourceFilePathInNewDirWorker(file, *outputDir, host.GetCurrentDirectory(), host.CommonSourceDirectory(), host.UseCaseSensitiveFileNames()) + } else { + path = file + } + declarationExtension := tspath.GetDeclarationEmitExtensionForPath(path) + return tspath.RemoveFileExtension(path) + declarationExtension +} + +func getSourceFilePathInNewDirWorker(fileName string, newDirPath string, currentDirectory string, commonSourceDirectory string, useCaseSensitiveFileNames bool) string { + sourceFilePath := tspath.GetNormalizedAbsolutePath(fileName, currentDirectory) + commonDir := tspath.GetCanonicalFileName(commonSourceDirectory, useCaseSensitiveFileNames) + canonFile := tspath.GetCanonicalFileName(sourceFilePath, useCaseSensitiveFileNames) + isSourceFileInCommonSourceDirectory := strings.HasPrefix(canonFile, commonDir) + if isSourceFileInCommonSourceDirectory { + sourceFilePath = sourceFilePath[len(commonSourceDirectory):] + } + return tspath.CombinePaths(newDirPath, sourceFilePath) } type outputPaths struct { @@ -354,6 +428,16 @@ type outputPaths struct { buildInfoPath string } +// DeclarationFilePath implements declarations.OutputPaths. +func (o *outputPaths) DeclarationFilePath() string { + return o.declarationFilePath +} + +// JsFilePath implements declarations.OutputPaths. +func (o *outputPaths) JsFilePath() string { + return o.jsFilePath +} + func getOutputPathsFor(sourceFile *ast.SourceFile, host EmitHost, forceDtsEmit bool) *outputPaths { options := host.Options() // !!! bundle not implemented, may be deprecated @@ -434,3 +518,22 @@ func getSourceFilesToEmit(host EmitHost, targetSourceFile *ast.SourceFile, force return sourceFileMayBeEmitted(sourceFile, host, forceDtsEmit) }) } + +func isJsonSourceFile(file *ast.SourceFile) bool { + return file.ScriptKind == core.ScriptKindJSON +} + +func isSourceFileNotJson(file *ast.SourceFile) bool { + return !isJsonSourceFile(file) +} + +func getDeclarationDiagnostics(host EmitHost, resolver printer.EmitResolver, file *ast.SourceFile) []*ast.Diagnostic { + fullFiles := core.Filter(getSourceFilesToEmit(host, file, false), isSourceFileNotJson) + if !core.Some(fullFiles, func(f *ast.SourceFile) bool { return f == file }) { + return []*ast.Diagnostic{} + } + options := host.Options() + transform := declarations.NewDeclarationTransformer(host, resolver, nil, options, "", "") + transform.TransformSourceFile(file) + return transform.GetDiagnostics() +} diff --git a/internal/compiler/program.go b/internal/compiler/program.go index f5fdb1b523..1419afae8a 100644 --- a/internal/compiler/program.go +++ b/internal/compiler/program.go @@ -71,6 +71,50 @@ type Program struct { unsupportedExtensions []string } +/** This should have similar behavior to 'processSourceFile' without diagnostics or mutation. */ +func (p *Program) GetSourceFileFromReference(origin *ast.SourceFile, ref *ast.FileReference) *ast.SourceFile { + // TODO: The module loader in corsa is fairly different than strada, it should probably be able to expose this functionality at some point, + // rather than redoing the logic approximately here, since most of the related logic now lives in module.Resolver + // Still, without the failed lookup reporting that only the loader does, this isn't terribly complicated + + fileName := tspath.ResolvePath(tspath.GetDirectoryPath(origin.FileName()), ref.FileName) + supportedExtensionsBase := tsoptions.GetSupportedExtensions(p.Options(), nil /*extraFileExtensions*/) + supportedExtensions := tsoptions.GetSupportedExtensionsWithJsonIfResolveJsonModule(p.Options(), supportedExtensionsBase) + allowNonTsExtensions := p.Options().AllowNonTsExtensions.IsTrue() + if tspath.HasExtension(fileName) { + if !allowNonTsExtensions { + canonicalFileName := tspath.GetCanonicalFileName(fileName, p.host.FS().UseCaseSensitiveFileNames()) + supported := false + for _, group := range supportedExtensions { + if tspath.FileExtensionIsOneOf(canonicalFileName, group) { + supported = true + break + } + } + if !supported { + return nil // unsupported extensions are forced to fail + } + } + + return p.GetSourceFile(fileName) + } + if allowNonTsExtensions { + extensionless := p.GetSourceFile(fileName) + if extensionless != nil { + return extensionless + } + } + + // Only try adding extensions from the first supported group (which should be .ts/.tsx/.d.ts) + for _, ext := range supportedExtensions[0] { + result := p.GetSourceFile(fileName + ext) + if result != nil { + return result + } + } + return nil +} + func NewProgram(options ProgramOptions) *Program { p := &Program{} p.programOptions = options @@ -294,6 +338,10 @@ func (p *Program) GetGlobalDiagnostics() []*ast.Diagnostic { return SortAndDeduplicateDiagnostics(globalDiagnostics) } +func (p *Program) GetDeclarationDiagnostics(sourceFile *ast.SourceFile) []*ast.Diagnostic { + return p.getDiagnosticsHelper(sourceFile, true /*ensureBound*/, true /*ensureChecked*/, p.getDeclarationDiagnosticsForFile) +} + func (p *Program) GetOptionsDiagnostics() []*ast.Diagnostic { return SortAndDeduplicateDiagnostics(append(p.GetGlobalDiagnostics(), p.getOptionsDiagnosticsOfConfigFile()...)) } @@ -380,6 +428,14 @@ func (p *Program) getSemanticDiagnosticsForFile(sourceFile *ast.SourceFile) []*a return filtered } +func (p *Program) getDeclarationDiagnosticsForFile(sourceFile *ast.SourceFile) []*ast.Diagnostic { + if sourceFile.IsDeclarationFile { + return []*ast.Diagnostic{} + } + host := &emitHost{program: p} + return getDeclarationDiagnostics(host, host.GetEmitResolver(sourceFile, true), sourceFile) +} + func isCommentOrBlankLine(text string, pos int) bool { for pos < len(text) && (text[pos] == ' ' || text[pos] == '\t') { pos++ diff --git a/internal/core/compileroptions.go b/internal/core/compileroptions.go index b35bf50f09..ec606be70d 100644 --- a/internal/core/compileroptions.go +++ b/internal/core/compileroptions.go @@ -252,8 +252,7 @@ func (options *CompilerOptions) GetEmitStandardClassFields() bool { } func (options *CompilerOptions) GetEmitDeclarations() bool { - // !!! - return false + return options.Declaration.IsTrue() || options.Composite.IsTrue() } func (options *CompilerOptions) GetAreDeclarationMapsEnabled() bool { diff --git a/internal/declarations/diagnostics.go b/internal/declarations/diagnostics.go new file mode 100644 index 0000000000..0e3bad19e9 --- /dev/null +++ b/internal/declarations/diagnostics.go @@ -0,0 +1,466 @@ +package declarations + +import ( + "fmt" + + "github.com/microsoft/typescript-go/internal/ast" + "github.com/microsoft/typescript-go/internal/compiler/diagnostics" + "github.com/microsoft/typescript-go/internal/printer" +) + +type GetSymbolAccessibilityDiagnostic = func(symbolAccessibilityResult printer.SymbolAccessibilityResult) *SymbolAccessibilityDiagnostic + +type SymbolAccessibilityDiagnostic struct { + errorNode *ast.Node + diagnosticMessage *diagnostics.Message + typeName *ast.Node +} + +func wrapSimpleDiagnosticSelector(node *ast.Node, selector func(node *ast.Node, symbolAccessibilityResult printer.SymbolAccessibilityResult) *diagnostics.Message) GetSymbolAccessibilityDiagnostic { + return func(symbolAccessibilityResult printer.SymbolAccessibilityResult) *SymbolAccessibilityDiagnostic { + diagnosticMessage := selector(node, symbolAccessibilityResult) + if diagnosticMessage == nil { + return nil + } + return &SymbolAccessibilityDiagnostic{ + errorNode: node, + diagnosticMessage: diagnosticMessage, + typeName: ast.GetNameOfDeclaration(node), + } + } +} + +func wrapNamedDiagnosticSelector(node *ast.Node, selector func(node *ast.Node, symbolAccessibilityResult printer.SymbolAccessibilityResult) *diagnostics.Message) GetSymbolAccessibilityDiagnostic { + return func(symbolAccessibilityResult printer.SymbolAccessibilityResult) *SymbolAccessibilityDiagnostic { + diagnosticMessage := selector(node, symbolAccessibilityResult) + if diagnosticMessage == nil { + return nil + } + name := ast.GetNameOfDeclaration(node) + return &SymbolAccessibilityDiagnostic{ + errorNode: name, + diagnosticMessage: diagnosticMessage, + typeName: name, + } + } +} + +func wrapFallbackErrorDiagnosticSelector(node *ast.Node, selector func(node *ast.Node, symbolAccessibilityResult printer.SymbolAccessibilityResult) *diagnostics.Message) GetSymbolAccessibilityDiagnostic { + return func(symbolAccessibilityResult printer.SymbolAccessibilityResult) *SymbolAccessibilityDiagnostic { + diagnosticMessage := selector(node, symbolAccessibilityResult) + if diagnosticMessage == nil { + return nil + } + errorNode := ast.GetNameOfDeclaration(node) + if errorNode == nil { + errorNode = node + } + return &SymbolAccessibilityDiagnostic{ + errorNode: errorNode, + diagnosticMessage: diagnosticMessage, + } + } +} + +func selectDiagnosticBasedOnModuleName(symbolAccessibilityResult printer.SymbolAccessibilityResult, moduleNotNameable *diagnostics.Message, privateModule *diagnostics.Message, nonModule *diagnostics.Message) *diagnostics.Message { + if len(symbolAccessibilityResult.ErrorModuleName) > 0 { + if symbolAccessibilityResult.Accessibility == printer.SymbolAccessibilityCannotBeNamed { + return moduleNotNameable + } + return privateModule + } + return nonModule +} + +func selectDiagnosticBasedOnModuleNameNoNameCheck(symbolAccessibilityResult printer.SymbolAccessibilityResult, privateModule *diagnostics.Message, nonModule *diagnostics.Message) *diagnostics.Message { + if len(symbolAccessibilityResult.ErrorModuleName) > 0 { + return privateModule + } + return nonModule +} + +func createGetSymbolAccessibilityDiagnosticForNodeName(node *ast.Node) GetSymbolAccessibilityDiagnostic { + if ast.IsSetAccessorDeclaration(node) || ast.IsGetAccessorDeclaration(node) { + return wrapSimpleDiagnosticSelector(node, getAccessorNameVisibilityDiagnosticMessage) + } else if ast.IsMethodDeclaration(node) || ast.IsMethodSignatureDeclaration(node) { + return wrapSimpleDiagnosticSelector(node, getMethodNameVisibilityDiagnosticMessage) + } else { + return createGetSymbolAccessibilityDiagnosticForNode(node) + } +} + +func getAccessorNameVisibilityDiagnosticMessage(node *ast.Node, symbolAccessibilityResult printer.SymbolAccessibilityResult) *diagnostics.Message { + if ast.IsStatic(node) { + return selectDiagnosticBasedOnModuleName( + symbolAccessibilityResult, + diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named, + diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_name_1_from_private_module_2, + diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_private_name_1, + ) + } else if node.Parent.Kind == ast.KindClassDeclaration { + return selectDiagnosticBasedOnModuleName( + symbolAccessibilityResult, + diagnostics.Public_property_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named, + diagnostics.Public_property_0_of_exported_class_has_or_is_using_name_1_from_private_module_2, + diagnostics.Public_property_0_of_exported_class_has_or_is_using_private_name_1, + ) + } else { + return selectDiagnosticBasedOnModuleNameNoNameCheck( + symbolAccessibilityResult, + diagnostics.Property_0_of_exported_interface_has_or_is_using_name_1_from_private_module_2, + diagnostics.Property_0_of_exported_interface_has_or_is_using_private_name_1, + ) + } +} + +func getMethodNameVisibilityDiagnosticMessage(node *ast.Node, symbolAccessibilityResult printer.SymbolAccessibilityResult) *diagnostics.Message { + if ast.IsStatic(node) { + return selectDiagnosticBasedOnModuleName( + symbolAccessibilityResult, + diagnostics.Public_static_method_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named, + diagnostics.Public_static_method_0_of_exported_class_has_or_is_using_name_1_from_private_module_2, + diagnostics.Public_static_method_0_of_exported_class_has_or_is_using_private_name_1, + ) + } else if node.Parent.Kind == ast.KindClassDeclaration { + return selectDiagnosticBasedOnModuleName( + symbolAccessibilityResult, + diagnostics.Public_method_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named, + diagnostics.Public_method_0_of_exported_class_has_or_is_using_name_1_from_private_module_2, + diagnostics.Public_method_0_of_exported_class_has_or_is_using_private_name_1, + ) + } else { + return selectDiagnosticBasedOnModuleNameNoNameCheck( + symbolAccessibilityResult, + diagnostics.Method_0_of_exported_interface_has_or_is_using_name_1_from_private_module_2, + diagnostics.Method_0_of_exported_interface_has_or_is_using_private_name_1, + ) + } +} + +func createGetSymbolAccessibilityDiagnosticForNode(node *ast.Node) GetSymbolAccessibilityDiagnostic { + if ast.IsVariableDeclaration(node) || ast.IsPropertyDeclaration(node) || ast.IsPropertySignatureDeclaration(node) || ast.IsPropertyAccessExpression(node) || ast.IsElementAccessExpression(node) || ast.IsBinaryExpression(node) || ast.IsBindingElement(node) || ast.IsConstructorDeclaration(node) { + return wrapSimpleDiagnosticSelector(node, getVariableDeclarationTypeVisibilityDiagnosticMessage) + } else if ast.IsSetAccessorDeclaration(node) || ast.IsGetAccessorDeclaration(node) { + return wrapNamedDiagnosticSelector(node, getAccessorDeclarationTypeVisibilityDiagnosticMessage) + } else if ast.IsConstructSignatureDeclaration(node) || ast.IsCallSignatureDeclaration(node) || ast.IsMethodDeclaration(node) || ast.IsMethodSignatureDeclaration(node) || ast.IsFunctionDeclaration(node) || ast.IsIndexSignatureDeclaration(node) { + return wrapFallbackErrorDiagnosticSelector(node, getReturnTypeVisibilityDiagnosticMessage) + } else if ast.IsParameter(node) { + if ast.IsParameterPropertyDeclaration(node, node.Parent) && ast.HasSyntacticModifier(node.Parent, ast.ModifierFlagsPrivate) { + return wrapSimpleDiagnosticSelector(node, getVariableDeclarationTypeVisibilityDiagnosticMessage) + } + return wrapSimpleDiagnosticSelector(node, getParameterDeclarationTypeVisibilityDiagnosticMessage) + } else if ast.IsTypeParameterDeclaration(node) { + return wrapSimpleDiagnosticSelector(node, getTypeParameterConstraintVisibilityDiagnosticMessage) + } else if ast.IsExpressionWithTypeArguments(node) { + // unique node selection behavior, inline closure + return func(symbolAccessibilityResult printer.SymbolAccessibilityResult) *SymbolAccessibilityDiagnostic { + var diagnosticMessage *diagnostics.Message + // Heritage clause is written by user so it can always be named + if ast.IsClassDeclaration(node.Parent.Parent) { + // Class or Interface implemented/extended is inaccessible + if ast.IsHeritageClause(node.Parent) && node.Parent.AsHeritageClause().Token == ast.KindImplementsKeyword { + diagnosticMessage = diagnostics.Implements_clause_of_exported_class_0_has_or_is_using_private_name_1 + } else { + if node.Parent.Parent.Name() != nil { + diagnosticMessage = diagnostics.X_extends_clause_of_exported_class_0_has_or_is_using_private_name_1 + } else { + diagnosticMessage = diagnostics.X_extends_clause_of_exported_class_has_or_is_using_private_name_0 + } + } + } else { + // interface is inaccessible + diagnosticMessage = diagnostics.X_extends_clause_of_exported_interface_0_has_or_is_using_private_name_1 + } + + return &SymbolAccessibilityDiagnostic{ + diagnosticMessage: diagnosticMessage, + errorNode: node, + typeName: ast.GetNameOfDeclaration(node.Parent.Parent), + } + } + } else if ast.IsImportEqualsDeclaration(node) { + return wrapSimpleDiagnosticSelector(node, func(_ *ast.Node, _ printer.SymbolAccessibilityResult) *diagnostics.Message { + return diagnostics.Import_declaration_0_is_using_private_name_1 + }) + } else if ast.IsTypeAliasDeclaration(node) /*|| ast.IsJSDocTypeAlias(node)*/ { // !!! JSDoc support + // unique node selection behavior, inline closure + return func(symbolAccessibilityResult printer.SymbolAccessibilityResult) *SymbolAccessibilityDiagnostic { + diagnosticMessage := selectDiagnosticBasedOnModuleNameNoNameCheck( + symbolAccessibilityResult, + diagnostics.Exported_type_alias_0_has_or_is_using_private_name_1_from_module_2, + diagnostics.Exported_type_alias_0_has_or_is_using_private_name_1, + ) + var errorNode *ast.Node + // !!! JSDoc Support + ////if ast.IsJSDocTypeAlias(node) { + //// errorNode = node.AsJSDocTypedefTag().TypeExpression + ////} else { + errorNode = node.AsTypeAliasDeclaration().Type + ////} + var typeName *ast.Node + // !!! JSDoc Support + ////if ast.IsJSDocTypeAlias(node) { + //// errorNode = getNameOfDeclaration(node) + ////} else { + typeName = node.Name() + ////} + return &SymbolAccessibilityDiagnostic{ + errorNode: errorNode, + diagnosticMessage: diagnosticMessage, + typeName: typeName, + } + } + } else { + panic(fmt.Sprintf("Attempted to set a declaration diagnostic context for unhandled node kind: %d", node.Kind)) + } +} + +func getVariableDeclarationTypeVisibilityDiagnosticMessage(node *ast.Node, symbolAccessibilityResult printer.SymbolAccessibilityResult) *diagnostics.Message { + if node.Kind == ast.KindVariableDeclaration || node.Kind == ast.KindBindingElement { + return selectDiagnosticBasedOnModuleName( + symbolAccessibilityResult, + diagnostics.Exported_variable_0_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named, + diagnostics.Exported_variable_0_has_or_is_using_name_1_from_private_module_2, + diagnostics.Exported_variable_0_has_or_is_using_private_name_1, + ) + + // This check is to ensure we don't report error on constructor parameter property as that error would be reported during parameter emit + // The only exception here is if the constructor was marked as private. we are not emitting the constructor parameters at all. + } else if node.Kind == ast.KindPropertyDeclaration || node.Kind == ast.KindPropertyAccessExpression || node.Kind == ast.KindElementAccessExpression || node.Kind == ast.KindBinaryExpression || node.Kind == ast.KindPropertySignature || + (node.Kind == ast.KindParameter && ast.HasSyntacticModifier(node.Parent, ast.ModifierFlagsPrivate)) { + // TODO(jfreeman): Deal with computed properties in error reporting. + if ast.IsStatic(node) { + return selectDiagnosticBasedOnModuleName( + symbolAccessibilityResult, + diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named, + diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_name_1_from_private_module_2, + diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_private_name_1, + ) + } else if node.Parent.Kind == ast.KindClassDeclaration || node.Kind == ast.KindParameter { + return selectDiagnosticBasedOnModuleName( + symbolAccessibilityResult, + diagnostics.Public_property_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named, + diagnostics.Public_property_0_of_exported_class_has_or_is_using_name_1_from_private_module_2, + diagnostics.Public_property_0_of_exported_class_has_or_is_using_private_name_1, + ) + } else { + // Interfaces cannot have types that cannot be named + return selectDiagnosticBasedOnModuleNameNoNameCheck( + symbolAccessibilityResult, + diagnostics.Property_0_of_exported_interface_has_or_is_using_name_1_from_private_module_2, + diagnostics.Property_0_of_exported_interface_has_or_is_using_private_name_1, + ) + } + } + return nil // TODO: Audit behavior - should this panic? potentially silent error state in strada +} + +func getAccessorDeclarationTypeVisibilityDiagnosticMessage(node *ast.Node, symbolAccessibilityResult printer.SymbolAccessibilityResult) *diagnostics.Message { + if node.Kind == ast.KindSetAccessor { + // Getters can infer the return type from the returned expression, but setters cannot, so the + // "_from_external_module_1_but_cannot_be_named" case cannot occur. + if ast.IsStatic(node) { + return selectDiagnosticBasedOnModuleNameNoNameCheck( + symbolAccessibilityResult, + diagnostics.Parameter_type_of_public_static_setter_0_from_exported_class_has_or_is_using_name_1_from_private_module_2, + diagnostics.Parameter_type_of_public_static_setter_0_from_exported_class_has_or_is_using_private_name_1, + ) + } else { + return selectDiagnosticBasedOnModuleNameNoNameCheck( + symbolAccessibilityResult, + diagnostics.Parameter_type_of_public_setter_0_from_exported_class_has_or_is_using_name_1_from_private_module_2, + diagnostics.Parameter_type_of_public_setter_0_from_exported_class_has_or_is_using_private_name_1, + ) + } + } else { + if ast.IsStatic(node) { + return selectDiagnosticBasedOnModuleName( + symbolAccessibilityResult, + diagnostics.Return_type_of_public_static_getter_0_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named, + diagnostics.Return_type_of_public_static_getter_0_from_exported_class_has_or_is_using_name_1_from_private_module_2, + diagnostics.Return_type_of_public_static_getter_0_from_exported_class_has_or_is_using_private_name_1, + ) + + } else { + return selectDiagnosticBasedOnModuleName( + symbolAccessibilityResult, + diagnostics.Return_type_of_public_getter_0_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named, + diagnostics.Return_type_of_public_getter_0_from_exported_class_has_or_is_using_name_1_from_private_module_2, + diagnostics.Return_type_of_public_getter_0_from_exported_class_has_or_is_using_private_name_1, + ) + } + } +} + +func getReturnTypeVisibilityDiagnosticMessage(node *ast.Node, symbolAccessibilityResult printer.SymbolAccessibilityResult) *diagnostics.Message { + switch node.Kind { + case ast.KindConstructSignature: + // Interfaces cannot have return types that cannot be named + return selectDiagnosticBasedOnModuleNameNoNameCheck( + symbolAccessibilityResult, + diagnostics.Return_type_of_constructor_signature_from_exported_interface_has_or_is_using_name_0_from_private_module_1, + diagnostics.Return_type_of_constructor_signature_from_exported_interface_has_or_is_using_private_name_0, + ) + case ast.KindCallSignature: + // Interfaces cannot have return types that cannot be named + return selectDiagnosticBasedOnModuleNameNoNameCheck( + symbolAccessibilityResult, + diagnostics.Return_type_of_call_signature_from_exported_interface_has_or_is_using_name_0_from_private_module_1, + diagnostics.Return_type_of_call_signature_from_exported_interface_has_or_is_using_private_name_0, + ) + case ast.KindIndexSignature: + // Interfaces cannot have return types that cannot be named + return selectDiagnosticBasedOnModuleNameNoNameCheck( + symbolAccessibilityResult, + diagnostics.Return_type_of_index_signature_from_exported_interface_has_or_is_using_name_0_from_private_module_1, + diagnostics.Return_type_of_index_signature_from_exported_interface_has_or_is_using_private_name_0, + ) + + case ast.KindMethodDeclaration, ast.KindMethodSignature: + if ast.IsStatic(node) { + return selectDiagnosticBasedOnModuleName( + symbolAccessibilityResult, + diagnostics.Return_type_of_public_static_method_from_exported_class_has_or_is_using_name_0_from_external_module_1_but_cannot_be_named, + diagnostics.Return_type_of_public_static_method_from_exported_class_has_or_is_using_name_0_from_private_module_1, + diagnostics.Return_type_of_public_static_method_from_exported_class_has_or_is_using_private_name_0, + ) + } else if node.Parent.Kind == ast.KindClassDeclaration { + return selectDiagnosticBasedOnModuleName( + symbolAccessibilityResult, + diagnostics.Return_type_of_public_method_from_exported_class_has_or_is_using_name_0_from_external_module_1_but_cannot_be_named, + diagnostics.Return_type_of_public_method_from_exported_class_has_or_is_using_name_0_from_private_module_1, + diagnostics.Return_type_of_public_method_from_exported_class_has_or_is_using_private_name_0, + ) + } else { + // Interfaces cannot have return types that cannot be named + return selectDiagnosticBasedOnModuleNameNoNameCheck( + symbolAccessibilityResult, + diagnostics.Return_type_of_method_from_exported_interface_has_or_is_using_name_0_from_private_module_1, + diagnostics.Return_type_of_method_from_exported_interface_has_or_is_using_private_name_0, + ) + } + case ast.KindFunctionDeclaration: + return selectDiagnosticBasedOnModuleName( + symbolAccessibilityResult, + diagnostics.Return_type_of_exported_function_has_or_is_using_name_0_from_external_module_1_but_cannot_be_named, + diagnostics.Return_type_of_exported_function_has_or_is_using_name_0_from_private_module_1, + diagnostics.Return_type_of_exported_function_has_or_is_using_private_name_0, + ) + default: + panic(fmt.Sprintf("This is unknown kind for signature: %d", node.Kind)) + } +} + +func getParameterDeclarationTypeVisibilityDiagnosticMessage(node *ast.Node, symbolAccessibilityResult printer.SymbolAccessibilityResult) *diagnostics.Message { + switch node.Parent.Kind { + case ast.KindConstructor: + return selectDiagnosticBasedOnModuleName( + symbolAccessibilityResult, + diagnostics.Parameter_0_of_constructor_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named, + diagnostics.Parameter_0_of_constructor_from_exported_class_has_or_is_using_name_1_from_private_module_2, + diagnostics.Parameter_0_of_constructor_from_exported_class_has_or_is_using_private_name_1, + ) + + case ast.KindConstructSignature, ast.KindConstructorType: + // Interfaces cannot have parameter types that cannot be named + return selectDiagnosticBasedOnModuleNameNoNameCheck( + symbolAccessibilityResult, + diagnostics.Parameter_0_of_constructor_signature_from_exported_interface_has_or_is_using_name_1_from_private_module_2, + diagnostics.Parameter_0_of_constructor_signature_from_exported_interface_has_or_is_using_private_name_1, + ) + + case ast.KindCallSignature: + // Interfaces cannot have parameter types that cannot be named + return selectDiagnosticBasedOnModuleNameNoNameCheck( + symbolAccessibilityResult, + diagnostics.Parameter_0_of_call_signature_from_exported_interface_has_or_is_using_name_1_from_private_module_2, + diagnostics.Parameter_0_of_call_signature_from_exported_interface_has_or_is_using_private_name_1, + ) + + case ast.KindIndexSignature: + // Interfaces cannot have parameter types that cannot be named + return selectDiagnosticBasedOnModuleNameNoNameCheck( + symbolAccessibilityResult, + diagnostics.Parameter_0_of_index_signature_from_exported_interface_has_or_is_using_name_1_from_private_module_2, + diagnostics.Parameter_0_of_index_signature_from_exported_interface_has_or_is_using_private_name_1, + ) + + case ast.KindMethodDeclaration, ast.KindMethodSignature: + if ast.IsStatic(node.Parent) { + return selectDiagnosticBasedOnModuleName( + symbolAccessibilityResult, + diagnostics.Parameter_0_of_public_static_method_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named, + diagnostics.Parameter_0_of_public_static_method_from_exported_class_has_or_is_using_name_1_from_private_module_2, + diagnostics.Parameter_0_of_public_static_method_from_exported_class_has_or_is_using_private_name_1, + ) + } else if node.Parent.Parent.Kind == ast.KindClassDeclaration { + return selectDiagnosticBasedOnModuleName( + symbolAccessibilityResult, + diagnostics.Parameter_0_of_public_method_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named, + diagnostics.Parameter_0_of_public_method_from_exported_class_has_or_is_using_name_1_from_private_module_2, + diagnostics.Parameter_0_of_public_method_from_exported_class_has_or_is_using_private_name_1, + ) + } else { + // Interfaces cannot have parameter types that cannot be named + return selectDiagnosticBasedOnModuleNameNoNameCheck( + symbolAccessibilityResult, + diagnostics.Parameter_0_of_method_from_exported_interface_has_or_is_using_name_1_from_private_module_2, + diagnostics.Parameter_0_of_method_from_exported_interface_has_or_is_using_private_name_1, + ) + } + + case ast.KindFunctionDeclaration, ast.KindFunctionType: + return selectDiagnosticBasedOnModuleName( + symbolAccessibilityResult, + diagnostics.Parameter_0_of_exported_function_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named, + diagnostics.Parameter_0_of_exported_function_has_or_is_using_name_1_from_private_module_2, + diagnostics.Parameter_0_of_exported_function_has_or_is_using_private_name_1, + ) + case ast.KindSetAccessor, ast.KindGetAccessor: + return selectDiagnosticBasedOnModuleName( + symbolAccessibilityResult, + diagnostics.Parameter_0_of_accessor_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named, + diagnostics.Parameter_0_of_accessor_has_or_is_using_name_1_from_private_module_2, + diagnostics.Parameter_0_of_accessor_has_or_is_using_private_name_1, + ) + default: + panic(fmt.Sprintf("Unknown parent for parameter: %d", node.Parent.Kind)) + } +} + +func getTypeParameterConstraintVisibilityDiagnosticMessage(node *ast.Node, symbolAccessibilityResult printer.SymbolAccessibilityResult) *diagnostics.Message { + // Type parameter constraints are named by user so we should always be able to name it + switch node.Parent.Kind { + case ast.KindClassDeclaration: + return diagnostics.Type_parameter_0_of_exported_class_has_or_is_using_private_name_1 + case ast.KindInterfaceDeclaration: + return diagnostics.Type_parameter_0_of_exported_interface_has_or_is_using_private_name_1 + case ast.KindMappedType: + return diagnostics.Type_parameter_0_of_exported_mapped_object_type_is_using_private_name_1 + case ast.KindConstructorType, ast.KindConstructSignature: + return diagnostics.Type_parameter_0_of_constructor_signature_from_exported_interface_has_or_is_using_private_name_1 + case ast.KindCallSignature: + return diagnostics.Type_parameter_0_of_call_signature_from_exported_interface_has_or_is_using_private_name_1 + case ast.KindMethodDeclaration, ast.KindMethodSignature: + if ast.IsStatic(node.Parent) { + return diagnostics.Type_parameter_0_of_public_static_method_from_exported_class_has_or_is_using_private_name_1 + } else if node.Parent.Parent.Kind == ast.KindClassDeclaration { + return diagnostics.Type_parameter_0_of_public_method_from_exported_class_has_or_is_using_private_name_1 + } else { + return diagnostics.Type_parameter_0_of_method_from_exported_interface_has_or_is_using_private_name_1 + } + case ast.KindFunctionType, ast.KindFunctionDeclaration: + return diagnostics.Type_parameter_0_of_exported_function_has_or_is_using_private_name_1 + + case ast.KindInferType: + return diagnostics.Extends_clause_for_inferred_type_0_has_or_is_using_private_name_1 + + case ast.KindTypeAliasDeclaration: + return diagnostics.Type_parameter_0_of_exported_type_alias_has_or_is_using_private_name_1 + + default: + panic(fmt.Sprintf("This is unknown parent for type parameter: %d", node.Parent.Kind)) + } +} + +// !!! TODO isolatedDeclarations createGetIsolatedDeclarationErrors diff --git a/internal/declarations/tracker.go b/internal/declarations/tracker.go new file mode 100644 index 0000000000..1777980ce4 --- /dev/null +++ b/internal/declarations/tracker.go @@ -0,0 +1,222 @@ +package declarations + +import ( + "github.com/microsoft/typescript-go/internal/ast" + "github.com/microsoft/typescript-go/internal/checker" + "github.com/microsoft/typescript-go/internal/compiler/diagnostics" + "github.com/microsoft/typescript-go/internal/core" + "github.com/microsoft/typescript-go/internal/nodebuilder" + "github.com/microsoft/typescript-go/internal/printer" + "github.com/microsoft/typescript-go/internal/scanner" +) + +type SymbolTrackerImpl struct { + resolver printer.EmitResolver + state *SymbolTrackerSharedState + fallbackStack []*ast.Node +} + +// GetModuleSpecifierGenerationHost implements checker.SymbolTracker. +func (s *SymbolTrackerImpl) GetModuleSpecifierGenerationHost() any { + return nil // !!! +} + +func (s *SymbolTrackerImpl) GetInnerSymbolTracker() nodebuilder.SymbolTracker { + return nil +} + +// 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(resolver printer.EmitResolver, state *SymbolTrackerSharedState) *SymbolTrackerImpl { + tracker := &SymbolTrackerImpl{resolver: resolver, state: state} + return tracker +} diff --git a/internal/declarations/transform.go b/internal/declarations/transform.go new file mode 100644 index 0000000000..f3e82aade6 --- /dev/null +++ b/internal/declarations/transform.go @@ -0,0 +1,1689 @@ +package declarations + +import ( + "fmt" + + "github.com/microsoft/typescript-go/internal/ast" + "github.com/microsoft/typescript-go/internal/compiler/diagnostics" + "github.com/microsoft/typescript-go/internal/core" + "github.com/microsoft/typescript-go/internal/jsnum" + "github.com/microsoft/typescript-go/internal/nodebuilder" + "github.com/microsoft/typescript-go/internal/printer" + "github.com/microsoft/typescript-go/internal/scanner" + "github.com/microsoft/typescript-go/internal/transformers" + "github.com/microsoft/typescript-go/internal/tspath" +) + +type ReferencedFilePair struct { + file *ast.SourceFile + ref *ast.FileReference +} + +type OutputPaths interface { + DeclarationFilePath() string + JsFilePath() string +} + +// Used to be passed in the TransformationContext, which is now just an EmitContext +type DeclarationEmitHost interface { + GetCurrentDirectory() string + UseCaseSensitiveFileNames() bool + GetSourceFileFromReference(origin *ast.SourceFile, ref *ast.FileReference) *ast.SourceFile + + GetOutputPathsFor(file *ast.SourceFile, forceDtsPaths bool) OutputPaths + GetResolutionModeOverride(node *ast.Node) core.ResolutionMode + GetEffectiveDeclarationFlags(node *ast.Node, flags ast.ModifierFlags) ast.ModifierFlags +} + +type DeclarationTransformer struct { + transformers.Transformer + host DeclarationEmitHost + compilerOptions *core.CompilerOptions + tracker *SymbolTrackerImpl + state *SymbolTrackerSharedState + resolver printer.EmitResolver + declarationFilePath string + declarationMapPath string + + isBundledEmit bool + needsDeclare bool + needsScopeFixMarker bool + resultHasScopeMarker bool + enclosingDeclaration *ast.Node + resultHasExternalModuleIndicator bool + suppressNewDiagnosticContexts bool + lateStatementReplacementMap map[ast.NodeId]*ast.Node + rawReferencedFiles []ReferencedFilePair + rawTypeReferenceDirectives []*ast.FileReference + rawLibReferenceDirectives []*ast.FileReference +} + +func NewDeclarationTransformer(host DeclarationEmitHost, resolver printer.EmitResolver, context *printer.EmitContext, compilerOptions *core.CompilerOptions, declarationFilePath string, declarationMapPath string) *DeclarationTransformer { + state := &SymbolTrackerSharedState{isolatedDeclarations: compilerOptions.IsolatedDeclarations.IsTrue(), resolver: resolver} + tracker := NewSymbolTracker(resolver, state) + // TODO: Use new host GetOutputPathsFor method instead of passing in entrypoint paths (which will also better support bundled emit) + tx := &DeclarationTransformer{ + host: host, + compilerOptions: compilerOptions, + tracker: tracker, + state: state, + resolver: resolver, + declarationFilePath: declarationFilePath, + declarationMapPath: declarationMapPath, + } + tx.NewTransformer(tx.visit, context) + return tx +} + +func (tx *DeclarationTransformer) GetDiagnostics() []*ast.Diagnostic { + return tx.state.diagnostics +} + +const declarationEmitNodeBuilderFlags = nodebuilder.FlagsMultilineObjectLiterals | + nodebuilder.FlagsWriteClassExpressionAsTypeLiteral | + nodebuilder.FlagsUseTypeOfFunction | + nodebuilder.FlagsUseStructuralFallback | + nodebuilder.FlagsAllowEmptyTuple | + nodebuilder.FlagsGenerateNamesForShadowedTypeParams | + nodebuilder.FlagsNoTruncation + +const declarationEmitInternalNodeBuilderFlags = nodebuilder.InternalFlagsAllowUnresolvedNames + +// functions as both `visitDeclarationStatements` and `transformRoot`, utilitzing SyntaxList nodes +func (tx *DeclarationTransformer) visit(node *ast.Node) *ast.Node { + if node == nil { + return nil + } + // !!! TODO: Bundle support? + switch node.Kind { + case ast.KindSourceFile: + return tx.visitSourceFile(node.AsSourceFile()) + // statements we keep but do something to + case ast.KindFunctionDeclaration, + ast.KindModuleDeclaration, + ast.KindImportEqualsDeclaration, + ast.KindInterfaceDeclaration, + ast.KindClassDeclaration, + ast.KindTypeAliasDeclaration, + ast.KindEnumDeclaration, + ast.KindVariableStatement, + ast.KindImportDeclaration, + ast.KindExportDeclaration, + ast.KindExportAssignment: + return tx.visitDeclarationStatements(node) + // statements we elide + case ast.KindBreakStatement, + ast.KindContinueStatement, + ast.KindDebuggerStatement, + ast.KindDoStatement, + ast.KindExpressionStatement, + ast.KindEmptyStatement, + ast.KindForInStatement, + ast.KindForOfStatement, + ast.KindForStatement, + ast.KindIfStatement, + ast.KindLabeledStatement, + ast.KindReturnStatement, + ast.KindSwitchStatement, + ast.KindThrowStatement, + ast.KindTryStatement, + ast.KindWhileStatement, + ast.KindWithStatement, + ast.KindNotEmittedStatement, + ast.KindBlock, + ast.KindMissingDeclaration, + ast.KindJSTypeAliasDeclaration: // !!! TODO: Jsdoc support + return nil + // parts of things, things we just visit children of + default: + return tx.visitDeclarationSubtree(node) + } +} + +func throwDiagnostic(result printer.SymbolAccessibilityResult) *SymbolAccessibilityDiagnostic { + panic("Diagnostic emitted without context") +} + +func (tx *DeclarationTransformer) visitSourceFile(node *ast.SourceFile) *ast.Node { + if node.IsDeclarationFile { + return node.AsNode() + } + + tx.isBundledEmit = false + tx.needsDeclare = true + tx.needsScopeFixMarker = false + tx.resultHasScopeMarker = false + tx.enclosingDeclaration = node.AsNode() + tx.state.getSymbolAccessibilityDiagnostic = throwDiagnostic + tx.resultHasExternalModuleIndicator = false + tx.suppressNewDiagnosticContexts = false + tx.state.lateMarkedStatements = make([]*ast.Node, 0) + tx.lateStatementReplacementMap = make(map[ast.NodeId]*ast.Node) + tx.rawReferencedFiles = make([]ReferencedFilePair, 0) + tx.rawTypeReferenceDirectives = make([]*ast.FileReference, 0) + tx.rawLibReferenceDirectives = make([]*ast.FileReference, 0) + tx.state.currentSourceFile = node + tx.collectFileReferences(node) + updated := tx.transformSourceFile(node) + tx.state.currentSourceFile = nil + return updated +} + +func (tx *DeclarationTransformer) collectFileReferences(sourceFile *ast.SourceFile) { + tx.rawReferencedFiles = append(tx.rawReferencedFiles, core.Map(sourceFile.ReferencedFiles, func(ref *ast.FileReference) ReferencedFilePair { return ReferencedFilePair{file: sourceFile, ref: ref} })...) + tx.rawTypeReferenceDirectives = append(tx.rawTypeReferenceDirectives, sourceFile.TypeReferenceDirectives...) + tx.rawLibReferenceDirectives = append(tx.rawLibReferenceDirectives, sourceFile.LibReferenceDirectives...) +} + +func (tx *DeclarationTransformer) transformSourceFile(node *ast.SourceFile) *ast.Node { + var combinedStatements *ast.StatementList + if ast.IsSourceFileJS(node) { + // !!! TODO: JS declaration emit support + combinedStatements = tx.Factory().NewNodeList([]*ast.Node{}) + } else { + statements := tx.Visitor().VisitNodes(node.Statements) + combinedStatements = tx.transformAndReplaceLatePaintedStatements(statements) + combinedStatements.Loc = statements.Loc // setTextRange + if ast.IsExternalModule(node) && (!tx.resultHasExternalModuleIndicator || (tx.needsScopeFixMarker && !tx.resultHasScopeMarker)) { + marker := createEmptyExports(tx.Factory()) + newList := append(combinedStatements.Nodes, marker) + withMarker := tx.Factory().NewNodeList(newList) + withMarker.Loc = combinedStatements.Loc + combinedStatements = withMarker + } + } + outputFilePath := tspath.GetDirectoryPath(tspath.NormalizeSlashes(tx.declarationFilePath)) + result := tx.Factory().UpdateSourceFile(node, combinedStatements) + result.AsSourceFile().LibReferenceDirectives = tx.getLibReferences() + result.AsSourceFile().TypeReferenceDirectives = tx.getTypeReferences() + result.AsSourceFile().HasNoDefaultLib = node.HasNoDefaultLib + result.AsSourceFile().IsDeclarationFile = true + result.AsSourceFile().ReferencedFiles = tx.getReferencedFiles(outputFilePath) + return result.AsNode() +} + +func createEmptyExports(factory *ast.NodeFactory) *ast.Node { + return factory.NewExportDeclaration(nil /*isTypeOnly*/, false, factory.NewNamedExports(factory.NewNodeList([]*ast.Node{})), nil, nil) +} + +func (tx *DeclarationTransformer) transformAndReplaceLatePaintedStatements(statements *ast.StatementList) *ast.StatementList { + // This is a `while` loop because `handleSymbolAccessibilityError` can see additional import aliases marked as visible during + // error handling which must now be included in the output and themselves checked for errors. + // For example: + // ``` + // module A { + // export module Q {} + // import B = Q; + // import C = B; + // export import D = C; + // } + // ``` + // In such a scenario, only Q and D are initially visible, but we don't consider imports as private names - instead we say they if they are referenced they must + // be recorded. So while checking D's visibility we mark C as visible, then we must check C which in turn marks B, completing the chain of + // dependent imports and allowing a valid declaration file output. Today, this dependent alias marking only happens for internal import aliases. + for true { + if len(tx.state.lateMarkedStatements) == 0 { + break + } + + next := tx.state.lateMarkedStatements[0] + tx.state.lateMarkedStatements = tx.state.lateMarkedStatements[1:] + + priorNeedsDeclare := tx.needsDeclare + tx.needsDeclare = next.Parent != nil && ast.IsSourceFile(next.Parent) && !(ast.IsExternalModule(next.Parent.AsSourceFile()) && tx.isBundledEmit) + + result := tx.transformTopLevelDeclaration(next) + + tx.needsDeclare = priorNeedsDeclare + original := tx.EmitContext().MostOriginal(next) + id := ast.GetNodeId(original) + tx.lateStatementReplacementMap[id] = result + } + + // And lastly, we need to get the final form of all those indetermine import declarations from before and add them to the output list + // (and remove them from the set to examine for outter declarations) + results := make([]*ast.Node, 0, len(statements.Nodes)) + for _, statement := range statements.Nodes { + if !ast.IsLateVisibilityPaintedStatement(statement) { + results = append(results, statement) + continue + } + original := tx.EmitContext().MostOriginal(statement) + id := ast.GetNodeId(original) + replacement, ok := tx.lateStatementReplacementMap[id] + if !ok || replacement == nil { + continue // not replaced, elide + } + if replacement.Kind == ast.KindSyntaxList { + if !tx.needsScopeFixMarker || !tx.resultHasExternalModuleIndicator { + for _, elem := range replacement.AsSyntaxList().Children { + if needsScopeMarker(elem) { + tx.needsScopeFixMarker = true + } + if ast.IsSourceFile(statement.Parent) && ast.IsExternalModuleIndicator(replacement) { + tx.resultHasExternalModuleIndicator = true + } + } + } + results = append(results, replacement.AsSyntaxList().Children...) + } else { + if needsScopeMarker(replacement) { + tx.needsScopeFixMarker = true + } + if ast.IsSourceFile(statement.Parent) && ast.IsExternalModuleIndicator(replacement) { + tx.resultHasExternalModuleIndicator = true + } + results = append(results, replacement) + } + } + + return tx.Factory().NewNodeList(results) +} + +func (tx *DeclarationTransformer) getReferencedFiles(outputFilePath string) (results []*ast.FileReference) { + // Handle path rewrites for triple slash ref comments + for _, pair := range tx.rawReferencedFiles { + sourceFile := pair.file + ref := pair.ref + + if !ref.Preserve { + continue + } + + file := tx.host.GetSourceFileFromReference(sourceFile, ref) + if file == nil { + continue + } + + var declFileName string + if file.IsDeclarationFile { + declFileName = file.FileName() + } else { + // !!! bundled emit support, omit bundled refs + // if (tx.isBundledEmit && contains((node as Bundle).sourceFiles, file)) continue + paths := tx.host.GetOutputPathsFor(file, true) + // Try to use output path for referenced file, or output js path if that doesn't exist, or the input path if all else fails + declFileName = paths.DeclarationFilePath() + if len(declFileName) == 0 { + declFileName = paths.JsFilePath() + } + if len(declFileName) == 0 { + declFileName = file.FileName() + } + } + // Should only be missing if the source file is missing a fileName (at which point we can't name a reference to it anyway) + // TODO: Shouldn't this be a crash or assert instead of a silent continue? + if len(declFileName) == 0 { + continue + } + + fileName := tspath.GetRelativePathToDirectoryOrUrl( + outputFilePath, + declFileName, + false, // TODO: Probably unsafe to assume this isn't a URL, but that's what strada does + tspath.ComparePathsOptions{ + CurrentDirectory: tx.host.GetCurrentDirectory(), + UseCaseSensitiveFileNames: tx.host.UseCaseSensitiveFileNames(), + }, + ) + + results = append(results, &ast.FileReference{ + TextRange: core.NewTextRange(-1, -1), + FileName: fileName, + ResolutionMode: ref.ResolutionMode, + Preserve: ref.Preserve, + }) + } + return results +} + +func (tx *DeclarationTransformer) getLibReferences() (result []*ast.FileReference) { + // clone retained references + for _, ref := range tx.rawLibReferenceDirectives { + if !ref.Preserve { + continue + } + result = append(result, &ast.FileReference{ + TextRange: core.NewTextRange(-1, -1), + FileName: ref.FileName, + ResolutionMode: ref.ResolutionMode, + Preserve: ref.Preserve, + }) + } + return result +} + +func (tx *DeclarationTransformer) getTypeReferences() (result []*ast.FileReference) { + // clone retained references + for _, ref := range tx.rawTypeReferenceDirectives { + if !ref.Preserve { + continue + } + result = append(result, &ast.FileReference{ + TextRange: core.NewTextRange(-1, -1), + FileName: ref.FileName, + ResolutionMode: ref.ResolutionMode, + Preserve: ref.Preserve, + }) + } + return result +} + +func (tx *DeclarationTransformer) visitDeclarationSubtree(input *ast.Node) *ast.Node { + // !!! TODO: stripInternal support? + // if (shouldStripInternal(input)) return nil + if ast.IsDeclaration(input) { + if isDeclarationAndNotVisible(tx.EmitContext(), tx.resolver, input) { + return nil + } + if ast.HasDynamicName(input) { + if tx.state.isolatedDeclarations { + // !!! isolatedDeclarations support + return nil + } else if tx.resolver.IsLateBound(tx.EmitContext().ParseNode(input)) || !ast.IsEntityNameExpression(input.Name().AsComputedPropertyName().Expression) { + return nil + } + } + } + + // Elide implementation signatures from overload sets + if ast.IsFunctionLike(input) && tx.resolver.IsImplementationOfOverload(input) { + return nil + } + + if input.Kind == ast.KindSemicolonClassElement { + return nil + } + + previousEnclosingDeclaration := tx.enclosingDeclaration + if isEnclosingDeclaration(input) { + tx.enclosingDeclaration = input + } + + canProdiceDiagnostic := canProduceDiagnostics(input) + oldWithinObjectLiteralType := tx.suppressNewDiagnosticContexts + shouldEnterSuppressNewDiagnosticsContextContext := (input.Kind == ast.KindTypeLiteral || input.Kind == ast.KindMappedType) && input.Parent.Kind != ast.KindTypeAliasDeclaration + + oldDiag := tx.state.getSymbolAccessibilityDiagnostic + if canProdiceDiagnostic && !tx.suppressNewDiagnosticContexts { + tx.state.getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNode(input) + } + oldName := tx.state.errorNameNode + + if shouldEnterSuppressNewDiagnosticsContextContext { + tx.suppressNewDiagnosticContexts = true + } + + var result *ast.Node + + switch input.Kind { + case ast.KindMappedType: + result = tx.transformMappedTypeNode(input.AsMappedTypeNode()) + case ast.KindHeritageClause: + result = tx.transformHeritageClause(input.AsHeritageClause()) + case ast.KindMethodSignature: + result = tx.transformMethodSignatureDeclaration(input.AsMethodSignatureDeclaration()) + case ast.KindMethodDeclaration: + result = tx.transformMethodDeclaration(input.AsMethodDeclaration()) + case ast.KindConstructSignature: + result = tx.transformConstructSignatureDeclaration(input.AsConstructSignatureDeclaration()) + case ast.KindConstructor: + result = tx.transformConstructorDeclaration(input.AsConstructorDeclaration()) + case ast.KindGetAccessor: + result = tx.transformGetAccesorDeclaration(input.AsGetAccessorDeclaration()) + case ast.KindSetAccessor: + result = tx.transformSetAccessorDeclaration(input.AsSetAccessorDeclaration()) + case ast.KindPropertyDeclaration: + result = tx.transformPropertyDeclaration(input.AsPropertyDeclaration()) + case ast.KindPropertySignature: + result = tx.transformPropertySignatureDeclaration(input.AsPropertySignatureDeclaration()) + case ast.KindCallSignature: + result = tx.transformCallSignatureDeclaration(input.AsCallSignatureDeclaration()) + case ast.KindIndexSignature: + result = tx.transformIndexSignatureDeclaration(input.AsIndexSignatureDeclaration()) + case ast.KindVariableDeclaration: + result = tx.transformVariableDeclaration(input.AsVariableDeclaration()) + case ast.KindTypeParameter: + result = tx.transformTypeParameterDeclaration(input.AsTypeParameter()) + case ast.KindExpressionWithTypeArguments: + result = tx.transformExpressionWithTypeArguments(input.AsExpressionWithTypeArguments()) + case ast.KindTypeReference: + result = tx.transformTypeReference(input.AsTypeReference()) + case ast.KindConditionalType: + result = tx.transformConditionalTypeNode(input.AsConditionalTypeNode()) + case ast.KindFunctionType: + result = tx.transformFunctionTypeNode(input.AsFunctionTypeNode()) + case ast.KindConstructorType: + result = tx.transformConstructorTypeNode(input.AsConstructorTypeNode()) + case ast.KindImportType: + result = tx.transformImportTypeNode(input.AsImportTypeNode()) + case ast.KindTypeQuery: + tx.checkEntityNameVisibility(input.AsTypeQueryNode().ExprName, tx.enclosingDeclaration) + result = tx.Visitor().VisitEachChild(input) + case ast.KindTupleType: + result = tx.Visitor().VisitEachChild(input) + if result != nil { + startLine, _ := scanner.GetLineAndCharacterOfPosition(tx.state.currentSourceFile, input.Loc.Pos()) + endLine, _ := scanner.GetLineAndCharacterOfPosition(tx.state.currentSourceFile, input.Loc.End()) + if startLine == endLine { + tx.EmitContext().AddEmitFlags(result, printer.EFSingleLine) + } + } + default: + result = tx.Visitor().VisitEachChild(input) + } + + if result != nil && canProdiceDiagnostic && ast.HasDynamicName(input) { + tx.checkName(input) + } + + tx.enclosingDeclaration = previousEnclosingDeclaration + tx.state.getSymbolAccessibilityDiagnostic = oldDiag + tx.state.errorNameNode = oldName + tx.suppressNewDiagnosticContexts = oldWithinObjectLiteralType + return result +} + +func (tx *DeclarationTransformer) checkName(node *ast.Node) { + oldDiag := tx.state.getSymbolAccessibilityDiagnostic + if !tx.suppressNewDiagnosticContexts { + tx.state.getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNodeName(node) + } + tx.state.errorNameNode = node.Name() + // !!! Debug.assert(hasDynamicName(node as NamedDeclaration)); // Should only be called with dynamic names + entityName := node.Name().AsComputedPropertyName().Expression + tx.checkEntityNameVisibility(entityName, tx.enclosingDeclaration) + if !tx.suppressNewDiagnosticContexts { + tx.state.getSymbolAccessibilityDiagnostic = oldDiag + } + tx.state.errorNameNode = nil +} + +func (tx *DeclarationTransformer) transformMappedTypeNode(input *ast.MappedTypeNode) *ast.Node { + // handle missing template type nodes, since the printer does not + var typeNode *ast.Node + if input.Type == nil { + typeNode = tx.Factory().NewKeywordTypeNode(ast.KindAnyKeyword) + } else { + typeNode = tx.Visitor().Visit(input.Type) + } + return tx.Factory().UpdateMappedTypeNode( + input, + input.ReadonlyToken, + tx.Visitor().Visit(input.TypeParameter), + tx.Visitor().Visit(input.NameType), + input.QuestionToken, + typeNode, + nil, + ) +} + +func (tx *DeclarationTransformer) transformHeritageClause(clause *ast.HeritageClause) *ast.Node { + retainedClauses := core.Filter(clause.Types.Nodes, func(t *ast.Node) bool { + return ast.IsEntityNameExpression(t.AsExpressionWithTypeArguments().Expression) || + (clause.Token == ast.KindExtendsKeyword && t.Expression().Kind == ast.KindNullKeyword) + }) + if len(retainedClauses) == len(clause.Types.Nodes) { + return tx.Visitor().VisitEachChild(clause.AsNode()) + } + return tx.Factory().UpdateHeritageClause( + clause, + tx.Visitor().VisitNodes(tx.Factory().NewNodeList(retainedClauses)), + ) +} + +func (tx *DeclarationTransformer) transformImportTypeNode(input *ast.ImportTypeNode) *ast.Node { + if !ast.IsLiteralImportTypeNode(input.AsNode()) { + return input.AsNode() + } + return tx.Factory().UpdateImportTypeNode( + input, + input.IsTypeOf, + tx.Factory().UpdateLiteralTypeNode( + input.Argument.AsLiteralTypeNode(), + tx.rewriteModuleSpecifier(input.AsNode(), input.Argument.AsLiteralTypeNode().Literal), + ), + input.Attributes, + input.Qualifier, + tx.Visitor().VisitNodes(input.TypeArguments), + ) +} + +func (tx *DeclarationTransformer) transformConstructorTypeNode(input *ast.ConstructorTypeNode) *ast.Node { + return tx.Factory().UpdateConstructorTypeNode( + input, + tx.ensureModifiers(input.AsNode()), + tx.Visitor().VisitNodes(input.TypeParameters), + tx.updateParamList(input.AsNode(), input.Parameters), + tx.Visitor().Visit(input.Type), + ) +} + +func (tx *DeclarationTransformer) transformFunctionTypeNode(input *ast.FunctionTypeNode) *ast.Node { + return tx.Factory().UpdateFunctionTypeNode( + input, + tx.Visitor().VisitNodes(input.TypeParameters), + tx.updateParamList(input.AsNode(), input.Parameters), + tx.Visitor().Visit(input.Type), + ) +} + +func (tx *DeclarationTransformer) transformConditionalTypeNode(input *ast.ConditionalTypeNode) *ast.Node { + checkType := tx.Visitor().Visit(input.CheckType) + extendsType := tx.Visitor().Visit(input.ExtendsType) + oldEnclosingDecl := tx.enclosingDeclaration + tx.enclosingDeclaration = input.TrueType + trueType := tx.Visitor().Visit(input.TrueType) + tx.enclosingDeclaration = oldEnclosingDecl + falseType := tx.Visitor().Visit(input.FalseType) + + return tx.Factory().UpdateConditionalTypeNode( + input, + checkType, + extendsType, + trueType, + falseType, + ) +} + +func (tx *DeclarationTransformer) transformTypeReference(input *ast.TypeReferenceNode) *ast.Node { + tx.checkEntityNameVisibility(input.TypeName, tx.enclosingDeclaration) + return tx.Visitor().VisitEachChild(input.AsNode()) +} + +func (tx *DeclarationTransformer) transformExpressionWithTypeArguments(input *ast.ExpressionWithTypeArguments) *ast.Node { + if ast.IsEntityName(input.Expression) || ast.IsEntityNameExpression(input.Expression) { + tx.checkEntityNameVisibility(input.Expression, tx.enclosingDeclaration) + } + return tx.Visitor().VisitEachChild(input.AsNode()) +} + +func (tx *DeclarationTransformer) transformTypeParameterDeclaration(input *ast.TypeParameterDeclaration) *ast.Node { + if isPrivateMethodTypeParameter(tx.host, input) && (input.DefaultType != nil || input.Constraint != nil) { + return tx.Factory().UpdateTypeParameterDeclaration( + input, + input.Modifiers(), + input.Name(), + nil, + nil, + ) + } + return tx.Visitor().VisitEachChild(input.AsNode()) +} + +func (tx *DeclarationTransformer) transformVariableDeclaration(input *ast.VariableDeclaration) *ast.Node { + if ast.IsBindingPattern(input.Name()) { + return tx.recreateBindingPattern(input.Name().AsBindingPattern()) + } + // Variable declaration types also suppress new diagnostic contexts, provided the contexts wouldn't be made for binding pattern types + tx.suppressNewDiagnosticContexts = true + return tx.Factory().UpdateVariableDeclaration( + input, + input.Name(), + nil, + tx.ensureType(input.AsNode(), false), + tx.ensureNoInitializer(input.AsNode()), + ) +} + +func (tx *DeclarationTransformer) recreateBindingPattern(input *ast.BindingPattern) *ast.Node { + var results []*ast.Node + for _, elem := range input.Elements.Nodes { + result := tx.recreateBindingElement(elem.AsBindingElement()) + if result == nil { + continue + } + if result.Kind == ast.KindSyntaxList { + results = append(results, result.AsSyntaxList().Children...) + } else { + results = append(results, result) + } + } + if len(results) == 0 { + return nil + } + if len(results) == 1 { + return results[1] + } + return tx.Factory().NewSyntaxList(results) +} + +func (tx *DeclarationTransformer) recreateBindingElement(e *ast.BindingElement) *ast.Node { + if e.Kind == ast.KindBindingElement { + return nil + } + if e.Name() == nil { + return nil + } + if !getBindingNameVisible(tx.resolver, e.AsNode()) { + return nil + } + if ast.IsBindingPattern(e.Name()) { + return tx.recreateBindingPattern(e.Name().AsBindingPattern()) + } + return tx.Factory().NewVariableDeclaration( + e.Name(), + nil, + tx.ensureType(e.AsNode(), false), + nil, // TODO: possible strada bug - not emitting const initialized binding pattern elements? + ) +} + +func (tx *DeclarationTransformer) transformIndexSignatureDeclaration(input *ast.IndexSignatureDeclaration) *ast.Node { + type_ := tx.Visitor().Visit(input.Type) + if type_ == nil { + type_ = tx.Factory().NewKeywordTypeNode(ast.KindAnyKeyword) + } + return tx.Factory().UpdateIndexSignatureDeclaration( + input, + tx.ensureModifiers(input.AsNode()), + tx.updateParamList(input.AsNode(), input.Parameters), + type_, + ) +} + +func (tx *DeclarationTransformer) transformCallSignatureDeclaration(input *ast.CallSignatureDeclaration) *ast.Node { + return tx.Factory().UpdateCallSignatureDeclaration( + input, + tx.ensureTypeParams(input.AsNode(), input.TypeParameters), + tx.updateParamList(input.AsNode(), input.Parameters), + tx.ensureType(input.AsNode(), false), + ) +} + +func (tx *DeclarationTransformer) transformPropertySignatureDeclaration(input *ast.PropertySignatureDeclaration) *ast.Node { + if ast.IsPrivateIdentifier(input.Name()) { + return nil + } + return tx.Factory().UpdatePropertySignatureDeclaration( + input, + tx.ensureModifiers(input.AsNode()), + input.Name(), + input.PostfixToken, + tx.ensureType(input.AsNode(), false), + tx.ensureNoInitializer(input.AsNode()), // TODO: possible strada bug (fixed here) - const property signatures never initialized + ) +} + +func (tx *DeclarationTransformer) transformPropertyDeclaration(input *ast.PropertyDeclaration) *ast.Node { + if ast.IsPrivateIdentifier(input.Name()) { + return nil + } + return tx.Factory().UpdatePropertyDeclaration( + input, + tx.ensureModifiers(input.AsNode()), + input.Name(), + input.PostfixToken, + tx.ensureType(input.AsNode(), false), + tx.ensureNoInitializer(input.AsNode()), + ) +} + +func (tx *DeclarationTransformer) transformSetAccessorDeclaration(input *ast.SetAccessorDeclaration) *ast.Node { + if ast.IsPrivateIdentifier(input.Name()) { + return nil + } + + return tx.Factory().UpdateSetAccessorDeclaration( + input, + tx.ensureModifiers(input.AsNode()), + input.Name(), + nil, // accessors shouldn't have type params + tx.updateAccessorParamList(input.AsNode(), tx.host.GetEffectiveDeclarationFlags(tx.EmitContext().ParseNode(input.AsNode()), ast.ModifierFlagsPrivate) != 0), + nil, + nil, + ) +} + +func (tx *DeclarationTransformer) transformGetAccesorDeclaration(input *ast.GetAccessorDeclaration) *ast.Node { + if ast.IsPrivateIdentifier(input.Name()) { + return nil + } + return tx.Factory().UpdateGetAccessorDeclaration( + input, + tx.ensureModifiers(input.AsNode()), + input.Name(), + nil, // accessors shouldn't have type params + tx.updateAccessorParamList(input.AsNode(), tx.host.GetEffectiveDeclarationFlags(tx.EmitContext().ParseNode(input.AsNode()), ast.ModifierFlagsPrivate) != 0), + tx.ensureType(input.AsNode(), false), + nil, + ) +} + +const defaultModifierFlagsMask = ast.ModifierFlagsAll ^ ast.ModifierFlagsPublic + +func (tx *DeclarationTransformer) updateAccessorParamList(input *ast.Node, isPrivate bool) *ast.ParameterList { + var newParams []*ast.Node + if !isPrivate { + thisParam := ast.GetThisParameter(input) + if thisParam != nil { + newParams = append(newParams, tx.ensureParameter(thisParam.AsParameterDeclaration(), defaultModifierFlagsMask)) + } + } + if ast.IsSetAccessorDeclaration(input) { + var valueParam *ast.Node + if !isPrivate { + if len(newParams) == 1 && len(input.AsSetAccessorDeclaration().Parameters.Nodes) >= 2 { + valueParam = tx.ensureParameter(input.AsSetAccessorDeclaration().Parameters.Nodes[1].AsParameterDeclaration(), defaultModifierFlagsMask) + } else if len(newParams) == 0 && len(input.AsSetAccessorDeclaration().Parameters.Nodes) >= 1 { + valueParam = tx.ensureParameter(input.AsSetAccessorDeclaration().Parameters.Nodes[0].AsParameterDeclaration(), defaultModifierFlagsMask) + } + } + if valueParam == nil { + // TODO: strada bug - no type printed on set accessor missing arg as though private + var type_ *ast.Node + if !isPrivate { + type_ = tx.Factory().NewKeywordExpression(ast.KindAnyKeyword) + } + valueParam = tx.Factory().NewParameterDeclaration( + nil, + nil, + tx.Factory().NewIdentifier("value"), + nil, + type_, + nil, + ) + } + newParams = append(newParams, valueParam) + } + return tx.Factory().NewNodeList(newParams) +} + +func (tx *DeclarationTransformer) transformConstructorDeclaration(input *ast.ConstructorDeclaration) *ast.Node { + // A constructor declaration may not have a type annotation + return tx.Factory().UpdateConstructorDeclaration( + input, + tx.ensureModifiers(input.AsNode()), + nil, // no type params + tx.updateParamListEx(input.AsNode(), input.Parameters, ast.ModifierFlagsNone), + nil, // no return type + nil, + ) +} + +func (tx *DeclarationTransformer) transformConstructSignatureDeclaration(input *ast.ConstructSignatureDeclaration) *ast.Node { + return tx.Factory().UpdateConstructSignatureDeclaration( + input, + tx.ensureTypeParams(input.AsNode(), input.TypeParameters), + tx.updateParamListEx(input.AsNode(), input.Parameters, ast.ModifierFlagsNone), + nil, // no return type + ) +} + +func (tx *DeclarationTransformer) omitPrivateMethodType(input *ast.Node) *ast.Node { + if input.Symbol() != nil && len(input.Symbol().Declarations) > 0 && input.Symbol().Declarations[0] != input { + return nil + } else { + return tx.Factory().NewPropertyDeclaration( + tx.ensureModifiers(input), + input.Name(), + nil, + nil, + nil, + ) + } +} + +func (tx *DeclarationTransformer) transformMethodSignatureDeclaration(input *ast.MethodSignatureDeclaration) *ast.Node { + if tx.host.GetEffectiveDeclarationFlags(tx.EmitContext().ParseNode(input.AsNode()), ast.ModifierFlagsPrivate) != 0 { + return tx.omitPrivateMethodType(input.AsNode()) + } else if ast.IsPrivateIdentifier(input.Name()) { + return nil + } else { + return tx.Factory().UpdateMethodSignatureDeclaration( + input, + tx.ensureModifiers(input.AsNode()), + input.Name(), + input.PostfixToken, + tx.ensureTypeParams(input.AsNode(), input.TypeParameters), + tx.updateParamList(input.AsNode(), input.Parameters), + tx.ensureType(input.AsNode(), false), + ) + } +} + +func (tx *DeclarationTransformer) transformMethodDeclaration(input *ast.MethodDeclaration) *ast.Node { + if tx.host.GetEffectiveDeclarationFlags(tx.EmitContext().ParseNode(input.AsNode()), ast.ModifierFlagsPrivate) != 0 { + return tx.omitPrivateMethodType(input.AsNode()) + } else if ast.IsPrivateIdentifier(input.Name()) { + return nil + } else { + return tx.Factory().UpdateMethodDeclaration( + input, + tx.ensureModifiers(input.AsNode()), + nil, + input.Name(), + input.PostfixToken, + tx.ensureTypeParams(input.AsNode(), input.TypeParameters), + tx.updateParamList(input.AsNode(), input.Parameters), + tx.ensureType(input.AsNode(), false), + nil, + ) + } +} + +func (tx *DeclarationTransformer) visitDeclarationStatements(input *ast.Node) *ast.Node { + // !!! TODO: stripInternal support? + // if (shouldStripInternal(input)) return nil + switch input.Kind { + case ast.KindExportDeclaration: + if ast.IsSourceFile(input.Parent) { + tx.resultHasExternalModuleIndicator = true + } + tx.resultHasScopeMarker = true + // Rewrite external module names if necessary + return tx.Factory().UpdateExportDeclaration( + input.AsExportDeclaration(), + input.Modifiers(), + input.IsTypeOnly(), + input.AsExportDeclaration().ExportClause, + tx.rewriteModuleSpecifier(input, input.AsExportDeclaration().ModuleSpecifier), + tx.tryGetResolutionModeOverride(input.AsExportDeclaration().Attributes), + ) + case ast.KindExportAssignment: + if ast.IsSourceFile(input.Parent) { + tx.resultHasExternalModuleIndicator = true + } + tx.resultHasScopeMarker = true + if input.AsExportAssignment().Expression.Kind == ast.KindIdentifier { + return input + } + // expression is non-identifier, create _default typed variable to reference + newId := tx.EmitContext().NewUniqueName("_default", printer.AutoGenerateOptions{Flags: printer.GeneratedIdentifierFlagsOptimistic}) + tx.state.getSymbolAccessibilityDiagnostic = func(_ printer.SymbolAccessibilityResult) *SymbolAccessibilityDiagnostic { + return &SymbolAccessibilityDiagnostic{ + diagnosticMessage: diagnostics.Default_export_of_the_module_has_or_is_using_private_name_0, + errorNode: input, + } + } + tx.tracker.PushErrorFallbackNode(input) + type_ := tx.ensureType(input, false) + varDecl := tx.Factory().NewVariableDeclaration(newId, nil, type_, nil) + tx.tracker.PopErrorFallbackNode() + var modList *ast.ModifierList + if tx.needsDeclare { + modList = tx.Factory().NewModifierList([]*ast.Node{tx.Factory().NewModifier(ast.KindDeclareKeyword)}) + } else { + modList = tx.Factory().NewModifierList([]*ast.Node{}) + } + statement := tx.Factory().NewVariableStatement(modList, tx.Factory().NewVariableDeclarationList(ast.NodeFlagsConst, tx.Factory().NewNodeList([]*ast.Node{varDecl}))) + + assignment := tx.Factory().UpdateExportAssignment(input.AsExportAssignment(), input.Modifiers(), newId) + // Remove coments from the export declaration and copy them onto the synthetic _default declaration + tx.preserveJsDoc(statement, input) + tx.removeAllComments(assignment) + tx.Factory().NewSyntaxList([]*ast.Node{statement, assignment}) + return nil + default: + result := tx.transformTopLevelDeclaration(input) + // Don't actually transform yet; just leave as original node - will be elided/swapped by late pass + original := tx.EmitContext().MostOriginal(input) + id := ast.GetNodeId(original) + tx.lateStatementReplacementMap[id] = result + return input + } +} + +func (tx *DeclarationTransformer) rewriteModuleSpecifier(parent *ast.Node, input *ast.Node) *ast.Node { + if input == nil { + return nil + } + tx.resultHasExternalModuleIndicator = tx.resultHasExternalModuleIndicator || (parent.Kind != ast.KindModuleDeclaration && parent.Kind != ast.KindImportType) + if ast.IsStringLiteralLike(input) { + if tx.isBundledEmit { + // !!! TODO: support bundled emit specifier rewriting + } + } + return input +} + +func (tx *DeclarationTransformer) tryGetResolutionModeOverride(node *ast.Node) *ast.Node { + if node == nil { + return node + } + mode := tx.host.GetResolutionModeOverride(node) + if mode != core.ResolutionModeNone { + return node + } + return nil +} + +func (tx *DeclarationTransformer) preserveJsDoc(updated *ast.Node, original *ast.Node) { + // !!! TODO: JSDoc comment support + // if (hasJSDocNodes(updated) && hasJSDocNodes(original)) { + // updated.jsDoc = original.jsDoc; + // } + // return setCommentRange(updated, getCommentRange(original)); +} + +func (tx *DeclarationTransformer) removeAllComments(node *ast.Node) { + tx.EmitContext().AddEmitFlags(node, printer.EFNoComments) + // !!! TODO: Also remove synthetic trailing/leading comments added by transforms + // emitNode.leadingComments = undefined; + // emitNode.trailingComments = undefined; +} + +func (tx *DeclarationTransformer) ensureType(node *ast.Node, ignorePrivate bool) *ast.Node { + if !ignorePrivate && tx.host.GetEffectiveDeclarationFlags(tx.EmitContext().ParseNode(node), ast.ModifierFlagsPrivate) != 0 { + // Private nodes emit no types (except private parameter properties, whose parameter types are actually visible) + return nil + } + + if tx.shouldPrintWithInitializer(node) { + // Literal const declarations will have an initializer ensured rather than a type + return nil + } + + // Should be removed createTypeOfDeclaration will actually now reuse the existing annotation so there is no real need to duplicate type walking + // Left in for now to minimize diff during syntactic type node builder refactor + if !ast.IsExportAssignment(node) && !ast.IsBindingElement(node) && node.Type() != nil && (!ast.IsParameter(node) || !tx.resolver.RequiresAddingImplicitUndefined(node, nil, tx.enclosingDeclaration)) { + return tx.Visitor().Visit(node.Type()) + } + + oldErrorNameNode := tx.state.errorNameNode + tx.state.errorNameNode = node.Name() + var oldDiag GetSymbolAccessibilityDiagnostic + if !tx.suppressNewDiagnosticContexts { + oldDiag = tx.state.getSymbolAccessibilityDiagnostic + if canProduceDiagnostics(node) { + tx.state.getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNode(node) + } + } + var typeNode *ast.Node + + if hasInferredType(node) { + typeNode = tx.resolver.CreateTypeOfDeclaration(tx.EmitContext(), node, tx.enclosingDeclaration, declarationEmitNodeBuilderFlags, declarationEmitInternalNodeBuilderFlags, tx.tracker) + } else if ast.IsFunctionLike(node) { + typeNode = tx.resolver.CreateReturnTypeOfSignatureDeclaration(tx.EmitContext(), node, tx.enclosingDeclaration, declarationEmitNodeBuilderFlags, declarationEmitInternalNodeBuilderFlags, tx.tracker) + } else { + // Debug.assertNever(node); // !!! + } + + tx.state.errorNameNode = oldErrorNameNode + if !tx.suppressNewDiagnosticContexts { + tx.state.getSymbolAccessibilityDiagnostic = oldDiag + } + if typeNode == nil { + return tx.Factory().NewKeywordTypeNode(ast.KindAnyKeyword) + } + return typeNode +} + +func (tx *DeclarationTransformer) shouldPrintWithInitializer(node *ast.Node) bool { + return canHaveLiteralInitializer(tx.host, node) && node.Initializer() != nil && tx.resolver.IsLiteralConstDeclaration(tx.EmitContext().MostOriginal(node)) +} + +func (tx *DeclarationTransformer) checkEntityNameVisibility(entityName *ast.Node, enclosingDeclaration *ast.Node) { + visibilityResult := tx.resolver.IsEntityNameVisible(entityName, enclosingDeclaration) + tx.tracker.handleSymbolAccessibilityError(visibilityResult) +} + +// Transforms the direct child of a source file into zero or more replacement statements +func (tx *DeclarationTransformer) transformTopLevelDeclaration(input *ast.Node) *ast.Node { + if len(tx.state.lateMarkedStatements) > 0 { + // Remove duplicates of the current statement from the deferred work queue (this was done via orderedRemoveItem in strada - why? to ensure the same backing array? microop?) + tx.state.lateMarkedStatements = core.Filter(tx.state.lateMarkedStatements, func(node *ast.Node) bool { return node != input }) + } + // !!! TODO: stripInternal support? + // if (shouldStripInternal(input)) return; + if input.Kind == ast.KindImportEqualsDeclaration { + return tx.transformImportEqualsDeclaration(input.AsImportEqualsDeclaration()) + } + if input.Kind == ast.KindImportDeclaration { + return tx.transformImportDeclaration(input.AsImportDeclaration()) + } + if ast.IsDeclaration(input) && isDeclarationAndNotVisible(tx.EmitContext(), tx.resolver, input) { + return nil + } + + // !!! TODO: JSDoc support + // if (isJSDocImportTag(input)) return; + + // Elide implementation signatures from overload sets + if ast.IsFunctionLike(input) && tx.resolver.IsImplementationOfOverload(input) { + return nil + } + previousEnclosingDeclaration := tx.enclosingDeclaration + if isEnclosingDeclaration(input) { + tx.enclosingDeclaration = input + } + + canProdiceDiagnostic := canProduceDiagnostics(input) + oldDiag := tx.state.getSymbolAccessibilityDiagnostic + oldName := tx.state.errorNameNode + if canProdiceDiagnostic { + tx.state.getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNode(input) + } + previousNeedsDeclare := tx.needsDeclare + + var result *ast.Node + switch input.Kind { + case ast.KindTypeAliasDeclaration: + result = tx.transformTypeAliasDeclaration(input.AsTypeAliasDeclaration()) + case ast.KindInterfaceDeclaration: + result = tx.transformInterfaceDeclaration(input.AsInterfaceDeclaration()) + case ast.KindFunctionDeclaration: + result = tx.transformFunctionDeclaration(input.AsFunctionDeclaration()) + case ast.KindModuleDeclaration: + result = tx.transformModuleDeclaration(input.AsModuleDeclaration()) + case ast.KindClassDeclaration: + result = tx.transformClassDeclaration(input.AsClassDeclaration()) + case ast.KindVariableStatement: + result = tx.transformVariableStatement(input.AsVariableStatement()) + case ast.KindEnumDeclaration: + result = tx.transformEnumDeclaration(input.AsEnumDeclaration()) + default: + // Anything left unhandled is an error, so this should be unreachable + panic(fmt.Sprintf("Unhandled top-level node in declaration emit: %q", input.Kind)) + } + + tx.enclosingDeclaration = previousEnclosingDeclaration + tx.state.getSymbolAccessibilityDiagnostic = oldDiag + tx.needsDeclare = previousNeedsDeclare + tx.state.errorNameNode = oldName + return result +} + +func (tx *DeclarationTransformer) transformTypeAliasDeclaration(input *ast.TypeAliasDeclaration) *ast.Node { + tx.needsDeclare = false + return tx.Factory().UpdateTypeAliasDeclaration( + input, + tx.ensureModifiers(input.AsNode()), + input.Name(), + tx.Visitor().VisitNodes(input.TypeParameters), + tx.Visitor().Visit(input.Type), + ) +} + +func (tx *DeclarationTransformer) transformInterfaceDeclaration(input *ast.InterfaceDeclaration) *ast.Node { + return tx.Factory().UpdateInterfaceDeclaration( + input, + tx.ensureModifiers(input.AsNode()), + input.Name(), + tx.Visitor().VisitNodes(input.TypeParameters), + tx.Visitor().VisitNodes(input.HeritageClauses), + tx.Visitor().VisitNodes(input.Members), + ) +} + +func (tx *DeclarationTransformer) transformFunctionDeclaration(input *ast.FunctionDeclaration) *ast.Node { + updated := tx.Factory().UpdateFunctionDeclaration( + input, + tx.ensureModifiers(input.AsNode()), + nil, + input.Name(), + tx.ensureTypeParams(input.AsNode(), input.TypeParameters), + tx.updateParamList(input.AsNode(), input.Parameters), + tx.ensureType(input.AsNode(), false), + nil, + ) + if updated == nil || !tx.resolver.IsExpandoFunctionDeclaration(input.AsNode()) || !shouldEmitFunctionProperties(input) { + return updated + } + // Add expando function properties to result + + // !!! TODO: expando function support + // props := tx.resolver.GetPropertiesOfContainerFunction(input) + // if tx.state.isolatedDeclarations { + // tx.state.reportExpandoFunctionErrors(input.AsNode()) + // } + return updated // !!! +} + +func (tx *DeclarationTransformer) transformModuleDeclaration(input *ast.ModuleDeclaration) *ast.Node { + // !!! TODO: module declarations are now parsed into nested module objects with export modifiers + // It'd be good to collapse those back in the declaration output, but the AST can't represent the + // `namespace a.b.c` shape for the printer (without using invalid identifier names). + mods := tx.ensureModifiers(input.AsNode()) + oldNeedsDeclare := tx.needsDeclare + tx.needsDeclare = false + inner := input.Body + if inner != nil && inner.Kind == ast.KindModuleBlock { + oldNeedsScopeFix := tx.needsScopeFixMarker + oldHasScopeFix := tx.resultHasScopeMarker + tx.resultHasScopeMarker = false + tx.needsScopeFixMarker = false + statements := tx.Visitor().VisitNodes(inner.AsModuleBlock().Statements) + lateStatements := tx.transformAndReplaceLatePaintedStatements(statements) + if input.Flags&ast.NodeFlagsAmbient != 0 { + tx.needsScopeFixMarker = false // If it was `declare`'d everything is implicitly exported already, ignore late printed "privates" + } + // With the final list of statements, there are 3 possibilities: + // 1. There's an export assignment or export declaration in the namespace - do nothing + // 2. Everything is exported and there are no export assignments or export declarations - strip all export modifiers + // 3. Some things are exported, some are not, and there's no marker - add an empty marker + if !ast.IsGlobalScopeAugmentation(input.AsNode()) && !tx.resultHasScopeMarker && !hasScopeMarker(lateStatements) { + if tx.needsScopeFixMarker { + lateStatements = tx.Factory().NewNodeList(append(lateStatements.Nodes, createEmptyExports(tx.Factory()))) + } else { + lateStatements = tx.EmitContext().NewNodeVisitor(tx.stripExportModifiers).VisitNodes(lateStatements) + } + } + + body := tx.Factory().UpdateModuleBlock(inner.AsModuleBlock(), lateStatements) + tx.needsDeclare = oldNeedsDeclare + tx.needsScopeFixMarker = oldNeedsScopeFix + tx.resultHasScopeMarker = oldHasScopeFix + + return tx.Factory().UpdateModuleDeclaration( + input, + mods, + input.Keyword, + input.Name(), + body, + ) + } + if inner != nil { + // trigger visit. ignore result (is deferred, so is just inner unless elided) + tx.Visitor().Visit(inner) + // eagerly transform nested namespaces (the nesting doesn't need any elision or painting done) + original := tx.EmitContext().MostOriginal(inner) + id := ast.GetNodeId(original) + body, _ := tx.lateStatementReplacementMap[id] + delete(tx.lateStatementReplacementMap, id) + return tx.Factory().UpdateModuleDeclaration( + input, + mods, + input.Keyword, + input.Name(), + body, + ) + } + return tx.Factory().UpdateModuleDeclaration( + input, + mods, + input.Keyword, + input.Name(), + nil, + ) +} + +func (tx *DeclarationTransformer) stripExportModifiers(statement *ast.Node) *ast.Node { + if statement == nil { + return nil + } + if ast.IsImportEqualsDeclaration(statement) || tx.host.GetEffectiveDeclarationFlags(tx.EmitContext().ParseNode(statement), ast.ModifierFlagsDefault) != 0 || !ast.CanHaveModifiers(statement) { + // `export import` statements should remain as-is, as imports are _not_ implicitly exported in an ambient namespace + // Likewise, `export default` classes and the like and just be `default`, so we preserve their `export` modifiers, too + return statement + } + + oldFlags := ast.GetCombinedModifierFlags(statement) + if oldFlags&ast.ModifierFlagsExport == 0 { + return statement + } + newFlags := oldFlags & (ast.ModifierFlagsAll ^ ast.ModifierFlagsExport) + modifiers := ast.CreateModifiersFromModifierFlags(newFlags, tx.Factory().NewModifier) + return ast.ReplaceModifiers(tx.Factory(), statement, tx.Factory().NewModifierList(modifiers)) +} + +func (tx *DeclarationTransformer) transformClassDeclaration(input *ast.ClassDeclaration) *ast.Node { + tx.state.errorNameNode = input.Name() + tx.tracker.PushErrorFallbackNode(input.AsNode()) + defer tx.tracker.PopErrorFallbackNode() + + modifiers := tx.ensureModifiers(input.AsNode()) + typeParameters := tx.ensureTypeParams(input.AsNode(), input.TypeParameters) + ctor := getFirstConstructorWithBody(input.AsNode()) + var parameterProperties []*ast.Node + if ctor != nil { + oldDiag := tx.state.getSymbolAccessibilityDiagnostic + for _, param := range ctor.AsConstructorDeclaration().Parameters.Nodes { + if !ast.HasSyntacticModifier(param, ast.ModifierFlagsParameterPropertyModifier) { + continue + } + // !!! TODO: stripInternal support? + // if (shouldStripInternal(param)) { continue } + tx.state.getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNode(param) + if param.Name().Kind == ast.KindIdentifier { + updated := tx.Factory().NewPropertyDeclaration( + tx.ensureModifiers(param), + param.Name(), + param.AsParameterDeclaration().QuestionToken, + tx.ensureType(param, false), + tx.ensureNoInitializer(param), + ) + tx.preserveJsDoc(updated, param) + parameterProperties = append(parameterProperties, updated) + } else { + // Pattern - this is currently an error, but we emit declarations for it somewhat correctly + // !!! is this worth reimplementing? We never made it not-an-error + } + } + tx.state.getSymbolAccessibilityDiagnostic = oldDiag + } + + // When the class has at least one private identifier, create a unique constant identifier to retain the nominal typing behavior + // Prevents other classes with the same public members from being used in place of the current class + var privateIdentifier *ast.Node + if core.Some(input.Members.Nodes, func(member *ast.Node) bool { return member.Name() != nil && ast.IsPrivateIdentifier(member.Name()) }) { + privateIdentifier = tx.Factory().NewPropertyDeclaration( + nil, + tx.Factory().NewPrivateIdentifier("#private"), + nil, + nil, + nil, + ) + } + + lateIndexes := tx.resolver.CreateLateBoundIndexSignatures( + tx.EmitContext(), + input.AsNode(), + tx.enclosingDeclaration, + declarationEmitNodeBuilderFlags, + declarationEmitInternalNodeBuilderFlags, + tx.tracker, + ) + + memberNodes := make([]*ast.Node, 0, len(input.Members.Nodes)) + if privateIdentifier != nil { + memberNodes = append(memberNodes, privateIdentifier) + } + memberNodes = append(memberNodes, lateIndexes...) + memberNodes = append(memberNodes, parameterProperties...) + visitResult := tx.Visitor().VisitNodes(input.Members) + if visitResult != nil && len(visitResult.Nodes) > 0 { + memberNodes = append(memberNodes, visitResult.Nodes...) + } + members := tx.Factory().NewNodeList(memberNodes) + + extendsClause := getEffectiveBaseTypeNode(input.AsNode()) + + if extendsClause != nil && !ast.IsEntityNameExpression(extendsClause.AsExpressionWithTypeArguments().Expression) && extendsClause.AsExpressionWithTypeArguments().Expression.Kind != ast.KindNullKeyword { + oldId := "default" + if ast.NodeIsPresent(input.Name()) && ast.IsIdentifier(input.Name()) && len(input.Name().AsIdentifier().Text) > 0 { + oldId = input.Name().AsIdentifier().Text + } + newId := tx.EmitContext().NewUniqueName(oldId+"_base", printer.AutoGenerateOptions{Flags: printer.GeneratedIdentifierFlagsOptimistic}) + tx.state.getSymbolAccessibilityDiagnostic = func(_ printer.SymbolAccessibilityResult) *SymbolAccessibilityDiagnostic { + return &SymbolAccessibilityDiagnostic{ + diagnosticMessage: diagnostics.X_extends_clause_of_exported_class_0_has_or_is_using_private_name_1, + errorNode: extendsClause, + typeName: input.Name(), + } + } + + varDecl := tx.Factory().NewVariableDeclaration( + newId, + nil, + tx.resolver.CreateTypeOfExpression(tx.EmitContext(), extendsClause.Expression(), input.AsNode(), declarationEmitNodeBuilderFlags, declarationEmitInternalNodeBuilderFlags, tx.tracker), + nil, + ) + var mods *ast.ModifierList + if tx.needsDeclare { + mods = tx.Factory().NewModifierList([]*ast.Node{tx.Factory().NewModifier(ast.KindDeclareKeyword)}) + } + statement := tx.Factory().NewVariableStatement( + mods, + tx.Factory().NewVariableDeclarationList(ast.NodeFlagsConst, tx.Factory().NewNodeList([]*ast.Node{varDecl})), + ) + newHeritageClause := tx.Factory().UpdateHeritageClause( + extendsClause.Parent.AsHeritageClause(), + tx.Factory().NewNodeList([]*ast.Node{ + tx.Factory().UpdateExpressionWithTypeArguments( + extendsClause.AsExpressionWithTypeArguments(), + newId, + tx.Visitor().VisitNodes(extendsClause.AsExpressionWithTypeArguments().TypeArguments), + ), + }), + ) + retainedHeritageClauses := tx.Visitor().VisitNodes(input.HeritageClauses) // should just be `implements` + heritageList := []*ast.Node{ + newHeritageClause, + } + if retainedHeritageClauses != nil && len(retainedHeritageClauses.Nodes) > 0 { + heritageList = append(heritageList, retainedHeritageClauses.Nodes...) + } + heritageClauses := tx.Factory().NewNodeList(heritageList) + + return tx.Factory().NewSyntaxList([]*ast.Node{ + statement, + tx.Factory().UpdateClassDeclaration( + input, + modifiers, + input.Name(), + typeParameters, + heritageClauses, + members, + ), + }) + } + + return tx.Factory().UpdateClassDeclaration( + input, + modifiers, + input.Name(), + typeParameters, + tx.Visitor().VisitNodes(input.HeritageClauses), + members, + ) +} + +func (tx *DeclarationTransformer) transformVariableStatement(input *ast.VariableStatement) *ast.Node { + visible := false + for _, decl := range input.DeclarationList.AsVariableDeclarationList().Declarations.Nodes { + visible = getBindingNameVisible(tx.resolver, decl) + if visible { + break + } + } + if !visible { + return nil + } + + nodes := tx.Visitor().VisitNodes(input.DeclarationList.AsVariableDeclarationList().Declarations) + if nodes != nil && len(nodes.Nodes) == 0 { + return nil + } + + modifiers := tx.ensureModifiers(input.AsNode()) + + var declList *ast.Node + if ast.IsVarUsing(input.DeclarationList) || ast.IsVarAwaitUsing(input.DeclarationList) { + declList = tx.Factory().NewVariableDeclarationList(ast.NodeFlagsConst, nodes) + tx.EmitContext().SetOriginal(declList, input.DeclarationList) + tx.EmitContext().SetCommentRange(declList, input.DeclarationList.Loc) + declList.Loc = input.DeclarationList.Loc + } else { + declList = tx.Factory().UpdateVariableDeclarationList(input.DeclarationList.AsVariableDeclarationList(), nodes) + } + return tx.Factory().UpdateVariableStatement(input, modifiers, declList) +} + +func (tx *DeclarationTransformer) transformEnumDeclaration(input *ast.EnumDeclaration) *ast.Node { + return tx.Factory().UpdateEnumDeclaration( + input, + tx.ensureModifiers(input.AsNode()), + input.Name(), + tx.Factory().NewNodeList(core.MapNonNil(input.Members.Nodes, func(m *ast.Node) *ast.Node { + // !!! TODO: stripInternal support? + // if (shouldStripInternal(m)) return; + + // !!! TODO: isolatedDeclarations support + // if ( + // isolatedDeclarations && m.initializer && enumValue?.hasExternalReferences && + // // This will be its own compiler error instead, so don't report. + // !isComputedPropertyName(m.name) + // ) { + // context.addDiagnostic(createDiagnosticForNode(m, Diagnostics.Enum_member_initializers_must_be_computable_without_references_to_external_symbols_with_isolatedDeclarations)); + // } + + // Rewrite enum values to their constants, if available + enumValue := tx.resolver.GetEnumMemberValue(m) + var newInitializer *ast.Node + switch value := enumValue.Value.(type) { + case jsnum.Number: + if value >= 0 { + newInitializer = tx.Factory().NewNumericLiteral(value.String()) + } else { + newInitializer = tx.Factory().NewPrefixUnaryExpression( + ast.KindMinusToken, + tx.Factory().NewNumericLiteral((-value).String()), + ) + } + case string: + newInitializer = tx.Factory().NewStringLiteral(value) + default: + // nil + newInitializer = nil + } + result := tx.Factory().UpdateEnumMember(m.AsEnumMember(), m.Name(), newInitializer) + tx.preserveJsDoc(result, m) + return result + })), + ) +} + +func (tx *DeclarationTransformer) ensureModifiers(node *ast.Node) *ast.ModifierList { + currentFlags := tx.host.GetEffectiveDeclarationFlags(tx.EmitContext().ParseNode(node), ast.ModifierFlagsAll) + newFlags := tx.ensureModifierFlags(node) + if currentFlags == newFlags { + // Elide decorators + mods := node.Modifiers() + if mods == nil { + return mods + } + return tx.Factory().NewModifierList(core.Filter(mods.Nodes, ast.IsModifier)) + } + result := ast.CreateModifiersFromModifierFlags(newFlags, tx.Factory().NewModifier) + if len(result) == 0 { + return nil + } + return tx.Factory().NewModifierList(result) +} + +func (tx *DeclarationTransformer) ensureModifierFlags(node *ast.Node) ast.ModifierFlags { + mask := ast.ModifierFlagsAll ^ (ast.ModifierFlagsPublic | ast.ModifierFlagsAsync | ast.ModifierFlagsOverride) // No async and override modifiers in declaration files + additions := ast.ModifierFlagsNone + if tx.needsDeclare && !isAlwaysType(node) { + additions = ast.ModifierFlagsAmbient + } + parentIsFile := node.Parent.Kind == ast.KindSourceFile + if !parentIsFile || (tx.isBundledEmit && parentIsFile && ast.IsExternalModule(node.Parent.AsSourceFile())) { + mask ^= ast.ModifierFlagsAmbient + additions = ast.ModifierFlagsNone + } + return maskModifierFlags(tx.host, node, mask, additions) +} + +func (tx *DeclarationTransformer) ensureTypeParams(node *ast.Node, params *ast.TypeParameterList) *ast.TypeParameterList { + if tx.host.GetEffectiveDeclarationFlags(tx.EmitContext().ParseNode(node), ast.ModifierFlagsPrivate) != 0 { + return nil + } + return tx.Visitor().VisitNodes(params) +} + +func (tx *DeclarationTransformer) updateParamList(node *ast.Node, params *ast.ParameterList) *ast.ParameterList { + return tx.updateParamListEx(node, params, ast.ModifierFlagsAll^ast.ModifierFlagsPublic) +} + +func (tx *DeclarationTransformer) updateParamListEx(node *ast.Node, params *ast.ParameterList, modifierMask ast.ModifierFlags) *ast.ParameterList { + if tx.host.GetEffectiveDeclarationFlags(tx.EmitContext().ParseNode(node), ast.ModifierFlagsPrivate) != 0 || len(params.Nodes) == 0 { + return tx.Factory().NewNodeList([]*ast.Node{}) + } + results := make([]*ast.Node, len(params.Nodes)) + for i, p := range params.Nodes { + results[i] = tx.ensureParameter(p.AsParameterDeclaration(), modifierMask) + } + return tx.Factory().NewNodeList(results) +} + +// Elide "public" modifier, as it is the default +func (tx *DeclarationTransformer) maskModifiers(node *ast.Node, mask ast.ModifierFlags, additions ast.ModifierFlags) *ast.ModifierList { + list := ast.CreateModifiersFromModifierFlags(maskModifierFlags(tx.host, node, mask, additions), tx.Factory().NewModifier) + return tx.Factory().NewModifierList(list) +} + +func (tx *DeclarationTransformer) ensureParameter(p *ast.ParameterDeclaration, modifierMask ast.ModifierFlags) *ast.Node { + oldDiag := tx.state.getSymbolAccessibilityDiagnostic + if !tx.suppressNewDiagnosticContexts { + tx.state.getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNode(p.AsNode()) + } + var questionToken *ast.TokenNode + if tx.resolver.IsOptionalParameter(p.AsNode()) { + if p.QuestionToken != nil { + questionToken = p.QuestionToken + } else { + questionToken = tx.Factory().NewToken(ast.KindQuestionToken) + } + } + result := tx.Factory().UpdateParameterDeclaration( + p, + tx.maskModifiers(p.AsNode(), modifierMask, ast.ModifierFlagsNone), + p.DotDotDotToken, + tx.filterBindingPatternInitializers(p.Name()), + questionToken, + tx.ensureType(p.AsNode(), true), + tx.ensureNoInitializer(p.AsNode()), + ) + tx.state.getSymbolAccessibilityDiagnostic = oldDiag + return result +} + +func (tx *DeclarationTransformer) ensureNoInitializer(node *ast.Node) *ast.Node { + if tx.shouldPrintWithInitializer(node) { + unwrappedInitializer := unwrapParenthesizedExpression(node.Initializer()) + if !isPrimitiveLiteralValue(unwrappedInitializer, true) { + tx.tracker.ReportInferenceFallback(node) + } + return tx.resolver.CreateLiteralConstValue(tx.EmitContext(), tx.EmitContext().ParseNode(node), tx.tracker) + } + return nil +} + +func (tx *DeclarationTransformer) filterBindingPatternInitializers(node *ast.Node) *ast.Node { + if node.Kind == ast.KindIdentifier { + return node + } else { + // TODO: visitor to avoid always making new nodes? + elements := make([]*ast.Node, 0, len(node.AsBindingPattern().Elements.Nodes)) + for _, elem := range node.AsBindingPattern().Elements.Nodes { + if elem.Kind == ast.KindOmittedExpression { + elements = append(elements, elem) + continue + } + if elem.PropertyName() != nil && ast.IsComputedPropertyName(elem.PropertyName()) && ast.IsEntityNameExpression(elem.PropertyName().Expression()) { + tx.checkEntityNameVisibility(elem.PropertyName().Expression(), tx.enclosingDeclaration) + } + if elem.Name() == nil { + elements = append(elements, elem) + continue + } + + elements = append(elements, tx.Factory().UpdateBindingElement( + elem.AsBindingElement(), + elem.AsBindingElement().DotDotDotToken, + elem.PropertyName(), + tx.filterBindingPatternInitializers(elem.Name()), + nil, + )) + } + elemList := tx.Factory().NewNodeList(elements) + return tx.Factory().UpdateBindingPattern(node.AsBindingPattern(), elemList) + } +} + +func (tx *DeclarationTransformer) transformImportEqualsDeclaration(decl *ast.ImportEqualsDeclaration) *ast.Node { + if !tx.resolver.IsDeclarationVisible(decl.AsNode()) { + return nil + } + if decl.ModuleReference.Kind == ast.KindExternalModuleReference { + // Rewrite external module names if necessary + specifier := ast.GetExternalModuleImportEqualsDeclarationExpression(decl.AsNode()) + return tx.Factory().UpdateImportEqualsDeclaration( + decl, + decl.Modifiers(), + decl.IsTypeOnly, + decl.Name(), + tx.Factory().UpdateExternalModuleReference(decl.ModuleReference.AsExternalModuleReference(), tx.rewriteModuleSpecifier(decl.AsNode(), specifier)), + ) + } else { + oldDiag := tx.state.getSymbolAccessibilityDiagnostic + tx.state.getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNode(decl.AsNode()) + tx.checkEntityNameVisibility(decl.ModuleReference, tx.enclosingDeclaration) + tx.state.getSymbolAccessibilityDiagnostic = oldDiag + return decl.AsNode() + } +} + +func (tx *DeclarationTransformer) transformImportDeclaration(decl *ast.ImportDeclaration) *ast.Node { + if decl.ImportClause == nil { + // import "mod" - possibly needed for side effects? (global interface patches, module augmentations, etc) + return tx.Factory().UpdateImportDeclaration( + decl, + decl.Modifiers(), + decl.ImportClause, + tx.rewriteModuleSpecifier(decl.AsNode(), decl.ModuleSpecifier), + tx.tryGetResolutionModeOverride(decl.Attributes), + ) + } + // The `importClause` visibility corresponds to the default's visibility. + var visibleDefaultBinding *ast.Node + if decl.ImportClause != nil && decl.ImportClause.Name() != nil && tx.resolver.IsDeclarationVisible(decl.ImportClause) { + visibleDefaultBinding = decl.ImportClause.Name() + } + if decl.ImportClause.AsImportClause().NamedBindings == nil { + // No named bindings (either namespace or list), meaning the import is just default or should be elided + if visibleDefaultBinding == nil { + return nil + } + return tx.Factory().UpdateImportDeclaration( + decl, + decl.Modifiers(), + tx.Factory().UpdateImportClause( + decl.ImportClause.AsImportClause(), + decl.ImportClause.AsImportClause().IsTypeOnly, + visibleDefaultBinding, + /*namedBindings*/ nil, + ), + tx.rewriteModuleSpecifier(decl.AsNode(), decl.ModuleSpecifier), + tx.tryGetResolutionModeOverride(decl.Attributes), + ) + } + if decl.ImportClause.AsImportClause().NamedBindings.Kind == ast.KindNamespaceImport { + // Namespace import (optionally with visible default) + var namedBindings *ast.Node + if tx.resolver.IsDeclarationVisible(decl.ImportClause.AsImportClause().NamedBindings) { + namedBindings = decl.ImportClause.AsImportClause().NamedBindings + } + if visibleDefaultBinding == nil && namedBindings == nil { + return nil + } + return tx.Factory().UpdateImportDeclaration( + decl, + decl.Modifiers(), + tx.Factory().UpdateImportClause( + decl.ImportClause.AsImportClause(), + decl.ImportClause.AsImportClause().IsTypeOnly, + visibleDefaultBinding, + namedBindings, + ), + tx.rewriteModuleSpecifier(decl.AsNode(), decl.ModuleSpecifier), + tx.tryGetResolutionModeOverride(decl.Attributes), + ) + } + // Named imports (optionally with visible default) + bindingList := core.Filter( + decl.ImportClause.AsImportClause().NamedBindings.AsNamedImports().Elements.Nodes, + func(b *ast.Node) bool { + return tx.resolver.IsDeclarationVisible(b) + }, + ) + if len(bindingList) > 0 || visibleDefaultBinding != nil { + var namedImports *ast.Node + if len(bindingList) > 0 { + namedImports = tx.Factory().UpdateNamedImports( + decl.ImportClause.AsImportClause().NamedBindings.AsNamedImports(), + tx.Factory().NewNodeList(bindingList), + ) + } + return tx.Factory().UpdateImportDeclaration( + decl, + decl.Modifiers(), + tx.Factory().UpdateImportClause( + decl.ImportClause.AsImportClause(), + decl.ImportClause.AsImportClause().IsTypeOnly, + visibleDefaultBinding, + namedImports, + ), + tx.rewriteModuleSpecifier(decl.AsNode(), decl.ModuleSpecifier), + tx.tryGetResolutionModeOverride(decl.Attributes), + ) + } + // Augmentation of export depends on import + if tx.resolver.IsImportRequiredByAugmentation(decl) { + // IsolatedDeclarations support + // if (isolatedDeclarations) { + // context.addDiagnostic(createDiagnosticForNode(decl, Diagnostics.Declaration_emit_for_this_file_requires_preserving_this_import_for_augmentations_This_is_not_supported_with_isolatedDeclarations)); + // } + return tx.Factory().UpdateImportDeclaration( + decl, + decl.Modifiers(), + /*importClause*/ nil, + tx.rewriteModuleSpecifier(decl.AsNode(), decl.ModuleSpecifier), + tx.tryGetResolutionModeOverride(decl.Attributes), + ) + } + // Nothing visible + return nil +} diff --git a/internal/declarations/util.go b/internal/declarations/util.go new file mode 100644 index 0000000000..df3d7e48e6 --- /dev/null +++ b/internal/declarations/util.go @@ -0,0 +1,238 @@ +package declarations + +import ( + "github.com/microsoft/typescript-go/internal/ast" + "github.com/microsoft/typescript-go/internal/core" + "github.com/microsoft/typescript-go/internal/printer" +) + +func needsScopeMarker(result *ast.Node) bool { + return !ast.IsAnyImportOrReExport(result) && !ast.IsExportAssignment(result) && !ast.HasSyntacticModifier(result, ast.ModifierFlagsExport) && !ast.IsAmbientModule(result) +} + +func canHaveLiteralInitializer(host DeclarationEmitHost, node *ast.Node) bool { + switch node.Kind { + case ast.KindPropertyDeclaration, + ast.KindPropertySignature: + return host.GetEffectiveDeclarationFlags(node, ast.ModifierFlagsPrivate) != 0 + case ast.KindParameter, + ast.KindVariableDeclaration: + return true + } + return false +} + +func canProduceDiagnostics(node *ast.Node) bool { + return ast.IsVariableDeclaration(node) || + ast.IsPropertyDeclaration(node) || + ast.IsPropertySignatureDeclaration(node) || + ast.IsBindingElement(node) || + ast.IsSetAccessorDeclaration(node) || + ast.IsGetAccessorDeclaration(node) || + ast.IsConstructSignatureDeclaration(node) || + ast.IsCallSignatureDeclaration(node) || + ast.IsMethodDeclaration(node) || + ast.IsMethodSignatureDeclaration(node) || + ast.IsFunctionDeclaration(node) || + ast.IsParameter(node) || + ast.IsTypeParameterDeclaration(node) || + ast.IsExpressionWithTypeArguments(node) || + ast.IsImportEqualsDeclaration(node) || + ast.IsTypeAliasDeclaration(node) || + ast.IsConstructorDeclaration(node) || + ast.IsIndexSignatureDeclaration(node) || + ast.IsPropertyAccessExpression(node) || + ast.IsElementAccessExpression(node) || + ast.IsBinaryExpression(node) // || // !!! TODO: JSDoc support + /* ast.IsJSDocTypeAlias(node); */ +} + +func hasInferredType(node *ast.Node) bool { + // Debug.type(node); // !!! + switch node.Kind { + case ast.KindParameter, + ast.KindPropertySignature, + ast.KindPropertyDeclaration, + ast.KindBindingElement, + ast.KindPropertyAccessExpression, + ast.KindElementAccessExpression, + ast.KindBinaryExpression, + ast.KindVariableDeclaration, + ast.KindExportAssignment, + ast.KindPropertyAssignment, + ast.KindShorthandPropertyAssignment, + ast.KindJSDocParameterTag, + ast.KindJSDocPropertyTag: + return true + default: + // assertType(node); // !!! + return false + } +} + +func isDeclarationAndNotVisible(emitContext *printer.EmitContext, resolver printer.EmitResolver, node *ast.Node) bool { + node = emitContext.ParseNode(node) + switch node.Kind { + case ast.KindFunctionDeclaration, + ast.KindModuleDeclaration, + ast.KindInterfaceDeclaration, + ast.KindClassDeclaration, + ast.KindTypeAliasDeclaration, + ast.KindEnumDeclaration: + return !resolver.IsDeclarationVisible(node) + // The following should be doing their own visibility checks based on filtering their members + case ast.KindVariableDeclaration: + return !getBindingNameVisible(resolver, node) + case ast.KindImportEqualsDeclaration, + ast.KindImportDeclaration, + ast.KindExportDeclaration, + ast.KindExportAssignment: + return false + case ast.KindClassStaticBlockDeclaration: + return true + } + return false +} + +func getBindingNameVisible(resolver printer.EmitResolver, elem *ast.Node) bool { + if ast.IsOmittedExpression(elem) { + return false + } + // TODO: parseArrayBindingElement _never_ parses out an OmittedExpression anymore, instead producing a nameless binding element + // Audit if OmittedExpression should be removed + if elem.Name() == nil { + return false + } + if ast.IsBindingPattern(elem.Name()) { + // If any child binding pattern element has been marked visible (usually by collect linked aliases), then this is visible + for _, elem := range elem.Name().AsBindingPattern().Elements.Nodes { + if getBindingNameVisible(resolver, elem) { + return true + } + } + return false + } else { + return resolver.IsDeclarationVisible(elem) + } +} + +func isEnclosingDeclaration(node *ast.Node) bool { + return ast.IsSourceFile(node) || + ast.IsTypeAliasDeclaration(node) || + ast.IsModuleDeclaration(node) || + ast.IsClassDeclaration(node) || + ast.IsInterfaceDeclaration(node) || + ast.IsFunctionLike(node) || + ast.IsIndexSignatureDeclaration(node) || + ast.IsMappedTypeNode(node) +} + +func isAlwaysType(node *ast.Node) bool { + if node.Kind == ast.KindInterfaceDeclaration { + return true + } + return false +} + +func maskModifierFlags(host DeclarationEmitHost, node *ast.Node, modifierMask ast.ModifierFlags, modifierAdditions ast.ModifierFlags) ast.ModifierFlags { + flags := host.GetEffectiveDeclarationFlags(node, modifierMask) | modifierAdditions + if flags&ast.ModifierFlagsDefault != 0 && (flags&ast.ModifierFlagsExport == 0) { + // A non-exported default is a nonsequitor - we usually try to remove all export modifiers + // from statements in ambient declarations; but a default export must retain its export modifier to be syntactically valid + flags ^= ast.ModifierFlagsExport + } + if flags&ast.ModifierFlagsDefault != 0 && flags&ast.ModifierFlagsAmbient != 0 { + flags ^= ast.ModifierFlagsAmbient // `declare` is never required alongside `default` (and would be an error if printed) + } + return flags +} + +func unwrapParenthesizedExpression(o *ast.Node) *ast.Node { + for o.Kind == ast.KindParenthesizedExpression { + o = o.Expression() + } + return o +} + +func isPrimitiveLiteralValue(node *ast.Node, includeBigInt bool) bool { + // !!! Debug.type(node); + switch node.Kind { + case ast.KindTrueKeyword, + ast.KindFalseKeyword, + ast.KindNumericLiteral, + ast.KindStringLiteral, + ast.KindNoSubstitutionTemplateLiteral: + return true + case ast.KindBigIntLiteral: + return includeBigInt + case ast.KindPrefixUnaryExpression: + if node.AsPrefixUnaryExpression().Operator == ast.KindMinusToken { + return ast.IsNumericLiteral(node.AsPrefixUnaryExpression().Operand) || (includeBigInt && ast.IsBigIntLiteral(node.AsPrefixUnaryExpression().Operand)) + } + if node.AsPrefixUnaryExpression().Operator == ast.KindPlusToken { + return ast.IsNumericLiteral(node.AsPrefixUnaryExpression().Operand) + } + return false + default: + // !!! assertType(node); + return false + } +} + +func isPrivateMethodTypeParameter(host DeclarationEmitHost, node *ast.TypeParameterDeclaration) bool { + return node.AsNode().Parent.Kind == ast.KindMethodDeclaration && host.GetEffectiveDeclarationFlags(node.AsNode().Parent, ast.ModifierFlagsPrivate) != 0 +} + +// If the ExpandoFunctionDeclaration have multiple overloads, then we only need to emit properties for the last one. +func shouldEmitFunctionProperties(input *ast.FunctionDeclaration) bool { + if input.Body != nil { // if it has an implementation, it must be the last one + return true + } + + overloadSignatures := core.Filter(input.Symbol.Declarations, func(decl *ast.Node) bool { + return ast.IsFunctionDeclaration(decl) + }) + + return len(overloadSignatures) == 0 || overloadSignatures[len(overloadSignatures)-1] == input.AsNode() +} + +func getFirstConstructorWithBody(node *ast.Node) *ast.Node { + for _, member := range node.Members() { + if ast.IsConstructorDeclaration(member) && ast.NodeIsPresent(member.Body()) { + return member + } + } + return nil +} + +func getEffectiveBaseTypeNode(node *ast.Node) *ast.Node { + baseType := getClassExtendsHeritageElement(node) + // !!! TODO: JSDoc support + // if (baseType && isInJSFile(node)) { + // // Prefer an @augments tag because it may have type parameters. + // const tag = getJSDocAugmentsTag(node); + // if (tag) { + // return tag.class; + // } + // } + return baseType +} + +func getClassExtendsHeritageElement(node *ast.Node) *ast.Node { + heritageClause := ast.GetHeritageClause(node, ast.KindExtendsKeyword) + if heritageClause != nil && len(heritageClause.AsHeritageClause().Types.Nodes) > 0 { + return heritageClause.AsHeritageClause().Types.Nodes[0] + } + return nil +} + +func isScopeMarker(node *ast.Node) bool { + return ast.IsExportAssignment(node) || ast.IsExportDeclaration(node) +} + +func hasScopeMarker(statements *ast.StatementList) bool { + if statements == nil { + return false + } + return core.Some(statements.Nodes, isScopeMarker) +} diff --git a/internal/execute/tsc.go b/internal/execute/tsc.go index be7d06f48d..33e4fa8f82 100644 --- a/internal/execute/tsc.go +++ b/internal/execute/tsc.go @@ -216,10 +216,8 @@ func compileAndEmit(sys System, program *compiler.Program, reportDiagnostic diag if len(diagnostics) == 0 { diagnostics = append(diagnostics, program.GetSemanticDiagnostics(nil)...) } - // TODO: declaration diagnostics - if len(diagnostics) == 0 && options.NoEmit == core.TSTrue && (options.Declaration.IsTrue() && options.Composite.IsTrue()) { - return nil, nil, ExitStatusNotImplemented - // addRange(allDiagnostics, program.getDeclarationDiagnostics(/*sourceFile*/ undefined, cancellationToken)); + if len(diagnostics) == 0 && options.GetEmitDeclarations() { + diagnostics = append(diagnostics, program.GetDeclarationDiagnostics(nil)...) } emitResult := &compiler.EmitResult{EmitSkipped: true, Diagnostics: []*ast.Diagnostic{}} diff --git a/internal/modulespecifiers/compare.go b/internal/modulespecifiers/compare.go new file mode 100644 index 0000000000..bcb6a27141 --- /dev/null +++ b/internal/modulespecifiers/compare.go @@ -0,0 +1,13 @@ +package modulespecifiers + +import ( + "strings" +) + +func CountPathComponents(path string) int { + initial := 0 + if strings.HasPrefix(path, "./") { + initial = 2 + } + return strings.Count(path[initial:], "/") +} diff --git a/internal/modulespecifiers/specifiers.go b/internal/modulespecifiers/specifiers.go new file mode 100644 index 0000000000..4ae905f93d --- /dev/null +++ b/internal/modulespecifiers/specifiers.go @@ -0,0 +1,18 @@ +package modulespecifiers + +import ( + "github.com/microsoft/typescript-go/internal/ast" + "github.com/microsoft/typescript-go/internal/core" +) + +func GetModuleSpecifiers( + moduleSymbol *ast.Symbol, + checker CheckerShape, + compilerOptions *core.CompilerOptions, + importingSourceFile *ast.SourceFile, + host any, + userPreferences *UserPreferences, + options *ModuleSpecifierOptions, +) []string { + return nil // !!! +} diff --git a/internal/modulespecifiers/types.go b/internal/modulespecifiers/types.go new file mode 100644 index 0000000000..88818469d3 --- /dev/null +++ b/internal/modulespecifiers/types.go @@ -0,0 +1,7 @@ +package modulespecifiers + +type CheckerShape interface{} + +type UserPreferences struct{} + +type ModuleSpecifierOptions struct{} diff --git a/internal/nodebuilder/types.go b/internal/nodebuilder/types.go new file mode 100644 index 0000000000..5b92bc869f --- /dev/null +++ b/internal/nodebuilder/types.go @@ -0,0 +1,79 @@ +// Exports interfaces and types defining the node builder - concrete implmentations are on top of the checker, but these types and interfaces are used by the emit resolver in the printer +package nodebuilder + +import "github.com/microsoft/typescript-go/internal/ast" + +// TODO: previously all symboltracker methods were optional, but now they're required. +type SymbolTracker interface { + GetModuleSpecifierGenerationHost() any // !!! + GetInnerSymbolTracker() SymbolTracker + + TrackSymbol(symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags) bool + ReportInaccessibleThisError() + ReportPrivateInBaseOfClassExpression(propertyName string) + ReportInaccessibleUniqueSymbolError() + ReportCyclicStructureError() + ReportLikelyUnsafeImportRequiredError(specifier string) + ReportTruncationError() + ReportNonlocalAugmentation(containingFile *ast.SourceFile, parentSymbol *ast.Symbol, augmentingSymbol *ast.Symbol) + ReportNonSerializableProperty(propertyName string) + + ReportInferenceFallback(node *ast.Node) + PushErrorFallbackNode(node *ast.Node) + PopErrorFallbackNode() +} + +// NOTE: If modifying this enum, must modify `TypeFormatFlags` too! +// dprint-ignore +type Flags int32 + +const ( + FlagsNone Flags = 0 + // Options + FlagsNoTruncation Flags = 1 << 0 + FlagsWriteArrayAsGenericType Flags = 1 << 1 + FlagsGenerateNamesForShadowedTypeParams Flags = 1 << 2 + FlagsUseStructuralFallback Flags = 1 << 3 + FlagsForbidIndexedAccessSymbolReferences Flags = 1 << 4 + FlagsWriteTypeArgumentsOfSignature Flags = 1 << 5 + FlagsUseFullyQualifiedType Flags = 1 << 6 + FlagsUseOnlyExternalAliasing Flags = 1 << 7 + FlagsSuppressAnyReturnType Flags = 1 << 8 + FlagsWriteTypeParametersInQualifiedName Flags = 1 << 9 + FlagsMultilineObjectLiterals Flags = 1 << 10 + FlagsWriteClassExpressionAsTypeLiteral Flags = 1 << 11 + FlagsUseTypeOfFunction Flags = 1 << 12 + FlagsOmitParameterModifiers Flags = 1 << 13 + FlagsUseAliasDefinedOutsideCurrentScope Flags = 1 << 14 + FlagsUseSingleQuotesForStringLiteralType Flags = 1 << 28 + FlagsNoTypeReduction Flags = 1 << 29 + FlagsOmitThisParameter Flags = 1 << 25 + // Error handling + FlagsAllowThisInObjectLiteral Flags = 1 << 15 + FlagsAllowQualifiedNameInPlaceOfIdentifier Flags = 1 << 16 + FlagsAllowAnonymousIdentifier Flags = 1 << 17 + FlagsAllowEmptyUnionOrIntersection Flags = 1 << 18 + FlagsAllowEmptyTuple Flags = 1 << 19 + FlagsAllowUniqueESSymbolType Flags = 1 << 20 + FlagsAllowEmptyIndexInfoType Flags = 1 << 21 + // Errors (cont.) + FlagsAllowNodeModulesRelativePaths Flags = 1 << 26 + FlagsIgnoreErrors Flags = FlagsAllowThisInObjectLiteral | FlagsAllowQualifiedNameInPlaceOfIdentifier | FlagsAllowAnonymousIdentifier | FlagsAllowEmptyUnionOrIntersection | FlagsAllowEmptyTuple | FlagsAllowEmptyIndexInfoType | FlagsAllowNodeModulesRelativePaths + // State + FlagsInObjectTypeLiteral Flags = 1 << 22 + FlagsInTypeAlias Flags = 1 << 23 + FlagsInInitialEntityName Flags = 1 << 24 +) + +/** @internal */ +// dprint-ignore + +type InternalFlags int32 + +const ( + InternalFlagsNone InternalFlags = 0 + InternalFlagsWriteComputedProps InternalFlags = 1 << 0 + InternalFlagsNoSyntacticPrinter InternalFlags = 1 << 1 + InternalFlagsDoNotIncludeSymbolChain InternalFlags = 1 << 2 + InternalFlagsAllowUnresolvedNames InternalFlags = 1 << 3 +) diff --git a/internal/printer/emitcontext.go b/internal/printer/emitcontext.go index a395b94e97..6d9cc7f8ae 100644 --- a/internal/printer/emitcontext.go +++ b/internal/printer/emitcontext.go @@ -633,6 +633,10 @@ func (c *EmitContext) NewRewriteRelativeImportExtensionsHelper(firstArgument *as // // NOTE: This is the equivalent to `setOriginalNode` in Strada. func (c *EmitContext) SetOriginal(node *ast.Node, original *ast.Node) { + c.SetOriginalEx(node, original, false) +} + +func (c *EmitContext) SetOriginalEx(node *ast.Node, original *ast.Node, allowOverwrite bool) { if original == nil { panic("Original cannot be nil.") } @@ -647,8 +651,10 @@ func (c *EmitContext) SetOriginal(node *ast.Node, original *ast.Node) { if emitNode := c.emitNodes.TryGet(original); emitNode != nil { c.emitNodes.Get(node).copyFrom(emitNode) } - } else if existing != original { + } else if !allowOverwrite && existing != original { panic("Original node already set.") + } else if allowOverwrite { + c.original[node] = original } } diff --git a/internal/printer/emitresolver.go b/internal/printer/emitresolver.go index b41172d895..52a4992608 100644 --- a/internal/printer/emitresolver.go +++ b/internal/printer/emitresolver.go @@ -3,8 +3,27 @@ package printer import ( "github.com/microsoft/typescript-go/internal/ast" "github.com/microsoft/typescript-go/internal/binder" + "github.com/microsoft/typescript-go/internal/evaluator" + "github.com/microsoft/typescript-go/internal/nodebuilder" ) +type SymbolAccessibility int32 + +const ( + SymbolAccessibilityAccessible SymbolAccessibility = iota + SymbolAccessibilityNotAccessible + SymbolAccessibilityCannotBeNamed + SymbolAccessibilityNotResolved +) + +type SymbolAccessibilityResult struct { + Accessibility SymbolAccessibility + AliasesToMakeVisible []*ast.Node // aliases that need to have this symbol visible + ErrorSymbolName string // Optional - symbol name that results in error + ErrorNode *ast.Node // Optional - node that results in error + ErrorModuleName string // Optional - If the symbol is not visible from module, module's name +} + type EmitResolver interface { binder.ReferenceResolver IsReferencedAliasDeclaration(node *ast.Node) bool @@ -12,4 +31,24 @@ type EmitResolver interface { IsTopLevelValueImportEqualsWithEntityName(node *ast.Node) bool MarkLinkedReferencesRecursively(file *ast.SourceFile) GetExternalModuleFileFromDeclaration(node *ast.Node) *ast.SourceFile + + // declaration emit checker functionality projections + IsSymbolAccessible(symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags, shouldComputeAliasToMarkVisible bool) SymbolAccessibilityResult + IsEntityNameVisible(entityName *ast.Node, enclosingDeclaration *ast.Node) SymbolAccessibilityResult // previously SymbolVisibilityResult in strada - ErrorModuleName never set + IsExpandoFunctionDeclaration(node *ast.Node) bool + IsLiteralConstDeclaration(node *ast.Node) bool + RequiresAddingImplicitUndefined(node *ast.Node, symbol *ast.Symbol, enclosingDeclaration *ast.Node) bool + IsDeclarationVisible(node *ast.Node) bool + IsImportRequiredByAugmentation(decl *ast.ImportDeclaration) bool + IsImplementationOfOverload(node *ast.SignatureDeclaration) bool + GetEnumMemberValue(node *ast.Node) evaluator.Result + IsLateBound(node *ast.Node) bool + IsOptionalParameter(node *ast.Node) bool + + // Node construction for declaration emit + CreateTypeOfDeclaration(emitContext *EmitContext, declaration *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node + CreateReturnTypeOfSignatureDeclaration(emitContext *EmitContext, signatureDeclaration *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node + CreateLiteralConstValue(emitContext *EmitContext, node *ast.Node, tracker nodebuilder.SymbolTracker) *ast.Node + CreateTypeOfExpression(emitContext *EmitContext, expression *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node + CreateLateBoundIndexSignatures(emitContext *EmitContext, container *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) []*ast.Node } diff --git a/internal/printer/printer.go b/internal/printer/printer.go index 7b65f042ac..a65e27ff16 100644 --- a/internal/printer/printer.go +++ b/internal/printer/printer.go @@ -1762,6 +1762,8 @@ func (p *Printer) emitTypeElement(node *ast.TypeElement) { p.emitSetAccessorDeclaration(node.AsSetAccessorDeclaration()) case ast.KindIndexSignature: p.emitIndexSignature(node.AsIndexSignatureDeclaration()) + case ast.KindNotEmittedTypeElement: + p.emitNotEmittedTypeElement(node.AsNotEmittedTypeElement()) default: panic(fmt.Sprintf("unexpected TypeElement: %v", node.Kind)) } @@ -3420,6 +3422,10 @@ func (p *Printer) emitNotEmittedStatement(node *ast.NotEmittedStatement) { p.exitNode(node.AsNode(), p.enterNode(node.AsNode())) } +func (p *Printer) emitNotEmittedTypeElement(node *ast.NotEmittedTypeElement) { + p.exitNode(node.AsNode(), p.enterNode(node.AsNode())) +} + // // Declarations // @@ -4920,7 +4926,8 @@ func (p *Printer) Write(node *ast.Node, sourceFile *ast.SourceFile, writer EmitT panic("not implemented") // Transformation nodes - // case ast.KindNotEmittedTypeElement: + case ast.KindNotEmittedTypeElement: + p.emitNotEmittedTypeElement(node.AsNotEmittedTypeElement()) default: switch { diff --git a/internal/printer/singlelinestringwriter.go b/internal/printer/singlelinestringwriter.go index 9f80c68a3f..60ed86ef53 100644 --- a/internal/printer/singlelinestringwriter.go +++ b/internal/printer/singlelinestringwriter.go @@ -2,13 +2,20 @@ package printer import ( "strings" + "sync" "unicode/utf8" "github.com/microsoft/typescript-go/internal/ast" "github.com/microsoft/typescript-go/internal/stringutil" ) -var SingleLineStringWriter EmitTextWriter = &singleLineStringWriter{} +var SingleLineStringWriterPool sync.Pool = sync.Pool{ + New: func() any { + return &singleLineStringWriter{} + }, +} + +var _ EmitTextWriter = &singleLineStringWriter{} type singleLineStringWriter struct { builder strings.Builder diff --git a/internal/testutil/tsbaseline/js_emit_baseline.go b/internal/testutil/tsbaseline/js_emit_baseline.go index ca048b8deb..b377233b72 100644 --- a/internal/testutil/tsbaseline/js_emit_baseline.go +++ b/internal/testutil/tsbaseline/js_emit_baseline.go @@ -1,6 +1,7 @@ package tsbaseline import ( + "slices" "strings" "testing" @@ -74,34 +75,33 @@ func DoJSEmitBaseline( jsCode.WriteString(fileOutput(file, harnessSettings)) } - // !!! Enable the following once .d.ts emit is implemented - ////if result.Dts.Size() > 0 { - //// jsCode += "\r\n\r\n" - //// for declFile := range result.Dts.Values() { - //// jsCode += fileOutput(declFile, harnessSettings) - //// } - ////} - //// - ////declFileContext := prepareDeclarationCompilationContext( - //// toBeCompiled, - //// otherFiles, - //// result, - //// harnessSettings, - //// options, - //// "", /*currentDirectory*/ - ////) - ////declFileCompilationResult := compileDeclarationFiles(t, declFileContext, result.Symlinks) - //// - ////if declFileCompilationResult != nil && len(declFileCompilationResult.declResult.Diagnostics) > 0 { - //// jsCode += "\r\n\r\n//// [DtsFileErrors]\r\n" - //// jsCode += "\r\n\r\n" - //// jsCode += getErrorBaseline( - //// t, - //// slices.Concat(tsConfigFiles, declFileCompilationResult.declInputFiles, declFileCompilationResult.declOtherFiles), - //// declFileCompilationResult.declResult.Diagnostics, - //// false, /*pretty*/ - //// ) - ////} else + if result.DTS.Size() > 0 { + jsCode.WriteString("\r\n\r\n") + for declFile := range result.DTS.Values() { + jsCode.WriteString(fileOutput(declFile, harnessSettings)) + } + } + + declFileContext := prepareDeclarationCompilationContext( + toBeCompiled, + otherFiles, + result, + harnessSettings, + options, + "", /*currentDirectory*/ + ) + declFileCompilationResult := compileDeclarationFiles(t, declFileContext, result.Symlinks) + + if declFileCompilationResult != nil && len(declFileCompilationResult.declResult.Diagnostics) > 0 { + jsCode.WriteString("\r\n\r\n//// [DtsFileErrors]\r\n") + jsCode.WriteString("\r\n\r\n") + jsCode.WriteString(getErrorBaseline( + t, + slices.Concat(tsConfigFiles, declFileCompilationResult.declInputFiles, declFileCompilationResult.declOtherFiles), + declFileCompilationResult.declResult.Diagnostics, + false, /*pretty*/ + )) + } // !!! Enable the following once noCheck is more comprehensive ////if !options.NoCheck.IsTrue() && !options.NoEmit.IsTrue() { diff --git a/internal/testutil/tsbaseline/type_symbol_baseline.go b/internal/testutil/tsbaseline/type_symbol_baseline.go index ef4336490d..0e87e8c6d8 100644 --- a/internal/testutil/tsbaseline/type_symbol_baseline.go +++ b/internal/testutil/tsbaseline/type_symbol_baseline.go @@ -11,6 +11,8 @@ import ( "github.com/microsoft/typescript-go/internal/checker" "github.com/microsoft/typescript-go/internal/compiler" "github.com/microsoft/typescript-go/internal/core" + "github.com/microsoft/typescript-go/internal/nodebuilder" + "github.com/microsoft/typescript-go/internal/printer" "github.com/microsoft/typescript-go/internal/scanner" "github.com/microsoft/typescript-go/internal/testutil" "github.com/microsoft/typescript-go/internal/testutil/baseline" @@ -370,21 +372,21 @@ func (walker *typeWriterWalker) writeTypeOrSymbol(node *ast.Node, isSymbolWalk b !isIntrinsicJsxTag(node, walker.currentSourceFile) { typeString = t.AsIntrinsicType().IntrinsicName() } else { - // !!! TODO: full type printing and underline when we have node builder - // const typeFormatFlags = ts.TypeFormatFlags.NoTruncation | ts.TypeFormatFlags.AllowUniqueESSymbolType | ts.TypeFormatFlags.GenerateNamesForShadowedTypeParams; - // let typeNode = this.checker.typeToTypeNode(type, node.parent, (typeFormatFlags & ts.TypeFormatFlags.NodeBuilderFlagsMask) | ts.NodeBuilderFlags.IgnoreErrors, ts.InternalNodeBuilderFlags.AllowUnresolvedNames)!; - // if (ts.isIdentifier(node) && ts.isTypeAliasDeclaration(node.parent) && node.parent.name === node && ts.isIdentifier(typeNode) && ts.idText(typeNode) === ts.idText(node)) { - // // for a complex type alias `type T = ...`, showing "T : T" isn't very helpful for type tests. When the type produced is the same as - // // the name of the type alias, recreate the type string without reusing the alias name - // typeNode = this.checker.typeToTypeNode(type, node.parent, ((typeFormatFlags | ts.TypeFormatFlags.InTypeAlias) & ts.TypeFormatFlags.NodeBuilderFlagsMask) | ts.NodeBuilderFlags.IgnoreErrors)!; - // } - - // const { printer, writer, underliner, reset } = createSyntheticNodeUnderliningPrinter(); - // printer.writeNode(ts.EmitHint.Unspecified, typeNode, this.currentSourceFile, writer); - // typeString = writer.getText(); - // underline = underliner.getText(); - // reset(); - typeString = fileChecker.TypeToString(t) + ctx := printer.NewEmitContext() + builder := checker.NewNodeBuilderAPI(fileChecker, ctx) + typeFormatFlags := checker.TypeFormatFlagsNoTruncation | checker.TypeFormatFlagsAllowUniqueESSymbolType | checker.TypeFormatFlagsGenerateNamesForShadowedTypeParams + typeNode := builder.TypeToTypeNode(t, node.Parent, nodebuilder.Flags(typeFormatFlags&checker.TypeFormatFlagsNodeBuilderFlagsMask)|nodebuilder.FlagsIgnoreErrors, nodebuilder.InternalFlagsAllowUnresolvedNames, nil) + if ast.IsIdentifier(node) && ast.IsTypeAliasDeclaration(node.Parent) && node.Parent.Name() == node && ast.IsIdentifier(typeNode) && typeNode.AsIdentifier().Text == node.AsIdentifier().Text { + // for a complex type alias `type T = ...`, showing "T : T" isn't very helpful for type tests. When the type produced is the same as + // the name of the type alias, recreate the type string without reusing the alias name + typeNode = builder.TypeToTypeNode(t, node.Parent, nodebuilder.Flags((typeFormatFlags|checker.TypeFormatFlagsInTypeAlias)&checker.TypeFormatFlagsNodeBuilderFlagsMask)|nodebuilder.FlagsIgnoreErrors, nodebuilder.InternalFlagsAllowUnresolvedNames, nil) + } + + // !!! TODO: port underline printer, memoize + writer := printer.NewTextWriter("") + printer := printer.NewPrinter(printer.PrinterOptions{RemoveComments: true}, printer.PrintHandlers{}, ctx) + printer.Write(typeNode, walker.currentSourceFile, writer, nil) + typeString = writer.String() } return &typeWriterResult{ line: line, diff --git a/internal/transformers/commonjsmodule.go b/internal/transformers/commonjsmodule.go index fcfa1773b2..16b0460e92 100644 --- a/internal/transformers/commonjsmodule.go +++ b/internal/transformers/commonjsmodule.go @@ -38,7 +38,7 @@ func NewCommonJSModuleTransformer(emitContext *printer.EmitContext, compilerOpti tx.assignmentPatternVisitor = emitContext.NewNodeVisitor(tx.visitAssignmentPattern) tx.languageVersion = compilerOptions.GetEmitScriptTarget() tx.moduleKind = compilerOptions.GetEmitModuleKind() - return tx.newTransformer(tx.visit, emitContext) + return tx.NewTransformer(tx.visit, emitContext) } // Pushes a new child node onto the ancestor tracking stack, returning the grandparent node to be restored later via `popNode`. diff --git a/internal/transformers/esmodule.go b/internal/transformers/esmodule.go index bbbc0bbce7..0fbd88fbf7 100644 --- a/internal/transformers/esmodule.go +++ b/internal/transformers/esmodule.go @@ -29,7 +29,7 @@ func NewESModuleTransformer(emitContext *printer.EmitContext, compilerOptions *c resolver = binder.NewReferenceResolver(compilerOptions, binder.ReferenceResolverHooks{}) } tx := &ESModuleTransformer{compilerOptions: compilerOptions, resolver: resolver, sourceFileMetaDataProvider: sourceFileMetaDataProvider} - return tx.newTransformer(tx.visit, emitContext) + return tx.NewTransformer(tx.visit, emitContext) } // Visits source elements that are not top-level or top-level nested statements. diff --git a/internal/transformers/impliedmodule.go b/internal/transformers/impliedmodule.go index b23018b8bd..011239bdb1 100644 --- a/internal/transformers/impliedmodule.go +++ b/internal/transformers/impliedmodule.go @@ -21,7 +21,7 @@ func NewImpliedModuleTransformer(emitContext *printer.EmitContext, compilerOptio resolver = binder.NewReferenceResolver(compilerOptions, binder.ReferenceResolverHooks{}) } tx := &ImpliedModuleTransformer{compilerOptions: compilerOptions, resolver: resolver, sourceFileMetaDataProvider: sourceFileMetaDataProvider} - return tx.newTransformer(tx.visit, emitContext) + return tx.NewTransformer(tx.visit, emitContext) } func (tx *ImpliedModuleTransformer) visit(node *ast.Node) *ast.Node { diff --git a/internal/transformers/importelision.go b/internal/transformers/importelision.go index 2ce400bdd3..57678a09d2 100644 --- a/internal/transformers/importelision.go +++ b/internal/transformers/importelision.go @@ -18,7 +18,7 @@ func NewImportElisionTransformer(emitContext *printer.EmitContext, compilerOptio panic("ImportElisionTransformer should not be used with VerbatimModuleSyntax") } tx := &ImportElisionTransformer{compilerOptions: compilerOptions, emitResolver: resolver} - return tx.newTransformer(tx.visit, emitContext) + return tx.NewTransformer(tx.visit, emitContext) } func (tx *ImportElisionTransformer) visit(node *ast.Node) *ast.Node { diff --git a/internal/transformers/modifiervisitor.go b/internal/transformers/modifiervisitor.go index 996b149722..78b5fa80ff 100644 --- a/internal/transformers/modifiervisitor.go +++ b/internal/transformers/modifiervisitor.go @@ -23,6 +23,6 @@ func extractModifiers(emitContext *printer.EmitContext, modifiers *ast.ModifierL return nil } tx := modifierVisitor{AllowedModifiers: allowed} - tx.newTransformer(tx.visit, emitContext) + tx.NewTransformer(tx.visit, emitContext) return tx.visitor.VisitModifiers(modifiers) } diff --git a/internal/transformers/runtimesyntax.go b/internal/transformers/runtimesyntax.go index 9a3703329c..3464d27d15 100644 --- a/internal/transformers/runtimesyntax.go +++ b/internal/transformers/runtimesyntax.go @@ -33,7 +33,7 @@ type RuntimeSyntaxTransformer struct { func NewRuntimeSyntaxTransformer(emitContext *printer.EmitContext, compilerOptions *core.CompilerOptions, resolver binder.ReferenceResolver) *Transformer { tx := &RuntimeSyntaxTransformer{compilerOptions: compilerOptions, resolver: resolver} - return tx.newTransformer(tx.visit, emitContext) + return tx.NewTransformer(tx.visit, emitContext) } // Pushes a new child node onto the ancestor tracking stack, returning the grandparent node to be restored later via `popNode`. diff --git a/internal/transformers/transformer.go b/internal/transformers/transformer.go index f321b779e5..5c3d636f9e 100644 --- a/internal/transformers/transformer.go +++ b/internal/transformers/transformer.go @@ -11,7 +11,7 @@ type Transformer struct { visitor *ast.NodeVisitor } -func (tx *Transformer) newTransformer(visit func(node *ast.Node) *ast.Node, emitContext *printer.EmitContext) *Transformer { +func (tx *Transformer) NewTransformer(visit func(node *ast.Node) *ast.Node, emitContext *printer.EmitContext) *Transformer { if tx.emitContext != nil { panic("Transformer already initialized") } @@ -24,6 +24,18 @@ func (tx *Transformer) newTransformer(visit func(node *ast.Node) *ast.Node, emit return tx } +func (tx *Transformer) EmitContext() *printer.EmitContext { + return tx.emitContext +} + +func (tx *Transformer) Visitor() *ast.NodeVisitor { + return tx.visitor +} + +func (tx *Transformer) Factory() *ast.NodeFactory { + return tx.factory +} + func (tx *Transformer) TransformSourceFile(file *ast.SourceFile) *ast.SourceFile { return tx.visitor.VisitSourceFile(file) } diff --git a/internal/transformers/typeeraser.go b/internal/transformers/typeeraser.go index aa5cfcf117..6594145c8e 100644 --- a/internal/transformers/typeeraser.go +++ b/internal/transformers/typeeraser.go @@ -15,7 +15,7 @@ type TypeEraserTransformer struct { func NewTypeEraserTransformer(emitContext *printer.EmitContext, compilerOptions *core.CompilerOptions) *Transformer { tx := &TypeEraserTransformer{compilerOptions: compilerOptions} - return tx.newTransformer(tx.visit, emitContext) + return tx.NewTransformer(tx.visit, emitContext) } // Pushes a new child node onto the ancestor tracking stack, returning the grandparent node to be restored later via `popNode`.