rapx/utils/
log.rs

1use chrono::Local;
2use fern::colors::{Color, ColoredLevelConfig};
3use fern::{self, Dispatch};
4use log::LevelFilter;
5use rustc_middle::mir::BasicBlock;
6use rustc_span::source_map::get_source_map;
7use rustc_span::{FileNameDisplayPreference, Pos, Span};
8use std::ops::Range;
9
10fn log_level() -> LevelFilter {
11    if let Ok(s) = std::env::var("RAP_LOG") {
12        match s.parse() {
13            Ok(level) => return level,
14            Err(err) => eprintln!("RAP_LOG is invalid: {err}"),
15        }
16    }
17    LevelFilter::Info
18}
19
20/// Detect `RAP_LOG` environment variable first; if it's not set,
21/// default to INFO level.
22pub fn init_log() -> Result<(), fern::InitError> {
23    let dispatch = Dispatch::new().level(log_level());
24
25    let color_line = ColoredLevelConfig::new()
26        .error(Color::Red)
27        .warn(Color::Yellow)
28        .info(Color::White)
29        .debug(Color::Blue)
30        .trace(Color::Cyan);
31
32    let color_level = color_line.info(Color::Green);
33    let stderr_dispatch = Dispatch::new()
34        .format(move |callback, args, record| {
35            let now = Local::now();
36            callback.finish(format_args!(
37                "{}{}|RAP|{}{}|: {}\x1B[0m",
38                format_args!(
39                    "\x1B[{}m",
40                    color_line.get_color(&record.level()).to_fg_str()
41                ),
42                now.format("%H:%M:%S"),
43                color_level.color(record.level()),
44                format_args!(
45                    "\x1B[{}m",
46                    color_line.get_color(&record.level()).to_fg_str()
47                ),
48                args
49            ))
50        })
51        .chain(std::io::stderr());
52
53    /* Note that we cannot dispatch to stdout due to some bugs */
54    dispatch.chain(stderr_dispatch).apply()?;
55    Ok(())
56}
57
58#[macro_export]
59macro_rules! rap_trace {
60    ($($arg:tt)+) => (
61        ::log::trace!(target: "RAP", $($arg)+)
62    );
63}
64
65#[macro_export]
66macro_rules! rap_debug {
67    ($($arg:tt)+) => (
68        ::log::debug!(target: "RAP", $($arg)+)
69    );
70}
71
72#[macro_export]
73macro_rules! rap_info {
74    ($($arg:tt)+) => (
75        ::log::info!(target: "RAP", $($arg)+)
76    );
77}
78
79#[macro_export]
80macro_rules! rap_warn {
81    ($($arg:tt)+) => (
82        ::log::warn!(target: "RAP", $($arg)+)
83    );
84}
85
86#[macro_export]
87macro_rules! rap_error {
88    ($($arg:tt)+) => (
89        ::log::error!(target: "RAP", $($arg)+)
90    );
91}
92
93pub fn rap_error_and_exit(msg: impl AsRef<str>) -> ! {
94    rap_error!("{}", msg.as_ref());
95    std::process::exit(1)
96}
97
98#[inline]
99pub fn span_to_source_code(span: Span) -> String {
100    get_source_map().unwrap().span_to_snippet(span).unwrap()
101}
102
103#[inline]
104pub fn span_to_first_line(span: Span) -> Span {
105    // extend the span to an entrie line or extract the first line if it has multiple lines
106    get_source_map()
107        .unwrap()
108        .span_extend_to_line(span.shrink_to_lo())
109}
110
111#[inline]
112pub fn span_to_trimmed_span(span: Span) -> Span {
113    // trim out the first few whitespace
114    span.trim_start(
115        get_source_map()
116            .unwrap()
117            .span_take_while(span, |c| c.is_whitespace()),
118    )
119    .unwrap()
120}
121
122#[inline]
123pub fn span_to_filename(span: Span) -> String {
124    get_source_map()
125        .unwrap()
126        .span_to_filename(span)
127        .display(FileNameDisplayPreference::Local)
128        .to_string()
129}
130
131#[inline]
132pub fn span_to_line_number(span: Span) -> usize {
133    get_source_map().unwrap().lookup_char_pos(span.lo()).line
134}
135
136pub fn get_variable_name<'tcx>(
137    body: &rustc_middle::mir::Body<'tcx>,
138    local_index: usize,
139) -> Option<String> {
140    let target_local = rustc_middle::mir::Local::from_usize(local_index);
141
142    for info in &body.var_debug_info {
143        if let rustc_middle::mir::VarDebugInfoContents::Place(place) = info.value {
144            if place.local == target_local && place.projection.is_empty() {
145                return Some(info.name.to_string());
146            }
147        }
148    }
149
150    None
151}
152
153pub fn get_basic_block_span<'tcx>(body: &rustc_middle::mir::Body<'tcx>, bb_index: usize) -> Span {
154    if bb_index >= body.basic_blocks.len() {
155        return body.span;
156    }
157
158    let bb = BasicBlock::from_usize(bb_index);
159    let block_data = &body.basic_blocks[bb];
160
161    if let Some(ref term) = block_data.terminator {
162        return term.source_info.span;
163    }
164
165    if let Some(stmt) = block_data.statements.first() {
166        return stmt.source_info.span;
167    }
168
169    body.span
170}
171
172#[inline]
173// this function computes the relative pos range of two spans which could be generated from two dirrerent files or not intersect with each other
174// warning: we just return 0..0 to drop off the unintersected pairs
175pub fn relative_pos_range(span: Span, sub_span: Span) -> Range<usize> {
176    if sub_span.lo() < span.lo() || sub_span.hi() > span.hi() {
177        return 0..0;
178    }
179    let offset = span.lo();
180    let lo = (sub_span.lo() - offset).to_usize();
181    let hi = (sub_span.hi() - offset).to_usize();
182    lo..hi
183}
184
185pub fn are_spans_in_same_file(span1: Span, span2: Span) -> bool {
186    let file1 = get_source_map().unwrap().lookup_source_file(span1.lo());
187    let file2 = get_source_map().unwrap().lookup_source_file(span2.lo());
188    file1.name == file2.name
189}