Skip to content

Commit 6fc7023

Browse files
committed
Implemented checks for breaking traits.
We don't check trait items if the trait itself suffered breaking changes.
1 parent b016e26 commit 6fc7023

File tree

5 files changed

+96
-28
lines changed

5 files changed

+96
-28
lines changed

src/semcheck/mapping.rs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ use syntax::ast::Name;
1717
/// losslessly. The *access* to the stored data happens through the same API, however.
1818
#[derive(Default)]
1919
pub struct IdMapping {
20-
/// Toplevel items' old `DefId` mapped to new `DefId`, as well as old and new exports.
20+
/// Toplevel items' old `DefId` mapped to old and new `Def`.
2121
toplevel_mapping: HashMap<DefId, (Def, Def)>,
22-
/// Trait items' old `DefId` mapped to new `DefId`.
23-
trait_item_mapping: HashMap<DefId, (Def, Def)>,
22+
/// Trait items' old `DefId` mapped to old and new `Def`.
23+
trait_item_mapping: HashMap<DefId, (Def, Def, DefId)>,
2424
/// Other item's old `DefId` mapped to new `DefId`.
2525
internal_mapping: HashMap<DefId, DefId>,
2626
/// Children mapping, allowing us to enumerate descendants in `AdtDef`s.
@@ -43,8 +43,8 @@ impl IdMapping {
4343
}
4444

4545
/// Add any trait item's old and new `DefId`s.
46-
pub fn add_trait_item(&mut self, old: Def, new: Def) {
47-
self.trait_item_mapping.insert(old.def_id(), (old, new));
46+
pub fn add_trait_item(&mut self, old: Def, new: Def, trait_def_id: DefId) {
47+
self.trait_item_mapping.insert(old.def_id(), (old, new, trait_def_id));
4848
}
4949

5050
/// Add any other item's old and new `DefId`s.
@@ -95,6 +95,11 @@ impl IdMapping {
9595
}
9696
}
9797

98+
/// Return the `DefId` of the trait a given item belongs to.
99+
pub fn get_trait_def(&self, item_def_id: &DefId) -> Option<DefId> {
100+
self.trait_item_mapping.get(item_def_id).map(|t| t.2)
101+
}
102+
98103
/// Tell us whether a `DefId` is present in the mappings.
99104
pub fn contains_id(&self, old: DefId) -> bool {
100105
self.toplevel_mapping.contains_key(&old) ||
@@ -111,10 +116,11 @@ impl IdMapping {
111116
}
112117

113118
/// Iterate over the toplevel and trait item pairs.
114-
pub fn items<'a>(&'a self) -> impl Iterator<Item = &'a (Def, Def)> + 'a {
119+
pub fn items<'a>(&'a self) -> impl Iterator<Item = (Def, Def)> + 'a {
115120
self.toplevel_mapping
116121
.values()
117-
.chain(self.trait_item_mapping.values())
122+
.cloned()
123+
.chain(self.trait_item_mapping.values().map(|&(o, n, _)| (o, n)))
118124
}
119125

120126
/// Iterate over the item pairs of all children of a given item.

src/semcheck/traverse.rs

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,12 @@ pub fn run_analysis<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, old: DefId, new: DefI
4343
}
4444

