Skip to content

Commit e39890c

Browse files
committed
Add additional type and expression keywords
1 parent 6fdba9f commit e39890c

File tree

9 files changed

+144
-47
lines changed

9 files changed

+144
-47
lines changed

src/compiler/checker.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -413,9 +413,9 @@ namespace ts {
413413
location = getParseTreeNode(location);
414414
return location ? getSymbolsInScope(location, meaning) : [];
415415
},
416-
getSymbolAtLocation: (node: Node) => {
416+
getSymbolAtLocation: node => {
417417
node = getParseTreeNode(node);
418-
return node && getSymbolAtLocation(node);
418+
return node ? getSymbolAtLocation(node) : undefined;
419419
},
420420
getShorthandAssignmentValueSymbol: node => {
421421
node = getParseTreeNode(node);
@@ -34249,7 +34249,7 @@ namespace ts {
3424934249
if (constructorDeclaration && constructorDeclaration.kind === SyntaxKind.Constructor) {
3425034250
return (<ClassDeclaration>constructorDeclaration.parent).symbol;
3425134251
}
34252-
break;
34252+
return undefined;
3425334253

3425434254
case SyntaxKind.StringLiteral:
3425534255
case SyntaxKind.NoSubstitutionTemplateLiteral:
@@ -34287,10 +34287,10 @@ namespace ts {
3428734287
return isLiteralImportTypeNode(node) ? getSymbolAtLocation(node.argument.literal) : undefined;
3428834288

3428934289
case SyntaxKind.ExportKeyword:
34290-
if (isExportAssignment(node.parent)) {
34291-
return Debug.assertDefined(node.parent.symbol);
34292-
}
34293-
break;
34290+
return isExportAssignment(node.parent) ? Debug.assertDefined(node.parent.symbol) : undefined;
34291+
34292+
default:
34293+
return undefined;
3429434294
}
3429534295
}
3429634296

src/harness/fourslashImpl.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2288,7 +2288,7 @@ namespace FourSlash {
22882288
return result;
22892289
}
22902290

2291-
private rangeText({ fileName, pos, end }: Range): string {
2291+
public rangeText({ fileName, pos, end }: Range): string {
22922292
return this.getFileContent(fileName).slice(pos, end);
22932293
}
22942294

@@ -3637,18 +3637,17 @@ namespace FourSlash {
36373637
const testData = parseTestData(absoluteBasePath, content, absoluteFileName);
36383638
const state = new TestState(absoluteFileName, absoluteBasePath, testType, testData);
36393639
const actualFileName = Harness.IO.resolvePath(fileName) || absoluteFileName;
3640-
const output = ts.transpileModule(content, { reportDiagnostics: true, fileName: actualFileName, compilerOptions: { target: ts.ScriptTarget.ES2015, sourceMap: true } });
3640+
const output = ts.transpileModule(content, { reportDiagnostics: true, fileName: actualFileName, compilerOptions: { target: ts.ScriptTarget.ES2015, inlineSourceMap: true } });
36413641
if (output.diagnostics!.length > 0) {
36423642
throw new Error(`Syntax error in ${absoluteBasePath}: ${output.diagnostics![0].messageText}`);
36433643
}
3644-
runCode(output, state, actualFileName);
3644+
runCode(output.outputText, state, actualFileName);
36453645
}
36463646

3647-
function runCode(output: ts.TranspileOutput, state: TestState, fileName: string): void {
3647+
function runCode(code: string, state: TestState, fileName: string): void {
36483648
// Compile and execute the test
36493649
const generatedFile = ts.changeExtension(fileName, ".js");
3650-
const mapFile = generatedFile + ".map";
3651-
const wrappedCode = `(function(test, goTo, plugins, verify, edit, debug, format, cancellation, classification, completion, verifyOperationIsCancelled) {${output.outputText}\n//# sourceURL=${generatedFile}\n})`;
3650+
const wrappedCode = `(function(test, goTo, plugins, verify, edit, debug, format, cancellation, classification, completion, verifyOperationIsCancelled) {${code}\n//# sourceURL=${generatedFile}\n})`;
36523651

36533652
type SourceMapSupportModule = typeof import("source-map-support") & {
36543653
// TODO(rbuckton): This is missing from the DT definitions and needs to be added.
@@ -3668,7 +3667,6 @@ namespace FourSlash {
36683667
sourceMapSupportModule?.install({
36693668
retrieveFile: path => {
36703669
return path === generatedFile ? wrappedCode :
3671-
path === mapFile ? output.sourceMapText! :
36723670
undefined!;
36733671
}
36743672
});
@@ -3687,7 +3685,7 @@ namespace FourSlash {
36873685
f(test, goTo, plugins, verify, edit, debug, format, cancellation, FourSlashInterface.Classification, FourSlashInterface.Completion, verifyOperationIsCancelled);
36883686
}
36893687
catch (err) {
3690-
// ensure we trigger 'source-map-support' while we still have the handler attached
3688+
// ensure 'source-map-support' is triggered while we still have the handler attached by accessing `error.stack`.
36913689
err.stack?.toString();
36923690
throw err;
36933691
}

src/harness/fourslashInterfaceImpl.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ namespace FourSlashInterface {
2323
return this.state.getRanges();
2424
}
2525

26+
public rangeText(range: FourSlash.Range): string {
27+
return this.state.rangeText(range);
28+
}
29+
2630
public spans(): ts.TextSpan[] {
2731
return this.ranges().map(r => ts.createTextSpan(r.pos, r.end - r.pos));
2832
}

src/services/utilities.ts

Lines changed: 45 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ namespace ts {
8888
}
8989

9090
export function getMeaningFromLocation(node: Node): SemanticMeaning {
91+
node = getAdjustedReferenceLocation(node);
9192
if (node.kind === SyntaxKind.SourceFile) {
9293
return SemanticMeaning.Value;
9394
}
@@ -114,24 +115,6 @@ namespace ts {
114115
// This might be T["name"], which is actually referencing a property and not a type. So allow both meanings.
115116
return SemanticMeaning.Type | SemanticMeaning.Value;
116117
}
117-
else if (isModifier(node) && contains(node.parent.modifiers, node)) {
118-
// on the modifier of a declaration
119-
return getMeaningFromDeclaration(node.parent);
120-
}
121-
else if (node.kind === SyntaxKind.ClassKeyword && isClassLike(node.parent) ||
122-
node.kind === SyntaxKind.InterfaceKeyword && isInterfaceDeclaration(node.parent) ||
123-
node.kind === SyntaxKind.TypeKeyword && isTypeAliasDeclaration(node.parent) ||
124-
node.kind === SyntaxKind.EnumKeyword && isEnumDeclaration(node.parent) ||
125-
node.kind === SyntaxKind.FunctionKeyword && isFunctionLikeDeclaration(node.parent) ||
126-
node.kind === SyntaxKind.GetKeyword && isGetAccessorDeclaration(node.parent) ||
127-
node.kind === SyntaxKind.SetKeyword && isSetAccessorDeclaration(node.parent) ||
128-
(node.kind === SyntaxKind.NamespaceKeyword || node.kind === SyntaxKind.ModuleKeyword) && isModuleDeclaration(node.parent)) {
129-
// on the keyword of a declaration
130-
return getMeaningFromDeclaration(node.parent);
131-
}
132-
else if (node.kind === SyntaxKind.TypeKeyword && isImportClause(node.parent) && node.parent.isTypeOnly) {
133-
return getMeaningFromDeclaration(node.parent.parent);
134-
}
135118
else {
136119
return SemanticMeaning.Value;
137120
}
@@ -986,8 +969,36 @@ namespace ts {
986969
return location;
987970
}
988971
}
972+
if (node.kind === SyntaxKind.ExtendsKeyword) {
973+
// ... <T /**/extends [|U|]> ...
974+
if (isTypeParameterDeclaration(parent) && parent.constraint && isTypeReferenceNode(parent.constraint)) {
975+
return parent.constraint.typeName;
976+
}
977+
// ... T /**/extends [|U|] ? ...
978+
if (isConditionalTypeNode(parent) && isTypeReferenceNode(parent.extendsType)) {
979+
return parent.extendsType.typeName;
980+
}
981+
}
982+
// ... T extends /**/infer [|U|] ? ...
983+
if (node.kind === SyntaxKind.InferKeyword && isInferTypeNode(parent)) {
984+
return parent.typeParameter.name;
985+
}
986+
// { [ [|K|] /**/in keyof T]: ... }
987+
if (node.kind === SyntaxKind.InKeyword && isTypeParameterDeclaration(parent) && isMappedTypeNode(parent.parent)) {
988+
return parent.name;
989+
}
990+
// /**/keyof [|T|]
991+
if (node.kind === SyntaxKind.KeyOfKeyword && isTypeOperatorNode(parent) && parent.operator === SyntaxKind.KeyOfKeyword &&
992+
isTypeReferenceNode(parent.type)) {
993+
return parent.type.typeName;
994+
}
995+
// /**/readonly [|name|][]
996+
if (node.kind === SyntaxKind.ReadonlyKeyword && isTypeOperatorNode(parent) && parent.operator === SyntaxKind.ReadonlyKeyword &&
997+
isArrayTypeNode(parent.type) && isTypeReferenceNode(parent.type.elementType)) {
998+
return parent.type.elementType.typeName;
999+
}
9891000
if (!forRename) {
990-
// /**/new [|name|](...)
1001+
// /**/new [|name|]
9911002
// /**/void [|name|]
9921003
// /**/void obj.[|name|]
9931004
// /**/typeof [|name|]
@@ -1007,6 +1018,21 @@ namespace ts {
10071018
return skipOuterExpressions(parent.expression);
10081019
}
10091020
}
1021+
// left /**/in [|name|]
1022+
// left /**/instanceof [|name|]
1023+
if ((node.kind === SyntaxKind.InKeyword || node.kind === SyntaxKind.InstanceOfKeyword) && isBinaryExpression(parent) && parent.operatorToken === node) {
1024+
return skipOuterExpressions(parent.right);
1025+
}
1026+
// left /**/as [|name|]
1027+
if (node.kind === SyntaxKind.AsKeyword && isAsExpression(parent) && isTypeReferenceNode(parent.type)) {
1028+
return parent.type.typeName;
1029+
}
1030+
// for (... /**/in [|name|])
1031+
// for (... /**/of [|name|])
1032+
if (node.kind === SyntaxKind.InKeyword && isForInStatement(parent) ||
1033+
node.kind === SyntaxKind.OfKeyword && isForOfStatement(parent)) {
1034+
return skipOuterExpressions(parent.expression);
1035+
}
10101036
}
10111037
return node;
10121038
}

tests/cases/fourslash/fourslash.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ declare namespace FourSlashInterface {
181181
ranges(): Range[];
182182
spans(): Array<{ start: number, length: number }>;
183183
rangesByText(): ts.Map<Range[]>;
184+
rangeText(range: Range): string;
184185
markerByName(s: string): Marker;
185186
symbolsInScope(range: Range): any[];
186187
setTypesRegistry(map: { [key: string]: void }): void;

tests/cases/fourslash/referencesForDeclarationKeywords.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ const [
7171
] = test.ranges();
7272
verify.referenceGroups(classDecl1_classKeyword, [{ definition: "class C1", ranges: [classDecl1_name] }]);
7373
verify.referenceGroups(classDecl1_extendsKeyword, [{ definition: "class Base", ranges: [baseDecl_name, classDecl1_extendsName, interfaceDecl1_extendsName] }]);
74-
verify.referenceGroups(classDecl1_implementsKeyword, [{ definition: "", ranges: [implemented1Decl_name, classDecl1_implementsName] }]);
74+
verify.referenceGroups(classDecl1_implementsKeyword, [{ definition: "interface Implemented1", ranges: [implemented1Decl_name, classDecl1_implementsName] }]);
7575
for (const keyword of [getDecl_getKeyword, setDecl_setKeyword]) {
7676
verify.referenceGroups(keyword, [{ definition: "(property) C1.e: number", ranges: [getDecl_name, setDecl_name] }]);
7777
}

tests/cases/fourslash/referencesForExpressionKeywords.ts

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,42 @@
33
////[|class [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeDelta": -1 |}C|] {
44
//// [|static [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeDelta": -1 |}x|] = 1;|]
55
////}|]
6-
/////*newKeyword*/new [|C|]();
7-
/////*voidKeyword*/void [|C|];
8-
/////*typeofKeyword*/typeof [|C|];
9-
/////*deleteKeyword*/delete [|C|].[|x|];
6+
////[|new|] [|C|]();
7+
////[|void|] [|C|];
8+
////[|typeof|] [|C|];
9+
////[|delete|] [|C|].[|x|];
1010
////async function* f() {
11-
//// /*yieldKeyword*/yield [|C|];
12-
//// /*awaitKeyword*/await [|C|];
11+
//// [|yield|] [|C|];
12+
//// [|await|] [|C|];
1313
////}
14+
////"x" [|in|] [|C|];
15+
////undefined [|instanceof|] [|C|];
16+
////undefined [|as|] [|C|];
1417

15-
const [, classDef,, xDef, newC, voidC, typeofC, deleteC, deleteCx, yieldC, awaitC] = test.ranges();
16-
for (const keyword of ["newKeyword", "voidKeyword", "typeofKeyword", "yieldKeyword", "awaitKeyword"]) {
17-
verify.referenceGroups(keyword, [{ definition: "class C", ranges: [classDef, newC, voidC, typeofC, deleteC, yieldC, awaitC] }]);
18-
}
19-
verify.referenceGroups("deleteKeyword", [{ definition: "(property) C.x: number", ranges: [xDef, deleteCx] }]);
18+
const [
19+
classDecl,
20+
classDecl_name,
21+
fieldDecl,
22+
fieldDecl_name,
23+
newKeyword,
24+
newC,
25+
voidKeyword,
26+
voidC,
27+
typeofKeyword,
28+
typeofC,
29+
deleteKeyword,
30+
deleteC,
31+
deleteCx,
32+
yieldKeyword,
33+
yieldC,
34+
awaitKeyword,
35+
awaitC,
36+
inKeyword,
37+
inC,
38+
instanceofKeyword,
39+
instanceofC,
40+
asKeyword,
41+
asC,
42+
] = test.ranges();
43+
verify.referenceGroups([newKeyword, voidKeyword, typeofKeyword, yieldKeyword, awaitKeyword, inKeyword, instanceofKeyword, asKeyword], [{ definition: "class C", ranges: [classDecl_name, newC, voidC, typeofC, deleteC, yieldC, awaitC, inC, instanceofC, asC] }]);
44+
verify.referenceGroups(deleteKeyword, [{ definition: "(property) C.x: number", ranges: [fieldDecl_name, deleteCx] }]);

tests/cases/fourslash/referencesForStatementKeywords.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ verify.referenceGroups([exportDecl4_exportKeyword, exportDecl4_typeKeyword, expo
249249
verify.referenceGroups(exportDecl4_asKeyword, [{ definition: "(alias) const j3: 2\nexport j3", ranges: [exportDecl4_name] }]);
250250

251251
// exportDecl5:
252-
verify.referenceGroups([exportDecl5_exportKeyword, exportDecl5_typeKeyword], [{ definition: "", ranges: [typeDecl1_name, exportDecl5_name] }]);
252+
verify.referenceGroups([exportDecl5_exportKeyword, exportDecl5_typeKeyword], [{ definition: "type Z1 = 1", ranges: [typeDecl1_name, exportDecl5_name] }]);
253253

254254
// exportDecl6:
255255
verify.noReferences(exportDecl6_exportKeyword);
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/// <reference path='fourslash.ts'/>
2+
3+
////[|{| "id": "interfaceDecl" |}interface [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeId": "interfaceDecl" |}I|] {}|]
4+
////function f<T [|extends|] [|I|]>() {}
5+
////type A1<T, [|{| "isWriteAccess": true, "isDefinition": true |}U|]> = T [|extends|] [|U|] ? 1 : 0;
6+
////type A2<T> = T extends [|infer|] [|{| "isWriteAccess": true, "isDefinition": true |}U|] ? 1 : 0;
7+
////type A3<T> = { [[|{| "id": "mappedType_param" |}[|{| "isWriteAccess": true, "isDefinition": true, "contextRangeId": "mappedType_param" |}P|] [|in|] keyof T|]]: 1 };
8+
////type A4<[|{| "isWriteAccess": true, "isDefinition": true |}T|]> = [|keyof|] [|T|];
9+
////type A5<[|{| "isWriteAccess": true, "isDefinition": true |}T|]> = [|readonly|] [|T|][];
10+
11+
const [
12+
interfaceDecl,
13+
interfaceDecl_name,
14+
15+
typeParam_extendsKeyword,
16+
typeParam_constraint,
17+
18+
typeParamA1_name,
19+
conditionalType_extendsKeyword,
20+
conditionalType_extendsType,
21+
22+
inferType_inferKeyword,
23+
inferType_type,
24+
25+
mappedType_param,
26+
mappedType_name,
27+
mappedType_inOperator,
28+
29+
typeParamA4_name,
30+
keyofOperator_keyofKeyword,
31+
keyofOperator_type,
32+
33+
typeParamA5_name,
34+
readonlyOperator_readonlyKeyword,
35+
readonlyOperator_elementType,
36+
] = test.ranges();
37+
38+
verify.referenceGroups(typeParam_extendsKeyword, [{ definition: "interface I", ranges: [interfaceDecl_name, typeParam_constraint] }]);
39+
verify.referenceGroups(conditionalType_extendsKeyword, [{ definition: "(type parameter) U in type A1<T, U>", ranges: [typeParamA1_name, conditionalType_extendsType] }]);
40+
verify.referenceGroups(inferType_inferKeyword, [{ definition: "(type parameter) U", ranges: [inferType_type] }]);
41+
verify.referenceGroups(mappedType_inOperator, [{ definition: "(type parameter) P", ranges: [mappedType_name] }]);
42+
verify.referenceGroups(keyofOperator_keyofKeyword, [{ definition: "(type parameter) T in type A4<T>", ranges: [typeParamA4_name, keyofOperator_type] }]);
43+
verify.referenceGroups(readonlyOperator_readonlyKeyword, [{ definition: "(type parameter) T in type A5<T>", ranges: [typeParamA5_name, readonlyOperator_elementType] }]);

0 commit comments

Comments
 (0)