-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Guess diagnostics #1941
Guess diagnostics #1941
Conversation
graphviz dot code: ```dot digraph G { A [ label = "Does the message contain\ncode snippets derived\nfrom real code?"] A -> B [ label = "Yes"] A -> C [ label = "No"] B [ label = "Are there multiple\nsnippets that may apply?"] B -> "Guess" [ label = "Yes" ] B -> D [ label = "No" ] D [ label = "Are there situations\nwhere the snippet is plain wrong?"] D -> E [ label = "Yes" ] D -> "Suggestion" [ label = "No" ] E [ label = "Can you detect those\nsituations programmatically?"] E -> G [ label = "Yes" ] E -> "Guess" [ label = "No" ] G [ label = "Suggestion where correct,\nGuess or no code hint otherwise"] C [ label = "Is the message reasonably short?"] C -> "Note" [ label = "Yes" ] C -> "Help" [ label = "No" ] } ```
cc @killercup |
text/0000-guesses.md
Outdated
arbitrary limits on the number of displayed items is removed. This limit | ||
is instead enforced by the command line diagnostic backend. | ||
|
||
## Json diagnostics backend API changes |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/Json/JSON
text/0000-guesses.md
Outdated
|
||
## Json diagnostics backend API changes | ||
|
||
The json object gets a new field `guesses` which is an array of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/json/JSON
= help: `std::collections::btree_map::Iter` | ||
= help: `std::collections::btree_set::Iter` | ||
= help: `std::collections::hash_map::Iter` | ||
= help: and 8 other candidates |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's up with all these lines starting with "help:" anyway? Probably not part of this RFC but this does not look helpful to me. Starting the first line with a (highlighted) "help" should suffice.
|
||
The following chart should clarify the decision process for subdiagnostics | ||
|
||
![chart](https://cloud.githubusercontent.com/assets/332036/23554529/be228b76-0025-11e7-99e7-627d60f5a328.png) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice! For sake of keeping this self contained you should either reproduce the state machine in prose, or include the chart as ASCII, though. I spent a few minutes to do the latter with asciiflow.com, feel free to copy this:
+--------------------------+
| Does the message contain |
+--------+ snippets derived +--------------+
| | from real code? | |
Yes | +--------------------------+ | No
| |
+-----------v--------------+ +--------v----------+
+---+ Are there multiple +---+ | Is the message |
| | snippets that may apply? | | | reasonably short? |
| +--------------------------+ | +---+-----------+---+
Yes | | No | |
| | Yes | | No
| +-------------------v---------+ | |
| | Are there situations where | +---v--+ +--v---+
| | the snippet is plain wrong? | | Note | | Help |
| +---+-----------------------+-+ +------+ +------+
| | |
| Yes | | No
| | |
| +-------------v----------------+ +-v----------+
| | Can you detect those | | Suggestion |
| | situations programmatically? | +------------+
| +------+-----------------+-----+
| | |
| | No Yes |
| | |
+--v----+ | +--------v------------------------+
| Guess <------+ | Suggestion where correct, |
+-------+ | Guess or no code hint otherwise |
+---------------------------------+
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The dot code is in the commit message: aed4d38
I'll always push the dot code together with changes in the image.
You can use an online tool like http://sandbox.kidstrythisathome.com/erdos/ to generate new images and edit the graph.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, nice, I hadn't seen that. Maybe put it in an addendum section?
text/0000-guesses.md
Outdated
`Guess` objects. Every `Guess` object contains an array of span + | ||
snippet pairs stating which part of the code should be replaced | ||
with what replacment. There is no need for all guesses to replace | ||
the same piece of code or even require the same number of replacements. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was the JSON structure ever defined in an RFC? All I could find was #1855 (currently open) which has some links. If not, it would be great to define (or at least note) the schema of the JSON objects here, either in Rust syntax or in JSON Schema syntax.
The current iteration of rustfix uses the compiler's JSON output. The Language Server spec may contain its own definition of how to represent these annotations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be better for rustfix to directly use the Diagnostics API like RLS wants to do
Quoting myself (also in the RFC, thanks!):
To clarify slightly - I don't think that the compiler can ever be 100% confident that a change is correct. Therefore, you always need user input to make changes. Given that, it doesn't seem useful to me to be able to express how confident the compiler is in making changes, since it won't affect the UI actually presented to the user. To expand on why I think there is a cost to adding another category of error suggestion:
|
I can't argue point 1 as it is entirely subjective and I've been writing Lint's for too long to be able to judge this even remotely objectively. Point 2: I don't see how this can be exposed to users, since it's only possible to be part of an existing error or warning message. Could you elaborate on this? Edit: I'd have no problem with changing the suggested back end api to only have a book flag to state the confidence level, and then adding a lint that forbids using that flag in the command line diagnostic output Making the lint categories automatically checkable will make them correctly used instead of having to rely on reviews. Also I trust rustfmt not to break my code, if it does it's a bug as I understand it. Why can't we treat suggestions the same? |
We currently expose "help" vs "note" to users, there is nothing to stop us exposing "guess" at some point (this is basically a 'slippery slope' argument). More realistically, an IDE or tool exposes this to users, rather than rustc doing so.
a lint cannot tell the difference between "make this variable a reference" and "make x into &x"
This is not good enough, a change could compile, but still be incorrect. For example (and I realise this would be a guess, not a suggestion or whatever, but the point stands for all suggestions and this is an easy example): error is "x does not need to be mutable" suggestion is to turn
This is impossible, how can a lint know if you should be confident enough to make a change without a user opting in (Or opting in in bulk, etc.) vs a mere guess at correctness?
Rustfmt does not change code, it only changes whitespace (well, pretty much, there are some other simple transforms, all provably semantics-preserving). The code before and after rustfmt'ing must always be semantically equivalent (and trivially so). The changes that the compiler and Clippy suggest are not trivially equivalent, by a long shot. |
A lint can tell whether there's a dataflow from a call to
This is exactly why I opened this RFC. In clippy we have many suggestions which we consider suboptimal, due to the situation you described. But we also have suggestions which are so very uncontroversial, that we want a way to mark them as automatically applicable.
The confidence would come from testing the lints through cargobomb. So developers would choose categories through best-effort (or just use guesses and let cargobomb detect that the guess should be a suggestion).
While clippy has quite a few nontrivial changes, I think the list below shows that clippy has a lot of trivial changes which are often not more complex than rustfmt's Note: Please don't argue the idiomaticality of lint suggestions in this PR, just whether the transformations are trivial (or incorrect) The lint list of trivial clippy transformations:
The lint list of nontrivial clippy transformations:
The lint list of possibly wrong clippy transformations:
The lint list of lints that detect bugs and have a suggestion
|
An easy way to avoid this is to have an extra boolean or enum argument on |
Sorry, could you explain a bit more how this helps please?
This relies on the fact that the project has enough test coverage to detect errors, which seems optimistic to rely on. This also seems like a big project to implement.
So I won't argue idiomaticality of individual lints, but suffice it to say there are more than a few on the 'trivial' list that I don't agree with in all situations and would be uncomfortable to blanket apply to my code. I think about half of those lints are suitable for automatic application (with others being almost certainly correct, but perhaps justified by the context, or IMO not that trivial). In any case, I think that there is enough room for doubt in automatic application that whether these things should be automatic or opt-in (i.e., suggestions or guesses) should be debated, and that is what leads to grey areas and messy boundaries and ultimately more complexity. I will say, that when considering the Clippy lints in detail, I see the motivation for this much more clearly than when considering mainly the compiler's suggestions (which, under my current thinking should basically all be guesses). I suppose that a Clippy-specific solution is not possible because of its tight integration with the compiler? Perhaps a confidence flag on errors that only Clippy uses? |
I don't think this guarantees avoiding the situation, but I agree it does make it less likely |
What about lints like unused imports in the compiler? Yes, that often means that someone has forgotten something, but I think a difference between rustfmt and rustfix is that you generally just run-and-forget rustfmt whereas rustfix is meant to be reviewed a bit more carefully. Not too much more, but a bit more. I'm fine with a confidence flag on errors only used by clippy, though the json needs to support it too. |
ISTM that being confident enough that a user should not opt-in to a change, but not so confident that they have to review the diff afterwards is a bad place to be, UX-wise. Unused imports seems like exactly the kind of lint where you should opt-in because it can be solved two different ways. |
My idea is that we don't have any code snippets whatsoever in let x = snippet(e.span);
sess.span_help(e.span, &format!("look at this {}, it's the reason", x)); By tracing the dataflow in the MIR of this piece of code, we can automatically detect this kind of code and forbid it inside rustc. We already have some lints in clippy than enforce our own invariants. These lints make little sense outside clippy. We also have lints helping with the API of serde. Such specialized lints can help us enforce the correct usage of code hints vs help/note.
True, but I believe it's ok for some bugs to slip through.
Not really, all the heavy duty work has been done. I'll just insert an intermediate call to (a future version of) rustfix (which knows about suggestions and guesses) and recompile with the same compiler instead of a different one like cargobomb does now.
I don't see how this is different from my proposal. If you don't want suggestions in rustc, but only guesses, it would be easy enough to simply not expose In fact, adding another field to In case you are talking about the backend API, I'd be happy to change it to just be a boolean flag instead of an enum, but I'd still want to make that explicit
I agree with you there. And I have a solution that I'm absolutely curious about what others think of it:
|
To me using deny normally feels like "compiler, don't let me get away with being lazy about this", i.e. it's for things I need to do, like deny(missing_docs). Using it to inform automatic code changes sounds like "compiler, please fix this for me whenever it comes up here (but only in this specific part of my code for some reason)" which just seems weird to me. I can't think of an example of an automatic code transformation rustc/clippy could apply which I'd want only in some parts of my code, much less exactly the parts where I'd deny() the corresponding warning. |
True... Ignore what I said then... Then the only nice solution left is a
The idea is that you'd deny them for your crate. It could be taken even further to only do the automatic fixing if the lint was denied for the entire crate. An example would be |
On some classes of errors, Clang provides so-called "Fix-It" hints (search for the term here) that tell you exactly what needs to be changed to fix the error. And not only do they tell you, they're also understood by IDE tools like vim's YouCompleteMe plugin. In vim I can hit a keybind over an erroneous line of C++ with a fix-it error and have it immediately, automatically fixed. I'd like it if rustc itself had a notion of fix-it errors. They would be especially easy to represent in the JSON error output for tools, and we can look at Clang for ideas on how to render them for humans. Then, to achieve the aims of this RFC, we could have clippy optionally emit fix-its in the exact same format, so IDEs could apply them the same way they apply rustc fix-its. A |
Note also that I have been using clang-tidy for over a year regularly and they should rename the And I love clang-tidy, is like clippy but for C++, it is really good, but fixing C++ code automatically seems very hard. IMO a EDIT: Fun fact: I also remember one case in which clang-tidy never terminates. It has two lints, one that tells you that an argument is going out of scope, and that if you pass it to a function you should consume it using |
It is. Rust is much easier IMO. We could eliminate this entire rfc by simply adding a whitelist to rustfix. All Lint's in the list get automatically applied. This moves the burden to rustfix instead of the lint producer. The whitelist could be modified by a rustfix.toml. @killercup opinions on such a whitelist? |
Assuming you mean the "rustfix" CLI tool, sure. But "rustfix" as the general idea of automatically fixing Rust code? That would mean every editor plugin that uses rustc's suggestion output would need to have such a list. A list, I might add, that is highly specific to rustc and clippy versions. |
Such a list will be necessary anyway, as there are obvious preferences as to what should be automatically fixed and what not. Similar to what should be linted being decided by allow and warn attributes. So how about a fix attribute for suggestions and guesses, where suggestions are fix by default and guesses are not. Then you can opt into new fixes like opting into new lints |
This transformation is not semantics preserving. The semantics-preserving version is:
|
It's "preserving the semantics you actually meant in the first place", partialeq with NaN is completely useless. |
Could Clippy talk directly to Rustfix? At this point it seems like this facility is purely for Clippy to communicate to rustfix, so it seems sub-optimal to have to alter the compiler. |
I'm really unconvinced that the compiler will never make use of this. In general I feel like builtin APIs are the way to go for something you eventually want IDEs to use. Also, most of clippy will live in rust-lang/rust soon anyway, so that distinction would be blurred anyway. I think it's suboptimal to basically duplicate an API (and have to figure out a way to thread args down to a sideloaded plugin). The compiler will be talking to rustfix too -- regardless of whether or not the compiler will be making certain-suggestions (as opposed to "guesses"). Rustfix already has an interactive mode where it takes all compiler suggestions and asks you individually if you want to apply them. So regardless of the situation on guesses, the compiler will be talking to rustfix via this API. Clippy uses the same API for diagnostics already, and thus talks to rustfix the same way. This is already the case, regardless of future situations of a "guess vs certain" API. The only difference is that clippy wants to pass an extra bit of information when communicating to rustfix (a bit of information which I feel the compiler would find useful too, but I digress). I think it's overkill to open a separate side channel to transmit this single bit of information. |
I'm playing around with the rust compiler these days. I'm trying to improve E0046, printing out copy-pastable ready-to-use trait method implementations (with I was thinking of a method to show such type of suggestions and then I see this RFC, just in time. |
@eulerdisk for now you can just use |
I don't think we're planning on doing this. AFAIK, the plan is that rustup will support pulling in Clippy to give it the privs it needs to work with the compiler even on stable. It will still live in its own repo and interact with the compiler via plugin mechanisms. The current effort is to move things out of the Rust repo (e.g., liblog, rustdoc), not move more things in. Anyway, I think you misunderstood my (admittedly over-brief) question - I'm not suggesting a side-channel as such (although I guess that might work), I'm wondering if there is a more general/scalable solution than adding specific complexity to a compiler-internal API to support the needs of a plugin and an external tool. I would prefer, for example, a generic way for plugins to add information to JSON error messages (and perhaps human-readable error messages too), which in Clippy's case could be a |
We could simply add a hashmap to the back end api. But I don't see the benefit. Clippy is so closely tied to the compiler, and any tool using the diagnostic api will be, too, that you either need to start creating a stable api or do it as it's done now and modify the api to fit the use cases. In the case of this PR, the single bool change to the back end api is so negligible, I fail to see how adding it can make anyone's life harder. We can even drop the frontend api suggested and implement it in clippy. The only change to rustc would be in the json back end and in an "unrelated" change allowing multiple suggestions per diagnostic. |
@nrc FWIW what I understood is we'd ship |
That was the original plan, and I personally am okay with it. However, acrichto wants there to be per-push blocking CI, without a complicated sync model (a very sensible ask IMO), which means that |
I am not sure how to proceed on this RFC. I could add a generic way to add data to diagnostics, but would prefer to do that once (if ever) there are more than one uses of such an API. In my book an underused API is worse than a adding a specific just-for-a-single-use-case field to an API that will never be stable to best of my knowledge. |
Implementation wise, how hard would it be to give it a try?
I mean, if the author of the RFC thinks it's important for rustfix and
wants to implement it, I wouldn't find it bad for this to be implemented on
nightly, try it with rustfix and the language server, and if it turns out
to not be that's great of an idea, remove it.
Or what's the worst that could happen?
…On Sat, 11 Mar 2017 at 13:51, Oliver Schneider ***@***.***> wrote:
I am not sure how to proceed on this RFC.
I could add a generic way to add data to diagnostics, but would prefer to
do that once (if ever) there are more than one uses of such an API. In my
book an underused API is worse than a adding a specific
just-for-a-single-use-case field to an API that will never be stable to
best of my knowledge.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#1941 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AA3NpkI90ztMKWaPmQKhWZa8tM5a5ps2ks5rkpjBgaJpZM4MSVU6>
.
|
There has been no activity on this RFC for two weeks. The current state is that there are four ways to continue:
My opinions and comments to these four ways
|
I think the issue is that once we start adding tool specific stuff to internal APIs, it becomes "easier" to add more and more in the future and then the API becomes big and messy. |
nominating: I suspect this is best addressed by the tools team (not the compiler one), but in any case, there's been no activity (beyond that of the RFC author) for over two weeks, so it would be good to get a shepherd assigned (assuming that's what the tools team does...) |
This has been dormant for a month. Can someone ping the tools team as suggested? |
cc @rust-lang/tools |
@oli-obk I think the problem is that @nrc is on parental leave :-) FWIW, the high-level idea here sounds great to me, and I agree with @Manishearth that this facility seems highly likely to be eventually used by the compiler, not just Clippy. Adding the feature will likely cause us to start finding opportunities. I also agree with @oli-obk that jumping straight to a generic plugin system seems like premature abstraction. What's the stabilization path here, and how difficult is the initial migration? Could we land this on a more or less experimental basis? |
Since plugins are unstable, there's no stabilization path. We can even leave it out of the json for now and require rustfix to hook into the diagnostics api directly. I can create a PR with one of the impls. I don't want to go around @nrc 's back and move this along with one of my fancier designs. I suggest we add the bool flag, since that has the most minimal impact, and can be converted into a more general system later easily |
I think we have to worry about the stabilization of the JSON format. But that can be appended to with minimal issues I guess. |
The tools team discussed this in our meeting today. The general sentiment was that the motivation here should be addressed in as specific and minimally disruptive way as possible. We would like to propose trying the bool flag and seeing how that works. In order to avoid the flag riding the trains to stability, by default, the compiler should not include the flag in the JSON output. A compiler plugin or tool (such as Clippy) should be able to opt-in to the flag existing either at compile time or run time, whichever is more convenient to implement. When generating suggestions (or other diagnostic) it should be optional whether the flag is provided or not. Thus, we see the following situation for testing things out: Clippy turns the flag on and Clippy lints set the confidence levels. Rustfix can read this flag to do automatic corrections. Under normal usage, the compiler does not issue the flag. Compiler errors mostly do not set the flag, but can incrementally do so where useful. We'll evaluate how useful this is along the usual path to stability. If this sounds OK to the stakeholders in this thread, then we don't think it needs a new RFC and can just be implemented. Assuming the above is OK, then this RFC should enter FCP with intent to close (@rfcbot FCP close). |
I'm all on board with this, but have no idea what you mean by opt-in. It can't be a cfg since that would require rebuilding rustc. Would it be allright if we didn't show the flag in the diagnostics api at all, but instead just added it to the structures in the background? Clippy could then experiment with the flag without inconveniencing every span_suggestion call in rustc |
I meant that there should be some bool somewhere like
This seems fine too. |
rustfix
should be the semantic companion torustfmt
(which is purely syntactical) and automatically change code to be more idiomatic.To do this, we need to distinguish between automatically applicable code replacements (suggestions) and heuristics (guesses)
Rendered
cc @nrc, @Manishearth, @mcarton, @llogiq, @petrochenkov
some previous discussion at rust-lang/rust#39458