Rename end_of_statement to end_of_last_statement (#3775)

This commit is contained in:
Charlie Marsh 2023-03-28 12:31:06 -04:00 committed by GitHub
parent 990b378c4d
commit 22d5b0071d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 39 additions and 32 deletions

View file

@ -1,4 +1,8 @@
use rustpython_parser::ast::{Constant, Expr, ExprKind, Stmt}; use rustpython_parser::ast::{Constant, Expr, ExprKind, Location, Stmt};
use ruff_python_ast::helpers::to_absolute;
use ruff_python_ast::newlines::StrExt;
use ruff_python_ast::source_code::Locator;
/// Return `true` if a function's return statement include at least one /// Return `true` if a function's return statement include at least one
/// non-`None` value. /// non-`None` value.
@ -16,3 +20,32 @@ pub fn result_exists(returns: &[(&Stmt, Option<&Expr>)]) -> bool {
.unwrap_or(false) .unwrap_or(false)
}) })
} }
/// Given a statement, find its "logical end".
///
/// For example: the statement could be following by a trailing semicolon, by an end-of-line
/// comment, or by any number of continuation lines (and then by a comment, and so on).
///
/// This method assumes that the statement is the last statement in its body; specifically, that
/// the statement isn't followed by a semicolon, followed by a multi-line statement.
pub fn end_of_last_statement(stmt: &Stmt, locator: &Locator) -> Location {
let contents = locator.skip(stmt.end_location.unwrap());
// End-of-file, so just return the end of the statement.
if contents.is_empty() {
return stmt.end_location.unwrap();
}
// Otherwise, find the end of the last line that's "part of" the statement.
for (lineno, line) in contents.universal_newlines().enumerate() {
if line.ends_with('\\') {
continue;
}
return to_absolute(
Location::new(lineno + 1, line.chars().count()),
stmt.end_location.unwrap(),
);
}
unreachable!("Expected to find end-of-statement")
}

View file

@ -4,14 +4,15 @@ use rustpython_parser::ast::{Constant, Expr, ExprKind, Location, Stmt, StmtKind}
use ruff_diagnostics::{AlwaysAutofixableViolation, Violation}; use ruff_diagnostics::{AlwaysAutofixableViolation, Violation};
use ruff_diagnostics::{Diagnostic, Edit}; use ruff_diagnostics::{Diagnostic, Edit};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::helpers::elif_else_range;
use ruff_python_ast::helpers::is_const_none; use ruff_python_ast::helpers::is_const_none;
use ruff_python_ast::helpers::{elif_else_range, end_of_statement};
use ruff_python_ast::types::Range; use ruff_python_ast::types::Range;
use ruff_python_ast::visitor::Visitor; use ruff_python_ast::visitor::Visitor;
use ruff_python_ast::whitespace::indentation; use ruff_python_ast::whitespace::indentation;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::registry::{AsRule, Rule}; use crate::registry::{AsRule, Rule};
use crate::rules::flake8_return::helpers::end_of_last_statement;
use super::branch::Branch; use super::branch::Branch;
use super::helpers::result_exists; use super::helpers::result_exists;
@ -222,7 +223,7 @@ fn implicit_return(checker: &mut Checker, stmt: &Stmt) {
content.push_str("return None"); content.push_str("return None");
diagnostic.set_fix(Edit::insertion( diagnostic.set_fix(Edit::insertion(
content, content,
end_of_statement(stmt, checker.locator), end_of_last_statement(stmt, checker.locator),
)); ));
} }
} }
@ -260,7 +261,7 @@ fn implicit_return(checker: &mut Checker, stmt: &Stmt) {
content.push_str("return None"); content.push_str("return None");
diagnostic.set_fix(Edit::insertion( diagnostic.set_fix(Edit::insertion(
content, content,
end_of_statement(stmt, checker.locator), end_of_last_statement(stmt, checker.locator),
)); ));
} }
} }
@ -299,7 +300,7 @@ fn implicit_return(checker: &mut Checker, stmt: &Stmt) {
content.push_str("return None"); content.push_str("return None");
diagnostic.set_fix(Edit::insertion( diagnostic.set_fix(Edit::insertion(
content, content,
end_of_statement(stmt, checker.locator), end_of_last_statement(stmt, checker.locator),
)); ));
} }
} }

View file

@ -13,7 +13,6 @@ use rustpython_parser::{lexer, Mode, StringKind, Tok};
use smallvec::{smallvec, SmallVec}; use smallvec::{smallvec, SmallVec};
use crate::context::Context; use crate::context::Context;
use crate::newlines::StrExt;
use crate::scope::{Binding, BindingKind}; use crate::scope::{Binding, BindingKind};
use crate::source_code::{Generator, Indexer, Locator, Stylist}; use crate::source_code::{Generator, Indexer, Locator, Stylist};
use crate::types::{CallPath, Range}; use crate::types::{CallPath, Range};
@ -1139,32 +1138,6 @@ pub fn first_colon_range(range: Range, locator: &Locator) -> Option<Range> {
range range
} }
/// Given a statement, find its "logical end".
///
/// For example: the statement could be following by a trailing semicolon, by an end-of-line
/// comment, or by any number of continuation lines (and then by a comment, and so on).
pub fn end_of_statement(stmt: &Stmt, locator: &Locator) -> Location {
let contents = locator.skip(stmt.end_location.unwrap());
// End-of-file, so just return the end of the statement.
if contents.is_empty() {
return stmt.end_location.unwrap();
}
// Otherwise, find the end of the last line that's "part of" the statement.
for (lineno, line) in contents.universal_newlines().enumerate() {
if line.ends_with('\\') {
continue;
}
return to_absolute(
Location::new(lineno + 1, line.chars().count()),
stmt.end_location.unwrap(),
);
}
unreachable!("Expected to find end-of-statement")
}
/// Return the `Range` of the first `Elif` or `Else` token in an `If` statement. /// Return the `Range` of the first `Elif` or `Else` token in an `If` statement.
pub fn elif_else_range(stmt: &Stmt, locator: &Locator) -> Option<Range> { pub fn elif_else_range(stmt: &Stmt, locator: &Locator) -> Option<Range> {
let StmtKind::If { body, orelse, .. } = &stmt.node else { let StmtKind::If { body, orelse, .. } = &stmt.node else {