-
Notifications
You must be signed in to change notification settings - Fork 13.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Better diagnostics when concatenating strings #39018
Comments
I'd like to take a stab at this if I could get some guidance as to where things in the compiler need to be changed and how. |
Sure! What you need to do is find the place in the code which emits the error on Somewhere there you'll want to add an if checking that the operation is add, and then check that the LHS and RHS are both Then, |
I'll be home soon so I'll get working on it then |
Cool. You can ask me (or others) for help in #rust-internals if you need it. |
I found where after a bit of prodding so I think I have this part but if I run into problems I'll definitely jump on #rust-internals |
For the It could even apply to anything that wants |
Yeah. It's just that the concatenation stuff is handled separately. I was planning on investigating this (or help someone investigate this) after this bug is fixed, by refactoring the functionality introduced here into a separate function that gets used in multiple error cases. |
@Manishearth here's the WIP that I have now: https://github.com/mgattozzi/rust/commit/007d45926f920b51cfab9924f0ee6461f527a2aa Here's the output though:
It seems the |
Hmm, You also might want to restructure the code so that the "an implementation of |
Let me try it out with string literals to see if I get something different. |
No it doesn't work. It looks like unless we identify what |
ugh. You could check the AST if it's a literal but that's not so great. Would there be a way of extracting this info? @eddyb ? |
Checking for a literal is okay-ish. You might be able to type-check the RHS before emitting the error, giving you the missing information. cc @nikomatsakis |
Yeah, type checking the RHS is what I want to do, but I don't know how 😄 |
It's this call. If I were you I would keep an |
@eddyb thanks for the tip. I'm going to work out a rough (but somewhat naive) implementation that works and then hopefully get that refined from there with some guidance. |
@Manishearth My most recent commit got it working now. While it's a bit naive (we could return early) it's doing what's needed for now. I think we can move from here into the next few parts of the issue. |
Okay, now try to suggest using Make the error message mention explicitly that the first operand must be a |
I've got it outputting a message but I'd like it to be the string literal with
How would I get it so that only the "Hello" part prints? Also having it underlined or with more spaces on the line for readability would be nice as well, but I think that's secondary compared to getting this part done right. |
Don't extract the string literal at all. There might not be one. Just use
|
Ahhh okay I see what you're saying after digging through some other code for examples. When I get this part working I'll let you know. |
@Manishearth This gets it working to output the proper help message and works regardless of how many uses of Not sure what to put for the error message when it's destructured in the match statement there. I think next would also be matching on things like |
Yeah, probably. Refactoring this into a method might help. It's also okay to land with just this one suggestion with a compile-fail test and fix the rest in a separate PR. Regardless, you probably should clean it up a bit and make a WIP PR so that it's easier to leave inline comments.
I don't know what you mean here? |
Okay sounds good. If you look here it's just a blank string. I'm not sure what to place here in the case where span_to_snippet would fail. |
Oh. Use a |
Ahhhh okay got it. I've split it all out into a separate method that just checks for the use of |
src/tests/compile-fail. Look at other tests in that folder, basically you can annotate errors and suggestions and stuff with |
Alright I'll play around with it! |
@Manishearth I've cleaned it up and put in a test case. The PR is here #39116 |
Nice work @mgattozzi! I have to ask though -- maybe we should just make adding strings work like people expect? I think the "fear of allocations" is the only reason not to? If so, it seems a bit overblown to me personally. I feel like I often get annoyed at places that we've made it less convenient to concatenate and manipulate strings because of the cost of allocation -- but usually the code where I'm manipulating strings is not in my hot path, or even if it is I often find I need the allocations anyway, I just have to express them differently. That said, the only major example of this I can come up with something around the way that |
I am .... conflicted about that. I would not oppose it however. Diagnostics is the path of least resistance here; not contingent on an rfc (I think this would be) and not controversial, so I went that path 😄 . Changing this with an impl works too. Similarly, #39029 can be replaced by a language change, I just am not as convinced if I want that language change to want to make an rfc for it. |
I thought coherence was the main problem, not the fear? (You can't |
Thank you @nikomatsakis! Personally I like that Rust is explicit. Sure it's inconvenient to have to add a |
Ah, this may be true. I feel like the inability to extend coherence to cover a set of crates is an annoyance I would like to overcome. :(
Certainly there is something of a trade-off involved. |
I've often wanted the ability to forward-declare an impl, saying something like #[forward_declare(StringAddition)]
impl Add<str> for str {
type Target = _;
fn add(&self, other: &str) -> Self::Target;
} This generates an instance of a new kind of item with item name and later, #[fills_declaration(core::StringAddition)]
impl Add<str> for str {
type Target = String;
fn add(&self, other: &str) -> String { /* body */}
} The system works the same way lang items do; these forward-declared impls are left dangling and can't be used until someone defines them, and you can only have one definition active (much like how redefining lang items doesn't work). However, from the point of view of coherence and conflict resolution, these impls do exist even if not defined (I'm not sure if any situation arises where this matters outside of Thus, the impl exists in two places at once. From the POV of coherence, it exists in libcore, but from the POV of codegen and type resolution, it exists in libstd. The main purpose is to handle cases like these where you have multiple separate crates that are nevertheless maintained as a single unit. Coherence exists so that I, a Servo developer, am not forced to deal with error messages that must be fixed in, say, hyper (e.g. when I include some other crate and now we have conflicting impls that can't be fixed on the servo-side). But in this case the developers of libstd and libcore are the same and it's fine to punch through coherence for this. Similarly, Servo itself is split into crates for maintainability and compile times, but being able to punch through coherence in the context of servo is pretty useful. Being able to say "I know that this can break if upstream-crate-X changes things, but I am upstream-crate-X and want to do it anyway" is useful. We've had to merge crates before because coherence was making things problematic. |
You don't have to do that, just don't mention it, and might as well do the same with the method. |
You will have to do something for inherent impls though. I guess you can just define the item name and leave it at that. |
Well... inherent impls don't do anything other than wrap the items inside, so each one would have to be annotated, and it'd be a bit of a mess. |
FWIW I think suggesting |
It feels to me like That does mean that people might end up doing |
pre-rfc'd the forward declaration thing in https://internals.rust-lang.org/t/pre-rfc-forward-impls/4628 |
Add clearer error message using `&str + &str` This is the first part of #39018. One of the common things for new users coming from more dynamic languages like JavaScript, Python or Ruby is to use `+` to concatenate strings. However, this doesn't work that way in Rust unless the first type is a `String`. This commit adds a check for this use case and outputs a new error as well as a suggestion to guide the user towards the desired behavior. It also adds a new test case to test the output of the error.
Fixed in #39116 |
A lot of people come from other languages and expect
"a" + "b"
to work, orx + y
wherex
andy
are bothString
s.We should be suggesting
"a".to_string() + "b"
in the first place,x + &y
in the second, and in both cases also recommend usingformat!()
instead.Context: https://www.reddit.com/r/rust/comments/5nl3fk/rust_severely_disappoints_me/dccdi17/
The text was updated successfully, but these errors were encountered: