Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/build/lib/propertyInitOrderChecker.js
3520 views
1
"use strict";
2
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
if (k2 === undefined) k2 = k;
4
var desc = Object.getOwnPropertyDescriptor(m, k);
5
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
desc = { enumerable: true, get: function() { return m[k]; } };
7
}
8
Object.defineProperty(o, k2, desc);
9
}) : (function(o, m, k, k2) {
10
if (k2 === undefined) k2 = k;
11
o[k2] = m[k];
12
}));
13
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
Object.defineProperty(o, "default", { enumerable: true, value: v });
15
}) : function(o, v) {
16
o["default"] = v;
17
});
18
var __importStar = (this && this.__importStar) || (function () {
19
var ownKeys = function(o) {
20
ownKeys = Object.getOwnPropertyNames || function (o) {
21
var ar = [];
22
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
return ar;
24
};
25
return ownKeys(o);
26
};
27
return function (mod) {
28
if (mod && mod.__esModule) return mod;
29
var result = {};
30
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
__setModuleDefault(result, mod);
32
return result;
33
};
34
})();
35
Object.defineProperty(exports, "__esModule", { value: true });
36
/*---------------------------------------------------------------------------------------------
37
* Copyright (c) Microsoft Corporation. All rights reserved.
38
* Licensed under the MIT License. See License.txt in the project root for license information.
39
*--------------------------------------------------------------------------------------------*/
40
const ts = __importStar(require("typescript"));
41
const path = __importStar(require("path"));
42
const fs = __importStar(require("fs"));
43
const TS_CONFIG_PATH = path.join(__dirname, '../../', 'src', 'tsconfig.json');
44
//
45
// #############################################################################################
46
//
47
// A custom typescript checker that ensure constructor properties are NOT used to initialize
48
// defined properties. This is needed for the times when `useDefineForClassFields` is gone.
49
//
50
// see https://github.com/microsoft/vscode/issues/243049, https://github.com/microsoft/vscode/issues/186726,
51
// https://github.com/microsoft/vscode/pull/241544
52
//
53
// #############################################################################################
54
//
55
var EntryKind;
56
(function (EntryKind) {
57
EntryKind[EntryKind["Span"] = 0] = "Span";
58
EntryKind[EntryKind["Node"] = 1] = "Node";
59
EntryKind[EntryKind["StringLiteral"] = 2] = "StringLiteral";
60
EntryKind[EntryKind["SearchedLocalFoundProperty"] = 3] = "SearchedLocalFoundProperty";
61
EntryKind[EntryKind["SearchedPropertyFoundLocal"] = 4] = "SearchedPropertyFoundLocal";
62
})(EntryKind || (EntryKind = {}));
63
const cancellationToken = {
64
isCancellationRequested: () => false,
65
throwIfCancellationRequested: () => { },
66
};
67
const seenFiles = new Set();
68
let errorCount = 0;
69
function createProgram(tsconfigPath) {
70
const tsConfig = ts.readConfigFile(tsconfigPath, ts.sys.readFile);
71
const configHostParser = { fileExists: fs.existsSync, readDirectory: ts.sys.readDirectory, readFile: file => fs.readFileSync(file, 'utf8'), useCaseSensitiveFileNames: process.platform === 'linux' };
72
const tsConfigParsed = ts.parseJsonConfigFileContent(tsConfig.config, configHostParser, path.resolve(path.dirname(tsconfigPath)), { noEmit: true });
73
const compilerHost = ts.createCompilerHost(tsConfigParsed.options, true);
74
return ts.createProgram(tsConfigParsed.fileNames, tsConfigParsed.options, compilerHost);
75
}
76
const program = createProgram(TS_CONFIG_PATH);
77
program.getTypeChecker();
78
for (const file of program.getSourceFiles()) {
79
if (!file || file.isDeclarationFile) {
80
continue;
81
}
82
visit(file);
83
}
84
if (seenFiles.size) {
85
console.log();
86
console.log(`Found ${errorCount} error${errorCount === 1 ? '' : 's'} in ${seenFiles.size} file${seenFiles.size === 1 ? '' : 's'}.`);
87
process.exit(errorCount);
88
}
89
function visit(node) {
90
if (ts.isParameter(node) && ts.isParameterPropertyDeclaration(node, node.parent)) {
91
checkParameterPropertyDeclaration(node);
92
}
93
ts.forEachChild(node, visit);
94
}
95
function checkParameterPropertyDeclaration(param) {
96
const uses = [...collectReferences(param.name, [])];
97
if (!uses.length) {
98
return;
99
}
100
const sourceFile = param.getSourceFile();
101
if (!seenFiles.has(sourceFile)) {
102
if (seenFiles.size) {
103
console.log(``);
104
}
105
console.log(`${formatFileName(param)}:`);
106
seenFiles.add(sourceFile);
107
}
108
else {
109
console.log(``);
110
}
111
console.log(` Parameter property '${param.name.getText()}' is used before its declaration.`);
112
for (const { stack, container } of uses) {
113
const use = stack[stack.length - 1];
114
console.log(` at ${formatLocation(use)}: ${formatMember(container)} -> ${formatStack(stack)}`);
115
errorCount++;
116
}
117
}
118
function* collectReferences(node, stack, requiresInvocationDepth = 0, seen = new Set()) {
119
for (const use of findAllReferencesInClass(node)) {
120
const container = findContainer(use);
121
if (!container || seen.has(container) || ts.isConstructorDeclaration(container)) {
122
continue;
123
}
124
seen.add(container);
125
const nextStack = [...stack, use];
126
let nextRequiresInvocationDepth = requiresInvocationDepth;
127
if (isInvocation(use) && nextRequiresInvocationDepth > 0) {
128
nextRequiresInvocationDepth--;
129
}
130
if (ts.isPropertyDeclaration(container) && nextRequiresInvocationDepth === 0) {
131
yield { stack: nextStack, container };
132
}
133
else if (requiresInvocation(container)) {
134
nextRequiresInvocationDepth++;
135
}
136
yield* collectReferences(container.name ?? container, nextStack, nextRequiresInvocationDepth, seen);
137
}
138
}
139
function requiresInvocation(definition) {
140
return ts.isMethodDeclaration(definition) || ts.isFunctionDeclaration(definition) || ts.isFunctionExpression(definition) || ts.isArrowFunction(definition);
141
}
142
function isInvocation(use) {
143
let location = use;
144
if (ts.isPropertyAccessExpression(location.parent) && location.parent.name === location) {
145
location = location.parent;
146
}
147
else if (ts.isElementAccessExpression(location.parent) && location.parent.argumentExpression === location) {
148
location = location.parent;
149
}
150
return ts.isCallExpression(location.parent) && location.parent.expression === location
151
|| ts.isTaggedTemplateExpression(location.parent) && location.parent.tag === location;
152
}
153
function formatFileName(node) {
154
const sourceFile = node.getSourceFile();
155
return path.resolve(sourceFile.fileName);
156
}
157
function formatLocation(node) {
158
const sourceFile = node.getSourceFile();
159
const { line, character } = ts.getLineAndCharacterOfPosition(sourceFile, node.pos);
160
return `${formatFileName(sourceFile)}(${line + 1},${character + 1})`;
161
}
162
function formatStack(stack) {
163
return stack.slice().reverse().map((use) => formatUse(use)).join(' -> ');
164
}
165
function formatMember(container) {
166
const name = container.name?.getText();
167
if (name) {
168
const className = findClass(container)?.name?.getText();
169
if (className) {
170
return `${className}.${name}`;
171
}
172
return name;
173
}
174
return '<unknown>';
175
}
176
function formatUse(use) {
177
let text = use.getText();
178
if (use.parent && ts.isPropertyAccessExpression(use.parent) && use.parent.name === use) {
179
if (use.parent.expression.kind === ts.SyntaxKind.ThisKeyword) {
180
text = `this.${text}`;
181
}
182
use = use.parent;
183
}
184
else if (use.parent && ts.isElementAccessExpression(use.parent) && use.parent.argumentExpression === use) {
185
if (use.parent.expression.kind === ts.SyntaxKind.ThisKeyword) {
186
text = `this['${text}']`;
187
}
188
use = use.parent;
189
}
190
if (ts.isCallExpression(use.parent)) {
191
text = `${text}(...)`;
192
}
193
return text;
194
}
195
function findContainer(node) {
196
return ts.findAncestor(node, ancestor => {
197
switch (ancestor.kind) {
198
case ts.SyntaxKind.PropertyDeclaration:
199
case ts.SyntaxKind.MethodDeclaration:
200
case ts.SyntaxKind.GetAccessor:
201
case ts.SyntaxKind.SetAccessor:
202
case ts.SyntaxKind.Constructor:
203
case ts.SyntaxKind.ClassStaticBlockDeclaration:
204
case ts.SyntaxKind.ArrowFunction:
205
case ts.SyntaxKind.FunctionExpression:
206
case ts.SyntaxKind.FunctionDeclaration:
207
case ts.SyntaxKind.Parameter:
208
return true;
209
}
210
return false;
211
});
212
}
213
function findClass(node) {
214
return ts.findAncestor(node, ts.isClassLike);
215
}
216
function* findAllReferencesInClass(node) {
217
const classDecl = findClass(node);
218
if (!classDecl) {
219
return [];
220
}
221
for (const ref of findAllReferences(node)) {
222
for (const entry of ref.references) {
223
if (entry.kind !== EntryKind.Node || entry.node === node) {
224
continue;
225
}
226
if (findClass(entry.node) === classDecl) {
227
yield entry.node;
228
}
229
}
230
}
231
}
232
// NOTE: The following uses TypeScript internals and are subject to change from version to version.
233
function findAllReferences(node) {
234
const sourceFile = node.getSourceFile();
235
const position = node.getStart();
236
const name = ts.getTouchingPropertyName(sourceFile, position);
237
const options = { use: ts.FindAllReferences.FindReferencesUse.References };
238
return ts.FindAllReferences.Core.getReferencedSymbolsForNode(position, name, program, [sourceFile], cancellationToken, options) ?? [];
239
}
240
var DefinitionKind;
241
(function (DefinitionKind) {
242
DefinitionKind[DefinitionKind["Symbol"] = 0] = "Symbol";
243
DefinitionKind[DefinitionKind["Label"] = 1] = "Label";
244
DefinitionKind[DefinitionKind["Keyword"] = 2] = "Keyword";
245
DefinitionKind[DefinitionKind["This"] = 3] = "This";
246
DefinitionKind[DefinitionKind["String"] = 4] = "String";
247
DefinitionKind[DefinitionKind["TripleSlashReference"] = 5] = "TripleSlashReference";
248
})(DefinitionKind || (DefinitionKind = {}));
249
//# sourceMappingURL=propertyInitOrderChecker.js.map
250