]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/diagnostics/metadata.rs
Auto merge of #61817 - eddyb:begone-gcx-attempt-2, r=oli-obk
[rust.git] / src / libsyntax / diagnostics / metadata.rs
1 //! This module contains utilities for outputting metadata for diagnostic errors.
2 //!
3 //! Each set of errors is mapped to a metadata file by a name, which is
4 //! currently always a crate name.
5
6 use std::collections::BTreeMap;
7 use std::env;
8 use std::fs::{remove_file, create_dir_all, File};
9 use std::io::Write;
10 use std::path::PathBuf;
11 use std::error::Error;
12 use rustc_serialize::json::as_json;
13
14 use syntax_pos::{Span, FileName};
15
16 use crate::ext::base::ExtCtxt;
17 use crate::diagnostics::plugin::{ErrorMap, ErrorInfo};
18
19 /// JSON encodable/decodable version of `ErrorInfo`.
20 #[derive(PartialEq, RustcDecodable, RustcEncodable)]
21 pub struct ErrorMetadata {
22     pub description: Option<String>,
23     pub use_site: Option<ErrorLocation>
24 }
25
26 /// Mapping from error codes to metadata that can be (de)serialized.
27 pub type ErrorMetadataMap = BTreeMap<String, ErrorMetadata>;
28
29 /// JSON encodable error location type with filename and line number.
30 #[derive(PartialEq, RustcDecodable, RustcEncodable)]
31 pub struct ErrorLocation {
32     pub filename: FileName,
33     pub line: usize
34 }
35
36 impl ErrorLocation {
37     /// Creates an error location from a span.
38     pub fn from_span(ecx: &ExtCtxt<'_>, sp: Span) -> ErrorLocation {
39         let loc = ecx.source_map().lookup_char_pos(sp.lo());
40         ErrorLocation {
41             filename: loc.file.name.clone(),
42             line: loc.line
43         }
44     }
45 }
46
47 /// Gets the directory where metadata for a given `prefix` should be stored.
48 ///
49 /// See `output_metadata`.
50 pub fn get_metadata_dir(prefix: &str) -> PathBuf {
51     env::var_os("RUSTC_ERROR_METADATA_DST")
52         .map(PathBuf::from)
53         .expect("env var `RUSTC_ERROR_METADATA_DST` isn't set")
54         .join(prefix)
55 }
56
57 /// Map `name` to a path in the given directory: <directory>/<name>.json
58 fn get_metadata_path(directory: PathBuf, name: &str) -> PathBuf {
59     directory.join(format!("{}.json", name))
60 }
61
62 /// Write metadata for the errors in `err_map` to disk, to a file corresponding to `prefix/name`.
63 ///
64 /// For our current purposes the prefix is the target architecture and the name is a crate name.
65 /// If an error occurs steps will be taken to ensure that no file is created.
66 pub fn output_metadata(ecx: &ExtCtxt<'_>, prefix: &str, name: &str, err_map: &ErrorMap)
67     -> Result<(), Box<dyn Error>>
68 {
69     // Create the directory to place the file in.
70     let metadata_dir = get_metadata_dir(prefix);
71     create_dir_all(&metadata_dir)?;
72
73     // Open the metadata file.
74     let metadata_path = get_metadata_path(metadata_dir, name);
75     let mut metadata_file = File::create(&metadata_path)?;
76
77     // Construct a serializable map.
78     let json_map = err_map.iter().map(|(k, &ErrorInfo { description, use_site })| {
79         let key = k.as_str().to_string();
80         let value = ErrorMetadata {
81             description: description.map(|n| n.as_str().to_string()),
82             use_site: use_site.map(|sp| ErrorLocation::from_span(ecx, sp))
83         };
84         (key, value)
85     }).collect::<ErrorMetadataMap>();
86
87     // Write the data to the file, deleting it if the write fails.
88     let result = write!(&mut metadata_file, "{}", as_json(&json_map));
89     if result.is_err() {
90         remove_file(&metadata_path)?;
91     }
92     Ok(result?)
93 }