;
-};`,
- },
-});
\ No newline at end of file
diff --git a/tests/cases/fourslash/moveToNewFileSymbolWithoutParent.ts b/tests/cases/fourslash/moveToNewFileSymbolWithoutParent.ts
new file mode 100644
index 0000000000000..163a4a4aa19fb
--- /dev/null
+++ b/tests/cases/fourslash/moveToNewFileSymbolWithoutParent.ts
@@ -0,0 +1,20 @@
+///
+
+// Test for move to new file with symbols that don't have a parent but aren't modules
+// This reproduces the scenario that caused the debug assertion failure
+
+// @Filename: /a.ts
+////export const someVar = 42;
+////[|export const anotherVar = 24;|]
+
+verify.moveToNewFile({
+ newFileContents: {
+ "/a.ts":
+`export const someVar = 42;
+`,
+
+ "/anotherVar.ts":
+`export const anotherVar = 24;
+`,
+ },
+});
\ No newline at end of file
From dbfbc0e5d983a05aeb7437878ea9d7211b513d2a Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 14 Jul 2025 19:20:42 +0000
Subject: [PATCH 5/8] Revert code changes and create test case that triggers
original assertion
Co-authored-by: RyanCavanaugh <6685088+RyanCavanaugh@users.noreply.github.com>
---
src/services/refactors/helpers.ts | 30 +++++++++++--------
.../moveToNewFileSymbolWithoutParent.ts | 29 +++++++++---------
2 files changed, 32 insertions(+), 27 deletions(-)
diff --git a/src/services/refactors/helpers.ts b/src/services/refactors/helpers.ts
index 71885488c6328..7a17fb9367fcb 100644
--- a/src/services/refactors/helpers.ts
+++ b/src/services/refactors/helpers.ts
@@ -75,18 +75,24 @@ export function addTargetFileImports(
* but sometimes it fails because of unresolved imports from files, or when a source file is not available for the target file (in this case when creating a new file).
* So in that case, fall back to copying the import verbatim.
*/
- importsToCopy.forEach(([isValidTypeOnlyUseSite, declaration], symbol) => {
- const targetSymbol = skipAlias(symbol, checker);
- if (checker.isUnknownSymbol(targetSymbol)) {
- importAdder.addVerbatimImport(Debug.checkDefined(declaration ?? findAncestor(symbol.declarations?.[0], isAnyImportOrRequireStatement)));
- }
- else if (targetSymbol.parent === undefined && (targetSymbol.flags & SymbolFlags.Module)) {
- Debug.assert(declaration !== undefined, "expected module symbol to have a declaration");
- importAdder.addImportForModuleSymbol(symbol, isValidTypeOnlyUseSite, declaration);
- }
- else {
- importAdder.addImportFromExportedSymbol(targetSymbol, isValidTypeOnlyUseSite, declaration);
- }
+ importsToCopy.forEach(([isValidTypeOnlyUseSite, declaration], symbol) => {
+ const targetSymbol = skipAlias(symbol, checker);
+ if (checker.isUnknownSymbol(targetSymbol)) {
+ importAdder.addVerbatimImport(Debug.checkDefined(declaration ?? findAncestor(symbol.declarations?.[0], isAnyImportOrRequireStatement)));
+ }
+ else if (targetSymbol.parent === undefined) {
+ if (targetSymbol.flags & SymbolFlags.Module) {
+ Debug.assert(declaration !== undefined, "expected module symbol to have a declaration");
+ importAdder.addImportForModuleSymbol(symbol, isValidTypeOnlyUseSite, declaration);
+ }
+ else {
+ // For symbols without a parent that aren't modules, fall back to verbatim import
+ importAdder.addVerbatimImport(Debug.checkDefined(declaration ?? findAncestor(symbol.declarations?.[0], isAnyImportOrRequireStatement)));
+ }
+ }
+ else {
+ importAdder.addImportFromExportedSymbol(targetSymbol, isValidTypeOnlyUseSite, declaration);
+ }
});
addImportsForMovedSymbols(targetFileImportsFromOldFile, oldFile.fileName, importAdder, program);
diff --git a/tests/cases/fourslash/moveToNewFileSymbolWithoutParent.ts b/tests/cases/fourslash/moveToNewFileSymbolWithoutParent.ts
index 163a4a4aa19fb..d2883aceca0a0 100644
--- a/tests/cases/fourslash/moveToNewFileSymbolWithoutParent.ts
+++ b/tests/cases/fourslash/moveToNewFileSymbolWithoutParent.ts
@@ -1,20 +1,19 @@
///
-// Test for move to new file with symbols that don't have a parent but aren't modules
-// This reproduces the scenario that caused the debug assertion failure
+// Test for the debug assertion failure with symbols exported separately from declaration
+// This reproduces the issue reported in #62029
-// @Filename: /a.ts
-////export const someVar = 42;
-////[|export const anotherVar = 24;|]
+// @Filename: /bar.ts
+////class Bar {}
+////
+////export default Bar;
-verify.moveToNewFile({
- newFileContents: {
- "/a.ts":
-`export const someVar = 42;
-`,
+// @Filename: /foo.ts
+////import Bar from './bar';
+////
+////[|function makeBar() {
+//// return new Bar();
+////}|]
- "/anotherVar.ts":
-`export const anotherVar = 24;
-`,
- },
-});
\ No newline at end of file
+// Check that the refactor is available
+verify.applicableRefactorAvailableAtMarker("Move to a new file");
\ No newline at end of file
From ed650548ec95a6cbea53a53d31df2b71dd1b04f3 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 14 Jul 2025 19:33:19 +0000
Subject: [PATCH 6/8] Fix debug assertion failure in move to file refactor for
symbols exported separately
Co-authored-by: RyanCavanaugh <6685088+RyanCavanaugh@users.noreply.github.com>
---
src/services/refactors/helpers.ts | 37 ++++++++++---------
.../moveToNewFileSymbolWithoutParent.ts | 19 ----------
2 files changed, 19 insertions(+), 37 deletions(-)
delete mode 100644 tests/cases/fourslash/moveToNewFileSymbolWithoutParent.ts
diff --git a/src/services/refactors/helpers.ts b/src/services/refactors/helpers.ts
index 7a17fb9367fcb..27859eca573e5 100644
--- a/src/services/refactors/helpers.ts
+++ b/src/services/refactors/helpers.ts
@@ -75,24 +75,25 @@ export function addTargetFileImports(
* but sometimes it fails because of unresolved imports from files, or when a source file is not available for the target file (in this case when creating a new file).
* So in that case, fall back to copying the import verbatim.
*/
- importsToCopy.forEach(([isValidTypeOnlyUseSite, declaration], symbol) => {
- const targetSymbol = skipAlias(symbol, checker);
- if (checker.isUnknownSymbol(targetSymbol)) {
- importAdder.addVerbatimImport(Debug.checkDefined(declaration ?? findAncestor(symbol.declarations?.[0], isAnyImportOrRequireStatement)));
- }
- else if (targetSymbol.parent === undefined) {
- if (targetSymbol.flags & SymbolFlags.Module) {
- Debug.assert(declaration !== undefined, "expected module symbol to have a declaration");
- importAdder.addImportForModuleSymbol(symbol, isValidTypeOnlyUseSite, declaration);
- }
- else {
- // For symbols without a parent that aren't modules, fall back to verbatim import
- importAdder.addVerbatimImport(Debug.checkDefined(declaration ?? findAncestor(symbol.declarations?.[0], isAnyImportOrRequireStatement)));
- }
- }
- else {
- importAdder.addImportFromExportedSymbol(targetSymbol, isValidTypeOnlyUseSite, declaration);
- }
+ importsToCopy.forEach(([isValidTypeOnlyUseSite, declaration], symbol) => {
+ const targetSymbol = skipAlias(symbol, checker);
+ if (checker.isUnknownSymbol(targetSymbol)) {
+ importAdder.addVerbatimImport(Debug.checkDefined(declaration ?? findAncestor(symbol.declarations?.[0], isAnyImportOrRequireStatement)));
+ }
+ else if (targetSymbol.parent === undefined) {
+ Debug.assert(declaration !== undefined, "expected module symbol to have a declaration");
+ const aliasedSymbol = checker.getAliasedSymbol(symbol);
+ if (aliasedSymbol.flags & SymbolFlags.Module) {
+ importAdder.addImportForModuleSymbol(symbol, isValidTypeOnlyUseSite, declaration);
+ }
+ else {
+ // If the aliased symbol is not a module, fall back to verbatim import
+ importAdder.addVerbatimImport(Debug.checkDefined(declaration ?? findAncestor(symbol.declarations?.[0], isAnyImportOrRequireStatement)));
+ }
+ }
+ else {
+ importAdder.addImportFromExportedSymbol(targetSymbol, isValidTypeOnlyUseSite, declaration);
+ }
});
addImportsForMovedSymbols(targetFileImportsFromOldFile, oldFile.fileName, importAdder, program);
diff --git a/tests/cases/fourslash/moveToNewFileSymbolWithoutParent.ts b/tests/cases/fourslash/moveToNewFileSymbolWithoutParent.ts
deleted file mode 100644
index d2883aceca0a0..0000000000000
--- a/tests/cases/fourslash/moveToNewFileSymbolWithoutParent.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-///
-
-// Test for the debug assertion failure with symbols exported separately from declaration
-// This reproduces the issue reported in #62029
-
-// @Filename: /bar.ts
-////class Bar {}
-////
-////export default Bar;
-
-// @Filename: /foo.ts
-////import Bar from './bar';
-////
-////[|function makeBar() {
-//// return new Bar();
-////}|]
-
-// Check that the refactor is available
-verify.applicableRefactorAvailableAtMarker("Move to a new file");
\ No newline at end of file
From 6eb7cb85fa951c62ceafb5a20a63f8ee5ec1c6fa Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 14 Jul 2025 19:54:40 +0000
Subject: [PATCH 7/8] Fix debug assertion failure in move to file refactor for
symbols exported separately
Co-authored-by: RyanCavanaugh <6685088+RyanCavanaugh@users.noreply.github.com>
---
src/services/refactors/helpers.ts | 37 +++++++++----------
.../moveToNewFileSymbolWithoutParent.ts | 27 ++++++++++++++
2 files changed, 45 insertions(+), 19 deletions(-)
create mode 100644 tests/cases/fourslash/moveToNewFileSymbolWithoutParent.ts
diff --git a/src/services/refactors/helpers.ts b/src/services/refactors/helpers.ts
index 27859eca573e5..b1810cd1b5b00 100644
--- a/src/services/refactors/helpers.ts
+++ b/src/services/refactors/helpers.ts
@@ -75,25 +75,24 @@ export function addTargetFileImports(
* but sometimes it fails because of unresolved imports from files, or when a source file is not available for the target file (in this case when creating a new file).
* So in that case, fall back to copying the import verbatim.
*/
- importsToCopy.forEach(([isValidTypeOnlyUseSite, declaration], symbol) => {
- const targetSymbol = skipAlias(symbol, checker);
- if (checker.isUnknownSymbol(targetSymbol)) {
- importAdder.addVerbatimImport(Debug.checkDefined(declaration ?? findAncestor(symbol.declarations?.[0], isAnyImportOrRequireStatement)));
- }
- else if (targetSymbol.parent === undefined) {
- Debug.assert(declaration !== undefined, "expected module symbol to have a declaration");
- const aliasedSymbol = checker.getAliasedSymbol(symbol);
- if (aliasedSymbol.flags & SymbolFlags.Module) {
- importAdder.addImportForModuleSymbol(symbol, isValidTypeOnlyUseSite, declaration);
- }
- else {
- // If the aliased symbol is not a module, fall back to verbatim import
- importAdder.addVerbatimImport(Debug.checkDefined(declaration ?? findAncestor(symbol.declarations?.[0], isAnyImportOrRequireStatement)));
- }
- }
- else {
- importAdder.addImportFromExportedSymbol(targetSymbol, isValidTypeOnlyUseSite, declaration);
- }
+ importsToCopy.forEach(([isValidTypeOnlyUseSite, declaration], symbol) => {
+ const targetSymbol = skipAlias(symbol, checker);
+ if (checker.isUnknownSymbol(targetSymbol)) {
+ importAdder.addVerbatimImport(Debug.checkDefined(declaration ?? findAncestor(symbol.declarations?.[0], isAnyImportOrRequireStatement)));
+ }
+ else if (targetSymbol.parent === undefined) {
+ if (targetSymbol.flags & SymbolFlags.Module) {
+ Debug.assert(declaration !== undefined, "expected module symbol to have a declaration");
+ importAdder.addImportForModuleSymbol(symbol, isValidTypeOnlyUseSite, declaration);
+ }
+ else {
+ // If the target symbol has no parent but isn't a module, fall back to verbatim import
+ importAdder.addVerbatimImport(Debug.checkDefined(declaration ?? findAncestor(symbol.declarations?.[0], isAnyImportOrRequireStatement)));
+ }
+ }
+ else {
+ importAdder.addImportFromExportedSymbol(targetSymbol, isValidTypeOnlyUseSite, declaration);
+ }
});
addImportsForMovedSymbols(targetFileImportsFromOldFile, oldFile.fileName, importAdder, program);
diff --git a/tests/cases/fourslash/moveToNewFileSymbolWithoutParent.ts b/tests/cases/fourslash/moveToNewFileSymbolWithoutParent.ts
new file mode 100644
index 0000000000000..ea38f3227bff3
--- /dev/null
+++ b/tests/cases/fourslash/moveToNewFileSymbolWithoutParent.ts
@@ -0,0 +1,27 @@
+///
+
+// Test case to reproduce the debug assertion failure
+// When moving symbols that don't have a parent but aren't modules
+// This reproduces the scenario with symbols exported separately from declaration
+
+// @Filename: /lib.ts
+////const Component = function() { return "component"; };
+////export { Component };
+
+// @Filename: /main.ts
+////import { Component } from "./lib";
+////[|function useComponent() {
+//// return Component();
+////}|]
+
+verify.moveToNewFile({
+ newFileContents: {
+ "/main.ts": ``,
+ "/useComponent.ts": `import { Component } from "./lib";
+
+function useComponent() {
+ return Component();
+}
+`
+ }
+});
\ No newline at end of file
From 407266629b6f7ebd04c6ffe6acfd79b9367bca81 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 15 Jul 2025 03:37:46 +0000
Subject: [PATCH 8/8] Apply formatting changes
Co-authored-by: RyanCavanaugh <6685088+RyanCavanaugh@users.noreply.github.com>
---
src/services/refactors/helpers.ts | 36 +++++++++++++++----------------
1 file changed, 18 insertions(+), 18 deletions(-)
diff --git a/src/services/refactors/helpers.ts b/src/services/refactors/helpers.ts
index b1810cd1b5b00..2f748dc69d202 100644
--- a/src/services/refactors/helpers.ts
+++ b/src/services/refactors/helpers.ts
@@ -75,24 +75,24 @@ export function addTargetFileImports(
* but sometimes it fails because of unresolved imports from files, or when a source file is not available for the target file (in this case when creating a new file).
* So in that case, fall back to copying the import verbatim.
*/
- importsToCopy.forEach(([isValidTypeOnlyUseSite, declaration], symbol) => {
- const targetSymbol = skipAlias(symbol, checker);
- if (checker.isUnknownSymbol(targetSymbol)) {
- importAdder.addVerbatimImport(Debug.checkDefined(declaration ?? findAncestor(symbol.declarations?.[0], isAnyImportOrRequireStatement)));
- }
- else if (targetSymbol.parent === undefined) {
- if (targetSymbol.flags & SymbolFlags.Module) {
- Debug.assert(declaration !== undefined, "expected module symbol to have a declaration");
- importAdder.addImportForModuleSymbol(symbol, isValidTypeOnlyUseSite, declaration);
- }
- else {
- // If the target symbol has no parent but isn't a module, fall back to verbatim import
- importAdder.addVerbatimImport(Debug.checkDefined(declaration ?? findAncestor(symbol.declarations?.[0], isAnyImportOrRequireStatement)));
- }
- }
- else {
- importAdder.addImportFromExportedSymbol(targetSymbol, isValidTypeOnlyUseSite, declaration);
- }
+ importsToCopy.forEach(([isValidTypeOnlyUseSite, declaration], symbol) => {
+ const targetSymbol = skipAlias(symbol, checker);
+ if (checker.isUnknownSymbol(targetSymbol)) {
+ importAdder.addVerbatimImport(Debug.checkDefined(declaration ?? findAncestor(symbol.declarations?.[0], isAnyImportOrRequireStatement)));
+ }
+ else if (targetSymbol.parent === undefined) {
+ if (targetSymbol.flags & SymbolFlags.Module) {
+ Debug.assert(declaration !== undefined, "expected module symbol to have a declaration");
+ importAdder.addImportForModuleSymbol(symbol, isValidTypeOnlyUseSite, declaration);
+ }
+ else {
+ // If the target symbol has no parent but isn't a module, fall back to verbatim import
+ importAdder.addVerbatimImport(Debug.checkDefined(declaration ?? findAncestor(symbol.declarations?.[0], isAnyImportOrRequireStatement)));
+ }
+ }
+ else {
+ importAdder.addImportFromExportedSymbol(targetSymbol, isValidTypeOnlyUseSite, declaration);
+ }
});
addImportsForMovedSymbols(targetFileImportsFromOldFile, oldFile.fileName, importAdder, program);