ICH: Add ability to test the ICH of exported metadata items.
This commit is contained in:
parent
f2c53ea66b
commit
6a2666d5b0
@ -59,9 +59,7 @@ use std::io::Write;
|
||||
use syntax::ast;
|
||||
use syntax::parse::token::InternedString;
|
||||
use syntax_pos::Span;
|
||||
|
||||
const IF_THIS_CHANGED: &'static str = "rustc_if_this_changed";
|
||||
const THEN_THIS_WOULD_NEED: &'static str = "rustc_then_this_would_need";
|
||||
use {ATTR_IF_THIS_CHANGED, ATTR_THEN_THIS_WOULD_NEED};
|
||||
|
||||
pub fn assert_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
|
||||
let _ignore = tcx.dep_graph.in_ignore();
|
||||
@ -91,7 +89,7 @@ pub fn assert_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
|
||||
assert!(tcx.sess.opts.debugging_opts.query_dep_graph,
|
||||
"cannot use the `#[{}]` or `#[{}]` annotations \
|
||||
without supplying `-Z query-dep-graph`",
|
||||
IF_THIS_CHANGED, THEN_THIS_WOULD_NEED);
|
||||
ATTR_IF_THIS_CHANGED, ATTR_THEN_THIS_WOULD_NEED);
|
||||
}
|
||||
|
||||
// Check paths.
|
||||
@ -125,7 +123,7 @@ impl<'a, 'tcx> IfThisChanged<'a, 'tcx> {
|
||||
fn process_attrs(&mut self, node_id: ast::NodeId, attrs: &[ast::Attribute]) {
|
||||
let def_id = self.tcx.map.local_def_id(node_id);
|
||||
for attr in attrs {
|
||||
if attr.check_name(IF_THIS_CHANGED) {
|
||||
if attr.check_name(ATTR_IF_THIS_CHANGED) {
|
||||
let dep_node_interned = self.argument(attr);
|
||||
let dep_node = match dep_node_interned {
|
||||
None => DepNode::Hir(def_id),
|
||||
@ -141,7 +139,7 @@ impl<'a, 'tcx> IfThisChanged<'a, 'tcx> {
|
||||
}
|
||||
};
|
||||
self.if_this_changed.push((attr.span, def_id, dep_node));
|
||||
} else if attr.check_name(THEN_THIS_WOULD_NEED) {
|
||||
} else if attr.check_name(ATTR_THEN_THIS_WOULD_NEED) {
|
||||
let dep_node_interned = self.argument(attr);
|
||||
let dep_node = match dep_node_interned {
|
||||
Some(ref n) => {
|
||||
|
@ -28,6 +28,7 @@
|
||||
//! at the beginning.
|
||||
|
||||
use syntax::ast;
|
||||
use std::cell::RefCell;
|
||||
use std::hash::{Hash, SipHasher, Hasher};
|
||||
use rustc::dep_graph::DepNode;
|
||||
use rustc::hir;
|
||||
@ -46,7 +47,42 @@ mod def_path_hash;
|
||||
mod svh_visitor;
|
||||
mod caching_codemap_view;
|
||||
|
||||
pub type IncrementalHashesMap = FnvHashMap<DepNode<DefId>, u64>;
|
||||
pub struct IncrementalHashesMap {
|
||||
hashes: FnvHashMap<DepNode<DefId>, u64>,
|
||||
|
||||
// These are the metadata hashes for the current crate as they were stored
|
||||
// during the last compilation session. They are only loaded if
|
||||
// -Z query-dep-graph was specified and are needed for auto-tests using
|
||||
// the #[rustc_metadata_dirty] and #[rustc_metadata_clean] attributes to
|
||||
// check whether some metadata hash has changed in between two revisions.
|
||||
pub prev_metadata_hashes: RefCell<FnvHashMap<DefId, u64>>,
|
||||
}
|
||||
|
||||
impl IncrementalHashesMap {
|
||||
pub fn new() -> IncrementalHashesMap {
|
||||
IncrementalHashesMap {
|
||||
hashes: FnvHashMap(),
|
||||
prev_metadata_hashes: RefCell::new(FnvHashMap()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, k: DepNode<DefId>, v: u64) -> Option<u64> {
|
||||
self.hashes.insert(k, v)
|
||||
}
|
||||
|
||||
pub fn iter<'a>(&'a self) -> ::std::collections::hash_map::Iter<'a, DepNode<DefId>, u64> {
|
||||
self.hashes.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ::std::ops::Index<&'a DepNode<DefId>> for IncrementalHashesMap {
|
||||
type Output = u64;
|
||||
|
||||
fn index(&self, index: &'a DepNode<DefId>) -> &u64 {
|
||||
&self.hashes[index]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn compute_incremental_hashes_map<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>)
|
||||
-> IncrementalHashesMap {
|
||||
@ -55,7 +91,7 @@ pub fn compute_incremental_hashes_map<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>)
|
||||
let hash_spans = tcx.sess.opts.debuginfo != NoDebugInfo;
|
||||
let mut visitor = HashItemsVisitor {
|
||||
tcx: tcx,
|
||||
hashes: FnvHashMap(),
|
||||
hashes: IncrementalHashesMap::new(),
|
||||
def_path_hashes: DefPathHashes::new(tcx),
|
||||
codemap: CachingCodemapView::new(tcx),
|
||||
hash_spans: hash_spans,
|
||||
|
@ -30,9 +30,15 @@ use std::hash::{Hash, SipHasher};
|
||||
use super::def_path_hash::DefPathHashes;
|
||||
use super::caching_codemap_view::CachingCodemapView;
|
||||
|
||||
const IGNORED_ATTRIBUTES: &'static [&'static str] = &["cfg",
|
||||
"rustc_clean",
|
||||
"rustc_dirty"];
|
||||
const IGNORED_ATTRIBUTES: &'static [&'static str] = &[
|
||||
"cfg",
|
||||
::ATTR_IF_THIS_CHANGED,
|
||||
::ATTR_THEN_THIS_WOULD_NEED,
|
||||
::ATTR_DIRTY,
|
||||
::ATTR_CLEAN,
|
||||
::ATTR_DIRTY_METADATA,
|
||||
::ATTR_CLEAN_METADATA
|
||||
];
|
||||
|
||||
pub struct StrictVersionHashVisitor<'a, 'hash: 'a, 'tcx: 'hash> {
|
||||
pub tcx: TyCtxt<'hash, 'tcx, 'tcx>,
|
||||
|
@ -35,6 +35,13 @@ extern crate serialize as rustc_serialize;
|
||||
#[macro_use] extern crate syntax;
|
||||
extern crate syntax_pos;
|
||||
|
||||
const ATTR_DIRTY: &'static str = "rustc_dirty";
|
||||
const ATTR_CLEAN: &'static str = "rustc_clean";
|
||||
const ATTR_DIRTY_METADATA: &'static str = "rustc_metadata_dirty";
|
||||
const ATTR_CLEAN_METADATA: &'static str = "rustc_metadata_clean";
|
||||
const ATTR_IF_THIS_CHANGED: &'static str = "rustc_if_this_changed";
|
||||
const ATTR_THEN_THIS_WOULD_NEED: &'static str = "rustc_then_this_would_need";
|
||||
|
||||
mod assert_dep_graph;
|
||||
mod calculate_svh;
|
||||
mod persist;
|
||||
|
@ -13,6 +13,7 @@
|
||||
use rustc::dep_graph::{DepNode, WorkProduct, WorkProductId};
|
||||
use rustc::hir::def_id::DefIndex;
|
||||
use std::sync::Arc;
|
||||
use rustc_data_structures::fnv::FnvHashMap;
|
||||
|
||||
use super::directory::DefPathIndex;
|
||||
|
||||
@ -93,6 +94,18 @@ pub struct SerializedMetadataHashes {
|
||||
/// a `DefPathIndex` that gets retracted to the current `DefId`
|
||||
/// (matching the one found in this structure).
|
||||
pub hashes: Vec<SerializedMetadataHash>,
|
||||
|
||||
/// For each DefIndex (as it occurs in SerializedMetadataHash), this
|
||||
/// map stores the DefPathIndex (as it occurs in DefIdDirectory), so
|
||||
/// that we can find the new DefId for a SerializedMetadataHash in a
|
||||
/// subsequent compilation session.
|
||||
///
|
||||
/// This map is only needed for running auto-tests using the
|
||||
/// #[rustc_metadata_dirty] and #[rustc_metadata_clean] attributes, and
|
||||
/// is only populated if -Z query-dep-graph is specified. It will be
|
||||
/// empty otherwise. Importing crates are perfectly happy with just having
|
||||
/// the DefIndex.
|
||||
pub index_map: FnvHashMap<DefIndex, DefPathIndex>
|
||||
}
|
||||
|
||||
/// The hash for some metadata that (when saving) will be exported
|
||||
|
@ -178,7 +178,6 @@ impl<'a,'tcx> DefIdDirectoryBuilder<'a,'tcx> {
|
||||
&self.directory.paths[id.index as usize]
|
||||
}
|
||||
|
||||
|
||||
pub fn map(&mut self, node: &DepNode<DefId>) -> DepNode<DefPathIndex> {
|
||||
node.map_def(|&def_id| Some(self.add(def_id))).unwrap()
|
||||
}
|
||||
|
@ -9,10 +9,10 @@
|
||||
// except according to those terms.
|
||||
|
||||
//! Debugging code to test the state of the dependency graph just
|
||||
//! after it is loaded from disk. For each node marked with
|
||||
//! `#[rustc_clean]` or `#[rustc_dirty]`, we will check that a
|
||||
//! suitable node for that item either appears or does not appear in
|
||||
//! the dep-graph, as appropriate:
|
||||
//! after it is loaded from disk and just after it has been saved.
|
||||
//! For each node marked with `#[rustc_clean]` or `#[rustc_dirty]`,
|
||||
//! we will check that a suitable node for that item either appears
|
||||
//! or does not appear in the dep-graph, as appropriate:
|
||||
//!
|
||||
//! - `#[rustc_dirty(label="TypeckItemBody", cfg="rev2")]` if we are
|
||||
//! in `#[cfg(rev2)]`, then there MUST NOT be a node
|
||||
@ -23,6 +23,22 @@
|
||||
//!
|
||||
//! Errors are reported if we are in the suitable configuration but
|
||||
//! the required condition is not met.
|
||||
//!
|
||||
//! The `#[rustc_metadata_dirty]` and `#[rustc_metadata_clean]` attributes
|
||||
//! can be used to check the incremental compilation hash (ICH) values of
|
||||
//! metadata exported in rlibs.
|
||||
//!
|
||||
//! - If a node is marked with `#[rustc_metadata_clean(cfg="rev2")]` we
|
||||
//! check that the metadata hash for that node is the same for "rev2"
|
||||
//! it was for "rev1".
|
||||
//! - If a node is marked with `#[rustc_metadata_dirty(cfg="rev2")]` we
|
||||
//! check that the metadata hash for that node is *different* for "rev2"
|
||||
//! than it was for "rev1".
|
||||
//!
|
||||
//! Note that the metadata-testing attributes must never specify the
|
||||
//! first revision. This would lead to a crash since there is no
|
||||
//! previous revision to compare things to.
|
||||
//!
|
||||
|
||||
use super::directory::RetracedDefIdDirectory;
|
||||
use super::load::DirtyNodes;
|
||||
@ -30,13 +46,14 @@ use rustc::dep_graph::{DepGraphQuery, DepNode};
|
||||
use rustc::hir;
|
||||
use rustc::hir::def_id::DefId;
|
||||
use rustc::hir::intravisit::Visitor;
|
||||
use rustc_data_structures::fnv::FnvHashSet;
|
||||
use syntax::ast::{self, Attribute, NestedMetaItem};
|
||||
use rustc_data_structures::fnv::{FnvHashSet, FnvHashMap};
|
||||
use syntax::parse::token::InternedString;
|
||||
use syntax_pos::Span;
|
||||
use rustc::ty::TyCtxt;
|
||||
|
||||
const DIRTY: &'static str = "rustc_dirty";
|
||||
const CLEAN: &'static str = "rustc_clean";
|
||||
use {ATTR_DIRTY, ATTR_CLEAN, ATTR_DIRTY_METADATA, ATTR_CLEAN_METADATA};
|
||||
|
||||
const LABEL: &'static str = "label";
|
||||
const CFG: &'static str = "cfg";
|
||||
|
||||
@ -70,50 +87,11 @@ pub struct DirtyCleanVisitor<'a, 'tcx:'a> {
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> DirtyCleanVisitor<'a, 'tcx> {
|
||||
fn expect_associated_value(&self, item: &NestedMetaItem) -> InternedString {
|
||||
if let Some(value) = item.value_str() {
|
||||
value
|
||||
} else {
|
||||
let msg = if let Some(name) = item.name() {
|
||||
format!("associated value expected for `{}`", name)
|
||||
} else {
|
||||
"expected an associated value".to_string()
|
||||
};
|
||||
|
||||
self.tcx.sess.span_fatal(item.span, &msg);
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a `#[rustc_dirty]` or `#[rustc_clean]` attribute, scan
|
||||
/// for a `cfg="foo"` attribute and check whether we have a cfg
|
||||
/// flag called `foo`.
|
||||
fn check_config(&self, attr: &ast::Attribute) -> bool {
|
||||
debug!("check_config(attr={:?})", attr);
|
||||
let config = &self.tcx.map.krate().config;
|
||||
debug!("check_config: config={:?}", config);
|
||||
for item in attr.meta_item_list().unwrap_or(&[]) {
|
||||
if item.check_name(CFG) {
|
||||
let value = self.expect_associated_value(item);
|
||||
debug!("check_config: searching for cfg {:?}", value);
|
||||
for cfg in &config[..] {
|
||||
if cfg.check_name(&value[..]) {
|
||||
debug!("check_config: matched {:?}", cfg);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
self.tcx.sess.span_fatal(
|
||||
attr.span,
|
||||
&format!("no cfg attribute"));
|
||||
}
|
||||
|
||||
fn dep_node(&self, attr: &Attribute, def_id: DefId) -> DepNode<DefId> {
|
||||
for item in attr.meta_item_list().unwrap_or(&[]) {
|
||||
if item.check_name(LABEL) {
|
||||
let value = self.expect_associated_value(item);
|
||||
let value = expect_associated_value(self.tcx, item);
|
||||
match DepNode::from_label_string(&value[..], def_id) {
|
||||
Ok(def_id) => return def_id,
|
||||
Err(()) => {
|
||||
@ -194,12 +172,12 @@ impl<'a, 'tcx> Visitor<'tcx> for DirtyCleanVisitor<'a, 'tcx> {
|
||||
fn visit_item(&mut self, item: &'tcx hir::Item) {
|
||||
let def_id = self.tcx.map.local_def_id(item.id);
|
||||
for attr in self.tcx.get_attrs(def_id).iter() {
|
||||
if attr.check_name(DIRTY) {
|
||||
if self.check_config(attr) {
|
||||
if attr.check_name(ATTR_DIRTY) {
|
||||
if check_config(self.tcx, attr) {
|
||||
self.assert_dirty(item, self.dep_node(attr, def_id));
|
||||
}
|
||||
} else if attr.check_name(CLEAN) {
|
||||
if self.check_config(attr) {
|
||||
} else if attr.check_name(ATTR_CLEAN) {
|
||||
if check_config(self.tcx, attr) {
|
||||
self.assert_clean(item, self.dep_node(attr, def_id));
|
||||
}
|
||||
}
|
||||
@ -207,3 +185,115 @@ impl<'a, 'tcx> Visitor<'tcx> for DirtyCleanVisitor<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_dirty_clean_metadata<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
prev_metadata_hashes: &FnvHashMap<DefId, u64>,
|
||||
current_metadata_hashes: &FnvHashMap<DefId, u64>) {
|
||||
if !tcx.sess.opts.debugging_opts.query_dep_graph {
|
||||
return;
|
||||
}
|
||||
|
||||
tcx.dep_graph.with_ignore(||{
|
||||
let krate = tcx.map.krate();
|
||||
krate.visit_all_items(&mut DirtyCleanMetadataVisitor {
|
||||
tcx: tcx,
|
||||
prev_metadata_hashes: prev_metadata_hashes,
|
||||
current_metadata_hashes: current_metadata_hashes,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
pub struct DirtyCleanMetadataVisitor<'a, 'tcx:'a, 'm> {
|
||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
prev_metadata_hashes: &'m FnvHashMap<DefId, u64>,
|
||||
current_metadata_hashes: &'m FnvHashMap<DefId, u64>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx, 'm> Visitor<'tcx> for DirtyCleanMetadataVisitor<'a, 'tcx, 'm> {
|
||||
fn visit_item(&mut self, item: &'tcx hir::Item) {
|
||||
let def_id = self.tcx.map.local_def_id(item.id);
|
||||
|
||||
for attr in self.tcx.get_attrs(def_id).iter() {
|
||||
if attr.check_name(ATTR_DIRTY_METADATA) {
|
||||
if check_config(self.tcx, attr) {
|
||||
self.assert_state(false, def_id, item.span);
|
||||
}
|
||||
} else if attr.check_name(ATTR_CLEAN_METADATA) {
|
||||
if check_config(self.tcx, attr) {
|
||||
self.assert_state(true, def_id, item.span);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx, 'm> DirtyCleanMetadataVisitor<'a, 'tcx, 'm> {
|
||||
|
||||
fn assert_state(&self, should_be_clean: bool, def_id: DefId, span: Span) {
|
||||
let item_path = self.tcx.item_path_str(def_id);
|
||||
debug!("assert_state({})", item_path);
|
||||
|
||||
if let Some(&prev_hash) = self.prev_metadata_hashes.get(&def_id) {
|
||||
let hashes_are_equal = prev_hash == self.current_metadata_hashes[&def_id];
|
||||
|
||||
if should_be_clean && !hashes_are_equal {
|
||||
self.tcx.sess.span_err(
|
||||
span,
|
||||
&format!("Metadata hash of `{}` is dirty, but should be clean",
|
||||
item_path));
|
||||
}
|
||||
|
||||
let should_be_dirty = !should_be_clean;
|
||||
if should_be_dirty && hashes_are_equal {
|
||||
self.tcx.sess.span_err(
|
||||
span,
|
||||
&format!("Metadata hash of `{}` is clean, but should be dirty",
|
||||
item_path));
|
||||
}
|
||||
} else {
|
||||
self.tcx.sess.span_err(
|
||||
span,
|
||||
&format!("Could not find previous metadata hash of `{}`",
|
||||
item_path));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a `#[rustc_dirty]` or `#[rustc_clean]` attribute, scan
|
||||
/// for a `cfg="foo"` attribute and check whether we have a cfg
|
||||
/// flag called `foo`.
|
||||
fn check_config(tcx: TyCtxt, attr: &ast::Attribute) -> bool {
|
||||
debug!("check_config(attr={:?})", attr);
|
||||
let config = &tcx.map.krate().config;
|
||||
debug!("check_config: config={:?}", config);
|
||||
for item in attr.meta_item_list().unwrap_or(&[]) {
|
||||
if item.check_name(CFG) {
|
||||
let value = expect_associated_value(tcx, item);
|
||||
debug!("check_config: searching for cfg {:?}", value);
|
||||
for cfg in &config[..] {
|
||||
if cfg.check_name(&value[..]) {
|
||||
debug!("check_config: matched {:?}", cfg);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
tcx.sess.span_fatal(
|
||||
attr.span,
|
||||
&format!("no cfg attribute"));
|
||||
}
|
||||
|
||||
fn expect_associated_value(tcx: TyCtxt, item: &NestedMetaItem) -> InternedString {
|
||||
if let Some(value) = item.value_str() {
|
||||
value
|
||||
} else {
|
||||
let msg = if let Some(name) = item.name() {
|
||||
format!("associated value expected for `{}`", name)
|
||||
} else {
|
||||
"expected an associated value".to_string()
|
||||
};
|
||||
|
||||
tcx.sess.span_fatal(item.span, &msg);
|
||||
}
|
||||
}
|
||||
|
@ -12,9 +12,10 @@
|
||||
|
||||
use rustc::dep_graph::DepNode;
|
||||
use rustc::hir::def_id::DefId;
|
||||
use rustc::hir::svh::Svh;
|
||||
use rustc::session::Session;
|
||||
use rustc::ty::TyCtxt;
|
||||
use rustc_data_structures::fnv::FnvHashSet;
|
||||
use rustc_data_structures::fnv::{FnvHashSet, FnvHashMap};
|
||||
use rustc_serialize::Decodable as RustcDecodable;
|
||||
use rustc_serialize::opaque::Decoder;
|
||||
use std::io::Read;
|
||||
@ -224,6 +225,9 @@ pub fn decode_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
|
||||
dirty_clean::check_dirty_clean_annotations(tcx, &dirty_raw_source_nodes, &retraced);
|
||||
|
||||
load_prev_metadata_hashes(tcx,
|
||||
&retraced,
|
||||
&mut *incremental_hashes_map.prev_metadata_hashes.borrow_mut());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -241,6 +245,9 @@ fn dirty_nodes<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
if let Some(dep_node) = retraced.map(&hash.dep_node) {
|
||||
let current_hash = hcx.hash(&dep_node).unwrap();
|
||||
if current_hash == hash.hash {
|
||||
debug!("initial_dirty_nodes: {:?} is clean (hash={:?})",
|
||||
dep_node.map_def(|&def_id| Some(tcx.def_path(def_id))).unwrap(),
|
||||
current_hash);
|
||||
continue;
|
||||
}
|
||||
debug!("initial_dirty_nodes: {:?} is dirty as hash is {:?}, was {:?}",
|
||||
@ -304,3 +311,50 @@ fn delete_dirty_work_product(tcx: TyCtxt,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn load_prev_metadata_hashes(tcx: TyCtxt,
|
||||
retraced: &RetracedDefIdDirectory,
|
||||
output: &mut FnvHashMap<DefId, u64>) {
|
||||
if !tcx.sess.opts.debugging_opts.query_dep_graph {
|
||||
return
|
||||
}
|
||||
|
||||
debug!("load_prev_metadata_hashes() - Loading previous metadata hashes");
|
||||
|
||||
let file_path = metadata_hash_export_path(tcx.sess);
|
||||
|
||||
if !file_path.exists() {
|
||||
debug!("load_prev_metadata_hashes() - Couldn't find file containing \
|
||||
hashes at `{}`", file_path.display());
|
||||
return
|
||||
}
|
||||
|
||||
debug!("load_prev_metadata_hashes() - File: {}", file_path.display());
|
||||
|
||||
let mut data = vec![];
|
||||
if !File::open(&file_path)
|
||||
.and_then(|mut file| file.read_to_end(&mut data)).is_ok() {
|
||||
debug!("load_prev_metadata_hashes() - Couldn't read file containing \
|
||||
hashes at `{}`", file_path.display());
|
||||
return
|
||||
}
|
||||
|
||||
debug!("load_prev_metadata_hashes() - Decoding hashes");
|
||||
let mut decoder = Decoder::new(&mut data, 0);
|
||||
let _ = Svh::decode(&mut decoder).unwrap();
|
||||
let serialized_hashes = SerializedMetadataHashes::decode(&mut decoder).unwrap();
|
||||
|
||||
debug!("load_prev_metadata_hashes() - Mapping DefIds");
|
||||
|
||||
assert_eq!(serialized_hashes.index_map.len(), serialized_hashes.hashes.len());
|
||||
for serialized_hash in serialized_hashes.hashes {
|
||||
let def_path_index = serialized_hashes.index_map[&serialized_hash.def_index];
|
||||
if let Some(def_id) = retraced.def_id(def_path_index) {
|
||||
let old = output.insert(def_id, serialized_hash.hash);
|
||||
assert!(old.is_none(), "already have hash for {:?}", def_id);
|
||||
}
|
||||
}
|
||||
|
||||
debug!("load_prev_metadata_hashes() - successfully loaded {} hashes",
|
||||
serialized_hashes.index_map.len());
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ use super::directory::*;
|
||||
use super::hash::*;
|
||||
use super::preds::*;
|
||||
use super::fs::*;
|
||||
use super::dirty_clean;
|
||||
|
||||
pub fn save_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
incremental_hashes_map: &IncrementalHashesMap,
|
||||
@ -37,16 +38,32 @@ pub fn save_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
if sess.opts.incremental.is_none() {
|
||||
return;
|
||||
}
|
||||
let mut hcx = HashContext::new(tcx, incremental_hashes_map);
|
||||
|
||||
let mut builder = DefIdDirectoryBuilder::new(tcx);
|
||||
let query = tcx.dep_graph.query();
|
||||
let mut hcx = HashContext::new(tcx, incremental_hashes_map);
|
||||
let preds = Predecessors::new(&query, &mut hcx);
|
||||
let mut current_metadata_hashes = FnvHashMap();
|
||||
|
||||
// IMPORTANT: We are saving the metadata hashes *before* the dep-graph,
|
||||
// since metadata-encoding might add new entries to the
|
||||
// DefIdDirectory (which is saved in the dep-graph file).
|
||||
save_in(sess,
|
||||
metadata_hash_export_path(sess),
|
||||
|e| encode_metadata_hashes(tcx,
|
||||
svh,
|
||||
&preds,
|
||||
&mut builder,
|
||||
&mut current_metadata_hashes,
|
||||
e));
|
||||
save_in(sess,
|
||||
dep_graph_path(sess),
|
||||
|e| encode_dep_graph(&preds, &mut builder, e));
|
||||
save_in(sess,
|
||||
metadata_hash_export_path(sess),
|
||||
|e| encode_metadata_hashes(tcx, svh, &preds, &mut builder, e));
|
||||
|
||||
let prev_metadata_hashes = incremental_hashes_map.prev_metadata_hashes.borrow();
|
||||
dirty_clean::check_dirty_clean_metadata(tcx,
|
||||
&*prev_metadata_hashes,
|
||||
¤t_metadata_hashes);
|
||||
}
|
||||
|
||||
pub fn save_work_products(sess: &Session) {
|
||||
@ -63,13 +80,17 @@ pub fn save_work_products(sess: &Session) {
|
||||
fn save_in<F>(sess: &Session, path_buf: PathBuf, encode: F)
|
||||
where F: FnOnce(&mut Encoder) -> io::Result<()>
|
||||
{
|
||||
debug!("save: storing data in {}", path_buf.display());
|
||||
|
||||
// delete the old dep-graph, if any
|
||||
// Note: It's important that we actually delete the old file and not just
|
||||
// truncate and overwrite it, since it might be a shared hard-link, the
|
||||
// underlying data of which we don't want to modify
|
||||
if path_buf.exists() {
|
||||
match fs::remove_file(&path_buf) {
|
||||
Ok(()) => {}
|
||||
Ok(()) => {
|
||||
debug!("save: remove old file");
|
||||
}
|
||||
Err(err) => {
|
||||
sess.err(&format!("unable to delete old dep-graph at `{}`: {}",
|
||||
path_buf.display(),
|
||||
@ -94,7 +115,9 @@ fn save_in<F>(sess: &Session, path_buf: PathBuf, encode: F)
|
||||
// write the data out
|
||||
let data = wr.into_inner();
|
||||
match File::create(&path_buf).and_then(|mut file| file.write_all(&data)) {
|
||||
Ok(_) => {}
|
||||
Ok(_) => {
|
||||
debug!("save: data written to disk successfully");
|
||||
}
|
||||
Err(err) => {
|
||||
sess.err(&format!("failed to write dep-graph to `{}`: {}",
|
||||
path_buf.display(),
|
||||
@ -159,18 +182,9 @@ pub fn encode_metadata_hashes(tcx: TyCtxt,
|
||||
svh: Svh,
|
||||
preds: &Predecessors,
|
||||
builder: &mut DefIdDirectoryBuilder,
|
||||
current_metadata_hashes: &mut FnvHashMap<DefId, u64>,
|
||||
encoder: &mut Encoder)
|
||||
-> io::Result<()> {
|
||||
let mut def_id_hashes = FnvHashMap();
|
||||
let mut def_id_hash = |def_id: DefId| -> u64 {
|
||||
*def_id_hashes.entry(def_id)
|
||||
.or_insert_with(|| {
|
||||
let index = builder.add(def_id);
|
||||
let path = builder.lookup_def_path(index);
|
||||
path.deterministic_hash(tcx)
|
||||
})
|
||||
};
|
||||
|
||||
// For each `MetaData(X)` node where `X` is local, accumulate a
|
||||
// hash. These are the metadata items we export. Downstream
|
||||
// crates will want to see a hash that tells them whether we might
|
||||
@ -178,7 +192,13 @@ pub fn encode_metadata_hashes(tcx: TyCtxt,
|
||||
// compiled.
|
||||
//
|
||||
// (I initially wrote this with an iterator, but it seemed harder to read.)
|
||||
let mut serialized_hashes = SerializedMetadataHashes { hashes: vec![] };
|
||||
let mut serialized_hashes = SerializedMetadataHashes {
|
||||
hashes: vec![],
|
||||
index_map: FnvHashMap()
|
||||
};
|
||||
|
||||
let mut def_id_hashes = FnvHashMap();
|
||||
|
||||
for (&target, sources) in &preds.inputs {
|
||||
let def_id = match *target {
|
||||
DepNode::MetaData(def_id) => {
|
||||
@ -188,6 +208,15 @@ pub fn encode_metadata_hashes(tcx: TyCtxt,
|
||||
_ => continue,
|
||||
};
|
||||
|
||||
let mut def_id_hash = |def_id: DefId| -> u64 {
|
||||
*def_id_hashes.entry(def_id)
|
||||
.or_insert_with(|| {
|
||||
let index = builder.add(def_id);
|
||||
let path = builder.lookup_def_path(index);
|
||||
path.deterministic_hash(tcx)
|
||||
})
|
||||
};
|
||||
|
||||
// To create the hash for each item `X`, we don't hash the raw
|
||||
// bytes of the metadata (though in principle we
|
||||
// could). Instead, we walk the predecessors of `MetaData(X)`
|
||||
@ -221,6 +250,22 @@ pub fn encode_metadata_hashes(tcx: TyCtxt,
|
||||
});
|
||||
}
|
||||
|
||||
if tcx.sess.opts.debugging_opts.query_dep_graph {
|
||||
for serialized_hash in &serialized_hashes.hashes {
|
||||
let def_id = DefId::local(serialized_hash.def_index);
|
||||
|
||||
// Store entry in the index_map
|
||||
let def_path_index = builder.add(def_id);
|
||||
serialized_hashes.index_map.insert(def_id.index, def_path_index);
|
||||
|
||||
// Record hash in current_metadata_hashes
|
||||
current_metadata_hashes.insert(def_id, serialized_hash.hash);
|
||||
}
|
||||
|
||||
debug!("save: stored index_map (len={}) for serialized hashes",
|
||||
serialized_hashes.index_map.len());
|
||||
}
|
||||
|
||||
// Encode everything.
|
||||
svh.encode(encoder)?;
|
||||
serialized_hashes.encode(encoder)?;
|
||||
|
@ -508,6 +508,16 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeGat
|
||||
is just used for rustc unit tests \
|
||||
and will never be stable",
|
||||
cfg_fn!(rustc_attrs))),
|
||||
("rustc_metadata_dirty", Whitelisted, Gated("rustc_attrs",
|
||||
"the `#[rustc_metadata_dirty]` attribute \
|
||||
is just used for rustc unit tests \
|
||||
and will never be stable",
|
||||
cfg_fn!(rustc_attrs))),
|
||||
("rustc_metadata_clean", Whitelisted, Gated("rustc_attrs",
|
||||
"the `#[rustc_metadata_clean]` attribute \
|
||||
is just used for rustc unit tests \
|
||||
and will never be stable",
|
||||
cfg_fn!(rustc_attrs))),
|
||||
("rustc_partition_reused", Whitelisted, Gated("rustc_attrs",
|
||||
"this attribute \
|
||||
is just used for rustc unit tests \
|
||||
|
238
src/test/incremental/hashes/struct_defs.rs
Normal file
238
src/test/incremental/hashes/struct_defs.rs
Normal file
@ -0,0 +1,238 @@
|
||||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
|
||||
// This test case tests the incremental compilation hash (ICH) implementation
|
||||
// for struct definitions.
|
||||
|
||||
// The general pattern followed here is: Change one thing between rev1 and rev2
|
||||
// and make sure that the hash has changed, then change nothing between rev2 and
|
||||
// rev3 and make sure that the hash has not changed.
|
||||
|
||||
// We also test the ICH for struct definitions exported in metadata. Same as
|
||||
// above, we want to make sure that the change between rev1 and rev2 also
|
||||
// results in a change of the ICH for the struct's metadata, and that it stays
|
||||
// the same between rev2 and rev3.
|
||||
|
||||
// must-compile-successfully
|
||||
// revisions: cfail1 cfail2 cfail3
|
||||
// compile-flags: -Z query-dep-graph
|
||||
|
||||
|
||||
#![allow(warnings)]
|
||||
#![feature(rustc_attrs)]
|
||||
#![crate_type="rlib"]
|
||||
|
||||
// Layout ----------------------------------------------------------------------
|
||||
#[cfg(cfail1)]
|
||||
pub struct LayoutPacked;
|
||||
|
||||
#[cfg(not(cfail1))]
|
||||
#[rustc_dirty(label="Hir", cfg="cfail2")]
|
||||
#[rustc_clean(label="Hir", cfg="cfail3")]
|
||||
#[rustc_metadata_dirty(cfg="cfail2")]
|
||||
#[rustc_metadata_clean(cfg="cfail3")]
|
||||
#[repr(packed)]
|
||||
pub struct LayoutPacked;
|
||||
|
||||
#[cfg(cfail1)]
|
||||
struct LayoutC;
|
||||
|
||||
#[cfg(not(cfail1))]
|
||||
#[rustc_dirty(label="Hir", cfg="cfail2")]
|
||||
#[rustc_clean(label="Hir", cfg="cfail3")]
|
||||
#[rustc_metadata_dirty(cfg="cfail2")]
|
||||
#[rustc_metadata_clean(cfg="cfail3")]
|
||||
#[repr(C)]
|
||||
struct LayoutC;
|
||||
|
||||
|
||||
// Tuple Struct Change Field Type ----------------------------------------------
|
||||
|
||||
#[cfg(cfail1)]
|
||||
struct TupleStructFieldType(i32);
|
||||
|
||||
#[cfg(not(cfail1))]
|
||||
#[rustc_dirty(label="Hir", cfg="cfail2")]
|
||||
#[rustc_clean(label="Hir", cfg="cfail3")]
|
||||
#[rustc_metadata_dirty(cfg="cfail2")]
|
||||
#[rustc_metadata_clean(cfg="cfail3")]
|
||||
struct TupleStructFieldType(u32);
|
||||
|
||||
|
||||
// Tuple Struct Add Field ------------------------------------------------------
|
||||
|
||||
#[cfg(cfail1)]
|
||||
struct TupleStructAddField(i32);
|
||||
|
||||
#[cfg(not(cfail1))]
|
||||
#[rustc_dirty(label="Hir", cfg="cfail2")]
|
||||
#[rustc_clean(label="Hir", cfg="cfail3")]
|
||||
#[rustc_metadata_dirty(cfg="cfail2")]
|
||||
#[rustc_metadata_clean(cfg="cfail3")]
|
||||
struct TupleStructAddField(i32, u32);
|
||||
|
||||
|
||||
// Tuple Struct Field Visibility -----------------------------------------------
|
||||
|
||||
#[cfg(cfail1)]
|
||||
struct TupleStructFieldVisibility(char);
|
||||
|
||||
#[cfg(not(cfail1))]
|
||||
#[rustc_dirty(label="Hir", cfg="cfail2")]
|
||||
#[rustc_clean(label="Hir", cfg="cfail3")]
|
||||
#[rustc_metadata_dirty(cfg="cfail2")]
|
||||
#[rustc_metadata_clean(cfg="cfail3")]
|
||||
struct TupleStructFieldVisibility(pub char);
|
||||
|
||||
|
||||
// Record Struct Field Type ----------------------------------------------------
|
||||
|
||||
#[cfg(cfail1)]
|
||||
struct RecordStructFieldType { x: f32 }
|
||||
|
||||
#[cfg(not(cfail1))]
|
||||
#[rustc_dirty(label="Hir", cfg="cfail2")]
|
||||
#[rustc_clean(label="Hir", cfg="cfail3")]
|
||||
#[rustc_metadata_dirty(cfg="cfail2")]
|
||||
#[rustc_metadata_clean(cfg="cfail3")]
|
||||
struct RecordStructFieldType { x: u64 }
|
||||
|
||||
|
||||
// Record Struct Field Name ----------------------------------------------------
|
||||
|
||||
#[cfg(cfail1)]
|
||||
struct RecordStructFieldName { x: f32 }
|
||||
|
||||
#[cfg(not(cfail1))]
|
||||
#[rustc_dirty(label="Hir", cfg="cfail2")]
|
||||
#[rustc_clean(label="Hir", cfg="cfail3")]
|
||||
#[rustc_metadata_dirty(cfg="cfail2")]
|
||||
#[rustc_metadata_clean(cfg="cfail3")]
|
||||
struct RecordStructFieldName { y: f32 }
|
||||
|
||||
|
||||
// Record Struct Add Field -----------------------------------------------------
|
||||
|
||||
#[cfg(cfail1)]
|
||||
struct RecordStructAddField { x: f32 }
|
||||
|
||||
#[cfg(not(cfail1))]
|
||||
#[rustc_dirty(label="Hir", cfg="cfail2")]
|
||||
#[rustc_clean(label="Hir", cfg="cfail3")]
|
||||
#[rustc_metadata_dirty(cfg="cfail2")]
|
||||
#[rustc_metadata_clean(cfg="cfail3")]
|
||||
struct RecordStructAddField { x: f32, y: () }
|
||||
|
||||
|
||||
// Record Struct Field Visibility ----------------------------------------------
|
||||
|
||||
#[cfg(cfail1)]
|
||||
struct RecordStructFieldVisibility { x: f32 }
|
||||
|
||||
#[cfg(not(cfail1))]
|
||||
#[rustc_dirty(label="Hir", cfg="cfail2")]
|
||||
#[rustc_clean(label="Hir", cfg="cfail3")]
|
||||
#[rustc_metadata_dirty(cfg="cfail2")]
|
||||
#[rustc_metadata_clean(cfg="cfail3")]
|
||||
struct RecordStructFieldVisibility { pub x: f32 }
|
||||
|
||||
|
||||
// Add Lifetime Parameter ------------------------------------------------------
|
||||
|
||||
#[cfg(cfail1)]
|
||||
struct AddLifetimeParameter<'a>(&'a f32, &'a f64);
|
||||
|
||||
#[cfg(not(cfail1))]
|
||||
#[rustc_dirty(label="Hir", cfg="cfail2")]
|
||||
#[rustc_clean(label="Hir", cfg="cfail3")]
|
||||
#[rustc_metadata_dirty(cfg="cfail2")]
|
||||
#[rustc_metadata_clean(cfg="cfail3")]
|
||||
struct AddLifetimeParameter<'a, 'b>(&'a f32, &'b f64);
|
||||
|
||||
|
||||
// Add Lifetime Parameter Bound ------------------------------------------------
|
||||
|
||||
#[cfg(cfail1)]
|
||||
struct AddLifetimeParameterBound<'a, 'b>(&'a f32, &'b f64);
|
||||
|
||||
#[cfg(not(cfail1))]
|
||||
#[rustc_dirty(label="Hir", cfg="cfail2")]
|
||||
#[rustc_clean(label="Hir", cfg="cfail3")]
|
||||
#[rustc_metadata_dirty(cfg="cfail2")]
|
||||
#[rustc_metadata_clean(cfg="cfail3")]
|
||||
struct AddLifetimeParameterBound<'a, 'b: 'a>(&'a f32, &'b f64);
|
||||
|
||||
#[cfg(cfail1)]
|
||||
struct AddLifetimeParameterBoundWhereClause<'a, 'b>(&'a f32, &'b f64);
|
||||
|
||||
#[cfg(not(cfail1))]
|
||||
#[rustc_dirty(label="Hir", cfg="cfail2")]
|
||||
#[rustc_clean(label="Hir", cfg="cfail3")]
|
||||
#[rustc_metadata_dirty(cfg="cfail2")]
|
||||
#[rustc_metadata_clean(cfg="cfail3")]
|
||||
struct AddLifetimeParameterBoundWhereClause<'a, 'b>(&'a f32, &'b f64)
|
||||
where 'b: 'a;
|
||||
|
||||
|
||||
// Add Type Parameter ----------------------------------------------------------
|
||||
|
||||
#[cfg(cfail1)]
|
||||
struct AddTypeParameter<T1>(T1, T1);
|
||||
|
||||
#[cfg(not(cfail1))]
|
||||
#[rustc_dirty(label="Hir", cfg="cfail2")]
|
||||
#[rustc_clean(label="Hir", cfg="cfail3")]
|
||||
#[rustc_metadata_dirty(cfg="cfail2")]
|
||||
#[rustc_metadata_clean(cfg="cfail3")]
|
||||
struct AddTypeParameter<T1, T2>(T1, T2);
|
||||
|
||||
|
||||
// Add Type Parameter Bound ----------------------------------------------------
|
||||
|
||||
#[cfg(cfail1)]
|
||||
struct AddTypeParameterBound<T>(T);
|
||||
|
||||
#[cfg(not(cfail1))]
|
||||
#[rustc_dirty(label="Hir", cfg="cfail2")]
|
||||
#[rustc_clean(label="Hir", cfg="cfail3")]
|
||||
#[rustc_metadata_dirty(cfg="cfail2")]
|
||||
#[rustc_metadata_clean(cfg="cfail3")]
|
||||
struct AddTypeParameterBound<T: Send>(T);
|
||||
|
||||
|
||||
#[cfg(cfail1)]
|
||||
struct AddTypeParameterBoundWhereClause<T>(T);
|
||||
|
||||
#[cfg(not(cfail1))]
|
||||
#[rustc_dirty(label="Hir", cfg="cfail2")]
|
||||
#[rustc_clean(label="Hir", cfg="cfail3")]
|
||||
#[rustc_metadata_dirty(cfg="cfail2")]
|
||||
#[rustc_metadata_clean(cfg="cfail3")]
|
||||
struct AddTypeParameterBoundWhereClause<T>(T) where T: Sync;
|
||||
|
||||
|
||||
// Empty struct ----------------------------------------------------------------
|
||||
|
||||
#[rustc_clean(label="Hir", cfg="cfail2")]
|
||||
#[rustc_metadata_clean(cfg="cfail2")]
|
||||
pub struct EmptyStruct;
|
||||
|
||||
|
||||
// Visibility ------------------------------------------------------------------
|
||||
|
||||
#[cfg(cfail1)]
|
||||
struct Visibility;
|
||||
|
||||
#[cfg(not(cfail1))]
|
||||
#[rustc_dirty(label="Hir", cfg="cfail2")]
|
||||
#[rustc_clean(label="Hir", cfg="cfail3")]
|
||||
#[rustc_metadata_clean(cfg="cfail3")]
|
||||
pub struct Visibility;
|
@ -182,42 +182,32 @@ pub struct TestProps {
|
||||
// testing harness and used when generating compilation
|
||||
// arguments. (In particular, it propagates to the aux-builds.)
|
||||
pub incremental_dir: Option<PathBuf>,
|
||||
// Specifies that a cfail test must actually compile without errors.
|
||||
pub must_compile_successfully: bool,
|
||||
}
|
||||
|
||||
impl TestProps {
|
||||
pub fn new() -> Self {
|
||||
let error_patterns = Vec::new();
|
||||
let aux_builds = Vec::new();
|
||||
let exec_env = Vec::new();
|
||||
let run_flags = None;
|
||||
let pp_exact = None;
|
||||
let check_lines = Vec::new();
|
||||
let build_aux_docs = false;
|
||||
let force_host = false;
|
||||
let check_stdout = false;
|
||||
let no_prefer_dynamic = false;
|
||||
let pretty_expanded = false;
|
||||
let pretty_compare_only = false;
|
||||
let forbid_output = Vec::new();
|
||||
TestProps {
|
||||
error_patterns: error_patterns,
|
||||
error_patterns: vec![],
|
||||
compile_flags: vec![],
|
||||
run_flags: run_flags,
|
||||
pp_exact: pp_exact,
|
||||
aux_builds: aux_builds,
|
||||
run_flags: None,
|
||||
pp_exact: None,
|
||||
aux_builds: vec![],
|
||||
revisions: vec![],
|
||||
rustc_env: vec![],
|
||||
exec_env: exec_env,
|
||||
check_lines: check_lines,
|
||||
build_aux_docs: build_aux_docs,
|
||||
force_host: force_host,
|
||||
check_stdout: check_stdout,
|
||||
no_prefer_dynamic: no_prefer_dynamic,
|
||||
pretty_expanded: pretty_expanded,
|
||||
exec_env: vec![],
|
||||
check_lines: vec![],
|
||||
build_aux_docs: false,
|
||||
force_host: false,
|
||||
check_stdout: false,
|
||||
no_prefer_dynamic: false,
|
||||
pretty_expanded: false,
|
||||
pretty_mode: format!("normal"),
|
||||
pretty_compare_only: pretty_compare_only,
|
||||
forbid_output: forbid_output,
|
||||
pretty_compare_only: false,
|
||||
forbid_output: vec![],
|
||||
incremental_dir: None,
|
||||
must_compile_successfully: false,
|
||||
}
|
||||
}
|
||||
|
||||
@ -313,6 +303,10 @@ impl TestProps {
|
||||
if let Some(of) = parse_forbid_output(ln) {
|
||||
self.forbid_output.push(of);
|
||||
}
|
||||
|
||||
if !self.must_compile_successfully {
|
||||
self.must_compile_successfully = parse_must_compile_successfully(ln);
|
||||
}
|
||||
});
|
||||
|
||||
for key in vec!["RUST_TEST_NOCAPTURE", "RUST_TEST_THREADS"] {
|
||||
@ -420,6 +414,10 @@ fn parse_pretty_compare_only(line: &str) -> bool {
|
||||
parse_name_directive(line, "pretty-compare-only")
|
||||
}
|
||||
|
||||
fn parse_must_compile_successfully(line: &str) -> bool {
|
||||
parse_name_directive(line, "must-compile-successfully")
|
||||
}
|
||||
|
||||
fn parse_env(line: &str, name: &str) -> Option<(String, String)> {
|
||||
parse_name_value_directive(line, name).map(|nv| {
|
||||
// nv is either FOO or FOO=BAR
|
||||
|
@ -129,13 +129,21 @@ impl<'test> TestCx<'test> {
|
||||
fn run_cfail_test(&self) {
|
||||
let proc_res = self.compile_test();
|
||||
|
||||
if proc_res.status.success() {
|
||||
self.fatal_proc_rec(
|
||||
&format!("{} test compiled successfully!", self.config.mode)[..],
|
||||
&proc_res);
|
||||
}
|
||||
if self.props.must_compile_successfully {
|
||||
if !proc_res.status.success() {
|
||||
self.fatal_proc_rec(
|
||||
"test compilation failed although it shouldn't!",
|
||||
&proc_res);
|
||||
}
|
||||
} else {
|
||||
if proc_res.status.success() {
|
||||
self.fatal_proc_rec(
|
||||
&format!("{} test compiled successfully!", self.config.mode)[..],
|
||||
&proc_res);
|
||||
}
|
||||
|
||||
self.check_correct_failure_status(&proc_res);
|
||||
self.check_correct_failure_status(&proc_res);
|
||||
}
|
||||
|
||||
let output_to_check = self.get_output(&proc_res);
|
||||
let expected_errors = errors::load_errors(&self.testpaths.file, self.revision);
|
||||
@ -147,6 +155,7 @@ impl<'test> TestCx<'test> {
|
||||
} else {
|
||||
self.check_error_patterns(&output_to_check, &proc_res);
|
||||
}
|
||||
|
||||
self.check_no_compiler_crash(&proc_res);
|
||||
self.check_forbid_output(&output_to_check, &proc_res);
|
||||
}
|
||||
@ -943,8 +952,12 @@ actual:\n\
|
||||
output_to_check: &str,
|
||||
proc_res: &ProcRes) {
|
||||
if self.props.error_patterns.is_empty() {
|
||||
self.fatal(&format!("no error pattern specified in {:?}",
|
||||
self.testpaths.file.display()));
|
||||
if self.props.must_compile_successfully {
|
||||
return
|
||||
} else {
|
||||
self.fatal(&format!("no error pattern specified in {:?}",
|
||||
self.testpaths.file.display()));
|
||||
}
|
||||
}
|
||||
let mut next_err_idx = 0;
|
||||
let mut next_err_pat = self.props.error_patterns[next_err_idx].trim();
|
||||
|
Loading…
Reference in New Issue
Block a user