mirror of https://github.com/astral-sh/ruff
parent
e1219bc27c
commit
2e5c8b9799
|
|
@ -13,7 +13,6 @@ use itertools::Itertools;
|
|||
use log::{debug, error};
|
||||
use rayon::iter::ParallelIterator;
|
||||
use rayon::iter::{IntoParallelIterator, ParallelBridge};
|
||||
use ruff_db::diagnostic::serde_impls::DiagnosticsSerializer;
|
||||
use ruff_linter::codes::Rule;
|
||||
use rustc_hash::FxHashMap;
|
||||
use tempfile::NamedTempFile;
|
||||
|
|
@ -21,10 +20,12 @@ use tempfile::NamedTempFile;
|
|||
use ruff_cache::{CacheKey, CacheKeyHasher};
|
||||
use ruff_db::diagnostic::Diagnostic;
|
||||
use ruff_diagnostics::Fix;
|
||||
use ruff_linter::message::create_lint_diagnostic;
|
||||
use ruff_linter::package::PackageRoot;
|
||||
use ruff_linter::{VERSION, warn_user};
|
||||
use ruff_macros::CacheKey;
|
||||
use ruff_notebook::NotebookIndex;
|
||||
use ruff_source_file::SourceFileBuilder;
|
||||
use ruff_text_size::{TextRange, TextSize};
|
||||
use ruff_workspace::Settings;
|
||||
use ruff_workspace::resolver::Resolver;
|
||||
|
|
@ -344,7 +345,22 @@ impl FileCache {
|
|||
let diagnostics = if lint.messages.is_empty() {
|
||||
Vec::new()
|
||||
} else {
|
||||
lint.messages.diagnostics().to_vec()
|
||||
let file = SourceFileBuilder::new(path.to_string_lossy(), &*lint.source).finish();
|
||||
lint.messages
|
||||
.iter()
|
||||
.map(|msg| {
|
||||
create_lint_diagnostic(
|
||||
&msg.body,
|
||||
msg.suggestion.as_ref(),
|
||||
msg.range,
|
||||
msg.fix.clone(),
|
||||
msg.parent,
|
||||
file.clone(),
|
||||
msg.noqa_offset,
|
||||
msg.rule,
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
let notebook_indexes = if let Some(notebook_index) = lint.notebook_index.as_ref() {
|
||||
FxHashMap::from_iter([(path.to_string_lossy().to_string(), notebook_index.clone())])
|
||||
|
|
@ -399,8 +415,7 @@ pub(crate) struct LintCacheData {
|
|||
/// Imports made.
|
||||
// pub(super) imports: ImportMap,
|
||||
/// Diagnostic messages.
|
||||
#[bincode(with_serde)]
|
||||
pub(super) messages: DiagnosticsSerializer,
|
||||
pub(super) messages: Vec<CacheMessage>,
|
||||
/// Source code of the file.
|
||||
///
|
||||
/// # Notes
|
||||
|
|
@ -423,7 +438,30 @@ impl LintCacheData {
|
|||
String::new() // No messages, no need to keep the source!
|
||||
};
|
||||
|
||||
let messages = DiagnosticsSerializer::new(diagnostics.to_vec());
|
||||
let messages = diagnostics
|
||||
.iter()
|
||||
// Parse the kebab-case rule name into a `Rule`. This will fail for syntax errors, so
|
||||
// this also serves to filter them out, but we shouldn't be caching files with syntax
|
||||
// errors anyway.
|
||||
.filter_map(|msg| Some((msg.name().parse().ok()?, msg)))
|
||||
.map(|(rule, msg)| {
|
||||
// Make sure that all message use the same source file.
|
||||
assert_eq!(
|
||||
msg.expect_ruff_source_file(),
|
||||
diagnostics.first().unwrap().expect_ruff_source_file(),
|
||||
"message uses a different source file"
|
||||
);
|
||||
CacheMessage {
|
||||
rule,
|
||||
body: msg.body().to_string(),
|
||||
suggestion: msg.suggestion().map(ToString::to_string),
|
||||
range: msg.expect_range(),
|
||||
parent: msg.parent(),
|
||||
fix: msg.fix().cloned(),
|
||||
noqa_offset: msg.noqa_offset(),
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
Self {
|
||||
messages,
|
||||
|
|
|
|||
|
|
@ -790,7 +790,6 @@ impl Annotation {
|
|||
/// These tags are used to provide additional information about the annotation.
|
||||
/// and are passed through to the language server protocol.
|
||||
#[derive(Debug, Clone, Eq, PartialEq, get_size2::GetSize)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum DiagnosticTag {
|
||||
/// Unused or unnecessary code. Used for unused parameters, unreachable code, etc.
|
||||
Unnecessary,
|
||||
|
|
@ -805,7 +804,6 @@ pub enum DiagnosticTag {
|
|||
///
|
||||
/// Rules use kebab case, e.g. `no-foo`.
|
||||
#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash, get_size2::GetSize)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||
pub struct LintName(&'static str);
|
||||
|
||||
impl LintName {
|
||||
|
|
@ -846,7 +844,6 @@ impl PartialEq<&str> for LintName {
|
|||
|
||||
/// Uniquely identifies the kind of a diagnostic.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash, get_size2::GetSize)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum DiagnosticId {
|
||||
Panic,
|
||||
|
||||
|
|
@ -1144,7 +1141,6 @@ impl From<crate::files::FileRange> for Span {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, get_size2::GetSize)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum Severity {
|
||||
Info,
|
||||
Warning,
|
||||
|
|
@ -1348,7 +1344,6 @@ impl std::fmt::Display for ConciseMessage<'_> {
|
|||
/// a blanket trait implementation for `IntoDiagnosticMessage` for
|
||||
/// anything that implements `std::fmt::Display`.
|
||||
#[derive(Clone, Debug, Eq, PartialEq, get_size2::GetSize)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct DiagnosticMessage(Box<str>);
|
||||
|
||||
impl DiagnosticMessage {
|
||||
|
|
@ -1457,271 +1452,3 @@ impl From<&SecondaryCode> for SecondaryCode {
|
|||
value.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
pub mod serde_impls {
|
||||
use std::{collections::HashMap, sync::Mutex};
|
||||
|
||||
use rustc_hash::FxHashMap;
|
||||
use serde::{
|
||||
Deserialize, Serialize,
|
||||
ser::{SerializeSeq, SerializeStruct},
|
||||
};
|
||||
|
||||
use super::{Annotation, Diagnostic, LintName, Span, SubDiagnostic};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DiagnosticsSerializer<'a> {
|
||||
source_files: Mutex<FxHashMap<String, String>>,
|
||||
diagnostics: &'a [Diagnostic],
|
||||
}
|
||||
|
||||
impl PartialEq for DiagnosticsSerializer<'_> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.diagnostics == other.diagnostics
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DiagnosticsSerializer<'a> {
|
||||
pub fn new(diagnostics: &'a [Diagnostic]) -> Self {
|
||||
Self {
|
||||
diagnostics,
|
||||
source_files: Mutex::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.diagnostics.is_empty()
|
||||
}
|
||||
|
||||
pub fn diagnostics(&self) -> &[Diagnostic] {
|
||||
&self.diagnostics
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for DiagnosticsSerializer {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
let mut s = serializer.serialize_struct("DiagnosticSerializer", 2)?;
|
||||
s.serialize_field(
|
||||
"diagnostics",
|
||||
&DiagnosticSerializer {
|
||||
diagnostics: &self.diagnostics,
|
||||
source_files: &self.source_files,
|
||||
},
|
||||
)?;
|
||||
s.serialize_field("source_files", &*self.source_files.lock().unwrap())?;
|
||||
|
||||
s.end()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for DiagnosticsSerializer {
|
||||
fn deserialize<D>(_deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
Ok(Self {
|
||||
source_files: Mutex::new(HashMap::deserialize(_deserializer)?),
|
||||
diagnostics: todo!(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct DiagnosticSerializer<'a> {
|
||||
source_files: &'a Mutex<FxHashMap<String, String>>,
|
||||
diagnostics: &'a [Diagnostic],
|
||||
}
|
||||
|
||||
impl Serialize for DiagnosticSerializer<'_> {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
let mut s = serializer.serialize_seq(Some(self.diagnostics.len()))?;
|
||||
|
||||
for diag in self.diagnostics {
|
||||
s.serialize_element(&DiagnosticSerializerInner {
|
||||
diagnostic: diag,
|
||||
source_files: self.source_files,
|
||||
})?;
|
||||
}
|
||||
|
||||
s.end()
|
||||
}
|
||||
}
|
||||
|
||||
struct DiagnosticSerializerInner<'a> {
|
||||
source_files: &'a Mutex<FxHashMap<String, String>>,
|
||||
diagnostic: &'a Diagnostic,
|
||||
}
|
||||
|
||||
impl Serialize for DiagnosticSerializerInner<'_> {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
let mut s = serializer.serialize_struct("Diagnostic", 7)?;
|
||||
|
||||
s.serialize_field("id", &self.diagnostic.inner.id)?;
|
||||
s.serialize_field("severity", &self.diagnostic.inner.severity)?;
|
||||
s.serialize_field("message", &self.diagnostic.inner.message)?;
|
||||
s.serialize_field(
|
||||
"annotations",
|
||||
&AnnotationsSerializer {
|
||||
annotations: &self.diagnostic.inner.annotations,
|
||||
source_files: self.source_files,
|
||||
},
|
||||
)?;
|
||||
s.serialize_field(
|
||||
"subs",
|
||||
&SubDiagnosticsSerializer {
|
||||
subdiagnostics: &self.diagnostic.inner.subs,
|
||||
source_files: self.source_files,
|
||||
},
|
||||
)?;
|
||||
s.serialize_field("fix", &self.diagnostic.inner.fix)?;
|
||||
s.serialize_field("parent", &self.diagnostic.inner.parent)?;
|
||||
s.serialize_field("noqa_offset", &self.diagnostic.inner.noqa_offset)?;
|
||||
s.serialize_field("secondary_code", &self.diagnostic.inner.secondary_code)?;
|
||||
|
||||
s.end()
|
||||
}
|
||||
}
|
||||
|
||||
struct AnnotationsSerializer<'a> {
|
||||
source_files: &'a Mutex<FxHashMap<String, String>>,
|
||||
annotations: &'a [Annotation],
|
||||
}
|
||||
|
||||
impl Serialize for AnnotationsSerializer<'_> {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
let mut s = serializer.serialize_seq(Some(self.annotations.len()))?;
|
||||
|
||||
for annotation in self.annotations {
|
||||
s.serialize_element(&AnnotationSerializer {
|
||||
annotation,
|
||||
source_files: self.source_files,
|
||||
})?;
|
||||
}
|
||||
|
||||
s.end()
|
||||
}
|
||||
}
|
||||
|
||||
struct AnnotationSerializer<'a> {
|
||||
source_files: &'a Mutex<FxHashMap<String, String>>,
|
||||
annotation: &'a Annotation,
|
||||
}
|
||||
|
||||
impl Serialize for AnnotationSerializer<'_> {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
let mut s = serializer.serialize_struct("AnnotationSerializer", 4)?;
|
||||
|
||||
s.serialize_field(
|
||||
"span",
|
||||
&SpanSerializer {
|
||||
span: &self.annotation.span,
|
||||
source_files: self.source_files,
|
||||
},
|
||||
)?;
|
||||
s.serialize_field("message", &self.annotation.message)?;
|
||||
s.serialize_field("is_primary", &self.annotation.is_primary)?;
|
||||
s.serialize_field("tags", &self.annotation.tags)?;
|
||||
|
||||
s.end()
|
||||
}
|
||||
}
|
||||
|
||||
struct SpanSerializer<'a> {
|
||||
source_files: &'a Mutex<FxHashMap<String, String>>,
|
||||
span: &'a Span,
|
||||
}
|
||||
|
||||
impl Serialize for SpanSerializer<'_> {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
let mut s = serializer.serialize_struct("SpanSerializer", 2)?;
|
||||
|
||||
let file = self.span.expect_ruff_file();
|
||||
let name = file.name();
|
||||
let mut source_files = self.source_files.lock().unwrap();
|
||||
source_files.insert(name.to_string(), file.source_text().to_string());
|
||||
|
||||
s.serialize_field("file", name)?;
|
||||
s.serialize_field("range", &self.span.range)?;
|
||||
|
||||
s.end()
|
||||
}
|
||||
}
|
||||
|
||||
struct SubDiagnosticsSerializer<'a> {
|
||||
source_files: &'a Mutex<FxHashMap<String, String>>,
|
||||
subdiagnostics: &'a [SubDiagnostic],
|
||||
}
|
||||
|
||||
impl Serialize for SubDiagnosticsSerializer<'_> {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
let mut s = serializer.serialize_seq(Some(self.subdiagnostics.len()))?;
|
||||
|
||||
for subdiagnostic in self.subdiagnostics {
|
||||
s.serialize_element(&SubDiagnosticSerializer {
|
||||
subdiagnostic,
|
||||
source_files: self.source_files,
|
||||
})?;
|
||||
}
|
||||
|
||||
s.end()
|
||||
}
|
||||
}
|
||||
|
||||
struct SubDiagnosticSerializer<'a> {
|
||||
source_files: &'a Mutex<FxHashMap<String, String>>,
|
||||
subdiagnostic: &'a SubDiagnostic,
|
||||
}
|
||||
|
||||
impl Serialize for SubDiagnosticSerializer<'_> {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
let mut s = serializer.serialize_struct("SubdiagnosticSerializer", 4)?;
|
||||
|
||||
s.serialize_field("severity", &self.subdiagnostic.inner.severity)?;
|
||||
s.serialize_field("message", &self.subdiagnostic.inner.message)?;
|
||||
s.serialize_field(
|
||||
"annotations",
|
||||
&AnnotationsSerializer {
|
||||
annotations: &self.subdiagnostic.inner.annotations,
|
||||
source_files: self.source_files,
|
||||
},
|
||||
)?;
|
||||
|
||||
s.end()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for LintName {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
let s = String::deserialize(deserializer)?.into_boxed_str();
|
||||
// TODO not really how we want to handle this, probably intern again
|
||||
Ok(LintName::of(Box::leak(s)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue