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

Intercept Command Line Errors from Clap #429

Open
InsertCreativityHere opened this issue Feb 2, 2023 · 5 comments
Open

Intercept Command Line Errors from Clap #429

InsertCreativityHere opened this issue Feb 2, 2023 · 5 comments
Labels
enhancement New feature or request
Milestone

Comments

@InsertCreativityHere
Copy link
Member

InsertCreativityHere commented Feb 2, 2023

Clap is the library that we use for generating and displaying a command line interface to the Slice compilers.
Currently, we interact with it with a parse method, which attempts to parse a user's command line input.
If the user typed everything correctly, it hands us the data pre-parsed in a struct.
If there was an error in the user's input, it displays errors in its own formatting then terminates the program.

I propose we switch to the try_parse method, which instead returns a Result<PreParsedData, Errors>.
This way, if an error happens, instead of emitting them and dying, it returns them to slicec where we can be in control of how they're emitted.

This has the benefit of letting us emit these errors in our own formatting, ensure users, build tools, and our future language server always receive consistently formatted error messages.
It also centralizes where errors can be emitted from; with this change there would only be place (we know of) where errors are stored and emitted: the DiagnosticReporter. Instead of having to think about multiple places where failures can be reported from.

@InsertCreativityHere InsertCreativityHere changed the title Intercept Command Intercept Command Line Errors from Clap Feb 2, 2023
@externl
Copy link
Member

externl commented Feb 3, 2023

Note that we should not follow it exactly but rusts behavior is this:

eg: Using --error-format json with an unknown argument

❯ rustc --error-format json --foobar
error: Unrecognized option: 'foobar'

eg: Using --error-format json with a known argument bug bad value

❯ rustc --error-format json
{"message":"no input filename given","code":null,"level":"error","spans":[],"children":[],"rendered":"error: no input filename given\n\n"}
❯ rustc --error-format json  --crate-type foo
{"message":"unknown crate type: `foo`","code":null,"level":"error","spans":[],"children":[],"rendered":"error: unknown crate type: `foo`\n\n"}
❯ rustc --error-format json  --crate-type
error: Argument to option 'crate-type' missing

So rustc is unsurprisingly not consistent here. I can think of a few options:

  1. The issue with using try_parse is that we still don't have access to the parsed args, most importantly diagnostic-format. We could drop clap_derive and use plain clap. This will give us more flexibility for parsing and validation of the command line arguments but is more work.

  2. Since the thing we care about the most at the start is the diagnostic format, we could perform a first pass check for the diagnostic format and save it (We can have a special struct just for this). Then parse the rest of the arguments. If there's a failure we can construct a diagnostic reporter with the correct format and issue the rest of the errors from clap.

  3. Another option is to do what we do now, and perform validation of command line arguments later parsing them. It's good enough for rust.

Option 2 would allow use to move validation of command line arguments back into pest_derive and allow us to have the correct output format. A helper function in slicec could assist the language mappings with this.

@ReeceHumphreys
Copy link
Contributor

I think option 2 is the right call, it will give us a bit more flexibility with any validation we want to do just for command line args. So we do the pass with parse to get the format, then do the second pass with try_parse so that we can handle any errors however we want.

@InsertCreativityHere
Copy link
Member Author

After much searching, there is a macro argument you can pass to clap that causes it to ignore errors:

#[command(rename_all = "kebab-case", ignore_errors = true)]
pub struct SliceOptions {

They make it very difficult to find though.

We could have 2 structs, one for the 'full parse', and another with only the critical fields in it for 'pre-parsing'.
Then we annotate the pre-parse version with ignore_errors, to make a best-effort attempt to determine the diagnostic output.
Regardless of whether the pre-parse version worked or not, we'd run the 'full parse' afterwards.

And both times we'd use try_parse instead of the parse that we currently use to avoid panicing on an error, letting us stay in control of the error handling logic.

@InsertCreativityHere InsertCreativityHere added this to the 0.1 milestone Apr 19, 2023
@externl externl modified the milestones: 0.1, 0.2 Apr 19, 2023
@InsertCreativityHere
Copy link
Member Author

As of #526, the only option that can fail to parse via clap is diagnostic-format.
We can still get general errors from users typing malformed or non-existent options though.

@InsertCreativityHere
Copy link
Member Author

After #619, arguments to --allow can also fail to parse with Clap now.

@InsertCreativityHere InsertCreativityHere modified the milestones: 0.2, Future Sep 19, 2023
@externl externl removed their assignment Feb 28, 2024
@InsertCreativityHere InsertCreativityHere added the enhancement New feature or request label Aug 5, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants