Handle parentless nodes in nodeIsDecorated (#20314)
This commit is contained in:
parent
433bfc555f
commit
d79a474e6d
|
@ -13048,7 +13048,7 @@ namespace ts {
|
|||
// must instead be rewritten to point to a temporary variable to avoid issues with the double-bind
|
||||
// behavior of class names in ES6.
|
||||
if (declaration.kind === SyntaxKind.ClassDeclaration
|
||||
&& nodeIsDecorated(declaration)) {
|
||||
&& nodeIsDecorated(declaration as ClassDeclaration)) {
|
||||
let container = getContainingClass(node);
|
||||
while (container !== undefined) {
|
||||
if (container === declaration && container.name !== node) {
|
||||
|
@ -20697,7 +20697,7 @@ namespace ts {
|
|||
|
||||
// skip this check for nodes that cannot have decorators. These should have already had an error reported by
|
||||
// checkGrammarDecorators.
|
||||
if (!nodeCanBeDecorated(node)) {
|
||||
if (!nodeCanBeDecorated(node, node.parent, node.parent.parent)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -25203,7 +25203,7 @@ namespace ts {
|
|||
if (!node.decorators) {
|
||||
return false;
|
||||
}
|
||||
if (!nodeCanBeDecorated(node)) {
|
||||
if (!nodeCanBeDecorated(node, node.parent, node.parent.parent)) {
|
||||
if (node.kind === SyntaxKind.MethodDeclaration && !nodeIsPresent((<MethodDeclaration>node).body)) {
|
||||
return grammarErrorOnFirstToken(node, Diagnostics.A_decorator_can_only_decorate_a_method_implementation_not_an_overload);
|
||||
}
|
||||
|
|
|
@ -1261,7 +1261,7 @@ namespace ts {
|
|||
* the class.
|
||||
*/
|
||||
function getDecoratedClassElements(node: ClassExpression | ClassDeclaration, isStatic: boolean): ReadonlyArray<ClassElement> {
|
||||
return filter(node.members, isStatic ? isStaticDecoratedClassElement : isInstanceDecoratedClassElement);
|
||||
return filter(node.members, isStatic ? m => isStaticDecoratedClassElement(m, node) : m => isInstanceDecoratedClassElement(m, node));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1270,8 +1270,8 @@ namespace ts {
|
|||
*
|
||||
* @param member The class member.
|
||||
*/
|
||||
function isStaticDecoratedClassElement(member: ClassElement) {
|
||||
return isDecoratedClassElement(member, /*isStatic*/ true);
|
||||
function isStaticDecoratedClassElement(member: ClassElement, parent: ClassLikeDeclaration) {
|
||||
return isDecoratedClassElement(member, /*isStatic*/ true, parent);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1280,8 +1280,8 @@ namespace ts {
|
|||
*
|
||||
* @param member The class member.
|
||||
*/
|
||||
function isInstanceDecoratedClassElement(member: ClassElement) {
|
||||
return isDecoratedClassElement(member, /*isStatic*/ false);
|
||||
function isInstanceDecoratedClassElement(member: ClassElement, parent: ClassLikeDeclaration) {
|
||||
return isDecoratedClassElement(member, /*isStatic*/ false, parent);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1290,8 +1290,8 @@ namespace ts {
|
|||
*
|
||||
* @param member The class member.
|
||||
*/
|
||||
function isDecoratedClassElement(member: ClassElement, isStatic: boolean) {
|
||||
return nodeOrChildIsDecorated(member)
|
||||
function isDecoratedClassElement(member: ClassElement, isStatic: boolean, parent: ClassLikeDeclaration) {
|
||||
return nodeOrChildIsDecorated(member, parent)
|
||||
&& isStatic === hasModifier(member, ModifierFlags.Static);
|
||||
}
|
||||
|
||||
|
|
|
@ -1205,7 +1205,10 @@ namespace ts {
|
|||
return (<CallExpression | Decorator>node).expression;
|
||||
}
|
||||
|
||||
export function nodeCanBeDecorated(node: Node): boolean {
|
||||
export function nodeCanBeDecorated(node: ClassDeclaration): true;
|
||||
export function nodeCanBeDecorated(node: ClassElement, parent: Node): boolean;
|
||||
export function nodeCanBeDecorated(node: Node, parent: Node, grandparent: Node): boolean;
|
||||
export function nodeCanBeDecorated(node: Node, parent?: Node, grandparent?: Node): boolean {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
// classes are valid targets
|
||||
|
@ -1213,43 +1216,51 @@ namespace ts {
|
|||
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
// property declarations are valid if their parent is a class declaration.
|
||||
return node.parent.kind === SyntaxKind.ClassDeclaration;
|
||||
return parent.kind === SyntaxKind.ClassDeclaration;
|
||||
|
||||
case SyntaxKind.GetAccessor:
|
||||
case SyntaxKind.SetAccessor:
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
// if this method has a body and its parent is a class declaration, this is a valid target.
|
||||
return (<FunctionLikeDeclaration>node).body !== undefined
|
||||
&& node.parent.kind === SyntaxKind.ClassDeclaration;
|
||||
&& parent.kind === SyntaxKind.ClassDeclaration;
|
||||
|
||||
case SyntaxKind.Parameter:
|
||||
// if the parameter's parent has a body and its grandparent is a class declaration, this is a valid target;
|
||||
return (<FunctionLikeDeclaration>node.parent).body !== undefined
|
||||
&& (node.parent.kind === SyntaxKind.Constructor
|
||||
|| node.parent.kind === SyntaxKind.MethodDeclaration
|
||||
|| node.parent.kind === SyntaxKind.SetAccessor)
|
||||
&& node.parent.parent.kind === SyntaxKind.ClassDeclaration;
|
||||
return (<FunctionLikeDeclaration>parent).body !== undefined
|
||||
&& (parent.kind === SyntaxKind.Constructor
|
||||
|| parent.kind === SyntaxKind.MethodDeclaration
|
||||
|| parent.kind === SyntaxKind.SetAccessor)
|
||||
&& grandparent.kind === SyntaxKind.ClassDeclaration;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function nodeIsDecorated(node: Node): boolean {
|
||||
export function nodeIsDecorated(node: ClassDeclaration): boolean;
|
||||
export function nodeIsDecorated(node: ClassElement, parent: Node): boolean;
|
||||
export function nodeIsDecorated(node: Node, parent: Node, grandparent: Node): boolean;
|
||||
export function nodeIsDecorated(node: Node, parent?: Node, grandparent?: Node): boolean {
|
||||
return node.decorators !== undefined
|
||||
&& nodeCanBeDecorated(node);
|
||||
&& nodeCanBeDecorated(node, parent, grandparent);
|
||||
}
|
||||
|
||||
export function nodeOrChildIsDecorated(node: Node): boolean {
|
||||
return nodeIsDecorated(node) || childIsDecorated(node);
|
||||
export function nodeOrChildIsDecorated(node: ClassDeclaration): boolean;
|
||||
export function nodeOrChildIsDecorated(node: ClassElement, parent: Node): boolean;
|
||||
export function nodeOrChildIsDecorated(node: Node, parent: Node, grandparent: Node): boolean;
|
||||
export function nodeOrChildIsDecorated(node: Node, parent?: Node, grandparent?: Node): boolean {
|
||||
return nodeIsDecorated(node, parent, grandparent) || childIsDecorated(node, parent);
|
||||
}
|
||||
|
||||
export function childIsDecorated(node: Node): boolean {
|
||||
export function childIsDecorated(node: ClassDeclaration): boolean;
|
||||
export function childIsDecorated(node: Node, parent: Node): boolean;
|
||||
export function childIsDecorated(node: Node, parent?: Node): boolean {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
return forEach((<ClassDeclaration>node).members, nodeOrChildIsDecorated);
|
||||
return forEach((<ClassDeclaration>node).members, m => nodeOrChildIsDecorated(m, node, parent));
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
case SyntaxKind.SetAccessor:
|
||||
return forEach((<FunctionLikeDeclaration>node).parameters, nodeIsDecorated);
|
||||
return forEach((<FunctionLikeDeclaration>node).parameters, p => nodeIsDecorated(p, node, parent));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -224,6 +224,32 @@ namespace ts {
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
// https://github.com/Microsoft/TypeScript/issues/17384
|
||||
testBaseline("transformAddDecoratedNode", () => {
|
||||
return ts.transpileModule("", {
|
||||
transformers: {
|
||||
before: [transformAddDecoratedNode],
|
||||
},
|
||||
compilerOptions: {
|
||||
target: ts.ScriptTarget.ES5,
|
||||
newLine: NewLineKind.CarriageReturnLineFeed,
|
||||
}
|
||||
}).outputText;
|
||||
|
||||
function transformAddDecoratedNode(_context: ts.TransformationContext) {
|
||||
return (sourceFile: ts.SourceFile): ts.SourceFile => {
|
||||
return visitNode(sourceFile);
|
||||
};
|
||||
function visitNode(sf: ts.SourceFile) {
|
||||
// produce `class Foo { @Bar baz() {} }`;
|
||||
const classDecl = ts.createClassDeclaration([], [], "Foo", /*typeParameters*/ undefined, /*heritageClauses*/ undefined, [
|
||||
ts.createMethod([ts.createDecorator(ts.createIdentifier("Bar"))], [], /**/ undefined, "baz", /**/ undefined, /**/ undefined, [], /**/ undefined, ts.createBlock([]))
|
||||
]);
|
||||
return ts.updateSourceFileNode(sf, [classDecl]);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
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;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var Foo = /** @class */ (function () {
|
||||
function Foo() {
|
||||
}
|
||||
Foo.prototype.baz = function () { };
|
||||
__decorate([
|
||||
Bar
|
||||
], Foo.prototype, "baz", null);
|
||||
Foo = __decorate([], Foo);
|
||||
return Foo;
|
||||
}());
|
Loading…
Reference in a new issue