From 9bcbca4e6c1f855f9020124064bfb48c5f4e0b5b Mon Sep 17 00:00:00 2001 From: Oleksandr T Date: Sat, 14 Nov 2020 23:14:07 +0200 Subject: [PATCH] fix(41534): fix children circular references --- src/services/navigationBar.ts | 15 +++--- .../fourslash/navigationBarItemsClass1.ts | 46 ++++++++++++++++++ .../fourslash/navigationBarItemsClass2.ts | 46 ++++++++++++++++++ .../fourslash/navigationBarItemsClass3.ts | 48 +++++++++++++++++++ .../fourslash/navigationBarItemsClass4.ts | 48 +++++++++++++++++++ .../fourslash/navigationBarItemsClass5.ts | 41 ++++++++++++++++ 6 files changed, 238 insertions(+), 6 deletions(-) create mode 100644 tests/cases/fourslash/navigationBarItemsClass1.ts create mode 100644 tests/cases/fourslash/navigationBarItemsClass2.ts create mode 100644 tests/cases/fourslash/navigationBarItemsClass3.ts create mode 100644 tests/cases/fourslash/navigationBarItemsClass4.ts create mode 100644 tests/cases/fourslash/navigationBarItemsClass5.ts diff --git a/src/services/navigationBar.ts b/src/services/navigationBar.ts index 4704e86846b..6b47663f37c 100644 --- a/src/services/navigationBar.ts +++ b/src/services/navigationBar.ts @@ -490,7 +490,6 @@ namespace ts.NavigationBar { [AssignmentDeclarationKind.ThisProperty]: false, }; function tryMergeEs5Class(a: NavigationBarNode, b: NavigationBarNode, bIndex: number, parent: NavigationBarNode): boolean | undefined { - function isPossibleConstructor(node: Node) { return isFunctionExpression(node) || isFunctionDeclaration(node) || isVariableDeclaration(node); } @@ -506,10 +505,10 @@ namespace ts.NavigationBar { if ((isEs5ClassMember[bAssignmentDeclarationKind] && isEs5ClassMember[aAssignmentDeclarationKind]) // merge two class elements || (isPossibleConstructor(a.node) && isEs5ClassMember[bAssignmentDeclarationKind]) // ctor function & member || (isPossibleConstructor(b.node) && isEs5ClassMember[aAssignmentDeclarationKind]) // member & ctor function - || (isClassDeclaration(a.node) && isEs5ClassMember[bAssignmentDeclarationKind]) // class (generated) & member + || (isClassDeclaration(a.node) && isSynthesized(a.node) && isEs5ClassMember[bAssignmentDeclarationKind]) // class (generated) & member || (isClassDeclaration(b.node) && isEs5ClassMember[aAssignmentDeclarationKind]) // member & class (generated) - || (isClassDeclaration(a.node) && isPossibleConstructor(b.node)) // class (generated) & ctor - || (isClassDeclaration(b.node) && isPossibleConstructor(a.node)) // ctor & class (generated) + || (isClassDeclaration(a.node) && isSynthesized(a.node) && isPossibleConstructor(b.node)) // class (generated) & ctor + || (isClassDeclaration(b.node) && isPossibleConstructor(a.node) && isSynthesized(a.node)) // ctor & class (generated) ) { let lastANode = a.additionalNodes && lastOrUndefined(a.additionalNodes) || a.node; @@ -528,11 +527,11 @@ namespace ts.NavigationBar { const ctor = emptyNavigationBarNode(ctorNode); ctor.indent = a.indent + 1; ctor.children = a.node === ctorFunction ? a.children : b.children; - a.children = a.node === ctorFunction ? concatenate([ctor], b.children || [b]) : concatenate(a.children || [a], [ctor]); + a.children = a.node === ctorFunction ? concatenate([ctor], b.children || [b]) : concatenate(a.children || [{ ...a }], [ctor]); } else { if (a.children || b.children) { - a.children = concatenate(a.children || [a], b.children || [b]); + a.children = concatenate(a.children || [{ ...a }], b.children || [b]); if (a.children) { mergeChildren(a.children, a); sortChildren(a.children); @@ -612,6 +611,10 @@ namespace ts.NavigationBar { } } + function isSynthesized(node: Node) { + return !!(node.flags & NodeFlags.Synthesized); + } + // We want to merge own children like `I` in in `module A { interface I {} } module A { interface I {} }` // We don't want to merge unrelated children like `m` in `const o = { a: { m() {} }, b: { m() {} } };` function isOwnChild(n: Node, parent: NavigationBarNode): boolean { diff --git a/tests/cases/fourslash/navigationBarItemsClass1.ts b/tests/cases/fourslash/navigationBarItemsClass1.ts new file mode 100644 index 00000000000..c9b523aae96 --- /dev/null +++ b/tests/cases/fourslash/navigationBarItemsClass1.ts @@ -0,0 +1,46 @@ +/// + +////function Foo() {} +////class Foo {} + +verify.navigationTree({ + text: "", + kind: "script", + childItems: [ + { + text: "Foo", + kind: "function" + }, + { + text: "Foo", + kind: "class" + } + ] +}); + +verify.navigationBar([ + { + text: "", + kind: "script", + childItems: [ + { + text: "Foo", + kind: "function" + }, + { + text: "Foo", + kind: "class" + } + ] + }, + { + text: "Foo", + kind: "function", + indent: 1 + }, + { + text: "Foo", + kind: "class", + indent: 1 + } +]); diff --git a/tests/cases/fourslash/navigationBarItemsClass2.ts b/tests/cases/fourslash/navigationBarItemsClass2.ts new file mode 100644 index 00000000000..5e7baafc8ac --- /dev/null +++ b/tests/cases/fourslash/navigationBarItemsClass2.ts @@ -0,0 +1,46 @@ +/// + +////class Foo {} +////function Foo() {} + +verify.navigationTree({ + text: "", + kind: "script", + childItems: [ + { + text: "Foo", + kind: "function" + }, + { + text: "Foo", + kind: "class" + } + ] +}); + +verify.navigationBar([ + { + text: "", + kind: "script", + childItems: [ + { + text: "Foo", + kind: "function" + }, + { + text: "Foo", + kind: "class" + } + ] + }, + { + text: "Foo", + kind: "function", + indent: 1 + }, + { + text: "Foo", + kind: "class", + indent: 1 + } +]); diff --git a/tests/cases/fourslash/navigationBarItemsClass3.ts b/tests/cases/fourslash/navigationBarItemsClass3.ts new file mode 100644 index 00000000000..532904f1570 --- /dev/null +++ b/tests/cases/fourslash/navigationBarItemsClass3.ts @@ -0,0 +1,48 @@ +/// + +// @allowJs: true +// @filename: /foo.js +////function Foo() {} +////class Foo {} + +verify.navigationTree({ + text: "", + kind: "script", + childItems: [ + { + text: "Foo", + kind: "function" + }, + { + text: "Foo", + kind: "class" + } + ] +}); + +verify.navigationBar([ + { + text: "", + kind: "script", + childItems: [ + { + text: "Foo", + kind: "function" + }, + { + text: "Foo", + kind: "class" + } + ] + }, + { + text: "Foo", + kind: "function", + indent: 1 + }, + { + text: "Foo", + kind: "class", + indent: 1 + } +]); diff --git a/tests/cases/fourslash/navigationBarItemsClass4.ts b/tests/cases/fourslash/navigationBarItemsClass4.ts new file mode 100644 index 00000000000..e6311a596f8 --- /dev/null +++ b/tests/cases/fourslash/navigationBarItemsClass4.ts @@ -0,0 +1,48 @@ +/// + +// @allowJs: true +// @filename: /foo.js +////class Foo {} +////function Foo() {} + +verify.navigationTree({ + text: "", + kind: "script", + childItems: [ + { + text: "Foo", + kind: "function" + }, + { + text: "Foo", + kind: "class" + } + ] +}); + +verify.navigationBar([ + { + text: "", + kind: "script", + childItems: [ + { + text: "Foo", + kind: "function" + }, + { + text: "Foo", + kind: "class" + } + ] + }, + { + text: "Foo", + kind: "function", + indent: 1 + }, + { + text: "Foo", + kind: "class", + indent: 1 + } +]); diff --git a/tests/cases/fourslash/navigationBarItemsClass5.ts b/tests/cases/fourslash/navigationBarItemsClass5.ts new file mode 100644 index 00000000000..85d7be3f1be --- /dev/null +++ b/tests/cases/fourslash/navigationBarItemsClass5.ts @@ -0,0 +1,41 @@ +/// + +////class Foo {} +////let Foo = 1; + +verify.navigationTree({ + text: "", + kind: "script", + childItems: [ + { + text: "Foo", + kind: "let" + }, + { + text: "Foo", + kind: "class" + } + ] +}); + +verify.navigationBar([ + { + text: "", + kind: "script", + childItems: [ + { + text: "Foo", + kind: "let" + }, + { + text: "Foo", + kind: "class" + } + ] + }, + { + text: "Foo", + kind: "class", + indent: 1 + } +])