clang-format: Add preprocessor directive indentation


clang-format: Add preprocessor directive indentation

This is an implementation for bug 17362 which adds support for indenting preprocessor statements inside if/ifdef/endif. This takes previous work from fmauch (https://github.com/fmauch/clang/tree/preprocessor_indent) and makes it into a full feature.
The context of this patch is that I'm a VMware intern, and I implemented this because VMware needs the feature. As such, some decisions were made based on what VMware wants, and I would appreciate suggestions on expanding this if necessary to use-cases other people may want.

This adds a new enum config option, IndentPPDirectives. Values are:

  • PPDIS_None (in config: None):
#if FOO
#if BAR
#include <foo>
  • PPDIS_AfterHash (in config: AfterHash):
#if FOO
#  if BAR
#    include <foo>
#  endif

This is meant to work whether spaces or tabs are used for indentation. Preprocessor indentation is independent of indentation for non-preprocessor lines.

Preprocessor indentation also attempts to ignore include guards with the checks:

  1. Include guards cover the entire file
  2. Include guards don't have #else
  3. Include guards begin with
#ifndef <var>
#define <var>

This patch allows UnwrappedLineParser::PPBranchLevel to be decremented to -1 (the initial value is -1) so the variable can be used for indent tracking.


  • This patch does not handle the case where there's code between the #ifndef and #define but all other conditions hold. This is because when the #define line is parsed, UnwrappedLineParser::Lines doesn't hold the previous code line yet, so we can't detect it. This is out of the scope of this patch.
  • This patch does not handle cases where legitimate lines may be outside an include guard. Examples are #pragma once and #pragma GCC diagnostic, or anything else that does not change the meaning of the file if it's included multiple times.
  • This does not detect when there is a single non-preprocessor line in front of an include-guard-like structure where other conditions hold because ScopedLineState hides the line.
  • Preprocessor indentation throws off TokenAnnotator::setCommentLineLevels so the indentation of comments immediately before indented preprocessor lines is toggled on each run. Fixing this issue appears to be a major change and too much complexity for this patch.

Contributed by @euhlmann!

Reviewers: djasper, klimek, krasimir

Reviewed By: djasper, krasimir

Subscribers: krasimir, mzeren-vmw, cfe-commits

Tags: #clang

Differential Revision: https://reviews.llvm.org/D35955


krasimirAug 30 2017, 7:34 AM
Differential Revision
D35955: clang-format: Add preprocessor directive indentation
rL312124: Driver: out-of-line static analyzer flag handling (NFC)

Event Timeline

panoscc added a subscriber: panoscc.Sep 1 2017, 8:30 AM

This is a wrong output when using AfterHash option:

#ifndef G
#define G

#define M

void foo();

#  define N

Not sure if this case is already covered in the defects section but everything bellow foo() has wrong indentation.

Added in the 6.0 release notes in r312535