diff --git a/internal/ast/ast.go b/internal/ast/ast.go index 72ee46c32f..abee405be0 100644 --- a/internal/ast/ast.go +++ b/internal/ast/ast.go @@ -631,6 +631,15 @@ func (n *Node) Statements() []*Node { return nil } +func (n *Node) CanHaveStatements() bool { + switch n.Kind { + case KindSourceFile, KindBlock, KindModuleBlock, KindCaseClause, KindDefaultClause: + return true + default: + return false + } +} + func (n *Node) ModifierFlags() ModifierFlags { modifiers := n.Modifiers() if modifiers != nil { diff --git a/internal/ast/nodeflags.go b/internal/ast/nodeflags.go index 2feb4f2a52..3261fc277c 100644 --- a/internal/ast/nodeflags.go +++ b/internal/ast/nodeflags.go @@ -42,6 +42,7 @@ const ( NodeFlagsInWithStatement NodeFlags = 1 << 24 // If any ancestor of node was the `statement` of a WithStatement (not the `expression`) NodeFlagsJsonFile NodeFlags = 1 << 25 // If node was parsed in a Json NodeFlagsDeprecated NodeFlags = 1 << 26 // If has '@deprecated' JSDoc tag + NodeFlagsUnreachable NodeFlags = 1 << 27 // If node is unreachable according to the binder NodeFlagsBlockScoped = NodeFlagsLet | NodeFlagsConst | NodeFlagsUsing NodeFlagsConstant = NodeFlagsConst | NodeFlagsUsing diff --git a/internal/ast/utilities.go b/internal/ast/utilities.go index b381ac436d..72ba07ee60 100644 --- a/internal/ast/utilities.go +++ b/internal/ast/utilities.go @@ -2336,6 +2336,12 @@ func getModuleInstanceStateForAliasTarget(node *Node, ancestors []*Node, visited return ModuleInstanceStateInstantiated } +func IsInstantiatedModule(node *Node, preserveConstEnums bool) bool { + moduleState := GetModuleInstanceState(node) + return moduleState == ModuleInstanceStateInstantiated || + (preserveConstEnums && moduleState == ModuleInstanceStateConstEnumOnly) +} + func NodeHasName(statement *Node, id *Node) bool { name := statement.Name() if name != nil { @@ -3832,3 +3838,21 @@ func GetFirstConstructorWithBody(node *Node) *Node { } return nil } + +// Returns true for nodes that are considered executable for the purposes of unreachable code detection. +func IsPotentiallyExecutableNode(node *Node) bool { + if KindFirstStatement <= node.Kind && node.Kind <= KindLastStatement { + if IsVariableStatement(node) { + declarationList := node.AsVariableStatement().DeclarationList + if GetCombinedNodeFlags(declarationList)&NodeFlagsBlockScoped != 0 { + return true + } + declarations := declarationList.AsVariableDeclarationList().Declarations.Nodes + return core.Some(declarations, func(d *Node) bool { + return d.Initializer() != nil + }) + } + return true + } + return IsClassDeclaration(node) || IsEnumDeclaration(node) || IsModuleDeclaration(node) +} diff --git a/internal/binder/binder.go b/internal/binder/binder.go index e7409cea4e..e2b41a7c48 100644 --- a/internal/binder/binder.go +++ b/internal/binder/binder.go @@ -42,10 +42,9 @@ const ( ) type Binder struct { - file *ast.SourceFile - bindFunc func(*ast.Node) bool - unreachableFlow *ast.FlowNode - reportedUnreachableFlow *ast.FlowNode + file *ast.SourceFile + bindFunc func(*ast.Node) bool + unreachableFlow *ast.FlowNode container *ast.Node thisContainer *ast.Node @@ -122,7 +121,6 @@ func bindSourceFile(file *ast.SourceFile) { b.file = file b.inStrictMode = b.options().BindInStrictMode && !file.IsDeclarationFile || ast.IsExternalModule(file) b.unreachableFlow = b.newFlowNode(ast.FlowFlagsUnreachable) - b.reportedUnreachableFlow = b.newFlowNode(ast.FlowFlagsUnreachable) b.bind(file.AsNode()) file.SymbolCount = b.symbolCount file.ClassifiableNames = b.classifiableNames @@ -1535,18 +1533,25 @@ func (b *Binder) bindChildren(node *ast.Node) { // Most nodes aren't valid in an assignment pattern, so we clear the value here // and set it before we descend into nodes that could actually be part of an assignment pattern. b.inAssignmentPattern = false - if b.checkUnreachable(node) { + + if b.currentFlow == b.unreachableFlow { + if flowNodeData := node.FlowNodeData(); flowNodeData != nil { + flowNodeData.FlowNode = nil + } + if ast.IsPotentiallyExecutableNode(node) { + node.Flags |= ast.NodeFlagsUnreachable + } b.bindEachChild(node) b.inAssignmentPattern = saveInAssignmentPattern return } - kind := node.Kind - if kind >= ast.KindFirstStatement && kind <= ast.KindLastStatement && (b.options().AllowUnreachableCode != core.TSTrue || kind == ast.KindReturnStatement) { - hasFlowNodeData := node.FlowNodeData() - if hasFlowNodeData != nil { - hasFlowNodeData.FlowNode = b.currentFlow + + if ast.KindFirstStatement <= node.Kind && node.Kind <= ast.KindLastStatement { + if flowNodeData := node.FlowNodeData(); flowNodeData != nil { + flowNodeData.FlowNode = b.currentFlow } } + switch node.Kind { case ast.KindWhileStatement: b.bindWhileStatement(node) @@ -1657,94 +1662,6 @@ func (b *Binder) bindEachStatementFunctionsFirst(statements *ast.NodeList) { } } -func (b *Binder) checkUnreachable(node *ast.Node) bool { - if b.currentFlow.Flags&ast.FlowFlagsUnreachable == 0 { - return false - } - if b.currentFlow == b.unreachableFlow { - // report errors on all statements except empty ones - // report errors on class declarations - // report errors on enums with preserved emit - // report errors on instantiated modules - reportError := ast.IsStatementButNotDeclaration(node) && !ast.IsEmptyStatement(node) || - ast.IsClassDeclaration(node) || - isEnumDeclarationWithPreservedEmit(node, b.options()) || - ast.IsModuleDeclaration(node) && b.shouldReportErrorOnModuleDeclaration(node) - if reportError { - b.currentFlow = b.reportedUnreachableFlow - if b.options().AllowUnreachableCode != core.TSTrue { - // unreachable code is reported if - // - user has explicitly asked about it AND - // - statement is in not ambient context (statements in ambient context is already an error - // so we should not report extras) AND - // - node is not variable statement OR - // - node is block scoped variable statement OR - // - node is not block scoped variable statement and at least one variable declaration has initializer - // Rationale: we don't want to report errors on non-initialized var's since they are hoisted - // On the other side we do want to report errors on non-initialized 'lets' because of TDZ - isError := unreachableCodeIsError(b.options()) && node.Flags&ast.NodeFlagsAmbient == 0 && (!ast.IsVariableStatement(node) || - ast.GetCombinedNodeFlags(node.AsVariableStatement().DeclarationList)&ast.NodeFlagsBlockScoped != 0 || - core.Some(node.AsVariableStatement().DeclarationList.AsVariableDeclarationList().Declarations.Nodes, func(d *ast.Node) bool { - return d.Initializer() != nil - })) - b.errorOnEachUnreachableRange(node, isError) - } - } - } - return true -} - -func (b *Binder) shouldReportErrorOnModuleDeclaration(node *ast.Node) bool { - instanceState := ast.GetModuleInstanceState(node) - return instanceState == ast.ModuleInstanceStateInstantiated || (instanceState == ast.ModuleInstanceStateConstEnumOnly && b.options().ShouldPreserveConstEnums) -} - -func (b *Binder) errorOnEachUnreachableRange(node *ast.Node, isError bool) { - if b.isExecutableStatement(node) && ast.IsBlock(node.Parent) { - statements := node.Parent.Statements() - index := slices.Index(statements, node) - var first, last *ast.Node - for _, s := range statements[index:] { - if b.isExecutableStatement(s) { - if first == nil { - first = s - } - last = s - } else if first != nil { - b.errorOrSuggestionOnRange(isError, first, last, diagnostics.Unreachable_code_detected) - first = nil - } - } - if first != nil { - b.errorOrSuggestionOnRange(isError, first, last, diagnostics.Unreachable_code_detected) - } - } else { - b.errorOrSuggestionOnNode(isError, node, diagnostics.Unreachable_code_detected) - } -} - -// As opposed to a pure declaration like an `interface` -func (b *Binder) isExecutableStatement(s *ast.Node) bool { - // Don't remove statements that can validly be used before they appear. - return !ast.IsFunctionDeclaration(s) && !b.isPurelyTypeDeclaration(s) && !(ast.IsVariableStatement(s) && ast.GetCombinedNodeFlags(s)&ast.NodeFlagsBlockScoped == 0 && - core.Some(s.AsVariableStatement().DeclarationList.AsVariableDeclarationList().Declarations.Nodes, func(d *ast.Node) bool { - return d.Initializer() == nil - })) -} - -func (b *Binder) isPurelyTypeDeclaration(s *ast.Node) bool { - switch s.Kind { - case ast.KindInterfaceDeclaration, ast.KindTypeAliasDeclaration, ast.KindJSTypeAliasDeclaration: - return true - case ast.KindModuleDeclaration: - return ast.GetModuleInstanceState(s) != ast.ModuleInstanceStateInstantiated - case ast.KindEnumDeclaration: - return !isEnumDeclarationWithPreservedEmit(s, b.options()) - default: - return false - } -} - func (b *Binder) setContinueTarget(node *ast.Node, target *ast.FlowLabel) *ast.FlowLabel { label := b.activeLabelList for label != nil && node.Parent.Kind == ast.KindLabeledStatement { @@ -2131,8 +2048,9 @@ func (b *Binder) bindLabeledStatement(node *ast.Node) { } b.bind(stmt.Label) b.bind(stmt.Statement) - if !b.activeLabelList.referenced && b.options().AllowUnusedLabels != core.TSTrue { - b.errorOrSuggestionOnNode(unusedLabelIsError(b.options()), stmt.Label, diagnostics.Unused_label) + if !b.activeLabelList.referenced { + // Mark the label as unused; the checker will decide whether to report it + stmt.Label.Flags |= ast.NodeFlagsUnreachable } b.activeLabelList = b.activeLabelList.next b.addAntecedent(postStatementLabel, b.currentFlow) @@ -2454,10 +2372,6 @@ func (b *Binder) bindInitializer(node *ast.Node) { b.currentFlow = b.finishFlowLabel(exitFlow) } -func isEnumDeclarationWithPreservedEmit(node *ast.Node, options core.SourceFileAffectingCompilerOptions) bool { - return node.Kind == ast.KindEnumDeclaration && (!ast.IsEnumConst(node) || options.ShouldPreserveConstEnums) -} - func setFlowNode(node *ast.Node, flowNode *ast.FlowNode) { data := node.FlowNodeData() if data != nil { @@ -2749,14 +2663,6 @@ func isFunctionSymbol(symbol *ast.Symbol) bool { return false } -func unreachableCodeIsError(options core.SourceFileAffectingCompilerOptions) bool { - return options.AllowUnreachableCode == core.TSFalse -} - -func unusedLabelIsError(options core.SourceFileAffectingCompilerOptions) bool { - return options.AllowUnusedLabels == core.TSFalse -} - func isStatementCondition(node *ast.Node) bool { switch node.Parent.Kind { case ast.KindIfStatement, ast.KindWhileStatement, ast.KindDoStatement: diff --git a/internal/checker/checker.go b/internal/checker/checker.go index 69a4f7c7f7..4fcf30c598 100644 --- a/internal/checker/checker.go +++ b/internal/checker/checker.go @@ -857,6 +857,8 @@ type Checker struct { activeTypeMappersCaches []map[string]*Type ambientModulesOnce sync.Once ambientModules []*ast.Symbol + withinUnreachableCode bool + reportedUnreachableNodes collections.Set[*ast.Node] mu sync.Mutex } @@ -2144,6 +2146,7 @@ func (c *Checker) checkSourceFile(ctx context.Context, sourceFile *ast.SourceFil c.wasCanceled = true } c.ctx = nil + c.reportedUnreachableNodes.Clear() links.typeChecked = true } } @@ -2160,10 +2163,12 @@ func (c *Checker) checkSourceElements(nodes []*ast.Node) { func (c *Checker) checkSourceElement(node *ast.Node) bool { if node != nil { saveCurrentNode := c.currentNode + saveWithinUnreachableCode := c.withinUnreachableCode c.currentNode = node c.instantiationCount = 0 c.checkSourceElementWorker(node) c.currentNode = saveCurrentNode + c.withinUnreachableCode = saveWithinUnreachableCode } return false } @@ -2179,13 +2184,13 @@ func (c *Checker) checkSourceElementWorker(node *ast.Node) { } } } - kind := node.Kind - if kind >= ast.KindFirstStatement && kind <= ast.KindLastStatement { - flowNode := node.FlowNodeData().FlowNode - if flowNode != nil && !c.isReachableFlowNode(flowNode) { - c.errorOrSuggestion(c.compilerOptions.AllowUnreachableCode == core.TSFalse, node, diagnostics.Unreachable_code_detected) + + if !c.withinUnreachableCode && c.compilerOptions.AllowUnreachableCode != core.TSTrue { + if c.checkSourceElementUnreachable(node) { + c.withinUnreachableCode = true } } + switch node.Kind { case ast.KindTypeParameter: c.checkTypeParameter(node) @@ -2308,6 +2313,87 @@ func (c *Checker) checkSourceElementWorker(node *ast.Node) { } } +func (c *Checker) checkSourceElementUnreachable(node *ast.Node) bool { + if !ast.IsPotentiallyExecutableNode(node) { + return false + } + + if c.reportedUnreachableNodes.Has(node) { + return true + } + + if !c.isSourceElementUnreachable(node) { + return false + } + + c.reportedUnreachableNodes.Add(node) + + sourceFile := ast.GetSourceFileOfNode(node) + + start := node.Pos() + end := node.End() + + parent := node.Parent + if parent.CanHaveStatements() { + statements := parent.Statements() + if offset := slices.Index(statements, node); offset >= 0 { + // Scan backwards to find the first unreachable unreported node; + // this may happen when producing region diagnostics where not all nodes + // will have been visited. + // TODO: enable this code once we support region diagnostics again. + first := offset + // for i := offset - 1; i >= 0; i-- { + // prevNode := statements[i] + // if !ast.IsPotentiallyExecutableNode(prevNode) || c.reportedUnreachableNodes.Has(prevNode) || !c.isSourceElementUnreachable(prevNode) { + // break + // } + // firstUnreachableIndex = i + // c.reportedUnreachableNodes.Add(prevNode) + // } + + last := offset + for i := offset + 1; i < len(statements); i++ { + nextNode := statements[i] + if !ast.IsPotentiallyExecutableNode(nextNode) || !c.isSourceElementUnreachable(nextNode) { + break + } + last = i + c.reportedUnreachableNodes.Add(nextNode) + } + + start = statements[first].Pos() + end = statements[last].End() + } + } + + start = scanner.SkipTrivia(sourceFile.Text(), start) + + diagnostic := ast.NewDiagnostic(sourceFile, core.NewTextRange(start, end), diagnostics.Unreachable_code_detected) + c.addErrorOrSuggestion(c.compilerOptions.AllowUnreachableCode == core.TSFalse, diagnostic) + + return true +} + +func (c *Checker) isSourceElementUnreachable(node *ast.Node) bool { + // Precondition: ast.IsPotentiallyExecutableNode is true + if node.Flags&ast.NodeFlagsUnreachable != 0 { + // The binder has determined that this code is unreachable. + // Ignore const enums unless preserveConstEnums is set. + switch node.Kind { + case ast.KindEnumDeclaration: + return !ast.IsEnumConst(node) || c.compilerOptions.ShouldPreserveConstEnums() + case ast.KindModuleDeclaration: + return ast.IsInstantiatedModule(node, c.compilerOptions.ShouldPreserveConstEnums()) + default: + return true + } + } else if flowNode := node.FlowNodeData().FlowNode; flowNode != nil { + // For code the binder doesn't know is unreachable, use control flow / types. + return !c.isReachableFlowNode(flowNode) + } + return false +} + // Function and class expression bodies are checked after all statements in the enclosing body. This is // to ensure constructs like the following are permitted: // @@ -4022,6 +4108,9 @@ func (c *Checker) checkLabeledStatement(node *ast.Node) { } } } + if labelNode.Flags&ast.NodeFlagsUnreachable != 0 && c.compilerOptions.AllowUnusedLabels != core.TSTrue { + c.errorOrSuggestion(c.compilerOptions.AllowUnusedLabels == core.TSFalse, labelNode, diagnostics.Unused_label) + } c.checkSourceElement(labeledStatement.Statement) } diff --git a/internal/core/compileroptions.go b/internal/core/compileroptions.go index 2f41f64dd6..057d034ec2 100644 --- a/internal/core/compileroptions.go +++ b/internal/core/compileroptions.go @@ -365,19 +365,13 @@ func (options *CompilerOptions) GetPathsBasePath(currentDirectory string) string // SourceFileAffectingCompilerOptions are the precomputed CompilerOptions values which // affect the parse and bind of a source file. type SourceFileAffectingCompilerOptions struct { - AllowUnreachableCode Tristate - AllowUnusedLabels Tristate - BindInStrictMode bool - ShouldPreserveConstEnums bool + BindInStrictMode bool } func (options *CompilerOptions) SourceFileAffecting() SourceFileAffectingCompilerOptions { options.sourceFileAffectingCompilerOptionsOnce.Do(func() { options.sourceFileAffectingCompilerOptions = SourceFileAffectingCompilerOptions{ - AllowUnreachableCode: options.AllowUnreachableCode, - AllowUnusedLabels: options.AllowUnusedLabels, - BindInStrictMode: options.AlwaysStrict.IsTrue() || options.Strict.IsTrue(), - ShouldPreserveConstEnums: options.ShouldPreserveConstEnums(), + BindInStrictMode: options.AlwaysStrict.IsTrue() || options.Strict.IsTrue(), } }) return options.sourceFileAffectingCompilerOptions diff --git a/internal/transformers/tstransforms/runtimesyntax.go b/internal/transformers/tstransforms/runtimesyntax.go index d1fa5e5fb8..8c3ad6a817 100644 --- a/internal/transformers/tstransforms/runtimesyntax.go +++ b/internal/transformers/tstransforms/runtimesyntax.go @@ -1148,7 +1148,7 @@ func (tx *RuntimeSyntaxTransformer) shouldEmitModuleDeclaration(node *ast.Module // If we can't find a parse tree node, assume the node is instantiated. return true } - return isInstantiatedModule(node.AsNode(), tx.compilerOptions.ShouldPreserveConstEnums()) + return ast.IsInstantiatedModule(node.AsNode(), tx.compilerOptions.ShouldPreserveConstEnums()) } func getInnermostModuleDeclarationFromDottedModule(moduleDeclaration *ast.ModuleDeclaration) *ast.ModuleDeclaration { diff --git a/internal/transformers/tstransforms/typeeraser.go b/internal/transformers/tstransforms/typeeraser.go index 99c70a8f39..f17962f6d4 100644 --- a/internal/transformers/tstransforms/typeeraser.go +++ b/internal/transformers/tstransforms/typeeraser.go @@ -109,7 +109,7 @@ func (tx *TypeEraserTransformer) visit(node *ast.Node) *ast.Node { case ast.KindModuleDeclaration: if !ast.IsIdentifier(node.Name()) || - !isInstantiatedModule(node, tx.compilerOptions.ShouldPreserveConstEnums()) || + !ast.IsInstantiatedModule(node, tx.compilerOptions.ShouldPreserveConstEnums()) || getInnermostModuleDeclarationFromDottedModule(node.AsModuleDeclaration()).Body == nil { // TypeScript module declarations are elided if they are not instantiated or have no body return tx.elide(node) diff --git a/internal/transformers/tstransforms/utilities.go b/internal/transformers/tstransforms/utilities.go index ead7be4bb8..9e0f04f17f 100644 --- a/internal/transformers/tstransforms/utilities.go +++ b/internal/transformers/tstransforms/utilities.go @@ -33,9 +33,3 @@ func constantExpression(value any, factory *printer.NodeFactory) *ast.Expression } return nil } - -func isInstantiatedModule(node *ast.ModuleDeclarationNode, preserveConstEnums bool) bool { - moduleState := ast.GetModuleInstanceState(node) - return moduleState == ast.ModuleInstanceStateInstantiated || - (preserveConstEnums && moduleState == ast.ModuleInstanceStateConstEnumOnly) -} diff --git a/testdata/baselines/reference/compiler/reachabilityChecks10.errors.txt b/testdata/baselines/reference/compiler/reachabilityChecks10.errors.txt new file mode 100644 index 0000000000..2b26eae20f --- /dev/null +++ b/testdata/baselines/reference/compiler/reachabilityChecks10.errors.txt @@ -0,0 +1,11 @@ +reachabilityChecks10.ts(2,1): error TS7027: Unreachable code detected. + + +==== reachabilityChecks10.ts (1 errors) ==== + throw new Error("") + console.log("1") + ~~~~~~~~~~~~~~~~ + console.log("2") + ~~~~~~~~~~~~~~~~ +!!! error TS7027: Unreachable code detected. + \ No newline at end of file diff --git a/testdata/baselines/reference/compiler/reachabilityChecks10.symbols b/testdata/baselines/reference/compiler/reachabilityChecks10.symbols new file mode 100644 index 0000000000..6ecc554153 --- /dev/null +++ b/testdata/baselines/reference/compiler/reachabilityChecks10.symbols @@ -0,0 +1,16 @@ +//// [tests/cases/compiler/reachabilityChecks10.ts] //// + +=== reachabilityChecks10.ts === +throw new Error("") +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + +console.log("1") +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) + +console.log("2") +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) + diff --git a/testdata/baselines/reference/compiler/reachabilityChecks10.types b/testdata/baselines/reference/compiler/reachabilityChecks10.types new file mode 100644 index 0000000000..d57914a480 --- /dev/null +++ b/testdata/baselines/reference/compiler/reachabilityChecks10.types @@ -0,0 +1,22 @@ +//// [tests/cases/compiler/reachabilityChecks10.ts] //// + +=== reachabilityChecks10.ts === +throw new Error("") +>new Error("") : Error +>Error : ErrorConstructor +>"" : "" + +console.log("1") +>console.log("1") : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>"1" : "1" + +console.log("2") +>console.log("2") : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>"2" : "2" + diff --git a/testdata/baselines/reference/compiler/reachabilityChecks11.errors.txt b/testdata/baselines/reference/compiler/reachabilityChecks11.errors.txt new file mode 100644 index 0000000000..b4497ee464 --- /dev/null +++ b/testdata/baselines/reference/compiler/reachabilityChecks11.errors.txt @@ -0,0 +1,104 @@ +reachabilityChecks11.ts(6,5): error TS7027: Unreachable code detected. +reachabilityChecks11.ts(18,5): error TS7027: Unreachable code detected. +reachabilityChecks11.ts(30,5): error TS7027: Unreachable code detected. +reachabilityChecks11.ts(47,5): error TS7027: Unreachable code detected. +reachabilityChecks11.ts(60,5): error TS7027: Unreachable code detected. +reachabilityChecks11.ts(69,5): error TS7027: Unreachable code detected. + + +==== reachabilityChecks11.ts (6 errors) ==== + // while (true); + var x = 1; + + module A { + while (true); + let x; + ~~~~~~ +!!! error TS7027: Unreachable code detected. + } + + module A1 { + do {} while(true); + module A { + interface F {} + } + } + + module A2 { + while (true); + module A { + ~~~~~~~~~~ + var x = 1; + ~~~~~~~~~~~~~~~~~~ + } + ~~~~~ +!!! error TS7027: Unreachable code detected. + } + + module A3 { + while (true); + type T = string; + } + + module A4 { + while (true); + module A { + ~~~~~~~~~~ + const enum E { X } + ~~~~~~~~~~~~~~~~~~~~~~~~~~ + } + ~~~~~ +!!! error TS7027: Unreachable code detected. + } + + function f1(x) { + if (x) { + return; + } + else { + throw new Error("123"); + } + var x; + } + + function f2() { + return; + class A { + ~~~~~~~~~ + } + ~~~~~ +!!! error TS7027: Unreachable code detected. + } + + module B { + for (; ;); + module C { + } + } + + function f3() { + do { + } while (true); + enum E { + ~~~~~~~~ + X = 1 + ~~~~~~~~~~~~~ + } + ~~~~~ +!!! error TS7027: Unreachable code detected. + } + + function f4() { + if (true) { + throw new Error(); + } + const enum E { + ~~~~~~~~~~~~~~ + X = 1 + ~~~~~~~~~~~~~ + } + ~~~~~ +!!! error TS7027: Unreachable code detected. + } + + \ No newline at end of file diff --git a/testdata/baselines/reference/compiler/reachabilityChecks11.js b/testdata/baselines/reference/compiler/reachabilityChecks11.js new file mode 100644 index 0000000000..1e69b95ff9 --- /dev/null +++ b/testdata/baselines/reference/compiler/reachabilityChecks11.js @@ -0,0 +1,153 @@ +//// [tests/cases/compiler/reachabilityChecks11.ts] //// + +//// [reachabilityChecks11.ts] +// while (true); +var x = 1; + +module A { + while (true); + let x; +} + +module A1 { + do {} while(true); + module A { + interface F {} + } +} + +module A2 { + while (true); + module A { + var x = 1; + } +} + +module A3 { + while (true); + type T = string; +} + +module A4 { + while (true); + module A { + const enum E { X } + } +} + +function f1(x) { + if (x) { + return; + } + else { + throw new Error("123"); + } + var x; +} + +function f2() { + return; + class A { + } +} + +module B { + for (; ;); + module C { + } +} + +function f3() { + do { + } while (true); + enum E { + X = 1 + } +} + +function f4() { + if (true) { + throw new Error(); + } + const enum E { + X = 1 + } +} + + + +//// [reachabilityChecks11.js] +// while (true); +var x = 1; +var A; +(function (A) { + while (true) + ; + let x; +})(A || (A = {})); +var A1; +(function (A1) { + do { } while (true); +})(A1 || (A1 = {})); +var A2; +(function (A2) { + while (true) + ; + let A; + (function (A) { + var x = 1; + })(A || (A = {})); +})(A2 || (A2 = {})); +var A3; +(function (A3) { + while (true) + ; +})(A3 || (A3 = {})); +var A4; +(function (A4) { + while (true) + ; + let A; + (function (A) { + let E; + (function (E) { + E[E["X"] = 0] = "X"; + })(E || (E = {})); + })(A || (A = {})); +})(A4 || (A4 = {})); +function f1(x) { + if (x) { + return; + } + else { + throw new Error("123"); + } + var x; +} +function f2() { + return; + class A { + } +} +var B; +(function (B) { + for (;;) + ; +})(B || (B = {})); +function f3() { + do { + } while (true); + let E; + (function (E) { + E[E["X"] = 1] = "X"; + })(E || (E = {})); +} +function f4() { + if (true) { + throw new Error(); + } + let E; + (function (E) { + E[E["X"] = 1] = "X"; + })(E || (E = {})); +} diff --git a/testdata/baselines/reference/compiler/reachabilityChecks11.symbols b/testdata/baselines/reference/compiler/reachabilityChecks11.symbols new file mode 100644 index 0000000000..450ccad803 --- /dev/null +++ b/testdata/baselines/reference/compiler/reachabilityChecks11.symbols @@ -0,0 +1,124 @@ +//// [tests/cases/compiler/reachabilityChecks11.ts] //// + +=== reachabilityChecks11.ts === +// while (true); +var x = 1; +>x : Symbol(x, Decl(reachabilityChecks11.ts, 1, 3)) + +module A { +>A : Symbol(A, Decl(reachabilityChecks11.ts, 1, 10)) + + while (true); + let x; +>x : Symbol(x, Decl(reachabilityChecks11.ts, 5, 7)) +} + +module A1 { +>A1 : Symbol(A1, Decl(reachabilityChecks11.ts, 6, 1)) + + do {} while(true); + module A { +>A : Symbol(A, Decl(reachabilityChecks11.ts, 9, 22)) + + interface F {} +>F : Symbol(F, Decl(reachabilityChecks11.ts, 10, 14)) + } +} + +module A2 { +>A2 : Symbol(A2, Decl(reachabilityChecks11.ts, 13, 1)) + + while (true); + module A { +>A : Symbol(A, Decl(reachabilityChecks11.ts, 16, 17)) + + var x = 1; +>x : Symbol(x, Decl(reachabilityChecks11.ts, 18, 11)) + } +} + +module A3 { +>A3 : Symbol(A3, Decl(reachabilityChecks11.ts, 20, 1)) + + while (true); + type T = string; +>T : Symbol(T, Decl(reachabilityChecks11.ts, 23, 17)) +} + +module A4 { +>A4 : Symbol(A4, Decl(reachabilityChecks11.ts, 25, 1)) + + while (true); + module A { +>A : Symbol(A, Decl(reachabilityChecks11.ts, 28, 17)) + + const enum E { X } +>E : Symbol(E, Decl(reachabilityChecks11.ts, 29, 14)) +>X : Symbol(E.X, Decl(reachabilityChecks11.ts, 30, 22)) + } +} + +function f1(x) { +>f1 : Symbol(f1, Decl(reachabilityChecks11.ts, 32, 1)) +>x : Symbol(x, Decl(reachabilityChecks11.ts, 34, 12), Decl(reachabilityChecks11.ts, 41, 7)) + + if (x) { +>x : Symbol(x, Decl(reachabilityChecks11.ts, 34, 12), Decl(reachabilityChecks11.ts, 41, 7)) + + return; + } + else { + throw new Error("123"); +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + } + var x; +>x : Symbol(x, Decl(reachabilityChecks11.ts, 34, 12), Decl(reachabilityChecks11.ts, 41, 7)) +} + +function f2() { +>f2 : Symbol(f2, Decl(reachabilityChecks11.ts, 42, 1)) + + return; + class A { +>A : Symbol(A, Decl(reachabilityChecks11.ts, 45, 11)) + } +} + +module B { +>B : Symbol(B, Decl(reachabilityChecks11.ts, 48, 1)) + + for (; ;); + module C { +>C : Symbol(C, Decl(reachabilityChecks11.ts, 51, 14)) + } +} + +function f3() { +>f3 : Symbol(f3, Decl(reachabilityChecks11.ts, 54, 1)) + + do { + } while (true); + enum E { +>E : Symbol(E, Decl(reachabilityChecks11.ts, 58, 19)) + + X = 1 +>X : Symbol(E.X, Decl(reachabilityChecks11.ts, 59, 12)) + } +} + +function f4() { +>f4 : Symbol(f4, Decl(reachabilityChecks11.ts, 62, 1)) + + if (true) { + throw new Error(); +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + } + const enum E { +>E : Symbol(E, Decl(reachabilityChecks11.ts, 67, 5)) + + X = 1 +>X : Symbol(E.X, Decl(reachabilityChecks11.ts, 68, 18)) + } +} + + diff --git a/testdata/baselines/reference/compiler/reachabilityChecks11.types b/testdata/baselines/reference/compiler/reachabilityChecks11.types new file mode 100644 index 0000000000..10cf27d433 --- /dev/null +++ b/testdata/baselines/reference/compiler/reachabilityChecks11.types @@ -0,0 +1,139 @@ +//// [tests/cases/compiler/reachabilityChecks11.ts] //// + +=== reachabilityChecks11.ts === +// while (true); +var x = 1; +>x : number +>1 : 1 + +module A { +>A : typeof A + + while (true); +>true : true + + let x; +>x : any +} + +module A1 { +>A1 : typeof A1 + + do {} while(true); +>true : true + + module A { + interface F {} + } +} + +module A2 { +>A2 : typeof A2 + + while (true); +>true : true + + module A { +>A : typeof A + + var x = 1; +>x : number +>1 : 1 + } +} + +module A3 { +>A3 : typeof A3 + + while (true); +>true : true + + type T = string; +>T : string +} + +module A4 { +>A4 : typeof A4 + + while (true); +>true : true + + module A { + const enum E { X } +>E : E +>X : E.X + } +} + +function f1(x) { +>f1 : (x: any) => void +>x : any + + if (x) { +>x : any + + return; + } + else { + throw new Error("123"); +>new Error("123") : Error +>Error : ErrorConstructor +>"123" : "123" + } + var x; +>x : any +} + +function f2() { +>f2 : () => void + + return; + class A { +>A : A + } +} + +module B { +>B : typeof B + + for (; ;); + module C { + } +} + +function f3() { +>f3 : () => void + + do { + } while (true); +>true : true + + enum E { +>E : E + + X = 1 +>X : E.X +>1 : 1 + } +} + +function f4() { +>f4 : () => void + + if (true) { +>true : true + + throw new Error(); +>new Error() : Error +>Error : ErrorConstructor + } + const enum E { +>E : E + + X = 1 +>X : E.X +>1 : 1 + } +} + + diff --git a/testdata/baselines/reference/compiler/reachabilityChecks9.errors.txt b/testdata/baselines/reference/compiler/reachabilityChecks9.errors.txt new file mode 100644 index 0000000000..75b9cf98da --- /dev/null +++ b/testdata/baselines/reference/compiler/reachabilityChecks9.errors.txt @@ -0,0 +1,37 @@ +reachabilityChecks9.ts(7,7): error TS7027: Unreachable code detected. +reachabilityChecks9.ts(20,7): error TS7027: Unreachable code detected. + + +==== reachabilityChecks9.ts (2 errors) ==== + // https://github.com/microsoft/TypeScript/issues/55562 + + function g(str: string) { + switch (str) { + case "a": + return; + console.log("1"); + ~~~~~~~~~~~~~~~~~ + console.log("2"); + ~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS7027: Unreachable code detected. + case "b": + console.log("3"); + } + } + + function h(str: string) { + switch (str) { + case "a": + console.log("1"); + default: + return; + console.log("2"); + ~~~~~~~~~~~~~~~~~ + console.log("3"); + ~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS7027: Unreachable code detected. + case "b": + console.log("4"); + } + } + \ No newline at end of file diff --git a/testdata/baselines/reference/compiler/reachabilityChecks9.symbols b/testdata/baselines/reference/compiler/reachabilityChecks9.symbols new file mode 100644 index 0000000000..d50f997b9a --- /dev/null +++ b/testdata/baselines/reference/compiler/reachabilityChecks9.symbols @@ -0,0 +1,65 @@ +//// [tests/cases/compiler/reachabilityChecks9.ts] //// + +=== reachabilityChecks9.ts === +// https://github.com/microsoft/TypeScript/issues/55562 + +function g(str: string) { +>g : Symbol(g, Decl(reachabilityChecks9.ts, 0, 0)) +>str : Symbol(str, Decl(reachabilityChecks9.ts, 2, 11)) + + switch (str) { +>str : Symbol(str, Decl(reachabilityChecks9.ts, 2, 11)) + + case "a": + return; + console.log("1"); +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) + + console.log("2"); +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) + + case "b": + console.log("3"); +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) + } +} + +function h(str: string) { +>h : Symbol(h, Decl(reachabilityChecks9.ts, 11, 1)) +>str : Symbol(str, Decl(reachabilityChecks9.ts, 13, 11)) + + switch (str) { +>str : Symbol(str, Decl(reachabilityChecks9.ts, 13, 11)) + + case "a": + console.log("1"); +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) + + default: + return; + console.log("2"); +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) + + console.log("3"); +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) + + case "b": + console.log("4"); +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) + } +} + diff --git a/testdata/baselines/reference/compiler/reachabilityChecks9.types b/testdata/baselines/reference/compiler/reachabilityChecks9.types new file mode 100644 index 0000000000..8bced2f44c --- /dev/null +++ b/testdata/baselines/reference/compiler/reachabilityChecks9.types @@ -0,0 +1,87 @@ +//// [tests/cases/compiler/reachabilityChecks9.ts] //// + +=== reachabilityChecks9.ts === +// https://github.com/microsoft/TypeScript/issues/55562 + +function g(str: string) { +>g : (str: string) => void +>str : string + + switch (str) { +>str : string + + case "a": +>"a" : "a" + + return; + console.log("1"); +>console.log("1") : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>"1" : "1" + + console.log("2"); +>console.log("2") : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>"2" : "2" + + case "b": +>"b" : "b" + + console.log("3"); +>console.log("3") : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>"3" : "3" + } +} + +function h(str: string) { +>h : (str: string) => void +>str : string + + switch (str) { +>str : string + + case "a": +>"a" : "a" + + console.log("1"); +>console.log("1") : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>"1" : "1" + + default: + return; + console.log("2"); +>console.log("2") : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>"2" : "2" + + console.log("3"); +>console.log("3") : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>"3" : "3" + + case "b": +>"b" : "b" + + console.log("4"); +>console.log("4") : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>"4" : "4" + } +} + diff --git a/testdata/baselines/reference/compiler/reachabilityChecksIgnored.js b/testdata/baselines/reference/compiler/reachabilityChecksIgnored.js new file mode 100644 index 0000000000..7635105d49 --- /dev/null +++ b/testdata/baselines/reference/compiler/reachabilityChecksIgnored.js @@ -0,0 +1,29 @@ +//// [tests/cases/compiler/reachabilityChecksIgnored.ts] //// + +//// [reachabilityChecksIgnored.ts] +function a() { + throw new Error(""); + + // @ts-ignore + console.log("unreachable"); +} + +function b() { + throw new Error(""); + + // @ts-expect-error + console.log("unreachable"); +} + + +//// [reachabilityChecksIgnored.js] +function a() { + throw new Error(""); + // @ts-ignore + console.log("unreachable"); +} +function b() { + throw new Error(""); + // @ts-expect-error + console.log("unreachable"); +} diff --git a/testdata/baselines/reference/compiler/reachabilityChecksIgnored.symbols b/testdata/baselines/reference/compiler/reachabilityChecksIgnored.symbols new file mode 100644 index 0000000000..04eccd0375 --- /dev/null +++ b/testdata/baselines/reference/compiler/reachabilityChecksIgnored.symbols @@ -0,0 +1,29 @@ +//// [tests/cases/compiler/reachabilityChecksIgnored.ts] //// + +=== reachabilityChecksIgnored.ts === +function a() { +>a : Symbol(a, Decl(reachabilityChecksIgnored.ts, 0, 0)) + + throw new Error(""); +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + + // @ts-ignore + console.log("unreachable"); +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +} + +function b() { +>b : Symbol(b, Decl(reachabilityChecksIgnored.ts, 5, 1)) + + throw new Error(""); +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + + // @ts-expect-error + console.log("unreachable"); +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +} + diff --git a/testdata/baselines/reference/compiler/reachabilityChecksIgnored.types b/testdata/baselines/reference/compiler/reachabilityChecksIgnored.types new file mode 100644 index 0000000000..838060e048 --- /dev/null +++ b/testdata/baselines/reference/compiler/reachabilityChecksIgnored.types @@ -0,0 +1,37 @@ +//// [tests/cases/compiler/reachabilityChecksIgnored.ts] //// + +=== reachabilityChecksIgnored.ts === +function a() { +>a : () => void + + throw new Error(""); +>new Error("") : Error +>Error : ErrorConstructor +>"" : "" + + // @ts-ignore + console.log("unreachable"); +>console.log("unreachable") : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>"unreachable" : "unreachable" +} + +function b() { +>b : () => void + + throw new Error(""); +>new Error("") : Error +>Error : ErrorConstructor +>"" : "" + + // @ts-expect-error + console.log("unreachable"); +>console.log("unreachable") : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>"unreachable" : "unreachable" +} + diff --git a/testdata/baselines/reference/submodule/compiler/reachabilityChecks1.errors.txt b/testdata/baselines/reference/submodule/compiler/reachabilityChecks1.errors.txt index d25977a05b..478dc40733 100644 --- a/testdata/baselines/reference/submodule/compiler/reachabilityChecks1.errors.txt +++ b/testdata/baselines/reference/submodule/compiler/reachabilityChecks1.errors.txt @@ -1,58 +1,77 @@ reachabilityChecks1.ts(2,1): error TS7027: Unreachable code detected. -reachabilityChecks1.ts(6,5): error TS7027: Unreachable code detected. -reachabilityChecks1.ts(18,5): error TS7027: Unreachable code detected. -reachabilityChecks1.ts(30,5): error TS7027: Unreachable code detected. reachabilityChecks1.ts(47,5): error TS7027: Unreachable code detected. +reachabilityChecks1.ts(51,1): error TS7027: Unreachable code detected. reachabilityChecks1.ts(60,5): error TS7027: Unreachable code detected. reachabilityChecks1.ts(69,5): error TS7027: Unreachable code detected. -==== reachabilityChecks1.ts (7 errors) ==== +==== reachabilityChecks1.ts (5 errors) ==== while (true); var x = 1; ~~~~~~~~~~ -!!! error TS7027: Unreachable code detected. + module A { + ~~~~~~~~~~ while (true); + ~~~~~~~~~~~~~~~~~ let x; - ~~~~~~ -!!! error TS7027: Unreachable code detected. + ~~~~~~~~~~ } + ~ + module A1 { + ~~~~~~~~~~~ do {} while(true); + ~~~~~~~~~~~~~~~~~~~~~~ module A { + ~~~~~~~~~~~~~~ interface F {} + ~~~~~~~~~~~~~~~~~~~~~~ } + ~~~~~ } + ~ + module A2 { + ~~~~~~~~~~~ while (true); + ~~~~~~~~~~~~~~~~~ module A { - ~~~~~~~~~~ + ~~~~~~~~~~~~~~ var x = 1; ~~~~~~~~~~~~~~~~~~ } ~~~~~ -!!! error TS7027: Unreachable code detected. } + ~ + module A3 { + ~~~~~~~~~~~ while (true); + ~~~~~~~~~~~~~~~~~ type T = string; + ~~~~~~~~~~~~~~~~~~~~ } + ~ + module A4 { + ~~~~~~~~~~~ while (true); + ~~~~~~~~~~~~~~~~~ module A { - ~~~~~~~~~~ + ~~~~~~~~~~~~~~ const enum E { X } ~~~~~~~~~~~~~~~~~~~~~~~~~~ } ~~~~~ -!!! error TS7027: Unreachable code detected. } + ~ +!!! error TS7027: Unreachable code detected. function f1(x) { if (x) { @@ -74,10 +93,16 @@ reachabilityChecks1.ts(69,5): error TS7027: Unreachable code detected. } module B { + ~~~~~~~~~~ for (; ;); + ~~~~~~~~~~~~~~ module C { + ~~~~~~~~~~~~~~ } + ~~~~~ } + ~ +!!! error TS7027: Unreachable code detected. function f3() { do { diff --git a/testdata/baselines/reference/submodule/compiler/reachabilityChecks1.errors.txt.diff b/testdata/baselines/reference/submodule/compiler/reachabilityChecks1.errors.txt.diff new file mode 100644 index 0000000000..873affdebe --- /dev/null +++ b/testdata/baselines/reference/submodule/compiler/reachabilityChecks1.errors.txt.diff @@ -0,0 +1,108 @@ +--- old.reachabilityChecks1.errors.txt ++++ new.reachabilityChecks1.errors.txt +@@= skipped -0, +0 lines =@@ + reachabilityChecks1.ts(2,1): error TS7027: Unreachable code detected. +-reachabilityChecks1.ts(6,5): error TS7027: Unreachable code detected. +-reachabilityChecks1.ts(18,5): error TS7027: Unreachable code detected. +-reachabilityChecks1.ts(30,5): error TS7027: Unreachable code detected. + reachabilityChecks1.ts(47,5): error TS7027: Unreachable code detected. ++reachabilityChecks1.ts(51,1): error TS7027: Unreachable code detected. + reachabilityChecks1.ts(60,5): error TS7027: Unreachable code detected. + reachabilityChecks1.ts(69,5): error TS7027: Unreachable code detected. + + +-==== reachabilityChecks1.ts (7 errors) ==== ++==== reachabilityChecks1.ts (5 errors) ==== + while (true); + var x = 1; + ~~~~~~~~~~ +-!!! error TS7027: Unreachable code detected. ++ + + module A { ++ ~~~~~~~~~~ + while (true); ++ ~~~~~~~~~~~~~~~~~ + let x; +- ~~~~~~ +-!!! error TS7027: Unreachable code detected. ++ ~~~~~~~~~~ + } ++ ~ ++ + + module A1 { ++ ~~~~~~~~~~~ + do {} while(true); ++ ~~~~~~~~~~~~~~~~~~~~~~ + module A { ++ ~~~~~~~~~~~~~~ + interface F {} ++ ~~~~~~~~~~~~~~~~~~~~~~ + } ++ ~~~~~ + } ++ ~ ++ + + module A2 { ++ ~~~~~~~~~~~ + while (true); ++ ~~~~~~~~~~~~~~~~~ + module A { +- ~~~~~~~~~~ ++ ~~~~~~~~~~~~~~ + var x = 1; + ~~~~~~~~~~~~~~~~~~ + } + ~~~~~ +-!!! error TS7027: Unreachable code detected. + } ++ ~ ++ + + module A3 { ++ ~~~~~~~~~~~ + while (true); ++ ~~~~~~~~~~~~~~~~~ + type T = string; ++ ~~~~~~~~~~~~~~~~~~~~ + } ++ ~ ++ + + module A4 { ++ ~~~~~~~~~~~ + while (true); ++ ~~~~~~~~~~~~~~~~~ + module A { +- ~~~~~~~~~~ ++ ~~~~~~~~~~~~~~ + const enum E { X } + ~~~~~~~~~~~~~~~~~~~~~~~~~~ + } + ~~~~~ +-!!! error TS7027: Unreachable code detected. + } ++ ~ ++!!! error TS7027: Unreachable code detected. + + function f1(x) { + if (x) { +@@= skipped -73, +92 lines =@@ + } + + module B { ++ ~~~~~~~~~~ + for (; ;); ++ ~~~~~~~~~~~~~~ + module C { ++ ~~~~~~~~~~~~~~ + } ++ ~~~~~ + } ++ ~ ++!!! error TS7027: Unreachable code detected. + + function f3() { + do { \ No newline at end of file diff --git a/testdata/baselines/reference/submodule/conformance/neverReturningFunctions1.errors.txt b/testdata/baselines/reference/submodule/conformance/neverReturningFunctions1.errors.txt index 8a7a883714..a15d15e17d 100644 --- a/testdata/baselines/reference/submodule/conformance/neverReturningFunctions1.errors.txt +++ b/testdata/baselines/reference/submodule/conformance/neverReturningFunctions1.errors.txt @@ -13,7 +13,6 @@ neverReturningFunctions1.ts(101,13): error TS7027: Unreachable code detected. neverReturningFunctions1.ts(103,9): error TS7027: Unreachable code detected. neverReturningFunctions1.ts(105,5): error TS7027: Unreachable code detected. neverReturningFunctions1.ts(111,9): error TS7027: Unreachable code detected. -neverReturningFunctions1.ts(112,9): error TS7027: Unreachable code detected. neverReturningFunctions1.ts(122,9): error TS7027: Unreachable code detected. neverReturningFunctions1.ts(127,9): error TS7027: Unreachable code detected. neverReturningFunctions1.ts(129,5): error TS7027: Unreachable code detected. @@ -23,7 +22,7 @@ neverReturningFunctions1.ts(148,9): error TS7027: Unreachable code detected. neverReturningFunctions1.ts(153,5): error TS7027: Unreachable code detected. -==== neverReturningFunctions1.ts (23 errors) ==== +==== neverReturningFunctions1.ts (22 errors) ==== function fail(message?: string): never { throw new Error(message); } @@ -163,10 +162,9 @@ neverReturningFunctions1.ts(153,5): error TS7027: Unreachable code detected. if (typeof x.a === "string") { fail(); x; // Unreachable - ~~ -!!! error TS7027: Unreachable code detected. + ~~~~~~~~~~~~~~~~~~~~ x.a; // Unreachable - ~~~~ + ~~~~~~~~~~~~ !!! error TS7027: Unreachable code detected. } x; // { a: string | number } diff --git a/testdata/baselines/reference/submodule/conformance/neverReturningFunctions1.errors.txt.diff b/testdata/baselines/reference/submodule/conformance/neverReturningFunctions1.errors.txt.diff new file mode 100644 index 0000000000..3f7c033e1c --- /dev/null +++ b/testdata/baselines/reference/submodule/conformance/neverReturningFunctions1.errors.txt.diff @@ -0,0 +1,32 @@ +--- old.neverReturningFunctions1.errors.txt ++++ new.neverReturningFunctions1.errors.txt +@@= skipped -12, +12 lines =@@ + neverReturningFunctions1.ts(103,9): error TS7027: Unreachable code detected. + neverReturningFunctions1.ts(105,5): error TS7027: Unreachable code detected. + neverReturningFunctions1.ts(111,9): error TS7027: Unreachable code detected. +-neverReturningFunctions1.ts(112,9): error TS7027: Unreachable code detected. + neverReturningFunctions1.ts(122,9): error TS7027: Unreachable code detected. + neverReturningFunctions1.ts(127,9): error TS7027: Unreachable code detected. + neverReturningFunctions1.ts(129,5): error TS7027: Unreachable code detected. +@@= skipped -10, +9 lines =@@ + neverReturningFunctions1.ts(153,5): error TS7027: Unreachable code detected. + + +-==== neverReturningFunctions1.ts (23 errors) ==== ++==== neverReturningFunctions1.ts (22 errors) ==== + function fail(message?: string): never { + throw new Error(message); + } +@@= skipped -140, +140 lines =@@ + if (typeof x.a === "string") { + fail(); + x; // Unreachable +- ~~ +-!!! error TS7027: Unreachable code detected. ++ ~~~~~~~~~~~~~~~~~~~~ + x.a; // Unreachable +- ~~~~ ++ ~~~~~~~~~~~~ + !!! error TS7027: Unreachable code detected. + } + x; // { a: string | number } \ No newline at end of file diff --git a/testdata/tests/cases/compiler/reachabilityChecks10.ts b/testdata/tests/cases/compiler/reachabilityChecks10.ts new file mode 100644 index 0000000000..e4f17b935c --- /dev/null +++ b/testdata/tests/cases/compiler/reachabilityChecks10.ts @@ -0,0 +1,7 @@ +// @strict: true +// @noEmit: true +// @allowUnreachableCode: false + +throw new Error("") +console.log("1") +console.log("2") diff --git a/testdata/tests/cases/compiler/reachabilityChecks11.ts b/testdata/tests/cases/compiler/reachabilityChecks11.ts new file mode 100644 index 0000000000..81dbcf3c7a --- /dev/null +++ b/testdata/tests/cases/compiler/reachabilityChecks11.ts @@ -0,0 +1,76 @@ +// @allowUnreachableCode: false +// @preserveConstEnums: true + +// while (true); +var x = 1; + +module A { + while (true); + let x; +} + +module A1 { + do {} while(true); + module A { + interface F {} + } +} + +module A2 { + while (true); + module A { + var x = 1; + } +} + +module A3 { + while (true); + type T = string; +} + +module A4 { + while (true); + module A { + const enum E { X } + } +} + +function f1(x) { + if (x) { + return; + } + else { + throw new Error("123"); + } + var x; +} + +function f2() { + return; + class A { + } +} + +module B { + for (; ;); + module C { + } +} + +function f3() { + do { + } while (true); + enum E { + X = 1 + } +} + +function f4() { + if (true) { + throw new Error(); + } + const enum E { + X = 1 + } +} + diff --git a/testdata/tests/cases/compiler/reachabilityChecks9.ts b/testdata/tests/cases/compiler/reachabilityChecks9.ts new file mode 100644 index 0000000000..601cdb65d4 --- /dev/null +++ b/testdata/tests/cases/compiler/reachabilityChecks9.ts @@ -0,0 +1,29 @@ +// @strict: true +// @noEmit: true +// @allowUnreachableCode: false + +// https://github.com/microsoft/TypeScript/issues/55562 + +function g(str: string) { + switch (str) { + case "a": + return; + console.log("1"); + console.log("2"); + case "b": + console.log("3"); + } +} + +function h(str: string) { + switch (str) { + case "a": + console.log("1"); + default: + return; + console.log("2"); + console.log("3"); + case "b": + console.log("4"); + } +} diff --git a/testdata/tests/cases/compiler/reachabilityChecksIgnored.ts b/testdata/tests/cases/compiler/reachabilityChecksIgnored.ts new file mode 100644 index 0000000000..c4f0145eef --- /dev/null +++ b/testdata/tests/cases/compiler/reachabilityChecksIgnored.ts @@ -0,0 +1,17 @@ +// @allowUnreachableCode: false +// @preserveConstEnums: true + + +function a() { + throw new Error(""); + + // @ts-ignore + console.log("unreachable"); +} + +function b() { + throw new Error(""); + + // @ts-expect-error + console.log("unreachable"); +}