From 277ae434114ac13a62e6747e964b54c155bf82f1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 14 Jul 2025 17:05:02 +0000 Subject: [PATCH 1/3] Initial plan From 6f002ef7f94e2db92802d42ed10d8a262992221b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 14 Jul 2025 17:13:53 +0000 Subject: [PATCH 2/3] Fix: Exclude generator functions from convert-to-class suggestions Co-authored-by: RyanCavanaugh <6685088+RyanCavanaugh@users.noreply.github.com> --- src/services/suggestionDiagnostics.ts | 40 ++++++++++++------- ...Class_generatorExpression_falsePositive.ts | 10 +++++ ...ctionToEs6Class_generator_falsePositive.ts | 10 +++++ ...rtFunctionToEs6Class_regularVsGenerator.ts | 17 ++++++++ 4 files changed, 62 insertions(+), 15 deletions(-) create mode 100644 tests/cases/fourslash/convertFunctionToEs6Class_generatorExpression_falsePositive.ts create mode 100644 tests/cases/fourslash/convertFunctionToEs6Class_generator_falsePositive.ts create mode 100644 tests/cases/fourslash/convertFunctionToEs6Class_regularVsGenerator.ts diff --git a/src/services/suggestionDiagnostics.ts b/src/services/suggestionDiagnostics.ts index 80fe528e791f1..1b29e16709b15 100644 --- a/src/services/suggestionDiagnostics.ts +++ b/src/services/suggestionDiagnostics.ts @@ -289,21 +289,31 @@ function getKeyFromNode(exp: FunctionLikeDeclaration) { return `${exp.pos.toString()}:${exp.end.toString()}`; } -function canBeConvertedToClass(node: Node, checker: TypeChecker): boolean { - if (isFunctionExpression(node)) { - if (isVariableDeclaration(node.parent) && node.symbol.members?.size) { - return true; - } - - const symbol = checker.getSymbolOfExpando(node, /*allowDeclaration*/ false); - return !!(symbol && (symbol.exports?.size || symbol.members?.size)); - } - - if (isFunctionDeclaration(node)) { - return !!node.symbol.members?.size; - } - - return false; +function canBeConvertedToClass(node: Node, checker: TypeChecker): boolean { + if (isFunctionExpression(node)) { + // Generator functions cannot be converted to classes + if (getFunctionFlags(node) & FunctionFlags.Generator) { + return false; + } + + if (isVariableDeclaration(node.parent) && node.symbol.members?.size) { + return true; + } + + const symbol = checker.getSymbolOfExpando(node, /*allowDeclaration*/ false); + return !!(symbol && (symbol.exports?.size || symbol.members?.size)); + } + + if (isFunctionDeclaration(node)) { + // Generator functions cannot be converted to classes + if (getFunctionFlags(node) & FunctionFlags.Generator) { + return false; + } + + return !!node.symbol.members?.size; + } + + return false; } /** @internal */ diff --git a/tests/cases/fourslash/convertFunctionToEs6Class_generatorExpression_falsePositive.ts b/tests/cases/fourslash/convertFunctionToEs6Class_generatorExpression_falsePositive.ts new file mode 100644 index 0000000000000..87c82f1789551 --- /dev/null +++ b/tests/cases/fourslash/convertFunctionToEs6Class_generatorExpression_falsePositive.ts @@ -0,0 +1,10 @@ +/// + +// @allowJs: true +// @Filename: /a.js +////const gen = function*() {}; +////gen.prototype.next = gen.prototype.next; +////gen.prototype.return = gen.prototype.return; + +// Generator function expressions should not trigger the convert-to-class suggestion +verify.getSuggestionDiagnostics([]); \ No newline at end of file diff --git a/tests/cases/fourslash/convertFunctionToEs6Class_generator_falsePositive.ts b/tests/cases/fourslash/convertFunctionToEs6Class_generator_falsePositive.ts new file mode 100644 index 0000000000000..77390d67162eb --- /dev/null +++ b/tests/cases/fourslash/convertFunctionToEs6Class_generator_falsePositive.ts @@ -0,0 +1,10 @@ +/// + +// @allowJs: true +// @Filename: /a.js +////function* gen() {} +////gen.prototype.next = gen.prototype.next; +////gen.prototype.return = gen.prototype.return; + +// Generator functions should not trigger the convert-to-class suggestion +verify.getSuggestionDiagnostics([]); \ No newline at end of file diff --git a/tests/cases/fourslash/convertFunctionToEs6Class_regularVsGenerator.ts b/tests/cases/fourslash/convertFunctionToEs6Class_regularVsGenerator.ts new file mode 100644 index 0000000000000..0d0c9653e61f1 --- /dev/null +++ b/tests/cases/fourslash/convertFunctionToEs6Class_regularVsGenerator.ts @@ -0,0 +1,17 @@ +/// + +// @allowJs: true +// @Filename: /a.js +////function [|regular|]() {} +////regular.prototype.method = function() { this.x = 1; }; +//// +////function* gen() {} +////gen.prototype.next = gen.prototype.next; +////gen.prototype.return = gen.prototype.return; + +// Regular constructor functions should still trigger the convert-to-class suggestion +// but generator functions should not +verify.getSuggestionDiagnostics([{ + message: "This constructor function may be converted to a class declaration.", + code: 80002, +}]); \ No newline at end of file From 6a6b5225c6c375ba01fb19f8f294b1c3dd4db6a9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 14 Jul 2025 17:15:07 +0000 Subject: [PATCH 3/3] Apply code formatting --- src/services/suggestionDiagnostics.ts | 50 +++++++++++++-------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/src/services/suggestionDiagnostics.ts b/src/services/suggestionDiagnostics.ts index 1b29e16709b15..c38257994c901 100644 --- a/src/services/suggestionDiagnostics.ts +++ b/src/services/suggestionDiagnostics.ts @@ -289,31 +289,31 @@ function getKeyFromNode(exp: FunctionLikeDeclaration) { return `${exp.pos.toString()}:${exp.end.toString()}`; } -function canBeConvertedToClass(node: Node, checker: TypeChecker): boolean { - if (isFunctionExpression(node)) { - // Generator functions cannot be converted to classes - if (getFunctionFlags(node) & FunctionFlags.Generator) { - return false; - } - - if (isVariableDeclaration(node.parent) && node.symbol.members?.size) { - return true; - } - - const symbol = checker.getSymbolOfExpando(node, /*allowDeclaration*/ false); - return !!(symbol && (symbol.exports?.size || symbol.members?.size)); - } - - if (isFunctionDeclaration(node)) { - // Generator functions cannot be converted to classes - if (getFunctionFlags(node) & FunctionFlags.Generator) { - return false; - } - - return !!node.symbol.members?.size; - } - - return false; +function canBeConvertedToClass(node: Node, checker: TypeChecker): boolean { + if (isFunctionExpression(node)) { + // Generator functions cannot be converted to classes + if (getFunctionFlags(node) & FunctionFlags.Generator) { + return false; + } + + if (isVariableDeclaration(node.parent) && node.symbol.members?.size) { + return true; + } + + const symbol = checker.getSymbolOfExpando(node, /*allowDeclaration*/ false); + return !!(symbol && (symbol.exports?.size || symbol.members?.size)); + } + + if (isFunctionDeclaration(node)) { + // Generator functions cannot be converted to classes + if (getFunctionFlags(node) & FunctionFlags.Generator) { + return false; + } + + return !!node.symbol.members?.size; + } + + return false; } /** @internal */