diff --git a/crates/ruff_db/src/diagnostic/mod.rs b/crates/ruff_db/src/diagnostic/mod.rs index e93bf87219..7979cafc68 100644 --- a/crates/ruff_db/src/diagnostic/mod.rs +++ b/crates/ruff_db/src/diagnostic/mod.rs @@ -494,13 +494,6 @@ impl Diagnostic { self.primary_span()?.range() } - /// Returns the [`TextRange`] for the diagnostic. - /// - /// Panics if the diagnostic has no primary span or if the span has no range. - pub fn expect_range(&self) -> TextRange { - self.range().expect("Expected a range for the primary span") - } - /// Returns the ordering of diagnostics based on the start of their ranges, if they have any. /// /// Panics if either diagnostic has no primary span, or if its file is not a `SourceFile`. diff --git a/crates/ruff_linter/src/checkers/noqa.rs b/crates/ruff_linter/src/checkers/noqa.rs index bf411511ce..926c7dcfb8 100644 --- a/crates/ruff_linter/src/checkers/noqa.rs +++ b/crates/ruff_linter/src/checkers/noqa.rs @@ -6,7 +6,7 @@ use itertools::Itertools; use rustc_hash::FxHashSet; use ruff_python_trivia::CommentRanges; -use ruff_text_size::Ranged; +use ruff_text_size::{Ranged, TextRange}; use crate::fix::edits::delete_comment; use crate::noqa::{ @@ -68,7 +68,7 @@ pub(crate) fn check_noqa( let noqa_offsets = diagnostic .parent() .into_iter() - .chain(std::iter::once(diagnostic.expect_range().start())) + .chain(diagnostic.range().map(TextRange::start).into_iter()) .map(|position| noqa_line_for.resolve(position)) .unique(); diff --git a/crates/ruff_linter/src/linter.rs b/crates/ruff_linter/src/linter.rs index eeb321cab9..62a7289103 100644 --- a/crates/ruff_linter/src/linter.rs +++ b/crates/ruff_linter/src/linter.rs @@ -513,10 +513,9 @@ fn diagnostics_to_messages( .map(|error| create_syntax_error_diagnostic(source_file.clone(), error, error)), ) .chain(diagnostics.into_iter().map(|mut diagnostic| { - let noqa_offset = directives - .noqa_line_for - .resolve(diagnostic.expect_range().start()); - diagnostic.set_noqa_offset(noqa_offset); + if let Some(range) = diagnostic.range() { + diagnostic.set_noqa_offset(directives.noqa_line_for.resolve(range.start())); + } diagnostic })) .collect() @@ -983,7 +982,7 @@ mod tests { &parsed, target_version, ); - diagnostics.sort_by_key(|diagnostic| diagnostic.expect_range().start()); + diagnostics.sort_by(Diagnostic::ruff_start_ordering); diagnostics } diff --git a/crates/ruff_linter/src/noqa.rs b/crates/ruff_linter/src/noqa.rs index 7ff082acdd..606ac5ad3b 100644 --- a/crates/ruff_linter/src/noqa.rs +++ b/crates/ruff_linter/src/noqa.rs @@ -886,7 +886,10 @@ fn find_noqa_comments<'a>( } } - let noqa_offset = noqa_line_for.resolve(message.expect_range().start()); + let noqa_offset = message + .range() + .map(|range| noqa_line_for.resolve(range.start())) + .unwrap_or_default(); // Or ignored by the directive itself? if let Some(directive_line) = directives.find_line_with_directive(noqa_offset) { diff --git a/crates/ruff_linter/src/rules/flake8_commas/rules/trailing_commas.rs b/crates/ruff_linter/src/rules/flake8_commas/rules/trailing_commas.rs index 380d71fa2c..e759f493b2 100644 --- a/crates/ruff_linter/src/rules/flake8_commas/rules/trailing_commas.rs +++ b/crates/ruff_linter/src/rules/flake8_commas/rules/trailing_commas.rs @@ -362,8 +362,7 @@ fn check_token( if let Some(mut diagnostic) = lint_context.report_diagnostic_if_enabled(ProhibitedTrailingComma, prev.range()) { - let range = diagnostic.expect_range(); - diagnostic.set_fix(Fix::safe_edit(Edit::range_deletion(range))); + diagnostic.set_fix(Fix::safe_edit(Edit::range_deletion(prev.range))); return; } } diff --git a/crates/ruff_linter/src/rules/isort/mod.rs b/crates/ruff_linter/src/rules/isort/mod.rs index 7368088f36..92d5dfe38d 100644 --- a/crates/ruff_linter/src/rules/isort/mod.rs +++ b/crates/ruff_linter/src/rules/isort/mod.rs @@ -642,7 +642,7 @@ mod tests { #[test_case(Path::new("order_by_type.py"))] fn order_by_type(path: &Path) -> Result<()> { let snapshot = format!("order_by_type_false_{}", path.to_string_lossy()); - let mut diagnostics = test_path( + let diagnostics = test_path( Path::new("isort").join(path).as_path(), &LinterSettings { isort: super::settings::Settings { @@ -653,7 +653,6 @@ mod tests { ..LinterSettings::for_rule(Rule::UnsortedImports) }, )?; - diagnostics.sort_by_key(|diagnostic| diagnostic.expect_range().start()); assert_diagnostics!(snapshot, diagnostics); Ok(()) } @@ -664,7 +663,7 @@ mod tests { "order_by_type_with_custom_classes_{}", path.to_string_lossy() ); - let mut diagnostics = test_path( + let diagnostics = test_path( Path::new("isort").join(path).as_path(), &LinterSettings { isort: super::settings::Settings { @@ -681,7 +680,6 @@ mod tests { ..LinterSettings::for_rule(Rule::UnsortedImports) }, )?; - diagnostics.sort_by_key(|diagnostic| diagnostic.expect_range().start()); assert_diagnostics!(snapshot, diagnostics); Ok(()) } @@ -692,7 +690,7 @@ mod tests { "order_by_type_with_custom_constants_{}", path.to_string_lossy() ); - let mut diagnostics = test_path( + let diagnostics = test_path( Path::new("isort").join(path).as_path(), &LinterSettings { isort: super::settings::Settings { @@ -711,7 +709,6 @@ mod tests { ..LinterSettings::for_rule(Rule::UnsortedImports) }, )?; - diagnostics.sort_by_key(|diagnostic| diagnostic.expect_range().start()); assert_diagnostics!(snapshot, diagnostics); Ok(()) } @@ -722,7 +719,7 @@ mod tests { "order_by_type_with_custom_variables_{}", path.to_string_lossy() ); - let mut diagnostics = test_path( + let diagnostics = test_path( Path::new("isort").join(path).as_path(), &LinterSettings { isort: super::settings::Settings { @@ -739,7 +736,6 @@ mod tests { ..LinterSettings::for_rule(Rule::UnsortedImports) }, )?; - diagnostics.sort_by_key(|diagnostic| diagnostic.expect_range().start()); assert_diagnostics!(snapshot, diagnostics); Ok(()) } @@ -749,7 +745,7 @@ mod tests { #[test_case(Path::new("force_sort_within_sections_future.py"))] fn force_sort_within_sections(path: &Path) -> Result<()> { let snapshot = format!("force_sort_within_sections_{}", path.to_string_lossy()); - let mut diagnostics = test_path( + let diagnostics = test_path( Path::new("isort").join(path).as_path(), &LinterSettings { isort: super::settings::Settings { @@ -761,7 +757,6 @@ mod tests { ..LinterSettings::for_rule(Rule::UnsortedImports) }, )?; - diagnostics.sort_by_key(|diagnostic| diagnostic.expect_range().start()); assert_diagnostics!(snapshot, diagnostics); Ok(()) } @@ -769,7 +764,7 @@ mod tests { #[test_case(Path::new("force_sort_within_sections_lines_between.py"))] fn force_sort_within_sections_lines_between(path: &Path) -> Result<()> { let snapshot = format!("force_sort_within_sections_{}", path.to_string_lossy()); - let mut diagnostics = test_path( + let diagnostics = test_path( Path::new("isort").join(path).as_path(), &LinterSettings { isort: super::settings::Settings { @@ -781,7 +776,6 @@ mod tests { ..LinterSettings::for_rule(Rule::UnsortedImports) }, )?; - diagnostics.sort_by_key(|diagnostic| diagnostic.expect_range().start()); assert_diagnostics!(snapshot, diagnostics); Ok(()) } @@ -1114,7 +1108,7 @@ mod tests { #[test_case(Path::new("no_lines_before.py"))] fn no_lines_before(path: &Path) -> Result<()> { let snapshot = format!("no_lines_before.py_{}", path.to_string_lossy()); - let mut diagnostics = test_path( + let diagnostics = test_path( Path::new("isort").join(path).as_path(), &LinterSettings { isort: super::settings::Settings { @@ -1131,7 +1125,6 @@ mod tests { ..LinterSettings::for_rule(Rule::UnsortedImports) }, )?; - diagnostics.sort_by_key(|diagnostic| diagnostic.expect_range().start()); assert_diagnostics!(snapshot, diagnostics); Ok(()) } @@ -1142,7 +1135,7 @@ mod tests { "no_lines_before_with_empty_sections.py_{}", path.to_string_lossy() ); - let mut diagnostics = test_path( + let diagnostics = test_path( Path::new("isort").join(path).as_path(), &LinterSettings { isort: super::settings::Settings { @@ -1156,7 +1149,6 @@ mod tests { ..LinterSettings::for_rule(Rule::UnsortedImports) }, )?; - diagnostics.sort_by_key(|diagnostic| diagnostic.expect_range().start()); assert_diagnostics!(snapshot, diagnostics); Ok(()) } @@ -1167,7 +1159,7 @@ mod tests { #[test_case(Path::new("lines_after_imports_class_after.py"))] fn lines_after_imports(path: &Path) -> Result<()> { let snapshot = format!("lines_after_imports_{}", path.to_string_lossy()); - let mut diagnostics = test_path( + let diagnostics = test_path( Path::new("isort").join(path).as_path(), &LinterSettings { isort: super::settings::Settings { @@ -1178,7 +1170,6 @@ mod tests { ..LinterSettings::for_rule(Rule::UnsortedImports) }, )?; - diagnostics.sort_by_key(|diagnostic| diagnostic.expect_range().start()); assert_diagnostics!(snapshot, diagnostics); Ok(()) } @@ -1188,7 +1179,7 @@ mod tests { #[test_case(Path::new("lines_after_imports_class_after.py"))] fn lines_after_imports_default_settings(path: &Path) -> Result<()> { let snapshot = path.to_string_lossy(); - let mut diagnostics = test_path( + let diagnostics = test_path( Path::new("isort").join(path).as_path(), &LinterSettings { src: vec![test_resource_path("fixtures/isort")], @@ -1199,7 +1190,6 @@ mod tests { ..LinterSettings::for_rule(Rule::UnsortedImports) }, )?; - diagnostics.sort_by_key(|diagnostic| diagnostic.expect_range().start()); assert_diagnostics!(&*snapshot, diagnostics); Ok(()) } @@ -1207,7 +1197,7 @@ mod tests { #[test_case(Path::new("lines_between_types.py"))] fn lines_between_types(path: &Path) -> Result<()> { let snapshot = format!("lines_between_types{}", path.to_string_lossy()); - let mut diagnostics = test_path( + let diagnostics = test_path( Path::new("isort").join(path).as_path(), &LinterSettings { isort: super::settings::Settings { @@ -1218,7 +1208,6 @@ mod tests { ..LinterSettings::for_rule(Rule::UnsortedImports) }, )?; - diagnostics.sort_by_key(|diagnostic| diagnostic.expect_range().start()); assert_diagnostics!(snapshot, diagnostics); Ok(()) } diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/extraneous_whitespace.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/extraneous_whitespace.rs index 1419525033..6dd9b49356 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/extraneous_whitespace.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/extraneous_whitespace.rs @@ -168,11 +168,11 @@ pub(crate) fn extraneous_whitespace(line: &LogicalLine, context: &LintContext) { { let (trailing, trailing_len) = line.trailing_whitespace(token); if !matches!(trailing, Whitespace::None) { + let range = TextRange::at(token.end(), trailing_len); if let Some(mut diagnostic) = context.report_diagnostic_if_enabled( WhitespaceAfterOpenBracket { symbol }, - TextRange::at(token.end(), trailing_len), + range, ) { - let range = diagnostic.expect_range(); diagnostic.set_fix(Fix::safe_edit(Edit::range_deletion(range))); } } @@ -184,11 +184,11 @@ pub(crate) fn extraneous_whitespace(line: &LogicalLine, context: &LintContext) { if let (Whitespace::Single | Whitespace::Many | Whitespace::Tab, offset) = line.leading_whitespace(token) { + let range = TextRange::at(token.start() - offset, offset); if let Some(mut diagnostic) = context.report_diagnostic_if_enabled( WhitespaceBeforeCloseBracket { symbol }, - TextRange::at(token.start() - offset, offset), + range, ) { - let range = diagnostic.expect_range(); diagnostic.set_fix(Fix::safe_edit(Edit::range_deletion(range))); } } @@ -210,13 +210,13 @@ pub(crate) fn extraneous_whitespace(line: &LogicalLine, context: &LintContext) { // If we're in the second half of a double colon, disallow // any whitespace (e.g., `foo[1: :2]` or `foo[1 : : 2]`). if matches!(prev_token, Some(TokenKind::Colon)) { + let range = TextRange::at(token.start() - offset, offset); if let Some(mut diagnostic) = context .report_diagnostic_if_enabled( WhitespaceBeforePunctuation { symbol }, - TextRange::at(token.start() - offset, offset), + range, ) { - let range = diagnostic.expect_range(); diagnostic .set_fix(Fix::safe_edit(Edit::range_deletion(range))); } @@ -227,13 +227,13 @@ pub(crate) fn extraneous_whitespace(line: &LogicalLine, context: &LintContext) { // Or `foo[index :, 2]`, but not `foo[index :, 2]`. if let (Whitespace::Many | Whitespace::Tab, offset) = whitespace { + let range = TextRange::at(token.start() - offset, offset); if let Some(mut diagnostic) = context .report_diagnostic_if_enabled( WhitespaceBeforePunctuation { symbol }, - TextRange::at(token.start() - offset, offset), + range, ) { - let range = diagnostic.expect_range(); diagnostic.set_fix(Fix::safe_edit( Edit::range_deletion(range), )); @@ -255,13 +255,13 @@ pub(crate) fn extraneous_whitespace(line: &LogicalLine, context: &LintContext) { // whitespace before the colon and so should the fix if let (Whitespace::Many | Whitespace::Tab, offset) = whitespace { + let range = TextRange::at(token.start() - offset, offset); if let Some(mut diagnostic) = context .report_diagnostic_if_enabled( WhitespaceBeforePunctuation { symbol }, - TextRange::at(token.start() - offset, offset), + range, ) { - let range = diagnostic.expect_range(); diagnostic.set_fix(Fix::safe_edits( Edit::range_deletion(range), [Edit::insertion( @@ -278,13 +278,13 @@ pub(crate) fn extraneous_whitespace(line: &LogicalLine, context: &LintContext) { .filter(|next| matches!(next.kind(), TokenKind::Colon)) .unwrap_or(&token); if line.trailing_whitespace(token) != whitespace { + let range = TextRange::at(token.start() - offset, offset); if let Some(mut diagnostic) = context .report_diagnostic_if_enabled( WhitespaceBeforePunctuation { symbol }, - TextRange::at(token.start() - offset, offset), + range, ) { - let range = diagnostic.expect_range(); diagnostic.set_fix(Fix::safe_edit( Edit::range_deletion(range), )); @@ -299,11 +299,11 @@ pub(crate) fn extraneous_whitespace(line: &LogicalLine, context: &LintContext) { // Avoid removing any whitespace for f-string debug expressions. continue; } + let range = TextRange::at(token.start() - offset, offset); if let Some(mut diagnostic) = context.report_diagnostic_if_enabled( WhitespaceBeforePunctuation { symbol }, - TextRange::at(token.start() - offset, offset), + range, ) { - let range = diagnostic.expect_range(); diagnostic.set_fix(Fix::safe_edit(Edit::range_deletion(range))); } } diff --git a/crates/ruff_linter/src/rules/pyflakes/mod.rs b/crates/ruff_linter/src/rules/pyflakes/mod.rs index ab553e2b72..7590c31a39 100644 --- a/crates/ruff_linter/src/rules/pyflakes/mod.rs +++ b/crates/ruff_linter/src/rules/pyflakes/mod.rs @@ -788,7 +788,7 @@ mod tests { &parsed, target_version, ); - messages.sort_by_key(|diagnostic| diagnostic.expect_range().start()); + messages.sort_by(Diagnostic::ruff_start_ordering); let actual = messages .iter() .filter(|msg| !msg.is_invalid_syntax()) diff --git a/crates/ruff_linter/src/test.rs b/crates/ruff_linter/src/test.rs index 546f7b66d6..b63e23fc87 100644 --- a/crates/ruff_linter/src/test.rs +++ b/crates/ruff_linter/src/test.rs @@ -383,17 +383,20 @@ Either ensure you always emit a fix or change `Violation::FIX_AVAILABILITY` to e // Not strictly necessary but adds some coverage for this code path by overriding the // noqa offset and the source file - let range = diagnostic.expect_range(); - diagnostic.set_noqa_offset(directives.noqa_line_for.resolve(range.start())); + if let Some(range) = diagnostic.range() { + diagnostic.set_noqa_offset(directives.noqa_line_for.resolve(range.start())); + } // This part actually is necessary to avoid long relative paths in snapshots. for annotation in diagnostic.annotations_mut() { - let range = annotation.get_span().range().unwrap(); - annotation.set_span(Span::from(source_code.clone()).with_range(range)); + if let Some(range) = annotation.get_span().range() { + annotation.set_span(Span::from(source_code.clone()).with_range(range)); + } } for sub in diagnostic.sub_diagnostics_mut() { for annotation in sub.annotations_mut() { - let range = annotation.get_span().range().unwrap(); - annotation.set_span(Span::from(source_code.clone()).with_range(range)); + if let Some(range) = annotation.get_span().range() { + annotation.set_span(Span::from(source_code.clone()).with_range(range)); + } } } diff --git a/crates/ruff_server/src/lint.rs b/crates/ruff_server/src/lint.rs index 5c764bfa58..0d3d62c8b0 100644 --- a/crates/ruff_server/src/lint.rs +++ b/crates/ruff_server/src/lint.rs @@ -234,7 +234,7 @@ fn to_lsp_diagnostic( index: &LineIndex, encoding: PositionEncoding, ) -> (usize, lsp_types::Diagnostic) { - let diagnostic_range = diagnostic.expect_range(); + let diagnostic_range = diagnostic.range().unwrap_or_default(); let name = diagnostic.name(); let body = diagnostic.body().to_string(); let fix = diagnostic.fix(); diff --git a/crates/ruff_wasm/src/lib.rs b/crates/ruff_wasm/src/lib.rs index bc11dfc310..d131448832 100644 --- a/crates/ruff_wasm/src/lib.rs +++ b/crates/ruff_wasm/src/lib.rs @@ -231,8 +231,12 @@ impl Workspace { .map(|msg| ExpandedMessage { code: msg.secondary_code_or_id().to_string(), message: msg.body().to_string(), - start_location: source_code.line_column(msg.expect_range().start()).into(), - end_location: source_code.line_column(msg.expect_range().end()).into(), + start_location: source_code + .line_column(msg.range().unwrap_or_default().start()) + .into(), + end_location: source_code + .line_column(msg.range().unwrap_or_default().end()) + .into(), fix: msg.fix().map(|fix| ExpandedFix { message: msg.first_help_text().map(ToString::to_string), edits: fix