Skip to content

Commit 0393611

Browse files
author
Robin Kruppe
committed
Check all repr hints together when checking for mis-applied attributes
Fixes #47094 Besides fixing that bug, this change has a user-visible effect on the spans in the "incompatible repr hints" warning and another error: they now point at `foo` and/or `bar` in `repr(foo, bar)` instead of the whole attribute. This is sometimes more precise (e.g., `#[repr(C, packed)]` on an enum points at the `packed`) but sometimes not. I moved a compile-fail test to a ui test to illustrate how it now looks in the common case of only one attribute.
1 parent 00fbfcc commit 0393611

7 files changed

+130
-44
lines changed

Diff for: src/librustc/hir/check_attr.rs

+44-38
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,15 @@ struct CheckAttrVisitor<'a> {
4747

4848
impl<'a> CheckAttrVisitor<'a> {
4949
/// Check any attribute.
50-
fn check_attribute(&self, attr: &ast::Attribute, item: &ast::Item, target: Target) {
51-
if let Some(name) = attr.name() {
52-
match &*name.as_str() {
53-
"inline" => self.check_inline(attr, item, target),
54-
"repr" => self.check_repr(attr, item, target),
55-
_ => (),
50+
fn check_attributes(&self, item: &ast::Item, target: Target) {
51+
for attr in &item.attrs {
52+
if let Some(name) = attr.name() {
53+
if name == "inline" {
54+
self.check_inline(attr, item, target)
55+
}
5656
}
5757
}
58+
self.check_repr(item, target);
5859
}
5960

6061
/// Check if an `#[inline]` is applied to a function.
@@ -66,63 +67,67 @@ impl<'a> CheckAttrVisitor<'a> {
6667
}
6768
}
6869

69-
/// Check if an `#[repr]` attr is valid.
70-
fn check_repr(&self, attr: &ast::Attribute, item: &ast::Item, target: Target) {
71-
let words = match attr.meta_item_list() {
72-
Some(words) => words,
73-
None => {
74-
return;
75-
}
76-
};
70+
/// Check if the `#[repr]` attributes on `item` are valid.
71+
fn check_repr(&self, item: &ast::Item, target: Target) {
72+
// Extract the names of all repr hints, e.g., [foo, bar, align] for:
73+
// ```
74+
// #[repr(foo)]
75+
// #[repr(bar, align(8))]
76+
// ```
77+
let hints: Vec<_> = item.attrs
78+
.iter()
79+
.filter(|attr| match attr.name() {
80+
Some(name) => name == "repr",
81+
None => false,
82+
})
83+
.filter_map(|attr| attr.meta_item_list())
84+
.flat_map(|hints| hints)
85+
.collect();
7786

7887
let mut int_reprs = 0;
7988
let mut is_c = false;
8089
let mut is_simd = false;
8190

82-
for word in words {
83-
84-
let name = match word.name() {
85-
Some(word) => word,
86-
None => continue,
91+
for hint in &hints {
92+
let name = if let Some(name) = hint.name() {
93+
name
94+
} else {
95+
// Invalid repr hint like repr(42). We don't check for unrecognized hints here
96+
// (libsyntax does that), so just ignore it.
97+
continue;
8798
};
8899

89-
let (message, label) = match &*name.as_str() {
100+
let (article, allowed_targets) = match &*name.as_str() {
90101
"C" => {
91102
is_c = true;
92103
if target != Target::Struct &&
93104
target != Target::Union &&
94105
target != Target::Enum {
95-
("attribute should be applied to struct, enum or union",
96-
"a struct, enum or union")
106+
("a", "struct, enum or union")
97107
} else {
98108
continue
99109
}
100110
}
101111
"packed" => {
102-
// Do not increment conflicting_reprs here, because "packed"
103-
// can be used to modify another repr hint
104112
if target != Target::Struct &&
105113
target != Target::Union {
106-
("attribute should be applied to struct or union",
107-
"a struct or union")
114+
("a", "struct or union")
108115
} else {
109116
continue
110117
}
111118
}
112119
"simd" => {
113120
is_simd = true;
114121
if target != Target::Struct {
115-
("attribute should be applied to struct",
116-
"a struct")
122+
("a", "struct")
117123
} else {
118124
continue
119125
}
120126
}
121127
"align" => {
122128
if target != Target::Struct &&
123129
target != Target::Union {
124-
("attribute should be applied to struct or union",
125-
"a struct or union")
130+
("a", "struct or union")
126131
} else {
127132
continue
128133
}
@@ -132,24 +137,27 @@ impl<'a> CheckAttrVisitor<'a> {
132137
"isize" | "usize" => {
133138
int_reprs += 1;
134139
if target != Target::Enum {
135-
("attribute should be applied to enum",
136-
"an enum")
140+
("an", "enum")
137141
} else {
138142
continue
139143
}
140144
}
141145
_ => continue,
142146
};
143-
struct_span_err!(self.sess, attr.span, E0517, "{}", message)
144-
.span_label(item.span, format!("not {}", label))
147+
struct_span_err!(self.sess, hint.span, E0517,
148+
"attribute should be applied to {}", allowed_targets)
149+
.span_label(item.span, format!("not {} {}", article, allowed_targets))
145150
.emit();
146151
}
147152

148153
// Warn on repr(u8, u16), repr(C, simd), and c-like-enum-repr(C, u8)
149154
if (int_reprs > 1)
150155
|| (is_simd && is_c)
151156
|| (int_reprs == 1 && is_c && is_c_like_enum(item)) {
152-
span_warn!(self.sess, attr.span, E0566,
157+
// Just point at all repr hints. This is not ideal, but tracking precisely which ones
158+
// are at fault is a huge hassle.
159+
let spans: Vec<_> = hints.iter().map(|hint| hint.span).collect();
160+
span_warn!(self.sess, spans, E0566,
153161
"conflicting representation hints");
154162
}
155163
}
@@ -158,9 +166,7 @@ impl<'a> CheckAttrVisitor<'a> {
158166
impl<'a> Visitor<'a> for CheckAttrVisitor<'a> {
159167
fn visit_item(&mut self, item: &'a ast::Item) {
160168
let target = Target::from_item(item);
161-
for attr in &item.attrs {
162-
self.check_attribute(attr, item, target);
163-
}
169+
self.check_attributes(item, target);
164170
visit::walk_item(self, item);
165171
}
166172
}

Diff for: src/test/compile-fail/attr-usage-repr.rs renamed to src/test/ui/attr-usage-repr.rs

-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
#![allow(dead_code)]
1211
#![feature(attr_literals)]
1312
#![feature(repr_simd)]
1413

Diff for: src/test/ui/attr-usage-repr.stderr

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
error[E0517]: attribute should be applied to struct, enum or union
2+
--> $DIR/attr-usage-repr.rs:14:8
3+
|
4+
14 | #[repr(C)] //~ ERROR: attribute should be applied to struct, enum or union
5+
| ^
6+
15 | fn f() {}
7+
| --------- not a struct, enum or union
8+
9+
error[E0517]: attribute should be applied to enum
10+
--> $DIR/attr-usage-repr.rs:26:8
11+
|
12+
26 | #[repr(i8)] //~ ERROR: attribute should be applied to enum
13+
| ^^
14+
27 | struct SInt(f64, f64);
15+
| ---------------------- not an enum
16+
17+
error[E0517]: attribute should be applied to struct or union
18+
--> $DIR/attr-usage-repr.rs:32:8
19+
|
20+
32 | #[repr(align(8))] //~ ERROR: attribute should be applied to struct
21+
| ^^^^^^^^
22+
33 | enum EAlign { A, B }
23+
| -------------------- not a struct or union
24+
25+
error[E0517]: attribute should be applied to struct or union
26+
--> $DIR/attr-usage-repr.rs:35:8
27+
|
28+
35 | #[repr(packed)] //~ ERROR: attribute should be applied to struct
29+
| ^^^^^^
30+
36 | enum EPacked { A, B }
31+
| --------------------- not a struct or union
32+
33+
error[E0517]: attribute should be applied to struct
34+
--> $DIR/attr-usage-repr.rs:38:8
35+
|
36+
38 | #[repr(simd)] //~ ERROR: attribute should be applied to struct
37+
| ^^^^
38+
39 | enum ESimd { A, B }
39+
| ------------------- not a struct
40+
41+
error: aborting due to 5 previous errors
42+

Diff for: src/test/ui/feature-gate-simd-ffi.rs

-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313

1414
#[repr(simd)]
1515
#[derive(Copy, Clone)]
16-
#[repr(C)]
1716
struct LocalSimd(u8, u8);
1817

1918
extern {

Diff for: src/test/ui/feature-gate-simd-ffi.stderr

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
error: use of SIMD type `LocalSimd` in FFI is highly experimental and may result in invalid code
2-
--> $DIR/feature-gate-simd-ffi.rs:20:17
2+
--> $DIR/feature-gate-simd-ffi.rs:19:17
33
|
4-
20 | fn baz() -> LocalSimd; //~ ERROR use of SIMD type
4+
19 | fn baz() -> LocalSimd; //~ ERROR use of SIMD type
55
| ^^^^^^^^^
66
|
77
= help: add #![feature(simd_ffi)] to the crate attributes to enable
88

99
error: use of SIMD type `LocalSimd` in FFI is highly experimental and may result in invalid code
10-
--> $DIR/feature-gate-simd-ffi.rs:21:15
10+
--> $DIR/feature-gate-simd-ffi.rs:20:15
1111
|
12-
21 | fn qux(x: LocalSimd); //~ ERROR use of SIMD type
12+
20 | fn qux(x: LocalSimd); //~ ERROR use of SIMD type
1313
| ^^^^^^^^^
1414
|
1515
= help: add #![feature(simd_ffi)] to the crate attributes to enable

Diff for: src/test/ui/issue-47094.rs

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// must-compile-successfully
12+
13+
#[repr(C,u8)]
14+
enum Foo {
15+
A,
16+
B,
17+
}
18+
19+
#[repr(C)]
20+
#[repr(u8)]
21+
enum Bar {
22+
A,
23+
B,
24+
}
25+
26+
fn main() {}

Diff for: src/test/ui/issue-47094.stderr

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
warning[E0566]: conflicting representation hints
2+
--> $DIR/issue-47094.rs:13:8
3+
|
4+
13 | #[repr(C,u8)]
5+
| ^ ^^
6+
7+
warning[E0566]: conflicting representation hints
8+
--> $DIR/issue-47094.rs:19:8
9+
|
10+
19 | #[repr(C)]
11+
| ^
12+
20 | #[repr(u8)]
13+
| ^^
14+

0 commit comments

Comments
 (0)