Skip to content

Commit 46296c0

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

File tree

7 files changed

+140
-48
lines changed

7 files changed

+140
-48
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: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ namespace FourSlash {
229229
}
230230
}
231231

232-
constructor(public originalInputFileName: string, private basePath: string, private testType: FourSlashTestType, public testData: FourSlashData) {
232+
constructor(private originalInputFileName: string, private basePath: string, private testType: FourSlashTestType, public testData: FourSlashData) {
233233
// Create a new Services Adapter
234234
this.cancellationToken = new TestCancellationToken();
235235
let compilationOptions = convertGlobalOptionsToCompilerOptions(this.testData.globalOptions);
@@ -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
}
@@ -3862,7 +3860,7 @@ namespace FourSlash {
38623860
markerValue = JSON.parse("{ " + text + " }");
38633861
}
38643862
catch (e) {
3865-
reportError(fileName, location.sourceLine, location.sourceColumn, "Unable to parse marker text " + e.message + "\nSource:\n {| " + text + " |}");
3863+
reportError(fileName, location.sourceLine, location.sourceColumn, "Unable to parse marker text " + e.message);
38663864
}
38673865

38683866
if (markerValue === undefined) {

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/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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy