]>
Commit | Line | Data |
---|---|---|
dfeec247 XL |
1 | use std::cmp; |
2 | ||
136023e0 | 3 | use rustc_data_structures::fx::FxHashMap; |
2b03887a FG |
4 | use rustc_data_structures::sorted_map::SortedMap; |
5 | use rustc_errors::{Diagnostic, DiagnosticBuilder, DiagnosticId, DiagnosticMessage, MultiSpan}; | |
6 | use rustc_hir::{HirId, ItemLocalId}; | |
fc512014 XL |
7 | use rustc_session::lint::{ |
8 | builtin::{self, FORBIDDEN_LINT_GROUPS}, | |
2b03887a | 9 | FutureIncompatibilityReason, Level, Lint, LintId, |
fc512014 | 10 | }; |
5e7ed085 | 11 | use rustc_session::Session; |
dfeec247 | 12 | use rustc_span::hygiene::MacroKind; |
04454e1e | 13 | use rustc_span::source_map::{DesugaringKind, ExpnKind}; |
fc512014 | 14 | use rustc_span::{symbol, Span, Symbol, DUMMY_SP}; |
dfeec247 | 15 | |
2b03887a FG |
16 | use crate::ty::TyCtxt; |
17 | ||
dfeec247 | 18 | /// How a lint level was set. |
fc512014 XL |
19 | #[derive(Clone, Copy, PartialEq, Eq, HashStable, Debug)] |
20 | pub enum LintLevelSource { | |
dfeec247 XL |
21 | /// Lint is at the default level as declared |
22 | /// in rustc or a plugin. | |
23 | Default, | |
24 | ||
25 | /// Lint level was set by an attribute. | |
2b03887a FG |
26 | Node { |
27 | name: Symbol, | |
28 | span: Span, | |
29 | /// RFC 2383 reason | |
30 | reason: Option<Symbol>, | |
31 | }, | |
dfeec247 XL |
32 | |
33 | /// Lint level was set by a command-line flag. | |
fc512014 XL |
34 | /// The provided `Level` is the level specified on the command line. |
35 | /// (The actual level may be lower due to `--cap-lints`.) | |
29967ef6 | 36 | CommandLine(Symbol, Level), |
dfeec247 XL |
37 | } |
38 | ||
fc512014 XL |
39 | impl LintLevelSource { |
40 | pub fn name(&self) -> Symbol { | |
41 | match *self { | |
42 | LintLevelSource::Default => symbol::kw::Default, | |
2b03887a | 43 | LintLevelSource::Node { name, .. } => name, |
fc512014 XL |
44 | LintLevelSource::CommandLine(name, _) => name, |
45 | } | |
46 | } | |
47 | ||
48 | pub fn span(&self) -> Span { | |
49 | match *self { | |
50 | LintLevelSource::Default => DUMMY_SP, | |
2b03887a | 51 | LintLevelSource::Node { span, .. } => span, |
fc512014 XL |
52 | LintLevelSource::CommandLine(_, _) => DUMMY_SP, |
53 | } | |
54 | } | |
55 | } | |
56 | ||
57 | /// A tuple of a lint level and its source. | |
5869c6ff | 58 | pub type LevelAndSource = (Level, LintLevelSource); |
dfeec247 | 59 | |
2b03887a FG |
60 | /// Return type for the `shallow_lint_levels_on` query. |
61 | /// | |
62 | /// This map represents the set of allowed lints and allowance levels given | |
63 | /// by the attributes for *a single HirId*. | |
64 | #[derive(Default, Debug, HashStable)] | |
65 | pub struct ShallowLintLevelMap { | |
66 | pub specs: SortedMap<ItemLocalId, FxHashMap<LintId, LevelAndSource>>, | |
dfeec247 XL |
67 | } |
68 | ||
2b03887a FG |
69 | /// From an initial level and source, verify the effect of special annotations: |
70 | /// `warnings` lint level and lint caps. | |
71 | /// | |
72 | /// The return of this function is suitable for diagnostics. | |
73 | pub fn reveal_actual_level( | |
74 | level: Option<Level>, | |
75 | src: &mut LintLevelSource, | |
76 | sess: &Session, | |
77 | lint: LintId, | |
78 | probe_for_lint_level: impl FnOnce(LintId) -> (Option<Level>, LintLevelSource), | |
79 | ) -> Level { | |
80 | // If `level` is none then we actually assume the default level for this lint. | |
81 | let mut level = level.unwrap_or_else(|| lint.lint.default_level(sess.edition())); | |
82 | ||
83 | // If we're about to issue a warning, check at the last minute for any | |
84 | // directives against the warnings "lint". If, for example, there's an | |
85 | // `allow(warnings)` in scope then we want to respect that instead. | |
86 | // | |
87 | // We exempt `FORBIDDEN_LINT_GROUPS` from this because it specifically | |
88 | // triggers in cases (like #80988) where you have `forbid(warnings)`, | |
89 | // and so if we turned that into an error, it'd defeat the purpose of the | |
90 | // future compatibility warning. | |
91 | if level == Level::Warn && lint != LintId::of(FORBIDDEN_LINT_GROUPS) { | |
92 | let (warnings_level, warnings_src) = probe_for_lint_level(LintId::of(builtin::WARNINGS)); | |
93 | if let Some(configured_warning_level) = warnings_level { | |
94 | if configured_warning_level != Level::Warn { | |
95 | level = configured_warning_level; | |
96 | *src = warnings_src; | |
dfeec247 XL |
97 | } |
98 | } | |
2b03887a | 99 | } |
dfeec247 | 100 | |
2b03887a FG |
101 | // Ensure that we never exceed the `--cap-lints` argument unless the source is a --force-warn |
102 | level = if let LintLevelSource::CommandLine(_, Level::ForceWarn(_)) = src { | |
103 | level | |
104 | } else { | |
105 | cmp::min(level, sess.opts.lint_cap.unwrap_or(Level::Forbid)) | |
106 | }; | |
dfeec247 | 107 | |
2b03887a FG |
108 | if let Some(driver_level) = sess.driver_lint_caps.get(&lint) { |
109 | // Ensure that we never exceed driver level. | |
110 | level = cmp::min(*driver_level, level); | |
dfeec247 XL |
111 | } |
112 | ||
2b03887a FG |
113 | level |
114 | } | |
115 | ||
116 | impl ShallowLintLevelMap { | |
117 | /// Perform a deep probe in the HIR tree looking for the actual level for the lint. | |
118 | /// This lint level is not usable for diagnostics, it needs to be corrected by | |
119 | /// `reveal_actual_level` beforehand. | |
120 | #[instrument(level = "trace", skip(self, tcx), ret)] | |
121 | fn probe_for_lint_level( | |
dfeec247 | 122 | &self, |
2b03887a | 123 | tcx: TyCtxt<'_>, |
dfeec247 | 124 | id: LintId, |
2b03887a | 125 | start: HirId, |
fc512014 | 126 | ) -> (Option<Level>, LintLevelSource) { |
2b03887a FG |
127 | if let Some(map) = self.specs.get(&start.local_id) |
128 | && let Some(&(level, src)) = map.get(&id) | |
129 | { | |
130 | return (Some(level), src); | |
dfeec247 | 131 | } |
2b03887a FG |
132 | |
133 | let mut owner = start.owner; | |
134 | let mut specs = &self.specs; | |
135 | ||
136 | for parent in tcx.hir().parent_id_iter(start) { | |
137 | if parent.owner != owner { | |
138 | owner = parent.owner; | |
139 | specs = &tcx.shallow_lint_levels_on(owner).specs; | |
136023e0 | 140 | } |
2b03887a FG |
141 | if let Some(map) = specs.get(&parent.local_id) |
142 | && let Some(&(level, src)) = map.get(&id) | |
143 | { | |
144 | return (Some(level), src); | |
dfeec247 XL |
145 | } |
146 | } | |
dfeec247 | 147 | |
2b03887a FG |
148 | (None, LintLevelSource::Default) |
149 | } | |
dfeec247 | 150 | |
2b03887a FG |
151 | /// Fetch and return the user-visible lint level for the given lint at the given HirId. |
152 | #[instrument(level = "trace", skip(self, tcx), ret)] | |
153 | pub fn lint_level_id_at_node( | |
dfeec247 | 154 | &self, |
2b03887a FG |
155 | tcx: TyCtxt<'_>, |
156 | lint: LintId, | |
157 | cur: HirId, | |
158 | ) -> (Level, LintLevelSource) { | |
159 | let (level, mut src) = self.probe_for_lint_level(tcx, lint, cur); | |
160 | let level = reveal_actual_level(level, &mut src, tcx.sess, lint, |lint| { | |
161 | self.probe_for_lint_level(tcx, lint, cur) | |
162 | }); | |
163 | (level, src) | |
dfeec247 XL |
164 | } |
165 | } | |
166 | ||
2b03887a FG |
167 | impl TyCtxt<'_> { |
168 | /// Fetch and return the user-visible lint level for the given lint at the given HirId. | |
169 | pub fn lint_level_at_node(self, lint: &'static Lint, id: HirId) -> (Level, LintLevelSource) { | |
170 | self.shallow_lint_levels_on(id.owner).lint_level_id_at_node(self, LintId::of(lint), id) | |
171 | } | |
dfeec247 | 172 | |
2b03887a FG |
173 | /// Walks upwards from `id` to find a node which might change lint levels with attributes. |
174 | /// It stops at `bound` and just returns it if reached. | |
175 | pub fn maybe_lint_level_root_bounded(self, mut id: HirId, bound: HirId) -> HirId { | |
176 | let hir = self.hir(); | |
177 | loop { | |
178 | if id == bound { | |
179 | return bound; | |
180 | } | |
dfeec247 | 181 | |
2b03887a FG |
182 | if hir.attrs(id).iter().any(|attr| Level::from_attr(attr).is_some()) { |
183 | return id; | |
184 | } | |
185 | let next = hir.get_parent_node(id); | |
186 | if next == id { | |
187 | bug!("lint traversal reached the root of the crate"); | |
188 | } | |
189 | id = next; | |
190 | } | |
dfeec247 XL |
191 | } |
192 | } | |
193 | ||
5e7ed085 FG |
194 | /// This struct represents a lint expectation and holds all required information |
195 | /// to emit the `unfulfilled_lint_expectations` lint if it is unfulfilled after | |
196 | /// the `LateLintPass` has completed. | |
197 | #[derive(Clone, Debug, HashStable)] | |
198 | pub struct LintExpectation { | |
199 | /// The reason for this expectation that can optionally be added as part of | |
200 | /// the attribute. It will be displayed as part of the lint message. | |
201 | pub reason: Option<Symbol>, | |
202 | /// The [`Span`] of the attribute that this expectation originated from. | |
203 | pub emission_span: Span, | |
204 | /// Lint messages for the `unfulfilled_lint_expectations` lint will be | |
205 | /// adjusted to include an additional note. Therefore, we have to track if | |
206 | /// the expectation is for the lint. | |
207 | pub is_unfulfilled_lint_expectations: bool, | |
04454e1e FG |
208 | /// This will hold the name of the tool that this lint belongs to. For |
209 | /// the lint `clippy::some_lint` the tool would be `clippy`, the same | |
210 | /// goes for `rustdoc`. This will be `None` for rustc lints | |
211 | pub lint_tool: Option<Symbol>, | |
5e7ed085 | 212 | } |
74b04a01 | 213 | |
5e7ed085 FG |
214 | impl LintExpectation { |
215 | pub fn new( | |
216 | reason: Option<Symbol>, | |
217 | emission_span: Span, | |
218 | is_unfulfilled_lint_expectations: bool, | |
04454e1e | 219 | lint_tool: Option<Symbol>, |
5e7ed085 | 220 | ) -> Self { |
04454e1e | 221 | Self { reason, emission_span, is_unfulfilled_lint_expectations, lint_tool } |
5e7ed085 FG |
222 | } |
223 | } | |
224 | ||
5e7ed085 FG |
225 | pub fn explain_lint_level_source( |
226 | lint: &'static Lint, | |
227 | level: Level, | |
228 | src: LintLevelSource, | |
229 | err: &mut Diagnostic, | |
230 | ) { | |
231 | let name = lint.name_lower(); | |
232 | match src { | |
233 | LintLevelSource::Default => { | |
234 | err.note_once(&format!("`#[{}({})]` on by default", level.as_str(), name)); | |
235 | } | |
236 | LintLevelSource::CommandLine(lint_flag_val, orig_level) => { | |
237 | let flag = match orig_level { | |
238 | Level::Warn => "-W", | |
239 | Level::Deny => "-D", | |
240 | Level::Forbid => "-F", | |
241 | Level::Allow => "-A", | |
923072b8 | 242 | Level::ForceWarn(_) => "--force-warn", |
5e7ed085 FG |
243 | Level::Expect(_) => { |
244 | unreachable!("the expect level does not have a commandline flag") | |
245 | } | |
246 | }; | |
247 | let hyphen_case_lint_name = name.replace('_', "-"); | |
248 | if lint_flag_val.as_str() == name { | |
249 | err.note_once(&format!( | |
250 | "requested on the command line with `{} {}`", | |
251 | flag, hyphen_case_lint_name | |
252 | )); | |
253 | } else { | |
254 | let hyphen_case_flag_val = lint_flag_val.as_str().replace('_', "-"); | |
255 | err.note_once(&format!( | |
256 | "`{} {}` implied by `{} {}`", | |
257 | flag, hyphen_case_lint_name, flag, hyphen_case_flag_val | |
258 | )); | |
259 | } | |
260 | } | |
2b03887a | 261 | LintLevelSource::Node { name: lint_attr_name, span, reason, .. } => { |
5e7ed085 FG |
262 | if let Some(rationale) = reason { |
263 | err.note(rationale.as_str()); | |
264 | } | |
2b03887a | 265 | err.span_note_once(span, "the lint level is defined here"); |
5e7ed085 FG |
266 | if lint_attr_name.as_str() != name { |
267 | let level_str = level.as_str(); | |
268 | err.note_once(&format!( | |
269 | "`#[{}({})]` implied by `#[{}({})]`", | |
270 | level_str, name, level_str, lint_attr_name | |
271 | )); | |
272 | } | |
273 | } | |
274 | } | |
275 | } | |
276 | ||
2b03887a FG |
277 | /// The innermost function for emitting lints. |
278 | /// | |
279 | /// If you are loocking to implement a lint, look for higher level functions, | |
280 | /// for example: | |
281 | /// - [`TyCtxt::emit_spanned_lint`] | |
282 | /// - [`TyCtxt::struct_span_lint_hir`] | |
283 | /// - [`TyCtxt::emit_lint`] | |
284 | /// - [`TyCtxt::struct_lint_node`] | |
285 | /// - `LintContext::lookup` | |
286 | /// | |
287 | /// ## `decorate` signature | |
288 | /// | |
289 | /// The return value of `decorate` is ignored by this function. So what is the | |
290 | /// point of returning `&'b mut DiagnosticBuilder<'a, ()>`? | |
291 | /// | |
292 | /// There are 2 reasons for this signature. | |
293 | /// | |
294 | /// First of all, it prevents accidental use of `.emit()` -- it's clear that the | |
295 | /// builder will be later used and shouldn't be emitted right away (this is | |
296 | /// especially important because the old API expected you to call `.emit()` in | |
297 | /// the closure). | |
298 | /// | |
299 | /// Second of all, it makes the most common case of adding just a single label | |
300 | /// /suggestion much nicer, since [`DiagnosticBuilder`] methods return | |
301 | /// `&mut DiagnosticBuilder`, you can just chain methods, without needed | |
302 | /// awkward `{ ...; }`: | |
303 | /// ```ignore pseudo-code | |
304 | /// struct_lint_level( | |
305 | /// ..., | |
306 | /// |lint| lint.span_label(sp, "lbl") | |
307 | /// // ^^^^^^^^^^^^^^^^^^^^^ returns `&mut DiagnosticBuilder` by default | |
308 | /// ) | |
309 | /// ``` | |
310 | pub fn struct_lint_level( | |
311 | sess: &Session, | |
dfeec247 XL |
312 | lint: &'static Lint, |
313 | level: Level, | |
fc512014 | 314 | src: LintLevelSource, |
dfeec247 | 315 | span: Option<MultiSpan>, |
2b03887a FG |
316 | msg: impl Into<DiagnosticMessage>, |
317 | decorate: impl for<'a, 'b> FnOnce( | |
318 | &'b mut DiagnosticBuilder<'a, ()>, | |
319 | ) -> &'b mut DiagnosticBuilder<'a, ()>, | |
74b04a01 XL |
320 | ) { |
321 | // Avoid codegen bloat from monomorphization by immediately doing dyn dispatch of `decorate` to | |
322 | // the "real" work. | |
2b03887a FG |
323 | fn struct_lint_level_impl( |
324 | sess: &Session, | |
74b04a01 XL |
325 | lint: &'static Lint, |
326 | level: Level, | |
fc512014 | 327 | src: LintLevelSource, |
74b04a01 | 328 | span: Option<MultiSpan>, |
2b03887a FG |
329 | msg: impl Into<DiagnosticMessage>, |
330 | decorate: Box< | |
331 | dyn '_ | |
332 | + for<'a, 'b> FnOnce( | |
333 | &'b mut DiagnosticBuilder<'a, ()>, | |
334 | ) -> &'b mut DiagnosticBuilder<'a, ()>, | |
335 | >, | |
74b04a01 | 336 | ) { |
29967ef6 | 337 | // Check for future incompatibility lints and issue a stronger warning. |
29967ef6 XL |
338 | let future_incompatible = lint.future_incompatible; |
339 | ||
136023e0 XL |
340 | let has_future_breakage = future_incompatible.map_or( |
341 | // Default allow lints trigger too often for testing. | |
064997fb | 342 | sess.opts.unstable_opts.future_incompat_test && lint.default_level != Level::Allow, |
136023e0 XL |
343 | |incompat| { |
344 | matches!(incompat.reason, FutureIncompatibilityReason::FutureReleaseErrorReportNow) | |
345 | }, | |
346 | ); | |
29967ef6 | 347 | |
74b04a01 | 348 | let mut err = match (level, span) { |
29967ef6 XL |
349 | (Level::Allow, span) => { |
350 | if has_future_breakage { | |
351 | if let Some(span) = span { | |
352 | sess.struct_span_allow(span, "") | |
353 | } else { | |
354 | sess.struct_allow("") | |
355 | } | |
356 | } else { | |
357 | return; | |
358 | } | |
74b04a01 | 359 | } |
5e7ed085 FG |
360 | (Level::Expect(expect_id), _) => { |
361 | // This case is special as we actually allow the lint itself in this context, but | |
362 | // we can't return early like in the case for `Level::Allow` because we still | |
363 | // need the lint diagnostic to be emitted to `rustc_error::HandlerInner`. | |
364 | // | |
365 | // We can also not mark the lint expectation as fulfilled here right away, as it | |
366 | // can still be cancelled in the decorate function. All of this means that we simply | |
367 | // create a `DiagnosticBuilder` and continue as we would for warnings. | |
368 | sess.struct_expect("", expect_id) | |
369 | } | |
923072b8 FG |
370 | (Level::ForceWarn(Some(expect_id)), Some(span)) => { |
371 | sess.struct_span_warn_with_expectation(span, "", expect_id) | |
372 | } | |
373 | (Level::ForceWarn(Some(expect_id)), None) => { | |
374 | sess.struct_warn_with_expectation("", expect_id) | |
375 | } | |
376 | (Level::Warn | Level::ForceWarn(None), Some(span)) => sess.struct_span_warn(span, ""), | |
377 | (Level::Warn | Level::ForceWarn(None), None) => sess.struct_warn(""), | |
3c0e092e XL |
378 | (Level::Deny | Level::Forbid, Some(span)) => { |
379 | let mut builder = sess.diagnostic().struct_err_lint(""); | |
380 | builder.set_span(span); | |
381 | builder | |
382 | } | |
383 | (Level::Deny | Level::Forbid, None) => sess.diagnostic().struct_err_lint(""), | |
74b04a01 | 384 | }; |
dfeec247 | 385 | |
2b03887a FG |
386 | err.set_is_lint(); |
387 | ||
74b04a01 XL |
388 | // If this code originates in a foreign macro, aka something that this crate |
389 | // did not itself author, then it's likely that there's nothing this crate | |
390 | // can do about it. We probably want to skip the lint entirely. | |
391 | if err.span.primary_spans().iter().any(|s| in_external_macro(sess, *s)) { | |
392 | // Any suggestions made here are likely to be incorrect, so anything we | |
393 | // emit shouldn't be automatically fixed by rustfix. | |
5099ac24 | 394 | err.disable_suggestions(); |
74b04a01 | 395 | |
17df50a5 XL |
396 | // If this is a future incompatible that is not an edition fixing lint |
397 | // it'll become a hard error, so we have to emit *something*. Also, | |
398 | // if this lint occurs in the expansion of a macro from an external crate, | |
399 | // allow individual lints to opt-out from being reported. | |
400 | let not_future_incompatible = | |
136023e0 | 401 | future_incompatible.map(|f| f.reason.edition().is_some()).unwrap_or(true); |
17df50a5 | 402 | if not_future_incompatible && !lint.report_in_external_macro { |
74b04a01 XL |
403 | err.cancel(); |
404 | // Don't continue further, since we don't want to have | |
405 | // `diag_span_note_once` called for a diagnostic that isn't emitted. | |
406 | return; | |
407 | } | |
dfeec247 | 408 | } |
74b04a01 | 409 | |
2b03887a FG |
410 | // Delay evaluating and setting the primary message until after we've |
411 | // suppressed the lint due to macros. | |
412 | err.set_primary_message(msg); | |
413 | ||
5e7ed085 FG |
414 | // Lint diagnostics that are covered by the expect level will not be emitted outside |
415 | // the compiler. It is therefore not necessary to add any information for the user. | |
416 | // This will therefore directly call the decorate function which will in turn emit | |
417 | // the `Diagnostic`. | |
418 | if let Level::Expect(_) = level { | |
419 | let name = lint.name_lower(); | |
420 | err.code(DiagnosticId::Lint { name, has_future_breakage, is_force_warn: false }); | |
2b03887a FG |
421 | |
422 | decorate(&mut err); | |
423 | err.emit(); | |
5e7ed085 | 424 | return; |
dfeec247 | 425 | } |
dfeec247 | 426 | |
5e7ed085 | 427 | let name = lint.name_lower(); |
923072b8 | 428 | let is_force_warn = matches!(level, Level::ForceWarn(_)); |
136023e0 | 429 | err.code(DiagnosticId::Lint { name, has_future_breakage, is_force_warn }); |
74b04a01 XL |
430 | |
431 | if let Some(future_incompatible) = future_incompatible { | |
5099ac24 FG |
432 | let explanation = match future_incompatible.reason { |
433 | FutureIncompatibilityReason::FutureReleaseError | |
434 | | FutureIncompatibilityReason::FutureReleaseErrorReportNow => { | |
435 | "this was previously accepted by the compiler but is being phased out; \ | |
436 | it will become a hard error in a future release!" | |
437 | .to_owned() | |
438 | } | |
439 | FutureIncompatibilityReason::FutureReleaseSemanticsChange => { | |
440 | "this will change its meaning in a future release!".to_owned() | |
441 | } | |
442 | FutureIncompatibilityReason::EditionError(edition) => { | |
443 | let current_edition = sess.edition(); | |
444 | format!( | |
445 | "this is accepted in the current edition (Rust {}) but is a hard error in Rust {}!", | |
446 | current_edition, edition | |
447 | ) | |
448 | } | |
449 | FutureIncompatibilityReason::EditionSemanticsChange(edition) => { | |
450 | format!("this changes meaning in Rust {}", edition) | |
451 | } | |
452 | FutureIncompatibilityReason::Custom(reason) => reason.to_owned(), | |
74b04a01 | 453 | }; |
5099ac24 | 454 | |
136023e0 XL |
455 | if future_incompatible.explain_reason { |
456 | err.warn(&explanation); | |
457 | } | |
458 | if !future_incompatible.reference.is_empty() { | |
459 | let citation = | |
460 | format!("for more information, see {}", future_incompatible.reference); | |
461 | err.note(&citation); | |
462 | } | |
74b04a01 | 463 | } |
dfeec247 | 464 | |
2b03887a FG |
465 | // Finally, run `decorate`. |
466 | decorate(&mut err); | |
467 | explain_lint_level_source(lint, level, src, &mut *err); | |
468 | err.emit() | |
74b04a01 | 469 | } |
2b03887a | 470 | struct_lint_level_impl(sess, lint, level, src, span, msg, Box::new(decorate)) |
dfeec247 XL |
471 | } |
472 | ||
473 | /// Returns whether `span` originates in a foreign crate's external macro. | |
474 | /// | |
475 | /// This is used to test whether a lint should not even begin to figure out whether it should | |
476 | /// be reported on the current node. | |
477 | pub fn in_external_macro(sess: &Session, span: Span) -> bool { | |
478 | let expn_data = span.ctxt().outer_expn_data(); | |
479 | match expn_data.kind { | |
c295e0f8 XL |
480 | ExpnKind::Inlined |
481 | | ExpnKind::Root | |
2b03887a FG |
482 | | ExpnKind::Desugaring( |
483 | DesugaringKind::ForLoop | DesugaringKind::WhileLoop | DesugaringKind::OpaqueTy, | |
484 | ) => false, | |
dfeec247 | 485 | ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) => true, // well, it's "external" |
136023e0 | 486 | ExpnKind::Macro(MacroKind::Bang, _) => { |
ba9703b0 XL |
487 | // Dummy span for the `def_site` means it's an external macro. |
488 | expn_data.def_site.is_dummy() || sess.source_map().is_imported(expn_data.def_site) | |
dfeec247 | 489 | } |
3dfed10e | 490 | ExpnKind::Macro { .. } => true, // definitely a plugin |
dfeec247 XL |
491 | } |
492 | } |