Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions crates/ruff_db/src/diagnostic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,11 @@ impl Diagnostic {
.find(|ann| ann.is_primary)
}

/// Returns a mutable borrow of all annotations of this diagnostic.
pub fn annotations_mut(&mut self) -> impl Iterator<Item = &mut Annotation> {
Arc::make_mut(&mut self.inner).annotations.iter_mut()
}

/// Returns the "primary" span of this diagnostic if one exists.
///
/// When there are multiple primary spans, then the first one that was
Expand Down Expand Up @@ -310,6 +315,11 @@ impl Diagnostic {
&self.inner.subs
}

/// Returns a shared borrow of the sub-diagnostics of this diagnostic.
pub fn sub_diagnostics_mut(&mut self) -> impl Iterator<Item = &mut SubDiagnostic> {
Arc::make_mut(&mut self.inner).subs.iter_mut()
}

/// Returns the fix for this diagnostic if it exists.
pub fn fix(&self) -> Option<&Fix> {
self.inner.fix.as_ref()
Expand Down Expand Up @@ -487,6 +497,16 @@ impl Diagnostic {
other.expect_range().start(),
))
}

/// Sort this diagnostic's sub-diagnostics by descending severity.
///
/// This is particularly helpful to move `help`-level sub-diagnostics to the end, after any
/// longer, `info`-level diagnostics.
pub fn sort_sub_diagnostics(&mut self) {
Arc::make_mut(&mut self.inner)
.subs
.sort_by_key(|sub| std::cmp::Reverse(sub.inner.severity));
}
}

#[derive(Debug, Clone, Eq, PartialEq, Hash, get_size2::GetSize)]
Expand Down Expand Up @@ -621,6 +641,11 @@ impl SubDiagnostic {
&self.inner.annotations
}

/// Returns a shared borrow of the annotations of this diagnostic.
pub fn annotations_mut(&mut self) -> impl Iterator<Item = &mut Annotation> {
self.inner.annotations.iter_mut()
}

/// Returns a shared borrow of the "primary" annotation of this diagnostic
/// if one exists.
///
Expand Down
7 changes: 6 additions & 1 deletion crates/ruff_db/src/diagnostic/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,12 @@ impl<'a> ResolvedDiagnostic<'a> {
.annotations
.iter()
.filter_map(|ann| {
let path = ann.span.file.path(resolver);
let path = ann
.span
.file
.relative_path(resolver)
.to_str()
.unwrap_or_else(|| ann.span.file.path(resolver));
let diagnostic_source = ann.span.file.diagnostic_source(resolver);
ResolvedAnnotation::new(path, &diagnostic_source, ann, resolver)
})
Expand Down
23 changes: 21 additions & 2 deletions crates/ruff_linter/src/checkers/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ use itertools::Itertools;
use log::debug;
use rustc_hash::{FxHashMap, FxHashSet};

use ruff_db::diagnostic::Diagnostic;
use ruff_db::diagnostic::{
Annotation, Diagnostic, IntoDiagnosticMessage, Span, SubDiagnostic, SubDiagnosticSeverity,
};
use ruff_diagnostics::{Applicability, Fix, IsolationLevel};
use ruff_notebook::{CellOffsets, NotebookIndex};
use ruff_python_ast::helpers::{collect_import_from_member, is_docstring_stmt, to_module_path};
Expand Down Expand Up @@ -3305,6 +3307,22 @@ impl DiagnosticGuard<'_, '_> {
Err(err) => log::debug!("Failed to create fix for {}: {}", self.name(), err),
}
}

/// Add an "info" sub-diagnostic with the given message and range.
///
/// Note that this shadows `Diagnostic::info` from the `Deref` implementation because we'll
/// usually want to attach a range here.
pub(crate) fn info<'a>(
&mut self,
message: impl IntoDiagnosticMessage + 'a,
range: impl Ranged,
) {
let mut sub = SubDiagnostic::new(SubDiagnosticSeverity::Info, message);
let span = Span::from(self.context.source_file.clone()).with_range(range.range());
let ann = Annotation::primary(span);
sub.annotate(ann);
self.diagnostic.as_mut().unwrap().sub(sub);
}
}

impl std::ops::Deref for DiagnosticGuard<'_, '_> {
Expand All @@ -3331,7 +3349,8 @@ impl Drop for DiagnosticGuard<'_, '_> {
return;
}

if let Some(diagnostic) = self.diagnostic.take() {
if let Some(mut diagnostic) = self.diagnostic.take() {
diagnostic.sort_sub_diagnostics();
self.context.diagnostics.borrow_mut().push(diagnostic);
}
}
Expand Down
12 changes: 11 additions & 1 deletion crates/ruff_linter/src/rules/pyupgrade/rules/pep695/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ pub(crate) struct TypeVar<'a> {
pub(crate) restriction: Option<TypeVarRestriction<'a>>,
pub(crate) kind: TypeParamKind,
pub(crate) default: Option<&'a Expr>,
/// The range of the type variable's definition for attaching to diagnostics.
///
/// This should always be `Some` except for `AnyStr`, which we handle but won't appear in the
/// same source file.
pub(crate) range: Option<TextRange>,
}

/// Wrapper for formatting a sequence of [`TypeVar`]s for use as a generic type parameter (e.g. `[T,
Expand Down Expand Up @@ -133,6 +138,7 @@ impl<'a> From<&'a TypeVar<'a>> for TypeParam {
name,
restriction,
kind,
range: _,
default: _, // TODO(brent) see below
}: &'a TypeVar<'a>,
) -> Self {
Expand Down Expand Up @@ -222,6 +228,7 @@ impl<'a> From<&'a TypeParam> for TypeVar<'a> {
kind,
restriction,
default: param.default(),
range: Some(param.range()),
}
}
}
Expand Down Expand Up @@ -259,6 +266,7 @@ impl<'a> Visitor<'a> for TypeVarReferenceVisitor<'a> {
restriction: Some(TypeVarRestriction::AnyStr),
kind: TypeParamKind::TypeVar,
default: None,
range: None,
});
return;
}
Expand All @@ -280,7 +288,7 @@ pub(crate) fn expr_name_to_type_var<'a>(
semantic: &'a SemanticModel,
name: &'a ExprName,
) -> Option<TypeVar<'a>> {
let StmtAssign { value, .. } = semantic
let assign @ StmtAssign { value, .. } = semantic
.lookup_symbol(name.id.as_str())
.and_then(|binding_id| semantic.binding(binding_id).source)
.map(|node_id| semantic.statement(node_id))?
Expand All @@ -297,6 +305,7 @@ pub(crate) fn expr_name_to_type_var<'a>(
restriction: None,
kind: TypeParamKind::TypeVar,
default: None,
range: Some(assign.range()),
});
}
}
Expand Down Expand Up @@ -349,6 +358,7 @@ pub(crate) fn expr_name_to_type_var<'a>(
restriction,
kind,
default,
range: Some(assign.range()),
});
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,15 @@ pub(crate) fn non_pep695_generic_class(checker: &Checker, class_def: &StmtClassD
return;
};

for type_var in &type_vars {
if let Some(range) = type_var.range {
diagnostic.info(
format_args!("Type variable `{}` defined here", type_var.name),
range,
);
}
}

// build the fix as a String to avoid removing comments from the entire function body
let type_params = DisplayTypeVars {
type_vars: &type_vars,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ pub(crate) fn non_pep695_type_alias_type(checker: &Checker, stmt: &StmtAssign) {
restriction: None,
kind: TypeParamKind::TypeVar,
default: None,
range: Some(name.range),
})
})
})
Expand Down
Loading
Loading