Skip to content

Commit 9f4f771

Browse files
committed
refactor: centralize path-anchor handling in import path resolution
- Added `PathAnchor` helpers in `src/rust_syntax.rs` to classify leading path segments (`crate`, `super`, `self`, `Self`, names) and removed scattered string-comparison dispatch from path rewriting and resolution paths. - Replaced repeated inline `crate`/`super`/`self` handling in compiler and fix modules with `PathAnchor`-driven logic to keep leading-segment interpretation aligned across `src/fixes` and `src/compiler`. - Rewrote two two-arm `match` expressions into combinator form in `src/fixes/runner/apply.rs` and `src/selection/metadata.rs` to use `map_or`/`map_or_else`.
1 parent c84f310 commit 9f4f771

16 files changed

Lines changed: 194 additions & 137 deletions

File tree

src/compiler/facade/exports.rs

Lines changed: 12 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use crate::compiler::settings::DriverSettings;
1818
use crate::compiler::source_cache;
1919
use crate::compiler::source_cache::SourceCache;
2020
use crate::rust_syntax;
21+
use crate::rust_syntax::PathAnchor;
2122

2223
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
2324
pub enum ParentFacadeFixSupport {
@@ -184,11 +185,7 @@ fn parent_boundary_has_matching_pub_use_glob(file: &File, child_module_name: &st
184185
let mut paths = Vec::new();
185186
source_cache::flatten_use_tree(Vec::new(), &item_use.tree, &mut paths);
186187
paths.into_iter().any(|path| {
187-
let normalized = if path.first().is_some_and(|segment| segment == "self") {
188-
&path[1..]
189-
} else {
190-
&path[..]
191-
};
188+
let normalized = rust_syntax::trim_leading_self(&path);
192189
normalized.len() == 2 && normalized[0] == child_module_name && normalized[1] == "*"
193190
})
194191
})
@@ -231,11 +228,7 @@ fn collect_matching_pub_use_exports(
231228
source_cache::flatten_use_tree(Vec::new(), &item_use.tree, &mut paths);
232229
let mut matched = false;
233230
for path in paths {
234-
let normalized = if path.first().is_some_and(|segment| segment == "self") {
235-
&path[1..]
236-
} else {
237-
&path[..]
238-
};
231+
let normalized = rust_syntax::trim_leading_self(&path);
239232
if normalized.len() >= 2
240233
&& normalized[0] == child_module_name
241234
&& normalized[1..].iter().any(|segment| segment == item_name)
@@ -283,11 +276,7 @@ fn pub_use_is_fix_supported_with_prefix(
283276
pub_use_is_fix_supported_with_prefix(next, &path.tree, child_module_name, item_name)
284277
},
285278
UseTree::Name(name) => {
286-
let normalized = if prefix.first().is_some_and(|segment| segment == "self") {
287-
&prefix[1..]
288-
} else {
289-
&prefix[..]
290-
};
279+
let normalized = rust_syntax::trim_leading_self(&prefix);
291280
normalized.len() == 1 && normalized[0] == child_module_name && name.ident == item_name
292281
},
293282
UseTree::Group(group) => group.items.iter().any(|item| {
@@ -300,17 +289,14 @@ fn pub_use_is_fix_supported_with_prefix(
300289
pub(super) fn parent_facade_visibility(vis: &Visibility) -> Option<ParentFacadeVisibility> {
301290
match vis {
302291
Visibility::Public(_) => Some(ParentFacadeVisibility::Public),
303-
Visibility::Restricted(restricted)
304-
if restricted.path.segments.len() == 1
305-
&& restricted.path.segments[0].ident == "super" =>
306-
{
307-
Some(ParentFacadeVisibility::Super)
308-
},
309-
Visibility::Restricted(restricted)
310-
if restricted.path.segments.len() == 1
311-
&& restricted.path.segments[0].ident == "crate" =>
312-
{
313-
Some(ParentFacadeVisibility::Crate)
292+
Visibility::Restricted(restricted) if restricted.path.segments.len() == 1 => {
293+
let path_anchor =
294+
PathAnchor::from(restricted.path.segments[0].ident.to_string().as_str());
295+
match path_anchor {
296+
PathAnchor::Super => Some(ParentFacadeVisibility::Super),
297+
PathAnchor::Crate => Some(ParentFacadeVisibility::Crate),
298+
PathAnchor::SelfMod | PathAnchor::SelfType | PathAnchor::Name => None,
299+
}
314300
},
315301
_ => None,
316302
}

src/compiler/facade/reference.rs

Lines changed: 35 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use crate::compiler::source_cache::ExtractedPaths;
1313
use crate::compiler::source_cache::PathOrigin;
1414
use crate::compiler::source_cache::SourceCache;
1515
use crate::compiler::source_cache::UseRename;
16+
use crate::rust_syntax::PathAnchor;
1617

1718
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1819
pub enum ParentFacadeUsage {
@@ -241,30 +242,38 @@ pub(super) fn resolve_module_relative_paths(
241242
return Vec::new();
242243
}
243244

244-
if raw.first().map(String::as_str) == Some("crate") {
245-
return vec![raw[1..].to_vec()];
246-
}
247-
248-
if raw.first().map(String::as_str) == Some("self") {
249-
let mut resolved = current_module_path.to_vec();
250-
resolved.extend(raw[1..].iter().cloned());
251-
return vec![resolved];
252-
}
253-
254-
if raw.first().map(String::as_str) == Some("super") {
255-
let mut index = 0usize;
256-
let mut resolved = current_module_path.to_vec();
257-
while raw.get(index).is_some_and(|segment| segment == "super") {
258-
if resolved.pop().is_none() {
259-
return Vec::new();
245+
let Some(path_anchor) = PathAnchor::first(raw) else {
246+
return Vec::new();
247+
};
248+
match path_anchor {
249+
PathAnchor::Crate => return vec![raw[1..].to_vec()],
250+
PathAnchor::SelfMod => {
251+
let mut resolved = current_module_path.to_vec();
252+
resolved.extend(raw[1..].iter().cloned());
253+
return vec![resolved];
254+
},
255+
PathAnchor::Super => {
256+
let mut index = 0usize;
257+
let mut resolved = current_module_path.to_vec();
258+
while raw
259+
.get(index)
260+
.is_some_and(|segment| PathAnchor::from(segment.as_str()) == PathAnchor::Super)
261+
{
262+
if resolved.pop().is_none() {
263+
return Vec::new();
264+
}
265+
index += 1;
260266
}
261-
index += 1;
262-
}
263-
if raw.get(index).is_some_and(|segment| segment == "self") {
264-
index += 1;
265-
}
266-
resolved.extend(raw[index..].iter().cloned());
267-
return vec![resolved];
267+
if raw
268+
.get(index)
269+
.is_some_and(|segment| PathAnchor::from(segment.as_str()) == PathAnchor::SelfMod)
270+
{
271+
index += 1;
272+
}
273+
resolved.extend(raw[index..].iter().cloned());
274+
return vec![resolved];
275+
},
276+
PathAnchor::SelfType | PathAnchor::Name => {},
268277
}
269278

270279
(0..=current_module_path.len())
@@ -458,20 +467,20 @@ fn relative_tail_mentions_child_item(
458467
child_module_path: &[String],
459468
item_name: &str,
460469
) -> bool {
461-
if path.first().map(String::as_str) == Some("crate") || child_module_path.is_empty() {
470+
if PathAnchor::first(path) == Some(PathAnchor::Crate) || child_module_path.is_empty() {
462471
return false;
463472
}
464473

465474
let mut tail_start = 0usize;
466475
while path
467476
.get(tail_start)
468-
.is_some_and(|segment| segment == "super")
477+
.is_some_and(|segment| PathAnchor::from(segment.as_str()) == PathAnchor::Super)
469478
{
470479
tail_start += 1;
471480
}
472481
if path
473482
.get(tail_start)
474-
.is_some_and(|segment| segment == "self")
483+
.is_some_and(|segment| PathAnchor::from(segment.as_str()) == PathAnchor::SelfMod)
475484
{
476485
tail_start += 1;
477486
}

src/compiler/source_cache.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use super::constants::SOURCE_DIR_BENCHES;
1818
use super::constants::SOURCE_DIR_EXAMPLES;
1919
use super::constants::SOURCE_DIR_SRC;
2020
use super::constants::SOURCE_DIR_TESTS;
21+
use crate::rust_syntax::PathAnchor;
2122
use crate::selection::CARGO_TARGET_KIND_LIB;
2223
use crate::selection::CARGO_TARGET_KIND_MAIN;
2324

@@ -233,10 +234,10 @@ fn collect_rust_source_files(dir: &Path, files: &mut Vec<PathBuf>) -> Result<()>
233234
}
234235

235236
pub(super) fn path_origin(raw: &[String]) -> PathOrigin {
236-
if raw.first().map(String::as_str) == Some("crate") {
237-
PathOrigin::Crate
238-
} else {
239-
PathOrigin::Relative
237+
match PathAnchor::first(raw) {
238+
Some(PathAnchor::Crate) => PathOrigin::Crate,
239+
Some(PathAnchor::Super | PathAnchor::SelfMod | PathAnchor::SelfType | PathAnchor::Name)
240+
| None => PathOrigin::Relative,
240241
}
241242
}
242243

src/compiler/visibility/use_sites.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ use rustc_middle::ty::TyCtxt;
4343
use rustc_middle::ty::Visibility;
4444

4545
use crate::compiler::persistence::UseSite;
46+
use crate::rust_syntax::PathAnchor;
4647

4748
/// Walk the entire crate's HIR and append every resolved
4849
/// expression/type/pattern path reference to `out`. The caller module is
@@ -399,7 +400,7 @@ pub(super) fn parent_module_path_segments(tcx: TyCtxt<'_>, def_id: LocalDefId) -
399400
.filter(|segment| !segment.is_empty())
400401
.map(String::from)
401402
.collect::<Vec<_>>();
402-
if segments.first().is_some_and(|segment| segment == "crate") {
403+
if PathAnchor::first(&segments) == Some(PathAnchor::Crate) {
403404
segments.remove(0);
404405
}
405406
segments

src/fixes/imports/path.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ use proc_macro2::LineColumn;
22
use syn::UseTree;
33

44
use crate::config::DiagnosticCode;
5+
use crate::rust_syntax;
6+
use crate::rust_syntax::PathAnchor;
57

68
pub(super) struct ImportCandidate {
79
pub(super) original: String,
@@ -15,8 +17,11 @@ pub(super) fn analyze_use_tree(
1517
tree: &UseTree,
1618
) -> Option<ImportCandidate> {
1719
let import = flatten_use_tree(tree)?;
18-
if import.segments.first()? != "crate" {
19-
return None;
20+
match PathAnchor::first(&import.segments)? {
21+
PathAnchor::Crate => {},
22+
PathAnchor::Super | PathAnchor::SelfMod | PathAnchor::SelfType | PathAnchor::Name => {
23+
return None;
24+
},
2025
}
2126

2227
let target_segments = &import.segments[1..];
@@ -54,7 +59,7 @@ pub(super) fn analyze_deep_super(
5459
tree: &UseTree,
5560
) -> Option<ImportCandidate> {
5661
let import = flatten_use_tree(tree)?;
57-
let super_count = import.segments.iter().take_while(|s| *s == "super").count();
62+
let super_count = rust_syntax::leading_super_count(&import.segments);
5863
if super_count < 2 {
5964
return None;
6065
}

src/fixes/inline_path_qualified_type/process.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use crate::fixes::imports::UseFix;
1212
use crate::reporting::Finding;
1313
use crate::reporting::FixSupport;
1414
use crate::reporting::Severity;
15+
use crate::rust_syntax::PathAnchor;
1516

1617
pub(super) struct OccurrenceContext<'a> {
1718
pub(super) path: &'a Path,
@@ -168,8 +169,11 @@ fn absolutize_import_path(import_path: &str, existing_imports: &BTreeSet<String>
168169
let Some((leading, rest)) = import_path.split_once("::") else {
169170
return import_path.to_string();
170171
};
171-
if leading == "crate" || leading == "super" || leading == "self" {
172-
return import_path.to_string();
172+
match PathAnchor::from(leading) {
173+
PathAnchor::Crate | PathAnchor::Super | PathAnchor::SelfMod => {
174+
return import_path.to_string();
175+
},
176+
PathAnchor::SelfType | PathAnchor::Name => {},
173177
}
174178
// Look for an existing `use a::b::<leading>;` (i.e. an import whose final
175179
// segment matches `leading` and which has at least one parent segment).

src/fixes/inline_path_qualified_type/scope.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use syn::spanned::Spanned;
77

88
use super::offsets;
99
use super::visitor;
10+
use crate::rust_syntax::PathAnchor;
1011

1112
pub(super) struct ScopeInfo {
1213
pub(super) span_start: usize,
@@ -142,7 +143,7 @@ pub(super) fn canonicalize_inserted_use_path(scope: &ScopeInfo, full_path: &str)
142143
let segments: Vec<&str> = full_path.split("::").collect();
143144
let super_count = segments
144145
.iter()
145-
.take_while(|segment| **segment == "super")
146+
.take_while(|segment| PathAnchor::from(**segment) == PathAnchor::Super)
146147
.count();
147148
if super_count < 2 || super_count > scope.module_path.len() {
148149
return full_path.to_string();

src/fixes/inline_path_qualified_type/visitor.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ use syn::UseTree;
2525
use syn::visit;
2626
use syn::visit::Visit;
2727

28+
use crate::rust_syntax::PathAnchor;
29+
2830
pub(super) struct InlinePathOccurrence {
2931
/// The original fully-qualified path as written (e.g.
3032
/// `crate::project::RustProject::Package`).
@@ -81,7 +83,8 @@ impl InlinePathVisitor {
8183
}
8284

8385
let first = &segments[0];
84-
let is_intra_crate = first == "crate" || first == "super";
86+
let path_anchor = PathAnchor::from(first.as_str());
87+
let is_intra_crate = path_anchor.is_crate_relative();
8588

8689
// For intra-crate paths, require at least 3 segments: `crate::Foo` and
8790
// `super::Foo` are already short, so adding a `use` would not shorten
@@ -93,7 +96,7 @@ impl InlinePathVisitor {
9396

9497
// Filter obvious non-crate roots. `self::` is a same-module reference,
9598
// not a candidate for a `use`.
96-
if first == "self" || first == "Self" {
99+
if path_anchor.is_explicit_self() {
97100
return;
98101
}
99102

src/fixes/prefer_module_import/function_imports.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use syn::visit::Visit;
99
use syn::visit::visit_item_mod;
1010

1111
use super::support;
12+
use crate::rust_syntax::PathAnchor;
1213

1314
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1415
pub(super) enum ImportTarget {
@@ -71,8 +72,8 @@ fn analyze_function_import(
7172
return None;
7273
}
7374

74-
let first = flat.segments.first()?;
75-
if first != "crate" && first != "super" {
75+
let path_anchor = PathAnchor::first(&flat.segments)?;
76+
if !path_anchor.is_crate_relative() {
7677
return None;
7778
}
7879

@@ -92,7 +93,7 @@ fn analyze_function_import(
9293

9394
let module_segments = &flat.segments[..flat.segments.len() - 1];
9495
let module_name = flat.segments[flat.segments.len() - 2].clone();
95-
if module_name == "super" || module_name == "crate" {
96+
if PathAnchor::from(module_name.as_str()).is_crate_relative() {
9697
return None;
9798
}
9899
if !support::is_snake_case_module_name(&module_name) {
@@ -104,10 +105,11 @@ fn analyze_function_import(
104105

105106
let shortened_module_segments =
106107
support::shorten_module_path(current_module_path, module_segments);
107-
let import_target = if shortened_module_segments.as_slice() == ["super"] {
108-
ImportTarget::ParentModule
109-
} else {
110-
ImportTarget::OtherModule
108+
let import_target = match shortened_module_segments.as_slice() {
109+
[segment] if PathAnchor::from(segment.as_str()) == PathAnchor::Super => {
110+
ImportTarget::ParentModule
111+
},
112+
_ => ImportTarget::OtherModule,
111113
};
112114
let module_path = shortened_module_segments.join("::");
113115
let replacement_use = if import_target == ImportTarget::ParentModule {

src/fixes/prefer_module_import/inline_calls.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use crate::fixes::imports::UseFix;
1919
use crate::reporting::Finding;
2020
use crate::reporting::FixSupport;
2121
use crate::reporting::Severity;
22+
use crate::rust_syntax::PathAnchor;
2223

2324
pub(super) struct InlineCallCandidate {
2425
pub(super) function_name: String,
@@ -196,8 +197,8 @@ fn analyze_inline_call(
196197
return None;
197198
}
198199

199-
let first = segments.first()?;
200-
if first != "crate" && first != "super" {
200+
let path_anchor = PathAnchor::first(&segments)?;
201+
if !path_anchor.is_crate_relative() {
201202
return None;
202203
}
203204

@@ -220,7 +221,7 @@ fn analyze_inline_call(
220221
}
221222

222223
let module_name = segments[segments.len() - 2].clone();
223-
if module_name == "super" || module_name == "crate" {
224+
if PathAnchor::from(module_name.as_str()).is_crate_relative() {
224225
return None;
225226
}
226227
if !support::is_snake_case_module_name(&module_name) {
@@ -232,10 +233,11 @@ fn analyze_inline_call(
232233

233234
let module_segments = &segments[..segments.len() - 1];
234235
let shortened = support::shorten_module_path(current_module_path, module_segments);
235-
let import_target = if shortened.as_slice() == ["super"] {
236-
ImportTarget::ParentModule
237-
} else {
238-
ImportTarget::OtherModule
236+
let import_target = match shortened.as_slice() {
237+
[segment] if PathAnchor::from(segment.as_str()) == PathAnchor::Super => {
238+
ImportTarget::ParentModule
239+
},
240+
_ => ImportTarget::OtherModule,
239241
};
240242
let module_path = shortened.join("::");
241243

0 commit comments

Comments
 (0)