about summary refs log tree commit diff
path: root/compiler/rustc_builtin_macros/src
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_builtin_macros/src')
-rw-r--r--compiler/rustc_builtin_macros/src/asm.rs5
-rw-r--r--compiler/rustc_builtin_macros/src/assert.rs35
-rw-r--r--compiler/rustc_builtin_macros/src/errors.rs22
-rw-r--r--compiler/rustc_builtin_macros/src/format.rs33
-rw-r--r--compiler/rustc_builtin_macros/src/source_util.rs103
-rw-r--r--compiler/rustc_builtin_macros/src/test_harness.rs3
-rw-r--r--compiler/rustc_builtin_macros/src/util.rs3
7 files changed, 135 insertions, 69 deletions
diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs
index 86b8e1ff8db..ae62b5ea2a0 100644
--- a/compiler/rustc_builtin_macros/src/asm.rs
+++ b/compiler/rustc_builtin_macros/src/asm.rs
@@ -1,4 +1,3 @@
-use lint::BuiltinLintDiag;
 use rustc_ast::tokenstream::TokenStream;
 use rustc_ast::{AsmMacro, token};
 use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
@@ -352,7 +351,7 @@ fn expand_preparsed_asm(
                     lint::builtin::BAD_ASM_STYLE,
                     find_span(".intel_syntax"),
                     ecx.current_expansion.lint_node_id,
-                    BuiltinLintDiag::AvoidUsingIntelSyntax,
+                    errors::AvoidIntelSyntax,
                 );
             }
             if template_str.contains(".att_syntax") {
@@ -360,7 +359,7 @@ fn expand_preparsed_asm(
                     lint::builtin::BAD_ASM_STYLE,
                     find_span(".att_syntax"),
                     ecx.current_expansion.lint_node_id,
-                    BuiltinLintDiag::AvoidUsingAttSyntax,
+                    errors::AvoidAttSyntax,
                 );
             }
         }
diff --git a/compiler/rustc_builtin_macros/src/assert.rs b/compiler/rustc_builtin_macros/src/assert.rs
index 013258a1b4e..855da5caa31 100644
--- a/compiler/rustc_builtin_macros/src/assert.rs
+++ b/compiler/rustc_builtin_macros/src/assert.rs
@@ -1,8 +1,8 @@
 mod context;
 
-use rustc_ast::token::{self, Delimiter};
+use rustc_ast::token::Delimiter;
 use rustc_ast::tokenstream::{DelimSpan, TokenStream};
-use rustc_ast::{DelimArgs, Expr, ExprKind, MacCall, Path, PathSegment};
+use rustc_ast::{DelimArgs, Expr, ExprKind, MacCall, Path, PathSegment, UnOp, token};
 use rustc_ast_pretty::pprust;
 use rustc_errors::PResult;
 use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult};
@@ -29,7 +29,7 @@ pub(crate) fn expand_assert<'cx>(
 
     // `core::panic` and `std::panic` are different macros, so we use call-site
     // context to pick up whichever is currently in scope.
-    let call_site_span = cx.with_call_site_ctxt(cond_expr.span);
+    let call_site_span = cx.with_call_site_ctxt(span);
 
     let panic_path = || {
         if use_panic_2021(span) {
@@ -63,7 +63,7 @@ pub(crate) fn expand_assert<'cx>(
                 }),
             })),
         );
-        assert_cond_check(cx, call_site_span, cond_expr, then)
+        expr_if_not(cx, call_site_span, cond_expr, then, None)
     }
     // If `generic_assert` is enabled, generates rich captured outputs
     //
@@ -88,33 +88,26 @@ pub(crate) fn expand_assert<'cx>(
                 )),
             )],
         );
-        assert_cond_check(cx, call_site_span, cond_expr, then)
+        expr_if_not(cx, call_site_span, cond_expr, then, None)
     };
 
     ExpandResult::Ready(MacEager::expr(expr))
 }
 
-/// `assert!($cond_expr, $custom_message)`
 struct Assert {
     cond_expr: Box<Expr>,
     custom_message: Option<TokenStream>,
 }
 
