1 //! This pass is only used for UNIT TESTS related to incremental
2 //! compilation. It tests whether a particular `.o` file will be re-used
3 //! from a previous compilation or whether it must be regenerated.
5 //! The user adds annotations to the crate of the following form:
8 //! #![rustc_partition_reused(module="spike", cfg="rpass2")]
9 //! #![rustc_partition_codegened(module="spike-x", cfg="rpass2")]
12 //! The first indicates (in the cfg `rpass2`) that `spike.o` will be
13 //! reused, the second that `spike-x.o` will be recreated. If these
14 //! annotations are inaccurate, errors are reported.
16 //! The reason that we use `cfg=...` and not `#[cfg_attr]` is so that
17 //! the HIR doesn't change as a result of the annotations, which might
18 //! perturb the reuse results.
20 //! `#![rustc_expected_cgu_reuse(module="spike", cfg="rpass2", kind="post-lto")]
21 //! allows for doing a more fine-grained check to see if pre- or post-lto data
25 use rustc_hir
::def_id
::LOCAL_CRATE
;
26 use rustc_middle
::mir
::mono
::CodegenUnitNameBuilder
;
27 use rustc_middle
::ty
::TyCtxt
;
28 use rustc_session
::cgu_reuse_tracker
::*;
29 use rustc_span
::symbol
::{sym, Symbol}
;
30 use std
::collections
::BTreeSet
;
32 pub fn assert_module_sources(tcx
: TyCtxt
<'_
>) {
33 tcx
.dep_graph
.with_ignore(|| {
34 if tcx
.sess
.opts
.incremental
.is_none() {
38 let available_cgus
= tcx
39 .collect_and_partition_mono_items(LOCAL_CRATE
)
42 .map(|cgu
| cgu
.name().to_string())
43 .collect
::<BTreeSet
<String
>>();
45 let ams
= AssertModuleSource { tcx, available_cgus }
;
47 for attr
in tcx
.hir().krate().item
.attrs
{
53 struct AssertModuleSource
<'tcx
> {
55 available_cgus
: BTreeSet
<String
>,
58 impl AssertModuleSource
<'tcx
> {
59 fn check_attr(&self, attr
: &ast
::Attribute
) {
60 let (expected_reuse
, comp_kind
) =
61 if self.tcx
.sess
.check_name(attr
, sym
::rustc_partition_reused
) {
62 (CguReuse
::PreLto
, ComparisonKind
::AtLeast
)
63 } else if self.tcx
.sess
.check_name(attr
, sym
::rustc_partition_codegened
) {
64 (CguReuse
::No
, ComparisonKind
::Exact
)
65 } else if self.tcx
.sess
.check_name(attr
, sym
::rustc_expected_cgu_reuse
) {
66 match self.field(attr
, sym
::kind
) {
67 sym
::no
=> (CguReuse
::No
, ComparisonKind
::Exact
),
68 sym
::pre_dash_lto
=> (CguReuse
::PreLto
, ComparisonKind
::Exact
),
69 sym
::post_dash_lto
=> (CguReuse
::PostLto
, ComparisonKind
::Exact
),
70 sym
::any
=> (CguReuse
::PreLto
, ComparisonKind
::AtLeast
),
72 self.tcx
.sess
.span_fatal(
74 &format
!("unknown cgu-reuse-kind `{}` specified", other
),
82 if !self.tcx
.sess
.opts
.debugging_opts
.query_dep_graph
{
83 self.tcx
.sess
.span_fatal(
85 "found CGU-reuse attribute but `-Zquery-dep-graph` was not specified",
89 if !self.check_config(attr
) {
90 debug
!("check_attr: config does not match, ignoring attr");
94 let user_path
= self.field(attr
, sym
::module
).to_string();
95 let crate_name
= self.tcx
.crate_name(LOCAL_CRATE
).to_string();
97 if !user_path
.starts_with(&crate_name
) {
99 "Found malformed codegen unit name `{}`. \
100 Codegen units names must always start with the name of the \
101 crate (`{}` in this case).",
102 user_path
, crate_name
104 self.tcx
.sess
.span_fatal(attr
.span
, &msg
);
107 // Split of the "special suffix" if there is one.
108 let (user_path
, cgu_special_suffix
) = if let Some(index
) = user_path
.rfind('
.'
) {
109 (&user_path
[..index
], Some(&user_path
[index
+ 1..]))
111 (&user_path
[..], None
)
114 let mut cgu_path_components
= user_path
.split('
-'
).collect
::<Vec
<_
>>();
116 // Remove the crate name
117 assert_eq
!(cgu_path_components
.remove(0), crate_name
);
119 let cgu_name_builder
= &mut CodegenUnitNameBuilder
::new(self.tcx
);
121 cgu_name_builder
.build_cgu_name(LOCAL_CRATE
, cgu_path_components
, cgu_special_suffix
);
123 debug
!("mapping '{}' to cgu name '{}'", self.field(attr
, sym
::module
), cgu_name
);
125 if !self.available_cgus
.contains(&*cgu_name
.as_str()) {
126 self.tcx
.sess
.span_err(
129 "no module named `{}` (mangled: {}). Available modules: {}",
134 .map(|cgu
| cgu
.to_string())
141 self.tcx
.sess
.cgu_reuse_tracker
.set_expectation(
150 fn field(&self, attr
: &ast
::Attribute
, name
: Symbol
) -> Symbol
{
151 for item
in attr
.meta_item_list().unwrap_or_else(Vec
::new
) {
152 if item
.has_name(name
) {
153 if let Some(value
) = item
.value_str() {
156 self.tcx
.sess
.span_fatal(
158 &format
!("associated value expected for `{}`", name
),
164 self.tcx
.sess
.span_fatal(attr
.span
, &format
!("no field `{}`", name
));
167 /// Scan for a `cfg="foo"` attribute and check whether we have a
168 /// cfg flag called `foo`.
169 fn check_config(&self, attr
: &ast
::Attribute
) -> bool
{
170 let config
= &self.tcx
.sess
.parse_sess
.config
;
171 let value
= self.field(attr
, sym
::cfg
);
172 debug
!("check_config(config={:?}, value={:?})", config
, value
);
173 if config
.iter().any(|&(name
, _
)| name
== value
) {
174 debug
!("check_config: matched");
177 debug
!("check_config: no match found");