mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-01 06:11:43 +00:00
Move flake8-executable
rules out of physical lines checker (#6039)
## Summary These only need the token stream, and we always prefer token-based to physical line-based rules. There are a few other changes snuck in here: - Renaming the rule files to match the diagnostic names (likely an error). - The "leading whitespace before shebang" rule now works regardless of where the comment occurs (i.e., if the shebang is on the second line, and the first line is blank, we flag and remove that leading whitespace).
This commit is contained in:
parent
7f3797185c
commit
776d598738
22 changed files with 235 additions and 224 deletions
2
crates/ruff/resources/test/fixtures/flake8_executable/EXE004_4.py
vendored
Executable file
2
crates/ruff/resources/test/fixtures/flake8_executable/EXE004_4.py
vendored
Executable file
|
@ -0,0 +1,2 @@
|
|||
|
||||
#!/usr/bin/env python
|
|
@ -1,18 +1,12 @@
|
|||
//! Lint rules based on checking physical lines.
|
||||
use std::path::Path;
|
||||
|
||||
use ruff_text_size::TextSize;
|
||||
|
||||
use ruff_diagnostics::Diagnostic;
|
||||
use ruff_python_ast::source_code::{Indexer, Locator, Stylist};
|
||||
use ruff_python_trivia::UniversalNewlines;
|
||||
|
||||
use crate::comments::shebang::ShebangDirective;
|
||||
use crate::registry::Rule;
|
||||
use crate::rules::flake8_copyright::rules::missing_copyright_notice;
|
||||
use crate::rules::flake8_executable::rules::{
|
||||
shebang_missing, shebang_newline, shebang_not_executable, shebang_python, shebang_whitespace,
|
||||
};
|
||||
use crate::rules::pycodestyle::rules::{
|
||||
doc_line_too_long, line_too_long, mixed_spaces_and_tabs, no_newline_at_end_of_file,
|
||||
tab_indentation, trailing_whitespace,
|
||||
|
@ -22,7 +16,6 @@ use crate::rules::pyupgrade::rules::unnecessary_coding_comment;
|
|||
use crate::settings::Settings;
|
||||
|
||||
pub(crate) fn check_physical_lines(
|
||||
path: &Path,
|
||||
locator: &Locator,
|
||||
stylist: &Stylist,
|
||||
indexer: &Indexer,
|
||||
|
@ -30,13 +23,7 @@ pub(crate) fn check_physical_lines(
|
|||
settings: &Settings,
|
||||
) -> Vec<Diagnostic> {
|
||||
let mut diagnostics: Vec<Diagnostic> = vec![];
|
||||
let mut has_any_shebang = false;
|
||||
|
||||
let enforce_shebang_not_executable = settings.rules.enabled(Rule::ShebangNotExecutable);
|
||||
let enforce_shebang_missing = settings.rules.enabled(Rule::ShebangMissingExecutableFile);
|
||||
let enforce_shebang_whitespace = settings.rules.enabled(Rule::ShebangLeadingWhitespace);
|
||||
let enforce_shebang_newline = settings.rules.enabled(Rule::ShebangNotFirstLine);
|
||||
let enforce_shebang_python = settings.rules.enabled(Rule::ShebangMissingPython);
|
||||
let enforce_doc_line_too_long = settings.rules.enabled(Rule::DocLineTooLong);
|
||||
let enforce_line_too_long = settings.rules.enabled(Rule::LineTooLong);
|
||||
let enforce_no_newline_at_end_of_file = settings.rules.enabled(Rule::MissingNewlineAtEndOfFile);
|
||||
|
@ -50,7 +37,6 @@ pub(crate) fn check_physical_lines(
|
|||
let enforce_copyright_notice = settings.rules.enabled(Rule::MissingCopyrightNotice);
|
||||
|
||||
let fix_unnecessary_coding_comment = settings.rules.should_fix(Rule::UTF8EncodingDeclaration);
|
||||
let fix_shebang_whitespace = settings.rules.should_fix(Rule::ShebangLeadingWhitespace);
|
||||
|
||||
let mut commented_lines_iter = indexer.comment_ranges().iter().peekable();
|
||||
let mut doc_lines_iter = doc_lines.iter().peekable();
|
||||
|
@ -69,43 +55,6 @@ pub(crate) fn check_physical_lines(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if enforce_shebang_missing
|
||||
|| enforce_shebang_not_executable
|
||||
|| enforce_shebang_whitespace
|
||||
|| enforce_shebang_newline
|
||||
|| enforce_shebang_python
|
||||
{
|
||||
if let Some(shebang) = ShebangDirective::try_extract(&line) {
|
||||
has_any_shebang = true;
|
||||
if enforce_shebang_not_executable {
|
||||
if let Some(diagnostic) =
|
||||
shebang_not_executable(path, line.range(), &shebang)
|
||||
{
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
if enforce_shebang_whitespace {
|
||||
if let Some(diagnostic) =
|
||||
shebang_whitespace(line.range(), &shebang, fix_shebang_whitespace)
|
||||
{
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
if enforce_shebang_newline {
|
||||
if let Some(diagnostic) =
|
||||
shebang_newline(line.range(), &shebang, index == 0)
|
||||
{
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
if enforce_shebang_python {
|
||||
if let Some(diagnostic) = shebang_python(line.range(), &shebang) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while doc_lines_iter
|
||||
|
@ -158,12 +107,6 @@ pub(crate) fn check_physical_lines(
|
|||
}
|
||||
}
|
||||
|
||||
if enforce_shebang_missing && !has_any_shebang {
|
||||
if let Some(diagnostic) = shebang_missing(path) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
if enforce_copyright_notice {
|
||||
if let Some(diagnostic) = missing_copyright_notice(locator, settings) {
|
||||
diagnostics.push(diagnostic);
|
||||
|
@ -175,8 +118,6 @@ pub(crate) fn check_physical_lines(
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::path::Path;
|
||||
|
||||
use rustpython_parser::lexer::lex;
|
||||
use rustpython_parser::Mode;
|
||||
|
||||
|
@ -198,7 +139,6 @@ mod tests {
|
|||
|
||||
let check_with_max_line_length = |line_length: LineLength| {
|
||||
check_physical_lines(
|
||||
Path::new("foo.py"),
|
||||
&locator,
|
||||
&stylist,
|
||||
&indexer,
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
//! Lint rules based on token traversal.
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
use rustpython_parser::lexer::LexResult;
|
||||
use rustpython_parser::Tok;
|
||||
|
||||
|
@ -11,15 +13,16 @@ use crate::lex::docstring_detection::StateMachine;
|
|||
use crate::registry::{AsRule, Rule};
|
||||
use crate::rules::ruff::rules::Context;
|
||||
use crate::rules::{
|
||||
eradicate, flake8_commas, flake8_fixme, flake8_implicit_str_concat, flake8_pyi, flake8_quotes,
|
||||
flake8_todos, pycodestyle, pygrep_hooks, pylint, pyupgrade, ruff,
|
||||
eradicate, flake8_commas, flake8_executable, flake8_fixme, flake8_implicit_str_concat,
|
||||
flake8_pyi, flake8_quotes, flake8_todos, pycodestyle, pygrep_hooks, pylint, pyupgrade, ruff,
|
||||
};
|
||||
use crate::settings::Settings;
|
||||
|
||||
pub(crate) fn check_tokens(
|
||||
tokens: &[LexResult],
|
||||
path: &Path,
|
||||
locator: &Locator,
|
||||
indexer: &Indexer,
|
||||
tokens: &[LexResult],
|
||||
settings: &Settings,
|
||||
is_stub: bool,
|
||||
) -> Vec<Diagnostic> {
|
||||
|
@ -143,6 +146,16 @@ pub(crate) fn check_tokens(
|
|||
flake8_pyi::rules::type_comment_in_stub(&mut diagnostics, locator, indexer);
|
||||
}
|
||||
|
||||
if settings.rules.any_enabled(&[
|
||||
Rule::ShebangNotExecutable,
|
||||
Rule::ShebangMissingExecutableFile,
|
||||
Rule::ShebangLeadingWhitespace,
|
||||
Rule::ShebangNotFirstLine,
|
||||
Rule::ShebangMissingPython,
|
||||
]) {
|
||||
flake8_executable::rules::from_tokens(tokens, path, locator, settings, &mut diagnostics);
|
||||
}
|
||||
|
||||
if settings.rules.any_enabled(&[
|
||||
Rule::InvalidTodoTag,
|
||||
Rule::MissingTodoAuthor,
|
||||
|
|
|
@ -1,15 +1,10 @@
|
|||
use ruff_python_trivia::{is_python_whitespace, Cursor};
|
||||
use ruff_text_size::{TextLen, TextSize};
|
||||
use std::ops::Deref;
|
||||
|
||||
use ruff_python_trivia::Cursor;
|
||||
|
||||
/// A shebang directive (e.g., `#!/usr/bin/env python3`).
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub(crate) struct ShebangDirective<'a> {
|
||||
/// The offset of the directive contents (e.g., `/usr/bin/env python3`) from the start of the
|
||||
/// line.
|
||||
pub(crate) offset: TextSize,
|
||||
/// The contents of the directive (e.g., `"/usr/bin/env python3"`).
|
||||
pub(crate) contents: &'a str,
|
||||
}
|
||||
pub(crate) struct ShebangDirective<'a>(&'a str);
|
||||
|
||||
impl<'a> ShebangDirective<'a> {
|
||||
/// Parse a shebang directive from a line, or return `None` if the line does not contain a
|
||||
|
@ -17,9 +12,6 @@ impl<'a> ShebangDirective<'a> {
|
|||
pub(crate) fn try_extract(line: &'a str) -> Option<Self> {
|
||||
let mut cursor = Cursor::new(line);
|
||||
|
||||
// Trim whitespace.
|
||||
cursor.eat_while(is_python_whitespace);
|
||||
|
||||
// Trim the `#!` prefix.
|
||||
if !cursor.eat_char('#') {
|
||||
return None;
|
||||
|
@ -28,10 +20,15 @@ impl<'a> ShebangDirective<'a> {
|
|||
return None;
|
||||
}
|
||||
|
||||
Some(Self {
|
||||
offset: line.text_len() - cursor.text_len(),
|
||||
contents: cursor.chars().as_str(),
|
||||
})
|
||||
Some(Self(cursor.chars().as_str()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for ShebangDirective<'_> {
|
||||
type Target = str;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -59,6 +56,12 @@ mod tests {
|
|||
assert_debug_snapshot!(ShebangDirective::try_extract(source));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shebang_match_trailing_comment() {
|
||||
let source = "#!/usr/bin/env python # trailing comment";
|
||||
assert_debug_snapshot!(ShebangDirective::try_extract(source));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shebang_leading_space() {
|
||||
let source = " #!/usr/bin/env python";
|
||||
|
|
|
@ -2,9 +2,4 @@
|
|||
source: crates/ruff/src/comments/shebang.rs
|
||||
expression: "ShebangDirective::try_extract(source)"
|
||||
---
|
||||
Some(
|
||||
ShebangDirective {
|
||||
offset: 4,
|
||||
contents: "/usr/bin/env python",
|
||||
},
|
||||
)
|
||||
None
|
||||
|
|
|
@ -3,8 +3,7 @@ source: crates/ruff/src/comments/shebang.rs
|
|||
expression: "ShebangDirective::try_extract(source)"
|
||||
---
|
||||
Some(
|
||||
ShebangDirective {
|
||||
offset: 2,
|
||||
contents: "/usr/bin/env python",
|
||||
},
|
||||
ShebangDirective(
|
||||
"/usr/bin/env python",
|
||||
),
|
||||
)
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
source: crates/ruff/src/comments/shebang.rs
|
||||
expression: "ShebangDirective::try_extract(source)"
|
||||
---
|
||||
Some(
|
||||
ShebangDirective(
|
||||
"/usr/bin/env python # trailing comment",
|
||||
),
|
||||
)
|
|
@ -100,7 +100,9 @@ pub fn check_path(
|
|||
.any(|rule_code| rule_code.lint_source().is_tokens())
|
||||
{
|
||||
let is_stub = is_python_stub_file(path);
|
||||
diagnostics.extend(check_tokens(locator, indexer, &tokens, settings, is_stub));
|
||||
diagnostics.extend(check_tokens(
|
||||
&tokens, path, locator, indexer, settings, is_stub,
|
||||
));
|
||||
}
|
||||
|
||||
// Run the filesystem-based rules.
|
||||
|
@ -193,7 +195,7 @@ pub fn check_path(
|
|||
.any(|rule_code| rule_code.lint_source().is_physical_lines())
|
||||
{
|
||||
diagnostics.extend(check_physical_lines(
|
||||
path, locator, stylist, indexer, &doc_lines, settings,
|
||||
locator, stylist, indexer, &doc_lines, settings,
|
||||
));
|
||||
}
|
||||
|
||||
|
|
|
@ -238,22 +238,16 @@ impl Rule {
|
|||
match self {
|
||||
Rule::InvalidPyprojectToml => LintSource::PyprojectToml,
|
||||
Rule::UnusedNOQA => LintSource::Noqa,
|
||||
|
||||
Rule::DocLineTooLong
|
||||
Rule::BidirectionalUnicode
|
||||
| Rule::BlankLineWithWhitespace
|
||||
| Rule::DocLineTooLong
|
||||
| Rule::LineTooLong
|
||||
| Rule::MixedSpacesAndTabs
|
||||
| Rule::MissingNewlineAtEndOfFile
|
||||
| Rule::UTF8EncodingDeclaration
|
||||
| Rule::ShebangMissingExecutableFile
|
||||
| Rule::ShebangNotExecutable
|
||||
| Rule::ShebangNotFirstLine
|
||||
| Rule::BidirectionalUnicode
|
||||
| Rule::ShebangMissingPython
|
||||
| Rule::ShebangLeadingWhitespace
|
||||
| Rule::TrailingWhitespace
|
||||
| Rule::TabIndentation
|
||||
| Rule::MissingCopyrightNotice
|
||||
| Rule::BlankLineWithWhitespace => LintSource::PhysicalLines,
|
||||
| Rule::MissingNewlineAtEndOfFile
|
||||
| Rule::MixedSpacesAndTabs
|
||||
| Rule::TabIndentation
|
||||
| Rule::TrailingWhitespace
|
||||
| Rule::UTF8EncodingDeclaration => LintSource::PhysicalLines,
|
||||
Rule::AmbiguousUnicodeCharacterComment
|
||||
| Rule::AmbiguousUnicodeCharacterDocstring
|
||||
| Rule::AmbiguousUnicodeCharacterString
|
||||
|
@ -264,33 +258,38 @@ impl Rule {
|
|||
| Rule::BlanketNOQA
|
||||
| Rule::BlanketTypeIgnore
|
||||
| Rule::CommentedOutCode
|
||||
| Rule::MultiLineImplicitStringConcatenation
|
||||
| Rule::ExtraneousParentheses
|
||||
| Rule::InvalidCharacterBackspace
|
||||
| Rule::InvalidCharacterSub
|
||||
| Rule::InvalidCharacterEsc
|
||||
| Rule::InvalidCharacterNul
|
||||
| Rule::InvalidCharacterSub
|
||||
| Rule::InvalidCharacterZeroWidthSpace
|
||||
| Rule::ExtraneousParentheses
|
||||
| Rule::InvalidEscapeSequence
|
||||
| Rule::SingleLineImplicitStringConcatenation
|
||||
| Rule::MissingTrailingComma
|
||||
| Rule::TrailingCommaOnBareTuple
|
||||
| Rule::MultipleStatementsOnOneLineColon
|
||||
| Rule::UselessSemicolon
|
||||
| Rule::MultipleStatementsOnOneLineSemicolon
|
||||
| Rule::ProhibitedTrailingComma
|
||||
| Rule::TypeCommentInStub
|
||||
| Rule::InvalidTodoTag
|
||||
| Rule::MissingTodoAuthor
|
||||
| Rule::MissingTodoLink
|
||||
| Rule::MissingTodoColon
|
||||
| Rule::MissingTodoDescription
|
||||
| Rule::InvalidTodoCapitalization
|
||||
| Rule::MissingSpaceAfterTodoColon
|
||||
| Rule::InvalidTodoTag
|
||||
| Rule::LineContainsFixme
|
||||
| Rule::LineContainsHack
|
||||
| Rule::LineContainsTodo
|
||||
| Rule::LineContainsXxx => LintSource::Tokens,
|
||||
| Rule::LineContainsXxx
|
||||
| Rule::MissingSpaceAfterTodoColon
|
||||
| Rule::MissingTodoAuthor
|
||||
| Rule::MissingTodoColon
|
||||
| Rule::MissingTodoDescription
|
||||
| Rule::MissingTodoLink
|
||||
| Rule::MissingTrailingComma
|
||||
| Rule::MultiLineImplicitStringConcatenation
|
||||
| Rule::MultipleStatementsOnOneLineColon
|
||||
| Rule::MultipleStatementsOnOneLineSemicolon
|
||||
| Rule::ProhibitedTrailingComma
|
||||
| Rule::ShebangLeadingWhitespace
|
||||
| Rule::ShebangMissingExecutableFile
|
||||
| Rule::ShebangMissingPython
|
||||
| Rule::ShebangNotExecutable
|
||||
| Rule::ShebangNotFirstLine
|
||||
| Rule::SingleLineImplicitStringConcatenation
|
||||
| Rule::TrailingCommaOnBareTuple
|
||||
| Rule::TypeCommentInStub
|
||||
| Rule::UselessSemicolon => LintSource::Tokens,
|
||||
Rule::IOError => LintSource::Io,
|
||||
Rule::UnsortedImports | Rule::MissingRequiredImport => LintSource::Imports,
|
||||
Rule::ImplicitNamespacePackage | Rule::InvalidModuleName => LintSource::Filesystem,
|
||||
|
|
|
@ -24,6 +24,7 @@ mod tests {
|
|||
#[test_case(Path::new("EXE004_1.py"))]
|
||||
#[test_case(Path::new("EXE004_2.py"))]
|
||||
#[test_case(Path::new("EXE004_3.py"))]
|
||||
#[test_case(Path::new("EXE004_4.py"))]
|
||||
#[test_case(Path::new("EXE005_1.py"))]
|
||||
#[test_case(Path::new("EXE005_2.py"))]
|
||||
#[test_case(Path::new("EXE005_3.py"))]
|
||||
|
|
|
@ -1,11 +1,60 @@
|
|||
pub(crate) use shebang_missing::*;
|
||||
pub(crate) use shebang_newline::*;
|
||||
pub(crate) use shebang_not_executable::*;
|
||||
pub(crate) use shebang_python::*;
|
||||
pub(crate) use shebang_whitespace::*;
|
||||
use std::path::Path;
|
||||
|
||||
mod shebang_missing;
|
||||
mod shebang_newline;
|
||||
use rustpython_parser::lexer::LexResult;
|
||||
use rustpython_parser::Tok;
|
||||
|
||||
use ruff_diagnostics::Diagnostic;
|
||||
use ruff_python_ast::source_code::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::comments::shebang::ShebangDirective;
|
||||
use crate::settings::Settings;
|
||||
|
||||
mod shebang_leading_whitespace;
|
||||
mod shebang_missing_executable_file;
|
||||
mod shebang_missing_python;
|
||||
mod shebang_not_executable;
|
||||
mod shebang_python;
|
||||
mod shebang_whitespace;
|
||||
mod shebang_not_first_line;
|
||||
|
||||
pub(crate) fn from_tokens(
|
||||
tokens: &[LexResult],
|
||||
path: &Path,
|
||||
locator: &Locator,
|
||||
settings: &Settings,
|
||||
diagnostics: &mut Vec<Diagnostic>,
|
||||
) {
|
||||
let mut has_any_shebang = false;
|
||||
for (tok, range) in tokens.iter().flatten() {
|
||||
if let Tok::Comment(comment) = tok {
|
||||
if let Some(shebang) = ShebangDirective::try_extract(comment) {
|
||||
has_any_shebang = true;
|
||||
|
||||
if let Some(diagnostic) = shebang_missing_python(*range, &shebang) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
if let Some(diagnostic) = shebang_not_executable(path, *range) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
if let Some(diagnostic) = shebang_leading_whitespace(*range, locator, settings) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
if let Some(diagnostic) = shebang_not_first_line(*range, locator) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !has_any_shebang {
|
||||
if let Some(diagnostic) = shebang_missing_executable_file(path) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
use std::ops::Sub;
|
||||
|
||||
use ruff_text_size::{TextRange, TextSize};
|
||||
|
||||
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit, Fix};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::source_code::Locator;
|
||||
use ruff_python_trivia::is_python_whitespace;
|
||||
|
||||
use crate::comments::shebang::ShebangDirective;
|
||||
use crate::registry::AsRule;
|
||||
use crate::settings::Settings;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for whitespace before a shebang directive.
|
||||
|
@ -46,31 +47,29 @@ impl AlwaysAutofixableViolation for ShebangLeadingWhitespace {
|
|||
}
|
||||
|
||||
/// EXE004
|
||||
pub(crate) fn shebang_whitespace(
|
||||
pub(crate) fn shebang_leading_whitespace(
|
||||
range: TextRange,
|
||||
shebang: &ShebangDirective,
|
||||
autofix: bool,
|
||||
locator: &Locator,
|
||||
settings: &Settings,
|
||||
) -> Option<Diagnostic> {
|
||||
let ShebangDirective {
|
||||
offset,
|
||||
contents: _,
|
||||
} = shebang;
|
||||
|
||||
if *offset > TextSize::from(2) {
|
||||
let leading_space_start = range.start();
|
||||
let leading_space_len = offset.sub(TextSize::new(2));
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
ShebangLeadingWhitespace,
|
||||
TextRange::at(leading_space_start, leading_space_len),
|
||||
);
|
||||
if autofix {
|
||||
diagnostic.set_fix(Fix::automatic(Edit::range_deletion(TextRange::at(
|
||||
leading_space_start,
|
||||
leading_space_len,
|
||||
))));
|
||||
}
|
||||
Some(diagnostic)
|
||||
} else {
|
||||
None
|
||||
// If the shebang is at the beginning of the file, abort.
|
||||
if range.start() == TextSize::from(0) {
|
||||
return None;
|
||||
}
|
||||
|
||||
// If the entire prefix _isn't_ whitespace, abort (this is handled by EXE005).
|
||||
if !locator
|
||||
.up_to(range.start())
|
||||
.chars()
|
||||
.all(|c| is_python_whitespace(c) || matches!(c, '\r' | '\n'))
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
let prefix = TextRange::up_to(range.start());
|
||||
let mut diagnostic = Diagnostic::new(ShebangLeadingWhitespace, prefix);
|
||||
if settings.rules.should_fix(diagnostic.kind.rule()) {
|
||||
diagnostic.set_fix(Fix::automatic(Edit::range_deletion(prefix)));
|
||||
}
|
||||
Some(diagnostic)
|
||||
}
|
|
@ -2,9 +2,8 @@
|
|||
|
||||
use std::path::Path;
|
||||
|
||||
use wsl;
|
||||
|
||||
use ruff_text_size::TextRange;
|
||||
use wsl;
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
|
@ -43,20 +42,22 @@ impl Violation for ShebangMissingExecutableFile {
|
|||
|
||||
/// EXE002
|
||||
#[cfg(target_family = "unix")]
|
||||
pub(crate) fn shebang_missing(filepath: &Path) -> Option<Diagnostic> {
|
||||
pub(crate) fn shebang_missing_executable_file(filepath: &Path) -> Option<Diagnostic> {
|
||||
// WSL supports Windows file systems, which do not have executable bits.
|
||||
// Instead, everything is executable. Therefore, we skip this rule on WSL.
|
||||
if wsl::is_wsl() {
|
||||
return None;
|
||||
}
|
||||
if let Ok(true) = is_executable(filepath) {
|
||||
let diagnostic = Diagnostic::new(ShebangMissingExecutableFile, TextRange::default());
|
||||
return Some(diagnostic);
|
||||
return Some(Diagnostic::new(
|
||||
ShebangMissingExecutableFile,
|
||||
TextRange::default(),
|
||||
));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(not(target_family = "unix"))]
|
||||
pub(crate) fn shebang_missing(_filepath: &Path) -> Option<Diagnostic> {
|
||||
pub(crate) fn shebang_missing_executable_file(_filepath: &Path) -> Option<Diagnostic> {
|
||||
None
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
use ruff_text_size::{TextLen, TextRange, TextSize};
|
||||
use ruff_text_size::TextRange;
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
|
@ -41,15 +41,13 @@ impl Violation for ShebangMissingPython {
|
|||
}
|
||||
|
||||
/// EXE003
|
||||
pub(crate) fn shebang_python(range: TextRange, shebang: &ShebangDirective) -> Option<Diagnostic> {
|
||||
let ShebangDirective { offset, contents } = shebang;
|
||||
|
||||
if contents.contains("python") || contents.contains("pytest") {
|
||||
None
|
||||
} else {
|
||||
Some(Diagnostic::new(
|
||||
ShebangMissingPython,
|
||||
TextRange::at(range.start() + offset, contents.text_len()).sub_start(TextSize::from(2)),
|
||||
))
|
||||
pub(crate) fn shebang_missing_python(
|
||||
range: TextRange,
|
||||
shebang: &ShebangDirective,
|
||||
) -> Option<Diagnostic> {
|
||||
if shebang.contains("python") || shebang.contains("pytest") {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(Diagnostic::new(ShebangMissingPython, range))
|
||||
}
|
|
@ -2,15 +2,12 @@
|
|||
|
||||
use std::path::Path;
|
||||
|
||||
use ruff_text_size::TextRange;
|
||||
use wsl;
|
||||
|
||||
use ruff_text_size::{TextLen, TextRange, TextSize};
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
|
||||
use crate::comments::shebang::ShebangDirective;
|
||||
use crate::registry::AsRule;
|
||||
#[cfg(target_family = "unix")]
|
||||
use crate::rules::flake8_executable::helpers::is_executable;
|
||||
|
||||
|
@ -45,34 +42,21 @@ impl Violation for ShebangNotExecutable {
|
|||
|
||||
/// EXE001
|
||||
#[cfg(target_family = "unix")]
|
||||
pub(crate) fn shebang_not_executable(
|
||||
filepath: &Path,
|
||||
range: TextRange,
|
||||
shebang: &ShebangDirective,
|
||||
) -> Option<Diagnostic> {
|
||||
pub(crate) fn shebang_not_executable(filepath: &Path, range: TextRange) -> Option<Diagnostic> {
|
||||
// WSL supports Windows file systems, which do not have executable bits.
|
||||
// Instead, everything is executable. Therefore, we skip this rule on WSL.
|
||||
if wsl::is_wsl() {
|
||||
return None;
|
||||
}
|
||||
let ShebangDirective { offset, contents } = shebang;
|
||||
|
||||
if let Ok(false) = is_executable(filepath) {
|
||||
let diagnostic = Diagnostic::new(
|
||||
ShebangNotExecutable,
|
||||
TextRange::at(range.start() + offset, contents.text_len()),
|
||||
);
|
||||
return Some(diagnostic);
|
||||
return Some(Diagnostic::new(ShebangNotExecutable, range));
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(not(target_family = "unix"))]
|
||||
pub(crate) fn shebang_not_executable(
|
||||
_filepath: &Path,
|
||||
_range: TextRange,
|
||||
_shebang: &ShebangDirective,
|
||||
) -> Option<Diagnostic> {
|
||||
pub(crate) fn shebang_not_executable(_filepath: &Path, _range: TextRange) -> Option<Diagnostic> {
|
||||
None
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use ruff_text_size::{TextLen, TextRange};
|
||||
use ruff_text_size::{TextRange, TextSize};
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
|
||||
use crate::comments::shebang::ShebangDirective;
|
||||
use ruff_python_ast::source_code::Locator;
|
||||
use ruff_python_trivia::is_python_whitespace;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for a shebang directive that is not at the beginning of the file.
|
||||
|
@ -42,19 +42,20 @@ impl Violation for ShebangNotFirstLine {
|
|||
}
|
||||
|
||||
/// EXE005
|
||||
pub(crate) fn shebang_newline(
|
||||
range: TextRange,
|
||||
shebang: &ShebangDirective,
|
||||
first_line: bool,
|
||||
) -> Option<Diagnostic> {
|
||||
let ShebangDirective { offset, contents } = shebang;
|
||||
|
||||
if first_line {
|
||||
None
|
||||
} else {
|
||||
Some(Diagnostic::new(
|
||||
ShebangNotFirstLine,
|
||||
TextRange::at(range.start() + offset, contents.text_len()),
|
||||
))
|
||||
pub(crate) fn shebang_not_first_line(range: TextRange, locator: &Locator) -> Option<Diagnostic> {
|
||||
// If the shebang is at the beginning of the file, abort.
|
||||
if range.start() == TextSize::from(0) {
|
||||
return None;
|
||||
}
|
||||
|
||||
// If the entire prefix is whitespace, abort (this is handled by EXE004).
|
||||
if locator
|
||||
.up_to(range.start())
|
||||
.chars()
|
||||
.all(|c| is_python_whitespace(c) || matches!(c, '\r' | '\n'))
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(Diagnostic::new(ShebangNotFirstLine, range))
|
||||
}
|
|
@ -1,10 +1,10 @@
|
|||
---
|
||||
source: crates/ruff/src/rules/flake8_executable/mod.rs
|
||||
---
|
||||
EXE001_1.py:1:3: EXE001 Shebang is present but file is not executable
|
||||
EXE001_1.py:1:1: EXE001 Shebang is present but file is not executable
|
||||
|
|
||||
1 | #!/usr/bin/python
|
||||
| ^^^^^^^^^^^^^^^ EXE001
|
||||
| ^^^^^^^^^^^^^^^^^ EXE001
|
||||
2 |
|
||||
3 | if __name__ == '__main__':
|
||||
|
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
---
|
||||
source: crates/ruff/src/rules/flake8_executable/mod.rs
|
||||
---
|
||||
EXE004_3.py:1:1: EXE002 The file is executable but no shebang is present
|
||||
EXE004_3.py:2:7: EXE005 Shebang should be at the beginning of the file
|
||||
|
|
||||
1 |
|
||||
| EXE002
|
||||
2 | pass #!/usr/bin/env python
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ EXE005
|
||||
|
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
---
|
||||
source: crates/ruff/src/rules/flake8_executable/mod.rs
|
||||
---
|
||||
EXE004_4.py:1:1: EXE004 [*] Avoid whitespace before shebang
|
||||
|
|
||||
1 | /
|
||||
2 | | #!/usr/bin/env python
|
||||
| |____^ EXE004
|
||||
|
|
||||
= help: Remove whitespace before shebang
|
||||
|
||||
ℹ Fix
|
||||
1 |-
|
||||
2 |- #!/usr/bin/env python
|
||||
1 |+#!/usr/bin/env python
|
||||
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
---
|
||||
source: crates/ruff/src/rules/flake8_executable/mod.rs
|
||||
---
|
||||
EXE005_1.py:3:3: EXE005 Shebang should be at the beginning of the file
|
||||
EXE005_1.py:3:1: EXE005 Shebang should be at the beginning of the file
|
||||
|
|
||||
2 | # A python comment
|
||||
3 | #!/usr/bin/python
|
||||
| ^^^^^^^^^^^^^^^ EXE005
|
||||
| ^^^^^^^^^^^^^^^^^ EXE005
|
||||
|
|
||||
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
---
|
||||
source: crates/ruff/src/rules/flake8_executable/mod.rs
|
||||
---
|
||||
EXE005_2.py:4:3: EXE005 Shebang should be at the beginning of the file
|
||||
EXE005_2.py:4:1: EXE005 Shebang should be at the beginning of the file
|
||||
|
|
||||
3 | # A python comment
|
||||
4 | #!/usr/bin/python
|
||||
| ^^^^^^^^^^^^^^^ EXE005
|
||||
| ^^^^^^^^^^^^^^^^^ EXE005
|
||||
|
|
||||
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
---
|
||||
source: crates/ruff/src/rules/flake8_executable/mod.rs
|
||||
---
|
||||
EXE005_3.py:6:3: EXE005 Shebang should be at the beginning of the file
|
||||
EXE005_3.py:6:1: EXE005 Shebang should be at the beginning of the file
|
||||
|
|
||||
4 | """
|
||||
5 | # A python comment
|
||||
6 | #!/usr/bin/python
|
||||
| ^^^^^^^^^^^^^^^ EXE005
|
||||
| ^^^^^^^^^^^^^^^^^ EXE005
|
||||
|
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue