datafusion_common/diagnostic.rs
1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements. See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership. The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License. You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied. See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18use crate::Span;
19
20/// Additional contextual information intended for end users, to help them
21/// understand what went wrong by providing human-readable messages, and
22/// locations in the source query that relate to the error in some way.
23///
24/// You can think of a single [`Diagnostic`] as a single "block" of output from
25/// rustc. i.e. either an error or a warning, optionally with some notes and
26/// help messages.
27///
28/// Example:
29///
30/// ```rust
31/// # use datafusion_common::{Location, Span, Diagnostic};
32/// let span = Some(Span {
33/// start: Location { line: 2, column: 1 },
34/// end: Location {
35/// line: 4,
36/// column: 15,
37/// },
38/// });
39/// let diagnostic = Diagnostic::new_error("Something went wrong", span)
40/// .with_help("Have you tried turning it on and off again?", None);
41/// ```
42#[derive(Debug, Clone)]
43pub struct Diagnostic {
44 pub kind: DiagnosticKind,
45 pub message: String,
46 pub span: Option<Span>,
47 pub notes: Vec<DiagnosticNote>,
48 pub helps: Vec<DiagnosticHelp>,
49}
50
51/// A note enriches a [`Diagnostic`] with extra information, possibly referring
52/// to different locations in the original SQL query, that helps contextualize
53/// the error and helps the end user understand why it occurred.
54///
55/// Example:
56/// SELECT id, name FROM users GROUP BY id
57/// Note: ^^^^ 'name' is not in the GROUP BY clause
58#[derive(Debug, Clone)]
59pub struct DiagnosticNote {
60 pub message: String,
61 pub span: Option<Span>,
62}
63
64/// A "help" enriches a [`Diagnostic`] with extra information, possibly
65/// referring to different locations in the original SQL query, that helps the
66/// user understand how they might fix the error or warning.
67///
68/// Example:
69/// SELECT id, name FROM users GROUP BY id
70/// Help: Add 'name' here ^^^^
71#[derive(Debug, Clone)]
72pub struct DiagnosticHelp {
73 pub message: String,
74 pub span: Option<Span>,
75}
76
77/// A [`Diagnostic`] can either be a hard error that prevents the query from
78/// being planned and executed, or a warning that indicates potential issues,
79/// performance problems, or causes for unexpected results, but is non-fatal.
80/// This enum expresses these two possibilities.
81#[derive(Debug, Clone, Copy, PartialEq, Eq)]
82pub enum DiagnosticKind {
83 Error,
84 Warning,
85}
86
87impl Diagnostic {
88 /// Creates a new [`Diagnostic`] for a fatal error that prevents the SQL
89 /// query from being planned and executed. Optionally takes in a [`Span`] to
90 /// describe the location in the source code that caused the error, should
91 /// be provided when available.
92 pub fn new_error(message: impl Into<String>, span: Option<Span>) -> Self {
93 Self {
94 kind: DiagnosticKind::Error,
95 message: message.into(),
96 span,
97 notes: Vec::new(),
98 helps: Vec::new(),
99 }
100 }
101
102 /// Creates a new [`Diagnostic`] for a NON-fatal warning, such as a
103 /// performance problem, or possible cause for undesired results. Optionally
104 /// takes in a [`Span`] to describe the location in the source code that
105 /// caused the error, should be provided when available.
106 pub fn new_warning(message: impl Into<String>, span: Option<Span>) -> Self {
107 Self {
108 kind: DiagnosticKind::Warning,
109 message: message.into(),
110 span,
111 notes: Vec::new(),
112 helps: Vec::new(),
113 }
114 }
115
116 /// Adds a "note" to the [`Diagnostic`], which can have zero or many. A "note"
117 /// helps contextualize the error and helps the end user understand why it
118 /// occurred. It can refer to an arbitrary location in the SQL query, or to
119 /// no location.
120 pub fn add_note(&mut self, message: impl Into<String>, span: Option<Span>) {
121 self.notes.push(DiagnosticNote {
122 message: message.into(),
123 span,
124 });
125 }
126
127 /// Adds a "help" to the [`Diagnostic`], which can have zero or many. A
128 /// "help" helps the user understand how they might fix the error or
129 /// warning. It can refer to an arbitrary location in the SQL query, or to
130 /// no location.
131 pub fn add_help(&mut self, message: impl Into<String>, span: Option<Span>) {
132 self.helps.push(DiagnosticHelp {
133 message: message.into(),
134 span,
135 });
136 }
137
138 /// Like [`Diagnostic::add_note`], but returns `self` to allow chaining.
139 pub fn with_note(mut self, message: impl Into<String>, span: Option<Span>) -> Self {
140 self.add_note(message.into(), span);
141 self
142 }
143
144 /// Like [`Diagnostic::add_help`], but returns `self` to allow chaining.
145 pub fn with_help(mut self, message: impl Into<String>, span: Option<Span>) -> Self {
146 self.add_help(message.into(), span);
147 self
148 }
149}