Skip to content

Commit ecb10bf

Browse files
committed
Fix repr(...) validation ICE
Includes minor refactor and more helpful errors in case of incorrect repr attributes
1 parent 2f601ef commit ecb10bf

File tree

3 files changed

+463
-78
lines changed

3 files changed

+463
-78
lines changed

compiler/rustc_attr/src/builtin.rs

+156-78
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
use rustc_ast::{self as ast, Attribute, Lit, LitKind, MetaItem, MetaItemKind, NestedMetaItem};
44
use rustc_ast_pretty::pprust;
5-
use rustc_errors::{struct_span_err, Applicability};
5+
use rustc_errors::{struct_span_err, Applicability, Handler};
66
use rustc_feature::{find_gated_cfg, is_builtin_attr_name, Features, GatedCfg};
77
use rustc_macros::HashStable_Generic;
88
use rustc_session::parse::{feature_err, ParseSess};
@@ -827,6 +827,150 @@ pub enum ReprAttr {
827827
ReprNoNiche,
828828
}
829829

830+
impl ReprAttr {
831+
fn try_from_meta_item(
832+
diagnostic: &Handler,
833+
item: &MetaItem,
834+
) -> Result</* incorrect attr if None */ Option<Self>, /* invalid attr */ ()> {
835+
use ReprAttr::*;
836+
let name = item.name_or_empty();
837+
let expect_no_args = || {
838+
if !item.is_word() {
839+
struct_span_err!(
840+
diagnostic,
841+
item.span,
842+
E0589,
843+
"invalid `repr({})` attribute: no arguments expected",
844+
name,
845+
)
846+
.span_suggestion(
847+
item.path.span.shrink_to_hi().to(item.span.shrink_to_hi()),
848+
"remove additional values",
849+
format!("{}", name),
850+
Applicability::MachineApplicable,
851+
)
852+
.emit();
853+
}
854+
};
855+
let expect_one_int_arg = || {
856+
if let MetaItemKind::NameValue(ref value) = item.kind {
857+
let mut err = struct_span_err!(
858+
diagnostic,
859+
item.span,
860+
E0693,
861+
"incorrect `repr({})` attribute format",
862+
name
863+
);
864+
match value.kind {
865+
ast::LitKind::Int(int, ast::LitIntType::Unsuffixed) => {
866+
err.span_suggestion(
867+
item.span,
868+
"use parentheses instead",
869+
format!("{}({})", name, int),
870+
Applicability::MachineApplicable,
871+
);
872+
}
873+
ast::LitKind::Str(s, _) => {
874+
err.span_suggestion(
875+
item.span,
876+
"use parentheses instead",
877+
format!("{}({})", name, s),
878+
Applicability::MachineApplicable,
879+
);
880+
}
881+
_ => {}
882+
}
883+
err.emit();
884+
} else if item.is_word() || matches!(item.meta_item_list(), Some([])) {
885+
struct_span_err!(
886+
diagnostic,
887+
item.span,
888+
E0693,
889+
"invalid `repr({})` attribute: expected a value",
890+
name
891+
)
892+
.span_suggestion(
893+
item.span,
894+
"add a value",
895+
format!("{}(/* value */)", name),
896+
Applicability::HasPlaceholders,
897+
)
898+
.emit();
899+
} else if let Some([NestedMetaItem::Literal(value)]) = item.meta_item_list() {
900+
match parse_alignment(&value.kind) {
901+
Ok(literal) => return Some(literal),
902+
Err(message) => {
903+
struct_span_err!(
904+
diagnostic,
905+
item.span,
906+
E0589,
907+
"invalid `repr({})` attribute: {}",
908+
name,
909+
message
910+
)
911+
.emit();
912+
}
913+
};
914+
} else if let Some([first_meta, .., last_meta]) = item.meta_item_list() {
915+
struct_span_err!(
916+
diagnostic,
917+
item.span,
918+
E0693,
919+
"invalid `repr({})` attribute: expected only one value",
920+
item.name_or_empty(),
921+
)
922+
.span_label(first_meta.span().to(last_meta.span()), "only one argument expected")
923+
.emit();
924+
}
925+
return None;
926+
};
927+
// Every possible repr variant must be covered here or else an ICE will occur
928+
Ok(Some(match name {
929+
sym::C => {
930+
expect_no_args();
931+
ReprC
932+
}
933+
sym::simd => {
934+
expect_no_args();
935+
ReprSimd
936+
}
937+
sym::transparent => {
938+
expect_no_args();
939+
ReprTransparent
940+
}
941+
sym::no_niche => {
942+
expect_no_args();
943+
ReprNoNiche
944+
}
945+
sym::align => {
946+
if let Some(arg) = expect_one_int_arg() {
947+
ReprAlign(arg)
948+
} else {
949+
return Ok(None);
950+
}
951+
}
952+
sym::packed => {
953+
if item.is_word() {
954+
ReprPacked(1)
955+
} else if let Some(arg) = expect_one_int_arg() {
956+
ReprPacked(arg)
957+
} else {
958+
return Ok(None);
959+
}
960+
}
961+
name => {
962+
if let Some(ty) = int_type_of_word(name) {
963+
expect_no_args();
964+
ReprInt(ty)
965+
} else {
966+
// Unrecognized repr attr
967+
return Err(());
968+
}
969+
}
970+
}))
971+
}
972+
}
973+
830974
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
831975
#[derive(Encodable, Decodable, HashStable_Generic)]
832976
pub enum IntType {
@@ -846,98 +990,32 @@ impl IntType {
846990
}
847991
}
848992

