|  | 
|  | 1 | +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT | 
|  | 2 | +// file at the top-level directory of this distribution and at | 
|  | 3 | +// http://rust-lang.org/COPYRIGHT. | 
|  | 4 | +// | 
|  | 5 | +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | 
|  | 6 | +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | 
|  | 7 | +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | 
|  | 8 | +// option. This file may not be copied, modified, or distributed | 
|  | 9 | +// except according to those terms. | 
|  | 10 | + | 
|  | 11 | +//! This module contains utilities for outputting metadata for diagnostic errors. | 
|  | 12 | +//! | 
|  | 13 | +//! Each set of errors is mapped to a metadata file by a name, which is | 
|  | 14 | +//! currently always a crate name. | 
|  | 15 | +
 | 
|  | 16 | +use std::collections::BTreeMap; | 
|  | 17 | +use std::env; | 
|  | 18 | +use std::path::PathBuf; | 
|  | 19 | +use std::fs::{read_dir, create_dir_all, OpenOptions, File}; | 
|  | 20 | +use std::io::{Read, Write}; | 
|  | 21 | +use std::error::Error; | 
|  | 22 | +use rustc_serialize::json::{self, as_json}; | 
|  | 23 | + | 
|  | 24 | +use codemap::Span; | 
|  | 25 | +use ext::base::ExtCtxt; | 
|  | 26 | +use diagnostics::plugin::{ErrorMap, ErrorInfo}; | 
|  | 27 | + | 
|  | 28 | +pub use self::Uniqueness::*; | 
|  | 29 | + | 
|  | 30 | +// Default metadata directory to use for extended error JSON. | 
|  | 31 | +const ERROR_METADATA_DIR_DEFAULT: &'static str = "tmp/extended-errors"; | 
|  | 32 | + | 
|  | 33 | +// The name of the environment variable that sets the metadata dir. | 
|  | 34 | +const ERROR_METADATA_VAR: &'static str = "ERROR_METADATA_DIR"; | 
|  | 35 | + | 
|  | 36 | +/// JSON encodable/decodable version of `ErrorInfo`. | 
|  | 37 | +#[derive(PartialEq, RustcDecodable, RustcEncodable)] | 
|  | 38 | +pub struct ErrorMetadata { | 
|  | 39 | +    pub description: Option<String>, | 
|  | 40 | +    pub use_site: Option<ErrorLocation> | 
|  | 41 | +} | 
|  | 42 | + | 
|  | 43 | +/// Mapping from error codes to metadata that can be (de)serialized. | 
|  | 44 | +pub type ErrorMetadataMap = BTreeMap<String, ErrorMetadata>; | 
|  | 45 | + | 
|  | 46 | +/// JSON encodable error location type with filename and line number. | 
|  | 47 | +#[derive(PartialEq, RustcDecodable, RustcEncodable)] | 
|  | 48 | +pub struct ErrorLocation { | 
|  | 49 | +    pub filename: String, | 
|  | 50 | +    pub line: usize | 
|  | 51 | +} | 
|  | 52 | + | 
|  | 53 | +impl ErrorLocation { | 
|  | 54 | +    /// Create an error location from a span. | 
|  | 55 | +    pub fn from_span(ecx: &ExtCtxt, sp: Span) -> ErrorLocation { | 
|  | 56 | +        let loc = ecx.codemap().lookup_char_pos_adj(sp.lo); | 
|  | 57 | +        ErrorLocation { | 
|  | 58 | +            filename: loc.filename, | 
|  | 59 | +            line: loc.line | 
|  | 60 | +        } | 
|  | 61 | +    } | 
|  | 62 | +} | 
|  | 63 | + | 
|  | 64 | +/// Type for describing the uniqueness of a set of error codes, as returned by `check_uniqueness`. | 
|  | 65 | +pub enum Uniqueness { | 
|  | 66 | +    /// All errors in the set checked are unique according to the metadata files checked. | 
|  | 67 | +    Unique, | 
|  | 68 | +    /// One or more errors in the set occur in another metadata file. | 
|  | 69 | +    /// This variant contains the first duplicate error code followed by the name | 
|  | 70 | +    /// of the metadata file where the duplicate appears. | 
|  | 71 | +    Duplicate(String, String) | 
|  | 72 | +} | 
|  | 73 | + | 
|  | 74 | +/// Get the directory where metadata files should be stored. | 
|  | 75 | +pub fn get_metadata_dir() -> PathBuf { | 
|  | 76 | +    match env::var(ERROR_METADATA_VAR) { | 
|  | 77 | +        Ok(v) => From::from(v), | 
|  | 78 | +        Err(_) => From::from(ERROR_METADATA_DIR_DEFAULT) | 
|  | 79 | +    } | 
|  | 80 | +} | 
|  | 81 | + | 
|  | 82 | +/// Get the path where error metadata for the set named by `name` should be stored. | 
|  | 83 | +fn get_metadata_path(name: &str) -> PathBuf { | 
|  | 84 | +    get_metadata_dir().join(format!("{}.json", name)) | 
|  | 85 | +} | 
|  | 86 | + | 
|  | 87 | +/// Check that the errors in `err_map` aren't present in any metadata files in the | 
|  | 88 | +/// metadata directory except the metadata file corresponding to `name`. | 
|  | 89 | +pub fn check_uniqueness(name: &str, err_map: &ErrorMap) -> Result<Uniqueness, Box<Error>> { | 
|  | 90 | +    let metadata_dir = get_metadata_dir(); | 
|  | 91 | +    let metadata_path = get_metadata_path(name); | 
|  | 92 | + | 
|  | 93 | +    // Create the error directory if it does not exist. | 
|  | 94 | +    try!(create_dir_all(&metadata_dir)); | 
|  | 95 | + | 
|  | 96 | +    // Check each file in the metadata directory. | 
|  | 97 | +    for entry in try!(read_dir(&metadata_dir)) { | 
|  | 98 | +        let path = try!(entry).path(); | 
|  | 99 | + | 
|  | 100 | +        // Skip any existing file for this set. | 
|  | 101 | +        if path == metadata_path { | 
|  | 102 | +            continue; | 
|  | 103 | +        } | 
|  | 104 | + | 
|  | 105 | +        // Read the metadata file into a string. | 
|  | 106 | +        let mut metadata_str = String::new(); | 
|  | 107 | +        try!( | 
|  | 108 | +            File::open(&path).and_then(|mut f| | 
|  | 109 | +            f.read_to_string(&mut metadata_str)) | 
|  | 110 | +        ); | 
|  | 111 | + | 
|  | 112 | +        // Parse the JSON contents. | 
|  | 113 | +        let metadata: ErrorMetadataMap = try!(json::decode(&metadata_str)); | 
|  | 114 | + | 
|  | 115 | +        // Check for duplicates. | 
|  | 116 | +        for err in err_map.keys() { | 
|  | 117 | +            let err_code = err.as_str(); | 
|  | 118 | +            if metadata.contains_key(err_code) { | 
|  | 119 | +                return Ok(Duplicate( | 
|  | 120 | +                    err_code.to_string(), | 
|  | 121 | +                    path.to_string_lossy().into_owned() | 
|  | 122 | +                )); | 
|  | 123 | +            } | 
|  | 124 | +        } | 
|  | 125 | +    } | 
|  | 126 | + | 
|  | 127 | +    Ok(Unique) | 
|  | 128 | +} | 
|  | 129 | + | 
|  | 130 | +/// Write metadata for the errors in `err_map` to disk, to a file corresponding to `name`. | 
|  | 131 | +pub fn output_metadata(ecx: &ExtCtxt, name: &str, err_map: &ErrorMap) | 
|  | 132 | +    -> Result<(), Box<Error>> | 
|  | 133 | +{ | 
|  | 134 | +    let metadata_path = get_metadata_path(name); | 
|  | 135 | + | 
|  | 136 | +    // Open the dump file. | 
|  | 137 | +    let mut dump_file = try!(OpenOptions::new() | 
|  | 138 | +        .write(true) | 
|  | 139 | +        .create(true) | 
|  | 140 | +        .open(&metadata_path) | 
|  | 141 | +    ); | 
|  | 142 | + | 
|  | 143 | +    // Construct a serializable map. | 
|  | 144 | +    let json_map = err_map.iter().map(|(k, &ErrorInfo { description, use_site })| { | 
|  | 145 | +        let key = k.as_str().to_string(); | 
|  | 146 | +        let value = ErrorMetadata { | 
|  | 147 | +            description: description.map(|n| n.as_str().to_string()), | 
|  | 148 | +            use_site: use_site.map(|sp| ErrorLocation::from_span(ecx, sp)) | 
|  | 149 | +        }; | 
|  | 150 | +        (key, value) | 
|  | 151 | +    }).collect::<ErrorMetadataMap>(); | 
|  | 152 | + | 
|  | 153 | +    try!(write!(&mut dump_file, "{}", as_json(&json_map))); | 
|  | 154 | +    Ok(()) | 
|  | 155 | +} | 
0 commit comments