Skip to content

Commit 2ccb399

Browse files
Fix nonsensical #[path] attributes being silently allowed
Changes: - compiler/rustc_hir/src/attrs/data_structures.rs: Extended AttributeKind::Path variant to carry AttrStyle alongside the Symbol, enabling inner/outer attribute distinction - compiler/rustc_attr_parsing/src/attributes/path.rs: Updated convert in SingleAttributeParser for PathParser to pass cx.attr_style into AttributeKind::Path - compiler/rustc_passes/src/check_attr.rs: Added check_path_attribute to validate and emit errors for useless #[path] and #![path] attributes on inline modules - compiler/rustc_passes/src/errors.rs: Added UselessPathAttribute and UselessInnerPathAttribute error structs - ests/ui/attributes/path-inline-module.rs: UI tests covering all four validation cases - ests/ui/attributes/path-inline-module.stderr: Expected diagnostics for the above tests
1 parent 3daae5e commit 2ccb399

6 files changed

Lines changed: 127 additions & 4 deletions

File tree

compiler/rustc_attr_parsing/src/attributes/path.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,6 @@ impl SingleAttributeParser for PathParser {
1919
let nv = cx.expect_name_value(args, cx.attr_span, None)?;
2020
let path = cx.expect_string_literal(nv)?;
2121

22-
Some(AttributeKind::Path(path))
22+
Some(AttributeKind::Path(path, cx.attr_style))
2323
}
2424
}

compiler/rustc_hir/src/attrs/data_structures.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1210,7 +1210,7 @@ pub enum AttributeKind {
12101210
},
12111211

12121212
/// Represents `#[path]`
1213-
Path(Symbol),
1213+
Path(Symbol, AttrStyle),
12141214

12151215
/// Represents `#[pattern_complexity_limit]`
12161216
PatternComplexityLimit {

compiler/rustc_passes/src/check_attr.rs

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
305305
AttributeKind::Optimize(..) => (),
306306
AttributeKind::PanicRuntime => (),
307307
AttributeKind::PatchableFunctionEntry { .. } => (),
308-
AttributeKind::Path(..) => (),
308+
AttributeKind::Path(_, style) => {
309+
self.check_path_attribute(*style, hir_id, span)
310+
}
309311
AttributeKind::PatternComplexityLimit { .. } => (),
310312
AttributeKind::PinV2(..) => (),
311313
AttributeKind::PreludeImport => (),
@@ -938,7 +940,49 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
938940
}
939941
}
940942
}
941-
943+
fn check_path_attribute(&self, style: AttrStyle, hir_id: HirId, span: Span) {
944+
let mod_span = self.tcx.hir_span(hir_id);
945+
if mod_span.from_expansion() {
946+
return;
947+
}
948+
let node = self.tcx.hir_node(hir_id);
949+
let item_kind = match node {
950+
hir::Node::Item(item) => Some(&item.kind),
951+
_ => None,
952+
};
953+
match item_kind {
954+
Some(ItemKind::Mod(_, module)) => {
955+
let is_outer_attr = matches!(style, ast::AttrStyle::Outer);
956+
let is_inner_attr = matches!(style, ast::AttrStyle::Inner);
957+
958+
let inner_span = module.spans.inner_span;
959+
let is_inline_module = self.tcx.sess.source_map()
960+
.lookup_char_pos(span.lo()).file.name
961+
== self.tcx.sess.source_map()
962+
.lookup_char_pos(inner_span.lo()).file.name;
963+
964+
let has_nested_external_modules = module.item_ids.iter().any(|item_id| {
965+
let item = self.tcx.hir_item(*item_id);
966+
if let ItemKind::Mod(_, nested_mod) = &item.kind {
967+
let nested_inner = nested_mod.spans.inner_span;
968+
let nested_item_span = self.tcx.hir_span(item.hir_id());
969+
return self.tcx.sess.source_map()
970+
.lookup_char_pos(nested_item_span.lo()).file.name
971+
!= self.tcx.sess.source_map()
972+
.lookup_char_pos(nested_inner.lo()).file.name;
973+
}
974+
false
975+
});
976+
if is_outer_attr && is_inline_module && !has_nested_external_modules {
977+
self.dcx().emit_err(errors::UselessPathAttribute { span });
978+
}
979+
else if is_inner_attr && !has_nested_external_modules {
980+
self.dcx().emit_err(errors::UselessInnerPathAttribute { span });
981+
}
982+
}
983+
_ => {}
984+
}
985+
}
942986
/// Checks `#[doc(inline)]`/`#[doc(no_inline)]` attributes.
943987
///
944988
/// A doc inlining attribute is invalid if it is applied to a non-`use` item, or

compiler/rustc_passes/src/errors.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,20 @@ use rustc_span::{DUMMY_SP, Ident, Span, Symbol};
1313
use crate::check_attr::ProcMacroKind;
1414
use crate::lang_items::Duplicate;
1515

16+
17+
#[derive(Diagnostic)]
18+
#[diag("attribute `#[path]` is useless on inline modules")]
19+
pub(crate) struct UselessPathAttribute {
20+
#[primary_span]
21+
pub span: Span,
22+
}
23+
#[derive(Diagnostic)]
24+
#[diag("attribute `#[path]` is useless here as there are no nested external modules")]
25+
pub(crate) struct UselessInnerPathAttribute {
26+
#[primary_span]
27+
pub span: Span,
28+
}
29+
1630
#[derive(Diagnostic)]
1731
#[diag("`#[loop_match]` should be applied to a loop")]
1832
pub(crate) struct LoopMatchAttr {
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// ERROR: #[path] on inline module with NO external submodules
2+
#[path = "foo.rs"]
3+
mod inline_module {} //~ ERROR attribute `#[path]` is useless on inline modules
4+
5+
// ERROR: #[path] on inline module with only inline submodules
6+
#[path = "foo.rs"]
7+
mod inline_with_inline_sub { //~ ERROR attribute `#[path]` is useless on inline modules
8+
mod inner {} // inline, not external
9+
}
10+
11+
// ERROR: #![path] inside module with no external submodules
12+
mod useless_inner { //~ ERROR attribute `#[path]` is useless here as there are no nested external modules
13+
#![path = "some_dir"]
14+
// no external submodules
15+
}
16+
17+
// ERROR: #![path] inside module where submodule is inline (has body)
18+
mod thread_inline_sub { //~ ERROR attribute `#[path]` is useless here as there are no nested external modules
19+
#![path = "thread_files"]
20+
#[path = "tls.rs"]
21+
mod local_data {} //~ ERROR attribute `#[path]` is useless on inline modules
22+
}
23+
24+
fn main() {}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
error: attribute `#[path]` is useless on inline modules
2+
--> $DIR/path-inline-module.rs:3:1
3+
|
4+
LL | mod inline_module {}
5+
| ^^^^^^^^^^^^^^^^^^^^
6+
7+
error: attribute `#[path]` is useless on inline modules
8+
--> $DIR/path-inline-module.rs:7:1
9+
|
10+
LL | / mod inline_with_inline_sub {
11+
LL | | mod inner {} // inline, not external
12+
LL | | }
13+
| |_^
14+
15+
error: attribute `#[path]` is useless here as there are no nested external modules
16+
--> $DIR/path-inline-module.rs:12:1
17+
|
18+
LL | / mod useless_inner {
19+
LL | | #![path = "some_dir"]
20+
LL | | // no external submodules
21+
LL | | }
22+
| |_^
23+
24+
error: attribute `#[path]` is useless here as there are no nested external modules
25+
--> $DIR/path-inline-module.rs:18:1
26+
|
27+
LL | / mod thread_inline_sub {
28+
LL | | #![path = "thread_files"]
29+
LL | | #[path = "tls.rs"]
30+
LL | | mod local_data {}
31+
LL | | }
32+
| |_^
33+
34+
error: attribute `#[path]` is useless on inline modules
35+
--> $DIR/path-inline-module.rs:21:5
36+
|
37+
LL | mod local_data {}
38+
| ^^^^^^^^^^^^^^^^^
39+
40+
error: aborting due to 5 previous errors
41+

0 commit comments

Comments
 (0)