4545
// third pass
46-
for &(old, new) in id_mapping.items() {
46+
for (old, new) in id_mapping.items() {
4747
diff_bounds(&mut changes, tcx, old.def_id(), new.def_id());
4848
}
4949

5050
// fourth pass
51-
for &(old, new) in id_mapping.items() {
51+
for (old, new) in id_mapping.items() {
5252
diff_types(&mut changes, &id_mapping, tcx, old, new);
5353
}
5454

@@ -134,11 +134,11 @@ fn diff_structure<'a, 'tcx>(changes: &mut ChangeSet,
134134
};
135135

136136
let output = o_vis == Public || n_vis == Public;
137-
changes.new_change(o.def.def_id(),
138-
n.def.def_id(),
137+
changes.new_change(o_def_id,
138+
n_def_id,
139139
o.ident.name,
140-
tcx.def_span(o.def.def_id()),
141-
tcx.def_span(n.def.def_id()),
140+
tcx.def_span(o_def_id),
141+
tcx.def_span(n_def_id),
142142
output);
143143

144144
if o_vis == Public && n_vis != Public {
@@ -419,13 +419,13 @@ fn diff_traits(changes: &mut ChangeSet,
419419
let old_def_id = old_def.def_id();
420420
let new_def_id = new_def.def_id();
421421

422-
id_mapping.add_trait_item(old_def, new_def);
423-
changes.new_change(old_def.def_id(),
424-
new_def.def_id(),
422+
id_mapping.add_trait_item(old_def, new_def, old);
423+
changes.new_change(old_def_id,
424+
new_def_id,
425425
*name,
426426
tcx.def_span(old_def_id),
427427
tcx.def_span(new_def_id),
428-
true);
428+
true); // TODO: bad to do this unconditionally
429429

430430
diff_generics(changes, id_mapping, tcx, true, old_def_id, new_def_id);
431431
diff_method(changes, tcx, old_item, new_item);
@@ -541,7 +541,9 @@ fn diff_types<'a, 'tcx>(changes: &mut ChangeSet<'tcx>,
541541
let old_def_id = old.def_id();
542542
let new_def_id = new.def_id();
543543

544-
if changes.item_breaking(old_def_id) {
544+
if changes.item_breaking(old_def_id) ||
545+
id_mapping.get_trait_def(&old_def_id)
546+
.map_or(false, |did| changes.item_breaking(did)) {
545547
return;
546548
}
547549

@@ -754,8 +756,12 @@ fn fold_to_new<'a, 'tcx, T>(id_mapping: &IdMapping,
754756
TyParam(param) => {
755757
if param.idx != 0 { // `Self` is special
756758
let old_did = index_map[&param.idx];
757-
let new_did = id_mapping.get_new_id(old_did);
758-
tcx.mk_param_from_def(&id_mapping.get_type_param(&new_did))
759+
if id_mapping.contains_id(old_did) {
760+
let new_did = id_mapping.get_new_id(old_did);
761+
tcx.mk_param_from_def(&id_mapping.get_type_param(&new_did))
762+
} else {
763+
tcx.mk_ty(TyParam(param))
764+
}
759765
} else {
760766
tcx.mk_ty(TyParam(param))
761767
}

tests/cases/traits/new.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,17 @@ pub trait Abc {
1212
fn test8(_: &Self) -> u8;
1313
fn test9(&self) -> u8;
1414
}
15+
16+
pub trait Bcd<A> {}
17+
18+
pub trait Cde {}
19+
20+
pub trait Def<A, B> {
21+
// The method is not broken - the impls are, but calls should work as expected, as
22+
// long as a proper impl is presented. Maybe this will need some more careful handling.
23+
fn def(&self, a: B) -> bool;
24+
}
25+
26+
pub trait Efg<A> {
27+
fn efg(&self, a: A) -> bool;
28+
}

tests/cases/traits/old.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,15 @@ pub trait Abc {
1212
fn test8(&self) -> u8;
1313
fn test9(_: &Self) -> u8;
1414
}
15+
16+
pub trait Bcd {}
17+
18+
pub trait Cde<A> {}
19+
20+
pub trait Def<A> {
21+
fn def(&self, a: A) -> bool;
22+
}
23+
24+
pub trait Efg<A, B> {
25+
fn efg(&self, a: B) -> bool;
26+
}

tests/cases/traits/stdout

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,6 @@ note: added defaulted item to trait (technically breaking)
3636
10 | | }
3737
| |_____^
3838

39-
warning: breaking changes in `test7`
40-
--> $REPO_PATH/tests/cases/traits/new.rs:11:5
41-
|
42-
11 | fn test7() -> u16;
43-
| ^^^^^^^^^^^^^^^^^^
44-
|
45-
= warning: type error: expected u8, found u16 (breaking)
46-
4739
warning: breaking changes in `test8`
4840
--> $REPO_PATH/tests/cases/traits/new.rs:12:5
4941
|
@@ -60,3 +52,41 @@ warning: technically breaking changes in `test9`
6052
|
6153
= note: added self-argument to method (technically breaking)
6254

55+
warning: breaking changes in `Bcd`
56+
--> $REPO_PATH/tests/cases/traits/new.rs:16:1
57+
|
58+
16 | pub trait Bcd<A> {}
59+
| ^^^^^^^^^^^^^^^^^^^
60+
|
61+
= warning: type parameter added (breaking)
62+
63+
warning: breaking changes in `Cde`
64+
--> $REPO_PATH/tests/cases/traits/new.rs:18:1
65+
|
66+
18 | pub trait Cde {}
67+
| ^^^^^^^^^^^^^^^^
68+
|
69+
= warning: type parameter removed (breaking)
70+
71+
warning: breaking changes in `Def`
72+
--> $REPO_PATH/tests/cases/traits/new.rs:20:1
73+
|
74+
20 | / pub trait Def<A, B> {
75+
21 | | // The method is not broken - the impls are, but calls should work as expected, as
76+
22 | | // long as a proper impl is presented. Maybe this will need some more careful handling.
77+
23 | | fn def(&self, a: B) -> bool;
78+
24 | | }
79+
| |_^
80+
|
81+
= warning: type parameter added (breaking)
82+
83+
warning: breaking changes in `Efg`
84+
--> $REPO_PATH/tests/cases/traits/new.rs:26:1
85+
|
86+
26 | / pub trait Efg<A> {
87+
27 | | fn efg(&self, a: A) -> bool;
88+
28 | | }
89+
| |_^
90+
|
91+
= warning: type parameter removed (breaking)
92+

0 commit comments

Comments
 (0)