diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index b0e0f969d9..d2fd63428b 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -403,17 +403,29 @@ impl<'db> SemanticsImpl<'db> { ) } - pub fn resolve_offset_in_format_args( + pub fn as_format_args_parts( &self, - string: ast::String, - offset: TextSize, - ) -> Option<(TextRange, Option)> { - debug_assert!(offset <= string.syntax().text_range().len()); - let literal = string.syntax().parent().filter(|it| it.kind() == SyntaxKind::LITERAL)?; - let format_args = ast::FormatArgsExpr::cast(literal.parent()?)?; - let source_analyzer = &self.analyze_no_infer(format_args.syntax())?; - let format_args = self.wrap_node_infile(format_args); - source_analyzer.resolve_offset_in_format_args(self.db, format_args.as_ref(), offset) + string: &ast::String, + ) -> Option)>> { + if let Some(quote) = string.open_quote_text_range() { + return self + .descend_into_macros(DescendPreference::SameText, string.syntax().clone()) + .into_iter() + .find_map(|token| { + let string = ast::String::cast(token)?; + let literal = + string.syntax().parent().filter(|it| it.kind() == SyntaxKind::LITERAL)?; + let format_args = ast::FormatArgsExpr::cast(literal.parent()?)?; + let source_analyzer = self.analyze_no_infer(format_args.syntax())?; + let format_args = self.wrap_node_infile(format_args); + let res = source_analyzer + .as_format_args_parts(self.db, format_args.as_ref())? + .map(|(range, res)| (range + quote.end(), res)) + .collect(); + Some(res) + }); + } + None } pub fn check_for_format_args_template( @@ -438,6 +450,19 @@ impl<'db> SemanticsImpl<'db> { None } + fn resolve_offset_in_format_args( + &self, + string: ast::String, + offset: TextSize, + ) -> Option<(TextRange, Option)> { + debug_assert!(offset <= string.syntax().text_range().len()); + let literal = string.syntax().parent().filter(|it| it.kind() == SyntaxKind::LITERAL)?; + let format_args = ast::FormatArgsExpr::cast(literal.parent()?)?; + let source_analyzer = &self.analyze_no_infer(format_args.syntax())?; + let format_args = self.wrap_node_infile(format_args); + source_analyzer.resolve_offset_in_format_args(self.db, format_args.as_ref(), offset) + } + /// Maps a node down by mapping its first and last token down. pub fn descend_node_into_attributes(&self, node: N) -> SmallVec<[N; 1]> { // This might not be the correct way to do this, but it works for now diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index 9fdee209cf..8afa7e0659 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -843,6 +843,29 @@ impl SourceAnalyzer { }) } + pub(crate) fn as_format_args_parts<'a>( + &'a self, + db: &'a dyn HirDatabase, + format_args: InFile<&ast::FormatArgsExpr>, + ) -> Option)> + 'a> { + Some(self.body_source_map()?.implicit_format_args(format_args)?.iter().map( + move |(range, name)| { + ( + *range, + resolve_hir_value_path( + db, + &self.resolver, + self.resolver.body_owner(), + &Path::from_known_path_with_no_generic(ModPath::from_segments( + PathKind::Plain, + Some(name.clone()), + )), + ), + ) + }, + )) + } + fn resolve_impl_method_or_trait_def( &self, db: &dyn HirDatabase, diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs index ac14afa132..45582f54b4 100644 --- a/crates/ide/src/syntax_highlighting.rs +++ b/crates/ide/src/syntax_highlighting.rs @@ -440,7 +440,7 @@ fn traverse( { continue; } - highlight_format_string(hl, &string, &expanded_string, range); + highlight_format_string(hl, sema, krate, &string, &expanded_string, range); if !string.is_raw() { highlight_escape_string(hl, &string, range.start()); diff --git a/crates/ide/src/syntax_highlighting/format.rs b/crates/ide/src/syntax_highlighting/format.rs index 2ef1315945..518e714547 100644 --- a/crates/ide/src/syntax_highlighting/format.rs +++ b/crates/ide/src/syntax_highlighting/format.rs @@ -1,14 +1,20 @@ //! Syntax highlighting for format macro strings. use ide_db::{ + defs::Definition, syntax_helpers::format_string::{is_format_string, lex_format_specifiers, FormatSpecifier}, SymbolKind, }; use syntax::{ast, TextRange}; -use crate::{syntax_highlighting::highlights::Highlights, HlRange, HlTag}; +use crate::{ + syntax_highlighting::{highlight::highlight_def, highlights::Highlights}, + HlRange, HlTag, +}; pub(super) fn highlight_format_string( stack: &mut Highlights, + sema: &hir::Semantics<'_, ide_db::RootDatabase>, + krate: hir::Crate, string: &ast::String, expanded_string: &ast::String, range: TextRange, @@ -27,6 +33,18 @@ pub(super) fn highlight_format_string( }); } }); + + if let Some(parts) = sema.as_format_args_parts(string) { + parts.into_iter().for_each(|(range, res)| { + if let Some(res) = res { + stack.add(HlRange { + range, + highlight: highlight_def(sema, krate, Definition::from(res)), + binding_hash: None, + }) + } + }) + } } fn highlight_format_specifier(kind: FormatSpecifier) -> Option { diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs index 1bffab29cf..d510c11c3d 100644 --- a/crates/ide/src/syntax_highlighting/highlight.rs +++ b/crates/ide/src/syntax_highlighting/highlight.rs @@ -348,7 +348,7 @@ fn calc_binding_hash(name: &hir::Name, shadow_count: u32) -> u64 { hash((name, shadow_count)) } -fn highlight_def( +pub(super) fn highlight_def( sema: &Semantics<'_, RootDatabase>, krate: hir::Crate, def: Definition, diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html b/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html index 06b66b302a..b8d38a60fc 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html @@ -90,17 +90,11 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd } } -#[rustc_builtin_macro] -macro_rules! concat {} -#[rustc_builtin_macro] -macro_rules! include {} -#[rustc_builtin_macro] -macro_rules! format_args {} -include!(concat!("foo/", "foo.rs")); +include!(concat!("foo/", "foo.rs")); fn main() { - format_args!("Hello, {}!", 92); + format_args!("Hello, {}!", 92); dont_color_me_braces!(); noop!(noop!(1)); } diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html index aa79cd9b82..b40295684d 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html @@ -48,42 +48,29 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd $crate::io::_print(format_args_nl!($($arg)*)); }) } -#[rustc_builtin_macro] -#[macro_export] -macro_rules! format_args_nl {} mod panic { pub macro panic_2015 { () => ( - $crate::panicking::panic("explicit panic") + panic("explicit panic") ), ($msg:literal $(,)?) => ( - $crate::panicking::panic($msg) + panic($msg) ), // Use `panic_str` instead of `panic_display::<&str>` for non_fmt_panic lint. ($msg:expr $(,)?) => ( - $crate::panicking::panic_str($msg) + panic_str($msg) ), // Special-case the single-argument case for const_panic. ("{}", $arg:expr $(,)?) => ( - $crate::panicking::panic_display(&$arg) + panic_display(&$arg) ), ($fmt:expr, $($arg:tt)+) => ( - $crate::panicking::panic_fmt(const_format_args!($fmt, $($arg)+)) + panic_fmt(const_format_args!($fmt, $($arg)+)) ), } } -#[rustc_builtin_macro(std_panic)] -#[macro_export] -macro_rules! panic {} -#[rustc_builtin_macro] -macro_rules! assert {} -#[rustc_builtin_macro] -macro_rules! asm {} -#[rustc_builtin_macro] -macro_rules! concat {} - macro_rules! toho { () => ($crate::panic!("not yet implemented")); ($($arg:tt)+) => ($crate::panic!("not yet implemented: {}", format_args!($($arg)+))); @@ -165,20 +152,22 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd println!("{ничоси}", ничоси = 92); println!("{:x?} {} ", thingy, n2); - panic!("{}", 0); - panic!("more {}", 1); - assert!(true, "{}", 1); - assert!(true, "{} asdasd", 1); + panic!("{}", 0); + panic!("more {}", 1); + assert!(true, "{}", 1); + assert!(true, "{} asdasd", 1); toho!("{}fmt", 0); let i: u64 = 3; let o: u64; - asm!( - "mov {0}, {1}", - "add {0}, 5", + asm!( + "mov {0}, {1}", + "add {0}, 5", out(reg) o, in(reg) i, ); - format_args!(concat!("{}"), "{}"); - format_args!("{} {} {} {} {} {}", backslash, format_args!("{}", 0), foo, "bar", toho!(), backslash); + const CONSTANT: () = (): + let mut m = (); + format_args!(concat!("{}"), "{}"); + format_args!("{} {} {} {} {} {} {backslash} {CONSTANT} {m}", backslash, format_args!("{}", 0), foo, "bar", toho!(), backslash); } \ No newline at end of file diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs index 542d899253..935b6b2cb9 100644 --- a/crates/ide/src/syntax_highlighting/tests.rs +++ b/crates/ide/src/syntax_highlighting/tests.rs @@ -48,6 +48,7 @@ fn macros() { check_highlighting( r#" //- proc_macros: mirror +//- minicore: fmt, include, concat //- /lib.rs crate:lib proc_macros::mirror! { { @@ -96,12 +97,6 @@ macro without_args { } } -#[rustc_builtin_macro] -macro_rules! concat {} -#[rustc_builtin_macro] -macro_rules! include {} -#[rustc_builtin_macro] -macro_rules! format_args {} include!(concat!("foo/", "foo.rs")); @@ -401,48 +396,35 @@ fn test_string_highlighting() { // thus, we have to copy the macro definition from `std` check_highlighting( r#" -//- minicore: fmt +//- minicore: fmt, assert, asm, concat, panic macro_rules! println { ($($arg:tt)*) => ({ $crate::io::_print(format_args_nl!($($arg)*)); }) } -#[rustc_builtin_macro] -#[macro_export] -macro_rules! format_args_nl {} mod panic { pub macro panic_2015 { () => ( - $crate::panicking::panic("explicit panic") + panic("explicit panic") ), ($msg:literal $(,)?) => ( - $crate::panicking::panic($msg) + panic($msg) ), // Use `panic_str` instead of `panic_display::<&str>` for non_fmt_panic lint. ($msg:expr $(,)?) => ( - $crate::panicking::panic_str($msg) + panic_str($msg) ), // Special-case the single-argument case for const_panic. ("{}", $arg:expr $(,)?) => ( - $crate::panicking::panic_display(&$arg) + panic_display(&$arg) ), ($fmt:expr, $($arg:tt)+) => ( - $crate::panicking::panic_fmt(const_format_args!($fmt, $($arg)+)) + panic_fmt(const_format_args!($fmt, $($arg)+)) ), } } -#[rustc_builtin_macro(std_panic)] -#[macro_export] -macro_rules! panic {} -#[rustc_builtin_macro] -macro_rules! assert {} -#[rustc_builtin_macro] -macro_rules! asm {} -#[rustc_builtin_macro] -macro_rules! concat {} - macro_rules! toho { () => ($crate::panic!("not yet implemented")); ($($arg:tt)+) => ($crate::panic!("not yet implemented: {}", format_args!($($arg)+))); @@ -538,8 +520,10 @@ fn main() { in(reg) i, ); + const CONSTANT: () = (): + let mut m = (); format_args!(concat!("{}"), "{}"); - format_args!("{} {} {} {} {} {}", backslash, format_args!("{}", 0), foo, "bar", toho!(), backslash); + format_args!("{} {} {} {} {} {} {backslash} {CONSTANT} {m}", backslash, format_args!("{}", 0), foo, "bar", toho!(), backslash); }"#, expect_file!["./test_data/highlight_strings.html"], false, diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs index d39d62f362..f766747d70 100644 --- a/crates/test-utils/src/minicore.rs +++ b/crates/test-utils/src/minicore.rs @@ -9,6 +9,8 @@ //! //! Available flags: //! add: +//! asm: +//! assert: //! as_ref: sized //! bool_impl: option, fn //! builtin_impls: @@ -1366,6 +1368,26 @@ mod macros { } // endregion:panic + // region:asm + #[macro_export] + #[rustc_builtin_macro] + macro_rules! asm { + ($($arg:tt)*) => { + /* compiler built-in */ + }; + } + // endregion:asm + + // region:assert + #[macro_export] + #[rustc_builtin_macro] + macro_rules! assert { + ($($arg:tt)*) => { + /* compiler built-in */ + }; + } + // endregion:assert + // region:fmt #[macro_export] #[rustc_builtin_macro] @@ -1381,6 +1403,13 @@ mod macros { ($fmt:expr, $($args:tt)*) => {{ /* compiler built-in */ }}; } + #[macro_export] + #[rustc_builtin_macro] + macro_rules! format_args_nl { + ($fmt:expr) => {{ /* compiler built-in */ }}; + ($fmt:expr, $($args:tt)*) => {{ /* compiler built-in */ }}; + } + #[macro_export] macro_rules! print { ($($arg:tt)*) => {{