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 */