use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, Severity}; // Diagnostic: macro-error // // This diagnostic is shown for macro expansion errors. // Diagnostic: attribute-expansion-disabled // // This diagnostic is shown for attribute proc macros when attribute expansions have been disabled. // Diagnostic: proc-macro-disabled // // This diagnostic is shown for proc macros that have been specifically disabled via `rust-analyzer.procMacro.ignored`. pub(crate) fn macro_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroError) -> Diagnostic { // Use more accurate position if available. let display_range = ctx.sema.diagnostics_display_range_for_range(d.range); Diagnostic::new( DiagnosticCode::Ra(d.kind, if d.error { Severity::Error } else { Severity::WeakWarning }), d.message.clone(), display_range, ) .stable() } // Diagnostic: macro-def-error // // This diagnostic is shown for macro expansion errors. pub(crate) fn macro_def_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroDefError) -> Diagnostic { // Use more accurate position if available. let display_range = match d.name { Some(name) => ctx.sema.diagnostics_display_range_for_range(d.node.with_value(name)), None => ctx.sema.diagnostics_display_range(d.node.map(|it| it.syntax_node_ptr())), }; Diagnostic::new( DiagnosticCode::Ra("macro-def-error", Severity::Error), d.message.clone(), display_range, ) .stable() } #[cfg(test)] mod tests { use crate::{ DiagnosticsConfig, tests::{check_diagnostics, check_diagnostics_with_config}, }; #[test] fn builtin_macro_fails_expansion() { check_diagnostics( r#" #[rustc_builtin_macro] macro_rules! include { () => {} } #[rustc_builtin_macro] macro_rules! compile_error { () => {} } include!("doesntexist"); //^^^^^^^^^^^^^ error: failed to load file `doesntexist` compile_error!("compile_error macro works"); //^^^^^^^^^^^^^ error: compile_error macro works compile_error! { "compile_error macro braced works" } //^^^^^^^^^^^^^ error: compile_error macro braced works "#, ); } #[test] fn eager_macro_concat() { check_diagnostics( r#" //- /lib.rs crate:lib deps:core use core::{panic, concat}; mod private { pub use core::concat; } macro_rules! m { () => { panic!(concat!($crate::private::concat!(""))); }; } fn f() { m!(); } //- /core.rs crate:core #[macro_export] #[rustc_builtin_macro] macro_rules! concat { () => {} } pub macro panic { ($msg:expr) => ( $crate::panicking::panic_str($msg) ), } "#, ); } #[test] fn include_macro_should_allow_empty_content() { let mut config = DiagnosticsConfig::test_sample(); // FIXME: This is a false-positive, the file is actually linked in via // `include!` macro config.disabled.insert("unlinked-file".to_owned()); check_diagnostics_with_config( config, r#" //- /lib.rs #[rustc_builtin_macro] macro_rules! include { () => {} } include!("foo/bar.rs"); //- /foo/bar.rs // empty "#, ); } #[test] fn good_out_dir_diagnostic() { // FIXME: The diagnostic here is duplicated for each eager expansion check_diagnostics( r#" #[rustc_builtin_macro] macro_rules! include { () => {} } #[rustc_builtin_macro] macro_rules! env { () => {} } #[rustc_builtin_macro] macro_rules! concat { () => {} } include!(concat!( // ^^^^^^ error: `OUT_DIR` not set, build scripts may have failed to run env!( //^^^ error: `OUT_DIR` not set, build scripts may have failed to run "OUT_DIR"), "/out.rs")); //^^^^^^^^^ error: `OUT_DIR` not set, build scripts may have failed to run "#, ); } #[test] fn register_tool() { cov_mark::check!(register_tool); check_diagnostics( r#" #![register_tool(tool)] #[tool::path] struct S; "#, ); // NB: we don't currently emit diagnostics here } #[test] fn macro_diag_builtin() { check_diagnostics( r#" //- minicore: fmt #[rustc_builtin_macro] macro_rules! env {} #[rustc_builtin_macro] macro_rules! include {} #[rustc_builtin_macro] macro_rules! compile_error {} #[rustc_builtin_macro] macro_rules! concat {} fn main() { // Test a handful of built-in (eager) macros: include!(invalid); //^^^^^^^ error: expected string literal include!("does not exist"); //^^^^^^^^^^^^^^^^ error: failed to load file `does not exist` include!(concat!("does ", "not ", "exist")); // ^^^^^^^^^^^^^^^^^^^^^^^^ error: failed to load file `does not exist` env!(invalid); //^^^^^^^ error: expected string literal env!("OUT_DIR"); //^^^^^^^^^ error: `OUT_DIR` not set, build scripts may have failed to run compile_error!("compile_error works"); //^^^^^^^^^^^^^ error: compile_error works // Lazy: format_args!(); //^^^^^^^^^^^ error: Syntax Error in Expansion: expected expression } "#, ); } #[test] fn macro_rules_diag() { check_diagnostics( r#" macro_rules! m { () => {}; } fn f() { m!(); m!(hi); //^ error: leftover tokens } "#, ); } #[test] fn dollar_crate_in_builtin_macro() { check_diagnostics( r#" #[macro_export] #[rustc_builtin_macro] macro_rules! format_args {} #[macro_export] macro_rules! arg { () => {} } #[macro_export] macro_rules! outer { () => { $crate::format_args!( "", $crate::arg!(1) ) }; } fn f() { outer!(); } //^^^^^^ error: leftover tokens //^^^^^^ error: Syntax Error in Expansion: expected expression "#, ) } #[test] fn def_diagnostic() { check_diagnostics( r#" macro_rules! foo { //^^^ error: expected subtree f => {}; } fn f() { foo!(); //^^^ error: macro definition has parse errors } "#, ) } #[test] fn expansion_syntax_diagnostic() { check_diagnostics( r#" macro_rules! foo { () => { struct; }; } fn f() { foo!(); //^^^ error: Syntax Error in Expansion: expected a name } "#, ) } #[test] fn include_does_not_break_diagnostics() { check_diagnostics( r#" //- minicore: include //- /lib.rs crate:lib include!("include-me.rs"); //- /include-me.rs /// long doc that pushes the diagnostic range beyond the first file's text length #[err] // ^^^ error: unresolved macro `err` mod prim_never {} "#, ); } #[test] fn no_stack_overflow_for_missing_binding() { check_diagnostics( r#" #[macro_export] macro_rules! boom { ( $($code:literal),+, $(param: $param:expr,)? ) => {{ let _ = $crate::boom!(@param $($param)*); }}; (@param) => { () }; (@param $param:expr) => { $param }; } fn it_works() { // NOTE: there is an error, but RA crashes before showing it boom!("RAND", param: c7.clone()); // ^^^^^ error: expected literal } "#, ); } }