From ab18beb131adf82fb3a49dc25ed2adf323f4f8d4 Mon Sep 17 00:00:00 2001 From: Kaio Duarte Date: Thu, 5 Jan 2023 11:54:11 +0000 Subject: [PATCH] feat(rome_js_analyze): `useYield` rule (#4037) Closes https://github.com/rome/tools/issues/3994 --- .../src/categories.rs | 1 + .../rome_js_analyze/src/analyzers/nursery.rs | 3 +- .../src/analyzers/nursery/use_yield.rs | 180 ++++++++++++ .../tests/specs/nursery/useYield.js | 93 ++++++ .../tests/specs/nursery/useYield.js.snap | 267 ++++++++++++++++++ .../src/configuration/linter/rules.rs | 13 +- editors/vscode/configuration_schema.json | 11 + npm/backend-jsonrpc/src/workspace.ts | 5 + npm/rome/configuration_schema.json | 11 + website/src/pages/lint/rules/index.mdx | 6 + website/src/pages/lint/rules/useYield.md | 56 ++++ 11 files changed, 641 insertions(+), 5 deletions(-) create mode 100644 crates/rome_js_analyze/src/analyzers/nursery/use_yield.rs create mode 100644 crates/rome_js_analyze/tests/specs/nursery/useYield.js create mode 100644 crates/rome_js_analyze/tests/specs/nursery/useYield.js.snap create mode 100644 website/src/pages/lint/rules/useYield.md diff --git a/crates/rome_diagnostics_categories/src/categories.rs b/crates/rome_diagnostics_categories/src/categories.rs index 9c79e05aa48..5c0c197b41d 100644 --- a/crates/rome_diagnostics_categories/src/categories.rs +++ b/crates/rome_diagnostics_categories/src/categories.rs @@ -93,6 +93,7 @@ define_dategories! { "lint/nursery/useValidForDirection": "https://docs.rome.tools/lint/rules/useValidForDirection", "lint/nursery/useHookAtTopLevel": "https://docs.rome.tools/lint/rules/useHookAtTopLevel", "lint/nursery/noDuplicateJsxProps": "https://docs.rome.tools/lint/rules/noDuplicateJsxProps", + "lint/nursery/useYield": "https://docs.rome.tools/lint/rules/useYield", // Insert new nursery rule here // performance diff --git a/crates/rome_js_analyze/src/analyzers/nursery.rs b/crates/rome_js_analyze/src/analyzers/nursery.rs index aefcc71953f..2f9ea994520 100644 --- a/crates/rome_js_analyze/src/analyzers/nursery.rs +++ b/crates/rome_js_analyze/src/analyzers/nursery.rs @@ -37,4 +37,5 @@ mod use_exponentiation_operator; mod use_is_nan; mod use_media_caption; mod use_numeric_literals; -declare_group! { pub (crate) Nursery { name : "nursery" , rules : [self :: no_access_key :: NoAccessKey , self :: no_assign_in_expressions :: NoAssignInExpressions , self :: no_banned_types :: NoBannedTypes , self :: no_comma_operator :: NoCommaOperator , self :: no_const_enum :: NoConstEnum , self :: no_constructor_return :: NoConstructorReturn , self :: no_distracting_elements :: NoDistractingElements , self :: no_duplicate_case :: NoDuplicateCase , self :: no_duplicate_jsx_props :: NoDuplicateJsxProps , self :: no_duplicate_object_keys :: NoDuplicateObjectKeys , self :: no_empty_interface :: NoEmptyInterface , self :: no_extra_non_null_assertion :: NoExtraNonNullAssertion , self :: no_extra_semicolons :: NoExtraSemicolons , self :: no_header_scope :: NoHeaderScope , self :: no_inner_declarations :: NoInnerDeclarations , self :: no_invalid_constructor_super :: NoInvalidConstructorSuper , self :: no_non_null_assertion :: NoNonNullAssertion , self :: no_precision_loss :: NoPrecisionLoss , self :: no_redundant_alt :: NoRedundantAlt , self :: no_redundant_use_strict :: NoRedundantUseStrict , self :: no_self_compare :: NoSelfCompare , self :: no_setter_return :: NoSetterReturn , self :: no_string_case_mismatch :: NoStringCaseMismatch , self :: no_unreachable_super :: NoUnreachableSuper , self :: no_unsafe_finally :: NoUnsafeFinally , self :: no_unused_labels :: NoUnusedLabels , self :: no_useless_switch_case :: NoUselessSwitchCase , self :: no_void_type_return :: NoVoidTypeReturn , self :: no_with :: NoWith , self :: use_default_parameter_last :: UseDefaultParameterLast , self :: use_default_switch_clause_last :: UseDefaultSwitchClauseLast , self :: use_enum_initializers :: UseEnumInitializers , self :: use_exponentiation_operator :: UseExponentiationOperator , self :: use_is_nan :: UseIsNan , self :: use_media_caption :: UseMediaCaption , self :: use_numeric_literals :: UseNumericLiterals ,] } } +mod use_yield; +declare_group! { pub (crate) Nursery { name : "nursery" , rules : [self :: no_access_key :: NoAccessKey , self :: no_assign_in_expressions :: NoAssignInExpressions , self :: no_banned_types :: NoBannedTypes , self :: no_comma_operator :: NoCommaOperator , self :: no_const_enum :: NoConstEnum , self :: no_constructor_return :: NoConstructorReturn , self :: no_distracting_elements :: NoDistractingElements , self :: no_duplicate_case :: NoDuplicateCase , self :: no_duplicate_jsx_props :: NoDuplicateJsxProps , self :: no_duplicate_object_keys :: NoDuplicateObjectKeys , self :: no_empty_interface :: NoEmptyInterface , self :: no_extra_non_null_assertion :: NoExtraNonNullAssertion , self :: no_extra_semicolons :: NoExtraSemicolons , self :: no_header_scope :: NoHeaderScope , self :: no_inner_declarations :: NoInnerDeclarations , self :: no_invalid_constructor_super :: NoInvalidConstructorSuper , self :: no_non_null_assertion :: NoNonNullAssertion , self :: no_precision_loss :: NoPrecisionLoss , self :: no_redundant_alt :: NoRedundantAlt , self :: no_redundant_use_strict :: NoRedundantUseStrict , self :: no_self_compare :: NoSelfCompare , self :: no_setter_return :: NoSetterReturn , self :: no_string_case_mismatch :: NoStringCaseMismatch , self :: no_unreachable_super :: NoUnreachableSuper , self :: no_unsafe_finally :: NoUnsafeFinally , self :: no_unused_labels :: NoUnusedLabels , self :: no_useless_switch_case :: NoUselessSwitchCase , self :: no_void_type_return :: NoVoidTypeReturn , self :: no_with :: NoWith , self :: use_default_parameter_last :: UseDefaultParameterLast , self :: use_default_switch_clause_last :: UseDefaultSwitchClauseLast , self :: use_enum_initializers :: UseEnumInitializers , self :: use_exponentiation_operator :: UseExponentiationOperator , self :: use_is_nan :: UseIsNan , self :: use_media_caption :: UseMediaCaption , self :: use_numeric_literals :: UseNumericLiterals , self :: use_yield :: UseYield ,] } } diff --git a/crates/rome_js_analyze/src/analyzers/nursery/use_yield.rs b/crates/rome_js_analyze/src/analyzers/nursery/use_yield.rs new file mode 100644 index 00000000000..4f79eab8e69 --- /dev/null +++ b/crates/rome_js_analyze/src/analyzers/nursery/use_yield.rs @@ -0,0 +1,180 @@ +use rome_analyze::context::RuleContext; +use rome_analyze::{ + declare_rule, AddVisitor, Phases, QueryMatch, Queryable, Rule, RuleDiagnostic, ServiceBag, + Visitor, VisitorContext, +}; +use rome_console::markup; +use rome_js_syntax::{ + AnyJsFunction, JsLanguage, JsMethodClassMember, JsMethodObjectMember, JsStatementList, + JsYieldExpression, TextRange, WalkEvent, +}; +use rome_rowan::{declare_node_union, AstNode, AstNodeList, Language, SyntaxNode}; + +declare_rule! { + /// Require generator functions to contain `yield`. + /// + /// This rule generates warnings for generator functions that do not have the `yield` keyword. + /// + /// Source: [require-await](https://eslint.org/docs/latest/rules/require-yield). + /// + /// ## Examples + /// + /// ### Invalid + /// + /// ```js,expect_diagnostic + /// function* foo() { + /// return 10; + /// } + /// ``` + /// + /// ### Valid + /// ```js + /// function* foo() { + /// yield 5; + /// return 10; + /// } + /// + /// function foo() { + /// return 10; + /// } + /// + /// // This rule does not warn on empty generator functions. + /// function* foo() { } + /// ``` + pub(crate) UseYield { + version: "next", + name: "useYield", + recommended: true, + } +} + +declare_node_union! { + pub(crate) AnyFunctionLike = AnyJsFunction | JsMethodObjectMember | JsMethodClassMember +} + +impl AnyFunctionLike { + fn is_generator(&self) -> bool { + match self { + AnyFunctionLike::AnyJsFunction(any_js_function) => any_js_function.is_generator(), + AnyFunctionLike::JsMethodClassMember(method_class_member) => { + method_class_member.star_token().is_some() + } + AnyFunctionLike::JsMethodObjectMember(method_obj_member) => { + method_obj_member.star_token().is_some() + } + } + } + + fn statements(&self) -> Option { + Some(match self { + AnyFunctionLike::AnyJsFunction(any_js_function) => any_js_function + .body() + .ok()? + .as_js_function_body()? + .statements(), + AnyFunctionLike::JsMethodClassMember(method_class_member) => { + method_class_member.body().ok()?.statements() + } + AnyFunctionLike::JsMethodObjectMember(method_obj_member) => { + method_obj_member.body().ok()?.statements() + } + }) + } +} + +#[derive(Default)] +struct MissingYieldVisitor { + /// Vector to hold a function node and a boolean indicating whether the function + /// contains an `yield` expression or not. + stack: Vec<(AnyFunctionLike, bool)>, +} + +impl Visitor for MissingYieldVisitor { + type Language = JsLanguage; + + fn visit( + &mut self, + event: &WalkEvent>, + mut ctx: VisitorContext, + ) { + match event { + WalkEvent::Enter(node) => { + // When the visitor enters a function node, push a new entry on the stack + if let Some(node) = AnyFunctionLike::cast_ref(node) { + self.stack.push((node, false)); + } + + if JsYieldExpression::can_cast(node.kind()) { + // When the visitor enters a `yield` expression, set the + // `has_yield` flag for the top entry on the stack to `true` + if let Some((_, has_yield)) = self.stack.last_mut() { + *has_yield = true; + } + } + } + WalkEvent::Leave(node) => { + // When the visitor exits a function, if it matches the node of the top-most + // entry of the stack and the `has_yield` flag is `false`, emit a query match + if let Some(exit_node) = AnyFunctionLike::cast_ref(node) { + if let Some((enter_node, has_yield)) = self.stack.pop() { + if enter_node == exit_node && !has_yield { + ctx.match_query(MissingYield(enter_node)); + } + } + } + } + } + } +} + +pub(crate) struct MissingYield(AnyFunctionLike); + +impl QueryMatch for MissingYield { + fn text_range(&self) -> TextRange { + self.0.range() + } +} + +impl Queryable for MissingYield { + type Input = Self; + type Language = JsLanguage; + type Output = AnyFunctionLike; + type Services = (); + + fn build_visitor( + analyzer: &mut impl AddVisitor, + _: &::Root, + ) { + analyzer.add_visitor(Phases::Syntax, MissingYieldVisitor::default); + } + + fn unwrap_match(_: &ServiceBag, query: &Self::Input) -> Self::Output { + query.0.clone() + } +} + +impl Rule for UseYield { + type Query = MissingYield; + type State = (); + type Signals = Option; + type Options = (); + + fn run(ctx: &RuleContext) -> Self::Signals { + let query = ctx.query(); + + // Don't emit diagnostic for non-generators or generators with an empty body + if !query.is_generator() || query.statements()?.is_empty() { + return None; + } + + Some(()) + } + + fn diagnostic(ctx: &RuleContext, _: &Self::State) -> Option { + Some(RuleDiagnostic::new( + rule_category!(), + ctx.query().range(), + markup! {"This generator function doesn't contain ""yield""."}, + )) + } +} diff --git a/crates/rome_js_analyze/tests/specs/nursery/useYield.js b/crates/rome_js_analyze/tests/specs/nursery/useYield.js new file mode 100644 index 00000000000..73cf45f2117 --- /dev/null +++ b/crates/rome_js_analyze/tests/specs/nursery/useYield.js @@ -0,0 +1,93 @@ +// Valid +function foo() { + return 0; +} + +function* foo() { + yield 0; +} + +function* foo() {} + +(function* foo() { + yield 0; +})(); + +(function* foo() {})(); + +const obj = { + *foo() { + yield 0; + }, +}; + +const obj = { *foo() {} }; + +class A { + *foo() { + yield 0; + } +} + +class A { + *foo() {} +} + +// Invalid +function* foo() { + return 0; +} + +(function* foo() { + return 0; +})(); + +const obj = { + *foo() { + return 0; + }, +}; + +class A { + *foo() { + return 0; + } +} + +function* foo() { + function* bar() { + yield 0; + } +} + +function* foo() { + function* bar() { + return 0; + } + yield 0; +} + +function* foo() { + function* bar() { + yield 0; + } + return 0; +} + +function* foo() { + const obj = { + *bar() { + return 0; + }, + }; + + class A { + *baz() { + return 0; + } + } + + if (a === 'a') { + yield 0; + } +} diff --git a/crates/rome_js_analyze/tests/specs/nursery/useYield.js.snap b/crates/rome_js_analyze/tests/specs/nursery/useYield.js.snap new file mode 100644 index 00000000000..0e92d3bc904 --- /dev/null +++ b/crates/rome_js_analyze/tests/specs/nursery/useYield.js.snap @@ -0,0 +1,267 @@ +--- +source: crates/rome_js_analyze/tests/spec_tests.rs +assertion_line: 92 +expression: useYield.js +--- +# Input +```js +// Valid +function foo() { + return 0; +} + +function* foo() { + yield 0; +} + +function* foo() {} + +(function* foo() { + yield 0; +})(); + +(function* foo() {})(); + +const obj = { + *foo() { + yield 0; + }, +}; + +const obj = { *foo() {} }; + +class A { + *foo() { + yield 0; + } +} + +class A { + *foo() {} +} + +// Invalid +function* foo() { + return 0; +} + +(function* foo() { + return 0; +})(); + +const obj = { + *foo() { + return 0; + }, +}; + +class A { + *foo() { + return 0; + } +} + +function* foo() { + function* bar() { + yield 0; + } +} + +function* foo() { + function* bar() { + return 0; + } + yield 0; +} + +function* foo() { + function* bar() { + yield 0; + } + return 0; +} + +function* foo() { + const obj = { + *bar() { + return 0; + }, + }; + + class A { + *baz() { + return 0; + } + } + + if (a === 'a') { + yield 0; + } +} + +``` + +# Diagnostics +``` +useYield.js:37:1 lint/nursery/useYield ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! This generator function doesn't contain yield. + + 36 │ // Invalid + > 37 │ function* foo() { + │ ^^^^^^^^^^^^^^^^^ + > 38 │ return 0; + > 39 │ } + │ ^ + 40 │ + 41 │ (function* foo() { + + +``` + +``` +useYield.js:41:2 lint/nursery/useYield ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! This generator function doesn't contain yield. + + 39 │ } + 40 │ + > 41 │ (function* foo() { + │ ^^^^^^^^^^^^^^^^^ + > 42 │ return 0; + > 43 │ })(); + │ ^ + 44 │ + 45 │ const obj = { + + +``` + +``` +useYield.js:46:2 lint/nursery/useYield ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! This generator function doesn't contain yield. + + 45 │ const obj = { + > 46 │ *foo() { + │ ^^^^^^^^ + > 47 │ return 0; + > 48 │ }, + │ ^ + 49 │ }; + 50 │ + + +``` + +``` +useYield.js:52:2 lint/nursery/useYield ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! This generator function doesn't contain yield. + + 51 │ class A { + > 52 │ *foo() { + │ ^^^^^^^^ + > 53 │ return 0; + > 54 │ } + │ ^ + 55 │ } + 56 │ + + +``` + +``` +useYield.js:57:1 lint/nursery/useYield ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! This generator function doesn't contain yield. + + 55 │ } + 56 │ + > 57 │ function* foo() { + │ ^^^^^^^^^^^^^^^^^ + > 58 │ function* bar() { + > 59 │ yield 0; + > 60 │ } + > 61 │ } + │ ^ + 62 │ + 63 │ function* foo() { + + +``` + +``` +useYield.js:64:2 lint/nursery/useYield ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! This generator function doesn't contain yield. + + 63 │ function* foo() { + > 64 │ function* bar() { + │ ^^^^^^^^^^^^^^^^^ + > 65 │ return 0; + > 66 │ } + │ ^ + 67 │ yield 0; + 68 │ } + + +``` + +``` +useYield.js:70:1 lint/nursery/useYield ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! This generator function doesn't contain yield. + + 68 │ } + 69 │ + > 70 │ function* foo() { + │ ^^^^^^^^^^^^^^^^^ + > 71 │ function* bar() { + > 72 │ yield 0; + > 73 │ } + > 74 │ return 0; + > 75 │ } + │ ^ + 76 │ + 77 │ function* foo() { + + +``` + +``` +useYield.js:79:3 lint/nursery/useYield ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! This generator function doesn't contain yield. + + 77 │ function* foo() { + 78 │ const obj = { + > 79 │ *bar() { + │ ^^^^^^^^ + > 80 │ return 0; + > 81 │ }, + │ ^ + 82 │ }; + 83 │ + + +``` + +``` +useYield.js:85:3 lint/nursery/useYield ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! This generator function doesn't contain yield. + + 84 │ class A { + > 85 │ *baz() { + │ ^^^^^^^^ + > 86 │ return 0; + > 87 │ } + │ ^ + 88 │ } + 89 │ + + +``` + + diff --git a/crates/rome_service/src/configuration/linter/rules.rs b/crates/rome_service/src/configuration/linter/rules.rs index e691492ebfb..b88b14d1a67 100644 --- a/crates/rome_service/src/configuration/linter/rules.rs +++ b/crates/rome_service/src/configuration/linter/rules.rs @@ -829,10 +829,12 @@ struct NurserySchema { use_valid_aria_props: Option, #[doc = "Ensure that the attribute passed to the lang attribute is a correct ISO language and/or country."] use_valid_lang: Option, + #[doc = "Require generator functions to contain yield."] + use_yield: Option, } impl Nursery { const CATEGORY_NAME: &'static str = "nursery"; - pub(crate) const CATEGORY_RULES: [&'static str; 49] = [ + pub(crate) const CATEGORY_RULES: [&'static str; 50] = [ "noAccessKey", "noAssignInExpressions", "noBannedTypes", @@ -882,8 +884,9 @@ impl Nursery { "useNumericLiterals", "useValidAriaProps", "useValidLang", + "useYield", ]; - const RECOMMENDED_RULES: [&'static str; 40] = [ + const RECOMMENDED_RULES: [&'static str; 41] = [ "noAssignInExpressions", "noBannedTypes", "noClassAssign", @@ -924,8 +927,9 @@ impl Nursery { "useNumericLiterals", "useValidAriaProps", "useValidLang", + "useYield", ]; - const RECOMMENDED_RULES_AS_FILTERS: [RuleFilter<'static>; 40] = [ + const RECOMMENDED_RULES_AS_FILTERS: [RuleFilter<'static>; 41] = [ RuleFilter::Rule("nursery", Self::CATEGORY_RULES[1]), RuleFilter::Rule("nursery", Self::CATEGORY_RULES[2]), RuleFilter::Rule("nursery", Self::CATEGORY_RULES[3]), @@ -966,6 +970,7 @@ impl Nursery { RuleFilter::Rule("nursery", Self::CATEGORY_RULES[46]), RuleFilter::Rule("nursery", Self::CATEGORY_RULES[47]), RuleFilter::Rule("nursery", Self::CATEGORY_RULES[48]), + RuleFilter::Rule("nursery", Self::CATEGORY_RULES[49]), ]; pub(crate) fn is_recommended(&self) -> bool { !matches!(self.recommended, Some(false)) } pub(crate) fn get_enabled_rules(&self) -> IndexSet { @@ -992,7 +997,7 @@ impl Nursery { pub(crate) fn is_recommended_rule(rule_name: &str) -> bool { Self::RECOMMENDED_RULES.contains(&rule_name) } - pub(crate) fn recommended_rules_as_filters() -> [RuleFilter<'static>; 40] { + pub(crate) fn recommended_rules_as_filters() -> [RuleFilter<'static>; 41] { Self::RECOMMENDED_RULES_AS_FILTERS } } diff --git a/editors/vscode/configuration_schema.json b/editors/vscode/configuration_schema.json index fa60a4a9c36..1eee1d16de2 100644 --- a/editors/vscode/configuration_schema.json +++ b/editors/vscode/configuration_schema.json @@ -1133,6 +1133,17 @@ "type": "null" } ] + }, + "useYield": { + "description": "Require generator functions to contain yield.", + "anyOf": [ + { + "$ref": "#/definitions/RuleConfiguration" + }, + { + "type": "null" + } + ] } } }, diff --git a/npm/backend-jsonrpc/src/workspace.ts b/npm/backend-jsonrpc/src/workspace.ts index e83686cd3f2..2101816c650 100644 --- a/npm/backend-jsonrpc/src/workspace.ts +++ b/npm/backend-jsonrpc/src/workspace.ts @@ -489,6 +489,10 @@ export interface Nursery { * Ensure that the attribute passed to the lang attribute is a correct ISO language and/or country. */ useValidLang?: RuleConfiguration; + /** + * Require generator functions to contain yield. + */ + useYield?: RuleConfiguration; } /** * A list of rules that belong to this group @@ -809,6 +813,7 @@ export type Category = | "lint/nursery/useValidForDirection" | "lint/nursery/useHookAtTopLevel" | "lint/nursery/noDuplicateJsxProps" + | "lint/nursery/useYield" | "lint/performance/noDelete" | "lint/security/noDangerouslySetInnerHtml" | "lint/security/noDangerouslySetInnerHtmlWithChildren" diff --git a/npm/rome/configuration_schema.json b/npm/rome/configuration_schema.json index fa60a4a9c36..1eee1d16de2 100644 --- a/npm/rome/configuration_schema.json +++ b/npm/rome/configuration_schema.json @@ -1133,6 +1133,17 @@ "type": "null" } ] + }, + "useYield": { + "description": "Require generator functions to contain yield.", + "anyOf": [ + { + "$ref": "#/definitions/RuleConfiguration" + }, + { + "type": "null" + } + ] } } }, diff --git a/website/src/pages/lint/rules/index.mdx b/website/src/pages/lint/rules/index.mdx index cd060491a8f..8f2a0442300 100644 --- a/website/src/pages/lint/rules/index.mdx +++ b/website/src/pages/lint/rules/index.mdx @@ -782,5 +782,11 @@ Ensures that ARIA properties aria-* are all valid. Ensure that the attribute passed to the lang attribute is a correct ISO language and/or country. +
+

+ useYield +

+Require generator functions to contain yield. +
diff --git a/website/src/pages/lint/rules/useYield.md b/website/src/pages/lint/rules/useYield.md new file mode 100644 index 00000000000..632e09d48b3 --- /dev/null +++ b/website/src/pages/lint/rules/useYield.md @@ -0,0 +1,56 @@ +--- +title: Lint Rule useYield +parent: lint/rules/index +--- + +# useYield (since vnext) + +Require generator functions to contain `yield`. + +This rule generates warnings for generator functions that do not have the `yield` keyword. + +Source: [require-await](https://eslint.org/docs/latest/rules/require-yield). + +## Examples + +### Invalid + +```jsx +function* foo() { + return 10; +} +``` + +
nursery/useYield.js:1:1 lint/nursery/useYield ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+   This generator function doesn't contain yield.
+  
+  > 1 │ function* foo() {
+   ^^^^^^^^^^^^^^^^^
+  > 2 │   return 10;
+  > 3 │ }
+   ^
+    4 │ 
+  
+
+ +### Valid + +```jsx +function* foo() { + yield 5; + return 10; +} + +function foo() { + return 10; +} + +// This rule does not warn on empty generator functions. +function* foo() { } +``` + +## Related links + +- [Disable a rule](/linter/#disable-a-lint-rule) +- [Rule options](/linter/#rule-options)