-
Notifications
You must be signed in to change notification settings - Fork 13.4k
Update BARE_TRAIT_OBJECT and ELLIPSIS_INCLUSIVE_RANGE_PATTERNS to errors in Rust 2021 #83213
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 9 commits
c2d0f14
1d84947
152c862
4f67392
2c1429c
a2b1347
d7b2263
cebc520
aa4457f
43f9d0a
cd8392d
4e25ae4
4193b0b
7a50392
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
Trait objects must include the `dyn` keyword. | ||
|
||
Erroneous code example: | ||
|
||
```edition2021,compile_fail,E0782 | ||
trait Foo {} | ||
fn test(arg: Box<Foo>) {} // error! | ||
``` | ||
|
||
Trait objects are a way to call methods on types that are not known until | ||
runtime but conform to some trait. | ||
|
||
Trait objects should be formed with `Box<dyn Foo>`, but in the code above | ||
`dyn` is left off. | ||
|
||
This makes it harder to see that `arg` is a trait object and not a | ||
simply a heap allocated type called `Foo`. | ||
|
||
To fix this issue, add `dyn` before the trait name. | ||
|
||
```edition2021 | ||
trait Foo {} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you still need to specify the edition here (as least to be coherent with the failing code example). |
||
fn test(arg: Box<dyn Foo>) {} // ok! | ||
``` | ||
|
||
This used to be allowed before edition 2021, but is now an error. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
The range pattern `...` is no longer allowed. | ||
rylev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Erroneous code example: | ||
|
||
```edition2021,compile_fail,E0783 | ||
match 2u8 { | ||
0...9 => println!("Got a number less than 10"), // error! | ||
_ => println!("Got a number 10 or more"), | ||
} | ||
``` | ||
|
||
Older Rust code using previous editions allowed `...` to stand for exclusive | ||
ranges which are now signified using `..=`. | ||
|
||
To make this code compile replace the `...` with `..=`. | ||
|
||
```edition2021 | ||
match 2u8 { | ||
0..=9 => println!("Got a number less than 10"), // ok! | ||
_ => println!("Got a number 10 or more"), | ||
} | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1655,7 +1655,11 @@ declare_lint! { | |
/// [`..` range expression]: https://doc.rust-lang.org/reference/expressions/range-expr.html | ||
pub ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, | ||
Warn, | ||
"`...` range patterns are deprecated" | ||
"`...` range patterns are deprecated", | ||
@future_incompatible = FutureIncompatibleInfo { | ||
reference: "issue #80165 <https://github.com/rust-lang/rust/issues/80165>", | ||
edition: Some(Edition::Edition2021), | ||
}; | ||
} | ||
|
||
#[derive(Default)] | ||
|
@@ -1699,32 +1703,57 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns { | |
let suggestion = "use `..=` for an inclusive range"; | ||
if parenthesise { | ||
self.node_id = Some(pat.id); | ||
cx.struct_span_lint(ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, pat.span, |lint| { | ||
let end = expr_to_string(&end); | ||
let replace = match start { | ||
Some(start) => format!("&({}..={})", expr_to_string(&start), end), | ||
None => format!("&(..={})", end), | ||
}; | ||
lint.build(msg) | ||
.span_suggestion( | ||
pat.span, | ||
suggestion, | ||
replace, | ||
Applicability::MachineApplicable, | ||
) | ||
.emit(); | ||
}); | ||
let end = expr_to_string(&end); | ||
let replace = match start { | ||
Some(start) => format!("&({}..={})", expr_to_string(&start), end), | ||
None => format!("&(..={})", end), | ||
}; | ||
if join.edition() >= Edition::Edition2021 { | ||
let mut err = | ||
rustc_errors::struct_span_err!(cx.sess, pat.span, E0783, "{}", msg,); | ||
err.span_suggestion( | ||
pat.span, | ||
suggestion, | ||
replace, | ||
Applicability::MachineApplicable, | ||
) | ||
.emit(); | ||
} else { | ||
cx.struct_span_lint(ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, pat.span, |lint| { | ||
lint.build(msg) | ||
.span_suggestion( | ||
pat.span, | ||
suggestion, | ||
replace, | ||
Applicability::MachineApplicable, | ||
) | ||
.emit(); | ||
}); | ||
} | ||
} else { | ||
cx.struct_span_lint(ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, join, |lint| { | ||
lint.build(msg) | ||
.span_suggestion_short( | ||
join, | ||
suggestion, | ||
"..=".to_owned(), | ||
Applicability::MachineApplicable, | ||
) | ||
.emit(); | ||
}); | ||
let replace = "..=".to_owned(); | ||
if join.edition() >= Edition::Edition2021 { | ||
let mut err = | ||
rustc_errors::struct_span_err!(cx.sess, pat.span, E0783, "{}", msg,); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note: lint passes typically do not have side effects, if a lint is disabled there should ideally be no behavior differences between running the pass or not (and that's a potential optimization to do in the future). I'm not sure how strictly the compiler team follows this; it's possible that when we do such optimizations we'll have to go back and audit our lints (which is fine!), but I just want to flag this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The only concern is that performing this check in the appropriate place as if we had no editions could cause divergence in the behavior. Emitting the error in the same place as we currently emit the lint should make the transition less painful than if we start denying in places where we currently don't even lint. That being said, this could be accepted as acceptable breakage to bestow on the ecosystem in the name of codebase cleanliness. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, I missed this discussion the first time around. A few thoughts:
Moving the logic out from the "lint helper" just seems like a bit of extra work, I'm not sure what would be a nice place to put it, we'd have to reproduce the "traverse the tree" code. That said, I don't object to it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @estebank I don't quite understand how behavior would change -- do you just mean altering the order of errors that get emitted? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm also not quite following. That said, I don't think we hold ourselves to a sufficiently high standard here today that I would be comfortable applying a "no pass" optimization if lints were disabled. We also merge all built-in lint passes into a few groups (by when they need to run), so there's not really an actual "per-lint" full traversal of any tree, so it's not obvious a major win is to be had. (But I don't think we've profiled much). I think this seems OK for now. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As long as we have tests for the hard error, I'm comfortable with it. |
||
err.span_suggestion_short( | ||
join, | ||
suggestion, | ||
replace, | ||
Applicability::MachineApplicable, | ||
) | ||
.emit(); | ||
} else { | ||
cx.struct_span_lint(ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, join, |lint| { | ||
lint.build(msg) | ||
.span_suggestion_short( | ||
join, | ||
suggestion, | ||
replace, | ||
Applicability::MachineApplicable, | ||
) | ||
.emit(); | ||
}); | ||
} | ||
}; | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,6 +30,7 @@ use rustc_middle::ty::{ | |
use rustc_session::lint; | ||
use rustc_session::lint::builtin::BARE_TRAIT_OBJECTS; | ||
use rustc_session::parse::feature_err; | ||
use rustc_span::edition::Edition; | ||
use rustc_span::source_map::{original_sp, DUMMY_SP}; | ||
use rustc_span::symbol::{kw, sym, Ident}; | ||
use rustc_span::{self, BytePos, MultiSpan, Span}; | ||
|
@@ -969,20 +970,42 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | |
if let TyKind::TraitObject([poly_trait_ref, ..], _, TraitObjectSyntax::None) = | ||
self_ty.kind | ||
{ | ||
self.tcx.struct_span_lint_hir(BARE_TRAIT_OBJECTS, hir_id, self_ty.span, |lint| { | ||
let mut db = lint | ||
.build(&format!("trait objects without an explicit `dyn` are deprecated")); | ||
let (sugg, app) = match self.tcx.sess.source_map().span_to_snippet(self_ty.span) | ||
{ | ||
Ok(s) if poly_trait_ref.trait_ref.path.is_global() => { | ||
(format!("<dyn ({})>", s), Applicability::MachineApplicable) | ||
} | ||
Ok(s) => (format!("<dyn {}>", s), Applicability::MachineApplicable), | ||
Err(_) => ("<dyn <type>>".to_string(), Applicability::HasPlaceholders), | ||
}; | ||
db.span_suggestion(self_ty.span, "use `dyn`", sugg, app); | ||
db.emit() | ||
}); | ||
let msg = "trait objects without an explicit `dyn` are deprecated"; | ||
let (sugg, app) = match self.tcx.sess.source_map().span_to_snippet(self_ty.span) { | ||
Ok(s) if poly_trait_ref.trait_ref.path.is_global() => { | ||
(format!("<dyn ({})>", s), Applicability::MachineApplicable) | ||
} | ||
Ok(s) => (format!("<dyn {}>", s), Applicability::MachineApplicable), | ||
Err(_) => ("<dyn <type>>".to_string(), Applicability::HasPlaceholders), | ||
}; | ||
let replace = String::from("use `dyn`"); | ||
if self.sess().edition() >= Edition::Edition2021 { | ||
let mut err = rustc_errors::struct_span_err!( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I do wish we could make this more unified. I feel like the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Filed #84625 |
||
self.sess(), | ||
self_ty.span, | ||
E0783, | ||
"{}", | ||
msg, | ||
); | ||
err.span_suggestion( | ||
self_ty.span, | ||
&sugg, | ||
replace, | ||
Applicability::MachineApplicable, | ||
) | ||
.emit(); | ||
} else { | ||
self.tcx.struct_span_lint_hir( | ||
BARE_TRAIT_OBJECTS, | ||
hir_id, | ||
self_ty.span, | ||
|lint| { | ||
let mut db = lint.build(msg); | ||
db.span_suggestion(self_ty.span, &replace, sugg, app); | ||
db.emit() | ||
}, | ||
); | ||
} | ||
} | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// edition:2018 | ||
#[deny(bare_trait_objects)] | ||
|
||
fn function(x: &SomeTrait, y: Box<SomeTrait>) { | ||
//~^ ERROR trait objects without an explicit `dyn` are deprecated | ||
//~| WARN this was previously accepted | ||
//~| ERROR trait objects without an explicit `dyn` are deprecated | ||
//~| WARN this was previously accepted | ||
let _x: &SomeTrait = todo!(); | ||
//~^ ERROR trait objects without an explicit `dyn` are deprecated | ||
//~| WARN this was previously accepted | ||
} | ||
|
||
trait SomeTrait {} | ||
|
||
fn main() {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
error: trait objects without an explicit `dyn` are deprecated | ||
--> $DIR/dyn-2018-edition-lint.rs:4:17 | ||
| | ||
LL | fn function(x: &SomeTrait, y: Box<SomeTrait>) { | ||
| ^^^^^^^^^ help: use `dyn`: `dyn SomeTrait` | ||
| | ||
note: the lint level is defined here | ||
--> $DIR/dyn-2018-edition-lint.rs:2:8 | ||
| | ||
LL | #[deny(bare_trait_objects)] | ||
| ^^^^^^^^^^^^^^^^^^ | ||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2021 edition! | ||
= note: for more information, see issue #80165 <https://github.com/rust-lang/rust/issues/80165> | ||
|
||
error: trait objects without an explicit `dyn` are deprecated | ||
--> $DIR/dyn-2018-edition-lint.rs:4:35 | ||
| | ||
LL | fn function(x: &SomeTrait, y: Box<SomeTrait>) { | ||
| ^^^^^^^^^ help: use `dyn`: `dyn SomeTrait` | ||
| | ||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2021 edition! | ||
= note: for more information, see issue #80165 <https://github.com/rust-lang/rust/issues/80165> | ||
|
||
error: trait objects without an explicit `dyn` are deprecated | ||
--> $DIR/dyn-2018-edition-lint.rs:9:14 | ||
| | ||
LL | let _x: &SomeTrait = todo!(); | ||
| ^^^^^^^^^ help: use `dyn`: `dyn SomeTrait` | ||
| | ||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2021 edition! | ||
= note: for more information, see issue #80165 <https://github.com/rust-lang/rust/issues/80165> | ||
|
||
error: aborting due to 3 previous errors | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
// edition:2021 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we have tests for this for edition 2018? Just want to make sure we test for the applicable suggestions not to break. Also, I don't think we've had a discussion around test coverage for people updating from editions earlier than CURRENT - 1. Should we be having that conversation? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My understanding is that that's simply not supported, you have to upgrade editions one by one. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So, we are currently testing Rust2018 though virtue of the compiler being on Rust 2018 itself. However, once it moves to Rust 2021 (no idea when that will happen), the tests that currently only assume the current edition (like this one) will need to be updated. We could explicitly make a test right now, making sure that the lint triggers in 2018 which will be a bit of a duplicate test but will ensure we still test this code path when the compiler is updated. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would be in favor of making an explicit test for code written in Rust 2018 as part of this PR. We want to check that it is still doing the expecting thing on rust 2018 code. |
||
|
||
fn function(x: &SomeTrait, y: Box<SomeTrait>) { | ||
//~^ ERROR trait objects must include the `dyn` keyword | ||
//~| ERROR trait objects must include the `dyn` keyword | ||
let _x: &SomeTrait = todo!(); | ||
//~^ ERROR trait objects must include the `dyn` keyword | ||
} | ||
|
||
trait SomeTrait {} | ||
|
||
fn main() {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
error[E0782]: trait objects must include the `dyn` keyword | ||
--> $DIR/dyn-2021-edition-error.rs:6:14 | ||
| | ||
LL | let _x: &SomeTrait = todo!(); | ||
| ^^^^^^^^^ | ||
| | ||
help: add `dyn` keyword before this trait | ||
| | ||
LL | let _x: &dyn SomeTrait = todo!(); | ||
| ^^^ | ||
|
||
error[E0782]: trait objects must include the `dyn` keyword | ||
--> $DIR/dyn-2021-edition-error.rs:3:17 | ||
| | ||
LL | fn function(x: &SomeTrait, y: Box<SomeTrait>) { | ||
| ^^^^^^^^^ | ||
| | ||
help: add `dyn` keyword before this trait | ||
| | ||
LL | fn function(x: &dyn SomeTrait, y: Box<SomeTrait>) { | ||
| ^^^ | ||
|
||
error[E0782]: trait objects must include the `dyn` keyword | ||
--> $DIR/dyn-2021-edition-error.rs:3:35 | ||
| | ||
LL | fn function(x: &SomeTrait, y: Box<SomeTrait>) { | ||
| ^^^^^^^^^ | ||
| | ||
help: add `dyn` keyword before this trait | ||
| | ||
LL | fn function(x: &SomeTrait, y: Box<dyn SomeTrait>) { | ||
| ^^^ | ||
|
||
error: aborting due to 3 previous errors | ||
|
||
For more information about this error, try `rustc --explain E0782`. |
Uh oh!
There was an error while loading. Please reload this page.