Change:
The positional indicator of the moved value appears with the wrong indentation as you can see in the screenshot: ! Apparently i don't know why this happens, i am using tabs (with 4 spaces width) to format my source. In the reality these lines look as follows:
Hello fellow tabs user! I have experienced this too, I was just too lazy to do a bug report. Thanks for filing it :)
Before rendering compiler messages to-be-printed are analyzed and some spaces are replaced with tabs in order to respect identation in the code. All that is done heuristically on a best-effort basis. Now we even multiline spans in nightly, which makes this problem even more difficult to solve. That means simply replacing a few spaces with tabs is not sufficient anymore. My opinion is that it would be best to simply render all tabs from the source code as 4 spaces in stderr. Anything else will be even more buggy. Thoughts? If you agree, I'll make a PR.
Tabs should be handled by putting a tab in the matching output. Seems that is not the right answer for all situations? I'm confused as to why this isn't working, as the original example seems like it should work okay.
Do you have a smaller repro we could use to fix the issue?
(btw, sorry for the delay in response, I've been on an extended vacation)
No. Take a look at this example: We have these lines connecting "starting here" and "ending here". How many underscores should those lines have? Depends on how many spaces your terminal/text editor/IDE/browser uses to display tabs. Ultimately, it's impossible to render error messages together with tabs properly accross all environments. Another option would be to convert tabs into 4 spaces before rendering. Then error/warning markers will not be misaligned with code - that's the major advantage of converting tabs to spaces.
There is no work-around for this that I can see, even setting tab-width to 4 doesn't work since the LHS number output is variable-width.
As said I think the best soltution is to render the tabs as spaces for the error output. It might be theorically possible to get the tab size from the terminal but that sounds like more effort this it is worth. I am rarely (never) copying the output of errors back into my source so it shouldn't be an issue. A bouns feature would be user-adjustable tab size but starting with 2 or 4 is probably sufficent.
Also is there any way to get this prioritized? It seems like having unaligned error messages is unacceptable as the text relies on the range indicators to make any sense.
Why not preserving the whitespace-chars of the specific line as given in the source, just replacing everything else from the occurrence of the first non-white-space on?
It should be safe to "repeat" the given line, and replace the non-whitespace-content with the debug output. Imagine the following: To handle that situation in line 4 right, the following steps should work: the line () and replace non-tabs by spaces: /insert the debug message from given index on (in this example 11 for the curly bracket):
I think the only way to resolve this is to replace each tab with a fixed amount of spaces. Your suggestion doesn't work because of cases like: Giving you errors like: It needs to draw the entire line with characters instead of whitespace.
- thanks i didn't think of that case.
From a suggestion by on IRC on what gcc/clang do, I've looked at their behaviour. The tested statement for clang was , so 4 tabs followed by , followed by (tab between and ). clang output: For gcc, I've omitted the last character to produce an invalid string literal. It gave the following error: Both treated the before the fsdf as "column 16" so they gave each tab one "column point". Now let's add a space to test whether they implement a "tab stop" algorithm, or a "tab == 4 whitespace" one. Clang output when I add one space before the : One space more between the line start and , but one space less between and . Gcc output of the same (with the last " removed): So clang basically implements the full "tab == go to the next tab stop" algorithm, while gcc replaces each tab by one whitespace, and continues to print whitespaces next to tabs. Used versions: , I suggest to take a similar path to the one clang is going, by keeping the column number as it is right now (the same that clang/gcc are using), and by either printing out each tab as 4 spaces or alternatively by implementing the full tab stop algorithm that apparently clang is doing. I'm generally neutral as to whether implement or , as both seem to fix the original bug to me. I guess once the general infrastructure stands, the algorithms should be equally easy to implement. column info needs to stay to be compatible with external tools like text editors which in turn probably want to be compatible with clang/gcc. E.g. when I do it jumps to column 33 of line 42, treating tabs as one "column point". Or gnome-builder displays errors inline and does something similar: ! This snippet uses tabs for indentation. is the case I've which requires us to walk the same path with `` characters that we walk with tabs, if we preserve them as we do right now. The only way out of this would be to detect the terminal's tab width (e.g. by printing a tab and then detecting how many columns the terminal has advanced), but this obviously doesn't work when you redirect the output to a file and then open it with a text editor or similar.
That sounds good. Yes, it's critical that the column number in the error messages count charaters. IIUC this issue is just that the printed messages don't lign up correctly because it prints some tabs in the source and expects to be able to line up some individual characters next to it. As far as tab-stops vs simple expansion for the error message I don't think it matters too much.
Simple replacement will break formatting for (bad-style) code. Obviously no-one should be using tabs after normal characters but some people will be and they will complain. Better just fix the problem now before it gets filed as an issue.
unfortunately the concept of is emerging.
while you are correct, it isn't relly possible to do that without knowing what that author has configured their tab size to. I guess it would be nice to do the work in tabstops with some value which could be adjusted but it seems like this case will never just work. We could consider making it configurable or looking for an file but this seems like more complexity then is required. All that being said I am clearly biased because I don't do this myself. It would be interesting to see if and how clang and GCC deal with this. I would assume they don't but I haven't tested.
That's fair, I'm convinced. Still, 4-space tabstops will never break anything that simple replacement won't break even worse and it's a pretty safe default to assume.
I think providing configuration options or following an editorconfig or similar are overengineering, but even if we want to add something like that, providing a support for 4-space tabstops is an important first step. I'll check tomorrow whether I want to provide a PR to fix at least this issue with the plan I've proposed above. If anyone of you is very eager to do the implementation themselves, please speak up!
With my PR I've chosen to go with gcc's strategy for now (tab mapped to one space always). Its the easiest to implement, and fixes the actual issue of mispositioning. Obviously its not beautiful, but if anyone wants to improve on that, please do so!
I've filed as a follow-up to not forget about the clang option
There's been a regression on this with help spans. See . The tests , and are still checked in an passing though so the regression is only with those specific help spans.

Discussion:
} } <del> pub fn copy_tabs(&mut self, row: usize) { if row < self.text.len() { for i in row + 1..self.text.len() { for j in 0..self.text[i].len() { if self.text[row].len() > j && self.text[row][j] == 't' && self.text[i][j] == ' ' { self.text[i][j] = 't'; } </del> <ins> fn replace_tabs(&mut self) { for line in self.text.iter_mut() { for c in line.iter_mut() { if *c == 't' { *c = ' '; </ins> } } }

let mut output: Vec<Vec<StyledString>> = vec![]; let mut styled_vec: Vec<StyledString> = vec![]; <del> // before we render, do a little patch-up work to support tabs self.copy_tabs(3); </del> <ins> // before we render, replace tabs with spaces self.replace_tabs(); </ins> for (row, row_style) in self.text.iter().zip(&self.styles) { let mut current_style = Style::NoStyle;

fn main() { bar; } <ins> fn foo() { "bar			boo" } </ins>

error[E0425]: cannot find value `bar` in this scope --> $DIR/tab.rs:14:2 | <del> 14 | tbar; | t^^^ not found in this scope </del> <ins> 14 |  bar; |  ^^^ not found in this scope </ins> <del> error: aborting due to previous error </del> <ins> error[E0308]: mismatched types --> $DIR/tab.rs:18:2 | 17 | fn foo() { |          - help: try adding a return type: `-> &'static str ` 18 |  "bar   boo" |  ^^^^^^^^^^^ expected (), found reference | = note: expected type `()` found type `&'static str` error: aborting due to 2 previous errors </ins>

<ins> // Copyright 2016 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. // ignore-tidy-tab fn main() { """; } </ins>

<ins> error: unterminated double quote string --> $DIR/tab_2.rs:14:7 | 14 |       """; |  _______^ 15 | | } | |__^ error: aborting due to previous error </ins>

<ins> // Copyright 2016 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. // ignore-tidy-tab fn main() { let some_vec = vec!["hi"]; some_vec.into_iter(); { println!("{:?}", some_vec); } } </ins>

<ins> error[E0382]: use of moved value: `some_vec` --> $DIR/tab_3.rs:17:20 | 15 |  some_vec.into_iter(); |  -------- value moved here 16 |  { 17 |   println!("{:?}", some_vec); |                    ^^^^^^^^ value used here after move | = note: move occurs because `some_vec` has type `std::vec::Vec<&str>`, which does not implement the `Copy` trait error: aborting due to previous error </ins>