Question:
pub fn must_use(&self) -> bool { true }  /// hello /// ///
this is a warning
 /// /// done pub fn warning1() {} /// Checking there is no bottom margin if "warning" is the last element. /// ///
this is a warning
 pub fn warning2() {}  } impl AsRef<str> for Foo {

I'm not a giant fan of the syntax because it looks odd when rendered by anything other than rustdoc, which I'd rather avoid, one of the major design concerns for intra-doc links was compatibility with other markdown parsers:
So, I agree, but I'm not convinced that the warning class is something that needs to be surfaced in the JSON backend as anything special. Also, bear in mind, HTML is a part of markdown: valid HTML (well a subset of HTML) is valid markdown. Users already can , and they do that in many cases. Markdown parsers already need to be able to handle HTML. There's nothing new there. Whatever consumes the JSON backend will need to be able to handle that too. I mean, yes, in theory, in practice people don't do this because of the barrier to entry (you have to get your tools to use that, including )
Also, this is incorrect, pulldown absolutely parses HTML. It has to, like I said valid HTML is valid Markdown. It doesn't parse it thoroughly, I believe you can mess it up with sufficiently complex HTML, but it parses HTML. What this changes is subtler: up till now you did not need any particular stylesheet to acceptably render rustdoc markdown from the JSON backend on your own (just use your favorite markdown renderer's stylesheet), but now you will need other css classes to handle rendering warnings as warnings.
I do not agree that this is a problem. I've used Phabricator's implementation of this approach happily and it is by design that warning banners are one paragraph. It is not the intention that you would put entire sections of documentation into a warning banner. They are for a brief callout. It's fine to refer to a subsequent section of the documentation from the callout if needed. I suppose this is a matter of taste but it's one where I believe the opinionatedness of the syntax is a positive thing.
So I discussed this a bit with on the side, we agree that the effect on the JSON backend isn't major since HTML is already a subset of markdown and docs use HTML already (especially for images and tables). My position is as follows: I strictly prefer HTML over some other scoped syntax with nuances people have to learn. I don't think there's as much value in unscoped syntax which makes my position "HTML or nothing", basically.


Question:
pub fn must_use(&self) -> bool { true }  /// hello /// ///
this is a warning
 /// /// done pub fn warning1() {} /// Checking there is no bottom margin if "warning" is the last element. /// ///
this is a warning
 pub fn warning2() {}  } impl AsRef<str> for Foo {

I'm not wholly against unscoped single-line syntax. We and syntax in W3C specs and you invariably need to add more stuff to it (there's an HTML class in these specs for this reason). Furthermore, Rust has a strong culture of wrapping text, so this means we need to work well while wrapped, and that means we're again introducing some form of scoping mechanism (e.g. indenting the text), which editors will have to learn, as well.
I don't know about admonitions specifically but there seems to be something of a community habit to use "magic tags" on fenced code blocks for this sort of features (e.g. github has a tag), maybe that could be an option? I don't know if pulldown_cmark has a hook for manipulating code blocks but… So a warning would be or maybe the tag would be to leave room for things like note/todo/danger/… Alternatively, take a gander at how other markdown implementations extend the language for this sort of features? e.g. seems to be using for admonitions.
Hmm, but is still for code, even if it has some extra behavior. Aside from the design concerns, this would be feasible to implement: we just extract the language string from the code block with pulldown.
Could you use something that's already built into markdown? For example, rustdoc could take and render it as an admonition.
That's what suggested: to use markdown and make some changes on rustdoc side. I think the codeblock approach is the best personally.
My bad, I didn't see that.
I wonder if it makes more sense to add this as a proc-macro. Rustdoc can still provide the CSS classes and things, and you could use them with HTML directly, but you could use or something and it would expand to the correct HTML tags and classes.
Might be better to have a then, no?
Is that going to be a special header similar to the "unsafe"/"experimental"/etc ones, or is that going to also be used within docs in an order-sensitive way? If the latter we should probably just use a CSS class; mixing and is ugly
Agreed.


Question:
pub fn must_use(&self) -> bool { true }  /// hello /// ///
this is a warning
 /// /// done pub fn warning1() {} /// Checking there is no bottom margin if "warning" is the last element. /// ///
this is a warning
 pub fn warning2() {}  } impl AsRef<str> for Foo {

Among the suggested ideas, my favourite one remains the code block with "warning" as tag:
Yeah, but triple backticks is also for preformatted text without further markup, so you won't be able to e.g. use text formatting (or other code) in there
True... Finding the right syntax is much more complicated than expected...
That's the issue of using markdown, for all that it has a pretty nice baseline syntax it really was not designed for extensibility.
We can always use : it'd allow to have text formatting inside it.
I would use , it's not quite kosher to invent arbitrary HTML tags (though you can use Custom Elements, which is more heavyweight)
Sounds good to me. It's funny because we kinda came back to the . :rofl:
I just thought: we could use tag in the markdown and then transform it into in the generated HTML?
My main objection to all of these is that rustdoc shouldn't be adding custom markdown syntax that no one else supports. Any new syntax we use should look reasonable even if rendered with the default renderer.
Well, it remains valid markdown since it's HTML (even though it's custom tag). I'm completely fine with , but it might be simpler/better for users to simply write .
Coming back to this (because of ), it seems like the conversation kinda agreed on adding a CSS class directly and then it would look like this in the docs: . I'll send a PR in the next days and then we can open an FCP to see if everyone is ok with it (and if not, we'll have code to help the debate).
This indeed seems like a better solution than the HTML block. It also plays better with something said about not linking it too much too HTML. This will look like a proper warning or note, mostly independent of the HTML generation.
They also seemed to not be too favourable to anything that needs some extra parsing from rustdoc iuc. I'm fine with bot the github solution and the CSS class.
Maybe one more comment to make here. The note-taking app, which uses Markdown also has a similar syntax to create these blocks. It is called . It uses a syntax similar to the syntax that is used for doc intra-links currently, and it is fully compatible with them.


Question:
pub fn must_use(&self) -> bool { true }  /// hello /// ///
this is a warning
 /// /// done pub fn warning1() {} /// Checking there is no bottom margin if "warning" is the last element. /// ///
this is a warning
 pub fn warning2() {}  } impl AsRef<str> for Foo {

I am uncertain if this is any better than what suggested from the GitHub syntax, but because of the similarity to the doc intra-link syntax. I do feel like this might be interesting because it should go well with backwards compatibility. However, this may look worse than the GitHub syntax solution when not going through cargo doc.
I thought I'd already left this comment, but I'd really like to avoid adding more markdown syntax where possible. This feature works fine without special syntax, I don't think we need it. edit: copying over the reasoning from the :
Although, I might not agree fully, I can fully appreciate and understand the reasoning you left in the corresponding PR. Still, I do think it might be worth to standardize some kind of syntax for some kind of warning block. This does not actually require customized markdown. Alternatively, I can suggest adding the GitHub flavor with emojis to the . This may help in standardizing short form warnings and notes within documentation. We could for example propose to standardize: which should look like the following I do value people's opinion on this. Is this a possible compromise?
That's a valid alternative, I think the yellow color is pretty important though, and it's useful for themes to be able to make styling decisions about warnings so I do think that having some way of marking it is important. I just don't think the way of marking it should be some magic invocation as opposed to CSS classes. (And I think that the bar for a feature that is adding new semantics to the Rustdoc markdown parser is much, much higher than that of one stabilizing a CSS class)
100% agree. I will look at the Rust API guidelines issue and possibly an RFC to rust to track the adoption in GitHub and possibly eventually merge. Thank you :smile:
Beyond all this discussion of third-party tools, I'd really like to make sure that whatever new syntax and presentation we add to rustdoc can also be to [mdBook]. That way, when the standard library starts using callouts, we can make sure the same style also gets used in The Book. Mostly, The Book seems to just use blockquotes, often with headings like [this one about ownership]. mdBook doesn't do anything special with it: it's [just a block quote with a header].


Question:
pub fn must_use(&self) -> bool { true }  /// hello /// ///
this is a warning
 /// /// done pub fn warning1() {} /// Checking there is no bottom margin if "warning" is the last element. /// ///
this is a warning
 pub fn warning2() {}  } impl AsRef<str> for Foo {

[mdBook]: [this one about ownership]: [just a block quote with a header]:
This is a good point. Once we have something merged here, I'll send a PR there to have an equivalent.
CC
I'd be happy to add some CSS rules to mdbook if that is what you are proposing.
Yeah, it is.
FYI, using a is not semantic and Microsoft GitHub is setting a bad precedence. — CommonMark Spec v0.30, denotes a in Markdown and is rendered as such. — W3C HTML spec, Unless you are quoting a source, this is not the correct element to use. While I disagree with even using altogether, at least some tools out there are at least translating the blockquote in admonition context into a (div has no semantic meaning which is unfortunately the best HTML option for admonitions). It would be ill-advised to follow Microsoft GitHub’s Markdown syntax fork—especially with their disregard for semantics. Similarly using a block would also be breaking semantics as an admonition is not preformatted text. CommonMark does have that could be used if the solution was preferred. There’s also other reasonable options noted here that don’t break semantics.
I agree that block quotes for callouts are bad, and would like to work with mdBook to roll out a common syntax (probably ) that will replace the ad-hoc, unsemantic markup that they use right now.
Using is what we agreed upon in The debate now is on the look itself.
Unfortunately isn't great because markdown has a "boundary" when HTML tags are used. I wanted to add a warning indicating that users probably wanted another trait, but intra-doc links don't work (which is the correct behavior per markdown spec).
Works perfectly fine though (well, minus the UI bug I just discovered haha): ! I used this code: I'll send a PR to improve documentation.
Ah. Well I suppose the issue wasn't with intra-doc links, but with inline code blocks. What I tried was that but with bar`, and it failed.
Works too: !
I opened which adds extra explanation on how to use markdown inside html tags. That should solve your issue.
That note is exactly what I was just looking in to. I had it inline, given my familiarity with HTML. TIL that matters.


Question:
pub fn must_use(&self) -> bool { true }  /// hello /// ///
this is a warning
 /// /// done pub fn warning1() {} /// Checking there is no bottom margin if "warning" is the last element. /// ///
this is a warning
 pub fn warning2() {}  } impl AsRef<str> for Foo {

It doesn't hurt to have it and if it can helps users, well, why not doing it. :)


Question: <ins> use foo::*; mod foo { pub mod bar { pub mod bar { pub mod bar {} } } } use bar::bar; //~ ERROR `bar` is ambiguous use bar::*; fn main() { } </ins>

<!-- Thank you for finding an Internal Compiler Error! If possible, try to provide a minimal verifiable example. You can read "Rust Bug Minimization Patterns" for how to create smaller examples. --<!-- If you're using the stable version of the compiler, you should also check if the bug also exists in the beta or nightly versions. --: <!-- Include a backtrace in the code block by setting in your environment. E.g. . --<details<summary<strongBacktrace</strong</summary<p</p</details
`
label +S-bug-has-mcve
What is the expected output here? Can this actually compile, and if so, what does refer to?


Question: <ins> // Regression test for #79902 // Check that evaluation (which is used to determine whether to copy a type in // MIR building) evaluates bounds from normalizing an impl after evaluating // any bounds on the impl. // check-pass trait A { type B; } trait M {} struct G<T, U>(*const T, *const U); impl<T, U> Clone for G<T, U> { fn clone(&self) -> Self { G { ..*self } } } impl<T, U> Copy for G<T, U::B> where T: A<B = U>, U: A, { } impl A for () { type B = (); } fn is_m<T: M>(_: T) {} fn main() { let x = G(&(), &()); drop(x); drop(x); } </ins>

https://crater- modify labels: +regression-from-stable-to-beta I-ICE
searched nightlies: from nightly-2020-09-24 to nightly-2020-12-11 regressed nightly: nightly-2020-10-07 searched commits: from to regressed commit: <details<summarybisected with <a href=''cargo-bisect-rustc</av0.6.0</summaryHost triple: aarch64-unknown-linux-gnu Reproduce with: </details
cc -- seems like this one might be an actual bug
Small case that I think shows this issue, it incorrectly fails to compile on beta/nightly.
N.B. I haven't gotten it to ICE, so I can't add it to the


Question: // stripping away any starting or ending parenthesis characters—hence this // test of the JSON error format. <del> #![warn(unused_parens)] </del> <ins> #![deny(unused_parens)] </ins> #![allow(unreachable_code)] fn main() { let _b = false; <del> if _b { </del> <ins> if _b { //~ ERROR unnecessary parentheses </ins> println!("hello"); }

Update test file to use + annotations instead of (see https://rust-) The files need to update are: - <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --This issue has been assigned to via . <!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
is the correct file name , not ? And what about this comment?
Is the problem fixed ? If not, I would like to work on it !
WIP PR has been closed due to inactivity so you can work on this.
I would like to be assigned to this issue. Am I supposed to do a new pull request ?
claim Go ahead!
Error: Parsing assign command in failed: claim Please let know if you're having trouble with this bot.
claim
Error: Parsing assign command in failed: claim Please let know if you're having trouble with this bot.
Hmm, it's odd, anyway, you can assign the issue to yourself by comment Ah, I mistaken the command, the collect one is , sorry.
claim
If I understood well the problem: I must replace all the by just , even if comment exist ? Because the output changes, I should modify the .stderr version I must change with in , and as should raise an error we add why add in the version ?


Question: // Note that unlike for slice patterns, // where `xs @ ..` is a legal sub-slice pattern, // it is not a legal sub-tuple pattern. <del> if pat.is_rest() { rest = Some((idx, pat.span)); break; </del> <ins> match pat.kind { // Found a sub-tuple rest pattern PatKind::Rest => { rest = Some((idx, pat.span)); break; } // Found a sub-tuple pattern `$binding_mode $ident @ ..`. // This is not allowed as a sub-tuple pattern PatKind::Ident(ref _bm, ident, Some(ref sub)) if sub.is_rest() => { rest = Some((idx, pat.span)); let sp = pat.span; self.diagnostic() .struct_span_err( sp, &format!("`{} @` is not allowed in a {}", ident.name, ctx), ) .span_label(sp, "this is only allowed in slice patterns") .help("remove this and bind each tuple field independently") .span_suggestion_verbose( sp, &format!("if you don't need to use the contents of {}, discard the tuple's remaining fields", ident), "..".to_string(), Applicability::MaybeIncorrect, ) .emit(); break; } _ => {} </ins> } // It was not a sub-tuple pattern so lower it normally. elems.push(self.lower_pat(pat)); }

and I are working on diagnostics for the rest idiom in slice patterns () and came across this issue when an attempt is made to use it in tuples and tuple structs. IIUC, this is allowed: and this is not allowed (note the addition of ): The error messages for the tuple and tuple struct cases include the following incorrect information: This should state that the id binding to rest is not allowed. patterns are allowed. is only allowed in slice patterns, not in tuple or tuple struct patterns. This should state that the idiom is only allowed in slice patterns. <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --<!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
appears to be the site of the check: This method is called:
Perhaps it's worth supporting "tuple slicing" pattern too in Rust:
that would implicitly create a tuple for (or a slice?). I don't think it'd be a good addition. We already support , which is much more useful in my eyes.
That syntax is meant to produce a shorter tuple xs with the omission of the first item of the tuple. lf you think it's an useless idea then let's ignore it :)
claim


Question: <ins> // Avoid panicking if the Clone trait is not found while building error suggestions // See #104870 #![feature(no_core, lang_items)] #![no_core] #[lang = "sized"] trait Sized {} #[lang = "copy"] trait Copy {} fn g<T>(x: T) {} fn f(x: *mut u8) { g(x); g(x); //~ ERROR use of moved value: `x` } fn main() {} </ins>

<!-- Thank you for finding an Internal Compiler Error! If possible, try to provide a minimal verifiable example. You can read "Rust Bug Minimization Patterns" for how to create smaller examples. --: : <!-- Include a backtrace in the code block by setting in your environment. E.g. . --<details<summary<strongBacktrace</strong</summary<p</p</details<!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --<!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
The real error isn't the unemitted diagnostic, it's the unwrap. The unemitted diagnostic only happens bc this unwrap occurs during diagnostics code, I think.
cc I think this might be and we try to suggest .clone() which is not available for the type though?
If you add ... to the example you no longer ICE and just hit the normalerror[E0382]: use of moved value: 'x' `
claim
Please use this minimized repro:

Every time I think "surely this will be fine", it bites me in the read as sure as the sun coming up.
Are we fixing ICEs? Not so sure we should bother...
This one was particularly easy to fix () and probably shouldn't've have been an in the first place (unwraps in diagnostics code are generally a code smell, since they should fail gracefully in strange situations) -- but I generally agree, had this fix required some large refactoring or implemented extra checks on a performance-sensitive path, then I think this should've been closed w/ a wontfix.


Question: <ins> // run-rustfix use std::any::Any; fn foo<T: Any>(value: &T) -> Box<dyn Any> { Box::new(value) as Box<dyn Any> //~^ ERROR lifetime may not live long enough } fn main() { let _ = foo(&5); } </ins>

we currently output The suggestion is incomplete and will not result in compilable code. Because the lifetime is coming from an argument of type , which is an unconstrained type parameter, itself will not have the needed lifetime and causes the obligation that we're trying to reverse by suggesting . The appropriate changes to the code would be either or , depending on whether appears in any other argument or not.
The suggested code .


Question: <ins> error: expected one of `!`, `.`, `::`, `;`, `?`, `else`, `{`, or an operator, found `,` --> $DIR/turbofish-arg-with-stray-colon.rs:2:17 | LL |     let x = Tr<A, A:>; |                 ^ expected one of 8 possible tokens | = note: type ascription syntax has been removed, see issue #101728 <https://github.com/rust-lang/rust/issues/101728> help: maybe write a path separator here | LL |     let x = Tr<A, A::>; |                    ~~ error: aborting due to 1 previous error </ins>

<!-- Thank you for filing a regression report! A regression is something that changed between versions of Rust but was not supposed to. Please provide a short summary of the regression, along with any information you feel is relevant to replicate it. --I tried this code: on stable 1.78.0, this checks in 0.02 seconds. on beta 1.79.0-beta.3, this hangs somewhere in , I killed it after 5 minutes of no progress.
cc
Sharing the stack would be useful
:thinking: that doesn't really seem to be it
You need to switch to like thread 2 or 3 if you're doing this in gdb
it's not particularly deep, but it looks downstream of so I guess this must be it
Minimal:


Question: } } <del> /// Evaluate a const function where all arguments (if any) are zero-sized types. /// The evaluation is memoized thanks to the query system. // FIXME: Consider moving this to `const_eval.rs`. pub(crate) fn eval_const_fn_call( &mut self, gid: GlobalId<'tcx>, ret: Option<(PlaceTy<'tcx, M::PointerTag>, mir::BasicBlock)>, ) -> InterpResult<'tcx> { trace!("eval_const_fn_call: {:?}", gid); let place = self.const_eval_raw(gid)?; let dest = ret.ok_or_else(|| err_ub!(Unreachable))?.0; self.copy_op(place.into(), dest)?; self.return_to_block(ret.map(|r| r.1))?; self.dump_place(*dest); return Ok(()); } </del> fn drop_in_place( &mut self, place: PlaceTy<'tcx, M::PointerTag>,

Follow-up to agreed to move to . Generally I propose that should not contain code that is only needed by CTFE. We have and in there are they need some very deep access to Miri engine internals, but at least those are separate modules. Also, has grown pretty messy, and it does not seem like the order of definitions in that file follows any principle. We could split the file into two similar to what I did with Miri: for implementing the machine trait and defining everything that is needed during CTFE execution, and for defining the setup functions and all the glue that connects CTFE with the rest of the compiler.


Question: | LL | unsafe extern "C" { | ^^^^^^ <ins> | = note: see issue #123743 <https://github.com/rust-lang/rust/issues/123743> for more information = help: add `#![feature(unsafe_extern_blocks)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date </ins> error: aborting due to 1 previous error

With the following code: We get this error: Making the block then gives: cc
:+1:, this should error in both cases but the diagnostics are misleading. Going to fix it. r?


Question: <ins> // Copyright 2013 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! POSIX file path handling use container::Container; use c_str::{CString, ToCStr}; use clone::Clone; use cmp::Eq; use from_str::FromStr; use iter::{AdditiveIterator, Extendable, Iterator}; use option::{Option, None, Some}; use str; use str::Str; use util; use vec; use vec::CopyableVector; use vec::{Vector, VectorVector}; use super::{GenericPath, GenericPathUnsafe}; /// Iterator that yields successive components of a Path pub type ComponentIter<'self> = vec::SplitIterator<'self, u8>; /// Represents a POSIX file path #[deriving(Clone, DeepClone)] pub struct Path { priv repr: ~[u8], // assumed to never be empty or contain NULs priv sepidx: Option<uint> // index of the final separator in repr } /// The standard path separator character pub static sep: u8 = '/' as u8; /// Returns whether the given byte is a path separator #[inline] pub fn is_sep(u: &u8) -> bool { *u == sep } impl Eq for Path { #[inline] fn eq(&self, other: &Path) -> bool { self.repr == other.repr } } impl FromStr for Path { fn from_str(s: &str) -> Option<Path> { let v = s.as_bytes(); if contains_nul(v) { None } else { Some(unsafe { GenericPathUnsafe::from_vec_unchecked(v) }) } } } impl ToCStr for Path { #[inline] fn to_c_str(&self) -> CString { // The Path impl guarantees no internal NUL unsafe { self.as_vec().to_c_str_unchecked() } } #[inline] unsafe fn to_c_str_unchecked(&self) -> CString { self.as_vec().to_c_str_unchecked() } } impl GenericPathUnsafe for Path { unsafe fn from_vec_unchecked(path: &[u8]) -> Path { let path = Path::normalize(path); assert!(!path.is_empty()); let idx = path.rposition_elem(&sep); Path{ repr: path, sepidx: idx } } unsafe fn set_dirname_unchecked(&mut self, dirname: &[u8]) { match self.sepidx { None if bytes!(".") == self.repr || bytes!("..") == self.repr => { self.repr = Path::normalize(dirname); } None => { let mut v = vec::with_capacity(dirname.len() + self.repr.len() + 1); v.push_all(dirname); v.push(sep); v.push_all(self.repr); self.repr = Path::normalize(v); } Some(0) if self.repr.len() == 1 && self.repr[0] == sep => { self.repr = Path::normalize(dirname); } Some(idx) if self.repr.slice_from(idx+1) == bytes!("..") => { self.repr = Path::normalize(dirname); } Some(idx) if dirname.is_empty() => { let v = Path::normalize(self.repr.slice_from(idx+1)); self.repr = v; } Some(idx) => { let mut v = vec::with_capacity(dirname.len() + self.repr.len() - idx); v.push_all(dirname); v.push_all(self.repr.slice_from(idx)); self.repr = Path::normalize(v); } } self.sepidx = self.repr.rposition_elem(&sep); } unsafe fn set_filename_unchecked(&mut self, filename: &[u8]) { match self.sepidx { None if bytes!("..") == self.repr => { let mut v = vec::with_capacity(3 + filename.len()); v.push_all(dot_dot_static); v.push(sep); v.push_all(filename); self.repr = Path::normalize(v); } None => { self.repr = Path::normalize(filename); } Some(idx) if self.repr.slice_from(idx+1) == bytes!("..") => { let mut v = vec::with_capacity(self.repr.len() + 1 + filename.len()); v.push_all(self.repr); v.push(sep); v.push_all(filename); self.repr = Path::normalize(v); } Some(idx) => { let mut v = vec::with_capacity(idx + 1 + filename.len()); v.push_all(self.repr.slice_to(idx+1)); v.push_all(filename); self.repr = Path::normalize(v); } } self.sepidx = self.repr.rposition_elem(&sep); } unsafe fn push_unchecked(&mut self, path: &[u8]) { if !path.is_empty() { if path[0] == sep { self.repr = Path::normalize(path); }  else { let mut v = vec::with_capacity(self.repr.len() + path.len() + 1); v.push_all(self.repr); v.push(sep); v.push_all(path); self.repr = Path::normalize(v); } self.sepidx = self.repr.rposition_elem(&sep); } } } impl GenericPath for Path { #[inline] fn as_vec<'a>(&'a self) -> &'a [u8] { self.repr.as_slice() } fn dirname<'a>(&'a self) -> &'a [u8] { match self.sepidx { None if bytes!("..") == self.repr => self.repr.as_slice(), None => dot_static, Some(0) => self.repr.slice_to(1), Some(idx) if self.repr.slice_from(idx+1) == bytes!("..") => self.repr.as_slice(), Some(idx) => self.repr.slice_to(idx) } } fn filename<'a>(&'a self) -> &'a [u8] { match self.sepidx { None if bytes!(".") == self.repr || bytes!("..") == self.repr => &[], None => self.repr.as_slice(), Some(idx) if self.repr.slice_from(idx+1) == bytes!("..") => &[], Some(idx) => self.repr.slice_from(idx+1) } } fn pop_opt(&mut self) -> Option<~[u8]> { match self.sepidx { None if bytes!(".") == self.repr => None, None => { let mut v = ~['.' as u8]; util::swap(&mut v, &mut self.repr); self.sepidx = None; Some(v) } Some(0) if bytes!("/") == self.repr => None, Some(idx) => { let v = self.repr.slice_from(idx+1).to_owned(); if idx == 0 { self.repr.truncate(idx+1); } else { self.repr.truncate(idx); } self.sepidx = self.repr.rposition_elem(&sep); Some(v) } } } #[inline] fn is_absolute(&self) -> bool { self.repr[0] == sep } fn is_ancestor_of(&self, other: &Path) -> bool { if self.is_absolute() != other.is_absolute() { false } else { let mut ita = self.component_iter(); let mut itb = other.component_iter(); if bytes!(".") == self.repr { return itb.next() != Some(bytes!("..")); } loop { match (ita.next(), itb.next()) { (None, _) => break, (Some(a), Some(b)) if a == b => { loop }, (Some(a), _) if a == bytes!("..") => { // if ita contains only .. components, it's an ancestor return ita.all(|x| x == bytes!("..")); } _ => return false } } true } } fn path_relative_from(&self, base: &Path) -> Option<Path> { if self.is_absolute() != base.is_absolute() { if self.is_absolute() { Some(self.clone()) } else { None } } else { let mut ita = self.component_iter(); let mut itb = base.component_iter(); let mut comps = ~[]; loop { match (ita.next(), itb.next()) { (None, None) => break, (Some(a), None) => { comps.push(a); comps.extend(&mut ita); break; } (None, _) => comps.push(dot_dot_static), (Some(a), Some(b)) if comps.is_empty() && a == b => (), (Some(a), Some(b)) if b == bytes!(".") => comps.push(a), (Some(_), Some(b)) if b == bytes!("..") => return None, (Some(a), Some(_)) => { comps.push(dot_dot_static); for _ in itb { comps.push(dot_dot_static); } comps.push(a); comps.extend(&mut ita); break; } } } Some(Path::new(comps.connect_vec(&sep))) } } } impl Path { /// Returns a new Path from a byte vector /// /// # Failure /// /// Raises the `null_byte` condition if the vector contains a NUL. #[inline] pub fn new(v: &[u8]) -> Path { GenericPath::from_vec(v) } /// Returns a new Path from a string /// /// # Failure /// /// Raises the `null_byte` condition if the str contains a NUL. #[inline] pub fn from_str(s: &str) -> Path { GenericPath::from_str(s) } /// Converts the Path into an owned byte vector pub fn into_vec(self) -> ~[u8] { self.repr } /// Converts the Path into an owned string, if possible pub fn into_str(self) -> Option<~str> { str::from_utf8_owned_opt(self.repr) } /// Returns a normalized byte vector representation of a path, by removing all empty /// components, and unnecessary . and .. components. pub fn normalize<V: Vector<u8>+CopyableVector<u8>>(v: V) -> ~[u8] { // borrowck is being very picky let val = { let is_abs = !v.as_slice().is_empty() && v.as_slice()[0] == sep; let v_ = if is_abs { v.as_slice().slice_from(1) } else { v.as_slice() }; let comps = normalize_helper(v_, is_abs); match comps { None => None, Some(comps) => { if is_abs && comps.is_empty() { Some(~[sep]) } else { let n = if is_abs { comps.len() } else { comps.len() - 1} + comps.iter().map(|v| v.len()).sum(); let mut v = vec::with_capacity(n); let mut it = comps.move_iter(); if !is_abs { match it.next() { None => (), Some(comp) => v.push_all(comp) } } for comp in it { v.push(sep); v.push_all(comp); } Some(v) } } } }; match val { None => v.into_owned(), Some(val) => val } } /// Returns an iterator that yields each component of the path in turn. /// Does not distinguish between absolute and relative paths, e.g. /// /a/b/c and a/b/c yield the same set of components. /// A path of "/" yields no components. A path of "." yields one component. pub fn component_iter<'a>(&'a self) -> ComponentIter<'a> { let v = if self.repr[0] == sep { self.repr.slice_from(1) } else { self.repr.as_slice() }; let mut ret = v.split_iter(is_sep); if v.is_empty() { // consume the empty "" component ret.next(); } ret } } // None result means the byte vector didn't need normalizing fn normalize_helper<'a>(v: &'a [u8], is_abs: bool) -> Option<~[&'a [u8]]> { if is_abs && v.as_slice().is_empty() { return None; } let mut comps: ~[&'a [u8]] = ~[]; let mut n_up = 0u; let mut changed = false; for comp in v.split_iter(is_sep) { if comp.is_empty() { changed = true } else if comp == bytes!(".") { changed = true } else if comp == bytes!("..") { if is_abs && comps.is_empty() { changed = true } else if comps.len() == n_up { comps.push(dot_dot_static); n_up += 1 } else { comps.pop_opt(); changed = true } } else { comps.push(comp) } } if changed { if comps.is_empty() && !is_abs { if v == bytes!(".") { return None; } comps.push(dot_static); } Some(comps) } else { None } } // FIXME (#8169): Pull this into parent module once visibility works #[inline(always)] fn contains_nul(v: &[u8]) -> bool { v.iter().any(|&x| x == 0) } static dot_static: &'static [u8] = &'static ['.' as u8]; static dot_dot_static: &'static [u8] = &'static ['.' as u8, '.' as u8]; #[cfg(test)] mod tests { use super::*; use option::{Some, None}; use iter::Iterator; use str; use vec::Vector; macro_rules! t( (s: $path:expr, $exp:expr) => ( { let path = $path; assert_eq!(path.as_str(), Some($exp)); } ); (v: $path:expr, $exp:expr) => ( { let path = $path; assert_eq!(path.as_vec(), $exp); } ) ) macro_rules! b( ($($arg:expr),+) => ( bytes!($($arg),+) ) ) #[test] fn test_paths() { t!(v: Path::new([]), b!(".")); t!(v: Path::new(b!("/")), b!("/")); t!(v: Path::new(b!("a/b/c")), b!("a/b/c")); t!(v: Path::new(b!("a/b/c", 0xff)), b!("a/b/c", 0xff)); t!(v: Path::new(b!(0xff, "/../foo", 0x80)), b!("foo", 0x80)); let p = Path::new(b!("a/b/c", 0xff)); assert_eq!(p.as_str(), None); t!(s: Path::from_str(""), "."); t!(s: Path::from_str("/"), "/"); t!(s: Path::from_str("hi"), "hi"); t!(s: Path::from_str("hi/"), "hi"); t!(s: Path::from_str("/lib"), "/lib"); t!(s: Path::from_str("/lib/"), "/lib"); t!(s: Path::from_str("hi/there"), "hi/there"); t!(s: Path::from_str("hi/there.txt"), "hi/there.txt"); t!(s: Path::from_str("hi/there/"), "hi/there"); t!(s: Path::from_str("hi/../there"), "there"); t!(s: Path::from_str("../hi/there"), "../hi/there"); t!(s: Path::from_str("/../hi/there"), "/hi/there"); t!(s: Path::from_str("foo/.."), "."); t!(s: Path::from_str("/foo/.."), "/"); t!(s: Path::from_str("/foo/../.."), "/"); t!(s: Path::from_str("/foo/../../bar"), "/bar"); t!(s: Path::from_str("/./hi/./there/."), "/hi/there"); t!(s: Path::from_str("/./hi/./there/./.."), "/hi"); t!(s: Path::from_str("foo/../.."), ".."); t!(s: Path::from_str("foo/../../.."), "../.."); t!(s: Path::from_str("foo/../../bar"), "../bar"); assert_eq!(Path::new(b!("foo/bar")).into_vec(), b!("foo/bar").to_owned()); assert_eq!(Path::new(b!("/foo/../../bar")).into_vec(), b!("/bar").to_owned()); assert_eq!(Path::from_str("foo/bar").into_str(), Some(~"foo/bar")); assert_eq!(Path::from_str("/foo/../../bar").into_str(), Some(~"/bar")); let p = Path::new(b!("foo/bar", 0x80)); assert_eq!(p.as_str(), None); assert_eq!(Path::new(b!("foo", 0xff, "/bar")).into_str(), None); } #[test] fn test_null_byte() { use path2::null_byte::cond; let mut handled = false; let mut p = do cond.trap(|v| { handled = true; assert_eq!(v.as_slice(), b!("foo/bar", 0)); (b!("/bar").to_owned()) }).inside { Path::new(b!("foo/bar", 0)) }; assert!(handled); assert_eq!(p.as_vec(), b!("/bar")); handled = false; do cond.trap(|v| { handled = true; assert_eq!(v.as_slice(), b!("f", 0, "o")); (b!("foo").to_owned()) }).inside { p.set_filename(b!("f", 0, "o")) }; assert!(handled); assert_eq!(p.as_vec(), b!("/foo")); handled = false; do cond.trap(|v| { handled = true; assert_eq!(v.as_slice(), b!("null/", 0, "/byte")); (b!("null/byte").to_owned()) }).inside { p.set_dirname(b!("null/", 0, "/byte")); }; assert!(handled); assert_eq!(p.as_vec(), b!("null/byte/foo")); handled = false; do cond.trap(|v| { handled = true; assert_eq!(v.as_slice(), b!("f", 0, "o")); (b!("foo").to_owned()) }).inside { p.push(b!("f", 0, "o")); }; assert!(handled); assert_eq!(p.as_vec(), b!("null/byte/foo/foo")); } #[test] fn test_null_byte_fail() { use path2::null_byte::cond; use task; macro_rules! t( ($name:expr => $code:block) => ( { let mut t = task::task(); t.supervised(); t.name($name); let res = do t.try $code; assert!(res.is_err()); } ) ) t!(~"new() w/nul" => { do cond.trap(|_| { (b!("null", 0).to_owned()) }).inside { Path::new(b!("foo/bar", 0)) }; }) t!(~"set_filename w/nul" => { let mut p = Path::new(b!("foo/bar")); do cond.trap(|_| { (b!("null", 0).to_owned()) }).inside { p.set_filename(b!("foo", 0)) }; }) t!(~"set_dirname w/nul" => { let mut p = Path::new(b!("foo/bar")); do cond.trap(|_| { (b!("null", 0).to_owned()) }).inside { p.set_dirname(b!("foo", 0)) }; }) t!(~"push w/nul" => { let mut p = Path::new(b!("foo/bar")); do cond.trap(|_| { (b!("null", 0).to_owned()) }).inside { p.push(b!("foo", 0)) }; }) } #[test] fn test_components() { macro_rules! t( (s: $path:expr, $op:ident, $exp:expr) => ( { let path = Path::from_str($path); assert_eq!(path.$op(), ($exp).as_bytes()); } ); (s: $path:expr, $op:ident, $exp:expr, opt) => ( { let path = Path::from_str($path); let left = path.$op().map(|&x| str::from_utf8_slice(x)); assert_eq!(left, $exp); } ); (v: $path:expr, $op:ident, $exp:expr) => ( { let path = Path::new($path); assert_eq!(path.$op(), $exp); } ) ) t!(v: b!("a/b/c"), filename, b!("c")); t!(v: b!("a/b/c", 0xff), filename, b!("c", 0xff)); t!(v: b!("a/b", 0xff, "/c"), filename, b!("c")); t!(s: "a/b/c", filename, "c"); t!(s: "/a/b/c", filename, "c"); t!(s: "a", filename, "a"); t!(s: "/a", filename, "a"); t!(s: ".", filename, ""); t!(s: "/", filename, ""); t!(s: "..", filename, ""); t!(s: "../..", filename, ""); t!(v: b!("a/b/c"), dirname, b!("a/b")); t!(v: b!("a/b/c", 0xff), dirname, b!("a/b")); t!(v: b!("a/b", 0xff, "/c"), dirname, b!("a/b", 0xff)); t!(s: "a/b/c", dirname, "a/b"); t!(s: "/a/b/c", dirname, "/a/b"); t!(s: "a", dirname, "."); t!(s: "/a", dirname, "/"); t!(s: ".", dirname, "."); t!(s: "/", dirname, "/"); t!(s: "..", dirname, ".."); t!(s: "../..", dirname, "../.."); t!(v: b!("hi/there.txt"), filestem, b!("there")); t!(v: b!("hi/there", 0x80, ".txt"), filestem, b!("there", 0x80)); t!(v: b!("hi/there.t", 0x80, "xt"), filestem, b!("there")); t!(s: "hi/there.txt", filestem, "there"); t!(s: "hi/there", filestem, "there"); t!(s: "there.txt", filestem, "there"); t!(s: "there", filestem, "there"); t!(s: ".", filestem, ""); t!(s: "/", filestem, ""); t!(s: "foo/.bar", filestem, ".bar"); t!(s: ".bar", filestem, ".bar"); t!(s: "..bar", filestem, "."); t!(s: "hi/there..txt", filestem, "there."); t!(s: "..", filestem, ""); t!(s: "../..", filestem, ""); t!(v: b!("hi/there.txt"), extension, Some(b!("txt"))); t!(v: b!("hi/there", 0x80, ".txt"), extension, Some(b!("txt"))); t!(v: b!("hi/there.t", 0x80, "xt"), extension, Some(b!("t", 0x80, "xt"))); t!(v: b!("hi/there"), extension, None); t!(v: b!("hi/there", 0x80), extension, None); t!(s: "hi/there.txt", extension, Some("txt"), opt); t!(s: "hi/there", extension, None, opt); t!(s: "there.txt", extension, Some("txt"), opt); t!(s: "there", extension, None, opt); t!(s: ".", extension, None, opt); t!(s: "/", extension, None, opt); t!(s: "foo/.bar", extension, None, opt); t!(s: ".bar", extension, None, opt); t!(s: "..bar", extension, Some("bar"), opt); t!(s: "hi/there..txt", extension, Some("txt"), opt); t!(s: "..", extension, None, opt); t!(s: "../..", extension, None, opt); } #[test] fn test_push() { macro_rules! t( (s: $path:expr, $join:expr) => ( { let path = ($path); let join = ($join); let mut p1 = Path::from_str(path); let p2 = p1.clone(); p1.push_str(join); assert_eq!(p1, p2.join_str(join)); } ) ) t!(s: "a/b/c", ".."); t!(s: "/a/b/c", "d"); t!(s: "a/b", "c/d"); t!(s: "a/b", "/c/d"); } #[test] fn test_push_path() { macro_rules! t( (s: $path:expr, $push:expr, $exp:expr) => ( { let mut p = Path::from_str($path); let push = Path::from_str($push); p.push_path(&push); assert_eq!(p.as_str(), Some($exp)); } ) ) t!(s: "a/b/c", "d", "a/b/c/d"); t!(s: "/a/b/c", "d", "/a/b/c/d"); t!(s: "a/b", "c/d", "a/b/c/d"); t!(s: "a/b", "/c/d", "/c/d"); t!(s: "a/b", ".", "a/b"); t!(s: "a/b", "../c", "a/c"); } #[test] fn test_pop() { macro_rules! t( (s: $path:expr, $left:expr, $right:expr) => ( { let mut p = Path::from_str($path); let file = p.pop_opt_str(); assert_eq!(p.as_str(), Some($left)); assert_eq!(file.map(|s| s.as_slice()), $right); } ); (v: [$($path:expr),+], [$($left:expr),+], Some($($right:expr),+)) => ( { let mut p = Path::new(b!($($path),+)); let file = p.pop_opt(); assert_eq!(p.as_vec(), b!($($left),+)); assert_eq!(file.map(|v| v.as_slice()), Some(b!($($right),+))); } ); (v: [$($path:expr),+], [$($left:expr),+], None) => ( { let mut p = Path::new(b!($($path),+)); let file = p.pop_opt(); assert_eq!(p.as_vec(), b!($($left),+)); assert_eq!(file, None); } ) ) t!(v: ["a/b/c"], ["a/b"], Some("c")); t!(v: ["a"], ["."], Some("a")); t!(v: ["."], ["."], None); t!(v: ["/a"], ["/"], Some("a")); t!(v: ["/"], ["/"], None); t!(v: ["a/b/c", 0x80], ["a/b"], Some("c", 0x80)); t!(v: ["a/b", 0x80, "/c"], ["a/b", 0x80], Some("c")); t!(v: [0xff], ["."], Some(0xff)); t!(v: ["/", 0xff], ["/"], Some(0xff)); t!(s: "a/b/c", "a/b", Some("c")); t!(s: "a", ".", Some("a")); t!(s: ".", ".", None); t!(s: "/a", "/", Some("a")); t!(s: "/", "/", None); assert_eq!(Path::new(b!("foo/bar", 0x80)).pop_opt_str(), None); assert_eq!(Path::new(b!("foo", 0x80, "/bar")).pop_opt_str(), Some(~"bar")); } #[test] fn test_join() { t!(v: Path::new(b!("a/b/c")).join(b!("..")), b!("a/b")); t!(v: Path::new(b!("/a/b/c")).join(b!("d")), b!("/a/b/c/d")); t!(v: Path::new(b!("a/", 0x80, "/c")).join(b!(0xff)), b!("a/", 0x80, "/c/", 0xff)); t!(s: Path::from_str("a/b/c").join_str(".."), "a/b"); t!(s: Path::from_str("/a/b/c").join_str("d"), "/a/b/c/d"); t!(s: Path::from_str("a/b").join_str("c/d"), "a/b/c/d"); t!(s: Path::from_str("a/b").join_str("/c/d"), "/c/d"); t!(s: Path::from_str(".").join_str("a/b"), "a/b"); t!(s: Path::from_str("/").join_str("a/b"), "/a/b"); } #[test] fn test_join_path() { macro_rules! t( (s: $path:expr, $join:expr, $exp:expr) => ( { let path = Path::from_str($path); let join = Path::from_str($join); let res = path.join_path(&join); assert_eq!(res.as_str(), Some($exp)); } ) ) t!(s: "a/b/c", "..", "a/b"); t!(s: "/a/b/c", "d", "/a/b/c/d"); t!(s: "a/b", "c/d", "a/b/c/d"); t!(s: "a/b", "/c/d", "/c/d"); t!(s: ".", "a/b", "a/b"); t!(s: "/", "a/b", "/a/b"); } #[test] fn test_with_helpers() { t!(v: Path::new(b!("a/b/c")).with_dirname(b!("d")), b!("d/c")); t!(v: Path::new(b!("a/b/c")).with_dirname(b!("d/e")), b!("d/e/c")); t!(v: Path::new(b!("a/", 0x80, "b/c")).with_dirname(b!(0xff)), b!(0xff, "/c")); t!(v: Path::new(b!("a/b/", 0x80)).with_dirname(b!("/", 0xcd)), b!("/", 0xcd, "/", 0x80)); t!(s: Path::from_str("a/b/c").with_dirname_str("d"), "d/c"); t!(s: Path::from_str("a/b/c").with_dirname_str("d/e"), "d/e/c"); t!(s: Path::from_str("a/b/c").with_dirname_str(""), "c"); t!(s: Path::from_str("a/b/c").with_dirname_str("/"), "/c"); t!(s: Path::from_str("a/b/c").with_dirname_str("."), "c"); t!(s: Path::from_str("a/b/c").with_dirname_str(".."), "../c"); t!(s: Path::from_str("/").with_dirname_str("foo"), "foo"); t!(s: Path::from_str("/").with_dirname_str(""), "."); t!(s: Path::from_str("/foo").with_dirname_str("bar"), "bar/foo"); t!(s: Path::from_str("..").with_dirname_str("foo"), "foo"); t!(s: Path::from_str("../..").with_dirname_str("foo"), "foo"); t!(s: Path::from_str("..").with_dirname_str(""), "."); t!(s: Path::from_str("../..").with_dirname_str(""), "."); t!(s: Path::from_str("foo").with_dirname_str(".."), "../foo"); t!(s: Path::from_str("foo").with_dirname_str("../.."), "../../foo"); t!(v: Path::new(b!("a/b/c")).with_filename(b!("d")), b!("a/b/d")); t!(v: Path::new(b!("a/b/c", 0xff)).with_filename(b!(0x80)), b!("a/b/", 0x80)); t!(v: Path::new(b!("/", 0xff, "/foo")).with_filename(b!(0xcd)), b!("/", 0xff, "/", 0xcd)); t!(s: Path::from_str("a/b/c").with_filename_str("d"), "a/b/d"); t!(s: Path::from_str(".").with_filename_str("foo"), "foo"); t!(s: Path::from_str("/a/b/c").with_filename_str("d"), "/a/b/d"); t!(s: Path::from_str("/").with_filename_str("foo"), "/foo"); t!(s: Path::from_str("/a").with_filename_str("foo"), "/foo"); t!(s: Path::from_str("foo").with_filename_str("bar"), "bar"); t!(s: Path::from_str("/").with_filename_str("foo/"), "/foo"); t!(s: Path::from_str("/a").with_filename_str("foo/"), "/foo"); t!(s: Path::from_str("a/b/c").with_filename_str(""), "a/b"); t!(s: Path::from_str("a/b/c").with_filename_str("."), "a/b"); t!(s: Path::from_str("a/b/c").with_filename_str(".."), "a"); t!(s: Path::from_str("/a").with_filename_str(""), "/"); t!(s: Path::from_str("foo").with_filename_str(""), "."); t!(s: Path::from_str("a/b/c").with_filename_str("d/e"), "a/b/d/e"); t!(s: Path::from_str("a/b/c").with_filename_str("/d"), "a/b/d"); t!(s: Path::from_str("..").with_filename_str("foo"), "../foo"); t!(s: Path::from_str("../..").with_filename_str("foo"), "../../foo"); t!(s: Path::from_str("..").with_filename_str(""), ".."); t!(s: Path::from_str("../..").with_filename_str(""), "../.."); t!(v: Path::new(b!("hi/there", 0x80, ".txt")).with_filestem(b!(0xff)), b!("hi/", 0xff, ".txt")); t!(v: Path::new(b!("hi/there.txt", 0x80)).with_filestem(b!(0xff)), b!("hi/", 0xff, ".txt", 0x80)); t!(v: Path::new(b!("hi/there", 0xff)).with_filestem(b!(0x80)), b!("hi/", 0x80)); t!(v: Path::new(b!("hi", 0x80, "/there")).with_filestem([]), b!("hi", 0x80)); t!(s: Path::from_str("hi/there.txt").with_filestem_str("here"), "hi/here.txt"); t!(s: Path::from_str("hi/there.txt").with_filestem_str(""), "hi/.txt"); t!(s: Path::from_str("hi/there.txt").with_filestem_str("."), "hi/..txt"); t!(s: Path::from_str("hi/there.txt").with_filestem_str(".."), "hi/...txt"); t!(s: Path::from_str("hi/there.txt").with_filestem_str("/"), "hi/.txt"); t!(s: Path::from_str("hi/there.txt").with_filestem_str("foo/bar"), "hi/foo/bar.txt"); t!(s: Path::from_str("hi/there.foo.txt").with_filestem_str("here"), "hi/here.txt"); t!(s: Path::from_str("hi/there").with_filestem_str("here"), "hi/here"); t!(s: Path::from_str("hi/there").with_filestem_str(""), "hi"); t!(s: Path::from_str("hi").with_filestem_str(""), "."); t!(s: Path::from_str("/hi").with_filestem_str(""), "/"); t!(s: Path::from_str("hi/there").with_filestem_str(".."), "."); t!(s: Path::from_str("hi/there").with_filestem_str("."), "hi"); t!(s: Path::from_str("hi/there.").with_filestem_str("foo"), "hi/foo."); t!(s: Path::from_str("hi/there.").with_filestem_str(""), "hi"); t!(s: Path::from_str("hi/there.").with_filestem_str("."), "."); t!(s: Path::from_str("hi/there.").with_filestem_str(".."), "hi/..."); t!(s: Path::from_str("/").with_filestem_str("foo"), "/foo"); t!(s: Path::from_str(".").with_filestem_str("foo"), "foo"); t!(s: Path::from_str("hi/there..").with_filestem_str("here"), "hi/here."); t!(s: Path::from_str("hi/there..").with_filestem_str(""), "hi"); t!(v: Path::new(b!("hi/there", 0x80, ".txt")).with_extension(b!("exe")), b!("hi/there", 0x80, ".exe")); t!(v: Path::new(b!("hi/there.txt", 0x80)).with_extension(b!(0xff)), b!("hi/there.", 0xff)); t!(v: Path::new(b!("hi/there", 0x80)).with_extension(b!(0xff)), b!("hi/there", 0x80, ".", 0xff)); t!(v: Path::new(b!("hi/there.", 0xff)).with_extension([]), b!("hi/there")); t!(s: Path::from_str("hi/there.txt").with_extension_str("exe"), "hi/there.exe"); t!(s: Path::from_str("hi/there.txt").with_extension_str(""), "hi/there"); t!(s: Path::from_str("hi/there.txt").with_extension_str("."), "hi/there.."); t!(s: Path::from_str("hi/there.txt").with_extension_str(".."), "hi/there..."); t!(s: Path::from_str("hi/there").with_extension_str("txt"), "hi/there.txt"); t!(s: Path::from_str("hi/there").with_extension_str("."), "hi/there.."); t!(s: Path::from_str("hi/there").with_extension_str(".."), "hi/there..."); t!(s: Path::from_str("hi/there.").with_extension_str("txt"), "hi/there.txt"); t!(s: Path::from_str("hi/.foo").with_extension_str("txt"), "hi/.foo.txt"); t!(s: Path::from_str("hi/there.txt").with_extension_str(".foo"), "hi/there..foo"); t!(s: Path::from_str("/").with_extension_str("txt"), "/"); t!(s: Path::from_str("/").with_extension_str("."), "/"); t!(s: Path::from_str("/").with_extension_str(".."), "/"); t!(s: Path::from_str(".").with_extension_str("txt"), "."); } #[test] fn test_setters() { macro_rules! t( (s: $path:expr, $set:ident, $with:ident, $arg:expr) => ( { let path = $path; let arg = $arg; let mut p1 = Path::from_str(path); p1.$set(arg); let p2 = Path::from_str(path); assert_eq!(p1, p2.$with(arg)); } ); (v: $path:expr, $set:ident, $with:ident, $arg:expr) => ( { let path = $path; let arg = $arg; let mut p1 = Path::new(path); p1.$set(arg); let p2 = Path::new(path); assert_eq!(p1, p2.$with(arg)); } ) ) t!(v: b!("a/b/c"), set_dirname, with_dirname, b!("d")); t!(v: b!("a/b/c"), set_dirname, with_dirname, b!("d/e")); t!(v: b!("a/", 0x80, "/c"), set_dirname, with_dirname, b!(0xff)); t!(s: "a/b/c", set_dirname_str, with_dirname_str, "d"); t!(s: "a/b/c", set_dirname_str, with_dirname_str, "d/e"); t!(s: "/", set_dirname_str, with_dirname_str, "foo"); t!(s: "/foo", set_dirname_str, with_dirname_str, "bar"); t!(s: "a/b/c", set_dirname_str, with_dirname_str, ""); t!(s: "../..", set_dirname_str, with_dirname_str, "x"); t!(s: "foo", set_dirname_str, with_dirname_str, "../.."); t!(v: b!("a/b/c"), set_filename, with_filename, b!("d")); t!(v: b!("/"), set_filename, with_filename, b!("foo")); t!(v: b!(0x80), set_filename, with_filename, b!(0xff)); t!(s: "a/b/c", set_filename_str, with_filename_str, "d"); t!(s: "/", set_filename_str, with_filename_str, "foo"); t!(s: ".", set_filename_str, with_filename_str, "foo"); t!(s: "a/b", set_filename_str, with_filename_str, ""); t!(s: "a", set_filename_str, with_filename_str, ""); t!(v: b!("hi/there.txt"), set_filestem, with_filestem, b!("here")); t!(v: b!("hi/there", 0x80, ".txt"), set_filestem, with_filestem, b!("here", 0xff)); t!(s: "hi/there.txt", set_filestem_str, with_filestem_str, "here"); t!(s: "hi/there.", set_filestem_str, with_filestem_str, "here"); t!(s: "hi/there", set_filestem_str, with_filestem_str, "here"); t!(s: "hi/there.txt", set_filestem_str, with_filestem_str, ""); t!(s: "hi/there", set_filestem_str, with_filestem_str, ""); t!(v: b!("hi/there.txt"), set_extension, with_extension, b!("exe")); t!(v: b!("hi/there.t", 0x80, "xt"), set_extension, with_extension, b!("exe", 0xff)); t!(s: "hi/there.txt", set_extension_str, with_extension_str, "exe"); t!(s: "hi/there.", set_extension_str, with_extension_str, "txt"); t!(s: "hi/there", set_extension_str, with_extension_str, "txt"); t!(s: "hi/there.txt", set_extension_str, with_extension_str, ""); t!(s: "hi/there", set_extension_str, with_extension_str, ""); t!(s: ".", set_extension_str, with_extension_str, "txt"); } #[test] fn test_getters() { macro_rules! t( (s: $path:expr, $filename:expr, $dirname:expr, $filestem:expr, $ext:expr) => ( { let path = $path; assert_eq!(path.filename_str(), $filename); assert_eq!(path.dirname_str(), $dirname); assert_eq!(path.filestem_str(), $filestem); assert_eq!(path.extension_str(), $ext); } ); (v: $path:expr, $filename:expr, $dirname:expr, $filestem:expr, $ext:expr) => ( { let path = $path; assert_eq!(path.filename(), $filename); assert_eq!(path.dirname(), $dirname); assert_eq!(path.filestem(), $filestem); assert_eq!(path.extension(), $ext); } ) ) t!(v: Path::new(b!("a/b/c")), b!("c"), b!("a/b"), b!("c"), None); t!(v: Path::new(b!("a/b/", 0xff)), b!(0xff), b!("a/b"), b!(0xff), None); t!(v: Path::new(b!("hi/there.", 0xff)), b!("there.", 0xff), b!("hi"), b!("there"), Some(b!(0xff))); t!(s: Path::from_str("a/b/c"), Some("c"), Some("a/b"), Some("c"), None); t!(s: Path::from_str("."), Some(""), Some("."), Some(""), None); t!(s: Path::from_str("/"), Some(""), Some("/"), Some(""), None); t!(s: Path::from_str(".."), Some(""), Some(".."), Some(""), None); t!(s: Path::from_str("../.."), Some(""), Some("../.."), Some(""), None); t!(s: Path::from_str("hi/there.txt"), Some("there.txt"), Some("hi"), Some("there"), Some("txt")); t!(s: Path::from_str("hi/there"), Some("there"), Some("hi"), Some("there"), None); t!(s: Path::from_str("hi/there."), Some("there."), Some("hi"), Some("there"), Some("")); t!(s: Path::from_str("hi/.there"), Some(".there"), Some("hi"), Some(".there"), None); t!(s: Path::from_str("hi/..there"), Some("..there"), Some("hi"), Some("."), Some("there")); t!(s: Path::new(b!("a/b/", 0xff)), None, Some("a/b"), None, None); t!(s: Path::new(b!("a/b/", 0xff, ".txt")), None, Some("a/b"), None, Some("txt")); t!(s: Path::new(b!("a/b/c.", 0x80)), None, Some("a/b"), Some("c"), None); t!(s: Path::new(b!(0xff, "/b")), Some("b"), None, Some("b"), None); } #[test] fn test_dir_file_path() { t!(v: Path::new(b!("hi/there", 0x80)).dir_path(), b!("hi")); t!(v: Path::new(b!("hi", 0xff, "/there")).dir_path(), b!("hi", 0xff)); t!(s: Path::from_str("hi/there").dir_path(), "hi"); t!(s: Path::from_str("hi").dir_path(), "."); t!(s: Path::from_str("/hi").dir_path(), "/"); t!(s: Path::from_str("/").dir_path(), "/"); t!(s: Path::from_str("..").dir_path(), ".."); t!(s: Path::from_str("../..").dir_path(), "../.."); macro_rules! t( (s: $path:expr, $exp:expr) => ( { let path = $path; let left = path.and_then_ref(|p| p.as_str()); assert_eq!(left, $exp); } ); (v: $path:expr, $exp:expr) => ( { let path = $path; let left = path.map(|p| p.as_vec()); assert_eq!(left, $exp); } ) ) t!(v: Path::new(b!("hi/there", 0x80)).file_path(), Some(b!("there", 0x80))); t!(v: Path::new(b!("hi", 0xff, "/there")).file_path(), Some(b!("there"))); t!(s: Path::from_str("hi/there").file_path(), Some("there")); t!(s: Path::from_str("hi").file_path(), Some("hi")); t!(s: Path::from_str(".").file_path(), None); t!(s: Path::from_str("/").file_path(), None); t!(s: Path::from_str("..").file_path(), None); t!(s: Path::from_str("../..").file_path(), None); } #[test] fn test_is_absolute() { assert_eq!(Path::from_str("a/b/c").is_absolute(), false); assert_eq!(Path::from_str("/a/b/c").is_absolute(), true); assert_eq!(Path::from_str("a").is_absolute(), false); assert_eq!(Path::from_str("/a").is_absolute(), true); assert_eq!(Path::from_str(".").is_absolute(), false); assert_eq!(Path::from_str("/").is_absolute(), true); assert_eq!(Path::from_str("..").is_absolute(), false); assert_eq!(Path::from_str("../..").is_absolute(), false); } #[test] fn test_is_ancestor_of() { macro_rules! t( (s: $path:expr, $dest:expr, $exp:expr) => ( { let path = Path::from_str($path); let dest = Path::from_str($dest); assert_eq!(path.is_ancestor_of(&dest), $exp); } ) ) t!(s: "a/b/c", "a/b/c/d", true); t!(s: "a/b/c", "a/b/c", true); t!(s: "a/b/c", "a/b", false); t!(s: "/a/b/c", "/a/b/c", true); t!(s: "/a/b", "/a/b/c", true); t!(s: "/a/b/c/d", "/a/b/c", false); t!(s: "/a/b", "a/b/c", false); t!(s: "a/b", "/a/b/c", false); t!(s: "a/b/c", "a/b/d", false); t!(s: "../a/b/c", "a/b/c", false); t!(s: "a/b/c", "../a/b/c", false); t!(s: "a/b/c", "a/b/cd", false); t!(s: "a/b/cd", "a/b/c", false); t!(s: "../a/b", "../a/b/c", true); t!(s: ".", "a/b", true); t!(s: ".", ".", true); t!(s: "/", "/", true); t!(s: "/", "/a/b", true); t!(s: "..", "a/b", true); t!(s: "../..", "a/b", true); } #[test] fn test_path_relative_from() { macro_rules! t( (s: $path:expr, $other:expr, $exp:expr) => ( { let path = Path::from_str($path); let other = Path::from_str($other); let res = path.path_relative_from(&other); assert_eq!(res.and_then_ref(|x| x.as_str()), $exp); } ) ) t!(s: "a/b/c", "a/b", Some("c")); t!(s: "a/b/c", "a/b/d", Some("../c")); t!(s: "a/b/c", "a/b/c/d", Some("..")); t!(s: "a/b/c", "a/b/c", Some(".")); t!(s: "a/b/c", "a/b/c/d/e", Some("../..")); t!(s: "a/b/c", "a/d/e", Some("../../b/c")); t!(s: "a/b/c", "d/e/f", Some("../../../a/b/c")); t!(s: "a/b/c", "/a/b/c", None); t!(s: "/a/b/c", "a/b/c", Some("/a/b/c")); t!(s: "/a/b/c", "/a/b/c/d", Some("..")); t!(s: "/a/b/c", "/a/b", Some("c")); t!(s: "/a/b/c", "/a/b/c/d/e", Some("../..")); t!(s: "/a/b/c", "/a/d/e", Some("../../b/c")); t!(s: "/a/b/c", "/d/e/f", Some("../../../a/b/c")); t!(s: "hi/there.txt", "hi/there", Some("../there.txt")); t!(s: ".", "a", Some("..")); t!(s: ".", "a/b", Some("../..")); t!(s: ".", ".", Some(".")); t!(s: "a", ".", Some("a")); t!(s: "a/b", ".", Some("a/b")); t!(s: "..", ".", Some("..")); t!(s: "a/b/c", "a/b/c", Some(".")); t!(s: "/a/b/c", "/a/b/c", Some(".")); t!(s: "/", "/", Some(".")); t!(s: "/", ".", Some("/")); t!(s: "../../a", "b", Some("../../../a")); t!(s: "a", "../../b", None); t!(s: "../../a", "../../b", Some("../a")); t!(s: "../../a", "../../a/b", Some("..")); t!(s: "../../a/b", "../../a", Some("b")); } #[test] fn test_component_iter() { macro_rules! t( (s: $path:expr, $exp:expr) => ( { let path = Path::from_str($path); let comps = path.component_iter().to_owned_vec(); let exp: &[&str] = $exp; let exps = exp.iter().map(|x| x.as_bytes()).to_owned_vec(); assert_eq!(comps, exps); } ); (v: [$($arg:expr),+], [$([$($exp:expr),*]),*]) => ( { let path = Path::new(b!($($arg),+)); let comps = path.component_iter().to_owned_vec(); let exp: &[&[u8]] = [$(b!($($exp),*)),*]; assert_eq!(comps.as_slice(), exp); } ) ) t!(v: ["a/b/c"], [["a"], ["b"], ["c"]]); t!(v: ["/", 0xff, "/a/", 0x80], [[0xff], ["a"], [0x80]]); t!(v: ["../../foo", 0xcd, "bar"], [[".."], [".."], ["foo", 0xcd, "bar"]]); t!(s: "a/b/c", ["a", "b", "c"]); t!(s: "a/b/d", ["a", "b", "d"]); t!(s: "a/b/cd", ["a", "b", "cd"]); t!(s: "/a/b/c", ["a", "b", "c"]); t!(s: "a", ["a"]); t!(s: "/a", ["a"]); t!(s: "/", []); t!(s: ".", ["."]); t!(s: "..", [".."]); t!(s: "../..", ["..", ".."]); t!(s: "../../foo", ["..", "..", "foo"]); } } </ins>

and all interfaces directly with the file-system should probably use rather than trying to coerce to handle these cases. (Similar issue to .)
nominating feature-complete
Accepted for backwards-compatible
My last referenced commit is a month old, but I'm still working on this issue (currently finishing up the support for Windows paths).
The issues here of dealing with filesystems that are not utf8 seem related to , at least tangentially.


Question: is_ty_must_use(cx, boxed_ty, expr, span) .map(|inner| MustUsePath::Boxed(Box::new(inner))) } <ins> ty::Adt(def, args) if cx.tcx.lang_items().pin_type() == Some(def.did()) => { let pinned_ty = args.type_at(0); is_ty_must_use(cx, pinned_ty, expr, span) .map(|inner| MustUsePath::Pinned(Box::new(inner))) } </ins> ty::Adt(def, _) => is_def_must_use(cx, def.did(), span), ty::Alias(ty::Opaque, ty::AliasTy { def_id: def, .. }) => { elaborate(

<!-- Thank you for filing a bug report! Please provide a short summary of the bug, along with any information you feel relevant to replicating the bug. --Defining traits that contain methods that return such a type: The compiler should probably have warned me about not using the of the , consistent with the behaviour of async functions in Rust 1.67.0. This will also work without the need for traits but async traits is one of the most common use cases for such a type. <!-- If you're using the stable version of the compiler, you should also check if the bug also exists in the beta or nightly versions. --: <!-- Include a backtrace in the code block by setting in your environment. E.g. . --<details<summaryBacktrace</summary<p</p</details<!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --<!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
We looked at this as the wg-async triage meeting today. We agree this is a bug and that the behavior should be consistent with on and/or . This is also likely a good first issue for someone wanting to contribute to async.
Would you say this is also a good issue for a first-time contributor to Rust? I'm not super familiar with the language, but I want to contribute to open source compilers, so I'm hoping to potentially take on this task.
Can I take this ?
Either one of you ( or are welcome to work on this! If it's your first time contributing to Rust, it might take a while to ramp up on it, but I find the best way to do that is to just do it. If you decided to have a go at it, feel free to ask any questions you have either here or in . I tend to watch #wg-async closer than GitHub issues, so you'll probably get a more prompt reply there.
this issue wasn't claimed so you are free to claim and work on it if you still are interested. Kindly comment with to claim the issue.
claim
This is also a very special case of


Question: <ins> // Copyright 2015 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your // option. This file may not be copied, modified, or distributed // except according to those terms. #![allow(dead_code)] enum FooMode { Check = 0x1001, } enum BarMode { Check = 0x2001, } enum Mode { Foo(FooMode), Bar(BarMode), } #[inline(never)] fn broken(mode: &Mode) -> u32 { for _ in 0..1 { if let Mode::Foo(FooMode::Check) = *mode { return 17 } if let Mode::Bar(BarMode::Check) = *mode { return 19 } } return 42; } fn main() { let mode = Mode::Bar(BarMode::Check); assert_eq!(broken(&mode), 19); } </ins>

This test program should exit quietly, but with , it instead fails the assertion: I also tested a really old 32-bit compiler, and it also failed: This test case is reduced from .
Probably a dup of (and )
Perhaps it's a dup. The other two issues are broken with and without optimizations, though. This issue only fails with optimizations.
Also: the expression in this issue doesn't actually require any overlap of the match patterns. (I'll update the test case.)
The bug also happens without a expression:
Good point. Also, this only happens with opt-level=2 (not 0 or 3).
cc Further refinement of the test case: Changing from to a large enough value avoids the bug. Apparently, the value needs to be large enough that, when rounded to the next highest power-of-two-minus-one, it is at least as large as . I think this is an LLVM bug. It involves the metadata. It's as if the compiler is reusing the range information from the check to elide the check (which is missing from the x64 assembly). Relevant unoptimized LLVM IR: isn't used in the test program. When I change its value, the only difference I notice in the unoptimized LLVM IR is in the range metadata (i.e. ).
Thanks Ryan! Indeed LLVM replaces the load with another disregarding the range information.
Patch is at
Patch landed upstream.
Using yesterday's nightly, fails on at this single test. The problem seems to be local llvm 3.6 is still allowed by the build system.


Question: <ins> // Copyright 2015 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your // option. This file may not be copied, modified, or distributed // except according to those terms. #[derive(Clone, Debug, PartialEq)] enum Expression { Dummy, Add(Box<Expression>), } use Expression::*; fn simplify(exp: Expression) -> Expression { match exp { Add(n) => *n.clone(), _ => Dummy } } fn main() { assert_eq!(simplify(Add(Box::new(Dummy))), Dummy); } </ins>

Rust compiler (rustc 1.1.0-nightly ( 2015-05-14) (built 2015-05-14) fails to compile this source: With the error: I can reproduce this ICE also on
Reduced it a bit:
The broken IR:
Looking through it quickly, it seems like the order of the cleanups is messed up? If is then from the switch we go to the label which just jumps to where we memcpy the value for into the alloca and then continue to the label. This is where the problem is since it tries to call on the value returned from . But that's the thing, we wouldn't have called in that arm of the branch and hence shouldn't need to call free either.
Adding a block around suppresses the error. I feel like we had a similar problem not too long ago.
This is "caused" by the micro optimization for dereferencing owned pointer rvalues in . Since is the "trailing" expression for the arm its cleanup will be scheduled in the same scope as the match expression. cc
Either way, this is a dupe of I believe had an idea for a fix.
it's simple, we remove the micro-optimization and always return an datum from . The possibility of returning datum also caused issues with field accesses - see for that fix. We have a static type system and the / marker types are there for a reason. If there is no answer to "how is an rvalue dereference or field meaningful?", then the return type for both of those should be .


Question: <ins> // Copyright 2015 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your // option. This file may not be copied, modified, or distributed // except according to those terms. #[derive(Clone, Debug, PartialEq)] enum Expression { Dummy, Add(Box<Expression>), } use Expression::*; fn simplify(exp: Expression) -> Expression { match exp { Add(n) => *n.clone(), _ => Dummy } } fn main() { assert_eq!(simplify(Add(Box::new(Dummy))), Dummy); } </ins>

Original testcase by (reduced from ): The debug log reveals that a (shallow free) cleanup of the rvalue is scheduled in the scope of AST node , while the dereference expression itself has id : reveals that id refers to the entire function's block: This means the cleanup of a value valid only within a match arm is scheduled outside the arm. cc
Dupe of Leaving open for now but we can close either.
This is caused by a micro-optimization specific to dereferences of rvalues. Is this used oftenly enough to justify its existence in the first place? Even if it is, I guess we can disable it in problematic cases like this, where the temporary scope of the match arm's RHS is outside the arm.
I think we can probably remove it, in general removing special treatment of seems like a Good Thing
Triage: this still ICEs.
can you point me/us at the micro-optimization that you mentioned above, so that I (we) can try removing it ourselves and measure the impact?
I only vaguely recall this issue, but I'm pretty sure I was talking about .
triage: still ICEs.
Indeed, it doesn't even need : Does this at least continue to ICE even if LLVM is compiled without assertions? If not, we may want to up the priority on this ticket.


Question: } } <del> //use private::Future; </del> <ins> #[cfg(imported)] use private::Future; </ins> fn bar(arg: Box<dyn private::Future>) { <ins> // Importing the trait means that we don't autoderef `Box<dyn Future>` </ins> arg.wait(); <del> //~^ ERROR the `wait` method cannot be invoked on a trait object </del> <ins> //[unimported]~^ ERROR the `wait` method cannot be invoked on a trait object </ins> } <del> fn main() { } </del> <ins> fn main() {} </ins>

<!-- Thank you for filing a bug report! Please provide a short summary of the bug, along with any information you feel relevant to replicating the bug. If you cannot produce a minimal reproduction case (something that would work in isolation), please provide the steps or even link to a repository that causes the problematic output to occur. --Given the following code: <!-- Please provide a link to --The current output is: <!-- The following is not always necessary. --Ideally the output should suggest -ing the out-of-scope trait, similar to E0599: <!-- If the problem is not self-explanatory, please provide a rationale for the change. --Someone tried to do dynamic dispatch between two underlying used a boxed trait object (with the type spelled , so wasn't in scope) and call the method on the boxed reader, and it was unclear from the error why the method wasn't accessible: <!-- If dramatically different output is caused by small changes, consider also adding them here. If you're using the stable version of the compiler, you should also check if the bug also exists in the beta or nightly versions. The output might also be different depending on the Edition. --This is reproducible on nightly (rustc 1.67.0-nightly ( 2022-12-01)).
Note: A sign there may be a problem worth fixing here is the code I wrote is a near copypaste of a top stack overflow answer for a :)
So, after some investigating, this appears to be a regression of , which essentially the same suggestion I proposed here: adding a for the trait object's trait. When generating the error when the lookup fails, the trait is initially included in the list of candidates, but it's filtered out by this (because the callee is the trait method, and so the parent of the callee is the trait, which is the candidate): This was introduced in by which also removed the corresponding suggestion from the test's expected output. It's not immediately clear to me from the PR () why this change was necessary. If the call is removed, the only UI test that fails is the one. Reverting that change would fix this issue, but I'm not sure if there are other cases I'm missing.


Question: } } <del> //use private::Future; </del> <ins> #[cfg(imported)] use private::Future; </ins> fn bar(arg: Box<dyn private::Future>) { <ins> // Importing the trait means that we don't autoderef `Box<dyn Future>` </ins> arg.wait(); <del> //~^ ERROR the `wait` method cannot be invoked on a trait object </del> <ins> //[unimported]~^ ERROR the `wait` method cannot be invoked on a trait object </ins> } <del> fn main() { } </del> <ins> fn main() {} </ins>

