Skip to content
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

Give all fatals, errors, and warnings unique diagnostic codes #15336

Merged
merged 1 commit into from Jul 11, 2014
Merged

Give all fatals, errors, and warnings unique diagnostic codes #15336

merged 1 commit into from Jul 11, 2014

Conversation

ghost
Copy link

@ghost ghost commented Jul 2, 2014

This is a continuation of @brson's work from #12144.

This implements the minimal scaffolding that allows mapping diagnostic messages to alpha-numeric codes, which could improve the searchability of errors. In addition, there's a new compiler option, --explain {code} which takes an error code and prints out a somewhat detailed explanation of the error. Example:

fn f(x: Option<bool>) {
    match x {
        Some(true) | Some(false) => (),
        None => (),
        Some(true) => ()
    }
}
[~/rust]$ ./build/x86_64-apple-darwin/stage2/bin/rustc ./diagnostics.rs --crate-type dylib
diagnostics.rs:5:3: 5:13 error: unreachable pattern [E0001] (pass `--explain E0001` to see a detailed explanation)
diagnostics.rs:5        Some(true) => ()
                        ^~~~~~~~~~
error: aborting due to previous error
[~/rust]$ ./build/x86_64-apple-darwin/stage2/bin/rustc --explain E0001

    This error suggests that the expression arm corresponding to the noted pattern
    will never be reached as for all possible values of the expression being matched,
    one of the preceeding patterns will match.

    This means that perhaps some of the preceeding patterns are too general, this
    one is too specific or the ordering is incorrect.

I've refrained from migrating many errors to actually use the new macros as it can be done in an incremental fashion but if we're happy with the approach, it'd be good to do all of them sooner rather than later.

Originally, I was going to make libdiagnostics a separate crate but that's posing some interesting challenges with semi-circular dependencies. In particular, librustc would have a plugin-phase dependency on libdiagnostics, which itself depends on librustc. Per my conversation with @alexcrichton, it seems like the snapshotting process would also have to change. So for now the relevant modules from libdiagnostics are included using #[path = ...] mod.

@steveklabnik
Copy link
Member

I'm really excited to see something like this land.

use syntax::parse::token;

local_data_key!(registered_diagnostics: RefCell<HashMap<Name, Option<Name>>>)
local_data_key!(used_diagnostics: RefCell<HashMap<Name, Span>>)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are the alternatives to using local data here? rustc is already filled with global state and it would be better not to make the situation worse.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, I wasn't too happy about this either. One way would be to extend the plugin infrastructure to allow plugins to carry state around. But I'm not sure if that's necessarily better than keeping it in TLS.

Another way would be to have the diagnostic table compile down to a struct definition:

struct X {
    A0001: &'static str
}

which still preserves the static checks but the error messages for say duplicate or unknown diagnostic codes would be pretty terrible.

@brson
Copy link
Contributor

brson commented Jul 3, 2014

This looks pretty awesome, but can you explain the architecture some? In particular I don't understand the role of libdiagnostics as a separate crate. It looks like a plugin, but then rustc includes a number of files from the other crate directly.

@brson
Copy link
Contributor

brson commented Jul 3, 2014

This appears to load a set of diagnostics specifically for rustc::middle. I would prefer to not have fine-grained groups of error codes because I suspect that it will be more-or-less unmaintainable as errors are introduced, removed, and the code is refactored to move errors between subsystems.

@brson
Copy link
Contributor

brson commented Jul 3, 2014

Oh, I see you addressed the libdiagnostics oddities in the OP.

FWIW I also think that doing any built-in functionality as a plugin would be a bootstrapping nightmare.

@brson
Copy link
Contributor

brson commented Jul 3, 2014

OK, after reading over this a number of times, I'm quite happy with it. My qualms are:

  • It adds a libdiagnostics directory then includes parts of it in multiple crates. It's weird. I'd like to hear alternatives.
  • It uses the plugin mechanism statically, unlike anything else. Making things plugins that can be plugins is probably a reasonable technique though, so I think we should keep it as is. @kmcallister, opinions?
  • It uses local data.
  • The fine-grained error groups. I'd prefer one giant table or one each for syntax/rustc.

@brson
Copy link
Contributor

brson commented Jul 3, 2014

Oh, also, by registering three of these macros at rustc startup, AIUI that means they will be exposed to all crates, not just syntax and rustc. We need to avoid that.

@brson
Copy link
Contributor

brson commented Jul 3, 2014

Oh, hm, I guess there are a few ways to look at the organization of error codes. With the organization you've laid out here one can put error code definitions near their use, but that doesn't mean every module's errors need to be namespaced (i.e. even if we put diagnostics defs in many places they can all be called "E####").

