add autofix

This commit is contained in:
Jonas Vacek 2025-10-05 17:00:25 +02:00
parent a2fcf0eb3f
commit 1047e895c7
No known key found for this signature in database
2 changed files with 100 additions and 11 deletions

View File

@ -1,10 +1,10 @@
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::{self as ast, Expr};
use ruff_python_semantic::Modules;
use ruff_text_size::Ranged;
use ruff_text_size::{Ranged, TextSize};
use crate::Violation;
use crate::checkers::ast::Checker;
use crate::{AlwaysFixableViolation, Edit, Fix};
/// ## What it does
/// Checks that all Django URL route definitions using `django.urls.path()`
@ -47,12 +47,16 @@ pub(crate) struct DjangoURLPathWithoutTrailingSlash {
url_pattern: String,
}
impl Violation for DjangoURLPathWithoutTrailingSlash {
impl AlwaysFixableViolation for DjangoURLPathWithoutTrailingSlash {
#[derive_message_formats]
fn message(&self) -> String {
let DjangoURLPathWithoutTrailingSlash { url_pattern } = self;
format!("URL route `{url_pattern}` is missing a trailing slash")
}
fn fix_title(&self) -> String {
"Add trailing slash".to_string()
}
}
/// DJ014
@ -94,11 +98,35 @@ pub(crate) fn url_path_without_trailing_slash(checker: &Checker, call: &ast::Exp
}
// Report diagnostic for routes without trailing slash
checker.report_diagnostic(
let mut diagnostic = checker.report_diagnostic(
DjangoURLPathWithoutTrailingSlash {
url_pattern: route.to_string(),
},
route_arg.range(),
);
// Generate fix: add trailing slash to the string content
// We need to find the position of the closing quote and insert "/" before it
let string_range = route_arg.range();
let locator = checker.locator();
let string_content = locator.slice(string_range);
// Find the closing quote(s) by working backwards from the end
// Handle both single quotes, double quotes, and their triple variants
let quote_len = if string_content.ends_with("'''") || string_content.ends_with("\"\"\"") {
3
} else if string_content.ends_with('\'') || string_content.ends_with('"') {
1
} else {
// Shouldn't happen for a valid string literal
return;
};
// Insert "/" before the closing quote(s)
let insertion_point = string_range.end() - TextSize::new(quote_len);
diagnostic.set_fix(Fix::safe_edit(Edit::insertion(
"/".to_string(),
insertion_point,
)));
}
}

View File

@ -1,7 +1,7 @@
---
source: crates/ruff_linter/src/rules/flake8_django/mod.rs
---
DJ014 URL route `help` is missing a trailing slash
DJ014 [*] URL route `help` is missing a trailing slash
--> DJ014.py:6:10
|
4 | # Errors - missing trailing slash
@ -11,8 +11,17 @@ DJ014 URL route `help` is missing a trailing slash
7 | path("about", views.about_view), # DJ014
8 | path("contact", views.contact_view), # DJ014
|
help: Add trailing slash
3 |
4 | # Errors - missing trailing slash
5 | urlpatterns = [
- path("help", views.help_view), # DJ014
6 + path("help/", views.help_view), # DJ014
7 | path("about", views.about_view), # DJ014
8 | path("contact", views.contact_view), # DJ014
9 | path("api/users", views.users_view), # DJ014
DJ014 URL route `about` is missing a trailing slash
DJ014 [*] URL route `about` is missing a trailing slash
--> DJ014.py:7:10
|
5 | urlpatterns = [
@ -22,8 +31,17 @@ DJ014 URL route `about` is missing a trailing slash
8 | path("contact", views.contact_view), # DJ014
9 | path("api/users", views.users_view), # DJ014
|
help: Add trailing slash
4 | # Errors - missing trailing slash
5 | urlpatterns = [
6 | path("help", views.help_view), # DJ014
- path("about", views.about_view), # DJ014
7 + path("about/", views.about_view), # DJ014
8 | path("contact", views.contact_view), # DJ014
9 | path("api/users", views.users_view), # DJ014
10 | path("blog/posts", views.posts_view), # DJ014
DJ014 URL route `contact` is missing a trailing slash
DJ014 [*] URL route `contact` is missing a trailing slash
--> DJ014.py:8:10
|
6 | path("help", views.help_view), # DJ014
@ -33,8 +51,17 @@ DJ014 URL route `contact` is missing a trailing slash
9 | path("api/users", views.users_view), # DJ014
10 | path("blog/posts", views.posts_view), # DJ014
|
help: Add trailing slash
5 | urlpatterns = [
6 | path("help", views.help_view), # DJ014
7 | path("about", views.about_view), # DJ014
- path("contact", views.contact_view), # DJ014
8 + path("contact/", views.contact_view), # DJ014
9 | path("api/users", views.users_view), # DJ014
10 | path("blog/posts", views.posts_view), # DJ014
11 | ]
DJ014 URL route `api/users` is missing a trailing slash
DJ014 [*] URL route `api/users` is missing a trailing slash
--> DJ014.py:9:10
|
7 | path("about", views.about_view), # DJ014
@ -44,8 +71,17 @@ DJ014 URL route `api/users` is missing a trailing slash
10 | path("blog/posts", views.posts_view), # DJ014
11 | ]
|
help: Add trailing slash
6 | path("help", views.help_view), # DJ014
7 | path("about", views.about_view), # DJ014
8 | path("contact", views.contact_view), # DJ014
- path("api/users", views.users_view), # DJ014
9 + path("api/users/", views.users_view), # DJ014
10 | path("blog/posts", views.posts_view), # DJ014
11 | ]
12 |
DJ014 URL route `blog/posts` is missing a trailing slash
DJ014 [*] URL route `blog/posts` is missing a trailing slash
--> DJ014.py:10:10
|
8 | path("contact", views.contact_view), # DJ014
@ -54,8 +90,17 @@ DJ014 URL route `blog/posts` is missing a trailing slash
| ^^^^^^^^^^^^
11 | ]
|
help: Add trailing slash
7 | path("about", views.about_view), # DJ014
8 | path("contact", views.contact_view), # DJ014
9 | path("api/users", views.users_view), # DJ014
- path("blog/posts", views.posts_view), # DJ014
10 + path("blog/posts/", views.posts_view), # DJ014
11 | ]
12 |
13 | # OK - has trailing slash
DJ014 URL route `bad` is missing a trailing slash
DJ014 [*] URL route `bad` is missing a trailing slash
--> DJ014.py:37:10
|
35 | urlpatterns_mixed = [
@ -65,8 +110,17 @@ DJ014 URL route `bad` is missing a trailing slash
38 | path("also-good/", views.also_good_view),
39 | path("also-bad", views.also_bad_view), # DJ014
|
help: Add trailing slash
34 | # Mixed cases
35 | urlpatterns_mixed = [
36 | path("good/", views.good_view),
- path("bad", views.bad_view), # DJ014
37 + path("bad/", views.bad_view), # DJ014
38 | path("also-good/", views.also_good_view),
39 | path("also-bad", views.also_bad_view), # DJ014
40 | ]
DJ014 URL route `also-bad` is missing a trailing slash
DJ014 [*] URL route `also-bad` is missing a trailing slash
--> DJ014.py:39:10
|
37 | path("bad", views.bad_view), # DJ014
@ -75,3 +129,10 @@ DJ014 URL route `also-bad` is missing a trailing slash
| ^^^^^^^^^^
40 | ]
|
help: Add trailing slash
36 | path("good/", views.good_view),
37 | path("bad", views.bad_view), # DJ014
38 | path("also-good/", views.also_good_view),
- path("also-bad", views.also_bad_view), # DJ014
39 + path("also-bad/", views.also_bad_view), # DJ014
40 | ]