Extract LineIndex independent methods from Locator (#13938)
Some checks are pending
CI / Fuzz the parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / ecosystem (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / cargo test (linux) (push) Blocked by required conditions
CI / cargo test (windows) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (release) (push) Blocked by required conditions
CI / cargo build (msrv) (push) Blocked by required conditions
CI / cargo fuzz (push) Blocked by required conditions
CI / pre-commit (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / benchmarks (push) Blocked by required conditions

This commit is contained in:
Micha Reiser 2024-10-28 08:53:41 +01:00 committed by GitHub
parent f8eb547fb4
commit 9f3a38d408
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
171 changed files with 1348 additions and 1284 deletions

View file

@ -26,11 +26,11 @@ pub(crate) fn bindings(checker: &mut Checker) {
&& !checker
.settings
.dummy_variable_rgx
.is_match(binding.name(checker.locator))
.is_match(binding.name(checker.source()))
{
let mut diagnostic = Diagnostic::new(
pyflakes::rules::UnusedVariable {
name: binding.name(checker.locator).to_string(),
name: binding.name(checker.source()).to_string(),
},
binding.range(),
);

View file

@ -1,10 +1,10 @@
use ruff_python_ast::str::raw_contents_range;
use ruff_text_size::{Ranged, TextRange};
use ruff_python_semantic::all::DunderAllName;
use ruff_python_semantic::{
BindingKind, ContextualizedDefinition, Definition, Export, Member, MemberKind,
};
use ruff_source_file::LineRanges;
use ruff_text_size::{Ranged, TextRange};
use crate::checkers::ast::Checker;
use crate::codes::Rule;

View file

@ -17,7 +17,7 @@ pub(crate) fn unresolved_references(checker: &mut Checker) {
if checker.enabled(Rule::UndefinedLocalWithImportStarUsage) {
checker.diagnostics.push(Diagnostic::new(
pyflakes::rules::UndefinedLocalWithImportStarUsage {
name: reference.name(checker.locator).to_string(),
name: reference.name(checker.source()).to_string(),
},
reference.range(),
));
@ -31,12 +31,12 @@ pub(crate) fn unresolved_references(checker: &mut Checker) {
// Allow __path__.
if checker.path.ends_with("__init__.py") {
if reference.name(checker.locator) == "__path__" {
if reference.name(checker.source()) == "__path__" {
continue;
}
}
let symbol_name = reference.name(checker.locator);
let symbol_name = reference.name(checker.source());
checker.diagnostics.push(Diagnostic::new(
pyflakes::rules::UndefinedName {

View file

@ -59,7 +59,7 @@ use ruff_python_semantic::{
};
use ruff_python_stdlib::builtins::{python_builtins, MAGIC_GLOBALS};
use ruff_python_trivia::CommentRanges;
use ruff_source_file::{Locator, OneIndexed, SourceRow};
use ruff_source_file::{OneIndexed, SourceRow};
use ruff_text_size::{Ranged, TextRange, TextSize};
use crate::checkers::ast::annotation::AnnotationContext;
@ -69,7 +69,7 @@ use crate::noqa::NoqaMapping;
use crate::registry::Rule;
use crate::rules::{flake8_pyi, flake8_type_checking, pyflakes, pyupgrade};
use crate::settings::{flags, LinterSettings};
use crate::{docstrings, noqa};
use crate::{docstrings, noqa, Locator};
mod analyze;
mod annotation;
@ -352,6 +352,10 @@ impl<'a> Checker<'a> {
self.locator
}
pub(crate) const fn source(&self) -> &'a str {
self.locator.contents()
}
/// The [`Stylist`] for the current file, which detects the current line ending, quote, and
/// indentation style.
pub(crate) const fn stylist(&self) -> &'a Stylist<'a> {

View file

@ -2,13 +2,13 @@ use std::path::Path;
use ruff_diagnostics::Diagnostic;
use ruff_python_trivia::CommentRanges;
use ruff_source_file::Locator;
use crate::registry::Rule;
use crate::rules::flake8_builtins::rules::builtin_module_shadowing;
use crate::rules::flake8_no_pep420::rules::implicit_namespace_package;
use crate::rules::pep8_naming::rules::invalid_module_name;
use crate::settings::LinterSettings;
use crate::Locator;
pub(crate) fn check_file_path(
path: &Path,

View file

@ -8,13 +8,13 @@ use ruff_python_ast::{ModModule, PySourceType};
use ruff_python_codegen::Stylist;
use ruff_python_index::Indexer;
use ruff_python_parser::Parsed;
use ruff_source_file::Locator;
use crate::directives::IsortDirectives;
use crate::registry::Rule;
use crate::rules::isort;
use crate::rules::isort::block::{Block, BlockBuilder};
use crate::settings::LinterSettings;
use crate::Locator;
#[allow(clippy::too_many_arguments)]
pub(crate) fn check_imports(

View file

@ -1,11 +1,11 @@
use crate::line_width::IndentWidth;
use ruff_diagnostics::Diagnostic;
use ruff_python_codegen::Stylist;
use ruff_python_index::Indexer;
use ruff_python_parser::{TokenKind, Tokens};
use ruff_source_file::Locator;
use ruff_source_file::LineRanges;
use ruff_text_size::{Ranged, TextRange};
use crate::line_width::IndentWidth;
use crate::registry::{AsRule, Rule};
use crate::rules::pycodestyle::rules::logical_lines::{
extraneous_whitespace, indentation, missing_whitespace, missing_whitespace_after_keyword,
@ -14,6 +14,7 @@ use crate::rules::pycodestyle::rules::logical_lines::{
whitespace_before_comment, whitespace_before_parameters, LogicalLines, TokenFlags,
};
use crate::settings::LinterSettings;
use crate::Locator;
/// Return the amount of indentation, expanding tabs to the next multiple of the settings' tab size.
pub(crate) fn expand_indent(line: &str, indent_width: IndentWidth) -> usize {

View file

@ -7,7 +7,6 @@ use rustc_hash::FxHashSet;
use ruff_diagnostics::{Diagnostic, Edit, Fix};
use ruff_python_trivia::CommentRanges;
use ruff_source_file::Locator;
use ruff_text_size::Ranged;
use crate::fix::edits::delete_comment;
@ -20,6 +19,7 @@ use crate::rules::pygrep_hooks;
use crate::rules::ruff;
use crate::rules::ruff::rules::{UnusedCodes, UnusedNOQA};
use crate::settings::LinterSettings;
use crate::Locator;
#[allow(clippy::too_many_arguments)]
pub(crate) fn check_noqa(
@ -33,13 +33,8 @@ pub(crate) fn check_noqa(
settings: &LinterSettings,
) -> Vec<usize> {
// Identify any codes that are globally exempted (within the current file).
let file_noqa_directives = FileNoqaDirectives::extract(
locator.contents(),
comment_ranges,
&settings.external,
path,
locator,
);
let file_noqa_directives =
FileNoqaDirectives::extract(locator, comment_ranges, &settings.external, path);
let exemption = FileExemption::from(&file_noqa_directives);
// Extract all `noqa` directives.

View file

@ -3,7 +3,7 @@
use ruff_diagnostics::Diagnostic;
use ruff_python_codegen::Stylist;
use ruff_python_index::Indexer;
use ruff_source_file::{Locator, UniversalNewlines};
use ruff_source_file::UniversalNewlines;
use ruff_text_size::TextSize;
use crate::registry::Rule;
@ -14,6 +14,7 @@ use crate::rules::pycodestyle::rules::{
};
use crate::rules::pylint;
use crate::settings::LinterSettings;
use crate::Locator;
pub(crate) fn check_physical_lines(
locator: &Locator,
@ -92,12 +93,12 @@ mod tests {
use ruff_python_codegen::Stylist;
use ruff_python_index::Indexer;
use ruff_python_parser::parse_module;
use ruff_source_file::Locator;
use crate::line_width::LineLength;
use crate::registry::Rule;
use crate::rules::pycodestyle;
use crate::settings::LinterSettings;
use crate::Locator;
use super::check_physical_lines;
@ -106,8 +107,8 @@ mod tests {
let line = "'\u{4e9c}' * 2"; // 7 in UTF-32, 9 in UTF-8.
let locator = Locator::new(line);
let parsed = parse_module(line).unwrap();
let indexer = Indexer::from_tokens(parsed.tokens(), &locator);
let stylist = Stylist::from_tokens(parsed.tokens(), &locator);
let indexer = Indexer::from_tokens(parsed.tokens(), locator.contents());
let stylist = Stylist::from_tokens(parsed.tokens(), locator.contents());
let check_with_max_line_length = |line_length: LineLength| {
check_physical_lines(

View file

@ -2,14 +2,12 @@
use std::path::Path;
use ruff_diagnostics::Diagnostic;
use ruff_notebook::CellOffsets;
use ruff_python_ast::PySourceType;
use ruff_python_codegen::Stylist;
use ruff_diagnostics::Diagnostic;
use ruff_python_index::Indexer;
use ruff_python_parser::Tokens;
use ruff_source_file::Locator;
use ruff_text_size::Ranged;
use crate::directives::TodoComment;
@ -20,6 +18,7 @@ use crate::rules::{
flake8_pyi, flake8_todos, pycodestyle, pygrep_hooks, pylint, pyupgrade, ruff,
};
use crate::settings::LinterSettings;
use crate::Locator;
#[allow(clippy::too_many_arguments)]
pub(crate) fn check_tokens(

View file

@ -4,15 +4,16 @@ use std::iter::Peekable;
use std::str::FromStr;
use bitflags::bitflags;
use ruff_python_parser::{TokenKind, Tokens};
use ruff_python_trivia::CommentRanges;
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
use ruff_python_index::Indexer;
use ruff_source_file::Locator;
use ruff_python_parser::{TokenKind, Tokens};
use ruff_python_trivia::CommentRanges;
use ruff_source_file::LineRanges;
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
use crate::noqa::NoqaMapping;
use crate::settings::LinterSettings;
use crate::Locator;
bitflags! {
#[derive(Debug, Copy, Clone)]
@ -360,24 +361,23 @@ impl TodoDirectiveKind {
#[cfg(test)]
mod tests {
use ruff_python_index::Indexer;
use ruff_python_parser::parse_module;
use ruff_python_trivia::CommentRanges;
use ruff_text_size::{TextLen, TextRange, TextSize};
use ruff_python_index::Indexer;
use ruff_source_file::Locator;
use crate::directives::{
extract_isort_directives, extract_noqa_line_for, TodoDirective, TodoDirectiveKind,
};
use crate::noqa::NoqaMapping;
use crate::Locator;
use super::IsortDirectives;
fn noqa_mappings(contents: &str) -> NoqaMapping {
let parsed = parse_module(contents).unwrap();
let locator = Locator::new(contents);
let indexer = Indexer::from_tokens(parsed.tokens(), &locator);
let indexer = Indexer::from_tokens(parsed.tokens(), locator.contents());
extract_noqa_line_for(parsed.tokens(), &locator, &indexer)
}

View file

@ -4,12 +4,13 @@
use std::iter::FusedIterator;
use std::slice::Iter;
use ruff_python_ast::statement_visitor::{walk_stmt, StatementVisitor};
use ruff_python_ast::{self as ast, Stmt, Suite};
use ruff_python_parser::{Token, TokenKind, Tokens};
use ruff_source_file::UniversalNewlineIterator;
use ruff_text_size::{Ranged, TextSize};
use ruff_python_ast::statement_visitor::{walk_stmt, StatementVisitor};
use ruff_source_file::{Locator, UniversalNewlineIterator};
use crate::Locator;
/// Extract doc lines (standalone comments) from a token sequence.
pub(crate) fn doc_lines_from_tokens(tokens: &Tokens) -> DocLines {

View file

@ -13,9 +13,9 @@ use unicode_normalization::UnicodeNormalization;
use ruff_python_ast::name::UnqualifiedName;
use ruff_python_ast::Stmt;
use ruff_python_codegen::Stylist;
use ruff_source_file::Locator;
use crate::cst::matchers::match_statement;
use crate::Locator;
/// Glue code to make libcst codegen work with ruff's Stylist
pub(crate) trait CodegenStylist<'a>: Codegen<'a> {

View file

@ -13,13 +13,14 @@ use ruff_python_trivia::{
has_leading_content, is_python_whitespace, CommentRanges, PythonWhitespace, SimpleTokenKind,
SimpleTokenizer,
};
use ruff_source_file::{Locator, NewlineWithTrailingNewline, UniversalNewlines};
use ruff_source_file::{LineRanges, NewlineWithTrailingNewline, UniversalNewlines};
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
use crate::cst::matchers::{match_function_def, match_indented_block, match_statement};
use crate::fix::codemods;
use crate::fix::codemods::CodegenStylist;
use crate::line_width::{IndentWidth, LineLength, LineWidthBuilder};
use crate::Locator;
/// Return the `Fix` to use when deleting a `Stmt`.
///
@ -48,9 +49,11 @@ pub(crate) fn delete_stmt(
if let Some(semicolon) = trailing_semicolon(stmt.end(), locator) {
let next = next_stmt_break(semicolon, locator);
Edit::deletion(stmt.start(), next)
} else if has_leading_content(stmt.start(), locator) {
} else if has_leading_content(stmt.start(), locator.contents()) {
Edit::range_deletion(stmt.range())
} else if let Some(start) = indexer.preceded_by_continuations(stmt.start(), locator) {
} else if let Some(start) =
indexer.preceded_by_continuations(stmt.start(), locator.contents())
{
Edit::deletion(start, stmt.end())
} else {
let range = locator.full_lines_range(stmt.range());
@ -595,13 +598,13 @@ mod tests {
use ruff_python_ast::Stmt;
use ruff_python_codegen::Stylist;
use ruff_python_parser::{parse_expression, parse_module};
use ruff_source_file::Locator;
use ruff_text_size::{Ranged, TextRange, TextSize};
use crate::fix::apply_fixes;
use crate::fix::edits::{
add_to_dunder_all, make_redundant_alias, next_stmt_break, trailing_semicolon,
};
use crate::Locator;
/// Parse the given source using [`Mode::Module`] and return the first statement.
fn parse_first_stmt(source: &str) -> Result<Stmt> {
@ -726,7 +729,7 @@ x = 1 \
let locator = Locator::new(raw);
let edits = {
let parsed = parse_expression(raw)?;
let stylist = Stylist::from_tokens(parsed.tokens(), &locator);
let stylist = Stylist::from_tokens(parsed.tokens(), locator.contents());
add_to_dunder_all(names.iter().copied(), parsed.expr(), &stylist)
};
let diag = {

View file

@ -1,15 +1,15 @@
use itertools::Itertools;
use std::collections::BTreeSet;
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
use itertools::Itertools;
use rustc_hash::{FxHashMap, FxHashSet};
use ruff_diagnostics::{Diagnostic, Edit, Fix, IsolationLevel, SourceMap};
use ruff_source_file::Locator;
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
use crate::linter::FixTable;
use crate::registry::{AsRule, Rule};
use crate::settings::types::UnsafeFixes;
use crate::Locator;
pub(crate) mod codemods;
pub(crate) mod edits;
@ -158,13 +158,12 @@ fn cmp_fix(rule1: Rule, rule2: Rule, fix1: &Fix, fix2: &Fix) -> std::cmp::Orderi
#[cfg(test)]
mod tests {
use ruff_text_size::{Ranged, TextSize};
use ruff_diagnostics::{Diagnostic, Edit, Fix, SourceMarker};
use ruff_source_file::Locator;
use ruff_text_size::{Ranged, TextSize};
use crate::fix::{apply_fixes, FixResult};
use crate::rules::pycodestyle::rules::MissingNewlineAtEndOfFile;
use crate::Locator;
#[allow(deprecated)]
fn create_diagnostics(edit: impl IntoIterator<Item = Edit>) -> Vec<Diagnostic> {

View file

@ -1,15 +1,16 @@
//! Insert statements into Python code.
use std::ops::Add;
use ruff_python_ast::Stmt;
use ruff_python_parser::{TokenKind, Tokens};
use ruff_text_size::{Ranged, TextSize};
use ruff_diagnostics::Edit;
use ruff_python_ast::helpers::is_docstring_stmt;
use ruff_python_ast::Stmt;
use ruff_python_codegen::Stylist;
use ruff_python_parser::{TokenKind, Tokens};
use ruff_python_trivia::{textwrap::indent, PythonWhitespace};
use ruff_source_file::{Locator, UniversalNewlineIterator};
use ruff_source_file::{LineRanges, UniversalNewlineIterator};
use ruff_text_size::{Ranged, TextSize};
use crate::Locator;
#[derive(Debug, Clone, PartialEq, Eq)]
pub(super) enum Placement<'a> {
@ -64,7 +65,7 @@ impl<'a> Insertion<'a> {
// Otherwise, advance to the next row.
locator.full_line_end(location)
} else {
locator.contents_start()
locator.bom_start_offset()
};
// Skip over commented lines, with whitespace separation.
@ -319,9 +320,11 @@ mod tests {
use ruff_python_codegen::Stylist;
use ruff_python_parser::parse_module;
use ruff_source_file::{LineEnding, Locator};
use ruff_source_file::LineEnding;
use ruff_text_size::TextSize;
use crate::Locator;
use super::Insertion;
#[test]
@ -329,7 +332,7 @@ mod tests {
fn insert(contents: &str) -> Result<Insertion> {
let parsed = parse_module(contents)?;
let locator = Locator::new(contents);
let stylist = Stylist::from_tokens(parsed.tokens(), &locator);
let stylist = Stylist::from_tokens(parsed.tokens(), locator.contents());
Ok(Insertion::start_of_file(parsed.suite(), &locator, &stylist))
}
@ -440,7 +443,7 @@ x = 1
fn insert(contents: &str, offset: TextSize) -> Insertion {
let parsed = parse_module(contents).unwrap();
let locator = Locator::new(contents);
let stylist = Stylist::from_tokens(parsed.tokens(), &locator);
let stylist = Stylist::from_tokens(parsed.tokens(), locator.contents());
Insertion::start_of_block(offset, &locator, &stylist, parsed.tokens())
}

View file

@ -16,13 +16,13 @@ use ruff_python_semantic::{
ImportedName, MemberNameImport, ModuleNameImport, NameImport, SemanticModel,
};
use ruff_python_trivia::textwrap::indent;
use ruff_source_file::Locator;
use ruff_text_size::{Ranged, TextSize};
use crate::cst::matchers::{match_aliases, match_import_from, match_statement};
use crate::fix;
use crate::fix::codemods::CodegenStylist;
use crate::importer::insertion::Insertion;
use crate::Locator;
mod insertion;

View file

@ -5,6 +5,7 @@
//!
//! [Ruff]: https://github.com/astral-sh/ruff
pub use locator::Locator;
pub use noqa::generate_noqa_edits;
#[cfg(feature = "clap")]
pub use registry::clap_completion::RuleParser;
@ -27,6 +28,7 @@ pub mod fs;
mod importer;
pub mod line_width;
pub mod linter;
mod locator;
pub mod logging;
pub mod message;
mod noqa;

View file

@ -14,7 +14,7 @@ use ruff_python_ast::{ModModule, PySourceType};
use ruff_python_codegen::Stylist;
use ruff_python_index::Indexer;
use ruff_python_parser::{ParseError, Parsed};
use ruff_source_file::{Locator, SourceFileBuilder};
use ruff_source_file::SourceFileBuilder;
use ruff_text_size::Ranged;
use crate::checkers::ast::check_ast;
@ -34,7 +34,7 @@ use crate::rules::ruff::rules::test_rules::{self, TestRule, TEST_RULES};
use crate::settings::types::UnsafeFixes;
use crate::settings::{flags, LinterSettings};
use crate::source_kind::SourceKind;
use crate::{directives, fs};
use crate::{directives, fs, Locator};
pub struct LinterResult {
/// A collection of diagnostic messages generated by the linter.
@ -335,10 +335,10 @@ pub fn add_noqa_to_path(
let locator = Locator::new(source_kind.source_code());
// Detect the current code style (lazily).
let stylist = Stylist::from_tokens(parsed.tokens(), &locator);
let stylist = Stylist::from_tokens(parsed.tokens(), locator.contents());
// Extra indices from the code.
let indexer = Indexer::from_tokens(parsed.tokens(), &locator);
let indexer = Indexer::from_tokens(parsed.tokens(), locator.contents());
// Extract the `# noqa` and `# isort: skip` directives from the source.
let directives = directives::extract_directives(
@ -393,10 +393,10 @@ pub fn lint_only(
let locator = Locator::new(source_kind.source_code());
// Detect the current code style (lazily).
let stylist = Stylist::from_tokens(parsed.tokens(), &locator);
let stylist = Stylist::from_tokens(parsed.tokens(), locator.contents());
// Extra indices from the code.
let indexer = Indexer::from_tokens(parsed.tokens(), &locator);
let indexer = Indexer::from_tokens(parsed.tokens(), locator.contents());
// Extract the `# noqa` and `# isort: skip` directives from the source.
let directives = directives::extract_directives(
@ -495,10 +495,10 @@ pub fn lint_fix<'a>(
let locator = Locator::new(transformed.source_code());
// Detect the current code style (lazily).
let stylist = Stylist::from_tokens(parsed.tokens(), &locator);
let stylist = Stylist::from_tokens(parsed.tokens(), locator.contents());
// Extra indices from the code.
let indexer = Indexer::from_tokens(parsed.tokens(), &locator);
let indexer = Indexer::from_tokens(parsed.tokens(), locator.contents());
// Extract the `# noqa` and `# isort: skip` directives from the source.
let directives = directives::extract_directives(

View file

@ -0,0 +1,224 @@
//! Struct used to efficiently slice source code at (row, column) Locations.
use std::cell::OnceCell;
use ruff_source_file::{LineIndex, LineRanges, OneIndexed, SourceCode, SourceLocation};
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
#[derive(Debug)]
pub struct Locator<'a> {
contents: &'a str,
index: OnceCell<LineIndex>,
}
impl<'a> Locator<'a> {
pub const fn new(contents: &'a str) -> Self {
Self {
contents,
index: OnceCell::new(),
}
}
pub fn with_index(contents: &'a str, index: LineIndex) -> Self {
Self {
contents,
index: OnceCell::from(index),
}
}
#[deprecated(
note = "This is expensive, avoid using outside of the diagnostic phase. Prefer the other `Locator` methods instead."
)]
pub fn compute_line_index(&self, offset: TextSize) -> OneIndexed {
self.to_index().line_index(offset)
}
#[deprecated(
note = "This is expensive, avoid using outside of the diagnostic phase. Prefer the other `Locator` methods instead."
)]
pub fn compute_source_location(&self, offset: TextSize) -> SourceLocation {
self.to_source_code().source_location(offset)
}
pub fn to_index(&self) -> &LineIndex {
self.index
.get_or_init(|| LineIndex::from_source_text(self.contents))
}
pub fn line_index(&self) -> Option<&LineIndex> {
self.index.get()
}
pub fn to_source_code(&self) -> SourceCode {
SourceCode::new(self.contents, self.to_index())
}
/// Take the source code up to the given [`TextSize`].
#[inline]
pub fn up_to(&self, offset: TextSize) -> &'a str {
&self.contents[TextRange::up_to(offset)]
}
/// Take the source code after the given [`TextSize`].
#[inline]
pub fn after(&self, offset: TextSize) -> &'a str {
&self.contents[usize::from(offset)..]
}
/// Finds the closest [`TextSize`] not exceeding the offset for which `is_char_boundary` is
/// `true`.
///
/// Can be replaced with `str::floor_char_boundary` once it's stable.
///
/// ## Examples
///
/// ```
/// # use ruff_text_size::{Ranged, TextRange, TextSize};
/// # use ruff_linter::Locator;
///
/// let locator = Locator::new("Hello");
///
/// assert_eq!(
/// locator.floor_char_boundary(TextSize::from(0)),
/// TextSize::from(0)
/// );
///
/// assert_eq!(
/// locator.floor_char_boundary(TextSize::from(5)),
/// TextSize::from(5)
/// );
///
/// let locator = Locator::new("α");
///
/// assert_eq!(
/// locator.floor_char_boundary(TextSize::from(0)),
/// TextSize::from(0)
/// );
///
/// assert_eq!(
/// locator.floor_char_boundary(TextSize::from(1)),
/// TextSize::from(0)
/// );
///
/// assert_eq!(
/// locator.floor_char_boundary(TextSize::from(2)),
/// TextSize::from(2)
/// );
/// ```
pub fn floor_char_boundary(&self, offset: TextSize) -> TextSize {
if offset >= self.text_len() {
self.text_len()
} else {
// We know that the character boundary is within four bytes.
(0u32..=3u32)
.map(TextSize::from)
.filter_map(|index| offset.checked_sub(index))
.find(|offset| self.contents.is_char_boundary(offset.to_usize()))
.unwrap_or_default()
}
}
/// Take the source code between the given [`TextRange`].
#[inline]
pub fn slice<T: Ranged>(&self, ranged: T) -> &'a str {
&self.contents[ranged.range()]
}
/// Return the underlying source code.
pub const fn contents(&self) -> &'a str {
self.contents
}
/// Return the number of bytes in the source code.
pub const fn len(&self) -> usize {
self.contents.len()
}
pub fn text_len(&self) -> TextSize {
self.contents.text_len()
}
/// Return `true` if the source code is empty.
pub const fn is_empty(&self) -> bool {
self.contents.is_empty()
}
}
// Override the `_str` methods from [`LineRanges`] to extend the lifetime to `'a`.
impl<'a> Locator<'a> {
/// Returns the text of the `offset`'s line.
///
/// See [`LineRanges::full_lines_str`].
pub fn full_line_str(&self, offset: TextSize) -> &'a str {
self.contents.full_line_str(offset)
}
/// Returns the text of the `offset`'s line.
///
/// See [`LineRanges::line_str`].
pub fn line_str(&self, offset: TextSize) -> &'a str {
self.contents.line_str(offset)
}
/// Returns the text of all lines that include `range`.
///
/// See [`LineRanges::lines_str`].
pub fn lines_str(&self, range: TextRange) -> &'a str {
self.contents.lines_str(range)
}
/// Returns the text of all lines that include `range`.
///
/// See [`LineRanges::full_lines_str`].
pub fn full_lines_str(&self, range: TextRange) -> &'a str {
self.contents.full_lines_str(range)
}
}
// Allow calling [`LineRanges`] methods on [`Locator`] directly.
impl LineRanges for Locator<'_> {
#[inline]
fn line_start(&self, offset: TextSize) -> TextSize {
self.contents.line_start(offset)
}
#[inline]
fn bom_start_offset(&self) -> TextSize {
self.contents.bom_start_offset()
}
#[inline]
fn full_line_end(&self, offset: TextSize) -> TextSize {
self.contents.full_line_end(offset)
}
#[inline]
fn line_end(&self, offset: TextSize) -> TextSize {
self.contents.line_end(offset)
}
#[inline]
fn full_line_str(&self, offset: TextSize) -> &str {
self.contents.full_line_str(offset)
}
#[inline]
fn line_str(&self, offset: TextSize) -> &str {
self.contents.line_str(offset)
}
#[inline]
fn contains_line_break(&self, range: TextRange) -> bool {
self.contents.contains_line_break(range)
}
#[inline]
fn lines_str(&self, range: TextRange) -> &str {
self.contents.lines_str(range)
}
#[inline]
fn full_lines_str(&self, range: TextRange) -> &str {
self.contents.full_lines_str(range)
}
}

View file

@ -14,17 +14,17 @@ pub use json_lines::JsonLinesEmitter;
pub use junit::JunitEmitter;
pub use pylint::PylintEmitter;
pub use rdjson::RdjsonEmitter;
pub use sarif::SarifEmitter;
pub use text::TextEmitter;
use ruff_diagnostics::{Diagnostic, DiagnosticKind, Fix};
use ruff_notebook::NotebookIndex;
use ruff_python_parser::ParseError;
use ruff_source_file::{Locator, SourceFile, SourceLocation};
use ruff_source_file::{SourceFile, SourceLocation};
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
pub use sarif::SarifEmitter;
pub use text::TextEmitter;
use crate::logging::DisplayParseErrorType;
use crate::registry::{AsRule, Rule};
use crate::Locator;
mod azure;
mod diff;
@ -310,10 +310,11 @@ mod tests {
use ruff_diagnostics::{Diagnostic, DiagnosticKind, Edit, Fix};
use ruff_notebook::NotebookIndex;
use ruff_python_parser::{parse_unchecked, Mode};
use ruff_source_file::{Locator, OneIndexed, SourceFileBuilder};
use ruff_source_file::{OneIndexed, SourceFileBuilder};
use ruff_text_size::{Ranged, TextRange, TextSize};
use crate::message::{Emitter, EmitterContext, Message};
use crate::Locator;
pub(super) fn create_syntax_error_messages() -> Vec<Message> {
let source = r"from os import

View file

@ -8,16 +8,17 @@ use std::path::Path;
use anyhow::Result;
use itertools::Itertools;
use log::warn;
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
use ruff_diagnostics::{Diagnostic, Edit};
use ruff_python_trivia::{indentation_at_offset, CommentRanges};
use ruff_source_file::{LineEnding, Locator};
use ruff_source_file::{LineEnding, LineRanges};
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
use crate::codes::NoqaCode;
use crate::fs::relativize_path;
use crate::registry::{AsRule, Rule, RuleSet};
use crate::rule_redirects::get_redirect_target;
use crate::Locator;
/// Generates an array of edits that matches the length of `diagnostics`.
/// Each potential edit in the array is paired, in order, with the associated diagnostic.
@ -33,8 +34,7 @@ pub fn generate_noqa_edits(
noqa_line_for: &NoqaMapping,
line_ending: LineEnding,
) -> Vec<Option<Edit>> {
let file_directives =
FileNoqaDirectives::extract(locator.contents(), comment_ranges, external, path, locator);
let file_directives = FileNoqaDirectives::extract(locator, comment_ranges, external, path);
let exemption = FileExemption::from(&file_directives);
let directives = NoqaDirectives::from_commented_ranges(comment_ranges, path, locator);
let comments = find_noqa_comments(diagnostics, locator, &exemption, &directives, noqa_line_for);
@ -352,16 +352,15 @@ impl<'a> FileNoqaDirectives<'a> {
/// Extract the [`FileNoqaDirectives`] for a given Python source file, enumerating any rules
/// that are globally ignored within the file.
pub(crate) fn extract(
contents: &'a str,
locator: &Locator<'a>,
comment_ranges: &CommentRanges,
external: &[String],
path: &Path,
locator: &Locator,
) -> Self {
let mut lines = vec![];
for range in comment_ranges {
match ParsedFileExemption::try_extract(&contents[range]) {
match ParsedFileExemption::try_extract(&locator.contents()[range]) {
Err(err) => {
#[allow(deprecated)]
let line = locator.compute_line_index(range.start());
@ -369,7 +368,7 @@ impl<'a> FileNoqaDirectives<'a> {
warn!("Invalid `# ruff: noqa` directive at {path_display}:{line}: {err}");
}
Ok(Some(exemption)) => {
if indentation_at_offset(range.start(), locator).is_none() {
if indentation_at_offset(range.start(), locator.contents()).is_none() {
#[allow(deprecated)]
let line = locator.compute_line_index(range.start());
let path_display = relativize_path(path);
@ -619,8 +618,7 @@ fn add_noqa_inner(
let mut count = 0;
// Whether the file is exempted from all checks.
let directives =
FileNoqaDirectives::extract(locator.contents(), comment_ranges, external, path, locator);
let directives = FileNoqaDirectives::extract(locator, comment_ranges, external, path);
let exemption = FileExemption::from(&directives);
let directives = NoqaDirectives::from_commented_ranges(comment_ranges, path, locator);
@ -1055,17 +1053,17 @@ mod tests {
use std::path::Path;
use insta::assert_debug_snapshot;
use ruff_text_size::{TextRange, TextSize};
use ruff_diagnostics::{Diagnostic, Edit};
use ruff_python_trivia::CommentRanges;
use ruff_source_file::{LineEnding, Locator};
use ruff_source_file::LineEnding;
use ruff_text_size::{TextRange, TextSize};
use crate::generate_noqa_edits;
use crate::noqa::{add_noqa_inner, Directive, NoqaMapping, ParsedFileExemption};
use crate::rules::pycodestyle::rules::{AmbiguousVariableName, UselessSemicolon};
use crate::rules::pyflakes::rules::UnusedVariable;
use crate::rules::pyupgrade::rules::PrintfStringFormatting;
use crate::{generate_noqa_edits, Locator};
#[test]
fn noqa_all() {

View file

@ -1,10 +1,12 @@
use crate::settings::LinterSettings;
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_trivia::CommentRanges;
use ruff_source_file::{Locator, UniversalNewlineIterator};
use ruff_source_file::{LineRanges, UniversalNewlineIterator};
use ruff_text_size::TextRange;
use crate::settings::LinterSettings;
use crate::Locator;
use super::super::detection::comment_contains_code;
/// ## What it does
@ -53,7 +55,7 @@ pub(crate) fn commented_out_code(
let mut comments = comment_ranges.into_iter().peekable();
// Iterate over all comments in the document.
while let Some(range) = comments.next() {
let line = locator.line(range.start());
let line = locator.line_str(range.start());
if is_script_tag_start(line) {
if skip_script_comments(range, &mut comments, locator) {
@ -176,11 +178,14 @@ fn is_script_tag_start(line: &str) -> bool {
#[cfg(test)]
mod tests {
use crate::rules::eradicate::rules::commented_out_code::skip_script_comments;
use ruff_python_parser::parse_module;
use ruff_python_trivia::CommentRanges;
use ruff_source_file::Locator;
use ruff_source_file::LineRanges;
use ruff_text_size::TextSize;
use crate::rules::eradicate::rules::commented_out_code::skip_script_comments;
use crate::Locator;
#[test]
fn script_comment() {
let code = r#"

View file

@ -6,10 +6,10 @@ use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::str::raw_contents;
use ruff_python_ast::{self as ast, Expr, Operator};
use ruff_source_file::Locator;
use ruff_text_size::Ranged;
use crate::checkers::ast::Checker;
use crate::Locator;
static SQL_REGEX: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(r"(?i)\b(select\s+.*\s+from\s|delete\s+from\s|(insert|replace)\s+.*\s+values\s|update\s+.*\s+set\s)")

View file

@ -1,9 +1,10 @@
use ruff_notebook::CellOffsets;
use ruff_python_semantic::SemanticModel;
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
use ruff_source_file::Locator;
use ruff_text_size::{Ranged, TextRange};
use crate::Locator;
/// Return `true` if the statement containing the current expression is the last
/// top-level expression in the cell. This assumes that the source is a Jupyter
/// Notebook.

View file

@ -1,11 +1,12 @@
use crate::fix::edits::pad;
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::{self as ast, Expr};
use ruff_python_stdlib::identifiers::{is_identifier, is_mangled_private};
use ruff_source_file::LineRanges;
use ruff_text_size::Ranged;
use crate::checkers::ast::Checker;
use crate::fix::edits::pad;
/// ## What it does
/// Checks for uses of `getattr` that take a constant attribute value as an

View file

@ -9,10 +9,11 @@ use ruff_python_semantic::analyze::function_type::is_stub;
use ruff_python_semantic::analyze::typing::{is_immutable_annotation, is_mutable_expr};
use ruff_python_semantic::SemanticModel;
use ruff_python_trivia::{indentation_at_offset, textwrap};
use ruff_source_file::Locator;
use ruff_source_file::LineRanges;
use ruff_text_size::Ranged;
use crate::checkers::ast::Checker;
use crate::Locator;
/// ## What it does
/// Checks for uses of mutable objects as function argument defaults.
@ -145,7 +146,7 @@ fn move_initialization(
// Avoid attempting to fix single-line functions.
let statement = body.peek()?;
if indexer.preceded_by_multi_statement_line(statement, locator) {
if indexer.preceded_by_multi_statement_line(statement, locator.contents()) {
return None;
}
@ -170,7 +171,7 @@ fn move_initialization(
content.push_str(stylist.line_ending().as_str());
// Determine the indentation depth of the function body.
let indentation = indentation_at_offset(statement.start(), locator)?;
let indentation = indentation_at_offset(statement.start(), locator.contents())?;
// Indent the edit to match the body indentation.
let mut content = textwrap::indent(&content, indentation).to_string();
@ -186,7 +187,7 @@ fn move_initialization(
if let Some(next) = body.peek() {
// If there's a second statement, insert _before_ it, but ensure this isn't a
// multi-statement line.
if indexer.in_multi_statement_line(statement, locator) {
if indexer.in_multi_statement_line(statement, locator.contents()) {
continue;
}
pos = locator.line_start(next.start());

View file

@ -3,9 +3,10 @@ use ruff_diagnostics::{Diagnostic, Edit, Fix};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_index::Indexer;
use ruff_python_parser::{TokenKind, Tokens};
use ruff_source_file::Locator;
use ruff_text_size::{Ranged, TextRange};
use crate::Locator;
/// Simplified token type.
#[derive(Copy, Clone, PartialEq, Eq)]
enum TokenType {

View file

@ -14,13 +14,13 @@ use ruff_diagnostics::{Edit, Fix};
use ruff_python_ast::{self as ast, Expr};
use ruff_python_codegen::Stylist;
use ruff_python_semantic::SemanticModel;
use ruff_source_file::Locator;
use ruff_text_size::{Ranged, TextRange};
use crate::cst::helpers::{negate, space};
use crate::fix::codemods::CodegenStylist;
use crate::fix::edits::pad;
use crate::rules::flake8_comprehensions::rules::ObjectType;
use crate::Locator;
use crate::{
checkers::ast::Checker,
cst::matchers::{

View file

@ -1,9 +1,9 @@
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_source_file::Locator;
use ruff_text_size::{TextRange, TextSize};
use crate::settings::LinterSettings;
use crate::Locator;
/// ## What it does
/// Checks for the absence of copyright notices within Python files.

View file

@ -1,14 +1,14 @@
use ruff_python_ast::{self as ast, Arguments, Expr, Stmt};
use ruff_source_file::Locator;
use ruff_text_size::Ranged;
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::whitespace;
use ruff_python_ast::{self as ast, Arguments, Expr, Stmt};
use ruff_python_codegen::Stylist;
use ruff_source_file::LineRanges;
use ruff_text_size::Ranged;
use crate::checkers::ast::Checker;
use crate::registry::Rule;
use crate::Locator;
/// ## What it does
/// Checks for the use of string literals in exception constructors.
@ -190,7 +190,7 @@ pub(crate) fn string_in_exception(checker: &mut Checker, stmt: &Stmt, exc: &Expr
let mut diagnostic =
Diagnostic::new(RawStringInException, first.range());
if let Some(indentation) =
whitespace::indentation(checker.locator(), stmt)
whitespace::indentation(checker.source(), stmt)
{
diagnostic.set_fix(generate_fix(
stmt,
@ -208,8 +208,7 @@ pub(crate) fn string_in_exception(checker: &mut Checker, stmt: &Stmt, exc: &Expr
Expr::FString(_) => {
if checker.enabled(Rule::FStringInException) {
let mut diagnostic = Diagnostic::new(FStringInException, first.range());
if let Some(indentation) = whitespace::indentation(checker.locator(), stmt)
{
if let Some(indentation) = whitespace::indentation(checker.source(), stmt) {
diagnostic.set_fix(generate_fix(
stmt,
first,
@ -231,7 +230,7 @@ pub(crate) fn string_in_exception(checker: &mut Checker, stmt: &Stmt, exc: &Expr
let mut diagnostic =
Diagnostic::new(DotFormatInException, first.range());
if let Some(indentation) =
whitespace::indentation(checker.locator(), stmt)
whitespace::indentation(checker.source(), stmt)
{
diagnostic.set_fix(generate_fix(
stmt,

View file

@ -1,17 +1,18 @@
use std::path::Path;
use crate::codes::Rule;
use crate::comments::shebang::ShebangDirective;
use crate::settings::LinterSettings;
use ruff_diagnostics::Diagnostic;
use ruff_python_trivia::CommentRanges;
use ruff_source_file::Locator;
pub(crate) use shebang_leading_whitespace::*;
pub(crate) use shebang_missing_executable_file::*;
pub(crate) use shebang_missing_python::*;
pub(crate) use shebang_not_executable::*;
pub(crate) use shebang_not_first_line::*;
use crate::codes::Rule;
use crate::comments::shebang::ShebangDirective;
use crate::settings::LinterSettings;
use crate::Locator;
mod shebang_leading_whitespace;
mod shebang_missing_executable_file;
mod shebang_missing_python;

View file

@ -1,9 +1,9 @@
use ruff_text_size::{TextRange, TextSize};
use crate::Locator;
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_trivia::is_python_whitespace;
use ruff_source_file::Locator;
/// ## What it does
/// Checks for whitespace before a shebang directive.

View file

@ -1,9 +1,9 @@
use ruff_text_size::{TextRange, TextSize};
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_trivia::is_python_whitespace;
use ruff_source_file::Locator;
use ruff_text_size::{TextRange, TextSize};
use crate::Locator;
/// ## What it does
/// Checks for a shebang directive that is not at the beginning of the file.

View file

@ -1,11 +1,12 @@
use ruff_python_ast::{self as ast, Expr, Operator};
use crate::settings::LinterSettings;
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_source_file::Locator;
use ruff_python_ast::{self as ast, Expr, Operator};
use ruff_source_file::LineRanges;
use ruff_text_size::Ranged;
use crate::settings::LinterSettings;
use crate::Locator;
/// ## What it does
/// Checks for string literals that are explicitly concatenated (using the
/// `+` operator).

View file

@ -7,10 +7,11 @@ use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::str::{leading_quote, trailing_quote};
use ruff_python_index::Indexer;
use ruff_python_parser::{TokenKind, Tokens};
use ruff_source_file::Locator;
use ruff_source_file::LineRanges;
use ruff_text_size::{Ranged, TextRange};
use crate::settings::LinterSettings;
use crate::Locator;
/// ## What it does
/// Checks for implicitly concatenated strings on a single line.

View file

@ -65,7 +65,7 @@ pub(crate) fn unconventional_import_alias(
let qualified_name = import.qualified_name().to_string();
let expected_alias = conventions.get(qualified_name.as_str())?;
let name = binding.name(checker.locator());
let name = binding.name(checker.source());
if name == expected_alias {
return None;
}

View file

@ -4,11 +4,11 @@ use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::PySourceType;
use ruff_python_trivia::CommentRanges;
use ruff_source_file::Locator;
use ruff_text_size::{TextRange, TextSize};
use crate::comments::shebang::ShebangDirective;
use crate::fs;
use crate::Locator;
/// ## What it does
/// Checks for packages that are missing an `__init__.py` file.

View file

@ -105,7 +105,7 @@ pub(crate) fn unnecessary_placeholder(checker: &mut Checker, body: &[Stmt]) {
};
let mut diagnostic = Diagnostic::new(UnnecessaryPlaceholder { kind }, stmt.range());
let edit = if let Some(index) = trailing_comment_start_offset(stmt, checker.locator()) {
let edit = if let Some(index) = trailing_comment_start_offset(stmt, checker.source()) {
Edit::range_deletion(stmt.range().add_end(index))
} else {
fix::edits::delete_stmt(stmt, None, checker.locator(), checker.indexer())

View file

@ -1,12 +1,11 @@
use ruff_python_ast::{self as ast, Expr};
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::{self as ast, Expr};
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
use ruff_source_file::Locator;
use ruff_text_size::{Ranged, TextSize};
use crate::checkers::ast::Checker;
use crate::Locator;
/// ## What it does
/// Checks for unnecessary dictionary unpacking operators (`**`).

View file

@ -1,11 +1,11 @@
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::{self as ast, comparable::ComparableExpr};
use ruff_source_file::Locator;
use ruff_text_size::{Ranged, TextSize};
use crate::checkers::ast::Checker;
use crate::fix::snippet::SourceCodeSnippet;
use crate::Locator;
/// ## What it does
/// Checks for redundant `Final[Literal[...]]` annotations.

View file

@ -5,13 +5,13 @@ use ruff_python_ast::{
self as ast, Expr, Operator, ParameterWithDefault, Parameters, Stmt, UnaryOp,
};
use ruff_python_semantic::{analyze::class::is_enumeration, ScopeKind, SemanticModel};
use ruff_source_file::Locator;
use ruff_text_size::Ranged;
use crate::checkers::ast::Checker;
use crate::importer::ImportRequest;
use crate::rules::flake8_pyi::rules::TypingModule;
use crate::settings::types::PythonVersion;
use crate::Locator;
/// ## What it does
/// Checks for typed function arguments in stubs with complex default values.

View file

@ -1,10 +1,12 @@
use regex::Regex;
use ruff_python_trivia::CommentRanges;
use ruff_source_file::Locator;
use std::sync::LazyLock;
use regex::Regex;
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_trivia::CommentRanges;
use crate::Locator;
/// ## What it does
/// Checks for the use of type comments (e.g., `x = 1 # type: int`) in stub

View file

@ -73,7 +73,7 @@ pub(crate) fn unaliased_collections_abc_set_import(
return None;
}
let name = binding.name(checker.locator());
let name = binding.name(checker.source());
if name == "AbstractSet" {
return None;
}

View file

@ -17,7 +17,7 @@ use ruff_python_ast::{
};
use ruff_python_ast::{visitor, whitespace};
use ruff_python_codegen::Stylist;
use ruff_source_file::Locator;
use ruff_source_file::LineRanges;
use ruff_text_size::Ranged;
use crate::checkers::ast::Checker;
@ -26,6 +26,7 @@ use crate::cst::matchers::match_indented_block;
use crate::cst::matchers::match_module;
use crate::fix::codemods::CodegenStylist;
use crate::importer::ImportRequest;
use crate::Locator;
use super::unittest_assert::UnittestAssert;
@ -386,7 +387,7 @@ pub(crate) fn unittest_raises_assertion(
);
if !checker
.comment_ranges()
.has_comments(call, checker.locator())
.has_comments(call, checker.source())
{
if let Some(args) = to_pytest_raises_args(checker, attr.as_str(), &call.arguments) {
diagnostic.try_set_fix(|| {
@ -622,11 +623,11 @@ fn parenthesize<'a>(expression: &Expression<'a>, parent: &Expression<'a>) -> Exp
/// `assert a == "hello"` and `assert b == "world"`.
fn fix_composite_condition(stmt: &Stmt, locator: &Locator, stylist: &Stylist) -> Result<Edit> {
// Infer the indentation of the outer block.
let outer_indent =
whitespace::indentation(locator, stmt).context("Unable to fix multiline statement")?;
let outer_indent = whitespace::indentation(locator.contents(), stmt)
.context("Unable to fix multiline statement")?;
// Extract the module text.
let contents = locator.lines(stmt.range());
let contents = locator.lines_str(stmt.range());
// If the block is indented, "embed" it in a function definition, to preserve
// indentation while retaining valid source code. (We'll strip the prefix later
@ -747,7 +748,7 @@ pub(crate) fn composite_condition(
&& !checker.comment_ranges().intersects(stmt.range())
&& !checker
.indexer()
.in_multi_statement_line(stmt, checker.locator())
.in_multi_statement_line(stmt, checker.source())
{
diagnostic.try_set_fix(|| {
fix_composite_condition(stmt, checker.locator(), checker.stylist())

View file

@ -9,6 +9,7 @@ use ruff_python_ast::Decorator;
use ruff_python_ast::{self as ast, Expr, Parameters, Stmt};
use ruff_python_semantic::analyze::visibility::is_abstract;
use ruff_python_semantic::SemanticModel;
use ruff_source_file::LineRanges;
use ruff_text_size::Ranged;
use ruff_text_size::{TextLen, TextRange};

View file

@ -1,16 +1,15 @@
use flake8_quotes::helpers::{contains_escaped_quote, raw_contents, unescape_string};
use flake8_quotes::settings::Quote;
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::visitor::{walk_f_string, Visitor};
use ruff_python_ast::{self as ast, AnyStringFlags, StringFlags, StringLike};
use ruff_source_file::Locator;
use ruff_text_size::{Ranged, TextRange, TextSize};
use crate::checkers::ast::Checker;
use crate::rules::flake8_quotes;
use crate::settings::LinterSettings;
use flake8_quotes::helpers::{contains_escaped_quote, raw_contents, unescape_string};
use flake8_quotes::settings::Quote;
use crate::Locator;
/// ## What it does
/// Checks for strings that include escaped quotes, and suggests changing

View file

@ -1,11 +1,11 @@
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix, FixAvailability, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::StringLike;
use ruff_source_file::Locator;
use ruff_text_size::{Ranged, TextRange};
use crate::checkers::ast::Checker;
use crate::registry::Rule;
use crate::Locator;
use super::super::settings::Quote;

View file

@ -1,10 +1,10 @@
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::{self as ast, AnyStringFlags, StringFlags, StringLike};
use ruff_source_file::Locator;
use ruff_text_size::{Ranged, TextRange};
use crate::checkers::ast::Checker;
use crate::Locator;
use super::super::helpers::{contains_escaped_quote, raw_contents, unescape_string};

View file

@ -1,8 +1,9 @@
use ruff_python_ast as ast;
use ruff_python_ast::Stmt;
use ruff_source_file::UniversalNewlines;
use ruff_text_size::{Ranged, TextSize};
use ruff_source_file::{Locator, UniversalNewlines};
use crate::Locator;
/// Return `true` if a function's return statement include at least one
/// non-`None` value.

View file

@ -15,7 +15,7 @@ use ruff_python_index::Indexer;
use ruff_python_semantic::analyze::visibility::is_property;
use ruff_python_semantic::SemanticModel;
use ruff_python_trivia::{is_python_whitespace, SimpleTokenKind, SimpleTokenizer};
use ruff_source_file::Locator;
use ruff_source_file::LineRanges;
use ruff_text_size::{Ranged, TextRange, TextSize};
use crate::checkers::ast::Checker;
@ -23,6 +23,7 @@ use crate::fix::edits;
use crate::fix::edits::adjust_indentation;
use crate::registry::{AsRule, Rule};
use crate::rules::flake8_return::helpers::end_of_last_statement;
use crate::Locator;
use super::super::branch::Branch;
use super::super::helpers::result_exists;
@ -453,7 +454,7 @@ fn is_noreturn_func(func: &Expr, semantic: &SemanticModel) -> bool {
fn add_return_none(checker: &mut Checker, stmt: &Stmt, range: TextRange) {
let mut diagnostic = Diagnostic::new(ImplicitReturn, range);
if let Some(indent) = indentation(checker.locator(), stmt) {
if let Some(indent) = indentation(checker.source(), stmt) {
let mut content = String::new();
content.push_str(checker.stylist().line_ending().as_str());
content.push_str(indent);
@ -851,14 +852,14 @@ fn remove_else(
};
// get the indentation of the `else`, since that is the indent level we want to end with
let Some(desired_indentation) = indentation(locator, elif_else) else {
let Some(desired_indentation) = indentation(locator.contents(), elif_else) else {
return Err(anyhow::anyhow!("Compound statement cannot be inlined"));
};
// If the statement is on the same line as the `else`, just remove the `else: `.
// Ex) `else: return True` -> `return True`
if let Some(first) = elif_else.body.first() {
if indexer.preceded_by_multi_statement_line(first, locator) {
if indexer.preceded_by_multi_statement_line(first, locator.contents()) {
return Ok(Fix::safe_edit(Edit::deletion(
elif_else.start(),
first.start(),

View file

@ -536,7 +536,7 @@ pub(crate) fn compare_with_tuple(checker: &mut Checker, expr: &Expr) {
// Avoid removing comments.
if checker
.comment_ranges()
.has_comments(expr, checker.locator())
.has_comments(expr, checker.source())
{
continue;
}

View file

@ -11,7 +11,7 @@ use ruff_python_ast::{self as ast, whitespace, ElifElseClause, Expr, Stmt};
use ruff_python_codegen::Stylist;
use ruff_python_semantic::analyze::typing::{is_sys_version_block, is_type_checking_block};
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
use ruff_source_file::Locator;
use ruff_source_file::LineRanges;
use ruff_text_size::{Ranged, TextRange};
use crate::checkers::ast::Checker;
@ -19,6 +19,7 @@ use crate::cst::helpers::space;
use crate::cst::matchers::{match_function_def, match_if, match_indented_block, match_statement};
use crate::fix::codemods::CodegenStylist;
use crate::fix::edits::fits;
use crate::Locator;
/// ## What it does
/// Checks for nested `if` statements that can be collapsed into a single `if`
@ -292,12 +293,12 @@ pub(super) fn collapse_nested_if(
nested_if: NestedIf,
) -> Result<Edit> {
// Infer the indentation of the outer block.
let Some(outer_indent) = whitespace::indentation(locator, &nested_if) else {
let Some(outer_indent) = whitespace::indentation(locator.contents(), &nested_if) else {
bail!("Unable to fix multiline statement");
};
// Extract the module text.
let contents = locator.lines(nested_if.range());
let contents = locator.lines_str(nested_if.range());
// If this is an `elif`, we have to remove the `elif` keyword for now. (We'll
// restore the `el` later on.)

View file

@ -5,11 +5,12 @@ use ruff_diagnostics::Edit;
use ruff_python_ast as ast;
use ruff_python_ast::whitespace;
use ruff_python_codegen::Stylist;
use ruff_source_file::Locator;
use ruff_source_file::LineRanges;
use ruff_text_size::Ranged;
use crate::cst::matchers::{match_function_def, match_indented_block, match_statement, match_with};
use crate::fix::codemods::CodegenStylist;
use crate::Locator;
/// (SIM117) Convert `with a: with b:` to `with a, b:`.
pub(crate) fn fix_multiple_with_statements(
@ -18,12 +19,12 @@ pub(crate) fn fix_multiple_with_statements(
with_stmt: &ast::StmtWith,
) -> Result<Edit> {
// Infer the indentation of the outer block.
let Some(outer_indent) = whitespace::indentation(locator, with_stmt) else {
let Some(outer_indent) = whitespace::indentation(locator.contents(), with_stmt) else {
bail!("Unable to fix multiline statement");
};
// Extract the module text.
let contents = locator.lines(with_stmt.range());
let contents = locator.lines_str(with_stmt.range());
// If the block is indented, "embed" it in a function definition, to preserve
// indentation while retaining valid source code. (We'll strip the prefix later

View file

@ -211,7 +211,7 @@ pub(crate) fn if_else_block_instead_of_dict_get(checker: &mut Checker, stmt_if:
);
if !checker
.comment_ranges()
.has_comments(stmt_if, checker.locator())
.has_comments(stmt_if, checker.source())
{
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
contents,
@ -300,7 +300,7 @@ pub(crate) fn if_exp_instead_of_dict_get(
);
if !checker
.comment_ranges()
.has_comments(expr, checker.locator())
.has_comments(expr, checker.source())
{
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
contents,

View file

@ -227,7 +227,7 @@ pub(crate) fn if_else_block_instead_of_if_exp(checker: &mut Checker, stmt_if: &a
);
if !checker
.comment_ranges()
.has_comments(stmt_if, checker.locator())
.has_comments(stmt_if, checker.source())
{
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
contents,

View file

@ -9,10 +9,11 @@ use ruff_python_ast::parenthesize::parenthesized_range;
use ruff_python_ast::stmt_if::{if_elif_branches, IfElifBranch};
use ruff_python_ast::{self as ast, Expr};
use ruff_python_trivia::{CommentRanges, SimpleTokenKind, SimpleTokenizer};
use ruff_source_file::Locator;
use ruff_source_file::LineRanges;
use ruff_text_size::{Ranged, TextRange};
use crate::checkers::ast::Checker;
use crate::Locator;
/// ## What it does
/// Checks for `if` branches with identical arm bodies.

View file

@ -200,7 +200,7 @@ pub(crate) fn needless_bool(checker: &mut Checker, stmt: &Stmt) {
// Generate the replacement condition.
let condition = if checker
.comment_ranges()
.has_comments(&range, checker.locator())
.has_comments(&range, checker.source())
{
None
} else {

View file

@ -7,6 +7,7 @@ use ruff_python_ast::{
self as ast, Arguments, CmpOp, Comprehension, Expr, ExprContext, Stmt, UnaryOp,
};
use ruff_python_codegen::Generator;
use ruff_source_file::LineRanges;
use ruff_text_size::{Ranged, TextRange};
use crate::checkers::ast::Checker;

View file

@ -3,6 +3,7 @@ use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::helpers;
use ruff_python_ast::name::UnqualifiedName;
use ruff_python_ast::{self as ast, ExceptHandler, Stmt};
use ruff_source_file::LineRanges;
use ruff_text_size::Ranged;
use ruff_text_size::{TextLen, TextRange};
@ -127,7 +128,7 @@ pub(crate) fn suppressible_exception(
);
if !checker
.comment_ranges()
.has_comments(stmt, checker.locator())
.has_comments(stmt, checker.source())
{
diagnostic.try_set_fix(|| {
// let range = statement_range(stmt, checker.locator(), checker.indexer());

View file

@ -8,7 +8,6 @@ use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::{self as ast, CmpOp, Expr, UnaryOp};
use ruff_python_codegen::Stylist;
use ruff_python_stdlib::str::{self};
use ruff_source_file::Locator;
use ruff_text_size::Ranged;
use crate::checkers::ast::Checker;
@ -16,6 +15,7 @@ use crate::cst::helpers::or_space;
use crate::cst::matchers::{match_comparison, transform_expression};
use crate::fix::edits::pad;
use crate::fix::snippet::SourceCodeSnippet;
use crate::Locator;
/// ## What it does
/// Checks for conditions that position a constant on the left-hand side of the

View file

@ -1,13 +1,14 @@
use regex::RegexSet;
use ruff_python_trivia::CommentRanges;
use ruff_source_file::Locator;
use ruff_text_size::{TextLen, TextRange, TextSize};
use std::sync::LazyLock;
use regex::RegexSet;
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_trivia::CommentRanges;
use ruff_text_size::{TextLen, TextRange, TextSize};
use crate::directives::{TodoComment, TodoDirective, TodoDirectiveKind};
use crate::Locator;
/// ## What it does
/// Checks that a TODO comment is labelled with "TODO".

View file

@ -1,8 +1,9 @@
use ruff_python_ast::{self as ast, Stmt};
use ruff_python_parser::Tokens;
use ruff_source_file::LineRanges;
use ruff_text_size::{Ranged, TextRange};
use ruff_source_file::Locator;
use crate::Locator;
use super::comments::Comment;
use super::helpers::trailing_comma;

View file

@ -4,11 +4,11 @@ use std::slice;
use ruff_notebook::CellOffsets;
use ruff_python_ast::statement_visitor::StatementVisitor;
use ruff_python_ast::{self as ast, ElifElseClause, ExceptHandler, MatchCase, Stmt};
use ruff_source_file::Locator;
use ruff_text_size::{Ranged, TextRange, TextSize};
use crate::directives::IsortDirectives;
use crate::rules::isort::helpers;
use crate::Locator;
/// A block of imports within a Python module.
#[derive(Debug, Default)]

View file

@ -1,9 +1,10 @@
use std::borrow::Cow;
use ruff_python_trivia::CommentRanges;
use ruff_source_file::Locator;
use ruff_text_size::{Ranged, TextRange};
use crate::Locator;
#[derive(Debug)]
pub(crate) struct Comment<'a> {
pub(crate) value: Cow<'a, str>,

View file

@ -1,10 +1,11 @@
use ruff_python_ast::Stmt;
use ruff_python_parser::{TokenKind, Tokens};
use ruff_python_trivia::PythonWhitespace;
use ruff_source_file::{Locator, UniversalNewlines};
use ruff_source_file::UniversalNewlines;
use ruff_text_size::Ranged;
use crate::rules::isort::types::TrailingComma;
use crate::Locator;
/// Return `true` if a `Stmt::ImportFrom` statement ends with a magic
/// trailing comma.

View file

@ -13,13 +13,13 @@ use order::order_imports;
use ruff_python_ast::PySourceType;
use ruff_python_codegen::Stylist;
use ruff_python_parser::Tokens;
use ruff_source_file::Locator;
use settings::Settings;
use types::EitherImport::{Import, ImportFrom};
use types::{AliasData, ImportBlock, TrailingComma};
use crate::line_width::{LineLength, LineWidthBuilder};
use crate::settings::types::PythonVersion;
use crate::Locator;
mod annotate;
pub(crate) mod block;
@ -282,11 +282,12 @@ mod tests {
use std::path::Path;
use anyhow::Result;
use ruff_python_semantic::{MemberNameImport, ModuleNameImport, NameImport};
use ruff_text_size::Ranged;
use rustc_hash::{FxHashMap, FxHashSet};
use test_case::test_case;
use ruff_python_semantic::{MemberNameImport, ModuleNameImport, NameImport};
use ruff_text_size::Ranged;
use crate::assert_messages;
use crate::registry::Rule;
use crate::rules::isort::categorize::{ImportSection, KnownModules};

View file

@ -5,12 +5,11 @@ use ruff_python_ast::{self as ast, ModModule, PySourceType, Stmt};
use ruff_python_codegen::Stylist;
use ruff_python_parser::Parsed;
use ruff_python_semantic::{FutureImport, NameImport};
use ruff_source_file::Locator;
use ruff_text_size::{TextRange, TextSize};
use crate::importer::Importer;
use crate::settings::LinterSettings;
use crate::Locator;
/// ## What it does
/// Adds any required imports, as specified by the user, to the top of the

View file

@ -10,12 +10,12 @@ use ruff_python_codegen::Stylist;
use ruff_python_index::Indexer;
use ruff_python_parser::Tokens;
use ruff_python_trivia::{leading_indentation, textwrap::indent, PythonWhitespace};
use ruff_source_file::{Locator, UniversalNewlines};
use ruff_source_file::{LineRanges, UniversalNewlines};
use ruff_text_size::{Ranged, TextRange};
use crate::line_width::LineWidthBuilder;
use crate::settings::LinterSettings;
use crate::Locator;
use super::super::block::Block;
use super::super::{comments, format_imports};
@ -98,8 +98,9 @@ pub(crate) fn organize_imports(
// Special-cases: there's leading or trailing content in the import block. These
// are too hard to get right, and relatively rare, so flag but don't fix.
if indexer.preceded_by_multi_statement_line(block.imports.first().unwrap(), locator)
|| indexer.followed_by_multi_statement_line(block.imports.last().unwrap(), locator)
if indexer.preceded_by_multi_statement_line(block.imports.first().unwrap(), locator.contents())
|| indexer
.followed_by_multi_statement_line(block.imports.last().unwrap(), locator.contents())
{
return Some(Diagnostic::new(UnsortedImports, range));
}
@ -114,7 +115,7 @@ pub(crate) fn organize_imports(
let trailing_line_end = if block.trailer.is_none() {
locator.full_line_end(range.end())
} else {
trailing_lines_end(block.imports.last().unwrap(), locator)
trailing_lines_end(block.imports.last().unwrap(), locator.contents())
};
// Generate the sorted import block.

View file

@ -4,11 +4,11 @@ use ruff_python_ast::helpers::is_const_true;
use ruff_python_ast::parenthesize::parenthesized_range;
use ruff_python_ast::{self as ast, Keyword, Stmt};
use ruff_python_trivia::CommentRanges;
use ruff_source_file::Locator;
use ruff_text_size::Ranged;
use crate::checkers::ast::Checker;
use crate::fix::edits::{remove_argument, Parentheses};
use crate::Locator;
/// ## What it does
/// Checks for `inplace=True` usages in `pandas` function and method

View file

@ -1,9 +1,9 @@
use ruff_python_ast::{self as ast, ExceptHandler, Expr, Stmt};
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::identifier::except;
use ruff_source_file::Locator;
use ruff_python_ast::{self as ast, ExceptHandler, Expr, Stmt};
use crate::Locator;
/// ## What it does
/// Checks for bare `except` catches in `try`-`except` statements.

View file

@ -1,28 +1,30 @@
use itertools::Itertools;
use ruff_notebook::CellOffsets;
use ruff_python_parser::TokenIterWithContext;
use ruff_python_parser::Tokens;
use std::cmp::Ordering;
use std::iter::Peekable;
use std::num::NonZeroU32;
use std::slice::Iter;
use itertools::Itertools;
use ruff_diagnostics::AlwaysFixableViolation;
use ruff_diagnostics::Diagnostic;
use ruff_diagnostics::Edit;
use ruff_diagnostics::Fix;
use ruff_macros::{derive_message_formats, violation};
use ruff_notebook::CellOffsets;
use ruff_python_ast::PySourceType;
use ruff_python_codegen::Stylist;
use ruff_python_parser::TokenIterWithContext;
use ruff_python_parser::TokenKind;
use ruff_python_parser::Tokens;
use ruff_python_trivia::PythonWhitespace;
use ruff_source_file::{Locator, UniversalNewlines};
use ruff_source_file::{LineRanges, UniversalNewlines};
use ruff_text_size::TextRange;
use ruff_text_size::TextSize;
use crate::checkers::logical_lines::expand_indent;
use crate::line_width::IndentWidth;
use crate::rules::pycodestyle::helpers::is_non_logical_token;
use crate::Locator;
/// Number of blank lines around top level classes and functions.
const BLANK_LINES_TOP_LEVEL: u32 = 2;

View file

@ -1,13 +1,13 @@
use ruff_notebook::CellOffsets;
use ruff_python_ast::PySourceType;
use ruff_python_parser::{TokenIterWithContext, TokenKind, Tokens};
use ruff_text_size::{Ranged, TextSize};
use ruff_diagnostics::{AlwaysFixableViolation, Violation};
use ruff_diagnostics::{Diagnostic, Edit, Fix};
use ruff_macros::{derive_message_formats, violation};
use ruff_notebook::CellOffsets;
use ruff_python_ast::PySourceType;
use ruff_python_index::Indexer;
use ruff_source_file::Locator;
use ruff_python_parser::{TokenIterWithContext, TokenKind, Tokens};
use ruff_text_size::{Ranged, TextSize};
use crate::Locator;
/// ## What it does
/// Checks for compound statements (multiple statements on the same line).
@ -170,7 +170,7 @@ pub(crate) fn compound_statements(
let mut diagnostic = Diagnostic::new(UselessSemicolon, range);
diagnostic.set_fix(Fix::safe_edit(Edit::deletion(
indexer
.preceded_by_continuations(range.start(), locator)
.preceded_by_continuations(range.start(), locator.contents())
.unwrap_or(range.start()),
range.end(),
)));

View file

@ -3,11 +3,11 @@ use memchr::memchr_iter;
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::{AnyStringFlags, FStringElement, StringLike, StringLikePart};
use ruff_source_file::Locator;
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
use crate::checkers::ast::Checker;
use crate::fix::edits::pad_start;
use crate::Locator;
/// ## What it does
/// Checks for invalid escape sequences.

View file

@ -79,10 +79,10 @@ pub(crate) fn lambda_assignment(
stmt.range(),
);
if !has_leading_content(stmt.start(), checker.locator())
&& !has_trailing_content(stmt.end(), checker.locator())
if !has_leading_content(stmt.start(), checker.source())
&& !has_trailing_content(stmt.end(), checker.source())
{
let first_line = checker.locator().line(stmt.start());
let first_line = checker.locator().line_str(stmt.start());
let indentation = leading_indentation(first_line);
let mut indented = String::new();
for (idx, line) in function(

View file

@ -336,7 +336,7 @@ pub(crate) fn literal_comparisons(checker: &mut Checker, compare: &ast::ExprComp
&compare.comparators,
compare.into(),
checker.comment_ranges(),
checker.locator(),
checker.source(),
);
for diagnostic in &mut diagnostics {
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(

View file

@ -1,26 +1,25 @@
use std::fmt::{Debug, Formatter};
use std::iter::FusedIterator;
use bitflags::bitflags;
pub(crate) use extraneous_whitespace::*;
pub(crate) use indentation::*;
pub(crate) use missing_whitespace::*;
pub(crate) use missing_whitespace_after_keyword::*;
pub(crate) use missing_whitespace_around_operator::*;
pub(crate) use redundant_backslash::*;
use ruff_python_parser::{TokenKind, Tokens};
use ruff_python_trivia::is_python_whitespace;
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
pub(crate) use space_around_operator::*;
pub(crate) use whitespace_around_keywords::*;
pub(crate) use whitespace_around_named_parameter_equals::*;
pub(crate) use whitespace_before_comment::*;
pub(crate) use whitespace_before_parameters::*;
use std::fmt::{Debug, Formatter};
use std::iter::FusedIterator;
use bitflags::bitflags;
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
use ruff_python_parser::{TokenKind, Tokens};
use ruff_python_trivia::is_python_whitespace;
use ruff_source_file::Locator;
use crate::rules::pycodestyle::helpers::is_non_logical_token;
use crate::Locator;
mod extraneous_whitespace;
mod indentation;
@ -579,7 +578,8 @@ impl TypeParamsState {
#[cfg(test)]
mod tests {
use ruff_python_parser::parse_module;
use ruff_source_file::Locator;
use crate::Locator;
use super::LogicalLines;

View file

@ -2,10 +2,11 @@ use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_index::Indexer;
use ruff_python_parser::TokenKind;
use ruff_source_file::Locator;
use ruff_source_file::LineRanges;
use ruff_text_size::{Ranged, TextRange, TextSize};
use crate::checkers::logical_lines::LogicalLinesContext;
use crate::Locator;
use super::LogicalLine;

View file

@ -2,11 +2,12 @@ use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_parser::TokenKind;
use ruff_python_trivia::PythonWhitespace;
use ruff_source_file::Locator;
use ruff_source_file::LineRanges;
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
use crate::checkers::logical_lines::LogicalLinesContext;
use crate::rules::pycodestyle::rules::logical_lines::LogicalLine;
use crate::Locator;
/// ## What it does
/// Checks if inline comments are separated by at least two spaces.

View file

@ -1,9 +1,9 @@
use ruff_text_size::{TextLen, TextRange};
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_codegen::Stylist;
use ruff_source_file::Locator;
use ruff_text_size::{TextLen, TextRange};
use crate::Locator;
/// ## What it does
/// Checks for files missing a new line at the end of the file.

View file

@ -6,10 +6,11 @@ use ruff_python_ast::{Alias, Stmt};
use ruff_python_codegen::Stylist;
use ruff_python_index::Indexer;
use ruff_python_trivia::indentation_at_offset;
use ruff_source_file::Locator;
use ruff_source_file::LineRanges;
use ruff_text_size::{Ranged, TextRange};
use crate::checkers::ast::Checker;
use crate::Locator;
/// ## What it does
/// Check for multiple imports on one line.
@ -68,7 +69,7 @@ fn split_imports(
indexer: &Indexer,
stylist: &Stylist,
) -> Fix {
if indexer.in_multi_statement_line(stmt, locator) {
if indexer.in_multi_statement_line(stmt, locator.contents()) {
// Ex) `x = 1; import os, sys` (convert to `x = 1; import os; import sys`)
let replacement = names
.iter()
@ -90,7 +91,8 @@ fn split_imports(
Fix::safe_edit(Edit::range_replacement(replacement, stmt.range()))
} else {
// Ex) `import os, sys` (convert to `import os\nimport sys`)
let indentation = indentation_at_offset(stmt.start(), locator).unwrap_or_default();
let indentation =
indentation_at_offset(stmt.start(), locator.contents()).unwrap_or_default();
// Generate newline-delimited imports.
let replacement = names

View file

@ -105,7 +105,7 @@ pub(crate) fn not_tests(checker: &mut Checker, unary_op: &ast::ExprUnaryOp) {
comparators,
unary_op.into(),
checker.comment_ranges(),
checker.locator(),
checker.source(),
),
unary_op.range(),
checker.locator(),
@ -126,7 +126,7 @@ pub(crate) fn not_tests(checker: &mut Checker, unary_op: &ast::ExprUnaryOp) {
comparators,
unary_op.into(),
checker.comment_ranges(),
checker.locator(),
checker.source(),
),
unary_op.range(),
checker.locator(),

View file

@ -1,9 +1,11 @@
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_index::Indexer;
use ruff_source_file::Locator;
use ruff_source_file::LineRanges;
use ruff_text_size::{TextRange, TextSize};
use crate::Locator;
/// ## What it does
/// Checks for indentation that uses tabs.
///

View file

@ -1,11 +1,12 @@
use ruff_diagnostics::{AlwaysFixableViolation, Applicability, Diagnostic, Edit, Fix};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_index::Indexer;
use ruff_source_file::{Line, Locator};
use ruff_source_file::Line;
use ruff_text_size::{TextLen, TextRange, TextSize};
use crate::registry::Rule;
use crate::settings::LinterSettings;
use crate::Locator;
/// ## What it does
/// Checks for superfluous trailing whitespace.
@ -100,7 +101,7 @@ pub(crate) fn trailing_whitespace(
diagnostic.set_fix(Fix::applicable_edit(
Edit::range_deletion(TextRange::new(
indexer
.preceded_by_continuations(line.start(), locator)
.preceded_by_continuations(line.start(), locator.contents())
.unwrap_or(range.start()),
range.end(),
)),

View file

@ -1,7 +1,7 @@
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_trivia::{indentation_at_offset, PythonWhitespace};
use ruff_source_file::{Line, UniversalNewlineIterator};
use ruff_source_file::{Line, LineRanges, UniversalNewlineIterator};
use ruff_text_size::Ranged;
use ruff_text_size::{TextLen, TextRange};
@ -240,7 +240,7 @@ pub(crate) fn blank_before_after_class(checker: &mut Checker, docstring: &Docstr
if let Some(first_line) = &first_line {
let trailing = first_line.as_str().trim_whitespace_start();
if let Some(next_statement) = trailing.strip_prefix(';') {
let indentation = indentation_at_offset(docstring.start(), checker.locator())
let indentation = indentation_at_offset(docstring.start(), checker.source())
.expect("Own line docstring must have indentation");
let mut diagnostic = Diagnostic::new(OneBlankLineAfterClass, docstring.range());
let line_ending = checker.stylist().line_ending().as_str();

View file

@ -2,7 +2,7 @@ use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::str::{is_triple_quote, leading_quote};
use ruff_python_semantic::Definition;
use ruff_source_file::{NewlineWithTrailingNewline, UniversalNewlineIterator};
use ruff_source_file::{LineRanges, NewlineWithTrailingNewline, UniversalNewlineIterator};
use ruff_text_size::{Ranged, TextRange, TextSize};
use crate::checkers::ast::Checker;

View file

@ -1464,7 +1464,7 @@ fn blanks_and_section_underline(
// Otherwise, documentation generators will not recognize the directive.
let is_sphinx = checker
.locator()
.line(blank_lines_after_dashes_end)
.line_str(blank_lines_after_dashes_end)
.trim_start()
.starts_with(".. ");
@ -1569,7 +1569,7 @@ fn blanks_and_section_underline(
// Otherwise, documentation generators will not recognize the directive.
let is_sphinx = checker
.locator()
.line(blank_lines_end)
.line_str(blank_lines_end)
.trim_start()
.starts_with(".. ");

View file

@ -5,10 +5,10 @@ use ruff_python_ast as ast;
use ruff_python_codegen::Stylist;
use ruff_python_semantic::Binding;
use ruff_python_trivia::{BackwardsTokenizer, SimpleTokenKind, SimpleTokenizer};
use ruff_source_file::Locator;
use ruff_text_size::Ranged;
use crate::cst::matchers::{match_call_mut, match_dict, transform_expression};
use crate::Locator;
/// Generate a [`Edit`] to remove unused keys from format dict.
pub(super) fn remove_unused_format_arguments_from_dict(

View file

@ -12,15 +12,12 @@ mod tests {
use anyhow::Result;
use regex::Regex;
use rustc_hash::FxHashMap;
use test_case::test_case;
use ruff_python_ast::PySourceType;
use ruff_python_codegen::Stylist;
use ruff_python_index::Indexer;
use ruff_python_trivia::textwrap::dedent;
use ruff_source_file::Locator;
use ruff_text_size::Ranged;
use crate::linter::check_path;
@ -31,6 +28,7 @@ mod tests {
use crate::settings::{flags, LinterSettings};
use crate::source_kind::SourceKind;
use crate::test::{test_contents, test_path, test_snippet};
use crate::Locator;
use crate::{assert_messages, directives};
#[test_case(Rule::UnusedImport, Path::new("F401_0.py"))]
@ -712,8 +710,8 @@ mod tests {
let parsed =
ruff_python_parser::parse_unchecked_source(source_kind.source_code(), source_type);
let locator = Locator::new(&contents);
let stylist = Stylist::from_tokens(parsed.tokens(), &locator);
let indexer = Indexer::from_tokens(parsed.tokens(), &locator);
let stylist = Stylist::from_tokens(parsed.tokens(), locator.contents());
let indexer = Indexer::from_tokens(parsed.tokens(), locator.contents());
let directives = directives::extract_directives(
parsed.tokens(),
directives::Flags::from_settings(&settings),

View file

@ -1,9 +1,9 @@
use ruff_python_ast::{self as ast, ExceptHandler};
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::identifier::except;
use ruff_source_file::Locator;
use ruff_python_ast::{self as ast, ExceptHandler};
use crate::Locator;
/// ## What it does
/// Checks for `except` blocks that handle all exceptions, but are not the last

View file

@ -1,10 +1,10 @@
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast as ast;
use ruff_source_file::Locator;
use ruff_text_size::{Ranged, TextRange, TextSize};
use crate::checkers::ast::Checker;
use crate::Locator;
/// ## What it does
/// Checks for f-strings that do not contain any placeholder expressions.

View file

@ -295,7 +295,7 @@ pub(crate) fn unused_import(checker: &Checker, scope: &Scope, diagnostics: &mut
continue;
};
let name = binding.name(checker.locator());
let name = binding.name(checker.source());
// If an import is marked as required, avoid treating it as unused, regardless of whether
// it was _actually_ used.

View file

@ -183,7 +183,7 @@ fn remove_unused_variable(binding: &Binding, checker: &Checker) -> Option<Fix> {
};
}
} else {
let name = binding.name(checker.locator());
let name = binding.name(checker.source());
let renamed = format!("_{name}");
if checker.settings.dummy_variable_rgx.is_match(&renamed) {
let edit = Edit::range_replacement(renamed, binding.range());

View file

@ -1,11 +1,11 @@
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_trivia::Cursor;
use ruff_source_file::Locator;
use ruff_text_size::{Ranged, TextRange};
use crate::noqa::{Directive, FileNoqaDirectives, NoqaDirectives, ParsedFileExemption};
use crate::settings::types::PreviewMode;
use crate::Locator;
/// ## What it does
/// Check for `noqa` annotations that suppress all diagnostics, as opposed to

View file

@ -1,14 +1,16 @@
use std::sync::LazyLock;
use anyhow::{anyhow, Result};
use memchr::memchr_iter;
use regex::Regex;
use std::sync::LazyLock;
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_trivia::CommentRanges;
use ruff_source_file::Locator;
use ruff_text_size::TextSize;
use crate::Locator;
/// ## What it does
/// Check for `type: ignore` annotations that suppress all type warnings, as
/// opposed to targeting specific type warnings.

View file

@ -6,11 +6,12 @@ use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::{self as ast, ElifElseClause, Stmt};
use ruff_python_codegen::Stylist;
use ruff_python_index::Indexer;
use ruff_source_file::Locator;
use ruff_source_file::LineRanges;
use ruff_text_size::{Ranged, TextRange};
use crate::checkers::ast::Checker;
use crate::fix::edits::adjust_indentation;
use crate::Locator;
/// ## What it does
/// Checks for `else` blocks that consist of a single `if` statement.
@ -113,7 +114,7 @@ fn convert_to_elif(
let trivia_range = TextRange::new(else_line_end, inner_if_line_start);
// Identify the indentation of the outer clause
let Some(indentation) = indentation(locator, else_clause) else {
let Some(indentation) = indentation(locator.contents(), else_clause) else {
return Err(anyhow::anyhow!("`else` is expected to be on its own line"));
};

View file

@ -1,9 +1,11 @@
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_trivia::{is_python_whitespace, CommentRanges};
use ruff_source_file::Locator;
use ruff_source_file::LineRanges;
use ruff_text_size::{TextRange, TextSize};
use crate::Locator;
/// ## What it does
/// Checks for a # symbol appearing on a line not followed by an actual comment.
///
@ -47,7 +49,7 @@ pub(crate) fn empty_comments(
comment_ranges: &CommentRanges,
locator: &Locator,
) {
let block_comments = comment_ranges.block_comments(locator);
let block_comments = comment_ranges.block_comments(locator.contents());
for range in comment_ranges {
// Ignore comments that are part of multi-line "comment blocks".

View file

@ -1,11 +1,11 @@
use ruff_text_size::{TextLen, TextRange, TextSize};
use ruff_diagnostics::AlwaysFixableViolation;
use ruff_diagnostics::Edit;
use ruff_diagnostics::{Diagnostic, DiagnosticKind, Fix};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_parser::TokenKind;
use ruff_source_file::Locator;
use ruff_text_size::{TextLen, TextRange, TextSize};
use crate::Locator;
/// ## What it does
/// Checks for strings that contain the control character `BS`.

View file

@ -157,7 +157,7 @@ pub(crate) fn nested_min_max(
let mut diagnostic = Diagnostic::new(NestedMinMax { func: min_max }, expr.range());
if !checker
.comment_ranges()
.has_comments(expr, checker.locator())
.has_comments(expr, checker.source())
{
let flattened_expr = Expr::Call(ast::ExprCall {
func: Box::new(func.clone()),

View file

@ -175,7 +175,7 @@ fn get_undecorated_methods(checker: &mut Checker, class_stmt: &Stmt, method_type
TextRange::new(stmt.range().start(), stmt.range().start()),
);
let indentation = indentation_at_offset(stmt.range().start(), checker.locator());
let indentation = indentation_at_offset(stmt.range().start(), checker.source());
match indentation {
Some(indentation) => {

Some files were not shown because too many files have changed in this diff Show more