diff --git a/Cargo.lock b/Cargo.lock index eea8adc90de4f..8a97566253d52 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1043,6 +1043,29 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f35c735096c0293d313e8f2a641627472b83d01b937177fe76e5e2708d31e0d" +[[package]] +name = "lazy-regex" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff63c423c68ea6814b7da9e88ce585f793c87ddd9e78f646970891769c8235d4" +dependencies = [ + "lazy-regex-proc_macros", + "once_cell", + "regex", +] + +[[package]] +name = "lazy-regex-proc_macros" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8edfc11b8f56ce85e207e62ea21557cfa09bb24a8f6b04ae181b086ff8611c22" +dependencies = [ + "proc-macro2", + "quote", + "regex", + "syn 1.0.109", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -1740,6 +1763,7 @@ dependencies = [ "insta", "is-macro", "itertools", + "lazy-regex", "libcst", "log", "natord", diff --git a/LICENSE b/LICENSE index 534e3357426bf..ce66578978d22 100644 --- a/LICENSE +++ b/LICENSE @@ -1176,6 +1176,8 @@ are: - flake8-django, licensed under the GPL license. +- flake8-copyright, licensed under the GPL license. + - rust-analyzer/text-size, licensed under the MIT license: """ Permission is hereby granted, free of charge, to any diff --git a/README.md b/README.md index 4b6a47b060904..7b6527b039cfa 100644 --- a/README.md +++ b/README.md @@ -257,6 +257,7 @@ quality tools, including: - [flake8-builtins](https://pypi.org/project/flake8-builtins/) - [flake8-commas](https://pypi.org/project/flake8-commas/) - [flake8-comprehensions](https://pypi.org/project/flake8-comprehensions/) +- [flake8-copyright](https://pypi.org/project/flake8-copyright/) - [flake8-datetimez](https://pypi.org/project/flake8-datetimez/) - [flake8-debugger](https://pypi.org/project/flake8-debugger/) - [flake8-django](https://pypi.org/project/flake8-django/) diff --git a/crates/ruff/Cargo.toml b/crates/ruff/Cargo.toml index efec4f8007dde..7d9f2d41d5af3 100644 --- a/crates/ruff/Cargo.toml +++ b/crates/ruff/Cargo.toml @@ -37,6 +37,7 @@ ignore = { workspace = true } imperative = { version = "1.0.4" } is-macro = { workspace = true } itertools = { workspace = true } +lazy-regex = { version = "2.5.0" } libcst = { workspace = true } log = { workspace = true } natord = { version = "1.0.9" } diff --git a/crates/ruff/resources/test/fixtures/flake8_copyright/C801.py b/crates/ruff/resources/test/fixtures/flake8_copyright/C801.py new file mode 100644 index 0000000000000..c46bff66df4be --- /dev/null +++ b/crates/ruff/resources/test/fixtures/flake8_copyright/C801.py @@ -0,0 +1,3 @@ +# Copyright (C) 2023 author + +import os \ No newline at end of file diff --git a/crates/ruff/resources/test/fixtures/flake8_copyright/C801_custom_author_fail.py b/crates/ruff/resources/test/fixtures/flake8_copyright/C801_custom_author_fail.py new file mode 100644 index 0000000000000..e832032d08988 --- /dev/null +++ b/crates/ruff/resources/test/fixtures/flake8_copyright/C801_custom_author_fail.py @@ -0,0 +1,3 @@ +# Copyright 2023 (c) rufffff + +import os \ No newline at end of file diff --git a/crates/ruff/resources/test/fixtures/flake8_copyright/C801_custom_regexp_pass.py b/crates/ruff/resources/test/fixtures/flake8_copyright/C801_custom_regexp_pass.py new file mode 100644 index 0000000000000..876ec69e3b8c8 --- /dev/null +++ b/crates/ruff/resources/test/fixtures/flake8_copyright/C801_custom_regexp_pass.py @@ -0,0 +1,5 @@ +# filling +# filling +# filling + +# Copyright 2023 (c) ruff \ No newline at end of file diff --git a/crates/ruff/resources/test/fixtures/flake8_copyright/C801_default_fail.py b/crates/ruff/resources/test/fixtures/flake8_copyright/C801_default_fail.py new file mode 100644 index 0000000000000..02bf84f188607 --- /dev/null +++ b/crates/ruff/resources/test/fixtures/flake8_copyright/C801_default_fail.py @@ -0,0 +1,3 @@ +import os + +# Copyright (c) 2023 ruff \ No newline at end of file diff --git a/crates/ruff/src/checkers/physical_lines.rs b/crates/ruff/src/checkers/physical_lines.rs index 47b647677d096..119f2f2fb68e8 100644 --- a/crates/ruff/src/checkers/physical_lines.rs +++ b/crates/ruff/src/checkers/physical_lines.rs @@ -6,8 +6,10 @@ use std::path::Path; use ruff_diagnostics::Diagnostic; use ruff_python_ast::newlines::StrExt; use ruff_python_ast::source_code::{Indexer, Locator, Stylist}; +use ruff_text_size::TextRange; use crate::registry::Rule; +use crate::rules::flake8_copyright::rules::{copyright_header_absent, HeaderLacksCopyright}; use crate::rules::flake8_executable::helpers::{extract_shebang, ShebangDirective}; use crate::rules::flake8_executable::rules::{ shebang_missing, shebang_newline, shebang_not_executable, shebang_python, shebang_whitespace, @@ -49,6 +51,10 @@ pub(crate) fn check_physical_lines( let enforce_blank_line_contains_whitespace = settings.rules.enabled(Rule::BlankLineWithWhitespace); let enforce_tab_indentation = settings.rules.enabled(Rule::TabIndentation); + let enforce_copyright_header = settings.rules.enabled(Rule::HeaderLacksCopyright); + + let mut checked_copyright_header_absent = false; + let mut chars_before_copyright_header: i64 = 0; let fix_unnecessary_coding_comment = settings.rules.should_fix(Rule::UTF8EncodingDeclaration); let fix_shebang_whitespace = settings.rules.should_fix(Rule::ShebangLeadingWhitespace); @@ -154,6 +160,24 @@ pub(crate) fn check_physical_lines( diagnostics.push(diagnostic); } } + if enforce_copyright_header && !checked_copyright_header_absent { + let missing_copyright_header = + copyright_header_absent(&line, settings, chars_before_copyright_header); + + match missing_copyright_header { + Some(false) => checked_copyright_header_absent = true, + Some(true) => { + let diagnostic = Diagnostic::new( + HeaderLacksCopyright, + TextRange::new(line.start(), line.end()), + ); + diagnostics.push(diagnostic); + checked_copyright_header_absent = true; + } + None => (), + } + chars_before_copyright_header += line.len() as i64; + } } if enforce_no_newline_at_end_of_file { diff --git a/crates/ruff/src/codes.rs b/crates/ruff/src/codes.rs index 6295daafd8e1d..22e17af8476c6 100644 --- a/crates/ruff/src/codes.rs +++ b/crates/ruff/src/codes.rs @@ -360,6 +360,9 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { (Flake8Simplify, "401") => (RuleGroup::Unspecified, Rule::IfElseBlockInsteadOfDictGet), (Flake8Simplify, "910") => (RuleGroup::Unspecified, Rule::DictGetWithNoneDefault), + // flake8-copyright + (Flake8Copyright, "1") => (RuleGroup::Unspecified, Rule::HeaderLacksCopyright), + // pyupgrade (Pyupgrade, "001") => (RuleGroup::Unspecified, Rule::UselessMetaclassType), (Pyupgrade, "003") => (RuleGroup::Unspecified, Rule::TypeOfPrimitive), diff --git a/crates/ruff/src/registry.rs b/crates/ruff/src/registry.rs index 367d4c588d092..ce9154ef8bfd5 100644 --- a/crates/ruff/src/registry.rs +++ b/crates/ruff/src/registry.rs @@ -306,6 +306,8 @@ ruff_macros::register_rules!( rules::flake8_simplify::rules::YodaConditions, rules::flake8_simplify::rules::IfElseBlockInsteadOfDictGet, rules::flake8_simplify::rules::DictGetWithNoneDefault, + // flake8-copyright + rules::flake8_copyright::rules::HeaderLacksCopyright, // pyupgrade rules::pyupgrade::rules::UselessMetaclassType, rules::pyupgrade::rules::TypeOfPrimitive, @@ -736,6 +738,9 @@ pub enum Linter { #[prefix = "COM"] Flake8Commas, /// [flake8-comprehensions](https://pypi.org/project/flake8-comprehensions/) + #[prefix = "C80"] + Flake8Copyright, + /// [flake8-copyright](https://pypi.org/project/flake8-copyright/) #[prefix = "C4"] Flake8Comprehensions, /// [flake8-datetimez](https://pypi.org/project/flake8-datetimez/) @@ -913,6 +918,7 @@ impl Rule { | Rule::ShebangLeadingWhitespace | Rule::TrailingWhitespace | Rule::TabIndentation + | Rule::HeaderLacksCopyright | Rule::BlankLineWithWhitespace => LintSource::PhysicalLines, Rule::AmbiguousUnicodeCharacterComment | Rule::AmbiguousUnicodeCharacterDocstring diff --git a/crates/ruff/src/rules/flake8_copyright/mod.rs b/crates/ruff/src/rules/flake8_copyright/mod.rs new file mode 100644 index 0000000000000..9064787a55ca1 --- /dev/null +++ b/crates/ruff/src/rules/flake8_copyright/mod.rs @@ -0,0 +1,69 @@ +//! Rules from [flake8-copyright](https://github.com/savoirfairelinux/flake8-copyright). +pub(crate) mod rules; + +pub mod settings; + +#[cfg(test)] +mod tests { + use std::path::Path; + + use anyhow::Result; + + use crate::registry::Rule; + use crate::test::test_path; + use crate::{assert_messages, settings}; + + #[test] + fn test_default_fail() -> Result<()> { + let diagnostics = test_path( + Path::new("flake8_copyright/C801_default_fail.py"), + &settings::Settings::for_rules(vec![Rule::HeaderLacksCopyright]), + )?; + assert_messages!("test_default_fail", diagnostics); + Ok(()) + } + + #[test] + fn test_default_pass() -> Result<()> { + let diagnostics = test_path( + Path::new("flake8_copyright/C801.py"), + &settings::Settings::for_rules(vec![Rule::HeaderLacksCopyright]), + )?; + assert!(diagnostics.is_empty()); + Ok(()) + } + + #[test] + fn test_custom_regex_fail() -> Result<()> { + let diagnostics = test_path( + Path::new("flake8_copyright/C801_custom_author_fail.py"), + &settings::Settings { + flake8_copyright: super::settings::Settings { + copyright_author: "ruff".to_string(), + copyright_regexp: "(?i)Copyright \\d{4} \\(C\\".to_string(), + copyright_min_file_size: 0, + }, + ..settings::Settings::for_rules(vec![Rule::HeaderLacksCopyright]) + }, + )?; + assert_messages!("test_custom_regex_fail", diagnostics); + Ok(()) + } + + #[test] + fn test_custom_regex_pass() -> Result<()> { + let diagnostics = test_path( + Path::new("flake8_copyright/C801_custom_regexp_pass.py"), + &settings::Settings { + flake8_copyright: super::settings::Settings { + copyright_author: "ruff".to_string(), + copyright_regexp: "(?i)Copyright \\d{4} \\(C\\)".to_string(), + copyright_min_file_size: 300, + }, + ..settings::Settings::for_rules(vec![Rule::HeaderLacksCopyright]) + }, + )?; + assert!(diagnostics.is_empty()); + Ok(()) + } +} diff --git a/crates/ruff/src/rules/flake8_copyright/rules/copyright_header_absent.rs b/crates/ruff/src/rules/flake8_copyright/rules/copyright_header_absent.rs new file mode 100644 index 0000000000000..e25284c8d709f --- /dev/null +++ b/crates/ruff/src/rules/flake8_copyright/rules/copyright_header_absent.rs @@ -0,0 +1,47 @@ +use ruff_diagnostics::Violation; +use ruff_macros::{derive_message_formats, violation}; +use ruff_python_ast::newlines::Line; + +use crate::settings::Settings; + +use lazy_regex::Regex; +#[violation] +pub struct HeaderLacksCopyright; + +impl Violation for HeaderLacksCopyright { + #[derive_message_formats] + fn message(&self) -> String { + format!("Copyright notice not present") + } +} +/// ## What it does +/// Checks for Copyright Header to exist within at the top of a file within `copyright_min_file_size chars` +/// format Copyright (C) +/// +/// Error code C801 +pub(crate) fn copyright_header_absent( + line: &Line, + settings: &Settings, + current_char_index: i64, +) -> Option { + let copyright_regexp = format!( + "{} {}", + settings.flake8_copyright.copyright_regexp, settings.flake8_copyright.copyright_author + ); + let regex = Regex::new(copyright_regexp.trim()).unwrap(); + + let out_of_range = + current_char_index > (settings.flake8_copyright.copyright_min_file_size as i64); + let copyright_missing = regex.find(line.as_str()).is_none(); + + if copyright_missing && out_of_range { + // Missing copyright header + return Some(true); + } + if !copyright_missing { + // Found copyright header, should stop checking + return Some(false); + } + // Missing copyright header, but need to keep checking + None +} diff --git a/crates/ruff/src/rules/flake8_copyright/rules/mod.rs b/crates/ruff/src/rules/flake8_copyright/rules/mod.rs new file mode 100644 index 0000000000000..8399511a7ffe7 --- /dev/null +++ b/crates/ruff/src/rules/flake8_copyright/rules/mod.rs @@ -0,0 +1,3 @@ +pub(crate) use copyright_header_absent::{copyright_header_absent, HeaderLacksCopyright}; + +mod copyright_header_absent; diff --git a/crates/ruff/src/rules/flake8_copyright/settings.rs b/crates/ruff/src/rules/flake8_copyright/settings.rs new file mode 100644 index 0000000000000..fe94a610b391f --- /dev/null +++ b/crates/ruff/src/rules/flake8_copyright/settings.rs @@ -0,0 +1,83 @@ +// //! Settings for the `flake8-copyright` plugin. + +use serde::{Deserialize, Serialize}; + +use ruff_macros::{CacheKey, CombineOptions, ConfigurationOptions}; +#[derive( + Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions, CombineOptions, +)] +#[serde( + deny_unknown_fields, + rename_all = "kebab-case", + rename = "Flake8CopyrightOptions" +)] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] +pub struct Options { + #[option( + default = "(?i)Copyright \\(C\\) \\d{4}", + value_type = "str", + example = r#"copyright-regexp = "(?i)Copyright \\(C\\) \\d{4}""# + )] + /// Regexp to check for copyright headers. + /// + /// Default is "Copyright\s+(\(C\)\s+)?\d{4}([-,]\d{4})*\s+%(author)s" + /// E.g # Copyright (c) 2023 ruff + pub copyright_regexp: Option, + #[option( + default = "", + value_type = "str", + example = r#"copyright-author = "ruff""# + )] + /// Author to check for specifically within the header + /// + /// Default is "" + /// E.g # Copyright (c) 2023 ruff + pub copyright_author: Option, + #[option( + default = r#"0"#, + value_type = "int", + example = r#" + # Ensure copyright exists within 1024 characters of header + copyright-min-file-size = 1024 + "# + )] + /// Minimum number of characters in a file before requiring a copyright notice + pub copyright_min_file_size: Option, +} + +#[derive(Debug, CacheKey)] +pub struct Settings { + pub copyright_regexp: String, + pub copyright_author: String, + pub copyright_min_file_size: isize, +} + +impl Default for Settings { + fn default() -> Self { + Self { + copyright_regexp: String::from("(?i)Copyright \\(C\\) \\d{4}"), + copyright_author: String::new(), + copyright_min_file_size: 0, + } + } +} + +impl From for Settings { + fn from(options: Options) -> Self { + Self { + copyright_regexp: options.copyright_regexp.unwrap_or_default(), + copyright_author: options.copyright_author.unwrap_or_default(), + copyright_min_file_size: options.copyright_min_file_size.unwrap_or_default(), + } + } +} + +impl From for Options { + fn from(settings: Settings) -> Self { + Self { + copyright_regexp: Some(settings.copyright_regexp), + copyright_author: Some(settings.copyright_author), + copyright_min_file_size: Some(settings.copyright_min_file_size), + } + } +} diff --git a/crates/ruff/src/rules/flake8_copyright/snapshots/ruff__rules__flake8_copyright__tests__test_custom_regex_fail.snap b/crates/ruff/src/rules/flake8_copyright/snapshots/ruff__rules__flake8_copyright__tests__test_custom_regex_fail.snap new file mode 100644 index 0000000000000..120882e896472 --- /dev/null +++ b/crates/ruff/src/rules/flake8_copyright/snapshots/ruff__rules__flake8_copyright__tests__test_custom_regex_fail.snap @@ -0,0 +1,12 @@ +--- +source: crates/ruff/src/rules/flake8_copyright/mod.rs +--- +C801_custom_author_fail.py:2:1: C801 Copyright notice not present + | +2 | # Copyright 2023 (c) rufffff +3 | + | C801 +4 | import os + | + + diff --git a/crates/ruff/src/rules/flake8_copyright/snapshots/ruff__rules__flake8_copyright__tests__test_default_fail.snap b/crates/ruff/src/rules/flake8_copyright/snapshots/ruff__rules__flake8_copyright__tests__test_default_fail.snap new file mode 100644 index 0000000000000..1ad5e594d0461 --- /dev/null +++ b/crates/ruff/src/rules/flake8_copyright/snapshots/ruff__rules__flake8_copyright__tests__test_default_fail.snap @@ -0,0 +1,12 @@ +--- +source: crates/ruff/src/rules/flake8_copyright/mod.rs +--- +C801_default_fail.py:2:1: C801 Copyright notice not present + | +2 | import os +3 | + | C801 +4 | # Copyright (c) 2023 ruff + | + + diff --git a/crates/ruff/src/rules/mod.rs b/crates/ruff/src/rules/mod.rs index c0fcd71b516fe..1b66e9b4022f1 100644 --- a/crates/ruff/src/rules/mod.rs +++ b/crates/ruff/src/rules/mod.rs @@ -10,6 +10,7 @@ pub mod flake8_bugbear; pub mod flake8_builtins; pub mod flake8_commas; pub mod flake8_comprehensions; +pub mod flake8_copyright; pub mod flake8_datetimez; pub mod flake8_debugger; pub mod flake8_django; diff --git a/crates/ruff/src/settings/configuration.rs b/crates/ruff/src/settings/configuration.rs index 1c7d3b29821bb..827414a1f33ae 100644 --- a/crates/ruff/src/settings/configuration.rs +++ b/crates/ruff/src/settings/configuration.rs @@ -16,9 +16,10 @@ use crate::fs; use crate::rule_selector::RuleSelector; use crate::rules::{ flake8_annotations, flake8_bandit, flake8_bugbear, flake8_builtins, flake8_comprehensions, - flake8_errmsg, flake8_gettext, flake8_implicit_str_concat, flake8_import_conventions, - flake8_pytest_style, flake8_quotes, flake8_self, flake8_tidy_imports, flake8_type_checking, - flake8_unused_arguments, isort, mccabe, pep8_naming, pycodestyle, pydocstyle, pylint, + flake8_copyright, flake8_errmsg, flake8_gettext, flake8_implicit_str_concat, + flake8_import_conventions, flake8_pytest_style, flake8_quotes, flake8_self, + flake8_tidy_imports, flake8_type_checking, flake8_unused_arguments, isort, mccabe, pep8_naming, + pycodestyle, pydocstyle, pylint, }; use crate::settings::options::Options; use crate::settings::types::{ @@ -72,6 +73,7 @@ pub struct Configuration { pub flake8_bugbear: Option, pub flake8_builtins: Option, pub flake8_comprehensions: Option, + pub flake8_copyright: Option, pub flake8_errmsg: Option, pub flake8_implicit_str_concat: Option, pub flake8_import_conventions: Option, @@ -223,6 +225,7 @@ impl Configuration { flake8_bugbear: options.flake8_bugbear, flake8_builtins: options.flake8_builtins, flake8_comprehensions: options.flake8_comprehensions, + flake8_copyright: options.flake8_copyright, flake8_errmsg: options.flake8_errmsg, flake8_gettext: options.flake8_gettext, flake8_implicit_str_concat: options.flake8_implicit_str_concat, @@ -299,6 +302,7 @@ impl Configuration { flake8_comprehensions: self .flake8_comprehensions .combine(config.flake8_comprehensions), + flake8_copyright: self.flake8_copyright.combine(config.flake8_copyright), flake8_errmsg: self.flake8_errmsg.combine(config.flake8_errmsg), flake8_gettext: self.flake8_gettext.combine(config.flake8_gettext), flake8_implicit_str_concat: self diff --git a/crates/ruff/src/settings/defaults.rs b/crates/ruff/src/settings/defaults.rs index 5d468784f407e..a0bd868c7f3a2 100644 --- a/crates/ruff/src/settings/defaults.rs +++ b/crates/ruff/src/settings/defaults.rs @@ -10,9 +10,10 @@ use crate::registry::Linter; use crate::rule_selector::{prefix_to_selector, RuleSelector}; use crate::rules::{ flake8_annotations, flake8_bandit, flake8_bugbear, flake8_builtins, flake8_comprehensions, - flake8_errmsg, flake8_gettext, flake8_implicit_str_concat, flake8_import_conventions, - flake8_pytest_style, flake8_quotes, flake8_self, flake8_tidy_imports, flake8_type_checking, - flake8_unused_arguments, isort, mccabe, pep8_naming, pycodestyle, pydocstyle, pylint, + flake8_copyright, flake8_errmsg, flake8_gettext, flake8_implicit_str_concat, + flake8_import_conventions, flake8_pytest_style, flake8_quotes, flake8_self, + flake8_tidy_imports, flake8_type_checking, flake8_unused_arguments, isort, mccabe, pep8_naming, + pycodestyle, pydocstyle, pylint, }; use crate::settings::types::FilePatternSet; @@ -90,6 +91,7 @@ impl Default for Settings { flake8_bugbear: flake8_bugbear::settings::Settings::default(), flake8_builtins: flake8_builtins::settings::Settings::default(), flake8_comprehensions: flake8_comprehensions::settings::Settings::default(), + flake8_copyright: flake8_copyright::settings::Settings::default(), flake8_errmsg: flake8_errmsg::settings::Settings::default(), flake8_implicit_str_concat: flake8_implicit_str_concat::settings::Settings::default(), flake8_import_conventions: flake8_import_conventions::settings::Settings::default(), diff --git a/crates/ruff/src/settings/mod.rs b/crates/ruff/src/settings/mod.rs index ed533340608f6..b163c9fea0f21 100644 --- a/crates/ruff/src/settings/mod.rs +++ b/crates/ruff/src/settings/mod.rs @@ -17,9 +17,10 @@ use crate::registry::{Rule, RuleNamespace, RuleSet, INCOMPATIBLE_CODES}; use crate::rule_selector::{RuleSelector, Specificity}; use crate::rules::{ flake8_annotations, flake8_bandit, flake8_bugbear, flake8_builtins, flake8_comprehensions, - flake8_errmsg, flake8_gettext, flake8_implicit_str_concat, flake8_import_conventions, - flake8_pytest_style, flake8_quotes, flake8_self, flake8_tidy_imports, flake8_type_checking, - flake8_unused_arguments, isort, mccabe, pep8_naming, pycodestyle, pydocstyle, pylint, + flake8_copyright, flake8_errmsg, flake8_gettext, flake8_implicit_str_concat, + flake8_import_conventions, flake8_pytest_style, flake8_quotes, flake8_self, + flake8_tidy_imports, flake8_type_checking, flake8_unused_arguments, isort, mccabe, pep8_naming, + pycodestyle, pydocstyle, pylint, }; use crate::settings::configuration::Configuration; use crate::settings::types::{FilePatternSet, PerFileIgnore, PythonVersion, SerializationFormat}; @@ -109,6 +110,7 @@ pub struct Settings { pub flake8_bugbear: flake8_bugbear::settings::Settings, pub flake8_builtins: flake8_builtins::settings::Settings, pub flake8_comprehensions: flake8_comprehensions::settings::Settings, + pub flake8_copyright: flake8_copyright::settings::Settings, pub flake8_errmsg: flake8_errmsg::settings::Settings, pub flake8_implicit_str_concat: flake8_implicit_str_concat::settings::Settings, pub flake8_import_conventions: flake8_import_conventions::settings::Settings, @@ -195,6 +197,7 @@ impl Settings { .flake8_comprehensions .map(Into::into) .unwrap_or_default(), + flake8_copyright: config.flake8_copyright.map(Into::into).unwrap_or_default(), flake8_errmsg: config.flake8_errmsg.map(Into::into).unwrap_or_default(), flake8_implicit_str_concat: config .flake8_implicit_str_concat diff --git a/crates/ruff/src/settings/options.rs b/crates/ruff/src/settings/options.rs index 7e7c0bb42386d..ac2ec9f6948d6 100644 --- a/crates/ruff/src/settings/options.rs +++ b/crates/ruff/src/settings/options.rs @@ -8,9 +8,10 @@ use ruff_macros::ConfigurationOptions; use crate::rule_selector::RuleSelector; use crate::rules::{ flake8_annotations, flake8_bandit, flake8_bugbear, flake8_builtins, flake8_comprehensions, - flake8_errmsg, flake8_gettext, flake8_implicit_str_concat, flake8_import_conventions, - flake8_pytest_style, flake8_quotes, flake8_self, flake8_tidy_imports, flake8_type_checking, - flake8_unused_arguments, isort, mccabe, pep8_naming, pycodestyle, pydocstyle, pylint, + flake8_copyright, flake8_errmsg, flake8_gettext, flake8_implicit_str_concat, + flake8_import_conventions, flake8_pytest_style, flake8_quotes, flake8_self, + flake8_tidy_imports, flake8_type_checking, flake8_unused_arguments, isort, mccabe, pep8_naming, + pycodestyle, pydocstyle, pylint, }; use crate::settings::types::{PythonVersion, SerializationFormat, Version}; @@ -480,6 +481,9 @@ pub struct Options { /// Options for the `flake8-comprehensions` plugin. pub flake8_comprehensions: Option, #[option_group] + /// Options for the `flake8-copyright` plugin. + pub flake8_copyright: Option, + #[option_group] /// Options for the `flake8-errmsg` plugin. pub flake8_errmsg: Option, #[option_group] diff --git a/crates/ruff_wasm/src/lib.rs b/crates/ruff_wasm/src/lib.rs index 388ea19bf2808..d614a2f8a03c5 100644 --- a/crates/ruff_wasm/src/lib.rs +++ b/crates/ruff_wasm/src/lib.rs @@ -9,9 +9,10 @@ use ruff::linter::{check_path, LinterResult}; use ruff::registry::AsRule; use ruff::rules::{ flake8_annotations, flake8_bandit, flake8_bugbear, flake8_builtins, flake8_comprehensions, - flake8_errmsg, flake8_gettext, flake8_implicit_str_concat, flake8_import_conventions, - flake8_pytest_style, flake8_quotes, flake8_self, flake8_tidy_imports, flake8_type_checking, - flake8_unused_arguments, isort, mccabe, pep8_naming, pycodestyle, pydocstyle, pylint, + flake8_copyright, flake8_errmsg, flake8_gettext, flake8_implicit_str_concat, + flake8_import_conventions, flake8_pytest_style, flake8_quotes, flake8_self, + flake8_tidy_imports, flake8_type_checking, flake8_unused_arguments, isort, mccabe, pep8_naming, + pycodestyle, pydocstyle, pylint, }; use ruff::settings::configuration::Configuration; use ruff::settings::options::Options; @@ -135,6 +136,7 @@ pub fn defaultSettings() -> Result { flake8_bugbear: Some(flake8_bugbear::settings::Settings::default().into()), flake8_builtins: Some(flake8_builtins::settings::Settings::default().into()), flake8_comprehensions: Some(flake8_comprehensions::settings::Settings::default().into()), + flake8_copyright: Some(flake8_copyright::settings::Settings::default().into()), flake8_errmsg: Some(flake8_errmsg::settings::Settings::default().into()), flake8_pytest_style: Some(flake8_pytest_style::settings::Settings::default().into()), flake8_quotes: Some(flake8_quotes::settings::Settings::default().into()), diff --git a/ruff.schema.json b/ruff.schema.json index 9b05164ff0dce..e8101aaad4d6f 100644 --- a/ruff.schema.json +++ b/ruff.schema.json @@ -198,6 +198,17 @@ } ] }, + "flake8-copyright": { + "description": "Options for the `flake8-copyright` plugin.", + "anyOf": [ + { + "$ref": "#/definitions/Flake8CopyrightOptions" + }, + { + "type": "null" + } + ] + }, "flake8-errmsg": { "description": "Options for the `flake8-errmsg` plugin.", "anyOf": [ @@ -715,6 +726,34 @@ }, "additionalProperties": false }, + "Flake8CopyrightOptions": { + "type": "object", + "properties": { + "copyright-author": { + "description": "Author to check for specifically within the header\n\nDefault is \"\" E.g # Copyright (c) ", + "type": [ + "string", + "null" + ] + }, + "copyright-min-file-size": { + "description": "Minimum number of characters in a file before requiring a copyright notice", + "type": [ + "integer", + "null" + ], + "format": "int" + }, + "copyright-regexp": { + "description": "Regexp to check for copyright headers.\n\nDefault is \"Copyright\\s+(\\(C\\)\\s+)?\\d{4}([-,]\\d{4})*\\s+%(author)s\" E.g # Copyright (c) 2023 ruff", + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + }, "Flake8ErrMsgOptions": { "type": "object", "properties": { @@ -1617,6 +1656,8 @@ "C417", "C418", "C419", + "C80", + "C801", "C9", "C90", "C901",