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

CommandLineParser #11518

Merged
merged 5 commits into from
Jul 7, 2021
Merged

CommandLineParser #11518

merged 5 commits into from
Jul 7, 2021

Conversation

cameel
Copy link
Member

@cameel cameel commented Jun 10, 2021

Related to #11386. Partial solution for #5303.
Depends on #11571. Merged.

The PR extracts the logic responsible for interpreting and validating command-line options from CommandLineInterface into a new class: CommandLineParser. The new class has clearly separated input and output and therefore can be easily tested.

Putting #11386 on top of this PR will let me actually have the whole path-related behavior covered by some sensible tests rather than just the part that has been extracted into FileReader. Hopefully it will also be a good first step for the full CLI refactor in the future.

To make this easier to review I tried to keep changes to the absolute minimum. I found some things that could be cleaned up or fixed but I'll be submitting these in separate PRs to keep the diff here understandable. I recommend reviewing individual commits and skipping the first one, which only copies CommandLineParser.cpp/.h without any modifications. This way you'll see in the second one that actual changes are pretty small and most of the churn is the removal of fragments that belong in the other class and switching to getting values from the new options struct.

@cameel cameel self-assigned this Jun 10, 2021
@cameel cameel force-pushed the command-line-parser branch from 06297f3 to fcab7e0 Compare June 10, 2021 18:23
@cameel cameel marked this pull request as draft June 14, 2021 13:14
@cameel cameel force-pushed the command-line-parser branch 2 times, most recently from 3f499a6 to 305eed6 Compare June 16, 2021 20:57
bool CommandLineOptions::operator==(CommandLineOptions const& _other) const
{
return
sourceFilePaths == _other.sourceFilePaths &&
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we really need this equality operator? Isn't it very dangerous in case we forget to add some new setting here?

Copy link
Member Author

Choose a reason for hiding this comment

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

The alternative is comparing individual properties one by one, with the same risk.

I thought about adding static_assert for the size of the struct like I did in operators for the structs containing booleans but the assert was almost as big as the comparison itself so I thought it was too much. It would decrease the risk a lot though.

Copy link
Contributor

Choose a reason for hiding this comment

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

Possible using something like here could help?

https://stackoverflow.com/questions/17660095/iterating-over-a-struct-in-c

Copy link
Member Author

Choose a reason for hiding this comment

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

I see that solutions boil down to writing a bunch of custom macros or using Boost.Fusion (which is also a bunch of macros and templates :)). I'd be fine with Boost.Fusion - looks like it would solve the problem of operators potentially going out of sync and usage is not complicated. Still, it adds a bunch of weird macro calls and I'd still have to repeat the list of members once (in macro invocation). But that's not a bad trade-off in my opinion. @chriseth What's your opinion on using Boost.Fusion?

Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure about that, especially since we already have the code here. I also heard that fusion is a beast. About going out of sync: Would an assert using sizeof not be enough?

Copy link
Member Author

Choose a reason for hiding this comment

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

I think the assert is good enough but this particular function does not have one. I'd have to list all field types from CommandLineOptions in it and there's a lot of them. I considered adding it but in the end I thought that an assert the size of the whole function might be an overkill :)

Also, C++20 will provide default equality operators which would be a much cleaner solution.

CommandLineOptions expectedOptions;
expectedOptions.sourceFilePaths = {"contract.sol"};
expectedOptions.coloredOutput = true;
expectedOptions.expectedExecutionsPerDeployment = 200;
Copy link
Contributor

Choose a reason for hiding this comment

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

This is a magic number that should appear exactly once in the whole project source.

Copy link
Member Author

Choose a reason for hiding this comment

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

It does appear once in non-test code and I think that's the part that matters. But I'll make it a constant anyway. Less duplication is always good.

Copy link
Contributor

Choose a reason for hiding this comment

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

I mean the question is what do we want to test? Do we want to test that it is the number 200 or do we want to test that the parser sets it to OptimiserSettings{}.expectedExecutionsPerDeployment?

Copy link
Member Author

@cameel cameel Jun 17, 2021

Choose a reason for hiding this comment

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

I'm often OK with hard-coding some things in tests because these values get cross-checked so you have to make a mistake in both places to miss the problem. Deduplicating stuff too much sometimes makes it easy for the wrong value to be carried over to the check in the test so I often prefer to see the "raw" values in tests.

That's my general approach but I get your point about this constant. Expecting a particular raw value is not the point of this test so we would indeed be better off with a constant. The problem is - this value is hard-coded all over the code base. I think this PR is not the best place to fix that. I have created a separate one: #11550.

@cameel cameel force-pushed the command-line-parser branch from 305eed6 to e070441 Compare June 17, 2021 14:03
@cameel
Copy link
Member Author

cameel commented Jun 17, 2021

I just pushed an updated version. It only changes the handling of the coloredOutput option to make it independent of the TTY. Checking the TTY is now done after option parsing, in CommandLineInterface. This should fix the test failures on Windows.

Once I see that it works, I'll remove the debug commits with stream operators and mark the PR as reviewable.

