npm ci
npx gulp local
npx gulp watch-tsc
node inspect built/local/tsc.js tests/cases/compiler/2dArrays.ts
npx gulp runtests -r spec -t 2dArrays
npx mocha -R scripts/failed-tests -O "reporter=spec" -g "2dArrays" --colors -t 40000 built/local/run.js
# update basline snapshots
npx gulp baseline-accept
git bisect start main v4.3.5 --
git bisect run bash test.sh
#! /bin/bash
npm i && git restore package-lock.json && npx gulp runtests --no-lint -r spec -t " autoCloseTagCrash" || exit 1
tests/cases/fourslash/autoCloseTagCrash.ts
/// <reference path='fourslash.ts' />
// @Filename : /0.tsx
////const x = <li>
//// <div>
//// <div>/*0*/
//// </div>
////</li>
verify . jsxClosingTag ( {
0 : { newText : "</div>" } ,
} ) ;
/src
/tsc/tsc.ts
/executeCommandLine/executeCommondLine.ts
/testRunner (becomes run.js and loaded to mocha when `gulp runtests`)
/runner.ts
/unittests/ (usual mocha tests)
/compilerRunner.ts
(programmatically generate "describe/it" based on /tests/cases files and
compare the output with /tests/baseline/references)
/compiler
/types.ts (core interfaces e.g. SourceFile, Program, Node, Type, etc...)
/program.ts
/parser.ts (9K LOC)
/checker.ts (40K LOC)
/emitter.ts (5K LOC)
/binder.ts
/tests
/cases
/baseline/references
ts.executeCommandLine, executeCommandLineWorker =>
performCompilation =>
createCompilerHostWorker
createProgram (construct `Program` instance) =>
processRootFile, processSourceFile, findSourceFile, findSourceFileWorker, host.getSourceFile =>
createSourceFile => Parser.parseSourceFile
emitFilesAndReportErrorsAndGetExitStatus => emitFilesAndReportErrors =>
program.getGlobalDiagnostics => getDiagnosticsProducingTypeChecker =>
createTypeChecker =>
initializeTypeChecker =>
bindSourceFile for each source
program.getSyntacticDiagnostics ... sourceFile.parseDiagnostics
program.getSemanticDiagnostics ... getBindAndCheckDiagnosticsForFileNoCache => typeChecker.getDiagnostics
program.emit ... emitFiles
parseSourceFile [parser.ts] => TODO
bindSourceFile [binder.ts] => TODO
getDiagnostics [checker.ts] ... checkSourceFileWorker =>
checkSourceElement for each statement => checkSourceElementWorker (branches by Node.kind)
emitFiles [emitter.ts] => TODO
control flow analysis and type narrowing
examples
tests/cases/conformance/expressions/typeGuards
tests/cases/conformance/types/union/discriminatedUnionTypes1.ts
tests/cases/compiler/narrow[XXX].ts
(e.g. narrowTypeByInstanceof.ts
)
baseline snapshots include inferred types e.g. in tests/baselines/reference/narrowTypeByInstanceof.types
TODO: how is .types
file generated?
types
typeof
instanceof
discriminated union
bindSourceFile => ??
CompilerBaselineRunner.runSuite =>
new CompilerTest => Harness.Compiler.compileFiles => compiler.compileFiles =>
ts.createProgram (parser)
Program.emit
ts.createCompilerDiagnostic (binder, checker)
new CompilationResult
CompilerTest.verifyTypesAndSymbols =>
Compiler.doTypeAndSymbolBaseline =>
TypeWriterWalker.getTypes => writeTypeOrSymbol =>
TypeChecker.getTypeAtLocation => ...
TypeChecker.getSymbolAtLocation => ...
#
# data structure
#
CompilerTest
CompilationResult
ts.Program
createTypeChecker =>
checker: TypeChekcer = { ... }
initializeTypeChecker => bindSourceFile => ...
return checker
TypeChecker.getDiagnostics => getDiagnosticsWorker =>
checkSourceFileWithEagerDiagnostics => checkSourceFile => checkSourceFileWorker =>
checkGrammarSourceFile => ...
checkSourceElement => checkSourceElementWorker =>
checkDeferredNodes => ??
checkSourceElementWorker =>
check `isReachableFlowNode` for `Diagnostics.Unreachable_code_detected` => ??
# switch by `SyntaxKind` e.g.
# if "return <expr>"
checkReturnStatement =>
getContainingFunctionOrClassStaticBlock
getSignatureFromDeclaration => ??
getReturnTypeOfSignature => ??
checkExpressionCached => checkExpression => ...
checkTypeAssignableToAndOptionallyElaborate => ??
checkExpression (return `Type`) =>
checkExpressionWorker =>
# switch by SyntaxKind e.g.
# if Identifier
checkIdentifier =>
# if BinaryExpression
checkBinaryExpression =>
instantiateTypeWithSingleGenericCallSignature => ??
checkCallExpression =>
getResolvedSignature => resolveSignature => resolveCallExpression =>
checkExpression (get type of function) => ...
getSignaturesOfType (union from function type overload?)
resolveCall =>
reorderCandidates
chooseOverload =>
checkTypeArguments =>
# for each type parameter
getConstraintOfTypeParameter
createTypeMapper
checkTypeAssignableTo => ...
createInferenceContext
inferTypeArguments =>
# infer type arguments based on "contextual" return type
getContextualType => ...
getReturnTypeOfSignature => ...
# for each argument
checkExpressionWithContextualType
inferTypes => inferFromTypes => ??
getInferredTypes => ??
getSignatureInstantiation =>
getSignatureInstantiationWithoutFillingInTypeArguments => ..
getSignatureApplicabilityError => ??
# report error if signature not found
getReturnTypeOfSignature =>
instantiateType => instantiateTypeWithAlias => instantiateTypeWorker =>
# switch by TypeFlags e.g.
# if TypeParameter
getMappedType => ??
# if Object (includes mapped type, interface, etc...)
getObjectTypeInstantiation => ??
checkIdentifier =>
getResolvedSymbol => resolveName(.. SymbolFlags.Value | SymbolFlags.ExportValue ..) =>
resolveNameHelper =>
# loop to traverse back AST ancestors.
# if `Node.locals` (TODO: binder sets up `locals`)
getSymbol(SymbolTable, __String, SymbolFlags) (SymbolFlags filters symbols by its semantics e.g. whether `Value` or `Type`)
getExportSymbolOfValueSymbolIfExported => ...
getNarrowedTypeOfSymbol => getTypeOfSymbol (plus additional hack for early destructuring?)
# magic of control flow narrowing
getControlFlowContainer
getFlowTypeOfReference =>
# main recursive call + loop for traversing flow
getTypeAtFlowNode =>
# switch by FlowFlags e.g.
# if FlowFlags.BranchLabel
getTypeAtFlowBranchLabel =>
for each antecedent of FlowLabel
getTypeAtFlowNode(antecedent)
getUnionOrEvolvingArrayType => ...
# if FlowFlags.Condition (i.e. TrueCondition or FalseCondition)
getTypeAtFlowCondition =>
getTypeAtFlowNode (recursively for antecedent)
narrowType(.. FlowCondition.node ..) =>
# switch by SyntaxKind e.g.
# if SyntaxKind.Identifier, PropertyAccessExpression, etc...
narrowTypeByTruthiness (e.g. TypeFacts.Truthy if FlowFlags.TrueCondition) =>
getAdjustedTypeWithFacts =>
getTypeWithFacts =>
filterType (e.g. with filter condition of getTypeFacts and TypeFacts.Truthy) =>
if TypeFlags.Union
filter UnionType.types
getDiscriminantPropertyAccess => getCandidateDiscriminantPropertyAccess =>
# e.g. if PropertyAccessExpression then check isMatchingReference(reference, PropertyAccessExpression.expression)
narrowTypeByDiscriminant =>
getAccessedPropertyName
getTypeOfPropertyOfType
filterType => ...
# if unary op with ExclamationToken (i.e. negation expression "! <expr>")
narrowType (with flipped condition) => ...
#
# binder
#
bindSourceFile => bind(Node) =>
setParent
bindWorker(Node) =>
# switch SyntaxKind e.g.
# if VariableDeclaration
bindVariableDeclarationOrBindingElement =>
# if isBlockOrCatchScoped
bindBlockScopedDeclaration => declareSymbol(blockScopeContainer.locals, ...) =>
# validate re-declaration
getDeclarationName
createSymbol
SymbolTable.set
# if isParameterDeclaration
declareSymbolAndAddToSymbolTable => ...
# if FunctionDeclaration
bindFunctionDeclaration => bindBlockScopedDeclaration => ...
getContainerFlags(Node)
# if container
bindContainer =>
# update local state e.g. container, blockScopeContainer, currentFlow, etc...
bindChildren => ...
# otherwise
bindChildren =>
# switch SyntaxKind e.g.
# "if" <expression> "then" <thenStatement> "else" <elseStatement>
bindIfStatement =>
bindCondition (<expression>, "then", "else") =>
doWithConditionalBranches =>
currentTrueTarget = "then" label
currentFalseTarget = "else" label
bind (<expression>) => ...
# by recursing `bind`, it can mutate state e.g. `currentTrueTarget`, `currentFalseTarget`
# based on the structure of <expression> e.g. `! <sub-expr>` will swap these two
# if not isLogicalExpression, etc... (in these special cases, similar routine is done in `bindLogicalLikeExpression`)
createFlowCondition => initFlowNode (if isNarrowingExpression)
addAntecedent
# for <thenStatement>
finishFlowLabel ("then")
bind (<thenStatement>)
addAntecedent ("postIf" to "current") => only add antescendent if not `FlowFlags.Unreachable`
# for <elseStatement>
finishFlowLabel ("else")
bind (<elseStatement>)
finishFlowLabel ("postif")
# if ReturnStatement or ThrowStatement
bindReturnOrThrow => set currentFlow = unreachableFlow
# <op> <expr>
bindPrefixUnaryExpressionFlow =>
# if "! <expr>" then temporary swap `currentTrueTarget` and `currentFalseTarget`
bindEachChild(node)
# <lhs> <op> <rhs>
bindBinaryExpressionFlow (cf. createBindBinaryExpressionFlow) =>
onEnter => bindLogicalLikeExpression => ...
# fallback to simply recurse children AST
bindEachChild => bind => ...
#
# data structure
#
Node
SyntaxKind
FlowNode
locals SymbolTable
Symbol
Declaration[]
SymbolFlags (e.g. separation of `Value` and `Type`)
TypeNode
CallExpression
typeArguments NodeArray<TypeNode>
Signature
instantiations Map<string, Signature>
target Signature
TypeMapper ??
resolvedReturnType
ObjectType
ObjectFlags (e.g. interface, mapped type, etc...)
#
# data structure (binder)
#
ContainerFlags (e.g. HasLocals, IsControlFlowContainer)
FlowFlags
FlowLabel < FlowNodeBase
antecedents FlowNode[]
FlowCondition < FlowNodeBase
node: Expression
antecedent: FlowNode
FlowType
tmp (assignment during control flow narrowing)
bindBinaryExpressionFlow =>
onExit =>
# if isAssignmentOperator (e.g. "=", "+=", etc..)
bindAssignmentTargetFlow(lhs) =>
# e.g. if identifier
createFlowMutation(FlowFlags.Assignment, ...)
FlowAssignment
node: Expression
antecedent: FlowNode
getTypeAtFlowNode =>
# if FlowFlags.Assignment
getTypeAtFlowAssignment =>
getAssignmentReducedType =>
getInitialOrAssignedType =>
getAssignedType =>
# if BinaryExpression (e.g. "=")
getAssignedTypeOfBinaryExpression =>
getTypeOfExpression(rhs) => checkExpression => ...
tmp (object literal with getter as generic parameter)