Skip to content

Commit 87e3500

Browse files
Merge #6765
6765: Fix file range computation in macros r=jonas-schievink a=jonas-schievink This also aligns the diagnostics behavior of `TestDB` with the one from the real IDE (by making the logic from `semantics.rs` a method on `InFile<&SyntaxNode>`), which makes bugs like this easier to find. This should fix the misplaced diagnostics seen in #6747 and other issues. bors r+ Co-authored-by: Jonas Schievink <[email protected]>
2 parents b01981e + da50271 commit 87e3500

File tree

6 files changed

+89
-98
lines changed

6 files changed

+89
-98
lines changed

crates/hir/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ pub use crate::{
3939
Struct, Trait, Type, TypeAlias, TypeParam, Union, VariantDef,
4040
},
4141
has_source::HasSource,
42-
semantics::{original_range, PathResolution, Semantics, SemanticsScope},
42+
semantics::{PathResolution, Semantics, SemanticsScope},
4343
};
4444

4545
pub use hir_def::{

crates/hir/src/semantics.rs

Lines changed: 4 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,7 @@ use hir_expand::{hygiene::Hygiene, name::AsName, ExpansionInfo};
1313
use hir_ty::associated_type_shorthand_candidates;
1414
use itertools::Itertools;
1515
use rustc_hash::{FxHashMap, FxHashSet};
16-
use syntax::{
17-
algo::{find_node_at_offset, skip_trivia_token},
18-
ast, AstNode, Direction, SyntaxNode, SyntaxToken, TextRange, TextSize,
19-
};
16+
use syntax::{algo::find_node_at_offset, ast, AstNode, SyntaxNode, SyntaxToken, TextSize};
2017

2118
use crate::{
2219
code_model::Access,
@@ -25,7 +22,7 @@ use crate::{
2522
semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
2623
source_analyzer::{resolve_hir_path, SourceAnalyzer},
2724
AssocItem, Callable, Crate, Field, Function, HirFileId, ImplDef, InFile, Local, MacroDef,
28-
Module, ModuleDef, Name, Origin, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam, VariantDef,
25+
Module, ModuleDef, Name, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam, VariantDef,
2926
};
3027

3128
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -372,15 +369,15 @@ impl<'db> SemanticsImpl<'db> {
372369

373370
fn original_range(&self, node: &SyntaxNode) -> FileRange {
374371
let node = self.find_file(node.clone());
375-
original_range(self.db, node.as_ref())
372+
node.as_ref().original_file_range(self.db.upcast())
376373
}
377374

378375
fn diagnostics_display_range(&self, diagnostics: &dyn Diagnostic) -> FileRange {
379376
let src = diagnostics.display_source();
380377
let root = self.db.parse_or_expand(src.file_id).unwrap();
381378
let node = src.value.to_node(&root);
382379
self.cache(root, src.file_id);
383-
original_range(self.db, src.with_value(&node))
380+
src.with_value(&node).original_file_range(self.db.upcast())
384381
}
385382

386383
fn ancestors_with_macros(&self, node: SyntaxNode) -> impl Iterator<Item = SyntaxNode> + '_ {
@@ -771,68 +768,3 @@ impl<'a> SemanticsScope<'a> {
771768
resolve_hir_path(self.db, &self.resolver, &path)
772769
}
773770
}
774-
775-
// FIXME: Change `HasSource` trait to work with `Semantics` and remove this?
776-
pub fn original_range(db: &dyn HirDatabase, node: InFile<&SyntaxNode>) -> FileRange {
777-
if let Some(range) = original_range_opt(db, node) {
778-
let original_file = range.file_id.original_file(db.upcast());
779-
if range.file_id == original_file.into() {
780-
return FileRange { file_id: original_file, range: range.value };
781-
}
782-
783-
log::error!("Fail to mapping up more for {:?}", range);
784-
return FileRange { file_id: range.file_id.original_file(db.upcast()), range: range.value };
785-
}
786-
787-
// Fall back to whole macro call
788-
if let Some(expansion) = node.file_id.expansion_info(db.upcast()) {
789-
if let Some(call_node) = expansion.call_node() {
790-
return FileRange {
791-
file_id: call_node.file_id.original_file(db.upcast()),
792-
range: call_node.value.text_range(),
793-
};
794-
}
795-
}
796-
797-
FileRange { file_id: node.file_id.original_file(db.upcast()), range: node.value.text_range() }
798-
}
799-
800-
fn original_range_opt(
801-
db: &dyn HirDatabase,
802-
node: InFile<&SyntaxNode>,
803-
) -> Option<InFile<TextRange>> {
804-
let expansion = node.file_id.expansion_info(db.upcast())?;
805-
806-
// the input node has only one token ?
807-
let single = skip_trivia_token(node.value.first_token()?, Direction::Next)?
808-
== skip_trivia_token(node.value.last_token()?, Direction::Prev)?;
809-
810-
Some(node.value.descendants().find_map(|it| {
811-
let first = skip_trivia_token(it.first_token()?, Direction::Next)?;
812-
let first = ascend_call_token(db, &expansion, node.with_value(first))?;
813-
814-
let last = skip_trivia_token(it.last_token()?, Direction::Prev)?;
815-
let last = ascend_call_token(db, &expansion, node.with_value(last))?;
816-
817-
if (!single && first == last) || (first.file_id != last.file_id) {
818-
return None;
819-
}
820-
821-
Some(first.with_value(first.value.text_range().cover(last.value.text_range())))
822-
})?)
823-
}
824-
825-
fn ascend_call_token(
826-
db: &dyn HirDatabase,
827-
expansion: &ExpansionInfo,
828-
token: InFile<SyntaxToken>,
829-
) -> Option<InFile<SyntaxToken>> {
830-
let (mapped, origin) = expansion.map_token_up(token.as_ref())?;
831-
if origin != Origin::Call {
832-
return None;
833-
}
834-
if let Some(info) = mapped.file_id.expansion_info(db.upcast()) {
835-
return ascend_call_token(db, &info, mapped);
836-
}
837-
Some(mapped)
838-
}

crates/hir_def/src/test_db.rs

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -158,17 +158,11 @@ impl TestDB {
158158
let src = d.display_source();
159159
let root = db.parse_or_expand(src.file_id).unwrap();
160160

161-
// Place all diagnostics emitted in macro files on the original caller.
162-
// Note that this does *not* match IDE behavior.
163-
let mut src = src.map(|ptr| ptr.to_node(&root));
164-
while let Some(exp) = src.file_id.call_node(db) {
165-
src = exp;
166-
}
161+
let node = src.map(|ptr| ptr.to_node(&root));
162+
let frange = node.as_ref().original_file_range(db);
167163

168-
let file_id = src.file_id.original_file(db);
169-
let range = src.value.text_range();
170164
let message = d.message().to_owned();
171-
actual.entry(file_id).or_default().push((range, message));
165+
actual.entry(frange.file_id).or_default().push((frange.range, message));
172166
});
173167

174168
for (file_id, diags) in actual.iter_mut() {

crates/hir_expand/src/lib.rs

Lines changed: 67 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ pub use mbe::{ExpandError, ExpandResult};
2020
use std::hash::Hash;
2121
use std::sync::Arc;
2222

23-
use base_db::{impl_intern_key, salsa, CrateId, FileId};
23+
use base_db::{impl_intern_key, salsa, CrateId, FileId, FileRange};
2424
use syntax::{
25-
algo,
25+
algo::{self, skip_trivia_token},
2626
ast::{self, AstNode},
27-
SyntaxNode, SyntaxToken, TextSize,
27+
Direction, SyntaxNode, SyntaxToken, TextRange, TextSize,
2828
};
2929

3030
use crate::ast_id_map::FileAstId;
@@ -445,6 +445,70 @@ impl InFile<SyntaxNode> {
445445
}
446446
}
447447

448+
impl<'a> InFile<&'a SyntaxNode> {
449+
pub fn original_file_range(self, db: &dyn db::AstDatabase) -> FileRange {
450+
if let Some(range) = original_range_opt(db, self) {
451+
let original_file = range.file_id.original_file(db);
452+
if range.file_id == original_file.into() {
453+
return FileRange { file_id: original_file, range: range.value };
454+
}
455+
456+
log::error!("Fail to mapping up more for {:?}", range);
457+
return FileRange { file_id: range.file_id.original_file(db), range: range.value };
458+
}
459+
460+
// Fall back to whole macro call.
461+
let mut node = self.cloned();
462+
while let Some(call_node) = node.file_id.call_node(db) {
463+
node = call_node;
464+
}
465+
466+
let orig_file = node.file_id.original_file(db);
467+
assert_eq!(node.file_id, orig_file.into());
468+
FileRange { file_id: orig_file, range: node.value.text_range() }
469+
}
470+
}
471+
472+
fn original_range_opt(
473+
db: &dyn db::AstDatabase,
474+
node: InFile<&SyntaxNode>,
475+
) -> Option<InFile<TextRange>> {
476+
let expansion = node.file_id.expansion_info(db)?;
477+
478+
// the input node has only one token ?
479+
let single = skip_trivia_token(node.value.first_token()?, Direction::Next)?
480+
== skip_trivia_token(node.value.last_token()?, Direction::Prev)?;
481+
482+
Some(node.value.descendants().find_map(|it| {
483+
let first = skip_trivia_token(it.first_token()?, Direction::Next)?;
484+
let first = ascend_call_token(db, &expansion, node.with_value(first))?;
485+
486+
let last = skip_trivia_token(it.last_token()?, Direction::Prev)?;
487+
let last = ascend_call_token(db, &expansion, node.with_value(last))?;
488+
489+
if (!single && first == last) || (first.file_id != last.file_id) {
490+
return None;
491+
}
492+
493+
Some(first.with_value(first.value.text_range().cover(last.value.text_range())))
494+
})?)
495+
}
496+
497+
fn ascend_call_token(
498+
db: &dyn db::AstDatabase,
499+
expansion: &ExpansionInfo,
500+
token: InFile<SyntaxToken>,
501+
) -> Option<InFile<SyntaxToken>> {
502+
let (mapped, origin) = expansion.map_token_up(token.as_ref())?;
503+
if origin != Origin::Call {
504+
return None;
505+
}
506+
if let Some(info) = mapped.file_id.expansion_info(db) {
507+
return ascend_call_token(db, &info, mapped);
508+
}
509+
Some(mapped)
510+
}
511+
448512
impl InFile<SyntaxToken> {
449513
pub fn ancestors_with_macros(
450514
self,

crates/ide/src/display/navigation_target.rs

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! FIXME: write short doc here
22
33
use either::Either;
4-
use hir::{original_range, AssocItem, FieldSource, HasSource, InFile, ModuleSource};
4+
use hir::{AssocItem, FieldSource, HasSource, InFile, ModuleSource};
55
use ide_db::base_db::{FileId, SourceDatabase};
66
use ide_db::{defs::Definition, RootDatabase};
77
use syntax::{
@@ -62,7 +62,8 @@ impl NavigationTarget {
6262
pub(crate) fn from_module_to_decl(db: &RootDatabase, module: hir::Module) -> NavigationTarget {
6363
let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default();
6464
if let Some(src) = module.declaration_source(db) {
65-
let frange = original_range(db, src.as_ref().map(|it| it.syntax()));
65+
let node = src.as_ref().map(|it| it.syntax());
66+
let frange = node.original_file_range(db);
6667
let mut res = NavigationTarget::from_syntax(
6768
frange.file_id,
6869
name,
@@ -104,8 +105,8 @@ impl NavigationTarget {
104105
let name =
105106
node.value.name().map(|it| it.text().clone()).unwrap_or_else(|| SmolStr::new("_"));
106107
let focus_range =
107-
node.value.name().map(|it| original_range(db, node.with_value(it.syntax())).range);
108-
let frange = original_range(db, node.map(|it| it.syntax()));
108+
node.value.name().map(|it| node.with_value(it.syntax()).original_file_range(db).range);
109+
let frange = node.map(|it| it.syntax()).original_file_range(db);
109110

110111
NavigationTarget::from_syntax(
111112
frange.file_id,
@@ -124,7 +125,7 @@ impl NavigationTarget {
124125
) -> NavigationTarget {
125126
let name =
126127
named.value.name().map(|it| it.text().clone()).unwrap_or_else(|| SmolStr::new("_"));
127-
let frange = original_range(db, node.map(|it| it.syntax()));
128+
let frange = node.map(|it| it.syntax()).original_file_range(db);
128129

129130
NavigationTarget::from_syntax(
130131
frange.file_id,
@@ -236,7 +237,7 @@ impl ToNav for hir::Module {
236237
(node.syntax(), node.name().map(|it| it.syntax().text_range()))
237238
}
238239
};
239-
let frange = original_range(db, src.with_value(syntax));
240+
let frange = src.with_value(syntax).original_file_range(db);
240241
NavigationTarget::from_syntax(frange.file_id, name, focus, frange.range, syntax.kind())
241242
}
242243
}
@@ -246,14 +247,14 @@ impl ToNav for hir::ImplDef {
246247
let src = self.source(db);
247248
let derive_attr = self.is_builtin_derive(db);
248249
let frange = if let Some(item) = &derive_attr {
249-
original_range(db, item.syntax())
250+
item.syntax().original_file_range(db)
250251
} else {
251-
original_range(db, src.as_ref().map(|it| it.syntax()))
252+
src.as_ref().map(|it| it.syntax()).original_file_range(db)
252253
};
253254
let focus_range = if derive_attr.is_some() {
254255
None
255256
} else {
256-
src.value.self_ty().map(|ty| original_range(db, src.with_value(ty.syntax())).range)
257+
src.value.self_ty().map(|ty| src.with_value(ty.syntax()).original_file_range(db).range)
257258
};
258259

259260
NavigationTarget::from_syntax(
@@ -278,7 +279,7 @@ impl ToNav for hir::Field {
278279
res
279280
}
280281
FieldSource::Pos(it) => {
281-
let frange = original_range(db, src.with_value(it.syntax()));
282+
let frange = src.with_value(it.syntax()).original_file_range(db);
282283
NavigationTarget::from_syntax(
283284
frange.file_id,
284285
"".into(),
@@ -331,7 +332,7 @@ impl ToNav for hir::Local {
331332
}
332333
Either::Right(it) => it.syntax().clone(),
333334
};
334-
let full_range = original_range(db, src.with_value(&node));
335+
let full_range = src.with_value(&node).original_file_range(db);
335336
let name = match self.name(db) {
336337
Some(it) => it.to_string().into(),
337338
None => "".into(),

crates/rust-analyzer/src/cli/analysis_stats.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use std::{
88

99
use hir::{
1010
db::{AstDatabase, DefDatabase, HirDatabase},
11-
original_range, AssocItem, Crate, HasSource, HirDisplay, ModuleDef,
11+
AssocItem, Crate, HasSource, HirDisplay, ModuleDef,
1212
};
1313
use hir_def::FunctionId;
1414
use hir_ty::{Ty, TypeWalk};
@@ -232,7 +232,7 @@ impl AnalysisStatsCmd {
232232
// But also, we should just turn the type mismatches into diagnostics and provide these
233233
let root = db.parse_or_expand(src.file_id).unwrap();
234234
let node = src.map(|e| e.to_node(&root).syntax().clone());
235-
let original_range = original_range(db, node.as_ref());
235+
let original_range = node.as_ref().original_file_range(db);
236236
let path = vfs.file_path(original_range.file_id);
237237
let line_index =
238238
host.analysis().file_line_index(original_range.file_id).unwrap();

0 commit comments

Comments
 (0)