@cameel cameel mentioned this pull request Jun 17, 2021
@cameel cameel force-pushed the command-line-parser branch from e070441 to 2cf6e62 Compare June 17, 2021 14:51
@cameel
Copy link
Member Author

cameel commented Jun 17, 2021

Still failing. This time because I missed one use of isatty() (for help text). This is a bit annoying because these things only fail on
Windows (probably becase on Linux isatty() already gets included through other headers and on Windows we need to define it).

I just pushed a fix. I changed CommandLineParser::parse() to take an argument saying whether we have an interactive terminal.

@cameel cameel force-pushed the command-line-parser branch from 2cf6e62 to 924430b Compare June 17, 2021 15:40
@cameel
Copy link
Member Author

cameel commented Jun 17, 2021

All tests passed so I'm done here. Marking as reviewable.

@cameel cameel marked this pull request as ready for review June 17, 2021 15:41
@cameel cameel mentioned this pull request Jun 21, 2021
6 tasks
libsolidity/formal/ModelCheckerSettings.h Outdated Show resolved Hide resolved
solc/CommandLineInterface.cpp Outdated Show resolved Hide resolved
solc/CommandLineInterface.cpp Outdated Show resolved Hide resolved
solc/CommandLineInterface.cpp Outdated Show resolved Hide resolved
solc/CommandLineInterface.cpp Outdated Show resolved Hide resolved
catch (FileNotFound const&)
{
// Should not happen if `fs::is_regular_file` is correct.
}
Copy link
Member

Choose a reason for hiding this comment

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

For the filesystem_error case we could generate a human readable message so the user knows why it failed. If the catch below should not happen, we could make it solAssert(false, "reason here");?

Copy link
Member Author

Choose a reason for hiding this comment

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

Agreed. Again, something I did not want to touch yet in this PR.

vector<string> libraries;
boost::split(libraries, data, boost::is_space() || boost::is_any_of(","), boost::token_compress_on);
for (string const& lib: libraries)
if (!lib.empty())
Copy link
Member

Choose a reason for hiding this comment

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

Huge nested if. What about:

for (string const& lib: libraries | ranges::views::filter([](auto const& lib) { return lib.empty(); })
    // .... (now without the huge if-body)...

Copy link
Member Author

Choose a reason for hiding this comment

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

Agreed. Again, something I did not want to touch yet in this PR.

solc/CommandLineParser.cpp Outdated Show resolved Hide resolved
solc/CommandLineParser.h Outdated Show resolved Hide resolved
test/CMakeLists.txt Outdated Show resolved Hide resolved
@cameel cameel force-pushed the command-line-parser branch from 924430b to dab3783 Compare June 21, 2021 12:09
@cameel
Copy link
Member Author

cameel commented Jun 21, 2021

Fixed about half of the small suggestions. The other half is fine but I'd like to leave them out of this PR. This one was meant to be mostly a split and not a complete cleanup of CommandLineInterface. There are too many small things to fix and I wanted to keep the PR manageable.

There are two changes I have not done yet: the CMakeLists.txt suggestion (#11518 (comment)) and checking compilation result (#11518 (comment)).

Copy link
Contributor

@Marenz Marenz left a comment

Choose a reason for hiding this comment

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

Looks good overall, found a few minor things.
Didn't check the fixup! ones yet.

solc/CommandLineParser.h Outdated Show resolved Hide resolved
solc/CommandLineParser.h Outdated Show resolved Hide resolved
static_assert(
sizeof(*this) == 15 * sizeof(bool),
"Remember to update code below if you add/remove fields."
);
Copy link
Contributor

Choose a reason for hiding this comment

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

nice.

bool CommandLineOptions::operator==(CommandLineOptions const& _other) const
{
return
sourceFilePaths == _other.sourceFilePaths &&
Copy link
Contributor

Choose a reason for hiding this comment

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

Possible using something like here could help?

https://stackoverflow.com/questions/17660095/iterating-over-a-struct-in-c

test/solc/CommandLineParser.cpp Outdated Show resolved Hide resolved
test/solc/CommandLineParser.cpp Outdated Show resolved Hide resolved
@cameel cameel force-pushed the command-line-parser branch from dab3783 to 2aa689f Compare June 22, 2021 15:53
@cameel cameel changed the base branch from develop to libsolcli-and-libphaser June 22, 2021 15:55
@cameel cameel force-pushed the command-line-parser branch 2 times, most recently from 82198a0 to 7200fda Compare July 2, 2021 19:58
Copy link
Member

@christianparpart christianparpart left a comment

Choose a reason for hiding this comment

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

@cameel LGTM plus the rest where you said it's for future PRs, which I agree with (to get that one through) :).

@christianparpart
Copy link
Member

@cameel well, a squash would make sense, I guess :)

@cameel cameel force-pushed the command-line-parser branch from 7200fda to 50e1b7a Compare July 5, 2021 12:07
@cameel
Copy link
Member Author

cameel commented Jul 5, 2021

Fixups squashed.

Marenz
Marenz previously approved these changes Jul 6, 2021
@cameel
Copy link
Member Author

cameel commented Jul 6, 2021

Just pushed the fix for the test -> text typo and squashed a fixup. No other changes.

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.

4 participants