initial revision of reachability checks in binder

This commit is contained in:
Vladimir Matveev
2015-09-03 10:49:39 -07:00
parent 690b87c209
commit b62ef0d452
75 changed files with 1570 additions and 204 deletions

View File

@@ -9,6 +9,21 @@ namespace ts {
Instantiated = 1,
ConstEnumOnly = 2
}
const enum Reachability {
Unintialized = 1 << 0,
Reachable = 1 << 1,
Unreachable = 1 << 2,
ReportedUnreachable = 1 << 3
}
function or(state1: Reachability, state2: Reachability): Reachability {
return (state1 | state2) & Reachability.Reachable
? Reachability.Reachable
: (state1 & state2) & Reachability.ReportedUnreachable
? Reachability.ReportedUnreachable
: Reachability.Unreachable;
}
export function getModuleInstanceState(node: Node): ModuleInstanceState {
// A module is uninstantiated if it contains only
@@ -77,18 +92,25 @@ namespace ts {
IsContainerWithLocals = IsContainer | HasLocals
}
export function bindSourceFile(file: SourceFile) {
export function bindSourceFile(file: SourceFile, options: CompilerOptions) {
let start = new Date().getTime();
bindSourceFileWorker(file);
bindSourceFileWorker(file, options);
bindTime += new Date().getTime() - start;
}
function bindSourceFileWorker(file: SourceFile) {
function bindSourceFileWorker(file: SourceFile, options: CompilerOptions) {
let parent: Node;
let container: Node;
let blockScopeContainer: Node;
let lastContainer: Node;
// state used by reachability checks
let hasExplicitReturn: boolean;
let currentReachabilityState: Reachability;
let labelStack: Reachability[];
let labels: Map<number>;
let implicitLabels: number[];
// If this file is an external module, then it is automatically in strict-mode according to
// ES6. If it is not an external module, then we'll determine if it is in strict mode or
// not depending on if we see "use strict" in certain places (or if we hit a class/namespace).
@@ -329,12 +351,273 @@ namespace ts {
blockScopeContainer.locals = undefined;
}
forEachChild(node, bind);
bindWithReachabilityChecks(node);
container = saveContainer;
parent = saveParent;
blockScopeContainer = savedBlockScopeContainer;
}
function shouldSaveReachabilityState(n: Node): boolean {
return n.kind === SyntaxKind.SourceFile || n.kind === SyntaxKind.ModuleBlock || isFunctionLike(n);
}
function bindWithReachabilityChecks(node: Node): void {
let savedReachabilityState: Reachability;
let savedLabelStack: Reachability[];
let savedLabels: Map<number>;
let savedImplicitLabels: number[];
let savedHasExplicitReturn: boolean;
let saveState = shouldSaveReachabilityState(node);
if (saveState) {
savedReachabilityState = currentReachabilityState;
savedLabelStack = labelStack;
savedLabels = labels;
savedImplicitLabels = implicitLabels;
savedHasExplicitReturn = hasExplicitReturn;
currentReachabilityState = Reachability.Reachable;
hasExplicitReturn = false;
labelStack = labels = implicitLabels = undefined;
}
if (!bindReachableStatement(node)) {
forEachChild(node, bind);
}
if (currentReachabilityState === Reachability.Reachable && isFunctionLike(node) && nodeIsPresent((<FunctionLikeDeclaration>node).body)) {
node.flags |= NodeFlags.HasImplicitReturn;
if (hasExplicitReturn) {
node.flags |= NodeFlags.HasExplicitReturn;
}
}
if (saveState) {
hasExplicitReturn = savedHasExplicitReturn;
currentReachabilityState = savedReachabilityState;
labelStack = savedLabelStack;
labels = savedLabels;
implicitLabels = savedImplicitLabels;
}
}
function bindReachableStatement(n: Node): boolean {
if (checkUnreachable(n)) {
return false;
}
switch(n.kind) {
case SyntaxKind.WhileStatement:
return bindWhileStatement(<WhileStatement>n);
case SyntaxKind.DoStatement:
return bindDoStatement(<DoStatement>n);
case SyntaxKind.ForStatement:
return bindForStatement(<ForStatement>n);
case SyntaxKind.ForInStatement:
case SyntaxKind.ForOfStatement:
return bindForInOrForOfStatement(<ForInStatement | ForOfStatement>n);
case SyntaxKind.IfStatement:
return bindIfStatement(<IfStatement>n);
case SyntaxKind.ReturnStatement:
case SyntaxKind.ThrowStatement:
return bindReturnOrThrow(<ReturnStatement | ThrowStatement>n);
case SyntaxKind.BreakStatement:
case SyntaxKind.ContinueStatement:
return bindBreakOrContinueStatement(<BreakOrContinueStatement>n);
case SyntaxKind.TryStatement:
return bindTryStatement(<TryStatement>n);
case SyntaxKind.SwitchStatement:
return bindSwitchStatement(<SwitchStatement>n);
case SyntaxKind.CaseBlock:
return bindCaseBlock(<CaseBlock>n);
case SyntaxKind.LabeledStatement:
return bindLabeledStatement(<LabeledStatement>n);
default:
return false;
}
}
function bindWhileStatement(n: WhileStatement): boolean {
const preWhileState =
n.expression.kind === SyntaxKind.FalseKeyword ? Reachability.Unreachable : currentReachabilityState;
const postWhileState =
n.expression.kind === SyntaxKind.TrueKeyword ? Reachability.Unreachable : currentReachabilityState;
// bind expressions (don't affect reachability)
bind(n.expression);
currentReachabilityState = preWhileState;
const postWhileLabel = pushImplicitLabel();
bind(n.statement);
popImplicitLabel(postWhileLabel, postWhileState)
return true;
}
function bindDoStatement(n: DoStatement): boolean {
const preDoState = currentReachabilityState;
const postDoLabel = pushImplicitLabel();
bind(n.statement);
var postDoState = n.expression.kind === SyntaxKind.TrueKeyword ? Reachability.Unreachable : preDoState;
popImplicitLabel(postDoLabel, postDoState);
// bind expressions (don't affect reachability)
bind(n.expression);
return true;
}
function bindForStatement(n: ForStatement): boolean {
const preForState = currentReachabilityState;
const postForLabel = pushImplicitLabel();
// bind expressions (don't affect reachability)
bind(n.initializer);
bind(n.condition);
bind(n.incrementor);
bind(n.statement);
// for statement is considered infinite when it condition is either omitted or is true keyword
// - for(..;;..)
// - for(..;true;..)
const isInfiniteLoop = (!n.condition || n.condition.kind === SyntaxKind.TrueKeyword);
const postForState = isInfiniteLoop ? Reachability.Unreachable : preForState;
popImplicitLabel(postForLabel, postForState);
return true;
}
function bindForInOrForOfStatement(n: ForInStatement | ForOfStatement): boolean {
const preStatementState = currentReachabilityState;
const postStatementLabel = pushImplicitLabel();
// bind expressions (don't affect reachability)
bind(n.initializer);
bind(n.expression);
bind(n.statement);
popImplicitLabel(postStatementLabel, preStatementState);
return true;
}
function bindIfStatement(n: IfStatement): boolean {
const ifTrueState = n.expression.kind === SyntaxKind.FalseKeyword ? Reachability.Unreachable : currentReachabilityState;
const ifFalseState = n.expression.kind === SyntaxKind.TrueKeyword ? Reachability.Unreachable : currentReachabilityState;
currentReachabilityState = ifTrueState;
// bind expression (don't affect reachability)
bind(n.expression);
bind(n.thenStatement);
if (n.elseStatement) {
const preElseState = currentReachabilityState;
currentReachabilityState = ifFalseState;
bind(n.elseStatement);
currentReachabilityState = or(currentReachabilityState, preElseState);
}
else {
currentReachabilityState = or(currentReachabilityState, ifFalseState);
}
return true;
}
function bindReturnOrThrow(n: ReturnStatement | ThrowStatement): boolean {
// bind expression (don't affect reachability)
bind(n.expression);
if (n.kind === SyntaxKind.ReturnStatement) {
hasExplicitReturn = true;
}
currentReachabilityState = Reachability.Unreachable;
return true;
}
function bindBreakOrContinueStatement(n: BreakOrContinueStatement): boolean {
// call bind on label (don't affect reachability)
bind(n.label);
if (n.kind === SyntaxKind.BreakStatement) {
jumpToLabel(n.label, currentReachabilityState);
}
else {
jumpToLabel(n.label, Reachability.Unreachable); // touch label so it will be marked a used
}
currentReachabilityState = Reachability.Unreachable;
return true;
}
function bindTryStatement(n: TryStatement): boolean {
// catch\finally blocks has the same reachability as try block
const preTryState = currentReachabilityState;
bind(n.tryBlock);
const postTryState = currentReachabilityState;
currentReachabilityState = preTryState;
bind(n.catchClause);
const postCatchState = currentReachabilityState;
currentReachabilityState = preTryState;
bind(n.finallyBlock);
// post catch/finally state is reachable if
// - post try state is reachable - control flow can fall out of try block
// - post catch state is reachable - control flow can fall out of catch block
currentReachabilityState = or(postTryState, postCatchState);
return true;
}
function bindSwitchStatement(n: SwitchStatement): boolean {
const preSwitchState = currentReachabilityState;
const postSwitchLabel = pushImplicitLabel();
// bind expression (don't affect reachability)
bind(n.expression);
bind(n.caseBlock);
const hasDefault = forEach(n.caseBlock.clauses, c => c.kind === SyntaxKind.DefaultClause);
// post switch state is unreachable if switch is exaustive (has a default case ) and does not have fallthrough from the last case
var postSwitchState = hasDefault && currentReachabilityState !== Reachability.Reachable ? Reachability.Unreachable : preSwitchState;
popImplicitLabel(postSwitchLabel, postSwitchState);
return true;
}
function bindCaseBlock(n: CaseBlock): boolean {
const startState = currentReachabilityState;
for(let clause of n.clauses) {
currentReachabilityState = startState;
bind(clause);
if (clause.statements.length && currentReachabilityState === Reachability.Reachable && options.noFallthroughCasesInSwitch) {
errorOnFirstToken(clause, Diagnostics.Fallthrough_case_in_switch);
}
}
return true;
}
function bindLabeledStatement(n: LabeledStatement): boolean {
// call bind on label (don't affect reachability)
bind(n.label);
var ok = pushNamedLabel(n.label);
bind(n.statement);
if (ok) {
popNamedLabel(n.label, currentReachabilityState);
}
return true;
}
function getContainerFlags(node: Node): ContainerFlags {
switch (node.kind) {
@@ -472,17 +755,6 @@ namespace ts {
: declareSymbol(file.locals, undefined, node, symbolFlags, symbolExcludes);
}
function isAmbientContext(node: Node): boolean {
while (node) {
if (node.flags & NodeFlags.Ambient) {
return true;
}
node = node.parent;
}
return false;
}
function hasExportDeclarations(node: ModuleDeclaration | SourceFile): boolean {
let body = node.kind === SyntaxKind.SourceFile ? node : (<ModuleDeclaration>node).body;
if (body.kind === SyntaxKind.SourceFile || body.kind === SyntaxKind.ModuleBlock) {
@@ -498,7 +770,7 @@ namespace ts {
function setExportContextFlag(node: ModuleDeclaration | SourceFile) {
// A declaration source file or ambient module declaration that contains no export declarations (but possibly regular
// declarations with export modifiers) is an export context in which declarations are implicitly exported.
if (isAmbientContext(node) && !hasExportDeclarations(node)) {
if (isInAmbientContext(node) && !hasExportDeclarations(node)) {
node.flags |= NodeFlags.ExportContext;
}
else {
@@ -750,11 +1022,11 @@ namespace ts {
function checkStrictModeWithStatement(node: WithStatement) {
// Grammar checking for withStatement
if (inStrictMode) {
grammarErrorOnFirstToken(node, Diagnostics.with_statements_are_not_allowed_in_strict_mode);
errorOnFirstToken(node, Diagnostics.with_statements_are_not_allowed_in_strict_mode);
}
}
function grammarErrorOnFirstToken(node: Node, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any) {
function errorOnFirstToken(node: Node, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any) {
let span = getSpanOfTokenAtPosition(file, node.pos);
file.bindDiagnostics.push(createFileDiagnostic(file, span.start, span.length, message, arg0, arg1, arg2));
}
@@ -764,6 +1036,10 @@ namespace ts {
}
function bind(node: Node) {
if (!node) {
return;
}
node.parent = parent;
let savedInStrictMode = inStrictMode;
@@ -1065,5 +1341,123 @@ namespace ts {
? bindAnonymousDeclaration(node, symbolFlags, "__computed")
: declareSymbolAndAddToSymbolTable(node, symbolFlags, symbolExcludes);
}
// reachability checks
function pushNamedLabel(name: Identifier): boolean {
initializeReachabilityStateIfNecessary();
if (hasProperty(labels, name.text)) {
return false;
}
labels[name.text] = labelStack.push(Reachability.Unintialized) - 1;
return true;
}
function pushImplicitLabel(): number {
initializeReachabilityStateIfNecessary();
let index = labelStack.push(Reachability.Unintialized) - 1;
implicitLabels.push(index);
return index;
}
function popNamedLabel(label: Identifier, outerState: Reachability): void {
initializeReachabilityStateIfNecessary();
let index = labels[label.text];
Debug.assert(index !== undefined);
Debug.assert(labelStack.length == index + 1);
labels[label.text] = undefined;
setCurrentStateAtLabel(labelStack.pop(), outerState, label);
}
function popImplicitLabel(implicitLabelIndex: number, outerState: Reachability): void {
initializeReachabilityStateIfNecessary();
Debug.assert(labelStack.length === implicitLabelIndex + 1, `Label stack: ${labelStack.length}, index:${implicitLabelIndex}`);
let i = implicitLabels.pop();
Debug.assert(implicitLabelIndex === i, `i: ${i}, index: ${implicitLabelIndex}`);
setCurrentStateAtLabel(labelStack.pop(), outerState, /*name*/ undefined);
}
function setCurrentStateAtLabel(innerMergedState: Reachability, outerState: Reachability, label: Identifier): void {
initializeReachabilityStateIfNecessary();
if (innerMergedState === Reachability.Unintialized) {
if (label && options.noUnusedLabels) {
file.bindDiagnostics.push(createDiagnosticForNode(label, Diagnostics.Unused_label));
}
currentReachabilityState = outerState;
}
else {
currentReachabilityState = or(innerMergedState, outerState);
}
}
function jumpToLabel(label: Identifier, outerState: Reachability): void {
initializeReachabilityStateIfNecessary();
const index = label ? labels[label.text] : lastOrUndefined(implicitLabels);
if (index === undefined) {
// reference to unknown label or
// break/continue used outside of loops
return;
}
const stateAtLabel = labelStack[index];
labelStack[index] = stateAtLabel === Reachability.Unintialized ? outerState : or(stateAtLabel, outerState);
}
function checkUnreachable(node: Node): boolean {
switch(currentReachabilityState) {
case Reachability.Unreachable:
const reportError =
isStatement(node) ||
node.kind === SyntaxKind.ClassDeclaration ||
node.kind === SyntaxKind.ModuleDeclaration ||
(node.kind === SyntaxKind.EnumDeclaration && (!isConstEnumDeclaration(node) || options.preserveConstEnums));
if (reportError) {
currentReachabilityState = Reachability.ReportedUnreachable;
// unreachable code is reported if
// - user has explicitly asked about it AND
// - statement is in ambient context (statements in ambient context is already an error so we shoult 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
const reportUnreachableCode =
options.noUnreachableCode &&
!isInAmbientContext(node) &&
(
node.kind !== SyntaxKind.VariableStatement ||
getCombinedNodeFlags((<VariableStatement>node).declarationList) & NodeFlags.BlockScoped ||
forEach((<VariableStatement>node).declarationList.declarations, d => d.initializer)
)
if (reportUnreachableCode) {
file.bindDiagnostics.push(createDiagnosticForNode(node, Diagnostics.Unreachable_code_detected));
}
}
case Reachability.ReportedUnreachable:
return true;
default:
return false;
}
}
function initializeReachabilityStateIfNecessary(): void {
if (labels) {
return;
}
currentReachabilityState = Reachability.Reachable;
labels = {};
labelStack = [];
implicitLabels = [];
}
}
}

View File

@@ -158,6 +158,8 @@ namespace ts {
let getGlobalPromiseConstructorLikeType: () => ObjectType;
let getGlobalThenableType: () => ObjectType;
let jsxElementClassType: Type;
let tupleTypes: Map<TupleType> = {};
let unionTypes: Map<UnionType> = {};
let intersectionTypes: Map<IntersectionType> = {};
@@ -7560,7 +7562,6 @@ namespace ts {
return prop || unknownSymbol;
}
let jsxElementClassType: Type = undefined;
function getJsxGlobalElementClassType(): Type {
if (!jsxElementClassType) {
jsxElementClassType = getExportedTypeFromNamespace(JsxNames.JSX, JsxNames.ElementClass);
@@ -9277,21 +9278,11 @@ namespace ts {
return aggregatedTypes;
}
function bodyContainsAReturnStatement(funcBody: Block) {
return forEachReturnStatement(funcBody, returnStatement => {
return true;
});
}
function bodyContainsSingleThrowStatement(body: Block) {
return (body.statements.length === 1) && (body.statements[0].kind === SyntaxKind.ThrowStatement);
}
// TypeScript Specification 1.0 (6.3) - July 2014
// An explicitly typed function whose return type isn't the Void or the Any type
// must have at least one return statement somewhere in its body.
// An exception to this rule is if the function implementation consists of a single 'throw' statement.
function checkIfNonVoidFunctionHasReturnExpressionsOrSingleThrowStatment(func: FunctionLikeDeclaration, returnType: Type): void {
function checkAllCodePathsInNonVoidFunctionReturnOrThrow(func: FunctionLikeDeclaration, returnType: Type): void {
if (!produceDiagnostics) {
return;
}
@@ -9302,26 +9293,20 @@ namespace ts {
}
// If all we have is a function signature, or an arrow function with an expression body, then there is nothing to check.
if (nodeIsMissing(func.body) || func.body.kind !== SyntaxKind.Block) {
// also if HasImplicitReturnValue flags is not set this means that all codepaths in function body end with return of throw
if (nodeIsMissing(func.body) || func.body.kind !== SyntaxKind.Block || !(func.flags & NodeFlags.HasImplicitReturn)) {
return;
}
let bodyBlock = <Block>func.body;
// Ensure the body has at least one return expression.
if (bodyContainsAReturnStatement(bodyBlock)) {
return;
if (func.flags & NodeFlags.HasExplicitReturn) {
if (compilerOptions.noImplicitReturns) {
error(func.type, Diagnostics.Not_all_code_paths_return_a_value);
}
}
// If there are no return expressions, then we need to check if
// the function body consists solely of a throw statement;
// this is to make an exception for unimplemented functions.
if (bodyContainsSingleThrowStatement(bodyBlock)) {
return;
else {
// This function does not conform to the specification.
error(func.type, Diagnostics.A_function_whose_declared_type_is_neither_void_nor_any_must_return_a_value);
}
// This function does not conform to the specification.
error(func.type, Diagnostics.A_function_whose_declared_type_is_neither_void_nor_any_must_return_a_value_or_consist_of_a_single_throw_statement);
}
function checkFunctionExpressionOrObjectLiteralMethod(node: FunctionExpression | MethodDeclaration, contextualMapper?: TypeMapper): Type {
@@ -9401,7 +9386,7 @@ namespace ts {
}
if (returnType && !node.asteriskToken) {
checkIfNonVoidFunctionHasReturnExpressionsOrSingleThrowStatment(node, isAsync ? promisedType : returnType);
checkAllCodePathsInNonVoidFunctionReturnOrThrow(node, isAsync ? promisedType : returnType);
}
if (node.body) {
@@ -10579,8 +10564,15 @@ namespace ts {
checkGrammarFunctionLikeDeclaration(node) || checkGrammarAccessor(node) || checkGrammarComputedPropertyName(node.name);
if (node.kind === SyntaxKind.GetAccessor) {
if (!isInAmbientContext(node) && nodeIsPresent(node.body) && !(bodyContainsAReturnStatement(<Block>node.body) || bodyContainsSingleThrowStatement(<Block>node.body))) {
error(node.name, Diagnostics.A_get_accessor_must_return_a_value_or_consist_of_a_single_throw_statement);
if (!isInAmbientContext(node) && nodeIsPresent(node.body) && (node.flags & NodeFlags.HasImplicitReturn)) {
if (node.flags & NodeFlags.HasExplicitReturn) {
if (compilerOptions.noImplicitReturns) {
error(node.name, Diagnostics.Not_all_code_paths_return_a_value);
}
}
else {
error(node.name, Diagnostics.A_get_accessor_must_return_a_value);
}
}
}
@@ -11505,7 +11497,7 @@ namespace ts {
promisedType = checkAsyncFunctionReturnType(node);
}
checkIfNonVoidFunctionHasReturnExpressionsOrSingleThrowStatment(node, isAsync ? promisedType : returnType);
checkAllCodePathsInNonVoidFunctionReturnOrThrow(node, isAsync ? promisedType : returnType);
}
if (produceDiagnostics && !node.type) {
@@ -14524,7 +14516,7 @@ namespace ts {
function initializeTypeChecker() {
// Bind all source files and propagate errors
forEach(host.getSourceFiles(), file => {
bindSourceFile(file);
bindSourceFile(file, compilerOptions);
});
// Initialize global symbol table

View File

@@ -244,7 +244,27 @@ namespace ts {
"classic": ModuleResolutionKind.Classic
},
description: Diagnostics.Specifies_module_resolution_strategy_Colon_node_Node_or_classic_TypeScript_pre_1_6
}
},
{
name: "noUnusedLabels",
type: "boolean",
description: Diagnostics.Report_error_on_unused_labels
},
{
name: "noImplicitReturns",
type: "boolean",
description: Diagnostics.Report_error_when_not_all_code_paths_in_function_return_a_value
},
{
name: "noFallthroughCasesInSwitch",
type: "boolean",
description: Diagnostics.Report_errors_for_fallthrough_cases_in_switch_statement
},
{
name: "noUnreachableCode",
type: "boolean",
description: Diagnostics.Report_errors_on_unreachable_code
}
];
/* @internal */

View File

@@ -256,7 +256,7 @@ namespace ts {
Neither_type_0_nor_type_1_is_assignable_to_the_other: { code: 2352, category: DiagnosticCategory.Error, key: "Neither type '{0}' nor type '{1}' is assignable to the other." },
Object_literal_may_only_specify_known_properties_and_0_does_not_exist_in_type_1: { code: 2353, category: DiagnosticCategory.Error, key: "Object literal may only specify known properties, and '{0}' does not exist in type '{1}'." },
No_best_common_type_exists_among_return_expressions: { code: 2354, category: DiagnosticCategory.Error, key: "No best common type exists among return expressions." },
A_function_whose_declared_type_is_neither_void_nor_any_must_return_a_value_or_consist_of_a_single_throw_statement: { code: 2355, category: DiagnosticCategory.Error, key: "A function whose declared type is neither 'void' nor 'any' must return a value or consist of a single 'throw' statement." },
A_function_whose_declared_type_is_neither_void_nor_any_must_return_a_value: { code: 2355, category: DiagnosticCategory.Error, key: "A function whose declared type is neither 'void' nor 'any' must return a value." },
An_arithmetic_operand_must_be_of_type_any_number_or_an_enum_type: { code: 2356, category: DiagnosticCategory.Error, key: "An arithmetic operand must be of type 'any', 'number' or an enum type." },
The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_property_or_indexer: { code: 2357, category: DiagnosticCategory.Error, key: "The operand of an increment or decrement operator must be a variable, property or indexer." },
The_left_hand_side_of_an_instanceof_expression_must_be_of_type_any_an_object_type_or_a_type_parameter: { code: 2358, category: DiagnosticCategory.Error, key: "The left-hand side of an 'instanceof' expression must be of type 'any', an object type or a type parameter." },
@@ -277,7 +277,7 @@ namespace ts {
Duplicate_number_index_signature: { code: 2375, category: DiagnosticCategory.Error, key: "Duplicate number index signature." },
A_super_call_must_be_the_first_statement_in_the_constructor_when_a_class_contains_initialized_properties_or_has_parameter_properties: { code: 2376, category: DiagnosticCategory.Error, key: "A 'super' call must be the first statement in the constructor when a class contains initialized properties or has parameter properties." },
Constructors_for_derived_classes_must_contain_a_super_call: { code: 2377, category: DiagnosticCategory.Error, key: "Constructors for derived classes must contain a 'super' call." },
A_get_accessor_must_return_a_value_or_consist_of_a_single_throw_statement: { code: 2378, category: DiagnosticCategory.Error, key: "A 'get' accessor must return a value or consist of a single 'throw' statement." },
A_get_accessor_must_return_a_value: { code: 2378, category: DiagnosticCategory.Error, key: "A 'get' accessor must return a value." },
Getter_and_setter_accessors_do_not_agree_in_visibility: { code: 2379, category: DiagnosticCategory.Error, key: "Getter and setter accessors do not agree in visibility." },
get_and_set_accessor_must_have_the_same_type: { code: 2380, category: DiagnosticCategory.Error, key: "'get' and 'set' accessor must have the same type." },
A_signature_with_an_implementation_cannot_use_a_string_literal_type: { code: 2381, category: DiagnosticCategory.Error, key: "A signature with an implementation cannot use a string literal type." },
@@ -570,6 +570,10 @@ namespace ts {
Initializes_a_TypeScript_project_and_creates_a_tsconfig_json_file: { code: 6070, category: DiagnosticCategory.Message, key: "Initializes a TypeScript project and creates a tsconfig.json file." },
Successfully_created_a_tsconfig_json_file: { code: 6071, category: DiagnosticCategory.Message, key: "Successfully created a tsconfig.json file." },
Suppress_excess_property_checks_for_object_literals: { code: 6072, category: DiagnosticCategory.Message, key: "Suppress excess property checks for object literals." },
Report_error_on_unused_labels: { code: 6073, category: DiagnosticCategory.Message, key: "Report error on unused labels." },
Report_error_when_not_all_code_paths_in_function_return_a_value: { code: 6074, category: DiagnosticCategory.Message, key: "Report error when not all code paths in function return a value." },
Report_errors_for_fallthrough_cases_in_switch_statement: { code: 6075, category: DiagnosticCategory.Message, key: "Report errors for fallthrough cases in switch statement." },
Report_errors_on_unreachable_code: { code: 6076, category: DiagnosticCategory.Message, key: "Report errors on unreachable code." },
Variable_0_implicitly_has_an_1_type: { code: 7005, category: DiagnosticCategory.Error, key: "Variable '{0}' implicitly has an '{1}' type." },
Parameter_0_implicitly_has_an_1_type: { code: 7006, category: DiagnosticCategory.Error, key: "Parameter '{0}' implicitly has an '{1}' type." },
Member_0_implicitly_has_an_1_type: { code: 7008, category: DiagnosticCategory.Error, key: "Member '{0}' implicitly has an '{1}' type." },
@@ -587,6 +591,10 @@ namespace ts {
Function_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions: { code: 7024, category: DiagnosticCategory.Error, key: "Function implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions." },
Generator_implicitly_has_type_0_because_it_does_not_yield_any_values_Consider_supplying_a_return_type: { code: 7025, category: DiagnosticCategory.Error, key: "Generator implicitly has type '{0}' because it does not yield any values. Consider supplying a return type." },
JSX_element_implicitly_has_type_any_because_no_interface_JSX_0_exists: { code: 7026, category: DiagnosticCategory.Error, key: "JSX element implicitly has type 'any' because no interface 'JSX.{0}' exists" },
Unreachable_code_detected: { code: 7027, category: DiagnosticCategory.Error, key: "Unreachable code detected." },
Unused_label: { code: 7028, category: DiagnosticCategory.Error, key: "Unused label." },
Fallthrough_case_in_switch: { code: 7029, category: DiagnosticCategory.Error, key: "Fallthrough case in switch." },
Not_all_code_paths_return_a_value: { code: 7030, category: DiagnosticCategory.Error, key: "Not all code paths return a value." },
You_cannot_rename_this_element: { code: 8000, category: DiagnosticCategory.Error, key: "You cannot rename this element." },
You_cannot_rename_elements_that_are_defined_in_the_standard_TypeScript_library: { code: 8001, category: DiagnosticCategory.Error, key: "You cannot rename elements that are defined in the standard TypeScript library." },
import_can_only_be_used_in_a_ts_file: { code: 8002, category: DiagnosticCategory.Error, key: "'import ... =' can only be used in a .ts file." },

View File

@@ -1013,7 +1013,7 @@
"category": "Error",
"code": 2354
},
"A function whose declared type is neither 'void' nor 'any' must return a value or consist of a single 'throw' statement.": {
"A function whose declared type is neither 'void' nor 'any' must return a value.": {
"category": "Error",
"code": 2355
},
@@ -1097,7 +1097,7 @@
"category": "Error",
"code": 2377
},
"A 'get' accessor must return a value or consist of a single 'throw' statement.": {
"A 'get' accessor must return a value.": {
"category": "Error",
"code": 2378
},
@@ -2270,7 +2270,22 @@
"category": "Message",
"code": 6072
},
"Report error on unused labels.": {
"category": "Message",
"code": 6073
},
"Report error when not all code paths in function return a value.": {
"category": "Message",
"code": 6074
},
"Report errors for fallthrough cases in switch statement.": {
"category": "Message",
"code": 6075
},
"Report errors on unreachable code.": {
"category": "Message",
"code": 6076
},
"Variable '{0}' implicitly has an '{1}' type.": {
"category": "Error",
"code": 7005
@@ -2339,8 +2354,22 @@
"category": "Error",
"code": 7026
},
"Unreachable code detected.": {
"category": "Error",
"code": 7027
},
"Unused label.": {
"category": "Error",
"code": 7028
},
"Fallthrough case in switch.": {
"category": "Error",
"code": 7029
},
"Not all code paths return a value.": {
"category": "Error",
"code": 7030
},
"You cannot rename this element.": {
"category": "Error",
"code": 8000

View File

@@ -376,6 +376,8 @@ namespace ts {
OctalLiteral = 0x00010000, // Octal numeric literal
Namespace = 0x00020000, // Namespace declaration
ExportContext = 0x00040000, // Export context (initialized by binding)
HasImplicitReturn = 0x00080000, // If function implicitly returns on one of codepaths (initialized by binding)
HasExplicitReturn = 0x00100000, // If function has explicit reachable return on one of codepaths (initialized by binding)
Modifier = Export | Ambient | Public | Private | Protected | Static | Abstract | Default | Async,
AccessibilityModifier = Public | Private | Protected,
@@ -2057,7 +2059,11 @@ namespace ts {
experimentalDecorators?: boolean;
experimentalAsyncFunctions?: boolean;
emitDecoratorMetadata?: boolean;
moduleResolution?: ModuleResolutionKind
moduleResolution?: ModuleResolutionKind,
noUnusedLabels?: boolean,
noImplicitReturns?: boolean,
noFallthroughCasesInSwitch?: boolean,
noUnreachableCode?: boolean,
/* @internal */ stripInternal?: boolean;
// Skip checking lib.d.ts to help speed up tests.

View File

@@ -1242,6 +1242,18 @@ module Harness {
setting.value.toLowerCase() === "preserve" ? ts.JsxEmit.Preserve :
ts.JsxEmit.None;
break;
case "nounusedlabels":
options.noUnusedLabels = setting.value === "true"
break;
case "noimplicitreturns":
options.noImplicitReturns = setting.value === "true"
break;
case "nofallthroughcasesinswitch":
options.noFallthroughCasesInSwitch = setting.value === "true"
break;
case "nounreachablecode":
options.noUnreachableCode = setting.value === "true"
break;
default:
throw new Error("Unsupported compiler setting " + setting.flag);

View File

@@ -446,7 +446,7 @@ namespace ts.BreakpointResolver {
// fall through.
case SyntaxKind.CatchClause:
return spanInNode(lastOrUndefined((<Block>node.parent).statements));;
return spanInNode(lastOrUndefined((<Block>node.parent).statements));
case SyntaxKind.CaseBlock:
// breakpoint in last statement of the last clause
@@ -493,9 +493,6 @@ namespace ts.BreakpointResolver {
default:
return spanInNode(node.parent);
}
// Default to parent node
return spanInNode(node.parent);
}
function spanInColonToken(node: Node): TextSpan {

View File

@@ -340,7 +340,6 @@ namespace ts.formatting {
return node;
}
}
return node;
}
}

View File

@@ -6,6 +6,9 @@ namespace ts.NavigateTo {
let patternMatcher = createPatternMatcher(searchValue);
let rawItems: RawNavigateToItem[] = [];
// This means "compare in a case insensitive manner."
let baseSensitivity: Intl.CollatorOptions = { sensitivity: "base" };
// Search the declarations in all files and output matched NavigateToItem into array of NavigateToItem[]
forEach(program.getSourceFiles(), sourceFile => {
cancellationToken.throwIfCancellationRequested();
@@ -162,8 +165,6 @@ namespace ts.NavigateTo {
return bestMatchKind;
}
// This means "compare in a case insensitive manner."
let baseSensitivity: Intl.CollatorOptions = { sensitivity: "base" };
function compareNavigateToItems(i1: RawNavigateToItem, i2: RawNavigateToItem) {
// TODO(cyrusn): get the gamut of comparisons that VS already uses here.
// Right now we just sort by kind first, and then by name of the item.

View File

@@ -4863,7 +4863,7 @@ namespace ts {
else if (!isFunctionLike(node)) {
forEachChild(node, aggregate);
}
};
}
}
/**
@@ -4910,7 +4910,7 @@ namespace ts {
else if (!isFunctionLike(node)) {
forEachChild(node, aggregate);
}
};
}
}
function ownsBreakOrContinueStatement(owner: Node, statement: BreakOrContinueStatement): boolean {
@@ -6219,8 +6219,6 @@ namespace ts {
}
return SemanticMeaning.Value | SemanticMeaning.Type | SemanticMeaning.Namespace;
Debug.fail("Unknown declaration type");
}
function isTypeReference(node: Node): boolean {

View File

@@ -320,7 +320,6 @@ namespace ts {
// TODO: should this be '==='?
if (settingsJson == null || settingsJson == "") {
throw Error("LanguageServiceShimHostAdapter.getCompilationSettings: empty compilationSettings");
return null;
}
return <CompilerOptions>JSON.parse(settingsJson);
}