Creating and Enforcing a Code Formatting Standard with clang-format

I've worked on many programming teams which nominally have a "programming style guide." This guide is often written down and placed in a place that developers rarely look. In almost every case the style guide is ignored, code reviews devolve into style arguments, and a multitude of styles develop inside of the source repository.

The MongoDB team has provided us with an excellent formatting principle:

A formatting process which is both manual and insufficient is doomed to be abandoned.

Instead of relying on programmers to follow a set of rules, we should automate the process to make it as simple and impersonal as possible. Luckily, the clang team has created a wonderful tool that we can leverage: clang-format.

By using clang-format, we can create a list of style rules, enable programmers to quickly reformat their code, and create formatting checks that run on our build servers to ensure compliance.

Thinking About Style Guidelines

Before you begin this adventure, it's important to have some style guidelines in mind for your team. You can use the handy programming style guide that your team has ignored, or you can review style guides and rules from around the web and decide which rules your team should adopt. A list of style guides that you can refer to is found in the next section.

Style is a contentious topic. Don't get dragged into unnecessary arguments. Whether spaces or tabs are used is ultimately unimportant, and once a tool is impersonally updating everyone's code it won't matter at all. Appoint someone as the ultimate decision maker if there is no clear winner for a style guideline.

Also, be aware that some style rules cannot be checked by the tool. Consider whether such a rule is ultimately important and enforce it in other ways.

Generating Your Config

While you can build your clang-format configuration from scratch, it's much easier to start with an existing style and make your modifications.

You can see the options enabled for each of the default styles by using this command:

clang-format --style=llvm -dump-config

You can override the style argument to match any of the default style sets:

Once you've selected a file as your baseline, dump the contents to a .clang-format file as a starting baseline:

clang-format --style=llvm -dump-config > .clang-format

The .clang_format file is where we will keep our custom style definition. When running clang-format in the future, we will specify -style=file so that clang-format knows to use our custom rules.

Now that you have a baseline, review the style options and tweak them for your project. You'll want to check the formatting style rules against your code to make sure the output works as expected.

You can also use an online .clang-format builder for a more interactive experience. Many style options in the interactive builder use live examples to let you compare different settings. Note that some options that are available in the latest clang-format build may not be available in the online builder.

Running clang-format

Running clang-format is relatively simple. Let's discuss some important options before we get into the details.

Style

The style argument is used to determine the style rules that clang-format will apply. You can use any of the styles described above, or -style=file to tell clang-format that it must use your .clang-format file.

In-place editing

By default, clang-format will display formatting discrepancies as shell output. I prefer to have clang-format update the files directly. This behavior is enabled using the -i option.

Fallback Style

When getting started with clang-format, it's easy to get into situations where clang-format cannot find your style file. In this situation, it will fallback to the LLVM style. You can determine the exact style to use as a fallback using the -fallback-style=<style> switch. Setting <style> to none will cause clang-format to fail if your file can't be located:

-fallback-style=none

Re-formatting files

At the time this article was written, we need to supply a list of files, as clang-format will not run recursively over your source tree. In the next post I will provide some sample wrapper scripts for clang-format.

Here's a command to get you started, which fill find all C and C++ files in the current directory tree:

find . -iname *.h -o -iname *.c -o -iname *.cpp -o -iname *.hpp \
    | xargs clang-format -style=file -i -fallback-style=none

Note the arguments used with clang-format: I have applied in-place editing, indicated that the style rules are in my .clang-format file, and that I want clang-format to fail if the style file is not found.

Disabling Formatting on a Piece of Code

There are certainly situations where we don't want clang-format to override the existing formatting. Perhaps formatting rules cannot be created to allow the desired format, or a block is specially formatted for readability reasons.

You can use comments in your code to disable clang-format from modifying a section of code:

// clang-format off
void unformatted_code:
// clang-format on

Block-style comments also work:

/* clang-format off */
void unformatted_code:
/* clang-format on */

Note the space in between the comment start (//) and clang-format. This space is required for the comment to be successfully detected.

Integrating With Your Editor

There are clang-format integrations for vim, emacs, BBEdit, and Visual Studio described in the clang-format documentation. You can also find a Sublime Text Package on Package Control

A Note on Versions

You'll need to ensure that everyone uses the same version of clang-format, or eventually you will run into configuration mismatches and output differences. This can be enforced by your dependency system, by including a binary in your repository, or by using scripts to check versions before running clang-format.

My Style Guide

Here are my current style settings. These options are available for clang-format version 4.0.1.

---
Language:        Cpp
# BasedOnStyle:  LLVM
AccessModifierOffset: -2
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlinesLeft: true
AlignOperands:   true
AlignTrailingComments: false
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortIfStatementsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Empty
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: true
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
  AfterClass:      true
  AfterControlStatement: true
  AfterEnum:       true
  AfterFunction:   true
  AfterNamespace:  true
  AfterObjCDeclaration: false
  AfterStruct:     true
  AfterUnion:      true
  BeforeCatch:     true
  BeforeElse:      true
  IndentBraces:    false
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Custom
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit:     100
CommentPragmas:  '^ IWYU pragma:'
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat:   false
ForEachMacros:   [ foreach, Q_FOREACH, BOOST_FOREACH ]
IncludeCategories:
  - Regex:           '^"(llvm|llvm-c|clang|clang-c)/'
    Priority:        2
  - Regex:           '^(<|"(gtest|isl|json)/)'
    Priority:        3
  - Regex:           '.*'
    Priority:        1
IncludeIsMainRegex: '$'
IndentCaseLabels: true
IndentWidth:     4
IndentWrappedFunctionNames: true
JavaScriptQuotes: Leave
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ''
MacroBlockEnd:   ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: Inner
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Left
ReflowComments:  true
SortIncludes:    true
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: Never
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles:  false
SpacesInContainerLiterals: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard:        Cpp11
TabWidth:        4
UseTab:          Always
...

Future Explorations

In future posts, I will be sharing some helpful wrapper scripts that I use with clang-format and my strategy for ensuring formatting compliance on my projects

Further Reading