Auto merge of #116855 - matthiaskrgr:rollup-i2izdwb, r=matthiaskrgr

Rollup of 5 pull requests

Successful merges:

 - #111072 (Add new simpler and more explicit syntax for check-cfg)
 - #116717 (Special case iterator chain checks for suggestion)
 - #116719 (Add MonoItems and Instance to stable_mir)
 - #116787 (Implement an internal lint encouraging use of `Span::eq_ctxt`)
 - #116827 (Make `handle_options` public again.)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2023-10-17 18:51:46 +00:00
commit 94ba57cef4
110 changed files with 1948 additions and 327 deletions

View file

@ -628,10 +628,10 @@ Using this flag looks like this:
```bash
$ rustdoc src/lib.rs -Z unstable-options \
--check-cfg='names()' --check-cfg='values(feature, "foo", "bar")'
--check-cfg='cfg(feature, values("foo", "bar"))'
```
The example above check every well known names (`target_os`, `doc`, `test`, ... via `names()`)
The example above check every well known names and values (`target_os`, `doc`, `test`, ...)
and check the values of `feature`: `foo` and `bar`.
### `--generate-link-to-definition`: Generate links on types in source code

View file

@ -10,18 +10,185 @@ This feature allows you to enable complete or partial checking of configuration.
check them. The `--check-cfg` option takes a value, called the _check cfg specification_. The
check cfg specification is parsed using the Rust metadata syntax, just as the `--cfg` option is.
`--check-cfg` option can take one of two forms:
`--check-cfg` option take one form:
1. `--check-cfg names(...)` enables checking condition names.
2. `--check-cfg values(...)` enables checking the values within list-valued conditions.
These two options are independent. `names` checks only the namespace of condition names
while `values` checks only the namespace of the values of list-valued conditions.
1. `--check-cfg cfg(...)` enables checking the values within list-valued conditions.
NOTE: No implicit expectation is added when using `--cfg` for both forms. Users are expected to
pass all expected names and values using `names(...)` and `values(...)`.
pass all expected names and values using `cfg(...)`.
## The `names(...)` form
## The `cfg(...)` form
The `cfg(...)` form enables checking the values within list-valued conditions. It has this
basic form:
```bash
rustc --check-cfg 'cfg(name1, ..., nameN, values("value1", "value2", ... "valueN"))'
```
where `name` is a bare identifier (has no quotes) and each `"value"` term is a quoted literal
string. `name` specifies the name of the condition, such as `feature` or `my_cfg`.
When the `cfg(...)` option is specified, `rustc` will check every `#[cfg(name = "value")]`
attribute, `#[cfg_attr(name = "value")]` attribute, `#[link(name = "a", cfg(name = "value"))]`
and `cfg!(name = "value")` call. It will check that the `"value"` specified is present in the
list of expected values. If `"value"` is not in it, then `rustc` will report an `unexpected_cfgs`
lint diagnostic. The default diagnostic level for this lint is `Warn`.
To enable checking of values, but to provide an empty set of expected values, use these forms:
```bash
rustc --check-cfg 'cfg(name1, ..., nameN)'
rustc --check-cfg 'cfg(name1, ..., nameN, values())'
```
To enable checking of name but not values (i.e. unknown expected values), use this form:
```bash
rustc --check-cfg 'cfg(name1, ..., nameN, values(any()))'
```
The `--check-cfg cfg(...)` option can be repeated, both for the same condition name and for
different names. If it is repeated for the same condition name, then the sets of values for that
condition are merged together (presedence is given to `any()`).
## Well known names and values
`rustc` has a internal list of well known names and their corresponding values.
Those well known names and values follows the same stability as what they refer to.
Well known values checking is always enabled as long as a `--check-cfg` argument is present.
Well known names checking is always enable as long as a `--check-cfg` argument is present
**unless** any `cfg(any())` argument is passed.
To disable checking of well known names, use this form:
```bash
rustc --check-cfg 'cfg(any())'
```
NOTE: If one want to enable values and names checking without having any cfg to declare, one
can use an empty `cfg()` argument.
## Examples
Consider this command line:
```bash
rustc --check-cfg 'cfg(feature, values("lion", "zebra"))' \
--cfg 'feature="lion"' -Z unstable-options \
example.rs
```
This command line indicates that this crate has two features: `lion` and `zebra`. The `lion`
feature is enabled, while the `zebra` feature is disabled. Exhaustive checking of names and
values are enabled by default. Consider compiling this code:
```rust
// This is expected, and tame_lion() will be compiled
#[cfg(feature = "lion")]
fn tame_lion(lion: Lion) {}
// This is expected, and ride_zebra() will NOT be compiled.
#[cfg(feature = "zebra")]
fn ride_zebra(zebra: Zebra) {}
// This is UNEXPECTED, and will cause a compiler warning (by default).
#[cfg(feature = "platypus")]
fn poke_platypus() {}
// This is UNEXPECTED, because 'feechure' is not a known condition name,
// and will cause a compiler warning (by default).
#[cfg(feechure = "lion")]
fn tame_lion() {}
// This is UNEXPECTED, because 'windows' is a well known condition name,
// and because 'windows' doens't take any values,
// and will cause a compiler warning (by default).
#[cfg(windows = "unix")]
fn tame_windows() {}
```
### Example: Checking condition names, but not values
```bash
# This turns on checking for condition names, but not values, such as 'feature' values.
rustc --check-cfg 'cfg(is_embedded, has_feathers, values(any()))' \
--cfg has_feathers -Z unstable-options
```
```rust
#[cfg(is_embedded)] // This is expected as "is_embedded" was provided in cfg()
fn do_embedded() {} // and because names exhaustiveness was not disabled
#[cfg(has_feathers)] // This is expected as "has_feathers" was provided in cfg()
fn do_features() {} // and because names exhaustiveness was not disbaled
#[cfg(has_feathers = "zapping")] // This is expected as "has_feathers" was provided in cfg()
// and because no value checking was enable for "has_feathers"
// no warning is emitted for the value "zapping"
fn do_zapping() {}
#[cfg(has_mumble_frotz)] // This is UNEXPECTED because names checking is enable and
// "has_mumble_frotz" was not provided in cfg()
fn do_mumble_frotz() {}
```
### Example: Checking feature values, but not condition names
```bash
# This turns on checking for feature values, but not for condition names.
rustc --check-cfg 'configure(feature, values("zapping", "lasers"))' \
--check-cfg 'cfg(any())' \
--cfg 'feature="zapping"' -Z unstable-options
```
```rust
#[cfg(is_embedded)] // This is doesn't raise a warning, because names checking was
// disabled by 'cfg(any())'
fn do_embedded() {}
#[cfg(has_feathers)] // Same as above, 'cfg(any())' was provided so no name
// checking is performed
fn do_features() {}
#[cfg(feature = "lasers")] // This is expected, "lasers" is in the cfg(feature) list
fn shoot_lasers() {}
#[cfg(feature = "monkeys")] // This is UNEXPECTED, because "monkeys" is not in the
// cfg(feature) list
fn write_shakespeare() {}
```
### Example: Checking both condition names and feature values
```bash
# This turns on checking for feature values and for condition names.
rustc --check-cfg 'cfg(is_embedded, has_feathers)' \
--check-cfg 'cfg(feature, values("zapping", "lasers"))' \
--cfg has_feathers --cfg 'feature="zapping"' -Z unstable-options
```
```rust
#[cfg(is_embedded)] // This is expected because "is_embedded" was provided in cfg()
fn do_embedded() {} // and doesn't take any value
#[cfg(has_feathers)] // This is expected because "has_feathers" was provided in cfg()
fn do_features() {} // and deosn't take any value
#[cfg(has_mumble_frotz)] // This is UNEXPECTED, because "has_mumble_frotz" was never provided
fn do_mumble_frotz() {}
#[cfg(feature = "lasers")] // This is expected, "lasers" is in the cfg(feature) list
fn shoot_lasers() {}
#[cfg(feature = "monkeys")] // This is UNEXPECTED, because "monkeys" is not in
// the cfg(feature) list
fn write_shakespeare() {}
```
## The deprecated `names(...)` form
The `names(...)` form enables checking the names. This form uses a named list:
@ -56,7 +223,7 @@ The first form enables checking condition names, while specifying that there are
condition names (outside of the set of well-known names defined by `rustc`). Omitting the
`--check-cfg 'names(...)'` option does not enable checking condition names.
## The `values(...)` form
## The deprecated `values(...)` form
The `values(...)` form enables checking the values within list-valued conditions. It has this
form:
@ -87,120 +254,3 @@ condition are merged together.
If `values()` is specified, then `rustc` will enable the checking of well-known values defined
by itself. Note that it's necessary to specify the `values()` form to enable the checking of
well known values, specifying the other forms doesn't implicitly enable it.
## Examples
Consider this command line:
```bash
rustc --check-cfg 'names(feature)' \
--check-cfg 'values(feature, "lion", "zebra")' \
--cfg 'feature="lion"' -Z unstable-options \
example.rs
```
This command line indicates that this crate has two features: `lion` and `zebra`. The `lion`
feature is enabled, while the `zebra` feature is disabled. Consider compiling this code:
```rust
// This is expected, and tame_lion() will be compiled
#[cfg(feature = "lion")]
fn tame_lion(lion: Lion) {}
// This is expected, and ride_zebra() will NOT be compiled.
#[cfg(feature = "zebra")]
fn ride_zebra(zebra: Zebra) {}
// This is UNEXPECTED, and will cause a compiler warning (by default).
#[cfg(feature = "platypus")]
fn poke_platypus() {}
// This is UNEXPECTED, because 'feechure' is not a known condition name,
// and will cause a compiler warning (by default).
#[cfg(feechure = "lion")]
fn tame_lion() {}
```
> Note: The `--check-cfg names(feature)` option is necessary only to enable checking the condition
> name, as in the last example. `feature` is a well-known (always-expected) condition name, and so
> it is not necessary to specify it in a `--check-cfg 'names(...)'` option. That option can be
> shortened to > `--check-cfg names()` in order to enable checking well-known condition names.
### Example: Checking condition names, but not values
```bash
# This turns on checking for condition names, but not values, such as 'feature' values.
rustc --check-cfg 'names(is_embedded, has_feathers)' \
--cfg has_feathers -Z unstable-options
```
```rust
#[cfg(is_embedded)] // This is expected as "is_embedded" was provided in names()
fn do_embedded() {}
#[cfg(has_feathers)] // This is expected as "has_feathers" was provided in names()
fn do_features() {}
#[cfg(has_feathers = "zapping")] // This is expected as "has_feathers" was provided in names()
// and because no value checking was enable for "has_feathers"
// no warning is emitted for the value "zapping"
fn do_zapping() {}
#[cfg(has_mumble_frotz)] // This is UNEXPECTED because names checking is enable and
// "has_mumble_frotz" was not provided in names()
fn do_mumble_frotz() {}
```
### Example: Checking feature values, but not condition names
```bash
# This turns on checking for feature values, but not for condition names.
rustc --check-cfg 'values(feature, "zapping", "lasers")' \
--cfg 'feature="zapping"' -Z unstable-options
```
```rust
#[cfg(is_embedded)] // This is doesn't raise a warning, because names checking was not
// enable (ie not names())
fn do_embedded() {}
#[cfg(has_feathers)] // Same as above, --check-cfg names(...) was never used so no name
// checking is performed
fn do_features() {}
#[cfg(feature = "lasers")] // This is expected, "lasers" is in the values(feature) list
fn shoot_lasers() {}
#[cfg(feature = "monkeys")] // This is UNEXPECTED, because "monkeys" is not in the
// --check-cfg values(feature) list
fn write_shakespeare() {}
```
### Example: Checking both condition names and feature values
```bash
# This turns on checking for feature values and for condition names.
rustc --check-cfg 'names(is_embedded, has_feathers)' \
--check-cfg 'values(feature, "zapping", "lasers")' \
--cfg has_feathers --cfg 'feature="zapping"' -Z unstable-options
```
```rust
#[cfg(is_embedded)] // This is expected because "is_embedded" was provided in names()
fn do_embedded() {}
#[cfg(has_feathers)] // This is expected because "has_feathers" was provided in names()
fn do_features() {}
#[cfg(has_mumble_frotz)] // This is UNEXPECTED, because has_mumble_frotz is not in the
// --check-cfg names(...) list
fn do_mumble_frotz() {}
#[cfg(feature = "lasers")] // This is expected, "lasers" is in the values(feature) list
fn shoot_lasers() {}
#[cfg(feature = "monkeys")] // This is UNEXPECTED, because "monkeys" is not in
// the values(feature) list
fn write_shakespeare() {}
```

