Skip to content
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

Refactor: make getter-return be consistent with others #11

Merged
merged 3 commits into from Sep 27, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
39 changes: 20 additions & 19 deletions rslint_core/src/groups/errors/getter_return.rs
@@ -1,5 +1,6 @@
use crate::rule_prelude::*;
use SyntaxKind::*;
use ast::*;

declare_lint! {
/**
Expand Down Expand Up @@ -70,29 +71,29 @@ impl CstRule for GetterReturn {
fn check_node(&self, node: &SyntaxNode, ctx: &mut RuleCtx) -> Option<()> {
match node.kind() {
CALL_EXPR => {
let expr = node.to::<ast::CallExpr>();
let expr = node.to::<CallExpr>();
if expr.callee().map_or(false, |e| {
e.syntax()
.structural_lossy_token_eq(&["Object", ".", "defineProperty"])
}) && expr.arguments()?.args().count() == 3
{
let args: Vec<ast::Expr> = expr.arguments().unwrap().args().collect();
let args: Vec<Expr> = expr.arguments().unwrap().args().collect();
if let Some(obj) = args
.iter()
.nth(2)
.and_then(|expr| expr.syntax().try_to::<ast::ObjectExpr>())
.and_then(|expr| expr.syntax().try_to::<ObjectExpr>())
{
for prop in obj.props() {
if let ast::ObjectProp::LiteralProp(literal_prop) = prop {
if literal_prop.key()?.syntax().text() != "get" {
if let ObjectProp::LiteralProp(literal_prop) = prop {
if literal_prop.key()?.text() != "get" {
continue;
}
match literal_prop.value()? {
ast::Expr::FnExpr(decl) => {
Expr::FnExpr(decl) => {
self.check_stmts(args[1].syntax(), decl.body()?.syntax(), decl.body()?.stmts(), ctx);
},
ast::Expr::ArrowExpr(arrow) => {
if let ast::ExprOrBlock::Block(block) = arrow.body()? {
Expr::ArrowExpr(arrow) => {
if let ExprOrBlock::Block(block) = arrow.body()? {
self.check_stmts(args[1].syntax(), block.syntax(), block.stmts(), ctx);
}
},
Expand All @@ -104,7 +105,7 @@ impl CstRule for GetterReturn {
}
}
GETTER => {
let getter = node.to::<ast::Getter>();
let getter = node.to::<Getter>();
if let Some(body) = getter.body() {
if let Some(key) = getter.key() {
self.check_stmts(key.syntax(), body.syntax(), body.stmts(), ctx);
Expand All @@ -118,33 +119,33 @@ impl CstRule for GetterReturn {
}

impl GetterReturn {
fn check_stmts(&self, key: &SyntaxNode, body: &SyntaxNode, mut stmts: impl Iterator<Item = ast::Stmt>, ctx: &mut RuleCtx) {
fn check_stmts(&self, key: &SyntaxNode, body: &SyntaxNode, mut stmts: impl Iterator<Item = Stmt>, ctx: &mut RuleCtx) {
if !stmts.any(|stmt| self.check_stmt(&stmt)) {
let err = ctx.err(self.name(), format!("Getter properties must always return a value, but `{}` does not.", key.trimmed_text()))
let err = ctx.err(self.name(), format!("getter properties must always return a value, but `{}` does not.", key.trimmed_text()))
.secondary(key.trimmed_range(), "this key is sometimes or always undefined...")
.primary(body.trimmed_range(), "...because this getter does not always return a value");

ctx.add_err(err);
}
}

fn check_stmt(&self, stmt: &ast::Stmt) -> bool {
fn check_stmt(&self, stmt: &Stmt) -> bool {
match stmt {
ast::Stmt::IfStmt(if_stmt) => self.check_if(if_stmt),
ast::Stmt::BlockStmt(block) => block.stmts().any(|stmt| self.check_stmt(&stmt)),
ast::Stmt::ReturnStmt(stmt) => stmt.value().is_some() || self.allow_implicit,
ast::Stmt::SwitchStmt(switch) => {
Stmt::IfStmt(if_stmt) => self.check_if(if_stmt),
Stmt::BlockStmt(block) => block.stmts().any(|stmt| self.check_stmt(&stmt)),
Stmt::ReturnStmt(stmt) => stmt.value().is_some() || self.allow_implicit,
Stmt::SwitchStmt(switch) => {
switch.cases().any(|case| match case {
ast::SwitchCase::CaseClause(clause) => clause.cons().any(|s| self.check_stmt(&s)),
ast::SwitchCase::DefaultClause(clause) => clause.cons().any(|s| self.check_stmt(&s))
SwitchCase::CaseClause(clause) => clause.cons().any(|s| self.check_stmt(&s)),
SwitchCase::DefaultClause(clause) => clause.cons().any(|s| self.check_stmt(&s))
})
}
_ => false
}
}

/// Check if an if statement unconditionally returns from the statement.
fn check_if(&self, stmt: &ast::IfStmt) -> bool {
fn check_if(&self, stmt: &IfStmt) -> bool {
if stmt.alt().is_none() {
return false;
}
Expand Down