(cc with whom I was discussing this on Mastodon)
Haven't look at this yet, maybe I misinterpreted what was achieving, and that commit just needs a revert -- can't recall why I did that.
Yeah, lol, I totally overlooked that the would be load-bearing due to the way we treat trait methods on as inherent. I'll revert , modify the test, etc.


Question: InvocationKind::Derive { ref path, .. } => path.span, } } <ins> pub fn attr_id(&self) -> Option<ast::AttrId> { match self.kind { InvocationKind::Attr { attr: Some(ref attr), .. } => Some(attr.id), _ => None, } } </ins> } pub struct MacroExpander<'a, 'b:'a> {

This is the code you are supposed to use according to the lint. It fails to compile, with It compiles fine if you use just one of the two attributes. Swapping attribute order makes it complain about the last non-derive attribute. This seems broken? cc
Confirmed, this behavior is broken. Possibly related to .
As a workaround specific to serde_derive, we allow multiple elements within one attribute.
yeah, that's what I ended up doing
This should be fixed in


Question: } fn crate_matches(&mut self, metadata: &MetadataBlob, libpath: &Path) -> Option<Svh> { <del> let root = metadata.get_root(); if let Some(is_proc_macro) = self.is_proc_macro { if root.macro_derive_registrar.is_some() != is_proc_macro { return None; } } </del> let rustc_version = rustc_version(); <del> if root.rustc_version != rustc_version { </del> <ins> let found_version = metadata.get_rustc_version(); if found_version != rustc_version { </ins> info!("Rejecting via version: expected {} got {}", rustc_version, <del> root.rustc_version); </del> <ins> found_version); </ins> self.rejected_via_version.push(CrateMismatch { path: libpath.to_path_buf(), <del> got: root.rustc_version, </del> <ins> got: found_version, </ins> }); return None; } <ins> let root = metadata.get_root(); if let Some(is_proc_macro) = self.is_proc_macro { if root.macro_derive_registrar.is_some() != is_proc_macro { return None; } } </ins> if self.should_match_name { if self.crate_name != root.name { info!("Rejecting via crate name");

thread 'rustc' panicked at 'index out of bounds: the len is but the index is ', ../src/libserialize kind of expected, after switching toolchain from 1.15 back to 1.14. cargo clean && cargo build "repaired it"
Do you have some more information on how to reproduce this?
I hit this compiling hyper with 1.13 stable.
Which version of hyper is that? I can't reproduce with latest .
It was with master. I tried and now it works. So rustc/cargo were using libs compiled when I tried this with 1.15nightly?
Sounds a bit like cargo was re-using libraries that it should have re-compiled because the compiler has changed. cc
Sorry if I did not save the full backtrace - It was my first issue report. I switched away from 1.15 nightly back to 1.14 nightly in order to solve a serde macro issue. Cargo cleaning and rebuilding fixed the issue. I will make sure to run again and capture the output if it happens again. On 11/16/2016 4:54 PM, Michael Woerister wrote:
Sounds suspiciously like a Cargo bug! If there's a reproduction I'd be more than willing to dig in.
I believe I just reproduced this in Cargo itself: When executing with the last few lines are: So what's happening here is: is populating the entire crate cache with a bunch of libraries on the nightly compiler. switching compilers, Cargo decides to recompile everything. doesn't delete everything as it recompiles, rather incrementally builds up a new crate cache compiler, given to that directory, will read crates in that directory. compiler panics when reading a crate compiled by a future compiler. So I take back what I said earlier about this likely being a Cargo bug, it seems like this may be a bug in the compiler trying to read stale metadata? I thought we had like 10 checks in place to ensure we didn't do that, but maybe it's still leaking through?
I'm going to tag this as a stable-to-nightly regression as this appears to have been introduced in nightly. If I build cargo with beta and then switch to beta, the compiler doesn't ICE.
The panic on stable happens in we check version information. Did we change how we encode version information?


Question: } fn crate_matches(&mut self, metadata: &MetadataBlob, libpath: &Path) -> Option<Svh> { <del> let root = metadata.get_root(); if let Some(is_proc_macro) = self.is_proc_macro { if root.macro_derive_registrar.is_some() != is_proc_macro { return None; } } </del> let rustc_version = rustc_version(); <del> if root.rustc_version != rustc_version { </del> <ins> let found_version = metadata.get_rustc_version(); if found_version != rustc_version { </ins> info!("Rejecting via version: expected {} got {}", rustc_version, <del> root.rustc_version); </del> <ins> found_version); </ins> self.rejected_via_version.push(CrateMismatch { path: libpath.to_path_buf(), <del> got: root.rustc_version, </del> <ins> got: found_version, </ins> }); return None; } <ins> let root = metadata.get_root(); if let Some(is_proc_macro) = self.is_proc_macro { if root.macro_derive_registrar.is_some() != is_proc_macro { return None; } } </ins> if self.should_match_name { if self.crate_name != root.name { info!("Rejecting via crate name");

cc
I incremented the header version number though... It should be impossible for older rustc to get that far.
on it looks the same on . Perhaps we just need a PR to bump that?
Oh it looks like my changes have reached stable. I wonder if perhaps the is in a precarious spot and/or has been moved or the serialization otherwise changed. This requires further investigation.
Why is this even calling leb128? Are we using variable-length integers in the metadata header?
I assumed leb128 was the crate being built, not one used by the compiler.
Sure enough. Now every change to requires us to update the LLVM version number, because checking the rustc version number requires parsing . I think we should go back to JSON version guard.
Ahh that's what it was. We can put it at the start then, before/after the absolute address. I'd forgotten that the whole struct would have to be decoded.
What was the PR that modified the versioning scheme? I gather that something has changed about it. Can somebody explain more clearly what is going wrong here?
I mistakenly put the version string inside a larger , somehow thinking that it being at the start helped, but if the whole (that is, ) can't be decoded (e.g. because its definition changed) then you don't get to see the version string, whereas before it was separate. The fix would be to separate it again, put it back at the start and leave it there. Or just encode/decode it before instead of as part of it. Either will work as long as it can't be affected by changes to the metadata schema. The PR that introduced the new schema was , although breakage didn't happen until changed later, for procedural macros and/or the name resolution changes ( would know).
removed the field from for macro modularization.
I believe I have a fix ready. Does this affect beta too now, or just nightly?
AFAIK it's just nightly, not beta or stable


Question: fn foo<'a>() -> &'a () { Hash([0; HASH_LEN]); init_hash(&mut [0; HASH_LEN]); <ins> let (_array,) = ([0; HASH_LEN],); </ins> &() }

According to my CI this started 2020-04-16 (https://travis-) - : <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --<!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
reports a regression in (PR ), cc
Assigning as and removing .
assign
Oh dear, I was hoping crater will find all such issues (this is ). I guess nobody in the wild is pattern-matching tuples with non-literal-length arrays. I can repro by adding this line () to the test (and the PR fixing this should IMO add the line to ): The fix is likely trivial: add another normalization, like in Presumably, for field accesses, this time (weird that doesn't trigger the bug). <hr/I've brought it up before w/ (but I don't know if I've seen a response), that the lack of normalization for array lengths in typeck is pushing complexity to the rest of the compiler and maybe we should do it in typeck. But then again, Lazy Normalization Is Coming, so maybe there's no point.
I believe I responded in the PR where you already asked me about that. I'm fine reintroducing a normalization phase


Question: <del> Subproject commit 732825dcff6d1f115225305ce5e0c9c9d876a0ff </del> <ins> Subproject commit e8642c7a2900bed28003a98d4db8b62290ac802f </ins>

Hello, this is your friendly neighborhood mergebot. After merging PR rust-lang/rust, I observed that the tool clippy-driver no longer builds. A follow-up PR to the repository is needed to fix the fallout. cc do you think you would have time to do the follow-up work? If so, that would be great! cc the PR reviewer, and nominating for compiler team prioritization.


Question: <ins> error: implementation of `FnOnce` is not general enough --> $DIR/issue_74400.rs:12:5 | LL |     f(data, identity) |     ^ implementation of `FnOnce` is not general enough | = note: `fn(&'2 T) -> &'2 T {identity::<&'2 T>}` must implement `FnOnce<(&'1 T,)>`, for any lifetime `'1`... = note: ...but it actually implements `FnOnce<(&'2 T,)>`, for some specific lifetime `'2` error: aborting due to previous error </ins>

Regression from to . The following code yields an incorrect error: On beta, this gives: On nightly, this:
would you be so kind to label this issue? It's sliding down the list while being hard to search.
Regression occurred between nightly-2020-06-23 and nightly-2020-06-24, I suspect is related (it's ?).
It would be great to find the culprit PR. ping cleanup
Hey Cleanup Crew ICE-breakers! This bug has been identified as a good "Cleanup ICE-breaking candidate". In case it's useful, here are some [instructions] for tackling these sorts of bugs. Maybe take a look? Thanks! <3 [instructions]: https://rustc-dev- cc
searched nightlies: from nightly-2020-06-23 to nightly-2020-06-24 regressed nightly: nightly-2020-06-24 searched commits: from to regressed commit: <details<summarybisected with <a href=''cargo-bisect-rustc</av0.5.1</summaryHost triple: x86_64-unknown-linux-gnu Reproduce with: </detailsIf we rule out document changes then we have
Bisection seems to confirm this, cc
With a little more git bisect it seems is the first bad commit And I guess the change to the test case also shows that
Assigning as and removing .
Well, I can certainly imagine how this could come about as a result of the leak-check moving, since the same error can now be detected at a different point during type-checking. What's happening in that confusing error, I imagine, is that we are now winding up with a "universe error" that comes out from region checking. The identity function was inferred to take an argument for some ... Well, in short, I imagine that the solution here is to improve the error reporting for the new point where the error occurs, but it'll take a bit more digging to give good mentoring instructions into how to go about doing that.
This is now a stable regression.
Discussed in T-compiler meeting. Seems related to (or potential duplicate of) ?
On today's nightly, the error is now: That seems correct, and with the extra lifetime, the snippet compiles:


Question: // stripping away any starting or ending parenthesis characters—hence this // test of the JSON error format. <del> #![warn(unused_parens)] </del> <ins> #![deny(unused_parens)] </ins> #![allow(unreachable_code)] fn main() { // We want to suggest the properly-balanced expression `1 / (2 + 3)`, not // the malformed `1 / (2 + 3` <del> let _a = (1 / (2 + 3)); </del> <ins> let _a = (1 / (2 + 3)); //~ERROR unnecessary parentheses </ins> f(); }

Update test file to use + annotations instead of (see https://rust-) The files need to update are: - <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --This issue has been assigned to via . <!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
is the correct file name , not ? And what about this comment?
Is the problem fixed ? If not, I would like to work on it !
WIP PR has been closed due to inactivity so you can work on this.
I would like to be assigned to this issue. Am I supposed to do a new pull request ?
claim Go ahead!
Error: Parsing assign command in failed: claim Please let know if you're having trouble with this bot.
claim
Error: Parsing assign command in failed: claim Please let know if you're having trouble with this bot.
Hmm, it's odd, anyway, you can assign the issue to yourself by comment Ah, I mistaken the command, the collect one is , sorry.
claim
If I understood well the problem: I must replace all the by just , even if comment exist ? Because the output changes, I should modify the .stderr version I must change with in , and as should raise an error we add why add in the version ?


Question: /// # Examples /// /// ``` <del> /// #![feature(mutex_unpoison)] /// </del> /// use std::sync::{Arc, RwLock}; /// use std::thread; ///

Feature gate: This is a tracking issue for functions to clear the poisoned flag on and . [x] [x] Implementation: [x] Final comment period (FCP) [ ] Stabilization PR <!-- Once the feature has gone through a few release cycles and there are no unresolved questions left, the feature might be ready for stabilization. If this feature didn't go through the RFC process, a final comment period (FCP) is always needed before stabilization. This works as follows: A library API team member can kick off the stabilization process, at which point the rfcbot will ask all the team members to verify they agree with stabilization. Once enough members agree and there are no concerns, the final comment period begins: this issue will be marked as such and will be listed in the next This Week in Rust newsletter. If no blocking concerns are raised in that period of 10 days, a stabilzation PR can be opened by anyone. --<!-- Include any open questions that need to be answered before the feature can be stabilised. If multiple (unrelated) big questions come up, it can be a good idea to open a separate issue for each, to make it easier to keep track of the discussions. It's useful to link any relevant discussions and conclusions (whether on GitHub, Zulip, or the internals forum) here. --- None yet.
Hi, I read (wich got automatically closed by the way) and the PR It seems all good. What is the next step to make it in the stable API ? Can I help somehow ?
Hi, sorry if flooding but are there any updates regarding this issue?
I think the next step is one of the rust developers needs to start the final comment period. IDK what the criteria for doing so are.
fcp merge
Team member has proposed to merge this. The next step is review by the rest of the tagged team members: [ ] [x] [x] [ ] [x] No concerns currently listed. Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See for info about what commands tagged team members can give me.
I was wondering whether the idea of had been considered and found that addressed it. SGTM!


Question: /// # Examples /// /// ``` <del> /// #![feature(mutex_unpoison)] /// </del> /// use std::sync::{Arc, RwLock}; /// use std::thread; ///

:bell: This is now entering its final comment period, as per the . :bell:
Given the state change this method causes, I am curious about rationale behind using vs ?
If it took , it wouldn't be possible to unpoison a mutex shared between threads despite that being the main case where poisoning can happen.
I just want to add that probably before stabilizing this method we should implement a lint which catches invalid patterns of using these methods since it is very easy to misuse. Or, at the very least, include information about potential footgun in documentation for these methods.
What kind of lint do you exactly want? What are the patterns in question that you are worried about?
The final comment period, with a disposition to merge, as per the , is now complete. As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed. This will be merged soon.
exactly the possible error outlined by Mara Bos: calling before acquiring a lock. The only way this usage is correct is when no other thread can act on mutex - either because they don't exist or because they are all blocked on something else. I believe that these situations are rare enough to flag such a pattern as at least suspicious.
If unpoison was a method of PoisonError or of MutexGuard instead of mutex, would it prevent such problems?
That was discussed here:


Question: // FIXME(valtrees): check whether const qualifs should behave the same // way for type and mir constants. let uneval = match constant.literal { <del> ConstantKind::Ty(ct) if matches!(ct.kind(), ty::ConstKind::Param(_)) => None, </del> <ins> ConstantKind::Ty(ct) if matches!(ct.kind(), ty::ConstKind::Param(_) | ty::ConstKind::Error(_)) => { None } </ins> ConstantKind::Ty(c) => bug!("expected ConstKind::Param here, found {:?}", c), ConstantKind::Unevaluated(uv, _) => Some(uv), ConstantKind::Val(..) => None,

<!-- Thank you for finding an Internal Compiler Error! If possible, try to provide a minimal verifiable example. You can read "Rust Bug Minimization Patterns" for how to create smaller examples. --<!-- If you're using the stable version of the compiler, you should also check if the bug also exists in the beta or nightly versions. --: <!-- Include a backtrace in the code block by setting in your environment. E.g. . --<details<summary<strongSample Backtrace</strong</summary<p</p</detailsThe original code () is already fixed by , however some other mutations of the original code still ICEs with the same backtrace and error. Thanks. <brCC <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --<!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
If the parsing error is not needed to manifest the issue, can that be fixed () in the MCVE you provided? since more error messages just obscures the underlying ICE that needs to be fixed here.
(also this could probably use a more descriptive title that reflects the ICE, and doesn't just point to another issue, but that's just my opinion)
Is that better?
claim


Question: } // At this point, we are calling a function, `callee`, whose `DefId` is known... if is_lang_panic_fn(tcx, callee) { self.check_op(ops::Panic); <ins> // const-eval of the `begin_panic` fn assumes the argument is `&str` if Some(callee) == tcx.lang_items().begin_panic_fn() { match args[0].ty(&self.ccx.body.local_decls, tcx).kind() { ty::Ref(_, ty, _) if ty.is_str() => (), _ => self.check_op(ops::PanicNonStr), } } </ins> return; }

The panic payload of the macro (as passed on to ) is generic, but the CTFE panic support assumes it will always be an . This leads to ICEs: Cc
I guess we could support this, but we'd have to find some way to render the errors. Maybe we need to wait for const traits so we can require const Debug?
Alternatively constck could require the argument to have type .
also requires a
Yeah, the libcore panic machinery does not support non-format-string panics.
Aye, left out the latter of my message. is likely the most common argument to , and it's not unusual with e.g. ; so I agree that waiting for const traits before supporting any other arguments is reasonable.
Im hitting this ICE on stable without feature flag:
Hm indeed, that is interesting: any idea why we go on here after seeing an error? Does this kind of behavior mean any part of CTFE is reachable on stable somehow?
yes. We're not gating CTFE on whether there were stability (or any other static checks) failing. I've been abusing this since 1.0 to demo things on the stable compiler and never got around to fixing it. I don't even know if we have a tracking issue for it.
:rofl: So what would be a possible approach here? Replace such consts by early enough that their MIR never actually gets evaluated? We might as well use this issue, IMO...
That would require us to mutate all MIR and potentially even HIR. Other possible solutions: poison the mir Body if const checks fail (by adding a field like or by just nuking the body by replacing it with ) and thus make allow const evaluation to check if the mir Body failed the checks and bail out before doing anything make the have an field similar to
On second thought, let's leave this issue for the ICE, and make a new one for "we can trigger nightly-only const code on stable because we don't abort compilation early enough":
Given that this ICE is pre-existing, as shown by , can we un-block ? I'd really like to see const_panic stabilized, and this seems like a minor issue to be blocking on.
That comment is an example of accidentally partially exposing an unstable feature on stable -- a separate bug tracked in I don't see how that's an argument for shipping that feature on stable when it is broken.


Question: } // At this point, we are calling a function, `callee`, whose `DefId` is known... if is_lang_panic_fn(tcx, callee) { self.check_op(ops::Panic); <ins> // const-eval of the `begin_panic` fn assumes the argument is `&str` if Some(callee) == tcx.lang_items().begin_panic_fn() { match args[0].ty(&self.ccx.body.local_decls, tcx).kind() { ty::Ref(_, ty, _) if ty.is_str() => (), _ => self.check_op(ops::PanicNonStr), } } </ins> return; }

In particular, applies to all unstable const features. Fixing this bug here is not even that hard I think: somewhere around here there needs to be a check that the argument has type . Re: , AFAIK that one is also blocked on figuring out how the diagnostics should look like, and a further concern (though maybe not blocking) is .
I guess that might make this a non-issue, where it is proposed also would require as first argument.
For reference: , which was recently merged. The plan is to fix the behaviour of in the Rust 2021 edition. Given that this ICE is listed as a blocker for , does this mean that stabilizing that will have to wait for the next edition?
No, because breaking changes are allowed to be made to unstable features without moving to a new edition; that's kinda the whole idea. The 2021 edition will effectively just extend the same restriction to all invocations, const context or not.
I'd like to tackle this but I'm not very experienced with the CTFE code. I get that we need to check from the that we've matched on in this branch and we need to match on the enum, but: For the first question, I see and that we probably want to check that the type of the tuple and that there's variants of that are , but do we stop the iteration when we get one that is a type? Is the projection list always guaranteed to terminate in a variant of that has a ? And once we have an instance of , do we just check that there's any_ projection in the list that has a ?
you can use to get the type of the . Then you can test its to be .


Question: let proc_macro_ty_method_path = |method| cx.expr_path(cx.path(span, vec![ proc_macro, bridge, client, proc_macro_ty, method, ])); <del> custom_derives.iter().map(|cd| { cx.expr_call(span, proc_macro_ty_method_path(custom_derive), vec![ cx.expr_str(cd.span, cd.trait_name), cx.expr_vec_slice( span, cd.attrs.iter().map(|&s| cx.expr_str(cd.span, s)).collect::<Vec<_>>() ), local_path(cd.span, cd.function_name), ]) }).chain(custom_attrs.iter().map(|ca| { cx.expr_call(span, proc_macro_ty_method_path(attr), vec![ cx.expr_str(ca.span, ca.function_name.name), local_path(ca.span, ca.function_name), ]) })).chain(custom_macros.iter().map(|cm| { cx.expr_call(span, proc_macro_ty_method_path(bang), vec![ cx.expr_str(cm.span, cm.function_name.name), local_path(cm.span, cm.function_name), ]) })).collect() </del> <ins> macros.iter().map(|m| { match m { ProcMacro::Derive(cd) => { cx.expr_call(span, proc_macro_ty_method_path(custom_derive), vec![ cx.expr_str(cd.span, cd.trait_name), cx.expr_vec_slice( span, cd.attrs.iter().map(|&s| cx.expr_str(cd.span, s)).collect::<Vec<_>>() ), local_path(cd.span, cd.function_name), ]) }, ProcMacro::Def(ca) => { let ident = match ca.def_type { ProcMacroDefType::Attr => attr, ProcMacroDefType::Bang => bang }; cx.expr_call(span, proc_macro_ty_method_path(ident), vec![ cx.expr_str(ca.span, ca.function_name.name), local_path(ca.span, ca.function_name), ]) } } }).collect() </ins> }; let decls_static = cx.item_static(

The PR improved the situation about documenting proc macros. Thanks for that! However, I noticed a bug with it. I'm using . Running results in this: ! Looks like a simple typo or off-by-one or something like that bug. When using only and not s, everything looks good to me. CC
Oh, and also, running prints: Not sure if this warning is expected there. And not sure if related to this bug.
This is a consequence of the fact that has not yet been merged. Once it gets merged, and rust updates its Cargo submodule, this issue should go away.
It turns out that this issue was unrelated. I've opened a PR to fix it.
I see. Thanks for your work on this :)


Question: } } <ins> fn get_absolute_rpaths(libs: &[Path]) -> ~[Path] { vec::map(libs, |a| get_absolute_rpath(a) ) } pub fn get_absolute_rpath(lib: &Path) -> Path { os::make_absolute(lib).dir_path() } </ins> #[cfg(stage0)] pub fn get_install_prefix_rpath(target_triple: &str) -> Path { let install_prefix = env!("CFG_PREFIX");

These are configured something like
I am not sure why this happens off hand, but when bootstrapping the non-build-triple compilers artifacts are copied from e.g. to .


Question: let attrs = self.parse_inner_attributes()?; let post_attr_lo = self.token.span; <del> let mut items = ThinVec::new(); while let Some(item) = self.parse_item(ForceCollect::No)? { self.maybe_consume_incorrect_semicolon(Some(&item)); </del> <ins> let mut items: ThinVec<P<_>> = ThinVec::new(); // There shouldn't be any stray semicolons before or after items. // `parse_item` consumes the appropriate semicolons so any leftover is an error. loop { while self.maybe_consume_incorrect_semicolon(items.last().map(|x| &**x)) {} // Eat all bad semicolons let Some(item) = self.parse_item(ForceCollect::No)? else { break; }; </ins> items.push(item); }

Tested on nightly and stable. The second diagnostic is strange. <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --<!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
label +A-diagnostics S-has-mvce
claim
any idea why rustbot ignored me?
Just guessing, the missing + for S-has-mcve maybe?
Oops, this should'nt've been closed.


Question: # use this rustfmt binary instead as the stage0 snapshot rustfmt. #rustfmt = "/path/to/rustfmt" <ins> # Instead of downloading the src/stage0 version of cargo-clippy specified, # use this cargo-clippy binary instead as the stage0 snapshot cargo-clippy. # # Note that this option should be used with the same toolchain as the `rustc` option above. # Otherwise, clippy is likely to fail due to a toolchain conflict. #cargo-clippy = "/path/to/cargo-clippy" </ins> # Whether to build documentation by default. If false, rustdoc and # friends will still be compiled but they will not be used to generate any # documentation.

I'm pretty sure it is possible to set custom paths for , & in but not for clippy.
label +T-bootstrap