mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-26 20:09:19 +00:00
internal: refactor macro error
This commit is contained in:
parent
1e4aaee7bb
commit
00303284b5
7 changed files with 178 additions and 198 deletions
|
@ -37,6 +37,7 @@ diagnostics![
|
||||||
UnresolvedImport,
|
UnresolvedImport,
|
||||||
UnresolvedMacroCall,
|
UnresolvedMacroCall,
|
||||||
UnresolvedProcMacro,
|
UnresolvedProcMacro,
|
||||||
|
MacroError,
|
||||||
MissingFields,
|
MissingFields,
|
||||||
InactiveCode,
|
InactiveCode,
|
||||||
];
|
];
|
||||||
|
@ -79,35 +80,12 @@ pub struct UnresolvedProcMacro {
|
||||||
pub macro_name: Option<String>,
|
pub macro_name: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Diagnostic: macro-error
|
|
||||||
//
|
|
||||||
// This diagnostic is shown for macro expansion errors.
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub struct MacroError {
|
pub struct MacroError {
|
||||||
pub file: HirFileId,
|
pub node: InFile<SyntaxNodePtr>,
|
||||||
pub node: SyntaxNodePtr,
|
|
||||||
pub message: String,
|
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)]
|
#[derive(Debug)]
|
||||||
pub struct UnimplementedBuiltinMacro {
|
pub struct UnimplementedBuiltinMacro {
|
||||||
pub file: HirFileId,
|
pub file: HirFileId,
|
||||||
|
|
|
@ -587,19 +587,19 @@ impl Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
DefDiagnosticKind::MacroError { ast, message } => {
|
DefDiagnosticKind::MacroError { ast, message } => {
|
||||||
let (file, ast) = match ast {
|
let node = match ast {
|
||||||
MacroCallKind::FnLike { ast_id, .. } => {
|
MacroCallKind::FnLike { ast_id, .. } => {
|
||||||
let node = ast_id.to_node(db.upcast());
|
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::Derive { ast_id, .. }
|
||||||
| MacroCallKind::Attr { ast_id, .. } => {
|
| MacroCallKind::Attr { ast_id, .. } => {
|
||||||
// FIXME: point to the attribute instead, this creates very large diagnostics
|
// FIXME: point to the attribute instead, this creates very large diagnostics
|
||||||
let node = ast_id.to_node(db.upcast());
|
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 } => {
|
DefDiagnosticKind::UnimplementedBuiltinMacro { ast } => {
|
||||||
|
@ -1046,11 +1046,13 @@ impl Function {
|
||||||
InactiveCode { node: node.clone(), cfg: cfg.clone(), opts: opts.clone() }
|
InactiveCode { node: node.clone(), cfg: cfg.clone(), opts: opts.clone() }
|
||||||
.into(),
|
.into(),
|
||||||
),
|
),
|
||||||
BodyDiagnostic::MacroError { node, message } => sink.push(MacroError {
|
BodyDiagnostic::MacroError { node, message } => acc.push(
|
||||||
file: node.file_id,
|
MacroError {
|
||||||
node: node.value.clone().into(),
|
node: node.clone().map(|it| it.into()),
|
||||||
message: message.to_string(),
|
message: message.to_string(),
|
||||||
}),
|
}
|
||||||
|
.into(),
|
||||||
|
),
|
||||||
BodyDiagnostic::UnresolvedProcMacro { node } => acc.push(
|
BodyDiagnostic::UnresolvedProcMacro { node } => acc.push(
|
||||||
UnresolvedProcMacro {
|
UnresolvedProcMacro {
|
||||||
node: node.clone().map(|it| it.into()),
|
node: node.clone().map(|it| it.into()),
|
||||||
|
|
|
@ -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]
|
#[test]
|
||||||
fn unresolved_macro_diag() {
|
fn unresolved_macro_diag() {
|
||||||
check_diagnostics(
|
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
|
|
||||||
}
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
|
@ -2,7 +2,6 @@ mod globs;
|
||||||
mod incremental;
|
mod incremental;
|
||||||
mod macros;
|
mod macros;
|
||||||
mod mod_resolution;
|
mod mod_resolution;
|
||||||
mod diagnostics;
|
|
||||||
mod primitives;
|
mod primitives;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -9,6 +9,7 @@ mod unresolved_extern_crate;
|
||||||
mod unresolved_import;
|
mod unresolved_import;
|
||||||
mod unresolved_macro_call;
|
mod unresolved_macro_call;
|
||||||
mod unresolved_proc_macro;
|
mod unresolved_proc_macro;
|
||||||
|
mod macro_error;
|
||||||
mod inactive_code;
|
mod inactive_code;
|
||||||
mod missing_fields;
|
mod missing_fields;
|
||||||
|
|
||||||
|
@ -229,6 +230,7 @@ pub(crate) fn diagnostics(
|
||||||
AnyDiagnostic::UnresolvedMacroCall(d) => unresolved_macro_call::unresolved_macro_call(&ctx, &d),
|
AnyDiagnostic::UnresolvedMacroCall(d) => unresolved_macro_call::unresolved_macro_call(&ctx, &d),
|
||||||
AnyDiagnostic::UnresolvedProcMacro(d) => unresolved_proc_macro::unresolved_proc_macro(&ctx, &d),
|
AnyDiagnostic::UnresolvedProcMacro(d) => unresolved_proc_macro::unresolved_proc_macro(&ctx, &d),
|
||||||
AnyDiagnostic::MissingFields(d) => missing_fields::missing_fields(&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) {
|
AnyDiagnostic::InactiveCode(d) => match inactive_code::inactive_code(&ctx, &d) {
|
||||||
Some(it) => it,
|
Some(it) => it,
|
||||||
|
|
163
crates/ide/src/diagnostics/macro_error.rs
Normal file
163
crates/ide/src/diagnostics/macro_error.rs
Normal 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
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue