charon_lib/ast/
meta_utils.rs1use crate::meta::*;
3use crate::names::{Disambiguator, Name, PathElem};
4use itertools::Itertools;
5use std::borrow::Cow;
6use std::cmp::Ordering;
7use std::iter::Iterator;
8use std::ops::Range;
9
10fn line_to_start_byte(source: &str, line_nbr: usize) -> usize {
14 let mut cur_byte = 0;
15 for (i, line) in source.split_inclusive('\n').enumerate() {
16 if line_nbr == i + 1 {
17 break;
18 }
19 cur_byte += line.len();
20 }
21 cur_byte
22}
23
24impl Loc {
25 fn dummy() -> Self {
26 Loc { line: 0, col: 0 }
27 }
28
29 fn min(l0: &Loc, l1: &Loc) -> Loc {
30 match l0.line.cmp(&l1.line) {
31 Ordering::Equal => Loc {
32 line: l0.line,
33 col: std::cmp::min(l0.col, l1.col),
34 },
35 Ordering::Less => *l0,
36 Ordering::Greater => *l1,
37 }
38 }
39
40 fn max(l0: &Loc, l1: &Loc) -> Loc {
41 match l0.line.cmp(&l1.line) {
42 Ordering::Equal => Loc {
43 line: l0.line,
44 col: std::cmp::max(l0.col, l1.col),
45 },
46 Ordering::Greater => *l0,
47 Ordering::Less => *l1,
48 }
49 }
50
51 pub fn to_byte(self, source: &str) -> usize {
52 line_to_start_byte(source, self.line) + self.col
53 }
54}
55
56impl RawSpan {
57 pub fn dummy() -> Self {
58 RawSpan {
59 file_id: FileId::from_raw(0),
60 beg: Loc::dummy(),
61 end: Loc::dummy(),
62 }
63 }
64
65 fn sort_key(&self) -> impl Ord {
67 (self.file_id, self.beg, self.end)
68 }
69
70 pub fn to_byte_range(self, source: &str) -> Range<usize> {
71 self.beg.to_byte(source)..self.end.to_byte(source)
72 }
73}
74
75impl PartialOrd for RawSpan {
77 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
78 Some(self.cmp(other))
79 }
80}
81impl Ord for RawSpan {
82 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
83 self.sort_key().cmp(&other.sort_key())
84 }
85}
86
87impl Span {
88 pub fn dummy() -> Self {
89 Span {
90 span: RawSpan::dummy(),
91 generated_from_span: None,
92 }
93 }
94}
95
96pub fn combine_span(m0: &Span, m1: &Span) -> Span {
99 if m0.span.file_id == m1.span.file_id {
101 let span = RawSpan {
102 file_id: m0.span.file_id,
103 beg: Loc::min(&m0.span.beg, &m1.span.beg),
104 end: Loc::max(&m0.span.end, &m1.span.end),
105 };
106
107 Span {
111 span,
112 generated_from_span: None,
113 }
114 } else {
115 *m0
118 }
119}
120
121pub fn combine_span_iter<'a, T: Iterator<Item = &'a Span>>(mut ms: T) -> Span {
123 let mut mc: Span = *ms.next().unwrap();
125 for m in ms {
126 mc = combine_span(&mc, m);
127 }
128
129 mc
130}
131
132impl FileName {
133 pub fn to_string(&self) -> Cow<'_, str> {
134 match self {
135 FileName::Virtual(path_buf) | FileName::Local(path_buf) => path_buf.to_string_lossy(),
136 FileName::NotReal(path) => Cow::Borrowed(path),
137 }
138 }
139}
140
141impl Attribute {
142 pub fn parse_from_raw(raw_attr: RawAttribute) -> Result<Self, String> {
144 let path = raw_attr.path.split("::").collect_vec();
147 let attr_name = if let &[path_start, attr_name] = path.as_slice()
148 && (path_start == "charon" || path_start == "aeneas")
149 {
150 attr_name
151 } else {
152 return Ok(Self::Unknown(raw_attr));
153 };
154
155 match Self::parse_special_attr(attr_name, raw_attr.args.as_deref())? {
156 Some(parsed) => Ok(parsed),
157 None => Err(format!(
158 "Unrecognized attribute: `{}`",
159 raw_attr.to_string()
160 )),
161 }
162 }
163
164 fn parse_special_attr(attr_name: &str, args: Option<&str>) -> Result<Option<Self>, String> {
166 let parsed = match attr_name {
167 "opaque" if args.is_none() => Self::Opaque,
169 "rename" if let Some(attr) = args => {
171 let Some(attr) = attr
172 .strip_prefix("\"")
173 .and_then(|attr| attr.strip_suffix("\""))
174 else {
175 return Err(format!(
176 "the new name should be between quotes: `rename(\"{attr}\")`."
177 ));
178 };
179
180 if attr.is_empty() {
181 return Err(format!("attribute `rename` should not be empty"));
182 }
183
184 let first_char = attr.chars().nth(0).unwrap();
185 let is_identifier = (first_char.is_alphabetic() || first_char == '_')
186 && attr.chars().all(|c| c.is_alphanumeric() || c == '_');
187 if !is_identifier {
188 return Err(format!(
189 "attribute `rename` should contain a valid identifier"
190 ));
191 }
192
193 Self::Rename(attr.to_string())
194 }
195 "variants_prefix" if let Some(attr) = args => {
197 let Some(attr) = attr
198 .strip_prefix("\"")
199 .and_then(|attr| attr.strip_suffix("\""))
200 else {
201 return Err(format!(
202 "the name should be between quotes: `variants_prefix(\"{attr}\")`."
203 ));
204 };
205
206 Self::VariantsPrefix(attr.to_string())
207 }
208 "variants_suffix" if let Some(attr) = args => {
210 let Some(attr) = attr
211 .strip_prefix("\"")
212 .and_then(|attr| attr.strip_suffix("\""))
213 else {
214 return Err(format!(
215 "the name should be between quotes: `variants_suffix(\"{attr}\")`."
216 ));
217 };
218
219 Self::VariantsSuffix(attr.to_string())
220 }
221 _ => return Ok(None),
222 };
223 Ok(Some(parsed))
224 }
225}
226
227impl ItemOpacity {
228 pub fn with_content_visibility(self, contents_are_public: bool) -> Self {
229 use ItemOpacity::*;
230 match self {
231 Invisible => Invisible,
232 Transparent => Transparent,
233 Foreign if contents_are_public => Transparent,
234 Foreign => Opaque,
235 Opaque => Opaque,
236 }
237 }
238
239 pub fn with_private_contents(self) -> Self {
240 self.with_content_visibility(false)
241 }
242}
243
244impl ItemMeta {
245 pub fn renamed_name(&self) -> Name {
246 let mut name = self.name.clone();
247 if let Some(rename) = self.attr_info.rename.clone() {
248 *name.name.last_mut().unwrap() = PathElem::Ident(rename, Disambiguator::new(0));
249 }
250 name
251 }
252}
253
254impl Default for Span {
255 fn default() -> Self {
256 Self::dummy()
257 }
258}