Skip to main content

rustc_session/
parse.rs

1//! Contains `ParseSess` which holds state living beyond what one `Parser` might.
2//! It also serves as an input to the parser itself.
3
4use std::str;
5use std::sync::Arc;
6
7use rustc_ast::attr::AttrIdGenerator;
8use rustc_ast::node_id::NodeId;
9use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
10use rustc_data_structures::sync::{AppendOnlyVec, DynSend, DynSync, Lock};
11use rustc_errors::annotate_snippet_emitter_writer::AnnotateSnippetEmitter;
12use rustc_errors::emitter::{EmitterWithNote, stderr_destination};
13use rustc_errors::{
14    BufferedEarlyLint, ColorConfig, DecorateDiagCompat, Diag, DiagCtxt, DiagCtxtHandle,
15    DiagMessage, EmissionGuarantee, Level, MultiSpan, StashKey,
16};
17use rustc_feature::{GateIssue, find_feature_issue};
18use rustc_span::edition::Edition;
19use rustc_span::hygiene::ExpnId;
20use rustc_span::source_map::{FilePathMapping, SourceMap};
21use rustc_span::{Span, Symbol, sym};
22
23use crate::Session;
24use crate::errors::{
25    CliFeatureDiagnosticHelp, FeatureDiagnosticForIssue, FeatureDiagnosticHelp,
26    FeatureDiagnosticSuggestion, FeatureGateError, SuggestUpgradeCompiler,
27};
28use crate::lint::builtin::UNSTABLE_SYNTAX_PRE_EXPANSION;
29use crate::lint::{Lint, LintId};
30
31/// Collected spans during parsing for places where a certain feature was
32/// used and should be feature gated accordingly in `check_crate`.
33#[derive(#[automatically_derived]
impl ::core::default::Default for GatedSpans {
    #[inline]
    fn default() -> GatedSpans {
        GatedSpans { spans: ::core::default::Default::default() }
    }
}Default)]
34pub struct GatedSpans {
35    pub spans: Lock<FxHashMap<Symbol, Vec<Span>>>,
36}
37
38impl GatedSpans {
39    /// Feature gate the given `span` under the given `feature`
40    /// which is same `Symbol` used in `unstable.rs`.
41    pub fn gate(&self, feature: Symbol, span: Span) {
42        self.spans.borrow_mut().entry(feature).or_default().push(span);
43    }
44
45    /// Ungate the last span under the given `feature`.
46    /// Panics if the given `span` wasn't the last one.
47    ///
48    /// Using this is discouraged unless you have a really good reason to.
49    pub fn ungate_last(&self, feature: Symbol, span: Span) {
50        let removed_span = self.spans.borrow_mut().entry(feature).or_default().pop().unwrap();
51        if true {
    match (&span, &removed_span) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val, ::core::option::Option::None);
            }
        }
    };
};debug_assert_eq!(span, removed_span);
52    }
53
54    /// Prepend the given set of `spans` onto the set in `self`.
55    pub fn merge(&self, mut spans: FxHashMap<Symbol, Vec<Span>>) {
56        let mut inner = self.spans.borrow_mut();
57        // The entries will be moved to another map so the drain order does not
58        // matter.
59        #[allow(rustc::potential_query_instability)]
60        for (gate, mut gate_spans) in inner.drain() {
61            spans.entry(gate).or_default().append(&mut gate_spans);
62        }
63        *inner = spans;
64    }
65}
66
67#[derive(#[automatically_derived]
impl ::core::default::Default for SymbolGallery {
    #[inline]
    fn default() -> SymbolGallery {
        SymbolGallery { symbols: ::core::default::Default::default() }
    }
}Default)]
68pub struct SymbolGallery {
69    /// All symbols occurred and their first occurrence span.
70    pub symbols: Lock<FxIndexMap<Symbol, Span>>,
71}
72
73impl SymbolGallery {
74    /// Insert a symbol and its span into symbol gallery.
75    /// If the symbol has occurred before, ignore the new occurrence.
76    pub fn insert(&self, symbol: Symbol, span: Span) {
77        self.symbols.lock().entry(symbol).or_insert(span);
78    }
79}
80
81// FIXME: this function now accepts `Session` instead of `ParseSess` and should be relocated
82/// Construct a diagnostic for a language feature error due to the given `span`.
83/// The `feature`'s `Symbol` is the one you used in `unstable.rs` and `rustc_span::symbol`.
84#[track_caller]
85pub fn feature_err(
86    sess: &Session,
87    feature: Symbol,
88    span: impl Into<MultiSpan>,
89    explain: impl Into<DiagMessage>,
90) -> Diag<'_> {
91    feature_err_issue(sess, feature, span, GateIssue::Language, explain)
92}
93
94/// Construct a diagnostic for a feature gate error.
95///
96/// This variant allows you to control whether it is a library or language feature.
97/// Almost always, you want to use this for a language feature. If so, prefer `feature_err`.
98#[track_caller]
99pub fn feature_err_issue(
100    sess: &Session,
101    feature: Symbol,
102    span: impl Into<MultiSpan>,
103    issue: GateIssue,
104    explain: impl Into<DiagMessage>,
105) -> Diag<'_> {
106    let span = span.into();
107
108    // Cancel an earlier warning for this same error, if it exists.
109    if let Some(span) = span.primary_span()
110        && let Some(err) = sess.dcx().steal_non_err(span, StashKey::EarlySyntaxWarning)
111    {
112        err.cancel()
113    }
114
115    let mut err = sess.dcx().create_err(FeatureGateError { span, explain: explain.into() });
116    add_feature_diagnostics_for_issue(&mut err, sess, feature, issue, false, None);
117    err
118}
119
120/// Construct a future incompatibility diagnostic for a feature gate.
121///
122/// This diagnostic is only a warning and *does not cause compilation to fail*.
123#[track_caller]
124pub fn feature_warn(sess: &Session, feature: Symbol, span: Span, explain: &'static str) {
125    feature_warn_issue(sess, feature, span, GateIssue::Language, explain);
126}
127
128/// Construct a future incompatibility diagnostic for a feature gate.
129///
130/// This diagnostic is only a warning and *does not cause compilation to fail*.
131///
132/// This variant allows you to control whether it is a library or language feature.
133/// Almost always, you want to use this for a language feature. If so, prefer `feature_warn`.
134#[track_caller]
135pub fn feature_warn_issue(
136    sess: &Session,
137    feature: Symbol,
138    span: Span,
139    issue: GateIssue,
140    explain: &'static str,
141) {
142    let mut err = sess.dcx().struct_span_warn(span, explain);
143    add_feature_diagnostics_for_issue(&mut err, sess, feature, issue, false, None);
144
145    // Decorate this as a future-incompatibility lint as in rustc_middle::lint::lint_level
146    let lint = UNSTABLE_SYNTAX_PRE_EXPANSION;
147    let future_incompatible = lint.future_incompatible.as_ref().unwrap();
148    err.is_lint(lint.name_lower(), /* has_future_breakage */ false);
149    err.warn(lint.desc);
150    err.note(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("for more information, see {0}",
                future_incompatible.reason.reference()))
    })format!("for more information, see {}", future_incompatible.reason.reference()));
