-
-
Notifications
You must be signed in to change notification settings - Fork 533
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(linter): implement useExplicitFunctionReturnType #3990
Changes from 10 commits
0f211fe
9543f15
e16f055
c067a6d
7d41348
8a300e4
2fe2884
ab88eee
6d15b19
899fd56
a3353cb
999f7ba
c1141c7
b7a1fe4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
use biome_analyze::{ | ||
context::RuleContext, declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleSource, | ||
}; | ||
use biome_console::markup; | ||
use biome_js_semantic::HasClosureAstNode; | ||
use biome_js_syntax::AnyJsBinding; | ||
use biome_js_syntax::{AnyJsFunction, JsGetterClassMember, JsMethodClassMember}; | ||
use biome_rowan::{declare_node_union, AstNode, TextRange}; | ||
|
||
declare_lint_rule! { | ||
/// Require explicit return types on functions and class methods. | ||
/// | ||
/// Functions in TypeScript often don't need to be given an explicit return type annotation. | ||
/// Leaving off the return type is less code to read or write and allows the compiler to infer it from the contents of the function. | ||
/// | ||
/// However, explicit return types do make it visually more clear what type is returned by a function. | ||
/// They can also speed up TypeScript type checking performance in large codebases with many large functions. | ||
/// Explicit return types also reduce the chance of bugs by asserting the return type, and it avoids surprising "action at a distance," where changing the body of one function may cause failures inside another function. | ||
/// | ||
/// This rule enforces that functions do have an explicit return type annotation. | ||
/// | ||
/// ## Examples | ||
/// | ||
/// ### Invalid | ||
/// | ||
/// ```ts,expect_diagnostic | ||
/// // Should indicate that no value is returned (void) | ||
/// function test() { | ||
/// return; | ||
/// } | ||
/// ``` | ||
/// | ||
/// ```ts,expect_diagnostic | ||
/// // Should indicate that a number is returned | ||
/// var fn = function () { | ||
/// return 1; | ||
/// }; | ||
/// ``` | ||
/// | ||
/// ```ts,expect_diagnostic | ||
/// // Should indicate that a string is returned | ||
/// var arrowFn = () => 'test'; | ||
/// ``` | ||
/// | ||
/// ```ts,expect_diagnostic | ||
/// class Test { | ||
/// // Should indicate that no value is returned (void) | ||
/// method() { | ||
/// return; | ||
/// } | ||
/// } | ||
/// ``` | ||
/// | ||
/// ```ts,expect_diagnostic | ||
/// // Should indicate that no value is returned (void) | ||
/// function test(a: number) { | ||
/// a += 1; | ||
/// } | ||
/// ``` | ||
/// | ||
/// ### Valid | ||
/// ```ts | ||
/// // No return value should be expected (void) | ||
/// function test(): void { | ||
/// return; | ||
/// } | ||
/// ``` | ||
/// | ||
/// ```ts | ||
/// // A return value of type number | ||
/// var fn = function (): number { | ||
/// return 1; | ||
/// } | ||
/// ``` | ||
/// | ||
/// ```ts | ||
/// // A return value of type string | ||
/// var arrowFn = (): string => 'test'; | ||
/// ``` | ||
/// | ||
/// ```ts | ||
/// class Test { | ||
/// // No return value should be expected (void) | ||
/// method(): void { | ||
/// return; | ||
/// } | ||
/// } | ||
/// ``` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about a function without any explicit There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes it is required, but I think it's nice to include it in the example. I added it to the invalid example! |
||
/// | ||
pub UseExplicitFunctionReturnType { | ||
version: "next", | ||
name: "useExplicitFunctionReturnType", | ||
language: "ts", | ||
recommended: false, | ||
sources: &[RuleSource::EslintTypeScript("explicit-function-return-type")], | ||
} | ||
} | ||
|
||
declare_node_union! { | ||
pub AnyJsFunctionAndMethod = AnyJsFunction | JsMethodClassMember | JsGetterClassMember | ||
Conaclos marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
impl Rule for UseExplicitFunctionReturnType { | ||
type Query = Ast<AnyJsFunctionAndMethod>; | ||
type State = TextRange; | ||
type Signals = Option<Self::State>; | ||
type Options = (); | ||
|
||
fn run(ctx: &RuleContext<Self>) -> Self::Signals { | ||
let node = ctx.query(); | ||
match node { | ||
AnyJsFunctionAndMethod::AnyJsFunction(func) => { | ||
if func.return_type_annotation().is_some() { | ||
return None; | ||
} | ||
|
||
let func_range = func.syntax().text_range(); | ||
if let Ok(Some(AnyJsBinding::JsIdentifierBinding(id))) = func.id() { | ||
return Some(TextRange::new( | ||
func_range.start(), | ||
id.syntax().text_range().end(), | ||
)); | ||
} | ||
|
||
Some(func_range) | ||
} | ||
AnyJsFunctionAndMethod::JsMethodClassMember(method) => { | ||
if method.return_type_annotation().is_some() { | ||
return None; | ||
} | ||
|
||
Some(method.node_text_range()) | ||
} | ||
AnyJsFunctionAndMethod::JsGetterClassMember(getter) => { | ||
if getter.return_type().is_some() { | ||
return None; | ||
} | ||
|
||
Some(getter.node_text_range()) | ||
} | ||
} | ||
} | ||
|
||
fn diagnostic(_: &RuleContext<Self>, state: &Self::State) -> Option<RuleDiagnostic> { | ||
Some( | ||
RuleDiagnostic::new( | ||
rule_category!(), | ||
state, | ||
markup! { | ||
"Missing return type on function." | ||
}, | ||
) | ||
.note(markup! { | ||
"Require explicit return types on functions and class methods." | ||
Conaclos marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}), | ||
) | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
function test(a: number, b: number) { | ||
return; | ||
} | ||
|
||
function test() { | ||
return; | ||
} | ||
|
||
var fn = function () { | ||
return 1; | ||
}; | ||
|
||
var arrowFn = () => "test"; | ||
|
||
class Test { | ||
constructor() {} | ||
get prop() { | ||
return 1; | ||
} | ||
set prop() {} | ||
method() { | ||
return; | ||
} | ||
arrow = () => "arrow"; | ||
private method() { | ||
return; | ||
} | ||
} | ||
Comment on lines
+1
to
+28
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be nice to add that explicit return types also reduce the chance of bugs by asserting the return type and it avoids surprising "action at a distance", where changing the body of one function may cause failures inside another function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the suggestion! I've added that point to the description!