849-
/// Parse #[repr(...)] forms.
993+
/// Parse `#[repr(...)]` forms.
850994
///
851995
/// Valid repr contents: any of the primitive integral type names (see
852996
/// `int_type_of_word`, below) to specify enum discriminant type; `C`, to use
853997
/// the same discriminant size that the corresponding C enum would or C
854998
/// structure layout, `packed` to remove padding, and `transparent` to elegate representation
855999
/// concerns to the only non-ZST field.
8561000
pub fn find_repr_attrs(sess: &Session, attr: &Attribute) -> Vec<ReprAttr> {
857-
use ReprAttr::*;
858-
8591001
let mut acc = Vec::new();
8601002
let diagnostic = &sess.parse_sess.span_diagnostic;
8611003
if attr.has_name(sym::repr) {
8621004
if let Some(items) = attr.meta_item_list() {
8631005
sess.mark_attr_used(attr);
8641006
for item in items {
865-
let mut recognised = false;
866-
if item.is_word() {
867-
let hint = match item.name_or_empty() {
868-
sym::C => Some(ReprC),
869-
sym::packed => Some(ReprPacked(1)),
870-
sym::simd => Some(ReprSimd),
871-
sym::transparent => Some(ReprTransparent),
872-
sym::no_niche => Some(ReprNoNiche),
873-
name => int_type_of_word(name).map(ReprInt),
874-
};
875-
876-
if let Some(h) = hint {
877-
recognised = true;
878-
acc.push(h);
1007+
match item
1008+
.meta_item()
1009+
.ok_or(())
1010+
.and_then(|item| ReprAttr::try_from_meta_item(diagnostic, &item))
1011+
{
1012+
Ok(None) => {
1013+
// Recognized attribute, but failed to parse (e.g. align() or packed(1, 2, 3))
8791014
}
880-
} else if let Some((name, value)) = item.name_value_literal() {
881-
let mut literal_error = None;
882-
if name == sym::align {
883-
recognised = true;
884-
match parse_alignment(&value.kind) {
885-
Ok(literal) => acc.push(ReprAlign(literal)),
886-
Err(message) => literal_error = Some(message),
887-
};
888-
} else if name == sym::packed {
889-
recognised = true;
890-
match parse_alignment(&value.kind) {
891-
Ok(literal) => acc.push(ReprPacked(literal)),
892-
Err(message) => literal_error = Some(message),
893-
};
1015+
Ok(Some(attr)) => acc.push(attr),
1016+
Err(()) => {
1017+
diagnostic.delay_span_bug(item.span(), "unrecognized representation hint")
8941018
}
895-
if let Some(literal_error) = literal_error {
896-
struct_span_err!(
897-
diagnostic,
898-
item.span(),
899-
E0589,
900-
"invalid `repr(align)` attribute: {}",
901-
literal_error
902-
)
903-
.emit();
904-
}
905-
} else if let Some(meta_item) = item.meta_item() {
906-
if meta_item.has_name(sym::align) {
907-
if let MetaItemKind::NameValue(ref value) = meta_item.kind {
908-
recognised = true;
909-
let mut err = struct_span_err!(
910-
diagnostic,
911-
item.span(),
912-
E0693,
913-
"incorrect `repr(align)` attribute format"
914-
);
915-
match value.kind {
916-
ast::LitKind::Int(int, ast::LitIntType::Unsuffixed) => {
917-
err.span_suggestion(
918-
item.span(),
919-
"use parentheses instead",
920-
format!("align({})", int),
921-
Applicability::MachineApplicable,
922-
);
923-
}
924-
ast::LitKind::Str(s, _) => {
925-
err.span_suggestion(
926-
item.span(),
927-
"use parentheses instead",
928-
format!("align({})", s),
929-
Applicability::MachineApplicable,
930-
);
931-
}
932-
_ => {}
933-
}
934-
err.emit();
935-
}
936-
}
937-
}
938-
if !recognised {
939-
// Not a word we recognize
940-
diagnostic.delay_span_bug(item.span(), "unrecognized representation hint");
9411019
}
9421020
}
9431021
}

src/test/ui/repr/basic.rs

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
#![feature(repr_simd)]
2+
#![feature(no_niche)]
3+
4+
#[repr(transparent())] //~ ERROR invalid `repr(transparent)` attribute: no arguments expected
5+
//~| ERROR invalid `repr(transparent)` attribute: no arguments expected
6+
struct S0(u32);
7+
8+
#[repr(transparent(1, 2, 3))] //~ ERROR invalid `repr(transparent)` attribute: no arguments expected
9+
//~| ERROR invalid `repr(transparent)` attribute: no arguments expected
10+
struct S1(u32);
11+
12+
#[repr(C())] //~ ERROR invalid `repr(C)` attribute: no arguments expected
13+
//~| ERROR invalid `repr(C)` attribute: no arguments expected
14+
struct S2(u32);
15+
16+
#[repr(C(1, 2, 3))] //~ ERROR invalid `repr(C)` attribute: no arguments expected
17+
//~| ERROR invalid `repr(C)` attribute: no arguments expected
18+
struct S3(u32);
19+
20+
#[repr(simd())] //~ ERROR invalid `repr(simd)` attribute: no arguments expected
21+
//~| ERROR invalid `repr(simd)` attribute: no arguments expected
22+
struct S4(u32);
23+
24+
#[repr(simd(1, 2, 3))] //~ ERROR invalid `repr(simd)` attribute: no arguments expected
25+
//~| ERROR invalid `repr(simd)` attribute: no arguments expected
26+
struct S5(u32);
27+
28+
#[repr(no_niche())] //~ ERROR invalid `repr(no_niche)` attribute: no arguments expected
29+
//~| ERROR invalid `repr(no_niche)` attribute: no arguments expected
30+
struct S6(u32);
31+
32+
#[repr(no_niche(1, 2, 3))] //~ ERROR invalid `repr(no_niche)` attribute: no arguments expected
33+
//~| ERROR invalid `repr(no_niche)` attribute: no arguments expected
34+
struct S7(u32);
35+
36+
#[repr(align())] //~ ERROR invalid `repr(align)` attribute: expected a value
37+
//~| ERROR invalid `repr(align)` attribute: expected a value
38+
struct S8(u32);
39+
40+
#[repr(align(1, 2, 3))] //~ ERROR invalid `repr(align)` attribute: expected only one value
41+
//~| ERROR invalid `repr(align)` attribute: expected only one value
42+
struct S9(u32);
43+
44+
#[repr(packed())] //~ ERROR invalid `repr(packed)` attribute: expected a value
45+
//~| ERROR invalid `repr(packed)` attribute: expected a value
46+
struct S10(u32);
47+
48+
#[repr(packed(1, 2, 3))] //~ ERROR invalid `repr(packed)` attribute: expected only one value
49+
//~| ERROR invalid `repr(packed)` attribute: expected only one value
50+
struct S11(u32);
51+
52+
#[repr(i8())] //~ ERROR invalid `repr(i8)` attribute: no arguments expected
53+
//~| ERROR invalid `repr(i8)` attribute: no arguments expected
54+
enum S12 { A, B }
55+
56+
#[repr(i8(1, 2, 3))] //~ ERROR invalid `repr(i8)` attribute: no arguments expected
57+
//~| ERROR invalid `repr(i8)` attribute: no arguments expected
58+
enum S13 { A, B }
59+
60+
#[repr] //~ ERROR malformed `repr` attribute input
61+
struct S14(u32);
62+
63+
#[repr(123)] //~ ERROR meta item in `repr` must be an identifier
64+
struct S15(u32);
65+
66+
#[repr("foo")] //~ ERROR meta item in `repr` must be an identifier
67+
struct S16(u32);
68+
69+
fn main() {}

0 commit comments

Comments
 (0)