mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-04 18:58:04 +00:00
Update MSRV to 1.85 and toolchain to 1.87 (#18126)
This commit is contained in:
parent
6e39250015
commit
196e4befba
19 changed files with 82 additions and 54 deletions
|
@ -4,7 +4,7 @@ resolver = "2"
|
|||
|
||||
[workspace.package]
|
||||
edition = "2021"
|
||||
rust-version = "1.84"
|
||||
rust-version = "1.85"
|
||||
homepage = "https://docs.astral.sh/ruff"
|
||||
documentation = "https://docs.astral.sh/ruff"
|
||||
repository = "https://github.com/astral-sh/ruff"
|
||||
|
@ -215,6 +215,7 @@ similar_names = "allow"
|
|||
single_match_else = "allow"
|
||||
too_many_lines = "allow"
|
||||
needless_continue = "allow" # An explicit continue can be more readable, especially if the alternative is an empty block.
|
||||
unnecessary_debug_formatting = "allow" # too many instances, the display also doesn't quote the path which is often desired in logs where we use them the most often.
|
||||
# Without the hashes we run into a `rustfmt` bug in some snapshot tests, see #13250
|
||||
needless_raw_string_hashes = "allow"
|
||||
# Disallowed restriction lints
|
||||
|
|
|
@ -133,7 +133,7 @@ struct SourceTextInner {
|
|||
#[derive(Eq, PartialEq)]
|
||||
enum SourceTextKind {
|
||||
Text(String),
|
||||
Notebook(Notebook),
|
||||
Notebook(Box<Notebook>),
|
||||
}
|
||||
|
||||
impl From<String> for SourceTextKind {
|
||||
|
@ -144,7 +144,7 @@ impl From<String> for SourceTextKind {
|
|||
|
||||
impl From<Notebook> for SourceTextKind {
|
||||
fn from(notebook: Notebook) -> Self {
|
||||
SourceTextKind::Notebook(notebook)
|
||||
SourceTextKind::Notebook(Box::new(notebook))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -463,17 +463,17 @@ fn not_found() -> std::io::Error {
|
|||
fn is_a_directory() -> std::io::Error {
|
||||
// Note: Rust returns `ErrorKind::IsADirectory` for this error but this is a nightly only variant :(.
|
||||
// So we have to use other for now.
|
||||
std::io::Error::new(std::io::ErrorKind::Other, "Is a directory")
|
||||
std::io::Error::other("Is a directory")
|
||||
}
|
||||
|
||||
fn not_a_directory() -> std::io::Error {
|
||||
// Note: Rust returns `ErrorKind::NotADirectory` for this error but this is a nightly only variant :(.
|
||||
// So we have to use `Other` for now.
|
||||
std::io::Error::new(std::io::ErrorKind::Other, "Not a directory")
|
||||
std::io::Error::other("Not a directory")
|
||||
}
|
||||
|
||||
fn directory_not_empty() -> std::io::Error {
|
||||
std::io::Error::new(std::io::ErrorKind::Other, "directory not empty")
|
||||
std::io::Error::other("directory not empty")
|
||||
}
|
||||
|
||||
fn create_dir_all(
|
||||
|
|
|
@ -1388,7 +1388,7 @@ pub fn soft_space_or_block_indent<Context>(content: &impl Format<Context>) -> Bl
|
|||
pub fn group<Context>(content: &impl Format<Context>) -> Group<Context> {
|
||||
Group {
|
||||
content: Argument::new(content),
|
||||
group_id: None,
|
||||
id: None,
|
||||
should_expand: false,
|
||||
}
|
||||
}
|
||||
|
@ -1396,14 +1396,14 @@ pub fn group<Context>(content: &impl Format<Context>) -> Group<Context> {
|
|||
#[derive(Copy, Clone)]
|
||||
pub struct Group<'a, Context> {
|
||||
content: Argument<'a, Context>,
|
||||
group_id: Option<GroupId>,
|
||||
id: Option<GroupId>,
|
||||
should_expand: bool,
|
||||
}
|
||||
|
||||
impl<Context> Group<'_, Context> {
|
||||
#[must_use]
|
||||
pub fn with_group_id(mut self, group_id: Option<GroupId>) -> Self {
|
||||
self.group_id = group_id;
|
||||
pub fn with_id(mut self, group_id: Option<GroupId>) -> Self {
|
||||
self.id = group_id;
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -1429,7 +1429,7 @@ impl<Context> Format<Context> for Group<'_, Context> {
|
|||
};
|
||||
|
||||
f.write_element(FormatElement::Tag(StartGroup(
|
||||
tag::Group::new().with_id(self.group_id).with_mode(mode),
|
||||
tag::Group::new().with_id(self.id).with_mode(mode),
|
||||
)));
|
||||
|
||||
Arguments::from(&self.content).fmt(f)?;
|
||||
|
@ -1443,7 +1443,7 @@ impl<Context> Format<Context> for Group<'_, Context> {
|
|||
impl<Context> std::fmt::Debug for Group<'_, Context> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("Group")
|
||||
.field("group_id", &self.group_id)
|
||||
.field("id", &self.id)
|
||||
.field("should_expand", &self.should_expand)
|
||||
.field("content", &"{{content}}")
|
||||
.finish()
|
||||
|
@ -1642,7 +1642,7 @@ impl<Context> std::fmt::Debug for BestFitParenthesize<'_, Context> {
|
|||
/// soft_line_break(),
|
||||
/// if_group_breaks(&token(")"))
|
||||
/// ])
|
||||
/// .with_group_id(Some(parentheses_id))
|
||||
/// .with_id(Some(parentheses_id))
|
||||
/// .fmt(f)
|
||||
/// });
|
||||
///
|
||||
|
@ -1991,7 +1991,7 @@ impl<Context> IfGroupBreaks<'_, Context> {
|
|||
/// })),
|
||||
/// token("]")
|
||||
/// ],
|
||||
/// ).with_group_id(Some(group_id))
|
||||
/// ).with_id(Some(group_id))
|
||||
/// ])
|
||||
/// })])?;
|
||||
///
|
||||
|
@ -2046,7 +2046,7 @@ impl<Context> std::fmt::Debug for IfGroupBreaks<'_, Context> {
|
|||
/// let id = f.group_id("head");
|
||||
///
|
||||
/// write!(f, [
|
||||
/// group(&token("Head")).with_group_id(Some(id)),
|
||||
/// group(&token("Head")).with_id(Some(id)),
|
||||
/// if_group_breaks(&indent(&token("indented"))).with_group_id(Some(id)),
|
||||
/// if_group_fits_on_line(&token("indented")).with_group_id(Some(id))
|
||||
/// ])
|
||||
|
@ -2071,7 +2071,7 @@ impl<Context> std::fmt::Debug for IfGroupBreaks<'_, Context> {
|
|||
/// let group_id = f.group_id("header");
|
||||
///
|
||||
/// write!(f, [
|
||||
/// group(&token("(aLongHeaderThatBreaksForSomeReason) =>")).with_group_id(Some(group_id)),
|
||||
/// group(&token("(aLongHeaderThatBreaksForSomeReason) =>")).with_id(Some(group_id)),
|
||||
/// indent_if_group_breaks(&format_args![hard_line_break(), token("a => b")], group_id)
|
||||
/// ])
|
||||
/// });
|
||||
|
@ -2101,7 +2101,7 @@ impl<Context> std::fmt::Debug for IfGroupBreaks<'_, Context> {
|
|||
/// let group_id = f.group_id("header");
|
||||
///
|
||||
/// write!(f, [
|
||||
/// group(&token("(aLongHeaderThatBreaksForSomeReason) =>")).with_group_id(Some(group_id)),
|
||||
/// group(&token("(aLongHeaderThatBreaksForSomeReason) =>")).with_id(Some(group_id)),
|
||||
/// indent_if_group_breaks(&format_args![hard_line_break(), token("a => b")], group_id)
|
||||
/// ])
|
||||
/// });
|
||||
|
|
|
@ -2002,7 +2002,7 @@ two lines`,
|
|||
token("The referenced group breaks."),
|
||||
hard_line_break()
|
||||
])
|
||||
.with_group_id(Some(group_id)),
|
||||
.with_id(Some(group_id)),
|
||||
group(&format_args![
|
||||
token("This group breaks because:"),
|
||||
soft_line_break_or_space(),
|
||||
|
@ -2027,7 +2027,7 @@ two lines`,
|
|||
write!(
|
||||
f,
|
||||
[
|
||||
group(&token("Group with id-2")).with_group_id(Some(id_2)),
|
||||
group(&token("Group with id-2")).with_id(Some(id_2)),
|
||||
hard_line_break()
|
||||
]
|
||||
)?;
|
||||
|
@ -2035,7 +2035,7 @@ two lines`,
|
|||
write!(
|
||||
f,
|
||||
[
|
||||
group(&token("Group with id-1 does not fit on the line because it exceeds the line width of 80 characters by")).with_group_id(Some(id_1)),
|
||||
group(&token("Group with id-1 does not fit on the line because it exceeds the line width of 80 characters by")).with_id(Some(id_1)),
|
||||
hard_line_break()
|
||||
]
|
||||
)?;
|
||||
|
|
|
@ -940,7 +940,7 @@ mod tests {
|
|||
#[test_case(Path::new("add_missing_cell_id.ipynb"), true; "add_missing_cell_id")]
|
||||
fn test_cell_id(path: &Path, has_id: bool) -> Result<()> {
|
||||
let source_notebook = Notebook::from_path(¬ebook_path(path))?;
|
||||
let source_kind = SourceKind::IpyNotebook(source_notebook);
|
||||
let source_kind = SourceKind::ipy_notebook(source_notebook);
|
||||
let (_, transformed) = test_contents(
|
||||
&source_kind,
|
||||
path,
|
||||
|
@ -1231,7 +1231,7 @@ mod tests {
|
|||
format!("async_comprehension_in_sync_comprehension_notebook_{python_version}");
|
||||
let path = Path::new("resources/test/fixtures/syntax_errors/async_comprehension.ipynb");
|
||||
let messages = test_contents_syntax_errors(
|
||||
&SourceKind::IpyNotebook(Notebook::from_path(path)?),
|
||||
&SourceKind::ipy_notebook(Notebook::from_path(path)?),
|
||||
path,
|
||||
&LinterSettings {
|
||||
unresolved_target_version: python_version.into(),
|
||||
|
|
|
@ -198,7 +198,7 @@ fn is_explicit_concatenation(expr: &Expr) -> Option<bool> {
|
|||
.iter()
|
||||
.map(is_explicit_concatenation)
|
||||
.collect::<Vec<_>>();
|
||||
if values.iter().any(|v| *v == Some(true)) {
|
||||
if values.contains(&Some(true)) {
|
||||
Some(true)
|
||||
} else if values.iter().all(|v| *v == Some(false)) {
|
||||
Some(false)
|
||||
|
|
|
@ -174,7 +174,7 @@ struct StringLiteralDisplay<'a> {
|
|||
/// The elts from the original AST node representing the display.
|
||||
/// Each elt is the AST representation of a single string literal
|
||||
/// element in the display
|
||||
elts: Cow<'a, Vec<ast::Expr>>,
|
||||
elts: Cow<'a, [ast::Expr]>,
|
||||
/// The source-code range of the display as a whole
|
||||
range: TextRange,
|
||||
/// What kind of a display is it? A dict, set, list or tuple?
|
||||
|
|
|
@ -16,15 +16,47 @@ use colored::Colorize;
|
|||
use crate::fs;
|
||||
use crate::text_helpers::ShowNonprinting;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, is_macro::Is)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum SourceKind {
|
||||
/// The source contains Python source code.
|
||||
Python(String),
|
||||
/// The source contains a Jupyter notebook.
|
||||
IpyNotebook(Notebook),
|
||||
IpyNotebook(Box<Notebook>),
|
||||
}
|
||||
|
||||
impl SourceKind {
|
||||
pub fn ipy_notebook(notebook: Notebook) -> Self {
|
||||
SourceKind::IpyNotebook(Box::new(notebook))
|
||||
}
|
||||
|
||||
pub fn as_ipy_notebook(&self) -> Option<&Notebook> {
|
||||
match self {
|
||||
SourceKind::IpyNotebook(notebook) => Some(notebook),
|
||||
SourceKind::Python(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_python(&self) -> Option<&str> {
|
||||
match self {
|
||||
SourceKind::Python(code) => Some(code),
|
||||
SourceKind::IpyNotebook(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expect_python(self) -> String {
|
||||
match self {
|
||||
SourceKind::Python(code) => code,
|
||||
SourceKind::IpyNotebook(_) => panic!("expected python code"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expect_ipy_notebook(self) -> Notebook {
|
||||
match self {
|
||||
SourceKind::IpyNotebook(notebook) => *notebook,
|
||||
SourceKind::Python(_) => panic!("expected ipy notebook"),
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub(crate) fn updated(&self, new_source: String, source_map: &SourceMap) -> Self {
|
||||
match self {
|
||||
|
@ -52,7 +84,7 @@ impl SourceKind {
|
|||
let notebook = Notebook::from_path(path)?;
|
||||
Ok(notebook
|
||||
.is_python_notebook()
|
||||
.then_some(Self::IpyNotebook(notebook)))
|
||||
.then_some(Self::IpyNotebook(Box::new(notebook))))
|
||||
} else {
|
||||
let contents = std::fs::read_to_string(path)?;
|
||||
Ok(Some(Self::Python(contents)))
|
||||
|
@ -69,7 +101,7 @@ impl SourceKind {
|
|||
let notebook = Notebook::from_source_code(&source_code)?;
|
||||
Ok(notebook
|
||||
.is_python_notebook()
|
||||
.then_some(Self::IpyNotebook(notebook)))
|
||||
.then_some(Self::IpyNotebook(Box::new(notebook))))
|
||||
} else {
|
||||
Ok(Some(Self::Python(source_code)))
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ pub(crate) fn assert_notebook_path(
|
|||
) -> Result<TestedNotebook, NotebookError> {
|
||||
let source_notebook = Notebook::from_path(path.as_ref())?;
|
||||
|
||||
let source_kind = SourceKind::IpyNotebook(source_notebook);
|
||||
let source_kind = SourceKind::ipy_notebook(source_notebook);
|
||||
let (messages, transformed) = test_contents(&source_kind, path.as_ref(), settings);
|
||||
let expected_notebook = Notebook::from_path(expected.as_ref())?;
|
||||
let linted_notebook = transformed.into_owned().expect_ipy_notebook();
|
||||
|
|
|
@ -255,7 +255,7 @@ impl<'ast> Format<PyFormatContext<'ast>> for FormatOptionalParentheses<'_, 'ast>
|
|||
soft_line_break(),
|
||||
if_group_breaks(&token(")"))
|
||||
])
|
||||
.with_group_id(Some(parens_id))]
|
||||
.with_id(Some(parens_id))]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -413,7 +413,7 @@ impl Format<PyFormatContext<'_>> for FormatStatementsLastExpression<'_> {
|
|||
soft_block_indent(&format_args![flat, inline_comments]),
|
||||
token(")"),
|
||||
])
|
||||
.with_group_id(Some(group_id))
|
||||
.with_id(Some(group_id))
|
||||
.should_expand(true)
|
||||
.fmt(f)
|
||||
});
|
||||
|
@ -433,7 +433,7 @@ impl Format<PyFormatContext<'_>> for FormatStatementsLastExpression<'_> {
|
|||
token(")"),
|
||||
inline_comments,
|
||||
])
|
||||
.with_group_id(Some(group_id))
|
||||
.with_id(Some(group_id))
|
||||
.should_expand(true)
|
||||
.fmt(f)
|
||||
});
|
||||
|
@ -501,7 +501,7 @@ impl Format<PyFormatContext<'_>> for FormatStatementsLastExpression<'_> {
|
|||
soft_block_indent(&format_args![f_string_flat, inline_comments]),
|
||||
token(")"),
|
||||
])
|
||||
.with_group_id(Some(group_id))
|
||||
.with_id(Some(group_id))
|
||||
.should_expand(true)
|
||||
.fmt(f)
|
||||
});
|
||||
|
@ -817,7 +817,7 @@ impl Format<PyFormatContext<'_>> for FormatStatementsLastExpression<'_> {
|
|||
space(),
|
||||
token("("),
|
||||
group(&soft_block_indent(&format_expanded))
|
||||
.with_group_id(Some(group_id))
|
||||
.with_id(Some(group_id))
|
||||
.should_expand(true),
|
||||
token(")"),
|
||||
inline_comments
|
||||
|
@ -875,7 +875,7 @@ impl Format<PyFormatContext<'_>> for FormatStatementsLastExpression<'_> {
|
|||
space(),
|
||||
token("("),
|
||||
group(&soft_block_indent(&format_expanded))
|
||||
.with_group_id(Some(group_id))
|
||||
.with_id(Some(group_id))
|
||||
.should_expand(true),
|
||||
token(")"),
|
||||
inline_comments
|
||||
|
|
|
@ -252,9 +252,7 @@ pub fn is_immutable_annotation(
|
|||
.is_some_and(|qualified_name| {
|
||||
is_immutable_non_generic_type(qualified_name.segments())
|
||||
|| is_immutable_generic_type(qualified_name.segments())
|
||||
|| extend_immutable_calls
|
||||
.iter()
|
||||
.any(|target| qualified_name == *target)
|
||||
|| extend_immutable_calls.contains(&qualified_name)
|
||||
})
|
||||
}
|
||||
Expr::Subscript(ast::ExprSubscript { value, slice, .. }) => semantic
|
||||
|
@ -308,9 +306,7 @@ pub fn is_immutable_func(
|
|||
.resolve_qualified_name(map_subscript(func))
|
||||
.is_some_and(|qualified_name| {
|
||||
is_immutable_return_type(qualified_name.segments())
|
||||
|| extend_immutable_calls
|
||||
.iter()
|
||||
.any(|target| qualified_name == *target)
|
||||
|| extend_immutable_calls.contains(&qualified_name)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -559,7 +559,7 @@ impl DocumentQuery {
|
|||
ruff_linter::source_kind::SourceKind::Python(document.contents().to_string())
|
||||
}
|
||||
Self::Notebook { notebook, .. } => {
|
||||
ruff_linter::source_kind::SourceKind::IpyNotebook(notebook.make_ruff_notebook())
|
||||
ruff_linter::source_kind::SourceKind::ipy_notebook(notebook.make_ruff_notebook())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -462,7 +462,7 @@ impl ConfigurationTransformer for EditorConfigurationTransformer<'_> {
|
|||
tracing::debug!(
|
||||
"Combining settings from editor-specified inline configuration"
|
||||
);
|
||||
match Configuration::from_options(options, None, project_root) {
|
||||
match Configuration::from_options(*options, None, project_root) {
|
||||
Ok(configuration) => editor_configuration.combine(configuration),
|
||||
Err(err) => {
|
||||
tracing::error!(
|
||||
|
@ -516,10 +516,10 @@ mod tests {
|
|||
#[test]
|
||||
fn inline_settings() {
|
||||
let editor_settings = ResolvedEditorSettings {
|
||||
configuration: Some(ResolvedConfiguration::Inline(Options {
|
||||
configuration: Some(ResolvedConfiguration::Inline(Box::new(Options {
|
||||
line_length: Some(LineLength::try_from(120).unwrap()),
|
||||
..Default::default()
|
||||
})),
|
||||
}))),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
|
@ -534,10 +534,10 @@ mod tests {
|
|||
#[test]
|
||||
fn inline_and_specific_settings_resolution_order() {
|
||||
let editor_settings = ResolvedEditorSettings {
|
||||
configuration: Some(ResolvedConfiguration::Inline(Options {
|
||||
configuration: Some(ResolvedConfiguration::Inline(Box::new(Options {
|
||||
line_length: Some(LineLength::try_from(120).unwrap()),
|
||||
..Default::default()
|
||||
})),
|
||||
}))),
|
||||
line_length: Some(LineLength::try_from(100).unwrap()),
|
||||
..Default::default()
|
||||
};
|
||||
|
|
|
@ -52,7 +52,7 @@ pub(crate) struct ResolvedEditorSettings {
|
|||
#[cfg_attr(test, derive(PartialEq, Eq))]
|
||||
pub(crate) enum ResolvedConfiguration {
|
||||
FilePath(PathBuf),
|
||||
Inline(Options),
|
||||
Inline(Box<Options>),
|
||||
}
|
||||
|
||||
impl TryFrom<&ClientConfiguration> for ResolvedConfiguration {
|
||||
|
@ -68,7 +68,7 @@ impl TryFrom<&ClientConfiguration> for ResolvedConfiguration {
|
|||
if options.extend.is_some() {
|
||||
Err(ResolvedConfigurationError::ExtendNotSupported)
|
||||
} else {
|
||||
Ok(ResolvedConfiguration::Inline(options))
|
||||
Ok(ResolvedConfiguration::Inline(Box::new(options)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -991,7 +991,7 @@ mod tests {
|
|||
fix_violation_enable: true,
|
||||
show_syntax_errors: true,
|
||||
editor_settings: ResolvedEditorSettings {
|
||||
configuration: Some(ResolvedConfiguration::Inline(Options {
|
||||
configuration: Some(ResolvedConfiguration::Inline(Box::new(Options {
|
||||
line_length: Some(LineLength::try_from(100).unwrap()),
|
||||
lint: Some(LintOptions {
|
||||
common: LintCommonOptions {
|
||||
|
@ -1005,7 +1005,7 @@ mod tests {
|
|||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
})),
|
||||
}))),
|
||||
extend_select: Some(vec![RuleSelector::from_str("RUF001").unwrap()]),
|
||||
..Default::default()
|
||||
}
|
||||
|
|
|
@ -594,7 +594,7 @@ impl PythonHomePath {
|
|||
system
|
||||
.is_directory(&canonicalized)
|
||||
.then_some(Self(canonicalized))
|
||||
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "not a directory"))
|
||||
.ok_or_else(|| io::Error::other("not a directory"))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5034,7 +5034,6 @@ impl<'db> Type<'db> {
|
|||
/// Note that this does not specialize generic classes, functions, or type aliases! That is a
|
||||
/// different operation that is performed explicitly (via a subscript operation), or implicitly
|
||||
/// via a call to the generic object.
|
||||
#[must_use]
|
||||
#[salsa::tracked]
|
||||
pub fn apply_specialization(
|
||||
self,
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
[toolchain]
|
||||
channel = "1.86"
|
||||
channel = "1.87"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue