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

ament_uncrustify and ament_clang_format cannot both be satisfied #146

Closed
rotu opened this issue Jun 28, 2019 · 26 comments
Closed

ament_uncrustify and ament_clang_format cannot both be satisfied #146

rotu opened this issue Jun 28, 2019 · 26 comments
Labels

Comments

@rotu
Copy link
Contributor

rotu commented Jun 28, 2019

ament_uncrustify and ament_clang_format have defaults which differ in a way that you can't satisfy both at the same time. What's worse, ament_uncrustify --reformat will often make changes that cause ament_clang_format to complain and vice versa.

One of these tools should be deprecated or they should enforce compatible code formatting styles.

Formatted with ament_uncrustify --format:

#pragma once
#include "rclcpp/rclcpp.hpp"
namespace openrover
{
template<typename T>
struct Timestamped
{
  Timestamped(const rclcpp::Time & time, T state)
  : time(time), state(state) {}

  using State = T;
  rclcpp::Time time;
  T state;
};
}

formatted with ament_clang_format --reformat:

#pragma once
#include "rclcpp/rclcpp.hpp"
namespace openrover {
template <typename T>
struct Timestamped {
  Timestamped(const rclcpp::Time& time, T state) : time(time), state(state) {}

  using State = T;
  rclcpp::Time time;
  T state;
};
}  // namespace openrover
@rotu rotu changed the title ament_uncrustify and ament_clang_format defaults disagree ament_uncrustify and ament_clang_format cannot both be satisfied Jun 29, 2019
@rotu
Copy link
Contributor Author

rotu commented Jul 3, 2019

Created #148 and #147
to bring these tools' defaults in line with the ROS2 developer guidelines.

@rotu
Copy link
Contributor Author

rotu commented Sep 6, 2019

@hidmic said (somewhere else, but I think this convo belongs here):

Alright, I gave this a shot. Some conclusions and a request for feedback follow.

Reconciling clang-format with the code style that uncrustify currently enforces throughout our code base has not been possible. It's simply not as flexible and configurable as uncrustify is (e.g. uncrustify allows one to ignore details altogether whereas clang-format usually does not). Thus, it became a matter of getting ament_clang_format's default configuration as close as possible to what we have right now while not violating the GSG nor the ROS2 C++ style guide, and hopefully adapt uncrustify's right after.

I think I've managed to do the former. Here you can find the modified .clang-format configuration file and here is rclcpp re-formatted. I'm still far from a fully compatible uncrustify configuration but it'd be good for someone else to take a rough look at the results before investing more time.

There are places where ament_uncrustify fails to comply with the written guide or goes beyond it. In general, I think uncrustify's behavior is more wrong and we shouldn't repeat arbitrary style mistakes because ament_uncrustify or rclcpp made them first (e.g. I hate reading this code: https://github.com/ros2/rclcpp/blob/a54a32915313960497bda2d601cdf98e9f2e9bf0/rclcpp/include/rclcpp/graph_listener.hpp).

I thing things like AlwaysBreakTemplateDeclarations: Yes, SplitEmptyFunction: true, SplitEmptyRecord: true, KeepEmptyLinesAtTheStartOfBlocks: true are examples of things rclcpp got wrong and violations of Google's (hence ROS2's) style guide.

@hidmic
Copy link
Contributor

hidmic commented Sep 6, 2019

@rotu thanks for reproducing my comment here!

I agree with you in that ament_uncrustify goes beyond the style guide, but from what I've seen it does not violate it. It may fail to enforce it or apply arbitrary formatting whenever the style guide has a gap, but that's ultimately a deficiency of the style guide, not the linter IMO.

Regarding the clang-format settings you mention, both SplitEmptyFunction and SplitEmptyRecord are set to true to comply with ROS2's exception on brace locations for functions and classes, KeepEmptyLinesAtTheStartOfBlocks is just there to prevent clang-format from removing blank between namespace openings and class definitions, and AlwaysBreakTemplateDeclarations effects fall in the undefined category IIRC so I set it so as to remain close to what's there already.

In any case, I personally think we should aim for readability rather than blind compliance with some style guide, which we can always change or ammend.

@rotu
Copy link
Contributor Author

rotu commented Sep 6, 2019

Here's at least one case where it enforces bad style: #173

I don't think I'm going to agree with you on most of those rules. The Google style guide is clear that vertical whitespace does not help in the beginning of a function. I assume the same applies to other blocks, and all those rules are examples of giving up vertical whitespace for negligible gain.

In any case, I personally think we should aim for readability rather than blind compliance with some style guide, which we can always change or ammend.

👍

@dirk-thomas
Copy link
Contributor

dirk-thomas commented Sep 6, 2019

I think uncrustify's behavior is more wrong and we shouldn't repeat arbitrary style mistakes because ament_uncrustify or rclcpp made them first

e.g. I hate reading this code: https://github.com/ros2/rclcpp/blob/a54a32915313960497bda2d601cdf98e9f2e9bf0/rclcpp/include/rclcpp/graph_listener.hpp).

@rotu An unspecific statement like this isn't helping in this discussion. Please bring up specific cases (not pointing to a whole file and stating you hate it) if you want the feedback to be actionable.

Here's at least one case where it enforces bad style: #173

@rotu The specific case is a bug in uncrustify - potentially being triggered by our current set of configuration options. The style does certainly not suggest to format the code like this. In other code parts where this is the case we even opted to disable uncrustify for these lines to keep the indentation as desired without a linter warning: https://github.com/ros2/rclcpp/blob/458967bb568df93d31dbff0e10d198b4a065db70/rclcpp/include/rclcpp/service.hpp#L168-L171

@rotu
Copy link
Contributor Author

rotu commented Sep 6, 2019

An unspecific statement like this isn't helping in this discussion.

Thank you for calling me out on that. I specifically meant having the function specifier and return type on different lines from the function name, which is done throughout that source file:

  RCLCPP_PUBLIC
  virtual
  void
  start_if_not_started();

The specific case is a bug in uncrustify

Kind of. It's due to aligning continued statements to the open parenthesis, which is against our style guide. In the case of a throw statement, it aligns to a virtual parenthesis that uncrustify places after the throw keyword. You might regard that uncrustify puts a virtual parenthesis after throw but not after return, but it's definitely a misconfiguration.

@dirk-thomas
Copy link
Contributor

I specifically meant having the function specifier and return type on different lines from the function name

👍 that is certainly not documented in the style guide and also against our general goal to minimize vertical space. So that is something I would agree we should reconsider / reformat.

Kind of. It's due to aligning continued statements to the open parenthesis, which is against our style guide.

Nobody is opposed to fix those and merge the PR #174. The acceptance criteria though is that afterwards CI still passes green - so existing code which needs to be changed must have a corresponding PR which can be tested together with #174 and pass all linters.

Basically a set of PRs when merged together will transition us from the current state passing on CI to a new state (with better style) which also passed on CI.

@rotu
Copy link
Contributor Author

rotu commented Sep 6, 2019

Nobody is opposed to fix those and merge the PR #174. The acceptance criteria though is that afterwards CI still passes green - so existing code which needs to be changed must have a corresponding PR which can be tested together with #174 and pass all linters.

I know. I just haven't time to do this in the one fell swoop required, but I will eventually! I bring it up to say it's a bug in ament_uncrustify, not one in uncrustify.

@dirk-thomas
Copy link
Contributor

I just haven't time to do this in the one fell swoop required, but I will eventually!

Please just keep changes minimal per set of PRs. So don't try to address other style issues in the same set. Otherwise chance are pretty high its gets delay due to difficulties reviewing it.

@hidmic
Copy link
Contributor

hidmic commented Sep 10, 2019

Replicating #148 (comment). In an attempt to get ament_clang_format to enforce a style that is closer to our current code base, the following ament_clang_format patch modifies the existing configuration.

Applying the existing .clang-format configuration file yields this diff. Each option modified in the configuration patch above affects that diff in the following ways:

  • SpaceAfterTemplateKeyword: false (diff)
  • BinPackArguments: false (diff)
  • BinPackParameters: false (diff)
  • BreakBeforeTernaryOperators: false (diff)
  • AllowShortFunctionsOnASingleLine: None (diff)

Given that settings that Keep... do not enforce anything but keep what's already there, these cannot be applied incrementally. Reformatting master yields:

  • KeepEmptyLinesAtTheStartOfBlocks: true + MaxEmptyLinesToKeep: 1 (diff)

Please, comment on which style variation of the ones that ament_clang_format can enforce you'd prefer so that ament_uncrustify configuration can be adapted to be compatible. The one I link here introduces the least variation but is probably not compliant with an strict interpretation of the GSG.

@wjwwood
Copy link
Contributor

wjwwood commented Sep 24, 2019

👍 that is certainly not documented in the style guide and also against our general goal to minimize vertical space. So that is something I would agree we should reconsider / reformat.

It is true that it is not documented in the style guide that you must do it this way, because you don't, but there's no problem with doing it either.

On the point that we should consider reformatting because it goes against a "goal to minimize vertical space", I completely disagree. We don't have a stated goal to do that as far as I know. The Google Style guideline (it even points out it is not really a hard rule) referenced above (#146 (comment)) is specifically speaking about blank lines. It never mentions separating parts of a declaration as wasting vertical space.

I would not approve any pull request that only makes this change to our style guide, linters, or the code itself.

@wjwwood
Copy link
Contributor

wjwwood commented Sep 24, 2019

@hidmic can you make a pull request with the proposed changes to rclcpp (I realize there are options to choose from, and so there's no one set of changes, but...), because without a pull request we cannot make inline comments.

@dirk-thomas
Copy link
Contributor

dirk-thomas commented Sep 24, 2019

On the point that we should consider reformatting because it goes against a "goal to minimize vertical space", I completely disagree. We don't have a stated goal to do that as far as I know.

https://google.github.io/styleguide/cppguide.html#Vertical_Whitespace

Minimize use of vertical whitespace.

The basic principle is: The more code that fits on one screen, the easier it is to follow and understand the control flow of the program.

@wjwwood
Copy link
Contributor

wjwwood commented Sep 24, 2019

I think you should have included the full quote:

The basic principle is: The more code that fits on one screen, the easier it is to follow and understand the control flow of the program. Use whitespace purposefully to provide separation in that flow.

It also says:

This is more a principle than a rule: don't use blank lines when you don't have to.

Then it says:

Blank lines inside a chain of if-else blocks may well help readability.
A blank line before a comment line usually helps readability — the introduction of a new comment suggests the start of a new thought, and the blank line makes it clear that the comment goes with the following thing instead of the preceding.

Suggesting that use of vertical space may be useful if it aids in readability. The rule exists, in my opinion, to avoid things like this:

int a = 1;


float b = 2;

Not to prevent things like:

int a = 1;

float b = 2;

// this is a comment
string s = "hi";

Or to separate function names and return types in order to have all the names start in the same column.

@hidmic
Copy link
Contributor

hidmic commented Sep 24, 2019

@wjwwood ros2/rclcpp#872

@rotu
Copy link
Contributor Author

rotu commented Sep 25, 2019

@wjwwood

Or to separate function names and return types in order to have all the names start in the same column.

I don't know how much more clear the Google C++ style guide can be on this point:

Return type on the same line as function name, parameters on the same line if they fit.

@rotu
Copy link
Contributor Author

rotu commented Sep 25, 2019

Also, while the GSG does say "no spaces inside empty braces", it does not say to not insert newlines in empty spaces. I think it is generally implied by the vertical whitespace section.

@wjwwood
Copy link
Contributor

wjwwood commented Sep 25, 2019

You're right, it does say that, I was not aware of that line, and I was wrong when I suggested this wasn't violating the GSG. However, the other rule about vertical whitespace I think still doesn't apply.

In that same section (https://google.github.io/styleguide/cppguide.html#Function_Declarations_and_Definitions) it also shows how to handle functions that are too long, for example:

If you have too much text to fit on one line:

ReturnType ClassName::ReallyLongFunctionName(Type par_name1, Type par_name2,
                                             Type par_name3) {
  DoSomething();
  ...
}

Which as a team, years ago, we decided we did not like for various reasons. This is why we have fairly detailed exceptions where we diverge from GSG on this point in our style guide:

https://index.ros.org/doc/ros2/Contributing/Developer-Guide/#examples

Also, wrapping the lines as I am often likely to do, is allowed when the function declaration is too long. My personal preference is to have the methods use the same wrapping pattern (if even one is too long and needs to be wrapped, wrap them all), but I can see that it would be more in line with the GSG if we didn't leave how to wrap functions declarations up to the developer.


I think, however, the point of this is issue (at least my understand from our most recent ROS 2 meeting) is whether or not we want to use clang-format to do linting, and not rehash all the ways we are diverging from GSG. We may need to change some things about our style to change to clang-format, and that might bring up closer to the GSG, but I don't think we need to get closer to GSG just for the sake of being closer to the GSG.

@rotu
Copy link
Contributor Author

rotu commented Sep 25, 2019

I very much like the wrapping style of ROS2. I think it's nicer than packing as many arguments as will fit on the first line and letting them overflow.

I didn't realize there was a meeting to discuss this and that's encouraging. Clang-format and clang-tidy are suuuper useful tools that not only create better code, but teach me how to be a better coder. I would like them to be the default, or at least compatible with the default settings of ament_lint_auto (i.e. ament_uncrustify).

@wjwwood
Copy link
Contributor

wjwwood commented Sep 25, 2019

I didn't realize there was a meeting to discuss this and that's encouraging. Clang-format and clang-tidy are suuuper useful tools that not only create better code, but teach me how to be a better coder. I would like them to be the default, or at least compatible with the default settings of ament_lint_auto (i.e. ament_uncrustify).

It came up at our most recent general ROS 2 meeting. We're looking into it, and we would generally like to switch to clang-format just for the network effect (so many people using it and better tool integration for it). But some of us aren't yet convinced that we can achieve the style we want with clang-format (that's what we're investigating now) and yet others are unconvinced that chaging anything is worth the effort and disruption (people maintaining patches on top of rclcpp would hate those diffs, for example).

So no promises, but we're looking into it.

As for clang-tidy, I think that could be applied without (whitespace) style changes, right?

@rotu
Copy link
Contributor Author

rotu commented Sep 25, 2019

As for clang-tidy, I think that could be applied without (whitespace) style changes, right?

Yes. You can turn off any of the clang-tidy checks, but I think only this one check critiques whitespace (and should be on because it's hella useful): https://clang.llvm.org/extra/clang-tidy/checks/readability-misleading-indentation.html

@Bi0T1N
Copy link
Contributor

Bi0T1N commented May 26, 2021

Are there any news about the current status of using/switching to clang-format or from the outcome of the meeting?
The provided .clang-format config seems to be made with an old version while the current version of clang-format is 12. Thus it gained a lot of new features and settings, I've provided my config for clang-format 10 in #293.

@clalancette
Copy link
Contributor

I certainly would be in favor of updating .clang-format to be as close as possible to our current style. However, I think we should currently only use clang-format features up to clang 10.0, as that is what is shipped in Ubuntu 20.04. Once we switch to Ubuntu 22.04, then we can consider using updated features.

In terms of switching to clang-format by default, I would be against that. We already have two C++ linters (cpplint and uncrustify), and I don't really see the advantage of adding/switching to another one.

@ThaddaSuper
Copy link

We already have two C++ linters (cpplint and uncrustify), and I don't really see the advantage of adding/switching to another one.

cpplint does not support auto formatting code, so it's not at all comparable to clang-format. clang-format allows to fully format your code without forcing you to follow any coding style which is always a nice thing and it's broadly used also by many well known projects like the Linux kernel, Firefox or MS Visual Studio. The support for common editors is also much better, Clang-Format in VS Code has more than 500.000 installs while Uncrustify in VS Code has just a bit more than 30.000 and also seems to be dead/deprecated. Also using clang-format has the advantage that it fully supports newer C++ standards as it is based on the clang compiler while uncrustify is complete independent and thus less proven and maybe slower at understanding new code from newer standards.
So I'd be in favour of clang-format for the points I mentioned. I'd also advice to use a default coding style implemented by others and following their rules (like the Google style) instead of developing a new one. Yours isn't widely used and probably only favoured by a very few programmers who use mini screens which can only show 5cm of code.

@ThaddaSuper
Copy link

Old code could be updated only when changed (there are tools like pre-commit) so that not the complete history is broken by a huge reformat of everything (even if it might make sense to do that so it's done within a single commit).
And that your current approach isn't fully save can easily be shown:
https://github.com/ros2/rclcpp/blob/master/rclcpp/src/rclcpp/node.cpp#L104:

static
rclcpp::QoS
get_parameter_events_qos(

https://github.com/ros2/rclcpp/blob/master/rclcpp/src/rclcpp/graph_listener.cpp#L238

static bool
has_node_(

https://github.com/ros2/rclcpp/blob/master/rclcpp/src/rclcpp/node_options.cpp#L93

const rcl_node_options_t *
NodeOptions::get_rcl_node_options() const

Probably more that can be found but I just clicked through a few files...

@clalancette
Copy link
Contributor

The original issue that was opened here was about uncrustify and clang-format giving different results on our code. While that is absolutely true, there isn't too much we can do about it. Both of them serve a very similar role, and give different results on the ROS 2 core code base. We also don't want to remove either as an option, since some downstream users want to use one or the other.

We also can't switch to clang-format exclusively either, since as far as we know it can't be configured to exactly match our current style. A newer version of clang-format may get us closer, but it is not clear that we can make it match 100%.

Finally, the suggestion to change our style is way too much work for way too little gain. We have a style that works OK, and we have a linter (uncrustify) that matches that style. Changing all of the code to anything else is busywork at this point and doesn't make any real improvements.

Because of all of that, and because there is nothing actionable to do here, I'm going to close this issue out. If you'd like to open a separate issue about changing the style, feel free to do so, but we are unlikely to do that without a very compelling reason.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

7 participants