- [Compiler flags](compiler-flags.md)
- [linker_flavor](compiler-flags/linker-flavor.md)
+ - [remap_path_prefix](compiler-flags/remap-path-prefix.md)
- [Language features](language-features.md)
- [abi_msp430_interrupt](language-features/abi-msp430-interrupt.md)
- [abi_ptx](language-features/abi-ptx.md)
--- /dev/null
+# `remap-path-prefix`
+
+The tracking issue for this feature is: [#41555](https://github.com/rust-lang/rust/issues/41555)
+
+------------------------
+
+The `-Z remap-path-prefix-from`, `-Z remap-path-prefix-to` commandline option
+pair allows to replace prefixes of any file paths the compiler emits in various
+places. This is useful for bringing debuginfo paths into a well-known form and
+for achieving reproducible builds independent of the directory the compiler was
+executed in. All paths emitted by the compiler are affected, including those in
+error messages.
+
+In order to map all paths starting with `/home/foo/my-project/src` to
+`/sources/my-project`, one would invoke the compiler as follows:
+
+```text
+rustc -Zremap-path-prefix-from="/home/foo/my-project/src" -Zremap-path-prefix-to="/sources/my-project"
+```
+
+Debuginfo for code from the file `/home/foo/my-project/src/foo/mod.rs`,
+for example, would then point debuggers to `/sources/my-project/foo/mod.rs`
+instead of the original file.
+
+The options can be specified multiple times when multiple prefixes should be
+mapped:
+
+```text
+rustc -Zremap-path-prefix-from="/home/foo/my-project/src" \
+ -Zremap-path-prefix-to="/sources/my-project" \
+ -Zremap-path-prefix-from="/home/foo/my-project/build-dir" \
+ -Zremap-path-prefix-to="/stable-build-dir"
+```
+
+When the options are given multiple times, the nth `-from` will be matched up
+with the nth `-to` and they can appear anywhere on the commandline. Mappings
+specified later on the line will take precedence over earlier ones.
elif key == "libraries":
lib_path.extend(val.lstrip(' =').split(';'))
- target_tools = ["gcc.exe", "ld.exe", "ar.exe", "dlltool.exe"]
+ target_tools = ["gcc.exe", "ld.exe", "ar.exe", "dlltool.exe",
+ "libwinpthread-1.dll"]
rustc_dlls = ["libstdc++-6.dll", "libwinpthread-1.dll"]
if target_triple.startswith("i686-"):
syntax::errors::registry::Registry::new(&[]),
Rc::new(DummyCrateStore));
let filemap = session.parse_sess.codemap()
- .new_filemap("<n/a>".to_string(), None, code);
+ .new_filemap("<n/a>".to_string(), code);
let mut lexer = lexer::StringReader::new(session.diagnostic(), filemap);
let cm = session.codemap();
// are *mostly* used as a part of that interface, but these should
// probably get a better home if someone can find one.
-use hir::def::{self, Def};
+use hir::def;
use hir::def_id::{CrateNum, DefId, DefIndex};
use hir::map as hir_map;
use hir::map::definitions::{Definitions, DefKey, DisambiguatedDefPathData};
fn crate_data_as_rc_any(&self, krate: CrateNum) -> Rc<Any>;
// item info
- fn describe_def(&self, def: DefId) -> Option<Def>;
fn def_span(&self, sess: &Session, def: DefId) -> Span;
fn stability(&self, def: DefId) -> Option<attr::Stability>;
fn deprecation(&self, def: DefId) -> Option<attr::Deprecation>;
fn crate_data_as_rc_any(&self, krate: CrateNum) -> Rc<Any>
{ bug!("crate_data_as_rc_any") }
// item info
- fn describe_def(&self, def: DefId) -> Option<Def> { bug!("describe_def") }
fn def_span(&self, sess: &Session, def: DefId) -> Span { bug!("def_span") }
fn stability(&self, def: DefId) -> Option<attr::Stability> { bug!("stability") }
fn deprecation(&self, def: DefId) -> Option<attr::Deprecation> { bug!("deprecation") }
// (See issue #38412)
fn skip_stability_check_due_to_privacy(self, mut def_id: DefId) -> bool {
// Check if `def_id` is a trait method.
- match self.sess.cstore.describe_def(def_id) {
+ match self.describe_def(def_id) {
Some(Def::Method(_)) |
Some(Def::AssociatedTy(_)) |
Some(Def::AssociatedConst(_)) => {
use middle::cstore;
use syntax::ast::{self, IntTy, UintTy};
+use syntax::codemap::FilePathMapping;
use syntax::parse::token;
use syntax::parse;
use syntax::symbol::Symbol;
self.incremental.is_none() ||
self.cg.codegen_units == 1
}
+
+ pub fn file_path_mapping(&self) -> FilePathMapping {
+ FilePathMapping::new(
+ self.debugging_opts.remap_path_prefix_from.iter().zip(
+ self.debugging_opts.remap_path_prefix_to.iter()
+ ).map(|(src, dst)| (src.clone(), dst.clone())).collect()
+ )
+ }
}
// The type of entry function, so
"Set the optimization fuel quota for a crate."),
print_fuel: Option<String> = (None, parse_opt_string, [TRACKED],
"Make Rustc print the total optimization fuel used by a crate."),
+ remap_path_prefix_from: Vec<String> = (vec![], parse_string_push, [TRACKED],
+ "add a source pattern to the file path remapping config"),
+ remap_path_prefix_to: Vec<String> = (vec![], parse_string_push, [TRACKED],
+ "add a mapping target to the file path remapping config"),
}
pub fn default_lib_output() -> CrateType {
// Convert strings provided as --cfg [cfgspec] into a crate_cfg
pub fn parse_cfgspecs(cfgspecs: Vec<String> ) -> ast::CrateConfig {
cfgspecs.into_iter().map(|s| {
- let sess = parse::ParseSess::new();
+ let sess = parse::ParseSess::new(FilePathMapping::empty());
let mut parser =
parse::new_parser_from_source_str(&sess, "cfgspec".to_string(), s.to_string());
output_types.insert(OutputType::Exe, None);
}
+ let remap_path_prefix_sources = debugging_opts.remap_path_prefix_from.len();
+ let remap_path_prefix_targets = debugging_opts.remap_path_prefix_from.len();
+
+ if remap_path_prefix_targets < remap_path_prefix_sources {
+ for source in &debugging_opts.remap_path_prefix_from[remap_path_prefix_targets..] {
+ early_error(error_format,
+ &format!("option `-Zremap-path-prefix-from='{}'` does not have \
+ a corresponding `-Zremap-path-prefix-to`", source))
+ }
+ } else if remap_path_prefix_targets > remap_path_prefix_sources {
+ for target in &debugging_opts.remap_path_prefix_to[remap_path_prefix_sources..] {
+ early_error(error_format,
+ &format!("option `-Zremap-path-prefix-to='{}'` does not have \
+ a corresponding `-Zremap-path-prefix-from`", target))
+ }
+ }
+
let mut cg = build_codegen_options(matches, error_format);
// Issue #30063: if user requests llvm-related output to one
// The name of the root source file of the crate, in the local file system.
// The path is always expected to be absolute. `None` means that there is no
// source file.
- pub local_crate_source_file: Option<PathBuf>,
- pub working_dir: PathBuf,
+ pub local_crate_source_file: Option<String>,
+ // The directory the compiler has been executed in plus a flag indicating
+ // if the value stored here has been affected by path remapping.
+ pub working_dir: (String, bool),
pub lint_store: RefCell<lint::LintStore>,
pub lints: RefCell<lint::LintTable>,
/// Set of (LintId, span, message) tuples tracking lint (sub)diagnostics
registry: errors::registry::Registry,
cstore: Rc<CrateStore>)
-> Session {
+ let file_path_mapping = sopts.file_path_mapping();
+
build_session_with_codemap(sopts,
dep_graph,
local_crate_source_file,
registry,
cstore,
- Rc::new(codemap::CodeMap::new()),
+ Rc::new(codemap::CodeMap::new(file_path_mapping)),
None)
}
Ok(t) => t,
Err(e) => {
panic!(span_diagnostic.fatal(&format!("Error loading host specification: {}", e)));
- }
+ }
};
let target_cfg = config::build_target_config(&sopts, &span_diagnostic);
let p_s = parse::ParseSess::with_span_handler(span_diagnostic, codemap);
None => Some(filesearch::get_or_default_sysroot())
};
+ let file_path_mapping = sopts.file_path_mapping();
+
// Make the path absolute, if necessary
- let local_crate_source_file = local_crate_source_file.map(|path|
- if path.is_absolute() {
- path.clone()
- } else {
- env::current_dir().unwrap().join(&path)
- }
- );
+ let local_crate_source_file = local_crate_source_file.map(|path| {
+ file_path_mapping.map_prefix(path.to_string_lossy().into_owned()).0
+ });
let optimization_fuel_crate = sopts.debugging_opts.fuel.as_ref().map(|i| i.0.clone());
let optimization_fuel_limit = Cell::new(sopts.debugging_opts.fuel.as_ref()
let print_fuel_crate = sopts.debugging_opts.print_fuel.clone();
let print_fuel = Cell::new(0);
+ let working_dir = env::current_dir().unwrap().to_string_lossy().into_owned();
+ let working_dir = file_path_mapping.map_prefix(working_dir);
+
let sess = Session {
dep_graph: dep_graph.clone(),
target: target_cfg,
derive_registrar_fn: Cell::new(None),
default_sysroot: default_sysroot,
local_crate_source_file: local_crate_source_file,
- working_dir: env::current_dir().unwrap(),
+ working_dir: working_dir,
lint_store: RefCell::new(lint::LintStore::new()),
lints: RefCell::new(lint::LintTable::new()),
one_time_diagnostics: RefCell::new(FxHashSet()),
use dep_graph::{DepGraph, DepNode, DepTrackingMap, DepTrackingMapConfig};
use hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
+use hir::def::Def;
use hir;
use middle::const_val;
use middle::privacy::AccessLevels;
}
}
+impl<'tcx> QueryDescription for queries::describe_def<'tcx> {
+ fn describe(_: TyCtxt, _: DefId) -> String {
+ bug!("describe_def")
+ }
+}
+
macro_rules! define_maps {
(<$tcx:tt>
$($(#[$attr:meta])*
pub mir_shims: mir_shim_dep_node(ty::InstanceDef<'tcx>) -> &'tcx RefCell<mir::Mir<'tcx>>,
pub def_symbol_name: SymbolName(DefId) -> ty::SymbolName,
- pub symbol_name: symbol_name_dep_node(ty::Instance<'tcx>) -> ty::SymbolName
+ pub symbol_name: symbol_name_dep_node(ty::Instance<'tcx>) -> ty::SymbolName,
+
+ pub describe_def: meta_data_node(DefId) -> Option<Def>
}
fn coherent_trait_dep_node((_, def_id): (CrateNum, DefId)) -> DepNode<DefId> {
fn const_eval_dep_node((def_id, _): (DefId, &Substs)) -> DepNode<DefId> {
DepNode::ConstEval(def_id)
}
+
+fn meta_data_node(def_id: DefId) -> DepNode<DefId> {
+ DepNode::MetaData(def_id)
+}
\ No newline at end of file
/// ID of the impl that the method belongs to. Otherwise, return `None`.
pub fn impl_of_method(self, def_id: DefId) -> Option<DefId> {
let item = if def_id.krate != LOCAL_CRATE {
- if let Some(Def::Method(_)) = self.sess.cstore.describe_def(def_id) {
+ if let Some(Def::Method(_)) = self.describe_def(def_id) {
Some(self.associated_item(def_id))
} else {
None
_ => Some((def_id, substs))
}
} else {
- match tcx.sess.cstore.describe_def(def_id) {
+ match tcx.describe_def(def_id) {
Some(Def::AssociatedConst(_)) => {
// As mentioned in the comments above for in-crate
// constants, we only try to find the expression for a
let cstore = Rc::new(CStore::new(&dep_graph));
let loader = file_loader.unwrap_or(box RealFileLoader);
- let codemap = Rc::new(CodeMap::with_file_loader(loader));
+ let codemap = Rc::new(CodeMap::with_file_loader(loader, sopts.file_path_mapping()));
let mut sess = session::build_session_with_codemap(
sopts, &dep_graph, input_file_path, descriptions, cstore.clone(), codemap, emitter_dest,
);
use std::rc::Rc;
use syntax::ast;
use syntax::abi::Abi;
-use syntax::codemap::CodeMap;
+use syntax::codemap::{CodeMap, FilePathMapping};
use errors;
use errors::emitter::Emitter;
use errors::{Level, DiagnosticBuilder};
&dep_graph,
None,
diagnostic_handler,
- Rc::new(CodeMap::new()),
+ Rc::new(CodeMap::new(FilePathMapping::empty())),
cstore.clone());
rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess));
let input = config::Input::Str {
use rustc::middle::cstore::{CrateStore, CrateSource, LibSource, DepKind,
ExternCrate, NativeLibrary, LinkMeta,
LinkagePreference, LoadedMacro, EncodedMetadata};
-use rustc::hir::def::{self, Def};
+use rustc::hir::def;
use rustc::middle::lang_items;
use rustc::session::Session;
use rustc::ty::{self, TyCtxt};
closure_type => { cdata.closure_ty(def_id.index, tcx) }
inherent_impls => { Rc::new(cdata.get_inherent_implementations_for_type(def_id.index)) }
is_foreign_item => { cdata.is_foreign_item(def_id.index) }
+ describe_def => { cdata.get_def(def_id.index) }
}
impl CrateStore for cstore::CStore {
self.get_crate_data(krate)
}
- fn describe_def(&self, def: DefId) -> Option<Def> {
- self.dep_graph.read(DepNode::MetaData(def));
- self.get_crate_data(def.krate).get_def(def.index)
- }
-
fn def_span(&self, sess: &Session, def: DefId) -> Span {
self.dep_graph.read(DepNode::MetaData(def));
self.get_crate_data(def.krate).get_span(def.index, sess)
let (name, def) = data.get_macro(id.index);
let source_name = format!("<{} macros>", name);
- let filemap = sess.parse_sess.codemap().new_filemap(source_name, None, def.body);
+ let filemap = sess.parse_sess.codemap().new_filemap(source_name, def.body);
let local_span = Span { lo: filemap.start_pos, hi: filemap.end_pos, ctxt: NO_EXPANSION };
let body = filemap_to_stream(&sess.parse_sess, filemap);
// We can't reuse an existing FileMap, so allocate a new one
// containing the information we need.
let syntax_pos::FileMap { name,
- abs_path,
+ name_was_remapped,
start_pos,
end_pos,
lines,
}
let local_version = local_codemap.new_imported_filemap(name,
- abs_path,
+ name_was_remapped,
source_length,
lines,
multibyte_chars);
use std::intrinsics;
use std::io::prelude::*;
use std::io::Cursor;
+use std::path::Path;
use std::rc::Rc;
use std::u32;
use syntax::ast::{self, CRATE_NODE_ID};
fn encode_codemap(&mut self) -> LazySeq<syntax_pos::FileMap> {
let codemap = self.tcx.sess.codemap();
let all_filemaps = codemap.files.borrow();
- self.lazy_seq_ref(all_filemaps.iter()
+ let adapted = all_filemaps.iter()
.filter(|filemap| {
// No need to re-export imported filemaps, as any downstream
// crate will import them from their original source.
!filemap.is_imported()
})
- .map(|filemap| &**filemap))
+ .map(|filemap| {
+ // When exporting FileMaps, we expand all paths to absolute
+ // paths because any relative paths are potentially relative to
+ // a wrong directory.
+ // However, if a path has been modified via
+ // `-Zremap-path-prefix` we assume the user has already set
+ // things up the way they want and don't touch the path values
+ // anymore.
+ let name = Path::new(&filemap.name);
+ let (ref working_dir, working_dir_was_remapped) = self.tcx.sess.working_dir;
+ if filemap.name_was_remapped ||
+ (name.is_relative() && working_dir_was_remapped) {
+ // This path of this FileMap has been modified by
+ // path-remapping, so we use it verbatim (and avoid cloning
+ // the whole map in the process).
+ filemap.clone()
+ } else {
+ let mut adapted = (**filemap).clone();
+ let abs_path = Path::new(working_dir).join(name)
+ .to_string_lossy()
+ .into_owned();
+ adapted.name = abs_path;
+ Rc::new(adapted)
+ }
+ })
+ .collect::<Vec<_>>();
+
+ self.lazy_seq_ref(adapted.iter().map(|fm| &**fm))
}
fn encode_def_path_table(&mut self) -> Lazy<DefPathTable> {
use std::collections::HashSet;
use std::collections::hash_map::DefaultHasher;
use std::hash::*;
+use std::path::Path;
use syntax::ast::{self, NodeId, PatKind, Attribute, CRATE_NODE_ID};
use syntax::parse::token;
pub fn dump_crate_info(&mut self, name: &str, krate: &ast::Crate) {
let source_file = self.tcx.sess.local_crate_source_file.as_ref();
let crate_root = source_file.map(|source_file| {
+ let source_file = Path::new(source_file);
match source_file.file_name() {
Some(_) => source_file.parent().unwrap().display().to_string(),
None => source_file.display().to_string(),
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use super::FunctionDebugContext;
+use super::{FunctionDebugContext, FunctionDebugContextData};
use super::metadata::file_metadata;
use super::utils::{DIB, span_start};
use llvm;
-use llvm::debuginfo::{DIScope, DISubprogram};
+use llvm::debuginfo::DIScope;
use common::CrateContext;
use rustc::mir::{Mir, VisibilityScope};
};
let mut scopes = IndexVec::from_elem(null_scope, &mir.visibility_scopes);
- let fn_metadata = match *debug_context {
- FunctionDebugContext::RegularContext(ref data) => data.fn_metadata,
+ let debug_context = match *debug_context {
+ FunctionDebugContext::RegularContext(ref data) => data,
FunctionDebugContext::DebugInfoDisabled |
FunctionDebugContext::FunctionWithoutDebugInfo => {
return scopes;
// Instantiate all scopes.
for idx in 0..mir.visibility_scopes.len() {
let scope = VisibilityScope::new(idx);
- make_mir_scope(ccx, &mir, &has_variables, fn_metadata, scope, &mut scopes);
+ make_mir_scope(ccx, &mir, &has_variables, debug_context, scope, &mut scopes);
}
scopes
fn make_mir_scope(ccx: &CrateContext,
mir: &Mir,
has_variables: &BitVector,
- fn_metadata: DISubprogram,
+ debug_context: &FunctionDebugContextData,
scope: VisibilityScope,
scopes: &mut IndexVec<VisibilityScope, MirDebugScope>) {
if scopes[scope].is_valid() {
let scope_data = &mir.visibility_scopes[scope];
let parent_scope = if let Some(parent) = scope_data.parent_scope {
- make_mir_scope(ccx, mir, has_variables, fn_metadata, parent, scopes);
+ make_mir_scope(ccx, mir, has_variables, debug_context, parent, scopes);
scopes[parent]
} else {
// The root is the function itself.
let loc = span_start(ccx, mir.span);
scopes[scope] = MirDebugScope {
- scope_metadata: fn_metadata,
+ scope_metadata: debug_context.fn_metadata,
file_start_pos: loc.file.start_pos,
file_end_pos: loc.file.end_pos,
};
// However, we don't skip creating a nested scope if
// our parent is the root, because we might want to
// put arguments in the root and not have shadowing.
- if parent_scope.scope_metadata != fn_metadata {
+ if parent_scope.scope_metadata != debug_context.fn_metadata {
scopes[scope] = parent_scope;
return;
}
}
let loc = span_start(ccx, scope_data.span);
- let file_metadata = file_metadata(ccx, &loc.file.name, &loc.file.abs_path);
+ let file_metadata = file_metadata(ccx,
+ &loc.file.name,
+ debug_context.defining_crate);
+
let scope_metadata = unsafe {
llvm::LLVMRustDIBuilderCreateLexicalBlock(
DIB(ccx),
DICompositeType, DILexicalBlock, DIFlags};
use rustc::hir::def::CtorKind;
-use rustc::hir::def_id::{DefId, LOCAL_CRATE};
+use rustc::hir::def_id::{DefId, CrateNum, LOCAL_CRATE};
use rustc::ty::fold::TypeVisitor;
use rustc::ty::subst::Substs;
use rustc::ty::util::TypeIdHasher;
use rustc::ty::layout::{self, LayoutTyper};
use session::config;
use util::nodemap::FxHashMap;
-use util::common::path2cstr;
use libc::{c_uint, c_longlong};
use std::ffi::CString;
-use std::path::Path;
use std::ptr;
use syntax::ast;
-use syntax::symbol::{Interner, InternedString};
+use syntax::symbol::{Interner, InternedString, Symbol};
use syntax_pos::{self, Span};
assert!(member_descriptions.len() == member_llvm_types.len());
- let loc = span_start(cx, span);
- let file_metadata = file_metadata(cx, &loc.file.name, &loc.file.abs_path);
+ let file_metadata = unknown_file_metadata(cx);
let metadata = composite_type_metadata(cx,
slice_llvm_type,
metadata
}
-pub fn file_metadata(cx: &CrateContext, path: &str, full_path: &Option<String>) -> DIFile {
- // FIXME (#9639): This needs to handle non-utf8 paths
- let work_dir = cx.sess().working_dir.to_str().unwrap();
- let file_name =
- full_path.as_ref().map(|p| p.as_str()).unwrap_or_else(|| {
- if path.starts_with(work_dir) {
- &path[work_dir.len() + 1..path.len()]
- } else {
- path
- }
- });
+pub fn file_metadata(cx: &CrateContext,
+ file_name: &str,
+ defining_crate: CrateNum) -> DIFile {
+ debug!("file_metadata: file_name: {}, defining_crate: {}",
+ file_name,
+ defining_crate);
- file_metadata_(cx, path, file_name, &work_dir)
+ let directory = if defining_crate == LOCAL_CRATE {
+ &cx.sess().working_dir.0[..]
+ } else {
+ // If the path comes from an upstream crate we assume it has been made
+ // independent of the compiler's working directory one way or another.
+ ""
+ };
+
+ file_metadata_raw(cx, file_name, directory)
}
pub fn unknown_file_metadata(cx: &CrateContext) -> DIFile {
- // Regular filenames should not be empty, so we abuse an empty name as the
- // key for the special unknown file metadata
- file_metadata_(cx, "", "<unknown>", "")
-
+ file_metadata_raw(cx, "<unknown>", "")
}
-fn file_metadata_(cx: &CrateContext, key: &str, file_name: &str, work_dir: &str) -> DIFile {
- if let Some(file_metadata) = debug_context(cx).created_files.borrow().get(key) {
+fn file_metadata_raw(cx: &CrateContext,
+ file_name: &str,
+ directory: &str)
+ -> DIFile {
+ let key = (Symbol::intern(file_name), Symbol::intern(directory));
+
+ if let Some(file_metadata) = debug_context(cx).created_files.borrow().get(&key) {
return *file_metadata;
}
- debug!("file_metadata: file_name: {}, work_dir: {}", file_name, work_dir);
+ debug!("file_metadata: file_name: {}, directory: {}", file_name, directory);
let file_name = CString::new(file_name).unwrap();
- let work_dir = CString::new(work_dir).unwrap();
+ let directory = CString::new(directory).unwrap();
+
let file_metadata = unsafe {
- llvm::LLVMRustDIBuilderCreateFile(DIB(cx), file_name.as_ptr(),
- work_dir.as_ptr())
+ llvm::LLVMRustDIBuilderCreateFile(DIB(cx),
+ file_name.as_ptr(),
+ directory.as_ptr())
};
let mut created_files = debug_context(cx).created_files.borrow_mut();
- created_files.insert(key.to_string(), file_metadata);
+ created_files.insert(key, file_metadata);
file_metadata
}
debug_context: &CrateDebugContext,
sess: &Session)
-> DIDescriptor {
- let work_dir = &sess.working_dir;
let compile_unit_name = match sess.local_crate_source_file {
None => fallback_path(scc),
- Some(ref abs_path) => {
- if abs_path.is_relative() {
- sess.warn("debuginfo: Invalid path to crate's local root source file!");
- fallback_path(scc)
- } else {
- match abs_path.strip_prefix(work_dir) {
- Ok(ref p) if p.is_relative() => {
- if p.starts_with(Path::new("./")) {
- path2cstr(p)
- } else {
- path2cstr(&Path::new(".").join(p))
- }
- }
- _ => fallback_path(scc)
- }
- }
+ Some(ref path) => {
+ CString::new(&path[..]).unwrap()
}
};
(option_env!("CFG_VERSION")).expect("CFG_VERSION"));
let compile_unit_name = compile_unit_name.as_ptr();
- let work_dir = path2cstr(&work_dir);
+
+ let work_dir = CString::new(&sess.working_dir.0[..]).unwrap();
let producer = CString::new(producer).unwrap();
let flags = "\0";
let split_name = "\0";
let (file_metadata, line_number) = if span != syntax_pos::DUMMY_SP {
let loc = span_start(cx, span);
- (file_metadata(cx, &loc.file.name, &loc.file.abs_path), loc.line as c_uint)
+ (file_metadata(cx, &loc.file.name, LOCAL_CRATE), loc.line as c_uint)
} else {
(unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER)
};
// Creates an "extension" of an existing DIScope into another file.
pub fn extend_scope_to_file(ccx: &CrateContext,
scope_metadata: DIScope,
- file: &syntax_pos::FileMap)
+ file: &syntax_pos::FileMap,
+ defining_crate: CrateNum)
-> DILexicalBlock {
- let file_metadata = file_metadata(ccx, &file.name, &file.abs_path);
+ let file_metadata = file_metadata(ccx, &file.name, defining_crate);
unsafe {
llvm::LLVMRustDIBuilderCreateLexicalBlockFile(
DIB(ccx),
use llvm;
use llvm::{ModuleRef, ContextRef, ValueRef};
use llvm::debuginfo::{DIFile, DIType, DIScope, DIBuilderRef, DISubprogram, DIArray, DIFlags};
-use rustc::hir::def_id::DefId;
+use rustc::hir::def_id::{DefId, CrateNum};
use rustc::ty::subst::Substs;
use abi::Abi;
use syntax_pos::{self, Span, Pos};
use syntax::ast;
+use syntax::symbol::Symbol;
use rustc::ty::layout;
pub mod gdb;
pub struct CrateDebugContext<'tcx> {
llcontext: ContextRef,
builder: DIBuilderRef,
- created_files: RefCell<FxHashMap<String, DIFile>>,
+ created_files: RefCell<FxHashMap<(Symbol, Symbol), DIFile>>,
created_enum_disr_types: RefCell<FxHashMap<(DefId, layout::Integer), DIType>>,
type_map: RefCell<TypeMap<'tcx>>,
}
impl FunctionDebugContext {
- fn get_ref<'a>(&'a self, span: Span) -> &'a FunctionDebugContextData {
+ pub fn get_ref<'a>(&'a self, span: Span) -> &'a FunctionDebugContextData {
match *self {
FunctionDebugContext::RegularContext(ref data) => data,
FunctionDebugContext::DebugInfoDisabled => {
pub struct FunctionDebugContextData {
fn_metadata: DISubprogram,
source_locations_enabled: Cell<bool>,
+ pub defining_crate: CrateNum,
}
pub enum VariableAccess<'a> {
return FunctionDebugContext::FunctionWithoutDebugInfo;
}
+ let def_id = instance.def_id();
let loc = span_start(cx, span);
- let file_metadata = file_metadata(cx, &loc.file.name, &loc.file.abs_path);
+ let file_metadata = file_metadata(cx, &loc.file.name, def_id.krate);
let function_type_metadata = unsafe {
let fn_signature = get_function_signature(cx, sig);
};
// Find the enclosing function, in case this is a closure.
- let def_key = cx.tcx().def_key(instance.def_id());
+ let def_key = cx.tcx().def_key(def_id);
let mut name = def_key.disambiguated_data.data.to_string();
let name_len = name.len();
- let fn_def_id = cx.tcx().closure_base_def_id(instance.def_id());
+ let enclosing_fn_def_id = cx.tcx().closure_base_def_id(def_id);
// Get_template_parameters() will append a `<...>` clause to the function
// name if necessary.
- let generics = cx.tcx().generics_of(fn_def_id);
+ let generics = cx.tcx().generics_of(enclosing_fn_def_id);
let substs = instance.substs.truncate_to(cx.tcx(), generics);
let template_parameters = get_template_parameters(cx,
&generics,
let fn_debug_context = FunctionDebugContextData {
fn_metadata: fn_metadata,
source_locations_enabled: Cell::new(false),
+ defining_crate: def_id.krate,
};
return FunctionDebugContext::RegularContext(fn_debug_context);
let cx = bcx.ccx;
let file = span_start(cx, span).file;
- let filename = file.name.clone();
- let file_metadata = file_metadata(cx, &filename[..], &file.abs_path);
+ let file_metadata = file_metadata(cx,
+ &file.name[..],
+ dbg_context.get_ref(span).defining_crate);
let loc = span_start(cx, span);
let type_metadata = type_metadata(cx, variable_type, span);
let span = ccx.tcx().def_span(def_id);
let (file, line) = if span != DUMMY_SP {
let loc = span_start(ccx, span);
- (file_metadata(ccx, &loc.file.name, &loc.file.abs_path), loc.line as c_uint)
+ (file_metadata(ccx, &loc.file.name, def_id.krate), loc.line as c_uint)
} else {
(unknown_file_metadata(ccx), UNKNOWN_LINE_NUMBER)
};
if pos < self.scopes[scope_id].file_start_pos ||
pos >= self.scopes[scope_id].file_end_pos {
let cm = self.ccx.sess().codemap();
- debuginfo::extend_scope_to_file(self.ccx, scope_metadata, &cm.lookup_char_pos(pos).file)
+ let defining_crate = self.debug_context.get_ref(DUMMY_SP).defining_crate;
+ debuginfo::extend_scope_to_file(self.ccx,
+ scope_metadata,
+ &cm.lookup_char_pos(pos).file,
+ defining_crate)
} else {
scope_metadata
}
trait",
trait_c.name);
- // Add a label to the Span containing just the type of the item
- let trait_c_node_id = tcx.hir.as_local_node_id(trait_c.def_id).unwrap();
- let trait_c_span = match tcx.hir.expect_trait_item(trait_c_node_id).node {
- TraitItemKind::Const(ref ty, _) => ty.span,
- _ => bug!("{:?} is not a trait const", trait_c),
- };
+ let trait_c_node_id = tcx.hir.as_local_node_id(trait_c.def_id);
+ let trait_c_span = trait_c_node_id.map(|trait_c_node_id| {
+ // Add a label to the Span containing just the type of the const
+ match tcx.hir.expect_trait_item(trait_c_node_id).node {
+ TraitItemKind::Const(ref ty, _) => ty.span,
+ _ => bug!("{:?} is not a trait const", trait_c),
+ }
+ });
infcx.note_type_err(&mut diag,
&cause,
- Some((trait_c_span, format!("type in trait"))),
+ trait_c_span.map(|span| (span, format!("type in trait"))),
Some(infer::ValuePairs::Types(ExpectedFound {
expected: trait_ty,
found: impl_ty,
..config::basic_options().clone()
};
- let codemap = Rc::new(codemap::CodeMap::new());
+ let codemap = Rc::new(codemap::CodeMap::new(sessopts.file_path_mapping()));
let diagnostic_handler = errors::Handler::with_tty_emitter(ColorConfig::Auto,
true,
false,
use std::io;
use std::io::prelude::*;
-use syntax::codemap::CodeMap;
+use syntax::codemap::{CodeMap, FilePathMapping};
use syntax::parse::lexer::{self, TokenAndSpan};
use syntax::parse::token;
use syntax::parse;
pub fn render_with_highlighting(src: &str, class: Option<&str>, id: Option<&str>,
extension: Option<&str>) -> String {
debug!("highlighting: ================\n{}\n==============", src);
- let sess = parse::ParseSess::new();
- let fm = sess.codemap().new_filemap("<stdin>".to_string(), None, src.to_string());
+ let sess = parse::ParseSess::new(FilePathMapping::empty());
+ let fm = sess.codemap().new_filemap("<stdin>".to_string(), src.to_string());
let mut out = Vec::new();
write_header(class, id, &mut out).unwrap();
/// be inserted into an element. C.f., `render_with_highlighting` which includes
/// an enclosing `<pre>` block.
pub fn render_inner_with_highlighting(src: &str) -> io::Result<String> {
- let sess = parse::ParseSess::new();
- let fm = sess.codemap().new_filemap("<stdin>".to_string(), None, src.to_string());
+ let sess = parse::ParseSess::new(FilePathMapping::empty());
+ let fm = sess.codemap().new_filemap("<stdin>".to_string(), src.to_string());
let mut out = Vec::new();
let mut classifier = Classifier::new(lexer::StringReader::new(&sess, fm), sess.codemap());
..config::basic_options().clone()
};
- let codemap = Rc::new(CodeMap::new());
+ let codemap = Rc::new(CodeMap::new(sessopts.file_path_mapping()));
let handler =
errors::Handler::with_tty_emitter(ColorConfig::Auto, true, false, Some(codemap.clone()));
}
}
let data = Arc::new(Mutex::new(Vec::new()));
- let codemap = Rc::new(CodeMap::new());
+ let codemap = Rc::new(CodeMap::new(sessopts.file_path_mapping()));
let emitter = errors::emitter::EmitterWriter::new(box Sink(data.clone()),
Some(codemap.clone()));
let old = io::set_panic(Some(box Sink(data.clone())));
pub struct CodeMap {
pub files: RefCell<Vec<Rc<FileMap>>>,
- file_loader: Box<FileLoader>
+ file_loader: Box<FileLoader>,
+ // This is used to apply the file path remapping as specified via
+ // -Zremap-path-prefix to all FileMaps allocated within this CodeMap.
+ path_mapping: FilePathMapping,
}
impl CodeMap {
- pub fn new() -> CodeMap {
+ pub fn new(path_mapping: FilePathMapping) -> CodeMap {
CodeMap {
files: RefCell::new(Vec::new()),
- file_loader: Box::new(RealFileLoader)
+ file_loader: Box::new(RealFileLoader),
+ path_mapping: path_mapping,
}
}
- pub fn with_file_loader(file_loader: Box<FileLoader>) -> CodeMap {
+ pub fn with_file_loader(file_loader: Box<FileLoader>,
+ path_mapping: FilePathMapping)
+ -> CodeMap {
CodeMap {
files: RefCell::new(Vec::new()),
- file_loader: file_loader
+ file_loader: file_loader,
+ path_mapping: path_mapping,
}
}
+ pub fn path_mapping(&self) -> &FilePathMapping {
+ &self.path_mapping
+ }
+
pub fn file_exists(&self, path: &Path) -> bool {
self.file_loader.file_exists(path)
}
pub fn load_file(&self, path: &Path) -> io::Result<Rc<FileMap>> {
let src = self.file_loader.read_file(path)?;
- let abs_path = self.file_loader.abs_path(path).map(|p| p.to_str().unwrap().to_string());
- Ok(self.new_filemap(path.to_str().unwrap().to_string(), abs_path, src))
+ Ok(self.new_filemap(path.to_str().unwrap().to_string(), src))
}
fn next_start_pos(&self) -> usize {
/// Creates a new filemap without setting its line information. If you don't
/// intend to set the line information yourself, you should use new_filemap_and_lines.
- pub fn new_filemap(&self, filename: FileName, abs_path: Option<FileName>,
- mut src: String) -> Rc<FileMap> {
+ pub fn new_filemap(&self, filename: FileName, mut src: String) -> Rc<FileMap> {
let start_pos = self.next_start_pos();
let mut files = self.files.borrow_mut();
let end_pos = start_pos + src.len();
+ let (filename, was_remapped) = self.path_mapping.map_prefix(filename);
+
let filemap = Rc::new(FileMap {
name: filename,
- abs_path: abs_path,
+ name_was_remapped: was_remapped,
src: Some(Rc::new(src)),
start_pos: Pos::from_usize(start_pos),
end_pos: Pos::from_usize(end_pos),
}
/// Creates a new filemap and sets its line information.
- pub fn new_filemap_and_lines(&self, filename: &str, abs_path: Option<&str>,
- src: &str) -> Rc<FileMap> {
- let fm = self.new_filemap(filename.to_string(),
- abs_path.map(|s| s.to_owned()),
- src.to_owned());
+ pub fn new_filemap_and_lines(&self, filename: &str, src: &str) -> Rc<FileMap> {
+ let fm = self.new_filemap(filename.to_string(), src.to_owned());
let mut byte_pos: u32 = fm.start_pos.0;
for line in src.lines() {
// register the start of this line
/// information for things inlined from other crates.
pub fn new_imported_filemap(&self,
filename: FileName,
- abs_path: Option<FileName>,
+ name_was_remapped: bool,
source_len: usize,
mut file_local_lines: Vec<BytePos>,
mut file_local_multibyte_chars: Vec<MultiByteChar>)
let filemap = Rc::new(FileMap {
name: filename,
- abs_path: abs_path,
+ name_was_remapped: name_was_remapped,
src: None,
start_pos: start_pos,
end_pos: end_pos,
}
}
+#[derive(Clone)]
+pub struct FilePathMapping {
+ mapping: Vec<(String, String)>,
+}
+
+impl FilePathMapping {
+ pub fn empty() -> FilePathMapping {
+ FilePathMapping {
+ mapping: vec![]
+ }
+ }
+
+ pub fn new(mapping: Vec<(String, String)>) -> FilePathMapping {
+ FilePathMapping {
+ mapping: mapping
+ }
+ }
+
+ /// Applies any path prefix substitution as defined by the mapping.
+ /// The return value is the remapped path and a boolean indicating whether
+ /// the path was affected by the mapping.
+ pub fn map_prefix(&self, path: String) -> (String, bool) {
+ // NOTE: We are iterating over the mapping entries from last to first
+ // because entries specified later on the command line should
+ // take precedence.
+ for &(ref from, ref to) in self.mapping.iter().rev() {
+ if path.starts_with(from) {
+ let mapped = path.replacen(from, to, 1);
+ return (mapped, true);
+ }
+ }
+
+ (path, false)
+ }
+}
+
// _____________________________________________________________________________
// Tests
//
#[test]
fn t1 () {
- let cm = CodeMap::new();
+ let cm = CodeMap::new(FilePathMapping::empty());
let fm = cm.new_filemap("blork.rs".to_string(),
- None,
"first line.\nsecond line".to_string());
fm.next_line(BytePos(0));
// Test we can get lines with partial line info.
#[test]
#[should_panic]
fn t2 () {
- let cm = CodeMap::new();
+ let cm = CodeMap::new(FilePathMapping::empty());
let fm = cm.new_filemap("blork.rs".to_string(),
- None,
"first line.\nsecond line".to_string());
// TESTING *REALLY* BROKEN BEHAVIOR:
fm.next_line(BytePos(0));
}
fn init_code_map() -> CodeMap {
- let cm = CodeMap::new();
+ let cm = CodeMap::new(FilePathMapping::empty());
let fm1 = cm.new_filemap("blork.rs".to_string(),
- None,
"first line.\nsecond line".to_string());
let fm2 = cm.new_filemap("empty.rs".to_string(),
- None,
"".to_string());
let fm3 = cm.new_filemap("blork2.rs".to_string(),
- None,
"first line.\nsecond line".to_string());
fm1.next_line(BytePos(0));
}
fn init_code_map_mbc() -> CodeMap {
- let cm = CodeMap::new();
+ let cm = CodeMap::new(FilePathMapping::empty());
// € is a three byte utf8 char.
let fm1 =
cm.new_filemap("blork.rs".to_string(),
- None,
"fir€st €€€€ line.\nsecond line".to_string());
let fm2 = cm.new_filemap("blork2.rs".to_string(),
- None,
"first line€€.\n€ second line".to_string());
fm1.next_line(BytePos(0));
/// lines in the middle of a file.
#[test]
fn span_to_snippet_and_lines_spanning_multiple_lines() {
- let cm = CodeMap::new();
+ let cm = CodeMap::new(FilePathMapping::empty());
let inputtext = "aaaaa\nbbbbBB\nCCC\nDDDDDddddd\neee\n";
let selection = " \n ~~\n~~~\n~~~~~ \n \n";
- cm.new_filemap_and_lines("blork.rs", None, inputtext);
+ cm.new_filemap_and_lines("blork.rs", inputtext);
let span = span_from_selection(inputtext, selection);
// check that we are extracting the text we thought we were extracting
/// Test failing to merge two spans on different lines
#[test]
fn span_merging_fail() {
- let cm = CodeMap::new();
+ let cm = CodeMap::new(FilePathMapping::empty());
let inputtext = "bbbb BB\ncc CCC\n";
let selection1 = " ~~\n \n";
let selection2 = " \n ~~~\n";
- cm.new_filemap_and_lines("blork.rs", None, inputtext);
+ cm.new_filemap_and_lines("blork.rs", inputtext);
let span1 = span_from_selection(inputtext, selection1);
let span2 = span_from_selection(inputtext, selection2);
fn string_to_stream(text: String, parse_sess: &ParseSess) -> TokenStream {
let filename = String::from("<macro expansion>");
- filemap_to_stream(parse_sess, parse_sess.codemap().new_filemap(filename, None, text))
+ filemap_to_stream(parse_sess, parse_sess.codemap().new_filemap(filename, text))
}
impl<'a, 'b> Folder for InvocationCollector<'a, 'b> {
// Add this input file to the code map to make it available as
// dependency information
let filename = format!("{}", file.display());
- cx.codemap().new_filemap_and_lines(&filename, None, &src);
+ cx.codemap().new_filemap_and_lines(&filename, &src);
base::MacEager::expr(cx.expr_str(sp, Symbol::intern(&src)))
}
// Add this input file to the code map to make it available as
// dependency information, but don't enter it's contents
let filename = format!("{}", file.display());
- cx.codemap().new_filemap_and_lines(&filename, None, "");
+ cx.codemap().new_filemap_and_lines(&filename, "");
base::MacEager::expr(cx.expr_lit(sp, ast::LitKind::ByteStr(Rc::new(bytes))))
}
// FIXME spec the JSON output properly.
-use codemap::CodeMap;
+use codemap::{CodeMap, FilePathMapping};
use syntax_pos::{self, MacroBacktrace, Span, SpanLabel, MultiSpan};
use errors::registry::Registry;
use errors::{DiagnosticBuilder, SubDiagnostic, RenderSpan, CodeSuggestion, CodeMapper};
}
pub fn basic() -> JsonEmitter {
- JsonEmitter::stderr(None, Rc::new(CodeMap::new()))
+ let file_path_mapping = FilePathMapping::empty();
+ JsonEmitter::stderr(None, Rc::new(CodeMap::new(file_path_mapping)))
}
pub fn new(dst: Box<Write + Send>,
let mut src = Vec::new();
srdr.read_to_end(&mut src).unwrap();
let src = String::from_utf8(src).unwrap();
- let cm = CodeMap::new();
- let filemap = cm.new_filemap(path, None, src);
+ let cm = CodeMap::new(sess.codemap().path_mapping().clone());
+ let filemap = cm.new_filemap(path, src);
let mut rdr = lexer::StringReader::new_raw(sess, filemap);
let mut comments: Vec<Comment> = Vec::new();
use ast::{self, Ident};
use syntax_pos::{self, BytePos, CharPos, Pos, Span, NO_EXPANSION};
-use codemap::CodeMap;
+use codemap::{CodeMap, FilePathMapping};
use errors::{FatalError, DiagnosticBuilder};
use parse::{token, ParseSess};
use str::char_at;
// I guess this is the only way to figure out if
// we're at the beginning of the file...
- let cmap = CodeMap::new();
+ let cmap = CodeMap::new(FilePathMapping::empty());
cmap.files.borrow_mut().push(self.filemap.clone());
let loc = cmap.lookup_char_pos_adj(self.pos);
debug!("Skipping a shebang");
sess: &'a ParseSess,
teststr: String)
-> StringReader<'a> {
- let fm = cm.new_filemap("zebra.rs".to_string(), None, teststr);
+ let fm = cm.new_filemap("zebra.rs".to_string(), teststr);
StringReader::new(sess, fm)
}
#[test]
fn t1() {
- let cm = Rc::new(CodeMap::new());
+ let cm = Rc::new(CodeMap::new(FilePathMapping::empty()));
let sh = mk_sess(cm.clone());
let mut string_reader = setup(&cm,
&sh,
#[test]
fn doublecolonparsing() {
- let cm = Rc::new(CodeMap::new());
+ let cm = Rc::new(CodeMap::new(FilePathMapping::empty()));
let sh = mk_sess(cm.clone());
check_tokenization(setup(&cm, &sh, "a b".to_string()),
vec![mk_ident("a"), token::Whitespace, mk_ident("b")]);
#[test]
fn dcparsing_2() {
- let cm = Rc::new(CodeMap::new());
+ let cm = Rc::new(CodeMap::new(FilePathMapping::empty()));
let sh = mk_sess(cm.clone());
check_tokenization(setup(&cm, &sh, "a::b".to_string()),
vec![mk_ident("a"), token::ModSep, mk_ident("b")]);
#[test]
fn dcparsing_3() {
- let cm = Rc::new(CodeMap::new());
+ let cm = Rc::new(CodeMap::new(FilePathMapping::empty()));
let sh = mk_sess(cm.clone());
check_tokenization(setup(&cm, &sh, "a ::b".to_string()),
vec![mk_ident("a"), token::Whitespace, token::ModSep, mk_ident("b")]);
#[test]
fn dcparsing_4() {
- let cm = Rc::new(CodeMap::new());
+ let cm = Rc::new(CodeMap::new(FilePathMapping::empty()));
let sh = mk_sess(cm.clone());
check_tokenization(setup(&cm, &sh, "a:: b".to_string()),
vec![mk_ident("a"), token::ModSep, token::Whitespace, mk_ident("b")]);
#[test]
fn character_a() {
- let cm = Rc::new(CodeMap::new());
+ let cm = Rc::new(CodeMap::new(FilePathMapping::empty()));
let sh = mk_sess(cm.clone());
assert_eq!(setup(&cm, &sh, "'a'".to_string()).next_token().tok,
token::Literal(token::Char(Symbol::intern("a")), None));
#[test]
fn character_space() {
- let cm = Rc::new(CodeMap::new());
+ let cm = Rc::new(CodeMap::new(FilePathMapping::empty()));
let sh = mk_sess(cm.clone());
assert_eq!(setup(&cm, &sh, "' '".to_string()).next_token().tok,
token::Literal(token::Char(Symbol::intern(" ")), None));
#[test]
fn character_escaped() {
- let cm = Rc::new(CodeMap::new());
+ let cm = Rc::new(CodeMap::new(FilePathMapping::empty()));
let sh = mk_sess(cm.clone());
assert_eq!(setup(&cm, &sh, "'\\n'".to_string()).next_token().tok,
token::Literal(token::Char(Symbol::intern("\\n")), None));
#[test]
fn lifetime_name() {
- let cm = Rc::new(CodeMap::new());
+ let cm = Rc::new(CodeMap::new(FilePathMapping::empty()));
let sh = mk_sess(cm.clone());
assert_eq!(setup(&cm, &sh, "'abc".to_string()).next_token().tok,
token::Lifetime(Ident::from_str("'abc")));
#[test]
fn raw_string() {
- let cm = Rc::new(CodeMap::new());
+ let cm = Rc::new(CodeMap::new(FilePathMapping::empty()));
let sh = mk_sess(cm.clone());
assert_eq!(setup(&cm, &sh, "r###\"\"#a\\b\x00c\"\"###".to_string())
.next_token()
#[test]
fn literal_suffixes() {
- let cm = Rc::new(CodeMap::new());
+ let cm = Rc::new(CodeMap::new(FilePathMapping::empty()));
let sh = mk_sess(cm.clone());
macro_rules! test {
($input: expr, $tok_type: ident, $tok_contents: expr) => {{
#[test]
fn nested_block_comments() {
- let cm = Rc::new(CodeMap::new());
+ let cm = Rc::new(CodeMap::new(FilePathMapping::empty()));
let sh = mk_sess(cm.clone());
let mut lexer = setup(&cm, &sh, "/* /* */ */'a'".to_string());
match lexer.next_token().tok {
#[test]
fn crlf_comments() {
- let cm = Rc::new(CodeMap::new());
+ let cm = Rc::new(CodeMap::new(FilePathMapping::empty()));
let sh = mk_sess(cm.clone());
let mut lexer = setup(&cm, &sh, "// test\r\n/// test\r\n".to_string());
let comment = lexer.next_token();
//! The main parser interface
use ast::{self, CrateConfig};
-use codemap::CodeMap;
+use codemap::{CodeMap, FilePathMapping};
use syntax_pos::{self, Span, FileMap, NO_EXPANSION};
use errors::{Handler, ColorConfig, DiagnosticBuilder};
use feature_gate::UnstableFeatures;
}
impl ParseSess {
- pub fn new() -> Self {
- let cm = Rc::new(CodeMap::new());
+ pub fn new(file_path_mapping: FilePathMapping) -> Self {
+ let cm = Rc::new(CodeMap::new(file_path_mapping));
let handler = Handler::with_tty_emitter(ColorConfig::Auto,
true,
false,
pub fn parse_stream_from_source_str<'a>(name: String, source: String, sess: &'a ParseSess)
-> TokenStream {
- filemap_to_stream(sess, sess.codemap().new_filemap(name, None, source))
+ filemap_to_stream(sess, sess.codemap().new_filemap(name, source))
}
// Create a new parser from a source string
pub fn new_parser_from_source_str<'a>(sess: &'a ParseSess, name: String, source: String)
-> Parser<'a> {
- filemap_to_parser(sess, sess.codemap().new_filemap(name, None, source))
+ filemap_to_parser(sess, sess.codemap().new_filemap(name, source))
}
/// Create a new parser, handling errors as appropriate
}
#[test] fn parse_ident_pat () {
- let sess = ParseSess::new();
+ let sess = ParseSess::new(FilePathMapping::empty());
let mut parser = string_to_parser(&sess, "b".to_string());
assert!(panictry!(parser.parse_pat())
== P(ast::Pat{
}
#[test] fn crlf_doc_comments() {
- let sess = ParseSess::new();
+ let sess = ParseSess::new(FilePathMapping::empty());
let name = "<source>".to_string();
let source = "/// doc comment\r\nfn foo() {}".to_string();
#[test]
fn ttdelim_span() {
- let sess = ParseSess::new();
+ let sess = ParseSess::new(FilePathMapping::empty());
let expr = parse::parse_expr_from_source_str("foo".to_string(),
"foo!( fn main() { body } )".to_string(), &sess).unwrap();
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use codemap::CodeMap;
+use codemap::{CodeMap, FilePathMapping};
use errors::Handler;
use errors::emitter::EmitterWriter;
use std::io;
fn test_harness(file_text: &str, span_labels: Vec<SpanLabel>, expected_output: &str) {
let output = Arc::new(Mutex::new(Vec::new()));
- let code_map = Rc::new(CodeMap::new());
- code_map.new_filemap_and_lines("test.rs", None, &file_text);
+ let code_map = Rc::new(CodeMap::new(FilePathMapping::empty()));
+ code_map.new_filemap_and_lines("test.rs", &file_text);
let primary_span = make_span(&file_text, &span_labels[0].start, &span_labels[0].end);
let mut msp = MultiSpan::from_span(primary_span);
// except according to those terms.
use ast::{self, Ident};
+use codemap::FilePathMapping;
use parse::{ParseSess, PResult, filemap_to_stream};
use parse::{lexer, new_parser_from_source_str};
use parse::parser::Parser;
/// Map a string to tts, using a made-up filename:
pub fn string_to_stream(source_str: String) -> TokenStream {
- let ps = ParseSess::new();
- filemap_to_stream(&ps, ps.codemap().new_filemap("bogofile".to_string(), None, source_str))
+ let ps = ParseSess::new(FilePathMapping::empty());
+ filemap_to_stream(&ps, ps.codemap().new_filemap("bogofile".to_string(), source_str))
}
/// Map string to parser (via tts)
/// Parse a string, return a crate.
pub fn string_to_crate (source_str : String) -> ast::Crate {
- let ps = ParseSess::new();
+ let ps = ParseSess::new(FilePathMapping::empty());
with_error_checking_parse(source_str, &ps, |p| {
p.parse_crate_mod()
})
/// Parse a string, return an expr
pub fn string_to_expr (source_str : String) -> P<ast::Expr> {
- let ps = ParseSess::new();
+ let ps = ParseSess::new(FilePathMapping::empty());
with_error_checking_parse(source_str, &ps, |p| {
p.parse_expr()
})
/// Parse a string, return an item
pub fn string_to_item (source_str : String) -> Option<P<ast::Item>> {
- let ps = ParseSess::new();
+ let ps = ParseSess::new(FilePathMapping::empty());
with_error_checking_parse(source_str, &ps, |p| {
p.parse_item()
})
/// Parse a string, return a stmt
pub fn string_to_stmt(source_str : String) -> Option<ast::Stmt> {
- let ps = ParseSess::new();
+ let ps = ParseSess::new(FilePathMapping::empty());
with_error_checking_parse(source_str, &ps, |p| {
p.parse_stmt()
})
/// Parse a string, return a pat. Uses "irrefutable"... which doesn't
/// (currently) affect parsing.
pub fn string_to_pat(source_str: String) -> P<ast::Pat> {
- let ps = ParseSess::new();
+ let ps = ParseSess::new(FilePathMapping::empty());
with_error_checking_parse(source_str, &ps, |p| {
p.parse_pat()
})
}
/// A single source in the CodeMap.
+#[derive(Clone)]
pub struct FileMap {
/// The name of the file that the source came from, source that doesn't
/// originate from files has names between angle brackets by convention,
/// e.g. `<anon>`
pub name: FileName,
- /// The absolute path of the file that the source came from.
- pub abs_path: Option<FileName>,
+ /// True if the `name` field above has been modified by -Zremap-path-prefix
+ pub name_was_remapped: bool,
/// The complete source code
pub src: Option<Rc<String>>,
/// The start position of this source in the CodeMap
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
s.emit_struct("FileMap", 6, |s| {
s.emit_struct_field("name", 0, |s| self.name.encode(s))?;
- s.emit_struct_field("abs_path", 1, |s| self.abs_path.encode(s))?;
+ s.emit_struct_field("name_was_remapped", 1, |s| self.name_was_remapped.encode(s))?;
s.emit_struct_field("start_pos", 2, |s| self.start_pos.encode(s))?;
s.emit_struct_field("end_pos", 3, |s| self.end_pos.encode(s))?;
s.emit_struct_field("lines", 4, |s| {
d.read_struct("FileMap", 6, |d| {
let name: String = d.read_struct_field("name", 0, |d| Decodable::decode(d))?;
- let abs_path: Option<String> =
- d.read_struct_field("abs_path", 1, |d| Decodable::decode(d))?;
+ let name_was_remapped: bool =
+ d.read_struct_field("name_was_remapped", 1, |d| Decodable::decode(d))?;
let start_pos: BytePos = d.read_struct_field("start_pos", 2, |d| Decodable::decode(d))?;
let end_pos: BytePos = d.read_struct_field("end_pos", 3, |d| Decodable::decode(d))?;
let lines: Vec<BytePos> = d.read_struct_field("lines", 4, |d| {
d.read_struct_field("multibyte_chars", 5, |d| Decodable::decode(d))?;
Ok(FileMap {
name: name,
- abs_path: abs_path,
+ name_was_remapped: name_was_remapped,
start_pos: start_pos,
end_pos: end_pos,
src: None,
--- /dev/null
+// Copyright 2017 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.
+
+// ignore-tidy-linelength
+
+// compile-flags: -g -Zremap-path-prefix-from={{cwd}} -Zremap-path-prefix-to=/the/aux-cwd -Zremap-path-prefix-from={{src-base}}/remap_path_prefix/auxiliary -Zremap-path-prefix-to=/the/aux-src
+
+#[inline]
+pub fn some_aux_function() -> i32 {
+ 1234
+}
--- /dev/null
+// Copyright 2017 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.
+
+// ignore-windows
+// ignore-tidy-linelength
+
+// compile-flags: -g -C no-prepopulate-passes -Zremap-path-prefix-from={{cwd}} -Zremap-path-prefix-to=/the/cwd -Zremap-path-prefix-from={{src-base}} -Zremap-path-prefix-to=/the/src
+// aux-build:remap_path_prefix_aux.rs
+
+extern crate remap_path_prefix_aux;
+
+// Here we check that the expansion of the file!() macro is mapped.
+// CHECK: internal constant [34 x i8] c"/the/src/remap_path_prefix/main.rs"
+pub static FILE_PATH: &'static str = file!();
+
+fn main() {
+ remap_path_prefix_aux::some_aux_function();
+}
+
+// Here we check that local debuginfo is mapped correctly.
+// CHECK: !DIFile(filename: "/the/src/remap_path_prefix/main.rs", directory: "/the/cwd")
+
+// And here that debuginfo from other crates are expanded to absolute paths.
+// CHECK: !DIFile(filename: "/the/aux-src/remap_path_prefix_aux.rs", directory: "")
extern crate syntax_pos;
use syntax::ast;
+use syntax::codemap::FilePathMapping;
use syntax::print::pprust;
use syntax::symbol::Symbol;
use syntax_pos::DUMMY_SP;
fn main() {
- let ps = syntax::parse::ParseSess::new();
+ let ps = syntax::parse::ParseSess::new(FilePathMapping::empty());
let mut resolver = syntax::ext::base::DummyResolver;
let mut cx = syntax::ext::base::ExtCtxt::new(
&ps,
--- /dev/null
+// Copyright 2017 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.
+
+#![feature(associated_consts)]
+
+pub trait Trait {
+ const CONST: u32;
+}
--- /dev/null
+// Copyright 2017 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.
+
+// aux-build:issue_41549.rs
+
+#![feature(associated_consts)]
+
+extern crate issue_41549;
+
+struct S;
+
+impl issue_41549::Trait for S {
+ const CONST: () = (); //~ ERROR incompatible type for trait
+}
+
+fn main() {}
use syntax_pos::DUMMY_SP;
fn main() {
- let ps = syntax::parse::ParseSess::new();
+ let ps = syntax::parse::ParseSess::new(codemap::FilePathMapping::empty());
let mut resolver = syntax::ext::base::DummyResolver;
let mut cx = syntax::ext::base::ExtCtxt::new(
&ps,
use syntax::ast::*;
use syntax::attr::*;
use syntax::ast;
+use syntax::codemap::FilePathMapping;
use syntax::parse;
use syntax::parse::{ParseSess, PResult};
use syntax::parse::new_parser_from_source_str;
}
fn check_expr_attrs(es: &str, expected: &[&str]) {
- let ps = ParseSess::new();
+ let ps = ParseSess::new(FilePathMapping::empty());
let e = expr(es, &ps).expect("parse error");
let actual = &e.attrs;
str_compare(es,
}
fn check_stmt_attrs(es: &str, expected: &[&str]) {
- let ps = ParseSess::new();
+ let ps = ParseSess::new(FilePathMapping::empty());
let e = stmt(es, &ps).expect("parse error");
let actual = e.node.attrs();
str_compare(es,
}
fn reject_expr_parse(es: &str) {
- let ps = ParseSess::new();
+ let ps = ParseSess::new(FilePathMapping::empty());
match expr(es, &ps) {
Ok(_) => panic!("parser did not reject `{}`", es),
Err(mut e) => e.cancel(),
}
fn reject_stmt_parse(es: &str) {
- let ps = ParseSess::new();
+ let ps = ParseSess::new(FilePathMapping::empty());
match stmt(es, &ps) {
Ok(_) => panic!("parser did not reject `{}`", es),
Err(mut e) => e.cancel(),
extern crate syntax;
extern crate syntax_pos;
+use syntax::codemap::FilePathMapping;
use syntax::print::pprust::*;
use syntax::symbol::Symbol;
use syntax_pos::DUMMY_SP;
fn main() {
- let ps = syntax::parse::ParseSess::new();
+ let ps = syntax::parse::ParseSess::new(FilePathMapping::empty());
let mut resolver = syntax::ext::base::DummyResolver;
let mut cx = syntax::ext::base::ExtCtxt::new(
&ps,
None,
&mut |ln| {
props.ignore =
- props.ignore || parse_name_directive(ln, "ignore-test") ||
- parse_name_directive(ln, &ignore_target(config)) ||
- parse_name_directive(ln, &ignore_architecture(config)) ||
- parse_name_directive(ln, &ignore_stage(config)) ||
- parse_name_directive(ln, &ignore_env(config)) ||
- (config.mode == common::Pretty && parse_name_directive(ln, "ignore-pretty")) ||
+ props.ignore || config.parse_name_directive(ln, "ignore-test") ||
+ config.parse_name_directive(ln, &ignore_target(config)) ||
+ config.parse_name_directive(ln, &ignore_architecture(config)) ||
+ config.parse_name_directive(ln, &ignore_stage(config)) ||
+ config.parse_name_directive(ln, &ignore_env(config)) ||
+ (config.mode == common::Pretty &&
+ config.parse_name_directive(ln, "ignore-pretty")) ||
(config.target != config.host &&
- parse_name_directive(ln, "ignore-cross-compile")) ||
+ config.parse_name_directive(ln, "ignore-cross-compile")) ||
ignore_gdb(config, ln) ||
ignore_lldb(config, ln) ||
ignore_llvm(config, ln);
- if let Some(s) = parse_aux_build(ln) {
+ if let Some(s) = config.parse_aux_build(ln) {
props.aux.push(s);
}
- props.should_fail = props.should_fail || parse_name_directive(ln, "should-fail");
+ props.should_fail = props.should_fail || config.parse_name_directive(ln, "should-fail");
});
return props;
}
if !line.contains("ignore-gdb-version") &&
- parse_name_directive(line, "ignore-gdb") {
+ config.parse_name_directive(line, "ignore-gdb") {
return true;
}
return false;
}
- if parse_name_directive(line, "ignore-lldb") {
+ if config.parse_name_directive(line, "ignore-lldb") {
return true;
}
}
}
- pub fn from_aux_file(&self, testfile: &Path, cfg: Option<&str>) -> Self {
+ pub fn from_aux_file(&self,
+ testfile: &Path,
+ cfg: Option<&str>,
+ config: &Config)
+ -> Self {
let mut props = TestProps::new();
// copy over select properties to the aux build:
props.incremental_dir = self.incremental_dir.clone();
- props.load_from(testfile, cfg);
+ props.load_from(testfile, cfg, config);
props
}
- pub fn from_file(testfile: &Path) -> Self {
+ pub fn from_file(testfile: &Path, config: &Config) -> Self {
let mut props = TestProps::new();
- props.load_from(testfile, None);
+ props.load_from(testfile, None, config);
props
}
/// tied to a particular revision `foo` (indicated by writing
/// `//[foo]`), then the property is ignored unless `cfg` is
/// `Some("foo")`.
- pub fn load_from(&mut self, testfile: &Path, cfg: Option<&str>) {
+ pub fn load_from(&mut self,
+ testfile: &Path,
+ cfg: Option<&str>,
+ config: &Config) {
iter_header(testfile,
cfg,
&mut |ln| {
- if let Some(ep) = parse_error_pattern(ln) {
+ if let Some(ep) = config.parse_error_pattern(ln) {
self.error_patterns.push(ep);
}
- if let Some(flags) = parse_compile_flags(ln) {
+ if let Some(flags) = config.parse_compile_flags(ln) {
self.compile_flags.extend(flags.split_whitespace()
.map(|s| s.to_owned()));
}
- if let Some(r) = parse_revisions(ln) {
+ if let Some(r) = config.parse_revisions(ln) {
self.revisions.extend(r);
}
if self.run_flags.is_none() {
- self.run_flags = parse_run_flags(ln);
+ self.run_flags = config.parse_run_flags(ln);
}
if self.pp_exact.is_none() {
- self.pp_exact = parse_pp_exact(ln, testfile);
+ self.pp_exact = config.parse_pp_exact(ln, testfile);
}
if !self.build_aux_docs {
- self.build_aux_docs = parse_build_aux_docs(ln);
+ self.build_aux_docs = config.parse_build_aux_docs(ln);
}
if !self.force_host {
- self.force_host = parse_force_host(ln);
+ self.force_host = config.parse_force_host(ln);
}
if !self.check_stdout {
- self.check_stdout = parse_check_stdout(ln);
+ self.check_stdout = config.parse_check_stdout(ln);
}
if !self.no_prefer_dynamic {
- self.no_prefer_dynamic = parse_no_prefer_dynamic(ln);
+ self.no_prefer_dynamic = config.parse_no_prefer_dynamic(ln);
}
if !self.pretty_expanded {
- self.pretty_expanded = parse_pretty_expanded(ln);
+ self.pretty_expanded = config.parse_pretty_expanded(ln);
}
- if let Some(m) = parse_pretty_mode(ln) {
+ if let Some(m) = config.parse_pretty_mode(ln) {
self.pretty_mode = m;
}
if !self.pretty_compare_only {
- self.pretty_compare_only = parse_pretty_compare_only(ln);
+ self.pretty_compare_only = config.parse_pretty_compare_only(ln);
}
- if let Some(ab) = parse_aux_build(ln) {
+ if let Some(ab) = config.parse_aux_build(ln) {
self.aux_builds.push(ab);
}
- if let Some(ee) = parse_env(ln, "exec-env") {
+ if let Some(ee) = config.parse_env(ln, "exec-env") {
self.exec_env.push(ee);
}
- if let Some(ee) = parse_env(ln, "rustc-env") {
+ if let Some(ee) = config.parse_env(ln, "rustc-env") {
self.rustc_env.push(ee);
}
- if let Some(cl) = parse_check_line(ln) {
+ if let Some(cl) = config.parse_check_line(ln) {
self.check_lines.push(cl);
}
- if let Some(of) = parse_forbid_output(ln) {
+ if let Some(of) = config.parse_forbid_output(ln) {
self.forbid_output.push(of);
}
if !self.must_compile_successfully {
- self.must_compile_successfully = parse_must_compile_successfully(ln);
+ self.must_compile_successfully = config.parse_must_compile_successfully(ln);
}
if !self.check_test_line_numbers_match {
- self.check_test_line_numbers_match = parse_check_test_line_numbers_match(ln);
+ self.check_test_line_numbers_match = config.parse_check_test_line_numbers_match(ln);
}
});
return;
}
-fn parse_error_pattern(line: &str) -> Option<String> {
- parse_name_value_directive(line, "error-pattern")
-}
+impl Config {
-fn parse_forbid_output(line: &str) -> Option<String> {
- parse_name_value_directive(line, "forbid-output")
-}
+ fn parse_error_pattern(&self, line: &str) -> Option<String> {
+ self.parse_name_value_directive(line, "error-pattern")
+ }
-fn parse_aux_build(line: &str) -> Option<String> {
- parse_name_value_directive(line, "aux-build")
-}
+ fn parse_forbid_output(&self, line: &str) -> Option<String> {
+ self.parse_name_value_directive(line, "forbid-output")
+ }
-fn parse_compile_flags(line: &str) -> Option<String> {
- parse_name_value_directive(line, "compile-flags")
-}
+ fn parse_aux_build(&self, line: &str) -> Option<String> {
+ self.parse_name_value_directive(line, "aux-build")
+ }
-fn parse_revisions(line: &str) -> Option<Vec<String>> {
- parse_name_value_directive(line, "revisions")
- .map(|r| r.split_whitespace().map(|t| t.to_string()).collect())
-}
+ fn parse_compile_flags(&self, line: &str) -> Option<String> {
+ self.parse_name_value_directive(line, "compile-flags")
+ }
-fn parse_run_flags(line: &str) -> Option<String> {
- parse_name_value_directive(line, "run-flags")
-}
+ fn parse_revisions(&self, line: &str) -> Option<Vec<String>> {
+ self.parse_name_value_directive(line, "revisions")
+ .map(|r| r.split_whitespace().map(|t| t.to_string()).collect())
+ }
-fn parse_check_line(line: &str) -> Option<String> {
- parse_name_value_directive(line, "check")
-}
+ fn parse_run_flags(&self, line: &str) -> Option<String> {
+ self.parse_name_value_directive(line, "run-flags")
+ }
-fn parse_force_host(line: &str) -> bool {
- parse_name_directive(line, "force-host")
-}
+ fn parse_check_line(&self, line: &str) -> Option<String> {
+ self.parse_name_value_directive(line, "check")
+ }
-fn parse_build_aux_docs(line: &str) -> bool {
- parse_name_directive(line, "build-aux-docs")
-}
+ fn parse_force_host(&self, line: &str) -> bool {
+ self.parse_name_directive(line, "force-host")
+ }
-fn parse_check_stdout(line: &str) -> bool {
- parse_name_directive(line, "check-stdout")
-}
+ fn parse_build_aux_docs(&self, line: &str) -> bool {
+ self.parse_name_directive(line, "build-aux-docs")
+ }
-fn parse_no_prefer_dynamic(line: &str) -> bool {
- parse_name_directive(line, "no-prefer-dynamic")
-}
+ fn parse_check_stdout(&self, line: &str) -> bool {
+ self.parse_name_directive(line, "check-stdout")
+ }
-fn parse_pretty_expanded(line: &str) -> bool {
- parse_name_directive(line, "pretty-expanded")
-}
+ fn parse_no_prefer_dynamic(&self, line: &str) -> bool {
+ self.parse_name_directive(line, "no-prefer-dynamic")
+ }
-fn parse_pretty_mode(line: &str) -> Option<String> {
- parse_name_value_directive(line, "pretty-mode")
-}
+ fn parse_pretty_expanded(&self, line: &str) -> bool {
+ self.parse_name_directive(line, "pretty-expanded")
+ }
-fn parse_pretty_compare_only(line: &str) -> bool {
- parse_name_directive(line, "pretty-compare-only")
-}
+ fn parse_pretty_mode(&self, line: &str) -> Option<String> {
+ self.parse_name_value_directive(line, "pretty-mode")
+ }
-fn parse_must_compile_successfully(line: &str) -> bool {
- parse_name_directive(line, "must-compile-successfully")
-}
+ fn parse_pretty_compare_only(&self, line: &str) -> bool {
+ self.parse_name_directive(line, "pretty-compare-only")
+ }
-fn parse_check_test_line_numbers_match(line: &str) -> bool {
- parse_name_directive(line, "check-test-line-numbers-match")
-}
+ fn parse_must_compile_successfully(&self, line: &str) -> bool {
+ self.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
- let mut strs: Vec<String> = nv.splitn(2, '=')
- .map(str::to_owned)
- .collect();
+ fn parse_check_test_line_numbers_match(&self, line: &str) -> bool {
+ self.parse_name_directive(line, "check-test-line-numbers-match")
+ }
- match strs.len() {
- 1 => (strs.pop().unwrap(), "".to_owned()),
- 2 => {
- let end = strs.pop().unwrap();
- (strs.pop().unwrap(), end)
+ fn parse_env(&self, line: &str, name: &str) -> Option<(String, String)> {
+ self.parse_name_value_directive(line, name).map(|nv| {
+ // nv is either FOO or FOO=BAR
+ let mut strs: Vec<String> = nv.splitn(2, '=')
+ .map(str::to_owned)
+ .collect();
+
+ match strs.len() {
+ 1 => (strs.pop().unwrap(), "".to_owned()),
+ 2 => {
+ let end = strs.pop().unwrap();
+ (strs.pop().unwrap(), end)
+ }
+ n => panic!("Expected 1 or 2 strings, not {}", n),
}
- n => panic!("Expected 1 or 2 strings, not {}", n),
- }
- })
-}
+ })
+ }
-fn parse_pp_exact(line: &str, testfile: &Path) -> Option<PathBuf> {
- if let Some(s) = parse_name_value_directive(line, "pp-exact") {
- Some(PathBuf::from(&s))
- } else {
- if parse_name_directive(line, "pp-exact") {
- testfile.file_name().map(PathBuf::from)
+ fn parse_pp_exact(&self, line: &str, testfile: &Path) -> Option<PathBuf> {
+ if let Some(s) = self.parse_name_value_directive(line, "pp-exact") {
+ Some(PathBuf::from(&s))
} else {
- None
+ if self.parse_name_directive(line, "pp-exact") {
+ testfile.file_name().map(PathBuf::from)
+ } else {
+ None
+ }
}
}
-}
-fn parse_name_directive(line: &str, directive: &str) -> bool {
- // This 'no-' rule is a quick hack to allow pretty-expanded and no-pretty-expanded to coexist
- line.contains(directive) && !line.contains(&("no-".to_owned() + directive))
-}
+ fn parse_name_directive(&self, line: &str, directive: &str) -> bool {
+ // This 'no-' rule is a quick hack to allow pretty-expanded and
+ // no-pretty-expanded to coexist
+ line.contains(directive) && !line.contains(&("no-".to_owned() + directive))
+ }
-pub fn parse_name_value_directive(line: &str, directive: &str) -> Option<String> {
- let keycolon = format!("{}:", directive);
- if let Some(colon) = line.find(&keycolon) {
- let value = line[(colon + keycolon.len())..line.len()].to_owned();
- debug!("{}: {}", directive, value);
- Some(value)
- } else {
- None
+ pub fn parse_name_value_directive(&self, line: &str, directive: &str) -> Option<String> {
+ let keycolon = format!("{}:", directive);
+ if let Some(colon) = line.find(&keycolon) {
+ let value = line[(colon + keycolon.len())..line.len()].to_owned();
+ debug!("{}: {}", directive, value);
+ Some(expand_variables(value, self))
+ } else {
+ None
+ }
}
}
let major: isize = version_string.parse().ok().expect(&error_string);
return major;
}
+
+fn expand_variables(mut value: String, config: &Config) -> String {
+ const CWD: &'static str = "{{cwd}}";
+ const SRC_BASE: &'static str = "{{src-base}}";
+ const BUILD_BASE: &'static str = "{{build-base}}";
+
+ if value.contains(CWD) {
+ let cwd = env::current_dir().unwrap();
+ value = value.replace(CWD, &cwd.to_string_lossy());
+ }
+
+ if value.contains(SRC_BASE) {
+ value = value.replace(SRC_BASE, &config.src_base.to_string_lossy());
+ }
+
+ if value.contains(BUILD_BASE) {
+ value = value.replace(BUILD_BASE, &config.build_base.to_string_lossy());
+ }
+
+ value
+}
use filetime::FileTime;
use json;
use header::TestProps;
-use header;
use procsrv;
use test::TestPaths;
use uidiff;
print!("\n\n");
}
debug!("running {:?}", testpaths.file.display());
- let base_props = TestProps::from_file(&testpaths.file);
+ let base_props = TestProps::from_file(&testpaths.file, &config);
let base_cx = TestCx { config: &config,
props: &base_props,
} else {
for revision in &base_props.revisions {
let mut revision_props = base_props.clone();
- revision_props.load_from(&testpaths.file, Some(&revision));
+ revision_props.load_from(&testpaths.file, Some(&revision), &config);
let rev_cx = TestCx {
config: &config,
props: &revision_props,
}
for &(ref command_directive, ref check_directive) in &directives {
- header::parse_name_value_directive(
+ self.config.parse_name_value_directive(
&line,
&command_directive).map(|cmd| {
commands.push(cmd)
});
- header::parse_name_value_directive(
+ self.config.parse_name_value_directive(
&line,
&check_directive).map(|cmd| {
check_lines.push(cmd)
if self.props.build_aux_docs {
for rel_ab in &self.props.aux_builds {
let aux_testpaths = self.compute_aux_test_paths(rel_ab);
- let aux_props = self.props.from_aux_file(&aux_testpaths.file, self.revision);
+ let aux_props = self.props.from_aux_file(&aux_testpaths.file,
+ self.revision,
+ self.config);
let aux_cx = TestCx {
config: self.config,
props: &aux_props,
for rel_ab in &self.props.aux_builds {
let aux_testpaths = self.compute_aux_test_paths(rel_ab);
- let aux_props = self.props.from_aux_file(&aux_testpaths.file, self.revision);
+ let aux_props = self.props.from_aux_file(&aux_testpaths.file,
+ self.revision,
+ self.config);
let mut crate_type = if aux_props.no_prefer_dynamic {
Vec::new()
} else {