]> git.lizzy.rs Git - rust.git/blob - src/librustc/ich/hcx.rs
Fix invalid associated type rendering in rustdoc
[rust.git] / src / librustc / ich / hcx.rs
1 // Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use hir;
12 use hir::def_id::DefId;
13 use ich::{self, CachingCodemapView};
14 use session::config::DebugInfoLevel::NoDebugInfo;
15 use ty;
16
17 use std::hash as std_hash;
18
19 use syntax::ast;
20 use syntax::attr;
21 use syntax::ext::hygiene::SyntaxContext;
22 use syntax::symbol::Symbol;
23 use syntax_pos::Span;
24
25 use rustc_data_structures::stable_hasher::{HashStable, StableHasher,
26                                            StableHasherResult};
27 use rustc_data_structures::accumulate_vec::AccumulateVec;
28
29 /// This is the context state available during incr. comp. hashing. It contains
30 /// enough information to transform DefIds and HirIds into stable DefPaths (i.e.
31 /// a reference to the TyCtxt) and it holds a few caches for speeding up various
32 /// things (e.g. each DefId/DefPath is only hashed once).
33 pub struct StableHashingContext<'a, 'tcx: 'a> {
34     tcx: ty::TyCtxt<'a, 'tcx, 'tcx>,
35     codemap: CachingCodemapView<'tcx>,
36     hash_spans: bool,
37     hash_bodies: bool,
38     overflow_checks_enabled: bool,
39     node_id_hashing_mode: NodeIdHashingMode,
40     // A sorted array of symbol keys for fast lookup.
41     ignored_attr_names: Vec<Symbol>,
42 }
43
44 #[derive(PartialEq, Eq, Clone, Copy)]
45 pub enum NodeIdHashingMode {
46     Ignore,
47     HashDefPath,
48     HashTraitsInScope,
49 }
50
51 impl<'a, 'tcx: 'a> StableHashingContext<'a, 'tcx> {
52
53     pub fn new(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>) -> Self {
54         let hash_spans_initial = tcx.sess.opts.debuginfo != NoDebugInfo;
55         let check_overflow_initial = tcx.sess.overflow_checks();
56
57         let mut ignored_attr_names: Vec<_> = ich::IGNORED_ATTRIBUTES
58             .iter()
59             .map(|&s| Symbol::intern(s))
60             .collect();
61
62         ignored_attr_names.sort();
63
64         StableHashingContext {
65             tcx: tcx,
66             codemap: CachingCodemapView::new(tcx),
67             hash_spans: hash_spans_initial,
68             hash_bodies: true,
69             overflow_checks_enabled: check_overflow_initial,
70             node_id_hashing_mode: NodeIdHashingMode::HashDefPath,
71             ignored_attr_names: ignored_attr_names,
72         }
73     }
74
75     #[inline]
76     pub fn while_hashing_hir_bodies<F: FnOnce(&mut Self)>(&mut self,
77                                                           hash_bodies: bool,
78                                                           f: F) {
79         let prev_hash_bodies = self.hash_bodies;
80         self.hash_bodies = hash_bodies;
81         f(self);
82         self.hash_bodies = prev_hash_bodies;
83     }
84
85     #[inline]
86     pub fn while_hashing_spans<F: FnOnce(&mut Self)>(&mut self,
87                                                      hash_spans: bool,
88                                                      f: F) {
89         let prev_hash_spans = self.hash_spans;
90         self.hash_spans = hash_spans;
91         f(self);
92         self.hash_spans = prev_hash_spans;
93     }
94
95     #[inline]
96     pub fn with_node_id_hashing_mode<F: FnOnce(&mut Self)>(&mut self,
97                                                            mode: NodeIdHashingMode,
98                                                            f: F) {
99         let prev = self.node_id_hashing_mode;
100         self.node_id_hashing_mode = mode;
101         f(self);
102         self.node_id_hashing_mode = prev;
103     }
104
105     #[inline]
106     pub fn tcx(&self) -> ty::TyCtxt<'a, 'tcx, 'tcx> {
107         self.tcx
108     }
109
110     #[inline]
111     pub fn def_path_hash(&mut self, def_id: DefId) -> u64 {
112         self.tcx.def_path_hash(def_id)
113     }
114
115     #[inline]
116     pub fn hash_spans(&self) -> bool {
117         self.hash_spans
118     }
119
120     #[inline]
121     pub fn hash_bodies(&self) -> bool {
122         self.hash_bodies
123     }
124
125     #[inline]
126     pub fn codemap(&mut self) -> &mut CachingCodemapView<'tcx> {
127         &mut self.codemap
128     }
129
130     #[inline]
131     pub fn is_ignored_attr(&self, name: Symbol) -> bool {
132         self.ignored_attr_names.binary_search(&name).is_ok()
133     }
134
135     pub fn hash_hir_item_like<F: FnOnce(&mut Self)>(&mut self,
136                                                     item_attrs: &[ast::Attribute],
137                                                     f: F) {
138         let prev_overflow_checks = self.overflow_checks_enabled;
139         if attr::contains_name(item_attrs, "rustc_inherit_overflow_checks") {
140             self.overflow_checks_enabled = true;
141         }
142         let prev_hash_node_ids = self.node_id_hashing_mode;
143         self.node_id_hashing_mode = NodeIdHashingMode::Ignore;
144
145         f(self);
146
147         self.node_id_hashing_mode = prev_hash_node_ids;
148         self.overflow_checks_enabled = prev_overflow_checks;
149     }
150
151     #[inline]
152     pub fn binop_can_panic_at_runtime(&self, binop: hir::BinOp_) -> bool
153     {
154         match binop {
155             hir::BiAdd |
156             hir::BiSub |
157             hir::BiMul => self.overflow_checks_enabled,
158
159             hir::BiDiv |
160             hir::BiRem => true,
161
162             hir::BiAnd |
163             hir::BiOr |
164             hir::BiBitXor |
165             hir::BiBitAnd |
166             hir::BiBitOr |
167             hir::BiShl |
168             hir::BiShr |
169             hir::BiEq |
170             hir::BiLt |
171             hir::BiLe |
172             hir::BiNe |
173             hir::BiGe |
174             hir::BiGt => false
175         }
176     }
177
178     #[inline]
179     pub fn unop_can_panic_at_runtime(&self, unop: hir::UnOp) -> bool
180     {
181         match unop {
182             hir::UnDeref |
183             hir::UnNot => false,
184             hir::UnNeg => self.overflow_checks_enabled,
185         }
186     }
187 }
188
189
190 impl<'a, 'tcx> HashStable<StableHashingContext<'a, 'tcx>> for ast::NodeId {
191     fn hash_stable<W: StableHasherResult>(&self,
192                                           hcx: &mut StableHashingContext<'a, 'tcx>,
193                                           hasher: &mut StableHasher<W>) {
194         match hcx.node_id_hashing_mode {
195             NodeIdHashingMode::Ignore => {
196                 // Most NodeIds in the HIR can be ignored, but if there is a
197                 // corresponding entry in the `trait_map` we need to hash that.
198                 // Make sure we don't ignore too much by checking that there is
199                 // no entry in a debug_assert!().
200                 debug_assert!(hcx.tcx.trait_map.get(self).is_none());
201             }
202             NodeIdHashingMode::HashDefPath => {
203                 hcx.tcx.hir.definitions().node_to_hir_id(*self).hash_stable(hcx, hasher);
204             }
205             NodeIdHashingMode::HashTraitsInScope => {
206                 if let Some(traits) = hcx.tcx.trait_map.get(self) {
207                     // The ordering of the candidates is not fixed. So we hash
208                     // the def-ids and then sort them and hash the collection.
209                     let mut candidates: AccumulateVec<[_; 8]> =
210                         traits.iter()
211                               .map(|&hir::TraitCandidate { def_id, import_id: _ }| {
212                                   hcx.def_path_hash(def_id)
213                               })
214                               .collect();
215                     if traits.len() > 1 {
216                         candidates.sort();
217                     }
218                     candidates.hash_stable(hcx, hasher);
219                 }
220             }
221         }
222     }
223 }
224
225 impl<'a, 'tcx> HashStable<StableHashingContext<'a, 'tcx>> for Span {
226
227     // Hash a span in a stable way. We can't directly hash the span's BytePos
228     // fields (that would be similar to hashing pointers, since those are just
229     // offsets into the CodeMap). Instead, we hash the (file name, line, column)
230     // triple, which stays the same even if the containing FileMap has moved
231     // within the CodeMap.
232     // Also note that we are hashing byte offsets for the column, not unicode
233     // codepoint offsets. For the purpose of the hash that's sufficient.
234     // Also, hashing filenames is expensive so we avoid doing it twice when the
235     // span starts and ends in the same file, which is almost always the case.
236     fn hash_stable<W: StableHasherResult>(&self,
237                                           hcx: &mut StableHashingContext<'a, 'tcx>,
238                                           hasher: &mut StableHasher<W>) {
239         use syntax_pos::Pos;
240
241         if !hcx.hash_spans {
242             return
243         }
244
245         // If this is not an empty or invalid span, we want to hash the last
246         // position that belongs to it, as opposed to hashing the first
247         // position past it.
248         let span_hi = if self.hi > self.lo {
249             // We might end up in the middle of a multibyte character here,
250             // but that's OK, since we are not trying to decode anything at
251             // this position.
252             self.hi - ::syntax_pos::BytePos(1)
253         } else {
254             self.hi
255         };
256
257         {
258             let loc1 = hcx.codemap().byte_pos_to_line_and_col(self.lo);
259             let loc1 = loc1.as_ref()
260                            .map(|&(ref fm, line, col)| (&fm.name[..], line, col.to_usize()))
261                            .unwrap_or(("???", 0, 0));
262
263             let loc2 = hcx.codemap().byte_pos_to_line_and_col(span_hi);
264             let loc2 = loc2.as_ref()
265                            .map(|&(ref fm, line, col)| (&fm.name[..], line, col.to_usize()))
266                            .unwrap_or(("???", 0, 0));
267
268             if loc1.0 == loc2.0 {
269                 std_hash::Hash::hash(&0u8, hasher);
270
271                 std_hash::Hash::hash(loc1.0, hasher);
272                 std_hash::Hash::hash(&loc1.1, hasher);
273                 std_hash::Hash::hash(&loc1.2, hasher);
274
275                 // Do not hash the file name twice
276                 std_hash::Hash::hash(&loc2.1, hasher);
277                 std_hash::Hash::hash(&loc2.2, hasher);
278             } else {
279                 std_hash::Hash::hash(&1u8, hasher);
280
281                 std_hash::Hash::hash(loc1.0, hasher);
282                 std_hash::Hash::hash(&loc1.1, hasher);
283                 std_hash::Hash::hash(&loc1.2, hasher);
284
285                 std_hash::Hash::hash(loc2.0, hasher);
286                 std_hash::Hash::hash(&loc2.1, hasher);
287                 std_hash::Hash::hash(&loc2.2, hasher);
288             }
289         }
290
291         if self.ctxt == SyntaxContext::empty() {
292             0u8.hash_stable(hcx, hasher);
293         } else {
294             1u8.hash_stable(hcx, hasher);
295             self.source_callsite().hash_stable(hcx, hasher);
296         }
297     }
298 }