Auto merge of #102995 - JohnTitor:rollup-yomkwge, r=JohnTitor
Rollup of 7 pull requests Successful merges: - #102641 (Support casting boxes to dyn*) - #102836 (rustc_target: Fix json target specs using LLD linker flavors in link args) - #102949 (should-skip-this: add missing backslash) - #102967 (Add test for issue 102964) - #102971 (tidy: error if a lang feature is already present) - #102974 (Fix small word dupe typos) - #102980 (rustdoc: merge separate `.item-info` CSS) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
3cf5fc58d5
25 changed files with 229 additions and 162 deletions
|
@ -14,6 +14,7 @@ use rustc_middle::ty::cast::{CastTy, IntTy};
|
|||
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
|
||||
use rustc_middle::ty::{self, adjustment::PointerCast, Instance, Ty, TyCtxt};
|
||||
use rustc_span::source_map::{Span, DUMMY_SP};
|
||||
use rustc_target::abi::Size;
|
||||
|
||||
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||
#[instrument(level = "trace", skip(self, bx))]
|
||||
|
@ -285,6 +286,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
bug!("Only valid to do a DynStar cast into a DynStar type")
|
||||
};
|
||||
let vtable = get_vtable(bx.cx(), source.ty(self.mir, bx.tcx()), trait_ref);
|
||||
let vtable = bx.pointercast(vtable, bx.cx().type_ptr_to(bx.cx().type_isize()));
|
||||
// FIXME(dyn-star): this is probably not the best way to check if this is
|
||||
// a pointer, and really we should ensure that the value is a suitable
|
||||
// pointer earlier in the compilation process.
|
||||
let data = match operand.layout.pointee_info_at(bx.cx(), Size::ZERO) {
|
||||
Some(_) => bx.ptrtoint(data, bx.cx().type_isize()),
|
||||
None => data,
|
||||
};
|
||||
OperandValue::Pair(data, vtable)
|
||||
}
|
||||
mir::CastKind::Pointer(
|
||||
|
|
|
@ -53,8 +53,8 @@ unsafe {
|
|||
```
|
||||
|
||||
Here, transmute is being used to convert the types of the fn arguments.
|
||||
This pattern is incorrect because, because the type of `foo` is a function
|
||||
**item** (`typeof(foo)`), which is zero-sized, and the target type (`fn()`)
|
||||
This pattern is incorrect because the type of `foo` is a function **item**
|
||||
(`typeof(foo)`), which is zero-sized, and the target type (`fn()`)
|
||||
is a function pointer, which is not zero-sized.
|
||||
This pattern should be rewritten. There are a few possible ways to do this:
|
||||
|
||||
|
|
|
@ -1283,7 +1283,7 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
assert!(old_value.is_none());
|
||||
}
|
||||
|
||||
/// Process the region constraints and return any any errors that
|
||||
/// Process the region constraints and return any errors that
|
||||
/// result. After this, no more unification operations should be
|
||||
/// done -- or the compiler will panic -- but it is legal to use
|
||||
/// `resolve_vars_if_possible` as well as `fully_resolve`.
|
||||
|
|
|
@ -1739,11 +1739,15 @@ impl TargetOptions {
|
|||
self.lld_flavor_json,
|
||||
self.linker_is_gnu_json,
|
||||
);
|
||||
match linker_flavor {
|
||||
LinkerFlavor::Gnu(_, Lld::Yes)
|
||||
| LinkerFlavor::Darwin(_, Lld::Yes)
|
||||
| LinkerFlavor::Msvc(Lld::Yes) => {}
|
||||
_ => add_link_args_iter(args, linker_flavor, args_json.iter().cloned()),
|
||||
// Normalize to no lld to avoid asserts.
|
||||
let linker_flavor = match linker_flavor {
|
||||
LinkerFlavor::Gnu(cc, _) => LinkerFlavor::Gnu(cc, Lld::No),
|
||||
LinkerFlavor::Darwin(cc, _) => LinkerFlavor::Darwin(cc, Lld::No),
|
||||
LinkerFlavor::Msvc(_) => LinkerFlavor::Msvc(Lld::No),
|
||||
_ => linker_flavor,
|
||||
};
|
||||
if !args.contains_key(&linker_flavor) {
|
||||
add_link_args_iter(args, linker_flavor, args_json.iter().cloned());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,7 +86,7 @@ fn test_errorkind_packing() {
|
|||
assert_eq!(Error::from(ErrorKind::NotFound).kind(), ErrorKind::NotFound);
|
||||
assert_eq!(Error::from(ErrorKind::PermissionDenied).kind(), ErrorKind::PermissionDenied);
|
||||
assert_eq!(Error::from(ErrorKind::Uncategorized).kind(), ErrorKind::Uncategorized);
|
||||
// Check that the innards look like like what we want.
|
||||
// Check that the innards look like what we want.
|
||||
assert_matches!(
|
||||
Error::from(ErrorKind::OutOfMemory).repr.data(),
|
||||
ErrorData::Simple(ErrorKind::OutOfMemory),
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
//! Since those syscalls have requirements that cannot be fully checked in advance and
|
||||
//! gathering additional information about file descriptors would require additional syscalls
|
||||
//! anyway it simply attempts to use them one after another (guided by inaccurate hints) to
|
||||
//! figure out which one works and and falls back to the generic read-write copy loop if none of them
|
||||
//! figure out which one works and falls back to the generic read-write copy loop if none of them
|
||||
//! does.
|
||||
//! Once a working syscall is found for a pair of file descriptors it will be called in a loop
|
||||
//! until the copy operation is completed.
|
||||
|
|
|
@ -19,7 +19,7 @@ if [[ -n "${CI_ONLY_WHEN_SUBMODULES_CHANGED-}" ]]; then
|
|||
# those files are present in the diff a submodule was updated.
|
||||
echo "Submodules were updated"
|
||||
elif ! (git diff --quiet "$BASE_COMMIT" -- \
|
||||
src/tools/clippy src/tools/rustfmt src/tools/miri
|
||||
src/tools/clippy src/tools/rustfmt src/tools/miri \
|
||||
library/std/src/sys); then
|
||||
# There is not an easy blanket search for subtrees. For now, manually list
|
||||
# the subtrees.
|
||||
|
|
|
@ -692,16 +692,13 @@ pre, .rustdoc.source .example-wrap {
|
|||
|
||||
.item-info {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.content .item-info code {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.content .item-info {
|
||||
margin-left: 24px;
|
||||
}
|
||||
|
||||
.item-info code {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
#main-content > .item-info {
|
||||
margin-top: 0;
|
||||
margin-left: 0;
|
||||
|
@ -1945,7 +1942,7 @@ in storage.js plus the media query with (min-width: 701px)
|
|||
}
|
||||
|
||||
/* Align summary-nested and unnested item-info gizmos. */
|
||||
.content .impl-items > .item-info {
|
||||
.impl-items > .item-info {
|
||||
margin-left: 34px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#![feature(negative_impls)]
|
||||
// edition:2018
|
||||
|
||||
// This tests the the specialized async-await-specific error when futures don't implement an
|
||||
// This tests the specialized async-await-specific error when futures don't implement an
|
||||
// auto trait (which is specifically Sync) due to some type that was captured.
|
||||
|
||||
struct Foo;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#![feature(negative_impls)]
|
||||
// edition:2018
|
||||
|
||||
// This tests the the specialized async-await-specific error when futures don't implement an
|
||||
// This tests the specialized async-await-specific error when futures don't implement an
|
||||
// auto trait (which is specifically Send) due to some type that was captured.
|
||||
|
||||
struct Foo;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#![feature(negative_impls)]
|
||||
// edition:2018
|
||||
|
||||
// This tests the the unspecialized async-await-specific error when futures don't implement an
|
||||
// This tests the unspecialized async-await-specific error when futures don't implement an
|
||||
// auto trait (which is not Send or Sync) due to some type that was captured.
|
||||
|
||||
auto trait Qux {}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#![feature(generic_const_exprs)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
// The goal is is to get an unevaluated const `ct` with a `Ty::Infer(TyVar(_#1t)` subst.
|
||||
// The goal is to get an unevaluated const `ct` with a `Ty::Infer(TyVar(_#1t)` subst.
|
||||
//
|
||||
// If we are then able to infer `ty::Infer(TyVar(_#1t) := Ty<ct>` we introduced an
|
||||
// artificial inference cycle.
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#![feature(generic_const_exprs)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
// The goal is is to get an unevaluated const `ct` with a `Ty::Infer(TyVar(_#1t)` subst.
|
||||
// The goal is to get an unevaluated const `ct` with a `Ty::Infer(TyVar(_#1t)` subst.
|
||||
//
|
||||
// If we are then able to infer `ty::Infer(TyVar(_#1t) := Ty<ct>` we introduced an
|
||||
// artificial inference cycle.
|
||||
|
|
|
@ -51,7 +51,7 @@ mod cross_crate {
|
|||
|
||||
let _ = nested::DeprecatedTupleStruct (1); //~ ERROR use of deprecated tuple struct `deprecation_lint::nested::DeprecatedTupleStruct`: text
|
||||
|
||||
// At the moment, the lint checker only checks stability in
|
||||
// At the moment, the lint checker only checks stability
|
||||
// in the arguments of macros.
|
||||
// Eventually, we will want to lint the contents of the
|
||||
// macro in the module *defining* it. Also, stability levels
|
||||
|
|
17
src/test/ui/dyn-star/box.rs
Normal file
17
src/test/ui/dyn-star/box.rs
Normal file
|
@ -0,0 +1,17 @@
|
|||
// run-pass
|
||||
// compile-flags: -C opt-level=0
|
||||
|
||||
#![feature(dyn_star)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
use std::fmt::Display;
|
||||
|
||||
fn make_dyn_star() -> dyn* Display {
|
||||
Box::new(42) as dyn* Display
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x = make_dyn_star();
|
||||
|
||||
println!("{x}");
|
||||
}
|
|
@ -47,8 +47,8 @@ unsafe {
|
|||
```
|
||||
|
||||
Here, transmute is being used to convert the types of the fn arguments.
|
||||
This pattern is incorrect because, because the type of `foo` is a function
|
||||
**item** (`typeof(foo)`), which is zero-sized, and the target type (`fn()`)
|
||||
This pattern is incorrect because the type of `foo` is a function **item**
|
||||
(`typeof(foo)`), which is zero-sized, and the target type (`fn()`)
|
||||
is a function pointer, which is not zero-sized.
|
||||
This pattern should be rewritten. There are a few possible ways to do this:
|
||||
|
||||
|
|
10
src/test/ui/issues/issue-102964.rs
Normal file
10
src/test/ui/issues/issue-102964.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
use std::rc::Rc;
|
||||
type Foo<'a, T> = &'a dyn Fn(&T);
|
||||
type RcFoo<'a, T> = Rc<Foo<'a, T>>;
|
||||
|
||||
fn bar_function<T>(function: Foo<T>) -> RcFoo<T> {
|
||||
//~^ ERROR mismatched types
|
||||
let rc = Rc::new(function);
|
||||
}
|
||||
|
||||
fn main() {}
|
19
src/test/ui/issues/issue-102964.stderr
Normal file
19
src/test/ui/issues/issue-102964.stderr
Normal file
|
@ -0,0 +1,19 @@
|
|||
error[E0308]: mismatched types
|
||||
--> $DIR/issue-102964.rs:5:41
|
||||
|
|
||||
LL | fn bar_function<T>(function: Foo<T>) -> RcFoo<T> {
|
||||
| ------------ ^^^^^^^^ expected struct `Rc`, found `()`
|
||||
| |
|
||||
| implicitly returns `()` as its body has no tail or `return` expression
|
||||
|
|
||||
= note: expected struct `Rc<&dyn for<'a> Fn(&'a T)>`
|
||||
found unit type `()`
|
||||
help: consider returning the local binding `rc`
|
||||
|
|
||||
LL ~ let rc = Rc::new(function);
|
||||
LL + rc
|
||||
|
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
|
@ -7,7 +7,7 @@ macro_rules! zip {
|
|||
zip!([$($rest),*], $a.zip($b), (x,y), [x,y])
|
||||
};
|
||||
|
||||
// Intermediate steps to build the zipped expression, the match pattern, and
|
||||
// Intermediate steps to build the zipped expression, the match pattern
|
||||
// and the output tuple of the closure, using macro hygiene to repeatedly
|
||||
// introduce new variables named 'x'.
|
||||
([$a:expr, $($rest:expr),*], $zip:expr, $pat:pat, [$($flat:expr),*]) => {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Test for for diagnostic improvement issue #75907
|
||||
// Test for diagnostic improvement issue #75907
|
||||
|
||||
mod foo {
|
||||
pub(crate) struct Foo(u8);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Test for for diagnostic improvement issue #75907, extern crate
|
||||
// Test for diagnostic improvement issue #75907, extern crate
|
||||
// aux-build:issue-75907.rs
|
||||
|
||||
extern crate issue_75907 as a;
|
||||
|
|
|
@ -130,7 +130,7 @@ mod cross_crate {
|
|||
let _ = UnstableTupleStruct (1);
|
||||
let _ = StableTupleStruct (1);
|
||||
|
||||
// At the moment, the lint checker only checks stability in
|
||||
// At the moment, the lint checker only checks stability
|
||||
// in the arguments of macros.
|
||||
// Eventually, we will want to lint the contents of the
|
||||
// macro in the module *defining* it. Also, stability levels
|
||||
|
|
|
@ -19,8 +19,8 @@ macro_rules! produce_it {
|
|||
// `print_def_site!` will respan the `$crate` identifier
|
||||
// with `Span::def_site()`. This should cause it to resolve
|
||||
// relative to `meta_macro`, *not* `make_macro` (despite
|
||||
// the fact that that `print_def_site` is produced by
|
||||
// a `macro_rules!` macro in `make_macro`).
|
||||
// the fact that `print_def_site` is produced by a
|
||||
// `macro_rules!` macro in `make_macro`).
|
||||
meta_macro::print_def_site!($crate::dummy!());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,8 +35,8 @@ macro_rules! produce_it
|
|||
// `print_def_site!` will respan the `$crate` identifier
|
||||
// with `Span::def_site()`. This should cause it to resolve
|
||||
// relative to `meta_macro`, *not* `make_macro` (despite
|
||||
// the fact that that `print_def_site` is produced by
|
||||
// a `macro_rules!` macro in `make_macro`).
|
||||
// the fact that `print_def_site` is produced by a
|
||||
// `macro_rules!` macro in `make_macro`).
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
//! * Language features in a group are sorted by feature name.
|
||||
|
||||
use crate::walk::{filter_dirs, walk, walk_many};
|
||||
use std::collections::HashMap;
|
||||
use std::collections::hash_map::{Entry, HashMap};
|
||||
use std::fmt;
|
||||
use std::fs;
|
||||
use std::num::NonZeroU32;
|
||||
|
@ -280,13 +280,14 @@ fn test_filen_gate(filen_underscore: &str, features: &mut Features) -> bool {
|
|||
}
|
||||
|
||||
pub fn collect_lang_features(base_compiler_path: &Path, bad: &mut bool) -> Features {
|
||||
let mut all = collect_lang_features_in(base_compiler_path, "active.rs", bad);
|
||||
all.extend(collect_lang_features_in(base_compiler_path, "accepted.rs", bad));
|
||||
all.extend(collect_lang_features_in(base_compiler_path, "removed.rs", bad));
|
||||
all
|
||||
let mut features = Features::new();
|
||||
collect_lang_features_in(&mut features, base_compiler_path, "active.rs", bad);
|
||||
collect_lang_features_in(&mut features, base_compiler_path, "accepted.rs", bad);
|
||||
collect_lang_features_in(&mut features, base_compiler_path, "removed.rs", bad);
|
||||
features
|
||||
}
|
||||
|
||||
fn collect_lang_features_in(base: &Path, file: &str, bad: &mut bool) -> Features {
|
||||
fn collect_lang_features_in(features: &mut Features, base: &Path, file: &str, bad: &mut bool) {
|
||||
let path = base.join("rustc_feature").join("src").join(file);
|
||||
let contents = t!(fs::read_to_string(&path));
|
||||
|
||||
|
@ -298,135 +299,145 @@ fn collect_lang_features_in(base: &Path, file: &str, bad: &mut bool) -> Features
|
|||
let mut in_feature_group = false;
|
||||
let mut prev_names = vec![];
|
||||
|
||||
contents
|
||||
.lines()
|
||||
.zip(1..)
|
||||
.filter_map(|(line, line_number)| {
|
||||
let line = line.trim();
|
||||
let lines = contents.lines().zip(1..);
|
||||
for (line, line_number) in lines {
|
||||
let line = line.trim();
|
||||
|
||||
// Within -start and -end, the tracking issue can be omitted.
|
||||
match line {
|
||||
"// no-tracking-issue-start" => {
|
||||
next_feature_omits_tracking_issue = true;
|
||||
return None;
|
||||
}
|
||||
"// no-tracking-issue-end" => {
|
||||
next_feature_omits_tracking_issue = false;
|
||||
return None;
|
||||
}
|
||||
_ => {}
|
||||
// Within -start and -end, the tracking issue can be omitted.
|
||||
match line {
|
||||
"// no-tracking-issue-start" => {
|
||||
next_feature_omits_tracking_issue = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if line.starts_with(FEATURE_GROUP_START_PREFIX) {
|
||||
if in_feature_group {
|
||||
tidy_error!(
|
||||
bad,
|
||||
"{}:{}: \
|
||||
new feature group is started without ending the previous one",
|
||||
path.display(),
|
||||
line_number,
|
||||
);
|
||||
}
|
||||
|
||||
in_feature_group = true;
|
||||
prev_names = vec![];
|
||||
return None;
|
||||
} else if line.starts_with(FEATURE_GROUP_END_PREFIX) {
|
||||
in_feature_group = false;
|
||||
prev_names = vec![];
|
||||
return None;
|
||||
"// no-tracking-issue-end" => {
|
||||
next_feature_omits_tracking_issue = false;
|
||||
continue;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let mut parts = line.split(',');
|
||||
let level = match parts.next().map(|l| l.trim().trim_start_matches('(')) {
|
||||
Some("active") => Status::Unstable,
|
||||
Some("incomplete") => Status::Unstable,
|
||||
Some("removed") => Status::Removed,
|
||||
Some("accepted") => Status::Stable,
|
||||
_ => return None,
|
||||
};
|
||||
let name = parts.next().unwrap().trim();
|
||||
|
||||
let since_str = parts.next().unwrap().trim().trim_matches('"');
|
||||
let since = match since_str.parse() {
|
||||
Ok(since) => Some(since),
|
||||
Err(err) => {
|
||||
tidy_error!(
|
||||
bad,
|
||||
"{}:{}: failed to parse since: {} ({:?})",
|
||||
path.display(),
|
||||
line_number,
|
||||
since_str,
|
||||
err,
|
||||
);
|
||||
None
|
||||
}
|
||||
};
|
||||
if line.starts_with(FEATURE_GROUP_START_PREFIX) {
|
||||
if in_feature_group {
|
||||
if prev_names.last() > Some(&name) {
|
||||
// This assumes the user adds the feature name at the end of the list, as we're
|
||||
// not looking ahead.
|
||||
let correct_index = match prev_names.binary_search(&name) {
|
||||
Ok(_) => {
|
||||
// This only occurs when the feature name has already been declared.
|
||||
tidy_error!(
|
||||
bad,
|
||||
"{}:{}: duplicate feature {}",
|
||||
path.display(),
|
||||
line_number,
|
||||
name,
|
||||
);
|
||||
// skip any additional checks for this line
|
||||
return None;
|
||||
}
|
||||
Err(index) => index,
|
||||
};
|
||||
|
||||
let correct_placement = if correct_index == 0 {
|
||||
"at the beginning of the feature group".to_owned()
|
||||
} else if correct_index == prev_names.len() {
|
||||
// I don't believe this is reachable given the above assumption, but it
|
||||
// doesn't hurt to be safe.
|
||||
"at the end of the feature group".to_owned()
|
||||
} else {
|
||||
format!(
|
||||
"between {} and {}",
|
||||
prev_names[correct_index - 1],
|
||||
prev_names[correct_index],
|
||||
)
|
||||
};
|
||||
|
||||
tidy_error!(
|
||||
bad,
|
||||
"{}:{}: feature {} is not sorted by feature name (should be {})",
|
||||
path.display(),
|
||||
line_number,
|
||||
name,
|
||||
correct_placement,
|
||||
);
|
||||
}
|
||||
prev_names.push(name);
|
||||
tidy_error!(
|
||||
bad,
|
||||
"{}:{}: \
|
||||
new feature group is started without ending the previous one",
|
||||
path.display(),
|
||||
line_number,
|
||||
);
|
||||
}
|
||||
|
||||
let issue_str = parts.next().unwrap().trim();
|
||||
let tracking_issue = if issue_str.starts_with("None") {
|
||||
if level == Status::Unstable && !next_feature_omits_tracking_issue {
|
||||
tidy_error!(
|
||||
bad,
|
||||
"{}:{}: no tracking issue for feature {}",
|
||||
path.display(),
|
||||
line_number,
|
||||
name,
|
||||
);
|
||||
}
|
||||
in_feature_group = true;
|
||||
prev_names = vec![];
|
||||
continue;
|
||||
} else if line.starts_with(FEATURE_GROUP_END_PREFIX) {
|
||||
in_feature_group = false;
|
||||
prev_names = vec![];
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut parts = line.split(',');
|
||||
let level = match parts.next().map(|l| l.trim().trim_start_matches('(')) {
|
||||
Some("active") => Status::Unstable,
|
||||
Some("incomplete") => Status::Unstable,
|
||||
Some("removed") => Status::Removed,
|
||||
Some("accepted") => Status::Stable,
|
||||
_ => continue,
|
||||
};
|
||||
let name = parts.next().unwrap().trim();
|
||||
|
||||
let since_str = parts.next().unwrap().trim().trim_matches('"');
|
||||
let since = match since_str.parse() {
|
||||
Ok(since) => Some(since),
|
||||
Err(err) => {
|
||||
tidy_error!(
|
||||
bad,
|
||||
"{}:{}: failed to parse since: {} ({:?})",
|
||||
path.display(),
|
||||
line_number,
|
||||
since_str,
|
||||
err,
|
||||
);
|
||||
None
|
||||
} else {
|
||||
let s = issue_str.split('(').nth(1).unwrap().split(')').next().unwrap();
|
||||
Some(s.parse().unwrap())
|
||||
};
|
||||
Some((name.to_owned(), Feature { level, since, has_gate_test: false, tracking_issue }))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
};
|
||||
if in_feature_group {
|
||||
if prev_names.last() > Some(&name) {
|
||||
// This assumes the user adds the feature name at the end of the list, as we're
|
||||
// not looking ahead.
|
||||
let correct_index = match prev_names.binary_search(&name) {
|
||||
Ok(_) => {
|
||||
// This only occurs when the feature name has already been declared.
|
||||
tidy_error!(
|
||||
bad,
|
||||
"{}:{}: duplicate feature {}",
|
||||
path.display(),
|
||||
line_number,
|
||||
name,
|
||||
);
|
||||
// skip any additional checks for this line
|
||||
continue;
|
||||
}
|
||||
Err(index) => index,
|
||||
};
|
||||
|
||||
let correct_placement = if correct_index == 0 {
|
||||
"at the beginning of the feature group".to_owned()
|
||||
} else if correct_index == prev_names.len() {
|
||||
// I don't believe this is reachable given the above assumption, but it
|
||||
// doesn't hurt to be safe.
|
||||
"at the end of the feature group".to_owned()
|
||||
} else {
|
||||
format!(
|
||||
"between {} and {}",
|
||||
prev_names[correct_index - 1],
|
||||
prev_names[correct_index],
|
||||
)
|
||||
};
|
||||
|
||||
tidy_error!(
|
||||
bad,
|
||||
"{}:{}: feature {} is not sorted by feature name (should be {})",
|
||||
path.display(),
|
||||
line_number,
|
||||
name,
|
||||
correct_placement,
|
||||
);
|
||||
}
|
||||
prev_names.push(name);
|
||||
}
|
||||
|
||||
let issue_str = parts.next().unwrap().trim();
|
||||
let tracking_issue = if issue_str.starts_with("None") {
|
||||
if level == Status::Unstable && !next_feature_omits_tracking_issue {
|
||||
tidy_error!(
|
||||
bad,
|
||||
"{}:{}: no tracking issue for feature {}",
|
||||
path.display(),
|
||||
line_number,
|
||||
name,
|
||||
);
|
||||
}
|
||||
None
|
||||
} else {
|
||||
let s = issue_str.split('(').nth(1).unwrap().split(')').next().unwrap();
|
||||
Some(s.parse().unwrap())
|
||||
};
|
||||
match features.entry(name.to_owned()) {
|
||||
Entry::Occupied(e) => {
|
||||
tidy_error!(
|
||||
bad,
|
||||
"{}:{} feature {name} already specified with status '{}'",
|
||||
path.display(),
|
||||
line_number,
|
||||
e.get().level,
|
||||
);
|
||||
}
|
||||
Entry::Vacant(e) => {
|
||||
e.insert(Feature { level, since, has_gate_test: false, tracking_issue });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_and_check_lib_features(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue