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