Still though, I tend to think putting the defs in one place will make their maintenance easier. I hope this information can be filled in by casual and newbie developers, and having to look all over the compiler for the right place to document an error seems more difficult than opening up the one canonical diagnostics file.

@ghost
Copy link
Author

ghost commented Jul 3, 2014

@brson Thanks for the review!

I agree it's pretty confusing to have libdiagnostics and not have it be a proper crate. I think I could just move all of its contents into libsyntax.

@ghost
Copy link
Author

ghost commented Jul 3, 2014

@brson I suppose the bootstrapping process could stay the same if libdiagnostics was only linked in to librustc at stage1 and higher. At stage0 rustc could always use dummy macros. That would solve both the crate separation and the macro visibility problems.

@ghost
Copy link
Author

ghost commented Jul 5, 2014

@brson Okay, I moved libdiagnostics into libsyntax and also made rustc only have one diagnostic table for the whole crate. The reliance on TLS is still there.

I'm currently looking into if we could still turn it into a dynamically loaded plugin with some changes to the build system so that the macros are not visible to the user but maybe it shouldn't block this PR?

@ghost
Copy link
Author

ghost commented Jul 5, 2014

I suppose a temporary solution to the visibility problem is that the driver could have an "are we compiling rustc" guard for those macros (and no, not anything like Ken Thompson's compiler hack :-)) but that to me is slightly more terrible than maybe just namespacing them.

@brson
Copy link
Contributor

brson commented Jul 8, 2014

@jakub- Thanks and sorry for the delay. If you rebase, and prefix the macros with double-underscore as a temporary 'dont-use-this' indicator, I'll be happy.

@ghost
Copy link
Author

ghost commented Jul 9, 2014

@brson Done!

@alexcrichton
Copy link
Member

Just to be 100% safe, could you put these behind some oddly named feature gate as well?

Other than that, this looks good to me!

@ghost
Copy link
Author

ghost commented Jul 10, 2014

@alexcrichton I would love to! However, feature gates live in rustc and syntax extensions only depend on libsyntax. Should libsyntax (and ParseSess, specifically) also know about feature gates, you think?

@huonw
Copy link
Member

huonw commented Jul 10, 2014

@jakub- you can check in a similar manner to how the asm! macro is feature gated: just check for uses of a macro with the relevant name.

(You could also just conditionally load the macros based on the feature gate.)

@ghost
Copy link
Author

ghost commented Jul 10, 2014

@huonw Right, of course. I ended up following your last suggestion and also added some tests.

@brson
Copy link
Contributor

brson commented Jul 10, 2014

@jakub- If you want to continue this line of development you might consider #8161

bors added a commit that referenced this pull request Jul 10, 2014
This is a continuation of @brson's work from #12144.

This implements the minimal scaffolding that allows mapping diagnostic messages to alpha-numeric codes, which could improve the searchability of errors. In addition, there's a new compiler option, `--explain {code}` which takes an error code and prints out a somewhat detailed explanation of the error. Example:

```rust
fn f(x: Option<bool>) {
	match x {
		Some(true) | Some(false) => (),
		None => (),
		Some(true) => ()
	}
}
```

```shell
[~/rust]$ ./build/x86_64-apple-darwin/stage2/bin/rustc ./diagnostics.rs --crate-type dylib
diagnostics.rs:5:3: 5:13 error: unreachable pattern [E0001] (pass `--explain E0001` to see a detailed explanation)
diagnostics.rs:5 		Some(true) => ()
                 		^~~~~~~~~~
error: aborting due to previous error
[~/rust]$ ./build/x86_64-apple-darwin/stage2/bin/rustc --explain E0001

    This error suggests that the expression arm corresponding to the noted pattern
    will never be reached as for all possible values of the expression being matched,
    one of the preceeding patterns will match.

    This means that perhaps some of the preceeding patterns are too general, this
    one is too specific or the ordering is incorrect.

```

I've refrained from migrating many errors to actually use the new macros as it can be done in an incremental fashion but if we're happy with the approach, it'd be good to do all of them sooner rather than later.

Originally, I was going to make libdiagnostics a separate crate but that's posing some interesting challenges with semi-circular dependencies. In particular, librustc would have a plugin-phase dependency on libdiagnostics, which itself depends on librustc. Per my conversation with @alexcrichton, it seems like the snapshotting process would also have to change. So for now the relevant modules from libdiagnostics are included using `#[path = ...] mod`.
@bors bors closed this Jul 11, 2014
@bors bors merged commit 9b9cce2 into rust-lang:master Jul 11, 2014
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants