summaryrefslogtreecommitdiffstats
path: root/third_party/rust/bindgen/diagnostics.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/bindgen/diagnostics.rs')
-rw-r--r--third_party/rust/bindgen/diagnostics.rs189
1 files changed, 189 insertions, 0 deletions
diff --git a/third_party/rust/bindgen/diagnostics.rs b/third_party/rust/bindgen/diagnostics.rs
new file mode 100644
index 0000000000..f765afe970
--- /dev/null
+++ b/third_party/rust/bindgen/diagnostics.rs
@@ -0,0 +1,189 @@
+//! Types and function used to emit pretty diagnostics for `bindgen`.
+//!
+//! The entry point of this module is the [`Diagnostic`] type.
+
+use std::fmt::Write;
+use std::io::{self, BufRead, BufReader};
+use std::{borrow::Cow, fs::File};
+
+use annotate_snippets::{
+ display_list::{DisplayList, FormatOptions},
+ snippet::{Annotation, Slice as ExtSlice, Snippet},
+};
+
+use annotate_snippets::snippet::AnnotationType;
+
+#[derive(Clone, Copy, Debug)]
+pub(crate) enum Level {
+ Error,
+ Warn,
+ Info,
+ Note,
+ Help,
+}
+
+impl From<Level> for AnnotationType {
+ fn from(level: Level) -> Self {
+ match level {
+ Level::Error => Self::Error,
+ Level::Warn => Self::Warning,
+ Level::Info => Self::Info,
+ Level::Note => Self::Note,
+ Level::Help => Self::Help,
+ }
+ }
+}
+
+/// A `bindgen` diagnostic.
+#[derive(Default)]
+pub(crate) struct Diagnostic<'a> {
+ title: Option<(Cow<'a, str>, Level)>,
+ slices: Vec<Slice<'a>>,
+ footer: Vec<(Cow<'a, str>, Level)>,
+}
+
+impl<'a> Diagnostic<'a> {
+ /// Add a title to the diagnostic and set its type.
+ pub(crate) fn with_title(
+ &mut self,
+ title: impl Into<Cow<'a, str>>,
+ level: Level,
+ ) -> &mut Self {
+ self.title = Some((title.into(), level));
+ self
+ }
+
+ /// Add a slice of source code to the diagnostic.
+ pub(crate) fn add_slice(&mut self, slice: Slice<'a>) -> &mut Self {
+ self.slices.push(slice);
+ self
+ }
+
+ /// Add a footer annotation to the diagnostic. This annotation will have its own type.
+ pub(crate) fn add_annotation(
+ &mut self,
+ msg: impl Into<Cow<'a, str>>,
+ level: Level,
+ ) -> &mut Self {
+ self.footer.push((msg.into(), level));
+ self
+ }
+
+ /// Print this diagnostic.
+ ///
+ /// The diagnostic is printed using `cargo:warning` if `bindgen` is being invoked by a build
+ /// script or using `eprintln` otherwise.
+ pub(crate) fn display(&self) {
+ std::thread_local! {
+ static INVOKED_BY_BUILD_SCRIPT: bool = std::env::var_os("CARGO_CFG_TARGET_ARCH").is_some();
+ }
+
+ let mut title = None;
+ let mut footer = vec![];
+ let mut slices = vec![];
+ if let Some((msg, level)) = &self.title {
+ title = Some(Annotation {
+ id: Some("bindgen"),
+ label: Some(msg.as_ref()),
+ annotation_type: (*level).into(),
+ })
+ }
+
+ for (msg, level) in &self.footer {
+ footer.push(Annotation {
+ id: None,
+ label: Some(msg.as_ref()),
+ annotation_type: (*level).into(),
+ });
+ }
+
+ // add additional info that this is generated by bindgen
+ // so as to not confuse with rustc warnings
+ footer.push(Annotation {
+ id: None,
+ label: Some("This diagnostic was generated by bindgen."),
+ annotation_type: AnnotationType::Info,
+ });
+
+ for slice in &self.slices {
+ if let Some(source) = &slice.source {
+ slices.push(ExtSlice {
+ source: source.as_ref(),
+ line_start: slice.line.unwrap_or_default(),
+ origin: slice.filename.as_deref(),
+ annotations: vec![],
+ fold: false,
+ })
+ }
+ }
+
+ let snippet = Snippet {
+ title,
+ footer,
+ slices,
+ opt: FormatOptions {
+ color: true,
+ ..Default::default()
+ },
+ };
+ let dl = DisplayList::from(snippet);
+
+ if INVOKED_BY_BUILD_SCRIPT.with(Clone::clone) {
+ // This is just a hack which hides the `warning:` added by cargo at the beginning of
+ // every line. This should be fine as our diagnostics already have a colorful title.
+ // FIXME (pvdrz): Could it be that this doesn't work in other languages?
+ let hide_warning = "\r \r";
+ let string = dl.to_string();
+ for line in string.lines() {
+ println!("cargo:warning={}{}", hide_warning, line);
+ }
+ } else {
+ eprintln!("{}\n", dl);
+ }
+ }
+}
+
+/// A slice of source code.
+#[derive(Default)]
+pub(crate) struct Slice<'a> {
+ source: Option<Cow<'a, str>>,
+ filename: Option<String>,
+ line: Option<usize>,
+}
+
+impl<'a> Slice<'a> {
+ /// Set the source code.
+ pub(crate) fn with_source(
+ &mut self,
+ source: impl Into<Cow<'a, str>>,
+ ) -> &mut Self {
+ self.source = Some(source.into());
+ self
+ }
+
+ /// Set the file, line and column.
+ pub(crate) fn with_location(
+ &mut self,
+ mut name: String,
+ line: usize,
+ col: usize,
+ ) -> &mut Self {
+ write!(name, ":{}:{}", line, col)
+ .expect("Writing to a string cannot fail");
+ self.filename = Some(name);
+ self.line = Some(line);
+ self
+ }
+}
+
+pub(crate) fn get_line(
+ filename: &str,
+ line: usize,
+) -> io::Result<Option<String>> {
+ let file = BufReader::new(File::open(filename)?);
+ if let Some(line) = file.lines().nth(line.wrapping_sub(1)) {
+ return line.map(Some);
+ }
+
+ Ok(None)
+}