mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-15 21:36:50 -05:00
Merge pull request #776 from Microsoft/getOccurrencesThrow
Support getOccurrencesAtPosition for 'throw' keywords.
This commit is contained in:
@@ -2635,6 +2635,11 @@ module ts {
|
||||
return getReturnOccurrences(<ReturnStatement>node.parent);
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.ThrowKeyword:
|
||||
if (hasKind(node.parent, SyntaxKind.ThrowStatement)) {
|
||||
return getThrowOccurrences(<ThrowStatement>node.parent);
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.TryKeyword:
|
||||
case SyntaxKind.CatchKeyword:
|
||||
case SyntaxKind.FinallyKeyword:
|
||||
@@ -2752,12 +2757,108 @@ module ts {
|
||||
}
|
||||
|
||||
var keywords: Node[] = []
|
||||
forEachReturnStatement(<Block>(<FunctionDeclaration>func).body, returnStatement => {
|
||||
forEachReturnStatement(<Block>func.body, returnStatement => {
|
||||
pushKeywordIf(keywords, returnStatement.getFirstToken(), SyntaxKind.ReturnKeyword);
|
||||
});
|
||||
|
||||
// Include 'throw' statements that do not occur within a try block.
|
||||
forEach(aggregateOwnedThrowStatements(func.body), throwStatement => {
|
||||
pushKeywordIf(keywords, throwStatement.getFirstToken(), SyntaxKind.ThrowKeyword);
|
||||
});
|
||||
|
||||
return map(keywords, getReferenceEntryFromNode);
|
||||
}
|
||||
|
||||
function getThrowOccurrences(throwStatement: ThrowStatement) {
|
||||
var owner = getThrowStatementOwner(throwStatement);
|
||||
|
||||
if (!owner) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
var keywords: Node[] = [];
|
||||
|
||||
forEach(aggregateOwnedThrowStatements(owner), throwStatement => {
|
||||
pushKeywordIf(keywords, throwStatement.getFirstToken(), SyntaxKind.ThrowKeyword);
|
||||
});
|
||||
|
||||
// If the "owner" is a function, then we equate 'return' and 'throw' statements in their
|
||||
// ability to "jump out" of the function, and include occurrences for both.
|
||||
if (owner.kind === SyntaxKind.FunctionBlock) {
|
||||
forEachReturnStatement(<Block>owner, returnStatement => {
|
||||
pushKeywordIf(keywords, returnStatement.getFirstToken(), SyntaxKind.ReturnKeyword);
|
||||
});
|
||||
}
|
||||
|
||||
return map(keywords, getReferenceEntryFromNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Aggregates all throw-statements within this node *without* crossing
|
||||
* into function boundaries and try-blocks with catch-clauses.
|
||||
*/
|
||||
function aggregateOwnedThrowStatements(node: Node): ThrowStatement[] {
|
||||
var statementAccumulator: ThrowStatement[] = []
|
||||
aggregate(node);
|
||||
return statementAccumulator;
|
||||
|
||||
function aggregate(node: Node): void {
|
||||
if (node.kind === SyntaxKind.ThrowStatement) {
|
||||
statementAccumulator.push(<ThrowStatement>node);
|
||||
}
|
||||
else if (node.kind === SyntaxKind.TryStatement) {
|
||||
var tryStatement = <TryStatement>node;
|
||||
|
||||
if (tryStatement.catchBlock) {
|
||||
aggregate(tryStatement.catchBlock);
|
||||
}
|
||||
else {
|
||||
// Exceptions thrown within a try block lacking a catch clause
|
||||
// are "owned" in the current context.
|
||||
aggregate(tryStatement.tryBlock);
|
||||
}
|
||||
|
||||
if (tryStatement.finallyBlock) {
|
||||
aggregate(tryStatement.finallyBlock);
|
||||
}
|
||||
}
|
||||
// Do not cross function boundaries.
|
||||
else if (!isAnyFunction(node)) {
|
||||
forEachChild(node, aggregate);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* For lack of a better name, this function takes a throw statement and returns the
|
||||
* nearest ancestor that is a try-block (whose try statement has a catch clause),
|
||||
* function-block, or source file.
|
||||
*/
|
||||
function getThrowStatementOwner(throwStatement: ThrowStatement): Node {
|
||||
var child: Node = throwStatement;
|
||||
|
||||
while (child.parent) {
|
||||
var parent = child.parent;
|
||||
|
||||
if (parent.kind === SyntaxKind.FunctionBlock || parent.kind === SyntaxKind.SourceFile) {
|
||||
return parent;
|
||||
}
|
||||
|
||||
// A throw-statement is only owned by a try-statement if the try-statement has
|
||||
// a catch clause, and if the throw-statement occurs within the try block.
|
||||
if (parent.kind === SyntaxKind.TryStatement) {
|
||||
var tryStatement = <TryStatement>parent;
|
||||
|
||||
if (tryStatement.tryBlock === child && tryStatement.catchBlock) {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
|
||||
child = parent;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getTryCatchFinallyOccurrences(tryStatement: TryStatement): ReferenceEntry[] {
|
||||
var keywords: Node[] = [];
|
||||
|
||||
58
tests/cases/fourslash/getOccurrencesThrow.ts
Normal file
58
tests/cases/fourslash/getOccurrencesThrow.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////function f(a: number) {
|
||||
//// try {
|
||||
//// throw "Hello";
|
||||
////
|
||||
//// try {
|
||||
//// throw 10;
|
||||
//// }
|
||||
//// catch (x) {
|
||||
//// [|return|] 100;
|
||||
//// }
|
||||
//// finally {
|
||||
//// throw 10;
|
||||
//// }
|
||||
//// }
|
||||
//// catch (x) {
|
||||
//// [|throw|] "Something";
|
||||
//// }
|
||||
//// finally {
|
||||
//// [|throw|] "Also something";
|
||||
//// }
|
||||
//// if (a > 0) {
|
||||
//// [|return|] (function () {
|
||||
//// return;
|
||||
//// return;
|
||||
//// return;
|
||||
////
|
||||
//// if (false) {
|
||||
//// return true;
|
||||
//// }
|
||||
//// throw "Hello!";
|
||||
//// })() || true;
|
||||
//// }
|
||||
////
|
||||
//// [|th/**/row|] 10;
|
||||
////
|
||||
//// var unusued = [1, 2, 3, 4].map(x => { throw 4 })
|
||||
////
|
||||
//// [|return|];
|
||||
//// [|return|] true;
|
||||
//// [|throw|] false;
|
||||
////}
|
||||
|
||||
test.ranges().forEach(r => {
|
||||
goTo.position(r.start);
|
||||
|
||||
test.ranges().forEach(range => {
|
||||
verify.occurrencesAtPositionContains(range, false);
|
||||
});
|
||||
|
||||
verify.occurrencesAtPositionCount(test.ranges().length);
|
||||
});
|
||||
|
||||
goTo.marker();
|
||||
test.ranges().forEach(range => {
|
||||
verify.occurrencesAtPositionContains(range, false);
|
||||
});
|
||||
58
tests/cases/fourslash/getOccurrencesThrow2.ts
Normal file
58
tests/cases/fourslash/getOccurrencesThrow2.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////function f(a: number) {
|
||||
//// try {
|
||||
//// throw "Hello";
|
||||
////
|
||||
//// try {
|
||||
//// [|t/**/hrow|] 10;
|
||||
//// }
|
||||
//// catch (x) {
|
||||
//// return 100;
|
||||
//// }
|
||||
//// finally {
|
||||
//// throw 10;
|
||||
//// }
|
||||
//// }
|
||||
//// catch (x) {
|
||||
//// throw "Something";
|
||||
//// }
|
||||
//// finally {
|
||||
//// throw "Also something";
|
||||
//// }
|
||||
//// if (a > 0) {
|
||||
//// return (function () {
|
||||
//// return;
|
||||
//// return;
|
||||
//// return;
|
||||
////
|
||||
//// if (false) {
|
||||
//// return true;
|
||||
//// }
|
||||
//// throw "Hello!";
|
||||
//// })() || true;
|
||||
//// }
|
||||
////
|
||||
//// throw 10;
|
||||
////
|
||||
//// var unusued = [1, 2, 3, 4].map(x => { throw 4 })
|
||||
////
|
||||
//// return;
|
||||
//// return true;
|
||||
//// throw false;
|
||||
////}
|
||||
|
||||
test.ranges().forEach(r => {
|
||||
goTo.position(r.start);
|
||||
|
||||
test.ranges().forEach(range => {
|
||||
verify.occurrencesAtPositionContains(range, false);
|
||||
});
|
||||
|
||||
verify.occurrencesAtPositionCount(test.ranges().length);
|
||||
});
|
||||
|
||||
goTo.marker();
|
||||
test.ranges().forEach(range => {
|
||||
verify.occurrencesAtPositionContains(range, false);
|
||||
});
|
||||
58
tests/cases/fourslash/getOccurrencesThrow3.ts
Normal file
58
tests/cases/fourslash/getOccurrencesThrow3.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////function f(a: number) {
|
||||
//// try {
|
||||
//// [|throw|] "Hello";
|
||||
////
|
||||
//// try {
|
||||
//// throw 10;
|
||||
//// }
|
||||
//// catch (x) {
|
||||
//// return 100;
|
||||
//// }
|
||||
//// finally {
|
||||
//// [|thr/**/ow|] 10;
|
||||
//// }
|
||||
//// }
|
||||
//// catch (x) {
|
||||
//// throw "Something";
|
||||
//// }
|
||||
//// finally {
|
||||
//// throw "Also something";
|
||||
//// }
|
||||
//// if (a > 0) {
|
||||
//// return (function () {
|
||||
//// return;
|
||||
//// return;
|
||||
//// return;
|
||||
////
|
||||
//// if (false) {
|
||||
//// return true;
|
||||
//// }
|
||||
//// throw "Hello!";
|
||||
//// })() || true;
|
||||
//// }
|
||||
////
|
||||
//// throw 10;
|
||||
////
|
||||
//// var unusued = [1, 2, 3, 4].map(x => { throw 4 })
|
||||
////
|
||||
//// return;
|
||||
//// return true;
|
||||
//// throw false;
|
||||
////}
|
||||
|
||||
test.ranges().forEach(r => {
|
||||
goTo.position(r.start);
|
||||
|
||||
test.ranges().forEach(range => {
|
||||
verify.occurrencesAtPositionContains(range, false);
|
||||
});
|
||||
|
||||
verify.occurrencesAtPositionCount(test.ranges().length);
|
||||
});
|
||||
|
||||
goTo.marker();
|
||||
test.ranges().forEach(range => {
|
||||
verify.occurrencesAtPositionContains(range, false);
|
||||
});
|
||||
58
tests/cases/fourslash/getOccurrencesThrow4.ts
Normal file
58
tests/cases/fourslash/getOccurrencesThrow4.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////function f(a: number) {
|
||||
//// try {
|
||||
//// throw "Hello";
|
||||
////
|
||||
//// try {
|
||||
//// throw 10;
|
||||
//// }
|
||||
//// catch (x) {
|
||||
//// return 100;
|
||||
//// }
|
||||
//// finally {
|
||||
//// throw 10;
|
||||
//// }
|
||||
//// }
|
||||
//// catch (x) {
|
||||
//// throw "Something";
|
||||
//// }
|
||||
//// finally {
|
||||
//// throw "Also something";
|
||||
//// }
|
||||
//// if (a > 0) {
|
||||
//// return (function () {
|
||||
//// [|return|];
|
||||
//// [|return|];
|
||||
//// [|return|];
|
||||
////
|
||||
//// if (false) {
|
||||
//// [|return|] true;
|
||||
//// }
|
||||
//// [|th/**/row|] "Hello!";
|
||||
//// })() || true;
|
||||
//// }
|
||||
////
|
||||
//// throw 10;
|
||||
////
|
||||
//// var unusued = [1, 2, 3, 4].map(x => { throw 4 })
|
||||
////
|
||||
//// return;
|
||||
//// return true;
|
||||
//// throw false;
|
||||
////}
|
||||
|
||||
test.ranges().forEach(r => {
|
||||
goTo.position(r.start);
|
||||
|
||||
test.ranges().forEach(range => {
|
||||
verify.occurrencesAtPositionContains(range, false);
|
||||
});
|
||||
|
||||
verify.occurrencesAtPositionCount(test.ranges().length);
|
||||
});
|
||||
|
||||
goTo.marker();
|
||||
test.ranges().forEach(range => {
|
||||
verify.occurrencesAtPositionContains(range, false);
|
||||
});
|
||||
58
tests/cases/fourslash/getOccurrencesThrow5.ts
Normal file
58
tests/cases/fourslash/getOccurrencesThrow5.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////function f(a: number) {
|
||||
//// try {
|
||||
//// throw "Hello";
|
||||
////
|
||||
//// try {
|
||||
//// throw 10;
|
||||
//// }
|
||||
//// catch (x) {
|
||||
//// return 100;
|
||||
//// }
|
||||
//// finally {
|
||||
//// throw 10;
|
||||
//// }
|
||||
//// }
|
||||
//// catch (x) {
|
||||
//// throw "Something";
|
||||
//// }
|
||||
//// finally {
|
||||
//// throw "Also something";
|
||||
//// }
|
||||
//// if (a > 0) {
|
||||
//// return (function () {
|
||||
//// return;
|
||||
//// return;
|
||||
//// return;
|
||||
////
|
||||
//// if (false) {
|
||||
//// return true;
|
||||
//// }
|
||||
//// throw "Hello!";
|
||||
//// })() || true;
|
||||
//// }
|
||||
////
|
||||
//// throw 10;
|
||||
////
|
||||
//// var unusued = [1, 2, 3, 4].map(x => { [|thr/**/ow|] 4 })
|
||||
////
|
||||
//// return;
|
||||
//// return true;
|
||||
//// throw false;
|
||||
////}
|
||||
|
||||
test.ranges().forEach(r => {
|
||||
goTo.position(r.start);
|
||||
|
||||
test.ranges().forEach(range => {
|
||||
verify.occurrencesAtPositionContains(range, false);
|
||||
});
|
||||
|
||||
verify.occurrencesAtPositionCount(test.ranges().length);
|
||||
});
|
||||
|
||||
goTo.marker();
|
||||
test.ranges().forEach(range => {
|
||||
verify.occurrencesAtPositionContains(range, false);
|
||||
});
|
||||
28
tests/cases/fourslash/getOccurrencesThrow6.ts
Normal file
28
tests/cases/fourslash/getOccurrencesThrow6.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////[|throw|] 100;
|
||||
////
|
||||
////try {
|
||||
//// throw 0;
|
||||
//// var x = () => { throw 0; };
|
||||
////}
|
||||
////catch (y) {
|
||||
//// var x = () => { throw 0; };
|
||||
//// [|throw|] 200;
|
||||
////}
|
||||
////finally {
|
||||
//// [|throw|] 300;
|
||||
////}
|
||||
|
||||
|
||||
|
||||
test.ranges().forEach(r => {
|
||||
goTo.position(r.start);
|
||||
|
||||
test.ranges().forEach(range => {
|
||||
verify.occurrencesAtPositionContains(range, false);
|
||||
});
|
||||
|
||||
verify.occurrencesAtPositionCount(test.ranges().length);
|
||||
});
|
||||
|
||||
31
tests/cases/fourslash/getOccurrencesThrow7.ts
Normal file
31
tests/cases/fourslash/getOccurrencesThrow7.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////try {
|
||||
//// [|throw|] 10;
|
||||
////
|
||||
//// try {
|
||||
//// throw 10;
|
||||
//// }
|
||||
//// catch (x) {
|
||||
//// [|throw|] 10;
|
||||
//// }
|
||||
//// finally {
|
||||
//// [|throw|] 10;
|
||||
//// }
|
||||
////}
|
||||
////finally {
|
||||
//// [|throw|] 10;
|
||||
////}
|
||||
////
|
||||
////[|throw|] 10;
|
||||
|
||||
test.ranges().forEach(r => {
|
||||
goTo.position(r.start);
|
||||
|
||||
test.ranges().forEach(range => {
|
||||
verify.occurrencesAtPositionContains(range, false);
|
||||
});
|
||||
|
||||
verify.occurrencesAtPositionCount(test.ranges().length);
|
||||
});
|
||||
|
||||
31
tests/cases/fourslash/getOccurrencesThrow8.ts
Normal file
31
tests/cases/fourslash/getOccurrencesThrow8.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////try {
|
||||
//// throw 10;
|
||||
////
|
||||
//// try {
|
||||
//// [|throw|] 10;
|
||||
//// }
|
||||
//// catch (x) {
|
||||
//// throw 10;
|
||||
//// }
|
||||
//// finally {
|
||||
//// throw 10;
|
||||
//// }
|
||||
////}
|
||||
////finally {
|
||||
//// throw 10;
|
||||
////}
|
||||
////
|
||||
////throw 10;
|
||||
|
||||
test.ranges().forEach(r => {
|
||||
goTo.position(r.start);
|
||||
|
||||
test.ranges().forEach(range => {
|
||||
verify.occurrencesAtPositionContains(range, false);
|
||||
});
|
||||
|
||||
verify.occurrencesAtPositionCount(test.ranges().length);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user