151
152    // A later feature_err call can steal and cancel this warning.
153    err.stash(span, StashKey::EarlySyntaxWarning);
154}
155
156/// Adds the diagnostics for a feature to an existing error.
157/// Must be a language feature!
158pub fn add_feature_diagnostics<G: EmissionGuarantee>(
159    err: &mut Diag<'_, G>,
160    sess: &Session,
161    feature: Symbol,
162) {
163    add_feature_diagnostics_for_issue(err, sess, feature, GateIssue::Language, false, None);
164}
165
166/// Adds the diagnostics for a feature to an existing error.
167///
168/// This variant allows you to control whether it is a library or language feature.
169/// Almost always, you want to use this for a language feature. If so, prefer
170/// `add_feature_diagnostics`.
171pub fn add_feature_diagnostics_for_issue<G: EmissionGuarantee>(
172    err: &mut Diag<'_, G>,
173    sess: &Session,
174    feature: Symbol,
175    issue: GateIssue,
176    feature_from_cli: bool,
177    inject_span: Option<Span>,
178) {
179    if let Some(n) = find_feature_issue(feature, issue) {
180        err.subdiagnostic(FeatureDiagnosticForIssue { n });
181    }
182
183    // #23973: do not suggest `#![feature(...)]` if we are in beta/stable
184    if sess.unstable_features.is_nightly_build() {
185        if feature_from_cli {
186            err.subdiagnostic(CliFeatureDiagnosticHelp { feature });
187        } else if let Some(span) = inject_span {
188            err.subdiagnostic(FeatureDiagnosticSuggestion { feature, span });
189        } else {
190            err.subdiagnostic(FeatureDiagnosticHelp { feature });
191        }
192        if feature == sym::rustc_attrs {
193            // We're unlikely to stabilize something out of `rustc_attrs`
194            // without at least renaming it, so pointing out how old
195            // the compiler is will do little good.
196        } else if sess.opts.unstable_opts.ui_testing {
197            err.subdiagnostic(SuggestUpgradeCompiler::ui_testing());
198        } else if let Some(suggestion) = SuggestUpgradeCompiler::new() {
199            err.subdiagnostic(suggestion);
200        }
201    }
202}
203
204/// This is only used by unstable_feature_bound as it does not have issue number information for now.
205/// This is basically the same as `feature_err_issue`
206/// but without the feature issue note. If we can do a lookup for issue number from feature name,
207/// then we should directly use `feature_err_issue` for ambiguity error of
208/// `#[unstable_feature_bound]`.
209#[track_caller]
210pub fn feature_err_unstable_feature_bound(
211    sess: &Session,
212    feature: Symbol,
213    span: impl Into<MultiSpan>,
214    explain: impl Into<DiagMessage>,
215) -> Diag<'_> {
216    let span = span.into();
217
218    // Cancel an earlier warning for this same error, if it exists.
219    if let Some(span) = span.primary_span() {
220        if let Some(err) = sess.dcx().steal_non_err(span, StashKey::EarlySyntaxWarning) {
221            err.cancel()
222        }
223    }
224
225    let mut err = sess.dcx().create_err(FeatureGateError { span, explain: explain.into() });
226
227    // #23973: do not suggest `#![feature(...)]` if we are in beta/stable
228    if sess.unstable_features.is_nightly_build() {
229        err.subdiagnostic(FeatureDiagnosticHelp { feature });
230
231        if feature == sym::rustc_attrs {
232            // We're unlikely to stabilize something out of `rustc_attrs`
233            // without at least renaming it, so pointing out how old
234            // the compiler is will do little good.
235        } else if sess.opts.unstable_opts.ui_testing {
236            err.subdiagnostic(SuggestUpgradeCompiler::ui_testing());
237        } else if let Some(suggestion) = SuggestUpgradeCompiler::new() {
238            err.subdiagnostic(suggestion);
239        }
240    }
241    err
242}
243
244/// Info about a parsing session.
245pub struct ParseSess {
246    dcx: DiagCtxt,
247    pub edition: Edition,
248    /// Places where raw identifiers were used. This is used to avoid complaining about idents
249    /// clashing with keywords in new editions.
250    pub raw_identifier_spans: AppendOnlyVec<Span>,
251    /// Places where identifiers that contain invalid Unicode codepoints but that look like they
252    /// should be. Useful to avoid bad tokenization when encountering emoji. We group them to
253    /// provide a single error per unique incorrect identifier.
254    pub bad_unicode_identifiers: Lock<FxIndexMap<Symbol, Vec<Span>>>,
255    source_map: Arc<SourceMap>,
256    pub buffered_lints: Lock<Vec<BufferedEarlyLint>>,
257    /// Contains the spans of block expressions that could have been incomplete based on the
258    /// operation token that followed it, but that the parser cannot identify without further
259    /// analysis.
260    pub ambiguous_block_expr_parse: Lock<FxIndexMap<Span, Span>>,
261    pub gated_spans: GatedSpans,
262    pub symbol_gallery: SymbolGallery,
263    /// Used to generate new `AttrId`s. Every `AttrId` is unique.
264    pub attr_id_generator: AttrIdGenerator,
265}
266
267impl ParseSess {
268    /// Used for testing.
269    pub fn new() -> Self {
270        let sm = Arc::new(SourceMap::new(FilePathMapping::empty()));
271        let emitter = Box::new(
272            AnnotateSnippetEmitter::new(stderr_destination(ColorConfig::Auto))
273                .sm(Some(Arc::clone(&sm))),
274        );
275        let dcx = DiagCtxt::new(emitter);
276        ParseSess::with_dcx(dcx, sm)
277    }
278
279    pub fn with_dcx(dcx: DiagCtxt, source_map: Arc<SourceMap>) -> Self {
280        Self {
281            dcx,
282            edition: ExpnId::root().expn_data().edition,
283            raw_identifier_spans: Default::default(),
284            bad_unicode_identifiers: Lock::new(Default::default()),
285            source_map,
286            buffered_lints: Lock::new(::alloc::vec::Vec::new()vec![]),
287            ambiguous_block_expr_parse: Lock::new(Default::default()),
288            gated_spans: GatedSpans::default(),
289            symbol_gallery: SymbolGallery::default(),
290            attr_id_generator: AttrIdGenerator::new(),
291        }
292    }
293
294    pub fn emitter_with_note(note: String) -> Self {
295        let sm = Arc::new(SourceMap::new(FilePathMapping::empty()));
296        let emitter = Box::new(AnnotateSnippetEmitter::new(stderr_destination(ColorConfig::Auto)));
297        let dcx = DiagCtxt::new(Box::new(EmitterWithNote { emitter, note }));
298        ParseSess::with_dcx(dcx, sm)
299    }
300
301    #[inline]
302    pub fn source_map(&self) -> &SourceMap {
303        &self.source_map
304    }
305
306    pub fn clone_source_map(&self) -> Arc<SourceMap> {
307        Arc::clone(&self.source_map)
308    }
309
310    pub fn buffer_lint(
311        &self,
312        lint: &'static Lint,
313        span: impl Into<MultiSpan>,
314        node_id: NodeId,
315        diagnostic: impl Into<DecorateDiagCompat>,
316    ) {
317        self.opt_span_buffer_lint(lint, Some(span.into()), node_id, diagnostic.into())
318    }
319
320    pub fn dyn_buffer_lint<
321        F: for<'a> FnOnce(DiagCtxtHandle<'a>, Level) -> Diag<'a, ()> + DynSync + DynSend + 'static,
322    >(
323        &self,
324        lint: &'static Lint,
325        span: impl Into<MultiSpan>,
326        node_id: NodeId,
327        callback: F,
328    ) {
329        self.opt_span_buffer_lint(
330            lint,
331            Some(span.into()),
332            node_id,
333            DecorateDiagCompat(Box::new(|dcx, level, _| callback(dcx, level))),
334        )
335    }
336
337    pub fn dyn_buffer_lint_sess<
338        F: for<'a> FnOnce(DiagCtxtHandle<'a>, Level, &Session) -> Diag<'a, ()>
339            + DynSync
340            + DynSend
341            + 'static,
342    >(
343        &self,
344        lint: &'static Lint,
345        span: impl Into<MultiSpan>,
346        node_id: NodeId,
347        callback: F,
348    ) {
349        self.opt_span_buffer_lint(
350            lint,
351            Some(span.into()),
352            node_id,
353            DecorateDiagCompat(Box::new(|dcx, level, sess| {
354                let sess = sess.downcast_ref::<Session>().expect("expected a `Session`");
355                callback(dcx, level, sess)
356            })),
357        )
358    }
359
360    pub(crate) fn opt_span_buffer_lint(
361        &self,
362        lint: &'static Lint,
363        span: Option<MultiSpan>,
364        node_id: NodeId,
365        diagnostic: DecorateDiagCompat,
366    ) {
367        self.buffered_lints.with_lock(|buffered_lints| {
368            buffered_lints.push(BufferedEarlyLint {
369                span,
370                node_id,
371                lint_id: LintId::of(lint),
372                diagnostic,
373            });
374        });
375    }
376
377    pub fn dcx(&self) -> DiagCtxtHandle<'_> {
378        self.dcx.handle()
379    }
380}