Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 16 additions & 17 deletions src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,22 @@ module ts {
nodeIsNestedInLabel(label: Identifier, requireIterationStatement: boolean, stopAtFunctionBoundary: boolean): ControlBlockContext;
}

export function isKeyword(token: SyntaxKind): boolean {
return SyntaxKind.FirstKeyword <= token && token <= SyntaxKind.LastKeyword;
}

export function isModifier(token: SyntaxKind): boolean {
switch (token) {
case SyntaxKind.PublicKeyword:
case SyntaxKind.PrivateKeyword:
case SyntaxKind.StaticKeyword:
case SyntaxKind.ExportKeyword:
case SyntaxKind.DeclareKeyword:
return true;
}
return false;
}

export function createSourceFile(filename: string, sourceText: string, languageVersion: ScriptTarget, version: string, isOpen: boolean = false): SourceFile {
var file: SourceFile;
var scanner: Scanner;
Expand Down Expand Up @@ -853,23 +869,6 @@ module ts {
return parseIdentifierName();
}


function isKeyword(token: SyntaxKind): boolean {
return SyntaxKind.FirstKeyword <= token && token <= SyntaxKind.LastKeyword;
}

function isModifier(token: SyntaxKind): boolean {
switch (token) {
case SyntaxKind.PublicKeyword:
case SyntaxKind.PrivateKeyword:
case SyntaxKind.StaticKeyword:
case SyntaxKind.ExportKeyword:
case SyntaxKind.DeclareKeyword:
return true;
}
return false;
}

function parseContextualModifier(t: SyntaxKind): boolean {
return token === t && tryParse(() => {
nextToken();
Expand Down
153 changes: 143 additions & 10 deletions src/services/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1314,12 +1314,13 @@ module ts {

function isAnyFunction(node: Node): boolean {
switch (node.kind) {
case SyntaxKind.FunctionExpression:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.ArrowFunction:
case SyntaxKind.Method:
case SyntaxKind.FunctionExpression:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.ArrowFunction:
case SyntaxKind.Constructor:
return true;
}
return false;
Expand Down Expand Up @@ -1933,7 +1934,9 @@ module ts {
current = child;
continue outer;
}
if (child.end > position) break;
if (child.end > position) {
break;
}
}
return current;
}
Expand Down Expand Up @@ -2160,13 +2163,143 @@ module ts {
return undefined;
}

if (node.kind !== SyntaxKind.Identifier &&
!isLiteralNameOfPropertyDeclarationOrIndexAccess(node) &&
!isNameOfExternalModuleImportOrDeclaration(node)) {
if (node.kind === SyntaxKind.Identifier || isLiteralNameOfPropertyDeclarationOrIndexAccess(node) || isNameOfExternalModuleImportOrDeclaration(node)) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please wrap this. if it's too long to fit in a CR, it's too long :)

return getReferencesForNode(node, [sourceFile]);
}

switch (node.kind) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this comment should be on the actual parent/hasKind functions.

case SyntaxKind.TryKeyword:
case SyntaxKind.CatchKeyword:
case SyntaxKind.FinallyKeyword:
if (hasKind(parent(parent(node)), SyntaxKind.TryStatement)) {
return getTryCatchFinallyOccurrences(<TryStatement>node.parent.parent);
}
break;
case SyntaxKind.SwitchKeyword:
if (hasKind(node.parent, SyntaxKind.SwitchStatement)) {
return getSwitchCaseDefaultOccurrences(<SwitchStatement>node.parent);
}
break;
case SyntaxKind.CaseKeyword:
case SyntaxKind.DefaultKeyword:
if (hasKind(parent(parent(node)), SyntaxKind.SwitchStatement)) {
return getSwitchCaseDefaultOccurrences(<SwitchStatement>node.parent.parent);
}
break;
case SyntaxKind.BreakKeyword:
if (hasKind(node.parent, SyntaxKind.BreakStatement)) {
return getBreakStatementOccurences(<BreakOrContinueStatement>node.parent);
}
break;
}

return undefined;

function getTryCatchFinallyOccurrences(tryStatement: TryStatement): ReferenceEntry[] {
var keywords: Node[] = [];

pushKeywordIf(keywords, tryStatement.getFirstToken(), SyntaxKind.TryKeyword);

if (tryStatement.catchBlock) {
pushKeywordIf(keywords, tryStatement.catchBlock.getFirstToken(), SyntaxKind.CatchKeyword);
}

if (tryStatement.finallyBlock) {
pushKeywordIf(keywords, tryStatement.finallyBlock.getFirstToken(), SyntaxKind.FinallyKeyword);
}

return keywordsToReferenceEntries(keywords);
}

function getSwitchCaseDefaultOccurrences(switchStatement: SwitchStatement) {
var keywords: Node[] = [];

pushKeywordIf(keywords, switchStatement.getFirstToken(), SyntaxKind.SwitchKeyword);

// Go through each clause in the switch statement, collecting the clause keywords.
forEach(switchStatement.clauses, clause => {
pushKeywordIf(keywords, clause.getFirstToken(), SyntaxKind.CaseKeyword, SyntaxKind.DefaultKeyword);

// For each clause, also recursively traverse the statements where we can find analogous breaks.
forEachChild(clause, function aggregateBreakKeywords(node: Node): void {
switch (node.kind) {
case SyntaxKind.BreakStatement:
// If the break statement has a label, it cannot be part of a switch block.
if (!(<BreakOrContinueStatement>node).label) {
pushKeywordIf(keywords, node.getFirstToken(), SyntaxKind.BreakKeyword);
}
// Fall through
case SyntaxKind.ForStatement:
case SyntaxKind.ForInStatement:
case SyntaxKind.DoStatement:
case SyntaxKind.WhileStatement:
case SyntaxKind.SwitchStatement:
return;
}

// Do not cross function boundaries.
if (!isAnyFunction(node)) {
forEachChild(node, aggregateBreakKeywords);
}
});
});

return keywordsToReferenceEntries(keywords);
}

function getBreakStatementOccurences(breakStatement: BreakOrContinueStatement): ReferenceEntry[]{
// TODO (drosen): Deal with labeled statements.
if (breakStatement.label) {
return undefined;
}

for (var owner = node.parent; owner; owner = owner.parent) {
switch (owner.kind) {
case SyntaxKind.ForStatement:
case SyntaxKind.ForInStatement:
case SyntaxKind.DoStatement:
case SyntaxKind.WhileStatement:
// TODO (drosen): Handle loops!
return undefined;

case SyntaxKind.SwitchStatement:
return getSwitchCaseDefaultOccurrences(<SwitchStatement>owner);

default:
if (isAnyFunction(owner)) {
return undefined;
}
}
}

return undefined;
}

return getReferencesForNode(node, [sourceFile]);
// returns true if 'node' is defined and has a matching 'kind'.
function hasKind(node: Node, kind: SyntaxKind) {
return !!(node && node.kind === kind);
}

// Null-propagating 'parent' function.
function parent(node: Node): Node {
return node && node.parent;
}

function pushKeywordIf(keywordList: Node[], token: Node, ...expected: SyntaxKind[]): void {
if (!token) {
return;
}

if (contains(<SyntaxKind[]>expected, token.kind)) {
keywordList.push(token);
}
}

function keywordsToReferenceEntries(keywords: Node[]): ReferenceEntry[]{
return map(keywords, keyword =>
new ReferenceEntry(filename, TypeScript.TextSpan.fromBounds(keyword.getStart(), keyword.end), /* isWriteAccess */ false)
);
}
}

function getReferencesAtPosition(filename: string, position: number): ReferenceEntry[] {
Expand Down Expand Up @@ -2284,13 +2417,13 @@ module ts {
var container = getContainerNode(declarations[i]);

if (scope && scope !== container) {
// Diffrent declarations have diffrent containers, bail out
// Different declarations have different containers, bail out
return undefined;
}

if (container.kind === SyntaxKind.SourceFile && !isExternalModule(<SourceFile>container)) {
// This is a global variable and not an external module, any declaration defined
// withen this scope is visible outside the file
// within this scope is visible outside the file
return undefined;
}

Expand Down Expand Up @@ -2957,7 +3090,7 @@ module ts {
// ["// hack 1", "// ", "hack 1", undefined, "hack"]
//
// Here are the relevant capture groups:
// 0) The full match for hte entire regex.
// 0) The full match for the entire regex.
// 1) The preamble to the message portion.
// 2) The message portion.
// 3...N) The descriptor that was matched - by index. 'undefined' for each
Expand Down
29 changes: 29 additions & 0 deletions tests/cases/fourslash/getOccurrencesSwitchCaseDefault.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/// <reference path='fourslash.ts' />

////[|sw/*1*/itch|] (10) {
//// [|/*2*/case|] 1:
//// [|cas/*3*/e|] 2:
//// [|c/*4*/ase|] 4:
//// [|c/*5*/ase|] 8:
//// foo: switch (20) {
//// case 1:
//// case 2:
//// break;
//// default:
//// break foo;
//// }
//// [|cas/*6*/e|] 0xBEEF:
//// [|defa/*7*/ult|]:
//// [|bre/*9*/ak|];
//// [|/*8*/case|] 16:
////}


for (var i = 1; i <= test.markers().length; i++) {
goTo.marker("" + i);
verify.occurrencesAtPositionCount(9);

test.ranges().forEach(range => {
verify.occurrencesAtPositionContains(range, false);
});
}
29 changes: 29 additions & 0 deletions tests/cases/fourslash/getOccurrencesSwitchCaseDefault2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/// <reference path='fourslash.ts' />

////switch (10) {
//// case 1:
//// case 2:
//// case 4:
//// case 8:
//// foo: [|swi/*1*/tch|] (20) {
//// [|/*2*/case|] 1:
//// [|cas/*3*/e|] 2:
//// [|b/*4*/reak|];
//// [|defaul/*5*/t|]:
//// break foo;
//// }
//// case 0xBEEF:
//// default:
//// break;
//// case 16:
////}


for (var i = 1; i <= test.markers().length; i++) {
goTo.marker("" + i);
verify.occurrencesAtPositionCount(5);

test.ranges().forEach(range => {
verify.occurrencesAtPositionContains(range, false);
});
}
57 changes: 57 additions & 0 deletions tests/cases/fourslash/getOccurrencesSwitchCaseDefaultBroken.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/// <reference path='fourslash.ts' />

////swi/*1*/tch(10) {
//// case 1:
//// case 2:
//// c/*2*/ase 4:
//// case 8:
//// case 0xBEEF:
//// de/*4*/fult:
//// break;
//// /*5*/cas 16:
//// c/*3*/ase 12:
//// function f() {
//// br/*11*/eak;
//// /*12*/break;
//// }
////}
////
////sw/*6*/itch (10) {
//// de/*7*/fault
//// case 1:
//// case 2
////
//// c/*8*/ose 4:
//// case 8:
//// case 0xBEEF:
//// bre/*9*/ak;
//// case 16:
//// () => bre/*10*/ak;
////}

for (var i = 1; i <= test.markers().length; i++) {
goTo.marker("" + i);

switch (i) {
case 1:
case 2:
case 3:
verify.occurrencesAtPositionCount(8);
break;
case 4:
case 5:
case 8:
verify.occurrencesAtPositionCount(1);
break;
case 6:
case 7:
case 9:
verify.occurrencesAtPositionCount(8);
break;
case 10:
case 11:
case 12:
verify.occurrencesAtPositionCount(0);
break;
}
}
25 changes: 25 additions & 0 deletions tests/cases/fourslash/getOccurrencesSwitchCaseDefaultNegatives.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/// <reference path='fourslash.ts' />

////switch/*1*/ (10) {
//// case/*2*/ 1:
//// case/*3*/ 2:
//// case/*4*/ 4:
//// case/*5*/ 8:
//// foo: switch/*6*/ (20) {
//// case/*7*/ 1:
//// case/*8*/ 2:
//// break/*9*/;
//// default/*10*/:
//// break foo;
//// }
//// case/*11*/ 0xBEEF:
//// default/*12*/:
//// break/*13*/;
//// case 16/*14*/:
////}


for (var i = 1; i <= test.markers().length; i++) {
goTo.marker("" + i);
verify.occurrencesAtPositionCount(0);
}
Loading