Handle parentless nodes in nodeIsDecorated (#20314)

This commit is contained in:
Wesley Wigham 2017-11-28 17:01:19 -08:00 committed by GitHub
parent 433bfc555f
commit d79a474e6d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 78 additions and 25 deletions

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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));
}
}

View file

@ -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]);
}
}
});
});
}

View file

@ -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;
}());