From 7f9ca8d7e2727c19f9b5f129e847c11ebf371160 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 18 Jun 2025 17:52:31 +0000 Subject: [PATCH 1/3] Initial plan for issue From 55213d837a98a2e8047041af28093ad34c30b461 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 18 Jun 2025 18:05:12 +0000 Subject: [PATCH 2/3] Initial analysis: identified bug in import fixing logic for symbols in computed property access Co-authored-by: RyanCavanaugh <6685088+RyanCavanaugh@users.noreply.github.com> --- .../completionsSymbolTypeOnlyImportBug.ts | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 tests/cases/fourslash/completionsSymbolTypeOnlyImportBug.ts diff --git a/tests/cases/fourslash/completionsSymbolTypeOnlyImportBug.ts b/tests/cases/fourslash/completionsSymbolTypeOnlyImportBug.ts new file mode 100644 index 0000000000000..1dbaa50dc626d --- /dev/null +++ b/tests/cases/fourslash/completionsSymbolTypeOnlyImportBug.ts @@ -0,0 +1,47 @@ +/// + +// Test case for symbol property auto-import issue +// Should import symbol as value, not type when there's already a type-only import + +// @Filename: /exportsSymbol.ts +////export const SYM_FOO_BAR = Symbol.for("foo.bar"); +//// +////export interface ObjWithSym { +//// [SYM_FOO_BAR]: any; +////} + +// @Filename: /usesSymbol.ts +////import type { ObjWithSym } from "./exportsSymbol"; +//// +////export declare const thing: ObjWithSym; +//// +////function main() { +//// thing[|SYM_FOO_BAR/**/|] +////} + +verify.completions({ + marker: "", + exact: [ + { name: "SYM_FOO_BAR", source: "/exportsSymbol", insertText: "SYM_FOO_BAR", hasAction: true }, + ], + preferences: { + includeInsertTextCompletions: true, + includeCompletionsForModuleExports: true, + }, +}); + +// This should create a separate value import, not add to the existing type-only import +verify.applyCodeActionFromCompletion("", { + name: "SYM_FOO_BAR", + source: "/exportsSymbol", + description: `Add import from "./exportsSymbol"`, + newFileContent: +`import type { ObjWithSym } from "./exportsSymbol"; +import { SYM_FOO_BAR } from "./exportsSymbol"; + +export declare const thing: ObjWithSym; + +function main() { + thing[SYM_FOO_BAR] +}` +}); \ No newline at end of file From 56895aab9814ffc43a11c22012cb429637d7ce30 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 18 Jun 2025 18:14:08 +0000 Subject: [PATCH 3/3] Fix symbol auto-import bug: prevent value symbols from being added to type-only imports Co-authored-by: RyanCavanaugh <6685088+RyanCavanaugh@users.noreply.github.com> --- src/services/codefixes/importFixes.ts | 38 +++++++++++-------- .../completionsSymbolTypeOnlyImportBug.ts | 12 +++--- 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/src/services/codefixes/importFixes.ts b/src/services/codefixes/importFixes.ts index 6034c9dfc3de1..e04b2b0f5250a 100644 --- a/src/services/codefixes/importFixes.ts +++ b/src/services/codefixes/importFixes.ts @@ -1098,22 +1098,28 @@ function getAddAsTypeOnly( return AddAsTypeOnly.Allowed; } -function tryAddToExistingImport(existingImports: readonly FixAddToExistingImportInfo[], isValidTypeOnlyUseSite: boolean, checker: TypeChecker, compilerOptions: CompilerOptions): FixAddToExistingImport | undefined { - let best: FixAddToExistingImport | undefined; - for (const existingImport of existingImports) { - const fix = getAddToExistingImportFix(existingImport); - if (!fix) continue; - const isTypeOnly = isTypeOnlyImportDeclaration(fix.importClauseOrBindingPattern); - if ( - fix.addAsTypeOnly !== AddAsTypeOnly.NotAllowed && isTypeOnly || - fix.addAsTypeOnly === AddAsTypeOnly.NotAllowed && !isTypeOnly - ) { - // Give preference to putting types in existing type-only imports and avoiding conversions - // of import statements to/from type-only. - return fix; - } - best ??= fix; - } +function tryAddToExistingImport(existingImports: readonly FixAddToExistingImportInfo[], isValidTypeOnlyUseSite: boolean, checker: TypeChecker, compilerOptions: CompilerOptions): FixAddToExistingImport | undefined { + let best: FixAddToExistingImport | undefined; + for (const existingImport of existingImports) { + const fix = getAddToExistingImportFix(existingImport); + if (!fix) continue; + const isTypeOnly = isTypeOnlyImportDeclaration(fix.importClauseOrBindingPattern); + + // Don't add value-only symbols to type-only imports + if (fix.addAsTypeOnly === AddAsTypeOnly.NotAllowed && isTypeOnly) { + continue; + } + + if ( + fix.addAsTypeOnly !== AddAsTypeOnly.NotAllowed && isTypeOnly || + fix.addAsTypeOnly === AddAsTypeOnly.NotAllowed && !isTypeOnly + ) { + // Give preference to putting types in existing type-only imports and avoiding conversions + // of import statements to/from type-only. + return fix; + } + best ??= fix; + } return best; function getAddToExistingImportFix({ declaration, importKind, symbol, targetFlags }: FixAddToExistingImportInfo): FixAddToExistingImport | undefined { diff --git a/tests/cases/fourslash/completionsSymbolTypeOnlyImportBug.ts b/tests/cases/fourslash/completionsSymbolTypeOnlyImportBug.ts index 1dbaa50dc626d..b4e175776c889 100644 --- a/tests/cases/fourslash/completionsSymbolTypeOnlyImportBug.ts +++ b/tests/cases/fourslash/completionsSymbolTypeOnlyImportBug.ts @@ -1,7 +1,7 @@ /// // Test case for symbol property auto-import issue -// Should import symbol as value, not type when there's already a type-only import +// Should import symbol as value, not add to existing type-only import // @Filename: /exportsSymbol.ts ////export const SYM_FOO_BAR = Symbol.for("foo.bar"); @@ -16,16 +16,16 @@ ////export declare const thing: ObjWithSym; //// ////function main() { -//// thing[|SYM_FOO_BAR/**/|] +//// thing[/**/] ////} +goTo.marker(""); + verify.completions({ - marker: "", - exact: [ - { name: "SYM_FOO_BAR", source: "/exportsSymbol", insertText: "SYM_FOO_BAR", hasAction: true }, + includes: [ + { name: "SYM_FOO_BAR", source: "/exportsSymbol", hasAction: true }, ], preferences: { - includeInsertTextCompletions: true, includeCompletionsForModuleExports: true, }, });