Skip to content

Commit 042b5dd

Browse files
borspoliorcetics
authored andcommitted
Auto merge of rust-lang#134844 - Zalathar:rollup-1225wh9, r=Zalathar
Rollup of 5 pull requests Successful merges: - rust-lang#134737 (Implement `default_overrides_default_fields` lint) - rust-lang#134760 (Migrate `branch-protection-check-IBT` to rmake.rs) - rust-lang#134829 (Migrate `libs-through-symlink` to rmake.rs) - rust-lang#134832 (Update `compiler-builtins` to 0.1.140) - rust-lang#134840 (compiletest: Only pass the post-colon value to `parse_normalize_rule`) r? `@ghost` `@rustbot` modify labels: rollup
2 parents 88d1989 + 8dcb260 commit 042b5dd

20 files changed

+687
-105
lines changed

compiler/rustc_codegen_cranelift/patches/0029-stdlib-Disable-f16-and-f128-in-compiler-builtins.patch

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ index 7165c3e48af..968552ad435 100644
1616

1717
[dependencies]
1818
core = { path = "../core" }
19-
-compiler_builtins = { version = "=0.1.138", features = ['rustc-dep-of-std'] }
20-
+compiler_builtins = { version = "=0.1.138", features = ['rustc-dep-of-std', 'no-f16-f128'] }
19+
-compiler_builtins = { version = "=0.1.140", features = ['rustc-dep-of-std'] }
20+
+compiler_builtins = { version = "=0.1.140", features = ['rustc-dep-of-std', 'no-f16-f128'] }
2121