-/// `match <cond> { true => {} _ => <then> }`
-fn assert_cond_check(cx: &ExtCtxt<'_>, span: Span, cond: Box<Expr>, then: Box<Expr>) -> Box<Expr> {
-    // Instead of expanding to `if !<cond> { <then> }`, we expand to
-    // `match <cond> { true => {} _ => <then> }`.
-    // This allows us to always complain about mismatched types instead of "cannot apply unary
-    // operator `!` to type `X`" when passing an invalid `<cond>`, while also allowing `<cond>` to
-    // be `&true`.
-    let els = cx.expr_block(cx.block(span, thin_vec![]));
-    let mut arms = thin_vec![];
-    arms.push(cx.arm(span, cx.pat_lit(span, cx.expr_bool(span, true)), els));
-    arms.push(cx.arm(span, cx.pat_wild(span), then));
-
-    // We wrap the `match` in a statement to limit the length of any borrows introduced in the
-    // condition.
-    cx.expr_block(cx.block(span, [cx.stmt_expr(cx.expr_match(span, cond, arms))].into()))
+// if !{ ... } { ... } else { ... }
+fn expr_if_not(
+    cx: &ExtCtxt<'_>,
+    span: Span,
+    cond: Box<Expr>,
+    then: Box<Expr>,
+    els: Option<Box<Expr>>,
+) -> Box<Expr> {
+    cx.expr_if(span, cx.expr(span, ExprKind::Unary(UnOp::Not, cond)), then, els)
 }
 
 fn parse_assert<'a>(cx: &ExtCtxt<'a>, sp: Span, stream: TokenStream) -> PResult<'a, Assert> {
diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs
index 54e8f750337..0993fdc5be4 100644
--- a/compiler/rustc_builtin_macros/src/errors.rs
+++ b/compiler/rustc_builtin_macros/src/errors.rs
@@ -3,9 +3,29 @@ use rustc_errors::{
     Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, MultiSpan, SingleLabelManySpans,
     Subdiagnostic,
 };
-use rustc_macros::{Diagnostic, Subdiagnostic};
+use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
 use rustc_span::{Ident, Span, Symbol};
 
+#[derive(LintDiagnostic)]
+#[diag(builtin_macros_avoid_intel_syntax)]
+pub(crate) struct AvoidIntelSyntax;
+
+#[derive(LintDiagnostic)]
+#[diag(builtin_macros_avoid_att_syntax)]
+pub(crate) struct AvoidAttSyntax;
+
+#[derive(LintDiagnostic)]
+#[diag(builtin_macros_incomplete_include)]
+pub(crate) struct IncompleteInclude;
+
+#[derive(LintDiagnostic)]
+#[diag(builtin_macros_unnameable_test_items)]
+pub(crate) struct UnnameableTestItems;
+
+#[derive(LintDiagnostic)]
+#[diag(builtin_macros_duplicate_macro_attribute)]
+pub(crate) struct DuplicateMacroAttribute;
+
 #[derive(Diagnostic)]
 #[diag(builtin_macros_requires_cfg_pattern)]
 pub(crate) struct RequiresCfgPattern {
diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs
index 6415e55e0b0..d70888205a5 100644
--- a/compiler/rustc_builtin_macros/src/format.rs
+++ b/compiler/rustc_builtin_macros/src/format.rs
@@ -565,9 +565,11 @@ fn make_format_args(
             &used,
             &args,
             &pieces,
+            &invalid_refs,
             detect_foreign_fmt,
             str_style,
             fmt_str,
+            uncooked_fmt_str.1.as_str(),
             fmt_span,
         );
     }