View file

@ -701,7 +701,7 @@ fn deref_method_same_type<'tcx>(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool {
fn in_postfix_position<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
if let Some(parent) = get_parent_expr(cx, e)
&& parent.span.ctxt() == e.span.ctxt()
&& parent.span.eq_ctxt(e.span)
{
match parent.kind {
ExprKind::Call(child, _) | ExprKind::MethodCall(_, child, _, _) | ExprKind::Index(child, _, _)

View file

@ -241,7 +241,7 @@ fn try_parse_contains<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Optio
},
],
_,
) if key_span.ctxt() == expr.span.ctxt() => {
) if key_span.eq_ctxt(expr.span) => {
let id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?;
let expr = ContainsExpr {
negated,

View file

@ -274,7 +274,7 @@ fn check_array(cx: &EarlyContext<'_>, expr: &Expr) {
for element in array {
if_chain! {
if let ExprKind::Binary(ref op, ref lhs, _) = element.kind;
if has_unary_equivalent(op.node) && lhs.span.ctxt() == op.span.ctxt();
if has_unary_equivalent(op.node) && lhs.span.eq_ctxt(op.span);
let space_span = lhs.span.between(op.span);
if let Some(space_snippet) = snippet_opt(cx, space_span);
let lint_span = lhs.span.with_lo(lhs.span.hi());

View file

@ -31,7 +31,7 @@ impl LateLintPass<'_> for UnderscoreTyped {
if !in_external_macro(cx.tcx.sess, local.span);
if let Some(ty) = local.ty; // Ensure that it has a type defined
if let TyKind::Infer = &ty.kind; // that type is '_'
if local.span.ctxt() == ty.span.ctxt();
if local.span.eq_ctxt(ty.span);
then {
// NOTE: Using `is_from_proc_macro` on `init` will require that it's initialized,
// this doesn't. Alternatively, `WithSearchPat` can be implemented for `Ty`

View file

@ -59,7 +59,7 @@ impl<'tcx> QuestionMark {
let Some(init) = local.init &&
local.els.is_none() &&
local.ty.is_none() &&
init.span.ctxt() == stmt.span.ctxt() &&
init.span.eq_ctxt(stmt.span) &&
let Some(if_let_or_match) = IfLetOrMatch::parse(cx, init)
{
match if_let_or_match {

View file

@ -57,7 +57,7 @@ fn check_arm<'tcx>(
}
},
};
if outer_pat.span.ctxt() == inner_scrutinee.span.ctxt();
if outer_pat.span.eq_ctxt(inner_scrutinee.span);
// match expression must be a local binding
// match <local> { .. }
if let Some(binding_id) = path_to_local(peel_ref_operators(cx, inner_scrutinee));

View file

@ -119,7 +119,7 @@ where
// it's being passed by value.
let scrutinee = peel_hir_expr_refs(scrutinee).0;
let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app);
let scrutinee_str = if scrutinee.span.ctxt() == expr.span.ctxt() && scrutinee.precedence().order() < PREC_POSTFIX {
let scrutinee_str = if scrutinee.span.eq_ctxt(expr.span) && scrutinee.precedence().order() < PREC_POSTFIX {
format!("({scrutinee_str})")
} else {
scrutinee_str.into()
@ -130,7 +130,7 @@ where
if_chain! {
if !some_expr.needs_unsafe_block;
if let Some(func) = can_pass_as_func(cx, id, some_expr.expr);
if func.span.ctxt() == some_expr.expr.span.ctxt();
if func.span.eq_ctxt(some_expr.expr.span);
then {
snippet_with_applicability(cx, func.span, "..", &mut app).into_owned()
} else {

View file

@ -56,7 +56,7 @@ pub(super) fn check<'tcx>(
// lint, with note if neither arg is > 1 line and both map() and
// unwrap_or_else() have the same span
let multiline = map_snippet.lines().count() > 1 || unwrap_snippet.lines().count() > 1;
let same_span = map_arg.span.ctxt() == unwrap_arg.span.ctxt();
let same_span = map_arg.span.eq_ctxt(unwrap_arg.span);
if same_span && !multiline {
let var_snippet = snippet(cx, recv.span, "..");
span_lint_and_sugg(

View file

@ -125,7 +125,7 @@ fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
if let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar(_)) = &arg.kind;
if let ExprKind::Call(called, [inner_expr]) = &inner_expr_with_q.kind;
if let ExprKind::Path(QPath::LangItem(LangItem::TryTraitBranch, ..)) = &called.kind;
if expr.span.ctxt() == inner_expr.span.ctxt();
if expr.span.eq_ctxt(inner_expr.span);
let expr_ty = cx.typeck_results().expr_ty(expr);
let inner_ty = cx.typeck_results().expr_ty(inner_expr);
if expr_ty == inner_ty;

View file

@ -51,7 +51,7 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions {
|| (path.ident.name == sym!(set_mode)
&& cx.tcx.is_diagnostic_item(sym::FsPermissions, adt.did()));
if let ExprKind::Lit(_) = param.kind;
if param.span.ctxt() == expr.span.ctxt();
if param.span.eq_ctxt(expr.span);
then {
let Some(snip) = snippet_opt(cx, param.span) else {
@ -70,7 +70,7 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions {
if let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id();
if match_def_path(cx, def_id, &paths::PERMISSIONS_FROM_MODE);
if let ExprKind::Lit(_) = param.kind;
if param.span.ctxt() == expr.span.ctxt();
if param.span.eq_ctxt(expr.span);
if let Some(snip) = snippet_opt(cx, param.span);
if !snip.starts_with("0o");
then {

View file

@ -48,7 +48,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantAsyncBlock {
let Some(body_expr) = desugar_async_block(cx, expr) &&
let Some(expr) = desugar_await(peel_blocks(body_expr)) &&
// The await prefix must not come from a macro as its content could change in the future.
expr.span.ctxt() == body_expr.span.ctxt() &&
expr.span.eq_ctxt(body_expr.span) &&
// An async block does not have immediate side-effects from a `.await` point-of-view.
(!expr.can_have_side_effects() || desugar_async_block(cx, expr).is_some()) &&
let Some(shortened_span) = walk_span_to_context(expr.span, span.ctxt())

View file

@ -50,7 +50,7 @@ impl EarlyLintPass for DerefAddrOf {
if_chain! {
if let ExprKind::Unary(UnOp::Deref, ref deref_target) = e.kind;
if let ExprKind::AddrOf(_, ref mutability, ref addrof_target) = without_parens(deref_target).kind;
if deref_target.span.ctxt() == e.span.ctxt();
if deref_target.span.eq_ctxt(e.span);
if !addrof_target.span.from_expansion();
then {
let mut applicability = Applicability::MachineApplicable;

View file

@ -33,7 +33,7 @@ impl LateLintPass<'_> for ConfusingXorAndPow {
if !in_external_macro(cx.sess(), expr.span)
&& let ExprKind::Binary(op, left, right) = &expr.kind
&& op.node == BinOpKind::BitXor
&& left.span.ctxt() == right.span.ctxt()
&& left.span.eq_ctxt(right.span)
&& let ExprKind::Lit(lit_left) = &left.kind
&& let ExprKind::Lit(lit_right) = &right.kind
&& matches!(lit_right.node, LitKind::Int(..) | LitKind::Float(..))

View file

@ -245,7 +245,7 @@ impl<'a> PanicExpn<'a> {
return None;
};
let result = match name {
"panic" if arg.span.ctxt() == expr.span.ctxt() => Self::Empty,
"panic" if arg.span.eq_ctxt(expr.span) => Self::Empty,
"panic" | "panic_str" => Self::Str(arg),
"panic_display" | "panic_cold_display" => {
let ExprKind::AddrOf(_, _, e) = &arg.kind else {