Skip to main content

formally_support/
location.rs

1//
2// ::formally - the open-source formal methods toolchain
3//
4// Copyright (c) 2025 Nicola Gigante
5//
6// Permission is hereby granted, free of charge, to any person obtaining a copy
7// of this software and associated documentation files (the "Software"), to deal
8// in the Software without restriction, including without limitation the rights
9// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10// copies of the Software, and to permit persons to whom the Software is
11// furnished to do so, subject to the following conditions:
12//
13// The above copyright notice and this permission notice shall be included in
14// all copies or substantial portions of the Software.
15//
16// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22// SOFTWARE.
23//
24
25use std::{
26    borrow::Cow,
27    error::Error,
28    fmt::{self, Display, Formatter},
29    hash::Hash,
30    ops::Deref,
31    ops::Sub,
32    path::{Path, PathBuf},
33    sync::Arc,
34};
35
36use crate::*;
37
38use derive_more::Display;
39
40/// Wrap any type with location information.
41///
42/// [Loc] is used to wrap a type to give it location information. It is a simple struct adjoining
43/// any given type to an `Option<Span>` and implementing [Located] and [Locatable]. This is meant to
44/// be done to types that are not under control and cannot be made to implement the traits directly
45/// because of the orphan instance rules, or because giving them a [Span] all the time does not make
46/// sense. Otherwise, deriving [Located] and [Locatable] directly using the associated derive
47/// macros is preferred.
48///
49/// It is often controverial to decide whether a [Located] type should compare equal accounting for
50/// its span or not. For this reason, [Loc] intentionally does not implement [PartialEq] nor [Eq].
51#[derive(Debug, Default, Clone, Hash, PartialEq, Eq)]
52pub struct Loc<T> {
53    pub value: T,
54    pub span: Option<Span>,
55}
56
57impl<T> Loc<T> {
58    /// Creates a new [Loc] from a value without any [Span] information.
59    pub const fn new(value: T) -> Loc<T> {
60        Loc { span: None, value }
61    }
62
63    /// Transforms the inner value by applying the given function
64    pub fn map<F, R>(self, f: F) -> Loc<R>
65    where
66        F: FnOnce(T) -> R,
67    {
68        Loc {
69            value: f(self.value),
70            span: self.span,
71        }
72    }
73}
74
75impl<T> Located for Loc<T> {
76    fn span(&self) -> Option<Span> {
77        self.span.clone()
78    }
79}
80
81impl<T> Locatable for Loc<T> {
82    type Located = Loc<T>;
83
84    fn over(self, span: impl Into<Option<Span>>) -> Loc<T> {
85        Loc {
86            span: span.into(),
87            ..self
88        }
89    }
90}
91
92impl<T: Display> Display for Loc<T> {
93    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
94        Display::fmt(&self.value, f)
95    }
96}
97
98impl<T: Error> Error for Loc<T> {}
99
100impl<T> From<T> for Loc<T> {
101    fn from(value: T) -> Self {
102        Loc::new(value)
103    }
104}
105
106impl<T> Deref for Loc<T> {
107    type Target = T;
108
109    fn deref(&self) -> &T {
110        &self.value
111    }
112}
113
114/// Tracks the origin of a set of source locations
115///
116/// This type is used as a component of a [Span] to tell the origin of a source file
117/// (e.g. an actual file, or stdin, or a buffer, etc.). This is mainly useful to give show error
118/// messages to the user.
119#[derive(Debug, Clone, Default, Hash, PartialEq, Eq)]
120pub enum Origin {
121    /// The origin of the source locations is unknown
122    #[default]
123    Unknown,
124    /// The origin of the source locations is an in-memory buffer or string
125    Buffer,
126    /// The origin of the source locations is the standard input stream
127    StdIn,
128    /// The origin of the source locations is a file at the given path
129    Path(Arc<PathBuf>),
130}
131
132impl Display for Origin {
133    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134        match self {
135            Origin::Unknown => f.write_str("<unknown>"),
136            Origin::StdIn => f.write_str("<stdin>"),
137            Origin::Buffer => f.write_str("<buffer>"),
138            Origin::Path(path) => Display::fmt(&path.display(), f),
139        }
140    }
141}
142
143impl<T: AsRef<Path>> From<T> for Origin {
144    fn from(value: T) -> Self {
145        Origin::Path(Arc::new(PathBuf::from(value.as_ref())))
146    }
147}
148
149/// Represent a position in a source file.
150///
151/// [Location] represents the position of a single character or token in a source file, given by its
152/// `position` which is the actual index in the corresponding buffer, and a pair of `line` and
153/// `column` information.
154///
155/// Line and column information start from zero, but the [Display] instance of [Location] and [Span]
156/// show line numbers to the user as starting from one.
157#[derive(Debug, Clone, Copy, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
158pub struct Location {
159    /// The index of this location in the corresponding buffer
160    pub position: usize,
161    /// The line number corresponding to this location
162    pub line: usize,
163    /// The column number corresponding to this location
164    pub column: usize,
165}
166
167impl Display for Location {
168    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
169        write!(f, "{}:{}", self.line + 1, self.column)
170    }
171}
172
173impl Sub for Location {
174    type Output = usize;
175
176    fn sub(self, rhs: Self) -> usize {
177        self.position - rhs.position
178    }
179}
180
181/// Track an interval between two locations in a source file.
182///
183/// [Span] tracks an interval in a source file, represented either as a pair of `begin`/`end`
184/// [Location] objects in the [Span::Span] variant, or as the [Span::Whole] variant that represents
185/// the entire source file under consideration.
186///
187/// All the components in `formally` try hard to preserve locations and spans across the board, in
188/// order to give users informative error messages even for errors originating quite far from the
189/// parsers.
190///
191/// This means that any type that can be created even indirectly by parsing something from the user
192/// should probably contain a [Span] and implement at least the [Located] trait, and maybe also
193/// [Locatable].
194#[derive(Debug, Clone, Hash, PartialEq, Eq)]
195pub enum Span {
196    /// The [Span] represents the whole source file at the given [Origin].
197    Whole(Origin),
198    /// The [Span] represents a built-in or primitive entity such as functions defined by SMT
199    /// theories
200    Builtin,
201    /// The [Span] represents the interval between `begin` (included) and `end` (excluded) at the
202    /// given [Origin].
203    Span {
204        /// The origin of the span.
205        origin: Origin,
206        /// The starting position of the interval (included).
207        begin: Location,
208        /// The end of the interval (excluded).
209        end: Location,
210    },
211}
212
213impl Default for Span {
214    /// The default [Span] is `Span::Whole(Origin::Unknown)`
215    fn default() -> Self {
216        Span::Whole(Origin::default())
217    }
218}
219
220impl Span {
221    /// Creates a default [Span], i.e. `Span::Whole(Origin::Unknown)`
222    pub fn new() -> Span {
223        Span::default()
224    }
225}
226
227impl Display for Span {
228    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
229        match self {
230            Span::Whole(origin) => Display::fmt(origin, f),
231            Span::Builtin => write!(f, "builtin"),
232            Span::Span { origin, begin, end } => {
233                if end.column > begin.column + 1 {
234                    write!(
235                        f,
236                        "{}:[{}:{}, {}:{})",
237                        origin,
238                        begin.line + 1,
239                        begin.column,
240                        end.line + 1,
241                        end.column
242                    )
243                } else {
244                    write!(f, "{}:{}:{}", origin, begin.line + 1, begin.column)
245                }
246            }
247        }
248    }
249}
250
251/// Types that track a [Span].
252///
253/// [Located] is a small trait to get a [Span] from types that track span information. The [Span] is
254/// returned in an [Option] because even types that track spans very precisely may not have such
255/// information available in the first place, probably when constructed by hand instead of being
256/// parsed.
257///
258/// [Located] can be automatically derived (see the corresponding proc-macro for details).
259pub trait Located {
260    /// Gets the [Span] tracked by the type.
261    fn span(&self) -> Option<Span>;
262}
263
264impl Located for Option<Span> {
265    fn span(&self) -> Option<Span> {
266        self.clone()
267    }
268}
269
270/// Add or set a [Span] to a type.
271///
272/// [Locatable] is for [Located] types that support changing their tracked [Span], or for types that
273/// can be turned into [Located] types. The [Locatable::over] method is a builder method used to
274/// create a new object with a different [Span], and the `Located` associated type tell the type of
275/// the result.
276///
277/// Example:
278/// ```text
279/// let term = term!(* x y).over(span);
280/// ```
281///
282/// [Locatable] can be automatically derived (see the corresponding proc-macro for details).
283pub trait Locatable: Sized {
284    type Located: Located;
285
286    /// Turns `self` into an object of type `Self::Located` tracking the new span.
287    fn over(self, span: impl Into<Option<Span>>) -> Self::Located;
288}
289
290impl Locatable for Option<Span> {
291    type Located = Option<Span>;
292
293    fn over(self, span: impl Into<Option<Span>>) -> Self::Located {
294        span.into()
295    }
296}
297
298/// A string associated with its source span.
299///
300/// [Identifier] just holds a `Cow<'a, str>` together with a [Span]. It is used in `formally`
301/// whenever an identifier coming from an origin source code has to be stored or passed as argument.
302/// Using [Cow] allows to avoid unnecessary clones when identifiers are passed directly as arguments
303/// from string slices.
304#[derive(Debug, Clone, Hash, PartialEq, Eq, Display, Located, Locatable)]
305#[display("{name}")]
306pub struct Identifier<'a> {
307    name: Cow<'a, str>,
308    span: Option<Span>,
309}
310
311impl<'a> Identifier<'a> {
312    pub const fn new(name: &'a str) -> Self {
313        Identifier {
314            name: Cow::Borrowed(name),
315            span: None,
316        }
317    }
318
319    /// Return a reference to the stored name.
320    pub fn name(&self) -> &str {
321        &self.name
322    }
323
324    /// Turn the identifier into the inner `Cow<'a, str>`
325    pub fn into_inner(self) -> Cow<'a, str> {
326        self.name
327    }
328
329    /// Turn the identifier into its inner string.
330    pub fn into_string(self) -> String {
331        self.name.into_owned()
332    }
333
334    pub fn into_owned(self) -> Identifier<'static> {
335        match self.name {
336            Cow::Borrowed(name) => Identifier {
337                name: Cow::Owned(name.to_string()),
338                span: self.span,
339            },
340            Cow::Owned(name) => Identifier {
341                name: Cow::Owned(name),
342                span: self.span,
343            },
344        }
345    }
346}
347
348impl Deref for Identifier<'_> {
349    type Target = str;
350
351    fn deref(&self) -> &str {
352        &self.name
353    }
354}
355
356impl From<String> for Identifier<'_> {
357    fn from(name: String) -> Self {
358        Identifier {
359            name: Cow::from(name),
360            span: None,
361        }
362    }
363}
364
365impl<'a> From<&'a str> for Identifier<'a> {
366    fn from(name: &'a str) -> Self {
367        Identifier::new(name)
368    }
369}
370
371impl<'a> From<Cow<'a, str>> for Identifier<'a> {
372    fn from(name: Cow<'a, str>) -> Self {
373        Identifier { name, span: None }
374    }
375}