@@ -645,9 +647,11 @@ fn report_missing_placeholders(
     used: &[bool],
     args: &FormatArguments,
     pieces: &[parse::Piece<'_>],
+    invalid_refs: &[(usize, Option<Span>, PositionUsedAs, FormatArgPositionKind)],
     detect_foreign_fmt: bool,
     str_style: Option<usize>,
     fmt_str: &str,
+    uncooked_fmt_str: &str,
     fmt_span: Span,
 ) {
     let mut diag = if let &[(span, named)] = &unused[..] {
@@ -762,6 +766,35 @@ fn report_missing_placeholders(
         diag.span_label(fmt_span, "formatting specifier missing");
     }
 
+    if !found_foreign && invalid_refs.is_empty() {
+        // Show example if user didn't use any format specifiers
+        let show_example = used.iter().all(|used| !used);
+
+        if !show_example {
+            if unused.len() > 1 {
+                diag.note(format!("consider adding {} format specifiers", unused.len()));
+            }
+        } else {
+            let msg = if unused.len() == 1 {
+                "a format specifier".to_string()
+            } else {
+                format!("{} format specifiers", unused.len())
+            };
+
+            let sugg = match str_style {
+                None => format!("\"{}{}\"", uncooked_fmt_str, "{}".repeat(unused.len())),
+                Some(n_hashes) => format!(
+                    "r{hashes}\"{uncooked_fmt_str}{fmt_specifiers}\"{hashes}",
+                    hashes = "#".repeat(n_hashes),
+                    fmt_specifiers = "{}".repeat(unused.len())
+                ),
+            };
+            let msg = format!("format specifiers use curly braces, consider adding {msg}");
+
+            diag.span_suggestion_verbose(fmt_span, msg, sugg, Applicability::MaybeIncorrect);
+        }
+    }
+
     diag.emit();
 }
 
diff --git a/compiler/rustc_builtin_macros/src/source_util.rs b/compiler/rustc_builtin_macros/src/source_util.rs
index 37bab5be542..16adaab15c5 100644
--- a/compiler/rustc_builtin_macros/src/source_util.rs
+++ b/compiler/rustc_builtin_macros/src/source_util.rs
@@ -1,3 +1,5 @@
+//! The implementation of built-in macros which relate to the file system.
+
 use std::path::{Path, PathBuf};
 use std::rc::Rc;
 use std::sync::Arc;
@@ -10,10 +12,11 @@ use rustc_expand::base::{
     DummyResult, ExpandResult, ExtCtxt, MacEager, MacResult, MacroExpanderResult, resolve_path,
 };
 use rustc_expand::module::DirOwnership;
-use rustc_lint_defs::BuiltinLintDiag;
-use rustc_parse::parser::{ForceCollect, Parser};
+use rustc_parse::lexer::StripTokens;
+use rustc_parse::parser::ForceCollect;
 use rustc_parse::{new_parser_from_file, unwrap_or_emit_fatal, utf8_error};
 use rustc_session::lint::builtin::INCOMPLETE_INCLUDE;
+use rustc_session::parse::ParseSess;
 use rustc_span::source_map::SourceMap;
 use rustc_span::{ByteSymbol, Pos, Span, Symbol};
 use smallvec::SmallVec;
@@ -23,11 +26,7 @@ use crate::util::{
     check_zero_tts, get_single_str_from_tts, get_single_str_spanned_from_tts, parse_expr,
 };
 
-// These macros all relate to the file system; they either return
-// the column/row/filename of the expression, or they include
-// a given file into the current one.
-
-/// line!(): expands to the current line number
+/// Expand `line!()` to the current line number.
 pub(crate) fn expand_line(
     cx: &mut ExtCtxt<'_>,
     sp: Span,
@@ -42,7 +41,7 @@ pub(crate) fn expand_line(
     ExpandResult::Ready(MacEager::expr(cx.expr_u32(topmost, loc.line as u32)))
 }
 
-/* column!(): expands to the current column number */
+/// Expand `column!()` to the current column number.
 pub(crate) fn expand_column(
     cx: &mut ExtCtxt<'_>,
     sp: Span,
@@ -57,9 +56,7 @@ pub(crate) fn expand_column(
     ExpandResult::Ready(MacEager::expr(cx.expr_u32(topmost, loc.col.to_usize() as u32 + 1)))
 }
 
-/// file!(): expands to the current filename */
-/// The source_file (`loc.file`) contains a bunch more information we could spit
-/// out if we wanted.
+/// Expand `file!()` to the current filename.
 pub(crate) fn expand_file(
     cx: &mut ExtCtxt<'_>,
     sp: Span,
@@ -81,6 +78,7 @@ pub(crate) fn expand_file(
     )))
 }
 
+/// Expand `stringify!($input)`.
 pub(crate) fn expand_stringify(
     cx: &mut ExtCtxt<'_>,
     sp: Span,
@@ -91,6 +89,7 @@ pub(crate) fn expand_stringify(
     ExpandResult::Ready(MacEager::expr(cx.expr_str(sp, Symbol::intern(&s))))
 }
 
+/// Expand `module_path!()` to (a textual representation of) the current module path.
 pub(crate) fn expand_mod(
     cx: &mut ExtCtxt<'_>,
     sp: Span,
@@ -104,9 +103,9 @@ pub(crate) fn expand_mod(
     ExpandResult::Ready(MacEager::expr(cx.expr_str(sp, Symbol::intern(&string))))
 }
 
-/// include! : parse the given file as an expr
-/// This is generally a bad idea because it's going to behave
-/// unhygienically.
+/// Expand `include!($input)`.
+///
+/// This works in item and expression position. Notably, it doesn't work in pattern position.
 pub(crate) fn expand_include<'cx>(
     cx: &'cx mut ExtCtxt<'_>,
     sp: Span,
@@ -116,64 +115,76 @@ pub(crate) fn expand_include<'cx>(
     let ExpandResult::Ready(mac) = get_single_str_from_tts(cx, sp, tts, "include!") else {
         return ExpandResult::Retry(());
     };
-    let file = match mac {
-        Ok(file) => file,
+    let path = match mac {
+        Ok(path) => path,
         Err(guar) => return ExpandResult::Ready(DummyResult::any(sp, guar)),
     };
     // The file will be added to the code map by the parser
-    let file = match resolve_path(&cx.sess, file.as_str(), sp) {
-        Ok(f) => f,
+    let path = match resolve_path(&cx.sess, path.as_str(), sp) {
+        Ok(path) => path,
         Err(err) => {
             let guar = err.emit();
             return ExpandResult::Ready(DummyResult::any(sp, guar));
         }
     };
-    let p = unwrap_or_emit_fatal(new_parser_from_file(cx.psess(), &file, Some(sp)));
 
     // If in the included file we have e.g., `mod bar;`,
-    // then the path of `bar.rs` should be relative to the directory of `file`.
+    // then the path of `bar.rs` should be relative to the directory of `path`.
     // See https://github.com/rust-lang/rust/pull/69838/files#r395217057 for a discussion.
     // `MacroExpander::fully_expand_fragment` later restores, so "stack discipline" is maintained.
-    let dir_path = file.parent().unwrap_or(&file).to_owned();
+    let dir_path = path.parent().unwrap_or(&path).to_owned();
     cx.current_expansion.module = Rc::new(cx.current_expansion.module.with_dir_path(dir_path));
     cx.current_expansion.dir_ownership = DirOwnership::Owned { relative: None };
 
     struct ExpandInclude<'a> {
-        p: Parser<'a>,
+        psess: &'a ParseSess,
+        path: PathBuf,
         node_id: ast::NodeId,
+        span: Span,
     }
     impl<'a> MacResult for ExpandInclude<'a> {
-        fn make_expr(mut self: Box<ExpandInclude<'a>>) -> Option<Box<ast::Expr>> {
-            let expr = parse_expr(&mut self.p).ok()?;
-            if self.p.token != token::Eof {
-                self.p.psess.buffer_lint(
+        fn make_expr(self: Box<ExpandInclude<'a>>) -> Option<Box<ast::Expr>> {
+            let mut p = unwrap_or_emit_fatal(new_parser_from_file(
+                self.psess,
+                &self.path,
+                // Don't strip frontmatter for backward compatibility, `---` may be the start of a
+                // manifold negation. FIXME: Ideally, we wouldn't strip shebangs here either.
+                StripTokens::Shebang,
+                Some(self.span),
+            ));
+            let expr = parse_expr(&mut p).ok()?;
+            if p.token != token::Eof {
+                p.psess.buffer_lint(
                     INCOMPLETE_INCLUDE,
-                    self.p.token.span,
+                    p.token.span,
                     self.node_id,
-                    BuiltinLintDiag::IncompleteInclude,
+                    errors::IncompleteInclude,
                 );
             }
             Some(expr)
         }
 
-        fn make_items(mut self: Box<ExpandInclude<'a>>) -> Option<SmallVec<[Box<ast::Item>; 1]>> {
+        fn make_items(self: Box<ExpandInclude<'a>>) -> Option<SmallVec<[Box<ast::Item>; 1]>> {
+            let mut p = unwrap_or_emit_fatal(new_parser_from_file(
+                self.psess,
+                &self.path,
+                StripTokens::ShebangAndFrontmatter,
+                Some(self.span),
+            ));
             let mut ret = SmallVec::new();
             loop {
-                match self.p.parse_item(ForceCollect::No) {
+                match p.parse_item(ForceCollect::No) {
                     Err(err) => {
                         err.emit();
                         break;
                     }
                     Ok(Some(item)) => ret.push(item),
                     Ok(None) => {
-                        if self.p.token != token::Eof {
-                            self.p
-                                .dcx()
-                                .create_err(errors::ExpectedItem {
-                                    span: self.p.token.span,
-                                    token: &pprust::token_to_string(&self.p.token),
-                                })
-                                .emit();
+                        if p.token != token::Eof {
+                            p.dcx().emit_err(errors::ExpectedItem {
+                                span: p.token.span,
+                                token: &pprust::token_to_string(&p.token),
+                            });
                         }
 
                         break;
@@ -184,10 +195,17 @@ pub(crate) fn expand_include<'cx>(
         }
     }
 
-    ExpandResult::Ready(Box::new(ExpandInclude { p, node_id: cx.current_expansion.lint_node_id }))
+    ExpandResult::Ready(Box::new(ExpandInclude {
+        psess: cx.psess(),
+        path,
+        node_id: cx.current_expansion.lint_node_id,
+        span: sp,
+    }))
 }
 
-/// `include_str!`: read the given file, insert it as a literal string expr
+/// Expand `include_str!($input)` to the content of the UTF-8-encoded file given by path `$input` as a string literal.
+///
+/// This works in expression, pattern and statement position.
 pub(crate) fn expand_include_str(
     cx: &mut ExtCtxt<'_>,
     sp: Span,
@@ -206,6 +224,7 @@ pub(crate) fn expand_include_str(
         Ok((bytes, bsp)) => match std::str::from_utf8(&bytes) {
             Ok(src) => {
                 let interned_src = Symbol::intern(src);
+                // MacEager converts the expr into a pat if need be.
                 MacEager::expr(cx.expr_str(cx.with_def_site_ctxt(bsp), interned_src))
             }
             Err(utf8err) => {
@@ -218,6 +237,9 @@ pub(crate) fn expand_include_str(
     })
 }
 
+/// Expand `include_bytes!($input)` to the content of the file given by path `$input`.
+///
+/// This works in expression, pattern and statement position.
 pub(crate) fn expand_include_bytes(
     cx: &mut ExtCtxt<'_>,
     sp: Span,
@@ -237,6 +259,7 @@ pub(crate) fn expand_include_bytes(
             // Don't care about getting the span for the raw bytes,
             // because the console can't really show them anyway.
             let expr = cx.expr(sp, ast::ExprKind::IncludedBytes(ByteSymbol::intern(&bytes)));
+            // MacEager converts the expr into a pat if need be.
             MacEager::expr(expr)
         }
         Err(dummy) => dummy,
diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs
index a9d91f77560..51089e5a1d3 100644
--- a/compiler/rustc_builtin_macros/src/test_harness.rs
+++ b/compiler/rustc_builtin_macros/src/test_harness.rs
@@ -11,7 +11,6 @@ use rustc_errors::DiagCtxtHandle;
 use rustc_expand::base::{ExtCtxt, ResolverExpand};
 use rustc_expand::expand::{AstFragment, ExpansionConfig};
 use rustc_feature::Features;
-use rustc_lint_defs::BuiltinLintDiag;
 use rustc_session::Session;
 use rustc_session::lint::builtin::UNNAMEABLE_TEST_ITEMS;
 use rustc_span::hygiene::{AstPass, SyntaxContext, Transparency};
@@ -165,7 +164,7 @@ impl<'a> Visitor<'a> for InnerItemLinter<'_> {
                 UNNAMEABLE_TEST_ITEMS,
                 attr.span,
                 i.id,
-                BuiltinLintDiag::UnnameableTestItems,
+                errors::UnnameableTestItems,
             );
         }
     }
diff --git a/compiler/rustc_builtin_macros/src/util.rs b/compiler/rustc_builtin_macros/src/util.rs
index 3a4585d5be9..e26f31dce67 100644
--- a/compiler/rustc_builtin_macros/src/util.rs
+++ b/compiler/rustc_builtin_macros/src/util.rs
@@ -5,7 +5,6 @@ use rustc_errors::{Applicability, Diag, ErrorGuaranteed};
 use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt};
 use rustc_expand::expand::AstFragment;
 use rustc_feature::AttributeTemplate;
-use rustc_lint_defs::BuiltinLintDiag;
 use rustc_lint_defs::builtin::DUPLICATE_MACRO_ATTRIBUTES;
 use rustc_parse::{exp, parser};
 use rustc_session::errors::report_lit_error;
@@ -49,7 +48,7 @@ pub(crate) fn warn_on_duplicate_attribute(ecx: &ExtCtxt<'_>, item: &Annotatable,
                 DUPLICATE_MACRO_ATTRIBUTES,
                 attr.span,
                 ecx.current_expansion.lint_node_id,
-                BuiltinLintDiag::DuplicateMacroAttribute,
+                errors::DuplicateMacroAttribute,
             );
         }
     }