Skip to content

Commit e8be5e8

Browse files
committed
Handle parentless nodes in isParameterPropertyDeclaration
Fixes microsoft#33295. This follows a similar pattern as in microsoft#20314 by requiring an explicit `parent` parameter. Where possible, it uses the appopriate variable at the call sites. In several locations there is no context available though (e.g. inspecting `valueDeclarations`) and we access `.parent` as the code previously did. From a cursory inspection this seems correct, these callpaths originate in phases where there must be a `parent` (i.e. in checker, binder, etc). Change-Id: I28e4726777b57237bec776e4001e9e69ac591b11
1 parent 26655db commit e8be5e8

File tree

13 files changed

+64
-18
lines changed

13 files changed

+64
-18
lines changed

src/compiler/binder.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2843,7 +2843,7 @@ namespace ts {
28432843

28442844
// If this is a property-parameter, then also declare the property symbol into the
28452845
// containing class.
2846-
if (isParameterPropertyDeclaration(node)) {
2846+
if (isParameterPropertyDeclaration(node, node.parent)) {
28472847
const classDeclaration = <ClassLikeDeclaration>node.parent.parent;
28482848
declareSymbol(classDeclaration.symbol.members!, classDeclaration.symbol, node, SymbolFlags.Property | (node.questionToken ? SymbolFlags.Optional : SymbolFlags.None), SymbolFlags.PropertyExcludes);
28492849
}

src/compiler/checker.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -25659,7 +25659,7 @@ namespace ts {
2565925659
for (const member of node.members) {
2566025660
if (member.kind === SyntaxKind.Constructor) {
2566125661
for (const param of (member as ConstructorDeclaration).parameters) {
25662-
if (isParameterPropertyDeclaration(param) && !isBindingPattern(param.name)) {
25662+
if (isParameterPropertyDeclaration(param, member) && !isBindingPattern(param.name)) {
2566325663
addName(instanceNames, param.name, param.name.escapedText, DeclarationMeaning.GetOrSetAccessor);
2566425664
}
2566525665
}
@@ -27420,7 +27420,7 @@ namespace ts {
2742027420
const parameter = local.valueDeclaration && tryGetRootParameterDeclaration(local.valueDeclaration);
2742127421
const name = local.valueDeclaration && getNameOfDeclaration(local.valueDeclaration);
2742227422
if (parameter && name) {
27423-
if (!isParameterPropertyDeclaration(parameter) && !parameterIsThisKeyword(parameter) && !isIdentifierThatStartsWithUnderscore(name)) {
27423+
if (!isParameterPropertyDeclaration(parameter, parameter.parent) && !parameterIsThisKeyword(parameter) && !isIdentifierThatStartsWithUnderscore(name)) {
2742427424
addDiagnostic(parameter, UnusedKind.Parameter, createDiagnosticForNode(name, Diagnostics._0_is_declared_but_its_value_is_never_read, symbolName(local)));
2742527425
}
2742627426
}

src/compiler/transformers/classFields.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,7 @@ namespace ts {
337337
if (constructor && constructor.body) {
338338
let parameterPropertyDeclarationCount = 0;
339339
for (let i = indexOfFirstStatement; i < constructor.body.statements.length; i++) {
340-
if (isParameterPropertyDeclaration(getOriginalNode(constructor.body.statements[i]))) {
340+
if (isParameterPropertyDeclaration(getOriginalNode(constructor.body.statements[i]), constructor)) {
341341
parameterPropertyDeclarationCount++;
342342
}
343343
else {

src/compiler/transformers/declarations/diagnostics.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ namespace ts {
135135
return getReturnTypeVisibilityError;
136136
}
137137
else if (isParameter(node)) {
138-
if (isParameterPropertyDeclaration(node) && hasModifier(node.parent, ModifierFlags.Private)) {
138+
if (isParameterPropertyDeclaration(node, node.parent) && hasModifier(node.parent, ModifierFlags.Private)) {
139139
return getVariableDeclarationTypeVisibilityError;
140140
}
141141
return getParameterDeclarationTypeVisibilityError;

src/compiler/transformers/ts.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -892,7 +892,7 @@ namespace ts {
892892
const members: ClassElement[] = [];
893893
const constructor = getFirstConstructorWithBody(node);
894894
const parametersWithPropertyAssignments = constructor &&
895-
filter(constructor.parameters, isParameterPropertyDeclaration);
895+
filter(constructor.parameters, p => isParameterPropertyDeclaration(p, constructor));
896896

897897
if (parametersWithPropertyAssignments) {
898898
for (const parameter of parametersWithPropertyAssignments) {
@@ -1904,7 +1904,7 @@ namespace ts {
19041904

19051905
function transformConstructorBody(body: Block, constructor: ConstructorDeclaration) {
19061906
const parametersWithPropertyAssignments = constructor &&
1907-
filter(constructor.parameters, isParameterPropertyDeclaration);
1907+
filter(constructor.parameters, p => isParameterPropertyDeclaration(p, constructor));
19081908
if (!some(parametersWithPropertyAssignments)) {
19091909
return visitFunctionBody(body, visitor, context);
19101910
}

src/compiler/utilities.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1009,7 +1009,7 @@ namespace ts {
10091009
}
10101010

10111011
export function isDeclarationReadonly(declaration: Declaration): boolean {
1012-
return !!(getCombinedModifierFlags(declaration) & ModifierFlags.Readonly && !isParameterPropertyDeclaration(declaration));
1012+
return !!(getCombinedModifierFlags(declaration) & ModifierFlags.Readonly && !isParameterPropertyDeclaration(declaration, declaration.parent));
10131013
}
10141014

10151015
export function isVarConst(node: VariableDeclaration | VariableDeclarationList): boolean {
@@ -4959,8 +4959,8 @@ namespace ts {
49594959
}
49604960

49614961
export type ParameterPropertyDeclaration = ParameterDeclaration & { parent: ConstructorDeclaration, name: Identifier };
4962-
export function isParameterPropertyDeclaration(node: Node): node is ParameterPropertyDeclaration {
4963-
return hasModifier(node, ModifierFlags.ParameterPropertyModifier) && node.parent.kind === SyntaxKind.Constructor;
4962+
export function isParameterPropertyDeclaration(node: Node, parent: Node): node is ParameterPropertyDeclaration {
4963+
return hasModifier(node, ModifierFlags.ParameterPropertyModifier) && parent.kind === SyntaxKind.Constructor;
49644964
}
49654965

49664966
export function isEmptyBindingPattern(node: BindingName): node is BindingPattern {

src/services/findAllReferences.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1147,7 +1147,7 @@ namespace ts.FindAllReferences.Core {
11471147
}
11481148

11491149
export function eachSymbolReferenceInFile<T>(definition: Identifier, checker: TypeChecker, sourceFile: SourceFile, cb: (token: Identifier) => T): T | undefined {
1150-
const symbol = isParameterPropertyDeclaration(definition.parent)
1150+
const symbol = isParameterPropertyDeclaration(definition.parent, definition.parent.parent)
11511151
? first(checker.getSymbolsOfParameterPropertyDeclaration(definition.parent, definition.text))
11521152
: checker.getSymbolAtLocation(definition);
11531153
if (!symbol) return undefined;
@@ -1888,7 +1888,7 @@ namespace ts.FindAllReferences.Core {
18881888
const res = fromRoot(symbol);
18891889
if (res) return res;
18901890

1891-
if (symbol.valueDeclaration && isParameterPropertyDeclaration(symbol.valueDeclaration)) {
1891+
if (symbol.valueDeclaration && isParameterPropertyDeclaration(symbol.valueDeclaration, symbol.valueDeclaration.parent)) {
18921892
// For a parameter property, now try on the other symbol (property if this was a parameter, parameter if this was a property).
18931893
const paramProps = checker.getSymbolsOfParameterPropertyDeclaration(cast(symbol.valueDeclaration, isParameter), symbol.name);
18941894
Debug.assert(paramProps.length === 2 && !!(paramProps[0].flags & SymbolFlags.FunctionScopedVariable) && !!(paramProps[1].flags & SymbolFlags.Property)); // is [parameter, property]

src/services/navigationBar.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ namespace ts.NavigationBar {
161161

162162
// Parameter properties are children of the class, not the constructor.
163163
for (const param of ctr.parameters) {
164-
if (isParameterPropertyDeclaration(param)) {
164+
if (isParameterPropertyDeclaration(param, ctr)) {
165165
addLeafNode(param);
166166
}
167167
}

src/services/refactors/generateGetAccessorAndSetAccessor.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ namespace ts.refactor.generateGetAccessorAndSetAccessor {
9292
}
9393

9494
function isAcceptedDeclaration(node: Node): node is AcceptedDeclaration {
95-
return isParameterPropertyDeclaration(node) || isPropertyDeclaration(node) || isPropertyAssignment(node);
95+
return isParameterPropertyDeclaration(node, node.parent) || isPropertyDeclaration(node) || isPropertyAssignment(node);
9696
}
9797

9898
function createPropertyName (name: string, originalName: AcceptedNameType) {
@@ -214,7 +214,7 @@ namespace ts.refactor.generateGetAccessorAndSetAccessor {
214214
}
215215

216216
function insertAccessor(changeTracker: textChanges.ChangeTracker, file: SourceFile, accessor: AccessorDeclaration, declaration: AcceptedDeclaration, container: ContainerDeclaration) {
217-
isParameterPropertyDeclaration(declaration)
217+
isParameterPropertyDeclaration(declaration, declaration.parent)
218218
? changeTracker.insertNodeAtClassStart(file, <ClassLikeDeclaration>container, accessor)
219219
: isPropertyAssignment(declaration)
220220
? changeTracker.insertNodeAfterComma(file, declaration, accessor)

src/testRunner/unittests/transform.ts

+29-1
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,34 @@ namespace ts {
300300
});
301301
});
302302

303+
// https://github.com/microsoft/TypeScript/issues/33295
304+
testBaseline("transformParameterProperty", () => {
305+
return transpileModule("", {
306+
transformers: {
307+
before: [transformAddParameterProperty],
308+
},
309+
compilerOptions: {
310+
target: ScriptTarget.ES5,
311+
newLine: NewLineKind.CarriageReturnLineFeed,
312+
}
313+
}).outputText;
314+
315+
function transformAddParameterProperty(_context: TransformationContext) {
316+
return (sourceFile: SourceFile): SourceFile => {
317+
return visitNode(sourceFile);
318+
};
319+
function visitNode(sf: SourceFile) {
320+
// produce `class Foo { constructor(@Dec private x) {} }`;
321+
// The decorator is required to trigger ts.ts transformations.
322+
const classDecl = createClassDeclaration([], [], "Foo", /*typeParameters*/ undefined, /*heritageClauses*/ undefined, [
323+
createConstructor(/*decorators*/ undefined, /*modifiers*/ undefined, [
324+
createParameter(/*decorators*/ [createDecorator(createIdentifier("Dec"))], /*modifiers*/ [createModifier(SyntaxKind.PrivateKeyword)], /*dotDotDotToken*/ undefined, "x")], createBlock([]))
325+
]);
326+
return updateSourceFileNode(sf, [classDecl]);
327+
}
328+
}
329+
});
330+
303331
function baselineDeclarationTransform(text: string, opts: TranspileOptions) {
304332
const fs = vfs.createFromFileSystem(Harness.IO, /*caseSensitive*/ true, { documents: [new documents.TextDocument("/.src/index.ts", text)] });
305333
const host = new fakes.CompilerHost(fs, opts.compilerOptions);
@@ -389,7 +417,7 @@ class Clazz {
389417
}
390418
`, {
391419
transformers: {
392-
before: [addSyntheticComment(n => isPropertyDeclaration(n) || isParameterPropertyDeclaration(n) || isClassDeclaration(n) || isConstructorDeclaration(n))],
420+
before: [addSyntheticComment(n => isPropertyDeclaration(n) || isParameterPropertyDeclaration(n, n.parent) || isClassDeclaration(n) || isConstructorDeclaration(n))],
393421
},
394422
compilerOptions: {
395423
target: ScriptTarget.ES2015,

tests/baselines/reference/api/tsserverlibrary.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -3264,7 +3264,7 @@ declare namespace ts {
32643264
parent: ConstructorDeclaration;
32653265
name: Identifier;
32663266
};
3267-
function isParameterPropertyDeclaration(node: Node): node is ParameterPropertyDeclaration;
3267+
function isParameterPropertyDeclaration(node: Node, parent: Node): node is ParameterPropertyDeclaration;
32683268
function isEmptyBindingPattern(node: BindingName): node is BindingPattern;
32693269
function isEmptyBindingElement(node: BindingElement): boolean;
32703270
function walkUpBindingElementsAndPatterns(binding: BindingElement): VariableDeclaration | ParameterDeclaration;

tests/baselines/reference/api/typescript.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -3264,7 +3264,7 @@ declare namespace ts {
32643264
parent: ConstructorDeclaration;
32653265
name: Identifier;
32663266
};
3267-
function isParameterPropertyDeclaration(node: Node): node is ParameterPropertyDeclaration;
3267+
function isParameterPropertyDeclaration(node: Node, parent: Node): node is ParameterPropertyDeclaration;
32683268
function isEmptyBindingPattern(node: BindingName): node is BindingPattern;
32693269
function isEmptyBindingElement(node: BindingElement): boolean;
32703270
function walkUpBindingElementsAndPatterns(binding: BindingElement): VariableDeclaration | ParameterDeclaration;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5+
return c > 3 && r && Object.defineProperty(target, key, r), r;
6+
};
7+
var __param = (this && this.__param) || function (paramIndex, decorator) {
8+
return function (target, key) { decorator(target, key, paramIndex); }
9+
};
10+
var Foo = /** @class */ (function () {
11+
function Foo(x) {
12+
this.x = x;
13+
}
14+
Foo = __decorate([
15+
__param(0, Dec)
16+
], Foo);
17+
return Foo;
18+
}());

0 commit comments

Comments
 (0)