2222
[dev-dependencies]
2323
rand = { version = "0.8.5", default-features = false, features = ["alloc"] }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
use rustc_data_structures::fx::FxHashMap;
2+
use rustc_errors::Diag;
3+
use rustc_hir as hir;
4+
use rustc_middle::ty;
5+
use rustc_session::{declare_lint, impl_lint_pass};
6+
use rustc_span::Symbol;
7+
use rustc_span::symbol::sym;
8+
9+
use crate::{LateContext, LateLintPass};
10+
11+
declare_lint! {
12+
/// The `default_overrides_default_fields` lint checks for manual `impl` blocks of the
13+
/// `Default` trait of types with default field values.
14+
///
15+
/// ### Example
16+
///
17+
/// ```rust,compile_fail
18+
/// #![feature(default_field_values)]
19+
/// struct Foo {
20+
/// x: i32 = 101,
21+
/// y: NonDefault,
22+
/// }
23+
///
24+
/// struct NonDefault;
25+
///
26+
/// #[deny(default_overrides_default_fields)]
27+
/// impl Default for Foo {
28+
/// fn default() -> Foo {
29+
/// Foo { x: 100, y: NonDefault }
30+
/// }
31+
/// }
32+
/// ```
33+
///
34+
/// {{produces}}
35+
///
36+
/// ### Explanation
37+
///
38+
/// Manually writing a `Default` implementation for a type that has
39+
/// default field values runs the risk of diverging behavior between
40+
/// `Type { .. }` and `<Type as Default>::default()`, which would be a
41+
/// foot-gun for users of that type that would expect these to be
42+
/// equivalent. If `Default` can't be derived due to some fields not
43+
/// having a `Default` implementation, we encourage the use of `..` for
44+
/// the fields that do have a default field value.
45+
pub DEFAULT_OVERRIDES_DEFAULT_FIELDS,
46+
Deny,
47+
"detect `Default` impl that should use the type's default field values",
48+
@feature_gate = default_field_values;
49+
}
50+
51+
#[derive(Default)]
52+
pub(crate) struct DefaultCouldBeDerived;
53+
54+
impl_lint_pass!(DefaultCouldBeDerived => [DEFAULT_OVERRIDES_DEFAULT_FIELDS]);
55+
56+
impl<'tcx> LateLintPass<'tcx> for DefaultCouldBeDerived {
57+
fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) {
58+
// Look for manual implementations of `Default`.
59+
let Some(default_def_id) = cx.tcx.get_diagnostic_item(sym::Default) else { return };
60+
let hir::ImplItemKind::Fn(_sig, body_id) = impl_item.kind else { return };
61+
let assoc = cx.tcx.associated_item(impl_item.owner_id);
62+
let parent = assoc.container_id(cx.tcx);
63+
if cx.tcx.has_attr(parent, sym::automatically_derived) {
64+
// We don't care about what `#[derive(Default)]` produces in this lint.
65+
return;
66+
}
67+
let Some(trait_ref) = cx.tcx.impl_trait_ref(parent) else { return };
68+
let trait_ref = trait_ref.instantiate_identity();
69+
if trait_ref.def_id != default_def_id {
70+
return;
71+
}
72+
let ty = trait_ref.self_ty();
73+
let ty::Adt(def, _) = ty.kind() else { return };
74+
75+
// We now know we have a manually written definition of a `<Type as Default>::default()`.
76+
77+
let hir = cx.tcx.hir();
78+
79+
let type_def_id = def.did();
80+
let body = hir.body(body_id);
81+
82+
// FIXME: evaluate bodies with statements and evaluate bindings to see if they would be
83+
// derivable.
84+
let hir::ExprKind::Block(hir::Block { stmts: _, expr: Some(expr), .. }, None) =
85+
body.value.kind
86+
else {
87+
return;
88+
};
89+
90+
// Keep a mapping of field name to `hir::FieldDef` for every field in the type. We'll use
91+
// these to check for things like checking whether it has a default or using its span for
92+
// suggestions.
93+
let orig_fields = match hir.get_if_local(type_def_id) {
94+
Some(hir::Node::Item(hir::Item {
95+
kind:
96+
hir::ItemKind::Struct(hir::VariantData::Struct { fields, recovered: _ }, _generics),
97+
..
98+
})) => fields.iter().map(|f| (f.ident.name, f)).collect::<FxHashMap<_, _>>(),
99+
_ => return,
100+
};
101+
102+
// We check `fn default()` body is a single ADT literal and get all the fields that are
103+
// being set.
104+
let hir::ExprKind::Struct(_qpath, fields, tail) = expr.kind else { return };
105+
106+
// We have a struct literal
107+
//
108+
// struct Foo {
109+
// field: Type,
110+
// }
111+
//
112+
// impl Default for Foo {
113+
// fn default() -> Foo {
114+
// Foo {
115+
// field: val,
116+
// }
117+
// }
118+
// }
119+
//
120+
// We would suggest `#[derive(Default)]` if `field` has a default value, regardless of what
121+
// it is; we don't want to encourage divergent behavior between `Default::default()` and
122+
// `..`.
123+
124+
if let hir::StructTailExpr::Base(_) = tail {
125+
// This is *very* niche. We'd only get here if someone wrote
126+
// impl Default for Ty {
127+
// fn default() -> Ty {
128+
// Ty { ..something() }
129+
// }
130+
// }
131+
// where `something()` would have to be a call or path.
132+
// We have nothing meaninful to do with this.
133+
return;
134+
}
135+
136+
// At least one of the fields with a default value have been overriden in
137+
// the `Default` implementation. We suggest removing it and relying on `..`
138+
// instead.
139+
let any_default_field_given =
140+
fields.iter().any(|f| orig_fields.get(&f.ident.name).and_then(|f| f.default).is_some());
141+
142+
if !any_default_field_given {
143+
// None of the default fields were actually provided explicitly, so the manual impl
144+
// doesn't override them (the user used `..`), so there's no risk of divergent behavior.
145+
return;
146+
}
147+
148+
let Some(local) = parent.as_local() else { return };
149+
let hir_id = cx.tcx.local_def_id_to_hir_id(local);
150+
let hir::Node::Item(item) = cx.tcx.hir_node(hir_id) else { return };
151+
cx.tcx.node_span_lint(DEFAULT_OVERRIDES_DEFAULT_FIELDS, hir_id, item.span, |diag| {
152+
mk_lint(diag, orig_fields, fields);
153+
});
154+
}
155+
}
156+
157+
fn mk_lint(
158+
diag: &mut Diag<'_, ()>,
159+
orig_fields: FxHashMap<Symbol, &hir::FieldDef<'_>>,
160+
fields: &[hir::ExprField<'_>],
161+
) {
162+
diag.primary_message("`Default` impl doesn't use the declared default field values");
163+
164+
// For each field in the struct expression
165+
// - if the field in the type has a default value, it should be removed
166+
// - elif the field is an expression that could be a default value, it should be used as the
167+
// field's default value (FIXME: not done).
168+
// - else, we wouldn't touch this field, it would remain in the manual impl
169+
let mut removed_all_fields = true;
170+
for field in fields {
171+
if orig_fields.get(&field.ident.name).and_then(|f| f.default).is_some() {
172+
diag.span_label(field.expr.span, "this field has a default value");
173+
} else {
174+
removed_all_fields = false;
175+
}
176+
}
177+
178+
diag.help(if removed_all_fields {
179+
"to avoid divergence in behavior between `Struct { .. }` and \
180+
`<Struct as Default>::default()`, derive the `Default`"
181+
} else {
182+
"use the default values in the `impl` with `Struct { mandatory_field, .. }` to avoid them \
183+
diverging over time"
184+
});
185+
}

compiler/rustc_lint/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ mod async_fn_in_trait;
4141
pub mod builtin;
4242
mod context;
4343
mod dangling;
44+
mod default_could_be_derived;
4445
mod deref_into_dyn_supertrait;
4546
mod drop_forget_useless;
4647
mod early;
@@ -85,6 +86,7 @@ use async_closures::AsyncClosureUsage;
8586
use async_fn_in_trait::AsyncFnInTrait;
8687
use builtin::*;
8788
use dangling::*;
89+
use default_could_be_derived::DefaultCouldBeDerived;
8890
use deref_into_dyn_supertrait::*;
8991
use drop_forget_useless::*;
9092
use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums;
@@ -189,6 +191,7 @@ late_lint_methods!(
189191
BuiltinCombinedModuleLateLintPass,
190192
[
191193
ForLoopsOverFallibles: ForLoopsOverFallibles,
194+
DefaultCouldBeDerived: DefaultCouldBeDerived::default(),
192195
DerefIntoDynSupertrait: DerefIntoDynSupertrait,
193196
DropForgetUseless: DropForgetUseless,
194197
ImproperCTypesDeclarations: ImproperCTypesDeclarations,

library/Cargo.lock

+2-2
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,9 @@ dependencies = [
6161

6262
[[package]]
6363
name = "compiler_builtins"
64-
version = "0.1.138"
64+
version = "0.1.140"
6565
source = "registry+https://github.com/rust-lang/crates.io-index"
66-
checksum = "53f0ea7fff95b51f84371588f06062557e96bbe363d2b36218ddb806f3ca8611"
66+
checksum = "df14d41c5d172a886df3753d54238eefb0f61c96cbd8b363c33ccc92c457bee3"
6767
dependencies = [
6868
"cc",
6969
"rustc-std-workspace-core",

library/alloc/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ edition = "2021"
1010

1111
[dependencies]
1212
core = { path = "../core" }
13-
compiler_builtins = { version = "=0.1.138", features = ['rustc-dep-of-std'] }
13+
compiler_builtins = { version = "=0.1.140", features = ['rustc-dep-of-std'] }
1414

1515
[dev-dependencies]
1616
rand = { version = "0.8.5", default-features = false, features = ["alloc"] }

library/std/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] }
1717
panic_unwind = { path = "../panic_unwind", optional = true }
1818
panic_abort = { path = "../panic_abort" }
1919
core = { path = "../core", public = true }
20-
compiler_builtins = { version = "=0.1.138" }
20+
compiler_builtins = { version = "=0.1.140" }
2121
unwind = { path = "../unwind" }
2222
hashbrown = { version = "0.15", default-features = false, features = [
2323
'rustc-dep-of-std',

src/tools/compiletest/src/header.rs

+11-11
Original file line numberDiff line numberDiff line change
@@ -978,10 +978,10 @@ impl Config {
978978
}
979979
}
980980

981-
fn parse_custom_normalization(&self, line: &str) -> Option<NormalizeRule> {
981+
fn parse_custom_normalization(&self, raw_directive: &str) -> Option<NormalizeRule> {
982982
// FIXME(Zalathar): Integrate name/value splitting into `DirectiveLine`
983983
// instead of doing it here.
984-
let (directive_name, _value) = line.split_once(':')?;
984+
let (directive_name, raw_value) = raw_directive.split_once(':')?;
985985

986986
let kind = match directive_name {
987987
"normalize-stdout" => NormalizeKind::Stdout,
@@ -991,11 +991,9 @@ impl Config {
991991
_ => return None,
992992
};
993993

994-
// FIXME(Zalathar): The normalize rule parser should only care about
995-
// the value part, not the "line" (which isn't even the whole line).
996-
let Some((regex, replacement)) = parse_normalize_rule(line) else {
994+
let Some((regex, replacement)) = parse_normalize_rule(raw_value) else {
997995
panic!(
998-
"couldn't parse custom normalization rule: `{line}`\n\
996+
"couldn't parse custom normalization rule: `{raw_directive}`\n\
999997
help: expected syntax is: `{directive_name}: \"REGEX\" -> \"REPLACEMENT\"`"
1000998
);
1001999
};
@@ -1141,24 +1139,26 @@ enum NormalizeKind {
11411139
/// Parses the regex and replacement values of a `//@ normalize-*` header,
11421140
/// in the format:
11431141
/// ```text
1144-
/// normalize-*: "REGEX" -> "REPLACEMENT"
1142+
/// "REGEX" -> "REPLACEMENT"
11451143
/// ```
1146-
fn parse_normalize_rule(header: &str) -> Option<(String, String)> {
1144+
fn parse_normalize_rule(raw_value: &str) -> Option<(String, String)> {
11471145
// FIXME: Support escaped double-quotes in strings.
11481146
let captures = static_regex!(
11491147
r#"(?x) # (verbose mode regex)
11501148
^
1151-
[^:\s]+:\s* # (header name followed by colon)
1149+
\s* # (leading whitespace)
11521150
"(?<regex>[^"]*)" # "REGEX"
11531151
\s+->\s+ # ->
11541152
"(?<replacement>[^"]*)" # "REPLACEMENT"
11551153
$
11561154
"#
11571155
)
1158-
.captures(header)?;
1156+
.captures(raw_value)?;
11591157
let regex = captures["regex"].to_owned();
11601158
let replacement = captures["replacement"].to_owned();
1161-
// FIXME: Support escaped new-line in strings.
1159+
// A `\n` sequence in the replacement becomes an actual newline.
1160+
// FIXME: Do unescaping in a less ad-hoc way, and perhaps support escaped
1161+
// backslashes and double-quotes.
11621162
let replacement = replacement.replace("\\n", "\n");
11631163
Some((regex, replacement))
11641164
}

src/tools/compiletest/src/header/tests.rs

+14-11
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,14 @@ fn make_test_description<R: Read>(
3535

3636
#[test]
3737
fn test_parse_normalize_rule() {
38-
let good_data = &[(
39-
r#"normalize-stderr-32bit: "something (32 bits)" -> "something ($WORD bits)""#,
40-
"something (32 bits)",
41-
"something ($WORD bits)",
42-
)];
38+
let good_data = &[
39+
(
40+
r#""something (32 bits)" -> "something ($WORD bits)""#,
41+
"something (32 bits)",
42+
"something ($WORD bits)",
43+
),
44+
(r#" " with whitespace" -> " replacement""#, " with whitespace", " replacement"),
45+
];
4346

4447
for &(input, expected_regex, expected_replacement) in good_data {
4548
let parsed = parse_normalize_rule(input);
@@ -49,15 +52,15 @@ fn test_parse_normalize_rule() {
4952
}
5053

5154
let bad_data = &[
52-
r#"normalize-stderr-32bit "something (32 bits)" -> "something ($WORD bits)""#,
53-
r#"normalize-stderr-16bit: something (16 bits) -> something ($WORD bits)"#,
54-
r#"normalize-stderr-32bit: something (32 bits) -> something ($WORD bits)"#,
55-
r#"normalize-stderr-32bit: "something (32 bits) -> something ($WORD bits)"#,
56-
r#"normalize-stderr-32bit: "something (32 bits)" -> "something ($WORD bits)"#,
57-
r#"normalize-stderr-32bit: "something (32 bits)" -> "something ($WORD bits)"."#,
55+
r#"something (11 bits) -> something ($WORD bits)"#,
56+
r#"something (12 bits) -> something ($WORD bits)"#,
57+
r#""something (13 bits) -> something ($WORD bits)"#,
58+
r#""something (14 bits)" -> "something ($WORD bits)"#,
59+
r#""something (15 bits)" -> "something ($WORD bits)"."#,
5860
];
5961

6062
for &input in bad_data {
63+
println!("- {input:?}");
6164
let parsed = parse_normalize_rule(input);
6265
assert_eq!(parsed, None);
6366
}
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
run-make/branch-protection-check-IBT/Makefile
21
run-make/cat-and-grep-sanity-check/Makefile
32
run-make/extern-fn-reachable/Makefile
43
run-make/jobserver-error/Makefile
5-
run-make/libs-through-symlinks/Makefile
64
run-make/split-debuginfo/Makefile
75
run-make/symbol-mangling-hashed/Makefile
86
run-make/translation/Makefile

tests/run-make/branch-protection-check-IBT/Makefile

-21
This file was deleted.

0 commit comments

Comments
 (0)