internal: refactor macro error

This commit is contained in:
Aleksey Kladov 2021-06-13 18:41:04 +03:00
parent 1e4aaee7bb
commit 00303284b5
7 changed files with 178 additions and 198 deletions

View File

@ -37,6 +37,7 @@ diagnostics![
UnresolvedImport,
UnresolvedMacroCall,
UnresolvedProcMacro,
MacroError,
MissingFields,
InactiveCode,
];
@ -79,35 +80,12 @@ pub struct UnresolvedProcMacro {
pub macro_name: Option<String>,
}
// Diagnostic: macro-error
//
// This diagnostic is shown for macro expansion errors.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct MacroError {
pub file: HirFileId,
pub node: SyntaxNodePtr,
pub node: InFile<SyntaxNodePtr>,
pub message: String,
}
impl Diagnostic for MacroError {
fn code(&self) -> DiagnosticCode {
DiagnosticCode("macro-error")
}
fn message(&self) -> String {
self.message.clone()
}
fn display_source(&self) -> InFile<SyntaxNodePtr> {
InFile::new(self.file, self.node.clone())
}
fn as_any(&self) -> &(dyn Any + Send + 'static) {
self
}
fn is_experimental(&self) -> bool {
// Newly added and not very well-tested, might contain false positives.
true
}
}
#[derive(Debug)]
pub struct UnimplementedBuiltinMacro {
pub file: HirFileId,

View File

@ -587,19 +587,19 @@ impl Module {
}
DefDiagnosticKind::MacroError { ast, message } => {
let (file, ast) = match ast {
let node = match ast {
MacroCallKind::FnLike { ast_id, .. } => {
let node = ast_id.to_node(db.upcast());
(ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node)))
ast_id.with_value(SyntaxNodePtr::from(AstPtr::new(&node)))
}
MacroCallKind::Derive { ast_id, .. }
| MacroCallKind::Attr { ast_id, .. } => {
// FIXME: point to the attribute instead, this creates very large diagnostics
let node = ast_id.to_node(db.upcast());
(ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node)))
ast_id.with_value(SyntaxNodePtr::from(AstPtr::new(&node)))
}
};
sink.push(MacroError { file, node: ast, message: message.clone() });
acc.push(MacroError { node, message: message.clone() }.into());
}
DefDiagnosticKind::UnimplementedBuiltinMacro { ast } => {
@ -1046,11 +1046,13 @@ impl Function {
InactiveCode { node: node.clone(), cfg: cfg.clone(), opts: opts.clone() }
.into(),
),
BodyDiagnostic::MacroError { node, message } => sink.push(MacroError {
file: node.file_id,
node: node.value.clone().into(),
message: message.to_string(),
}),
BodyDiagnostic::MacroError { node, message } => acc.push(
MacroError {
node: node.clone().map(|it| it.into()),
message: message.to_string(),
}
.into(),
),
BodyDiagnostic::UnresolvedProcMacro { node } => acc.push(
UnresolvedProcMacro {
node: node.clone().map(|it| it.into()),

View File

@ -88,67 +88,6 @@ mod m {
);
}
#[test]
fn macro_diag_builtin() {
check_diagnostics(
r#"
#[rustc_builtin_macro]
macro_rules! env {}
#[rustc_builtin_macro]
macro_rules! include {}
#[rustc_builtin_macro]
macro_rules! compile_error {}
#[rustc_builtin_macro]
macro_rules! format_args {
() => {}
}
fn f() {
// Test a handful of built-in (eager) macros:
include!(invalid);
//^^^^^^^^^^^^^^^^^ could not convert tokens
include!("does not exist");
//^^^^^^^^^^^^^^^^^^^^^^^^^^ failed to load file `does not exist`
env!(invalid);
//^^^^^^^^^^^^^ could not convert tokens
env!("OUT_DIR");
//^^^^^^^^^^^^^^^ `OUT_DIR` not set, enable "run build scripts" to fix
compile_error!("compile_error works");
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ compile_error works
// Lazy:
format_args!();
//^^^^^^^^^^^^^^ no rule matches input tokens
}
"#,
);
}
#[test]
fn macro_rules_diag() {
check_diagnostics(
r#"
macro_rules! m {
() => {};
}
fn f() {
m!();
m!(hi);
//^^^^^^ leftover tokens
}
"#,
);
}
#[test]
fn unresolved_macro_diag() {
check_diagnostics(
@ -161,30 +100,3 @@ fn f() {
);
}
#[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!();
//^^^^^^^^ leftover tokens
}
"#,
)
}

View File

@ -2,7 +2,6 @@ mod globs;
mod incremental;
mod macros;
mod mod_resolution;
mod diagnostics;
mod primitives;
use std::sync::Arc;

View File

