#![cfg_attr(test, allow(clippy::expect_used, clippy::unwrap_used))]
use crate::span::SourceSpan;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Applicability {
MachineApplicable,
MaybeIncorrect,
HasPlaceholders,
Unspecified,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Suggestion {
message: String,
replacement: String,
applicability: Applicability,
}
impl Suggestion {
#[must_use]
pub fn new(
message: impl Into<String>,
replacement: impl Into<String>,
applicability: Applicability,
) -> Self {
Self {
message: message.into(),
replacement: replacement.into(),
applicability,
}
}
#[must_use]
pub fn message(&self) -> &str {
&self.message
}
#[must_use]
pub fn replacement(&self) -> &str {
&self.replacement
}
#[must_use]
pub const fn applicability(&self) -> Applicability {
self.applicability
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Diagnostic {
code: String,
message: String,
span: SourceSpan,
notes: Vec<String>,
helps: Vec<String>,
suggestions: Vec<Suggestion>,
}
impl Diagnostic {
#[must_use]
pub fn code(&self) -> &str {
&self.code
}
#[must_use]
pub fn message(&self) -> &str {
&self.message
}
#[must_use]
pub const fn span(&self) -> SourceSpan {
self.span
}
#[must_use]
pub fn notes(&self) -> &[String] {
&self.notes
}
#[must_use]
pub fn helps(&self) -> &[String] {
&self.helps
}
#[must_use]
pub fn suggestions(&self) -> &[Suggestion] {
&self.suggestions
}
}
pub struct DiagnosticBuilder {
diagnostic: Diagnostic,
}
impl DiagnosticBuilder {
fn new(code: impl Into<String>, message: impl Into<String>, span: SourceSpan) -> Self {
Self {
diagnostic: Diagnostic {
code: code.into(),
message: message.into(),
span,
notes: Vec::new(),
helps: Vec::new(),
suggestions: Vec::new(),
},
}
}
#[must_use]
pub fn note(mut self, note: impl Into<String>) -> Self {
self.diagnostic.notes.push(note.into());
self
}
#[must_use]
pub fn help(mut self, help: impl Into<String>) -> Self {
self.diagnostic.helps.push(help.into());
self
}
#[must_use]
pub fn suggestion(mut self, suggestion: Suggestion) -> Self {
self.diagnostic.suggestions.push(suggestion);
self
}
#[must_use]
pub fn build(self) -> Diagnostic {
self.diagnostic
}
}
#[must_use]
pub fn span_lint(
code: impl Into<String>,
message: impl Into<String>,
span: SourceSpan,
) -> DiagnosticBuilder {
DiagnosticBuilder::new(code, message, span)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::span::{SourceLocation, SourceSpan};
use rstest::rstest;
#[rstest]
fn builds_diagnostic() {
let span = SourceSpan::new(SourceLocation::new(2, 1), SourceLocation::new(2, 5))
.expect("valid span for diagnostic test");
let diagnostic = span_lint("lint", "Message", span)
.note("Note")
.help("Help")
.suggestion(Suggestion::new(
"Fix",
"fix()",
Applicability::MachineApplicable,
))
.build();
assert_eq!(diagnostic.code(), "lint");
assert_eq!(diagnostic.notes(), &[String::from("Note")]);
assert_eq!(diagnostic.helps(), &[String::from("Help")]);
assert_eq!(diagnostic.suggestions().len(), 1);
}
}