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{ line: 4, column: 15 }
35/// });
36/// let diagnostic = Diagnostic::new_error("Something went wrong", span)
37/// .with_help("Have you tried turning it on and off again?", None);
38/// ```
39#[derive(Debug, Clone)]
40pub struct Diagnostic {
41 pub kind: DiagnosticKind,
42 pub message: String,
43 pub span: Option<Span>,
44 pub notes: Vec<DiagnosticNote>,
45 pub helps: Vec<DiagnosticHelp>,
46}
47
48/// A note enriches a [`Diagnostic`] with extra information, possibly referring
49/// to different locations in the original SQL query, that helps contextualize
50/// the error and helps the end user understand why it occurred.
51///
52/// Example:
53/// SELECT id, name FROM users GROUP BY id
54/// Note: ^^^^ 'name' is not in the GROUP BY clause
55#[derive(Debug, Clone)]
56pub struct DiagnosticNote {
57 pub message: String,
58 pub span: Option<Span>,
59}
60
61/// A "help" enriches a [`Diagnostic`] with extra information, possibly
62/// referring to different locations in the original SQL query, that helps the
63/// user understand how they might fix the error or warning.
64///
65/// Example:
66/// SELECT id, name FROM users GROUP BY id
67/// Help: Add 'name' here ^^^^
68#[derive(Debug, Clone)]
69pub struct DiagnosticHelp {
70 pub message: String,
71 pub span: Option<Span>,
72}
73
74/// A [`Diagnostic`] can either be a hard error that prevents the query from
75/// being planned and executed, or a warning that indicates potential issues,
76/// performance problems, or causes for unexpected results, but is non-fatal.
77/// This enum expresses these two possibilities.
78#[derive(Debug, Clone, Copy, PartialEq, Eq)]
79pub enum DiagnosticKind {
80 Error,
81 Warning,
82}
83
84impl Diagnostic {
85 /// Creates a new [`Diagnostic`] for a fatal error that prevents the SQL
86 /// query from being planned and executed. Optionally takes in a [`Span`] to
87 /// describe the location in the source code that caused the error, should
88 /// be provided when available.
89 pub fn new_error(message: impl Into<String>, span: Option<Span>) -> Self {
90 Self {
91 kind: DiagnosticKind::Error,
92 message: message.into(),
93 span,
94 notes: Vec::new(),
95 helps: Vec::new(),
96 }
97 }
98
99 /// Creates a new [`Diagnostic`] for a NON-fatal warning, such as a
100 /// performance problem, or possible cause for undesired results. Optionally
101 /// takes in a [`Span`] to describe the location in the source code that
102 /// caused the error, should be provided when available.
103 pub fn new_warning(message: impl Into<String>, span: Option<Span>) -> Self {
104 Self {
105 kind: DiagnosticKind::Warning,
106 message: message.into(),
107 span,
108 notes: Vec::new(),
109 helps: Vec::new(),
110 }
111 }
112
113 /// Adds a "note" to the [`Diagnostic`], which can have zero or many. A "note"
114 /// helps contextualize the error and helps the end user understand why it
115 /// occurred. It can refer to an arbitrary location in the SQL query, or to
116 /// no location.
117 pub fn add_note(&mut self, message: impl Into<String>, span: Option<Span>) {
118 self.notes.push(DiagnosticNote {
119 message: message.into(),
120 span,
121 });
122 }
123
124 /// Adds a "help" to the [`Diagnostic`], which can have zero or many. A
125 /// "help" helps the user understand how they might fix the error or
126 /// warning. It can refer to an arbitrary location in the SQL query, or to
127 /// no location.
128 pub fn add_help(&mut self, message: impl Into<String>, span: Option<Span>) {
129 self.helps.push(DiagnosticHelp {
130 message: message.into(),
131 span,
132 });
133 }
134
135 /// Like [`Diagnostic::add_note`], but returns `self` to allow chaining.
136 pub fn with_note(mut self, message: impl Into<String>, span: Option<Span>) -> Self {
137 self.add_note(message.into(), span);
138 self
139 }
140
141 /// Like [`Diagnostic::add_help`], but returns `self` to allow chaining.
142 pub fn with_help(mut self, message: impl Into<String>, span: Option<Span>) -> Self {
143 self.add_help(message.into(), span);
144 self
145 }
146}