@ -1,76 +0,0 @@
use base_db::fixture::WithFixture;
use crate::test_db::TestDB;
fn check_diagnostics(ra_fixture: &str) {
let db: TestDB = TestDB::with_files(ra_fixture);
db.check_diagnostics();
}
fn check_no_diagnostics(ra_fixture: &str) {
let db: TestDB = TestDB::with_files(ra_fixture);
db.check_no_diagnostics();
}
#[test]
fn builtin_macro_fails_expansion() {
check_diagnostics(
r#"
//- /lib.rs
#[rustc_builtin_macro]
macro_rules! include { () => {} }
include!("doesntexist");
//^^^^^^^^^^^^^^^^^^^^^^^^ failed to load file `doesntexist`
"#,
);
}
#[test]
fn include_macro_should_allow_empty_content() {
check_no_diagnostics(
r#"
//- /lib.rs
#[rustc_builtin_macro]
macro_rules! include { () => {} }
include!("bar.rs");
//- /bar.rs
// empty
"#,
);
}
#[test]
fn good_out_dir_diagnostic() {
check_diagnostics(
r#"
#[rustc_builtin_macro]
macro_rules! include { () => {} }
#[rustc_builtin_macro]
macro_rules! env { () => {} }
#[rustc_builtin_macro]
macro_rules! concat { () => {} }
include!(concat!(env!("OUT_DIR"), "/out.rs"));
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `OUT_DIR` not set, enable "run build scripts" to fix
"#,
);
}
#[test]
fn register_attr_and_tool() {
cov_mark::check!(register_attr);
cov_mark::check!(register_tool);
check_no_diagnostics(
r#"
#![register_tool(tool)]
#![register_attr(attr)]
#[tool::path]
#[attr]
struct S;
"#,
);
// NB: we don't currently emit diagnostics here
}

View File

@ -9,6 +9,7 @@ mod unresolved_extern_crate;
mod unresolved_import;
mod unresolved_macro_call;
mod unresolved_proc_macro;
mod macro_error;
mod inactive_code;
mod missing_fields;
@ -229,6 +230,7 @@ pub(crate) fn diagnostics(
AnyDiagnostic::UnresolvedMacroCall(d) => unresolved_macro_call::unresolved_macro_call(&ctx, &d),
AnyDiagnostic::UnresolvedProcMacro(d) => unresolved_proc_macro::unresolved_proc_macro(&ctx, &d),
AnyDiagnostic::MissingFields(d) => missing_fields::missing_fields(&ctx, &d),
AnyDiagnostic::MacroError(d) => macro_error::macro_error(&ctx, &d),
AnyDiagnostic::InactiveCode(d) => match inactive_code::inactive_code(&ctx, &d) {
Some(it) => it,

View File

@ -0,0 +1,163 @@
use crate::diagnostics::{Diagnostic, DiagnosticsContext};
// Diagnostic: macro-error
//
// This diagnostic is shown for macro expansion errors.
pub(super) fn macro_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroError) -> Diagnostic {
Diagnostic::new(
"macro-error",
d.message.clone(),
ctx.sema.diagnostics_display_range(d.node.clone()).range,
)
.experimental()
}
#[cfg(test)]
mod tests {
use crate::diagnostics::tests::{check_diagnostics, check_no_diagnostics};
#[test]
fn builtin_macro_fails_expansion() {
check_diagnostics(
r#"
#[rustc_builtin_macro]
macro_rules! include { () => {} }
include!("doesntexist");
//^^^^^^^^^^^^^^^^^^^^^^^^ failed to load file `doesntexist`
"#,
);
}
#[test]
fn include_macro_should_allow_empty_content() {
check_diagnostics(
r#"
//- /lib.rs
#[rustc_builtin_macro]
macro_rules! include { () => {} }
include!("foo/bar.rs");
//- /foo/bar.rs
// empty
"#,
);
}
#[test]
fn good_out_dir_diagnostic() {
check_diagnostics(
r#"
#[rustc_builtin_macro]
macro_rules! include { () => {} }
#[rustc_builtin_macro]
macro_rules! env { () => {} }
#[rustc_builtin_macro]
macro_rules! concat { () => {} }
include!(concat!(env!("OUT_DIR"), "/out.rs"));
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `OUT_DIR` not set, enable "run build scripts" to fix
"#,
);
}
#[test]
fn register_attr_and_tool() {
cov_mark::check!(register_attr);
cov_mark::check!(register_tool);
check_no_diagnostics(
r#"
#![register_tool(tool)]
#![register_attr(attr)]
#[tool::path]
#[attr]
struct S;
"#,
);
// NB: we don't currently emit diagnostics here
}
#[test]
fn macro_diag_builtin() {
check_diagnostics(
r#"
#[rustc_builtin_macro]
macro_rules! env {}
#[rustc_builtin_macro]
macro_rules! include {}
#[rustc_builtin_macro]
macro_rules! compile_error {}
#[rustc_builtin_macro]
macro_rules! format_args { () => {} }
fn main() {
// Test a handful of built-in (eager) macros:
include!(invalid);
//^^^^^^^^^^^^^^^^^ could not convert tokens
include!("does not exist");
//^^^^^^^^^^^^^^^^^^^^^^^^^^ failed to load file `does not exist`
env!(invalid);
//^^^^^^^^^^^^^ could not convert tokens
env!("OUT_DIR");
//^^^^^^^^^^^^^^^ `OUT_DIR` not set, enable "run build scripts" to fix
compile_error!("compile_error works");
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ compile_error works
// Lazy:
format_args!();
//^^^^^^^^^^^^^^ no rule matches input tokens
}
"#,
);
}
#[test]
fn macro_rules_diag() {
check_diagnostics(
r#"
macro_rules! m {
() => {};
}
fn f() {
m!();
m!(hi);
//^^^^^^ 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!();
} //^^^^^^^^ leftover tokens
"#,
)
}
}