r181326 - Config file support for clang-format, part 1.

Aaron Ballman aaron at aaronballman.com
Thu May 9 13:24:32 PDT 2013


On Tue, May 7, 2013 at 11:32 AM, Alexander Kornienko <alexfh at google.com> wrote:
> Author: alexfh
> Date: Tue May  7 10:32:14 2013
> New Revision: 181326
>
> URL: http://llvm.org/viewvc/llvm-project?rev=181326&view=rev
> Log:
> Config file support for clang-format, part 1.
>
> Summary:
> Added parseConfiguration method, which reads FormatStyle from YAML
> string. This supports all FormatStyle fields and an additional BasedOnStyle
> field, which can be used to specify base style.
>
> Reviewers: djasper, klimek
>
> Reviewed By: djasper
>
> CC: cfe-commits
>
> Differential Revision: http://llvm-reviews.chandlerc.com/D754
>
> Modified:
>     cfe/trunk/include/clang/Format/Format.h
>     cfe/trunk/lib/Format/Format.cpp
>     cfe/trunk/unittests/Format/FormatTest.cpp
>
> Modified: cfe/trunk/include/clang/Format/Format.h
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Format/Format.h?rev=181326&r1=181325&r2=181326&view=diff
> ==============================================================================
> --- cfe/trunk/include/clang/Format/Format.h (original)
> +++ cfe/trunk/include/clang/Format/Format.h Tue May  7 10:32:14 2013
> @@ -17,6 +17,7 @@
>
>  #include "clang/Frontend/FrontendAction.h"
>  #include "clang/Tooling/Refactoring.h"
> +#include "llvm/Support/system_error.h"
>
>  namespace clang {
>
> @@ -110,6 +111,18 @@ FormatStyle getChromiumStyle();
>  /// https://developer.mozilla.org/en-US/docs/Developer_Guide/Coding_Style.
>  FormatStyle getMozillaStyle();
>
> +/// \brief Returns a predefined style by name.
> +///
> +/// Currently supported names: LLVM, Google, Chromium, Mozilla. Names are
> +/// compared case-insensitively.
> +FormatStyle getPredefinedStyle(StringRef Name);
> +
> +/// \brief Parse configuration from YAML-formatted text.
> +llvm::error_code parseConfiguration(StringRef Text, FormatStyle *Style);
> +
> +/// \brief Gets configuration in a YAML string.
> +std::string configurationAsText(const FormatStyle &Style);
> +
>  /// \brief Reformats the given \p Ranges in the token stream coming out of
>  /// \c Lex.
>  ///
>
> Modified: cfe/trunk/lib/Format/Format.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Format/Format.cpp?rev=181326&r1=181325&r2=181326&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Format/Format.cpp (original)
> +++ cfe/trunk/lib/Format/Format.cpp Tue May  7 10:32:14 2013
> @@ -28,9 +28,59 @@
>  #include "llvm/ADT/STLExtras.h"
>  #include "llvm/Support/Allocator.h"
>  #include "llvm/Support/Debug.h"
> +#include "llvm/Support/YAMLTraits.h"
>  #include <queue>
>  #include <string>
>
> +namespace llvm {
> +namespace yaml {
> +template <>
> +struct ScalarEnumerationTraits<clang::format::FormatStyle::LanguageStandard> {
> +  static void enumeration(IO &io,
> +                          clang::format::FormatStyle::LanguageStandard &value) {
> +    io.enumCase(value, "C++03", clang::format::FormatStyle::LS_Cpp03);
> +    io.enumCase(value, "C++11", clang::format::FormatStyle::LS_Cpp11);
> +    io.enumCase(value, "Auto", clang::format::FormatStyle::LS_Auto);
> +  }
> +};
> +
> +template <> struct MappingTraits<clang::format::FormatStyle> {
> +  static void mapping(llvm::yaml::IO &IO, clang::format::FormatStyle &Style) {
> +    if (!IO.outputting()) {
> +      StringRef BasedOnStyle;
> +      IO.mapOptional("BasedOnStyle", BasedOnStyle);
> +
> +      if (!BasedOnStyle.empty())
> +        Style = clang::format::getPredefinedStyle(BasedOnStyle);
> +    }
> +
> +    IO.mapOptional("AccessModifierOffset", Style.AccessModifierOffset);
> +    IO.mapOptional("AlignEscapedNewlinesLeft", Style.AlignEscapedNewlinesLeft);
> +    IO.mapOptional("AllowAllParametersOfDeclarationOnNextLine",
> +                   Style.AllowAllParametersOfDeclarationOnNextLine);
> +    IO.mapOptional("AllowShortIfStatementsOnASingleLine",
> +                   Style.AllowShortIfStatementsOnASingleLine);
> +    IO.mapOptional("BinPackParameters", Style.BinPackParameters);
> +    IO.mapOptional("ColumnLimit", Style.ColumnLimit);
> +    IO.mapOptional("ConstructorInitializerAllOnOneLineOrOnePerLine",
> +                   Style.ConstructorInitializerAllOnOneLineOrOnePerLine);
> +    IO.mapOptional("DerivePointerBinding", Style.DerivePointerBinding);
> +    IO.mapOptional("IndentCaseLabels", Style.IndentCaseLabels);
> +    IO.mapOptional("MaxEmptyLinesToKeep", Style.MaxEmptyLinesToKeep);
> +    IO.mapOptional("ObjCSpaceBeforeProtocolList",
> +                   Style.ObjCSpaceBeforeProtocolList);
> +    IO.mapOptional("PenaltyExcessCharacter", Style.PenaltyExcessCharacter);
> +    IO.mapOptional("PenaltyReturnTypeOnItsOwnLine",
> +                   Style.PenaltyReturnTypeOnItsOwnLine);
> +    IO.mapOptional("PointerBindsToType", Style.PointerBindsToType);
> +    IO.mapOptional("SpacesBeforeTrailingComments",
> +                   Style.SpacesBeforeTrailingComments);
> +    IO.mapOptional("Standard", Style.Standard);
> +  }
> +};
> +}
> +}
> +
>  namespace clang {
>  namespace format {
>
> @@ -98,6 +148,37 @@ FormatStyle getMozillaStyle() {
>    return MozillaStyle;
>  }
>
> +FormatStyle getPredefinedStyle(StringRef Name) {
> +  if (Name.equals_lower("llvm"))
> +    return getLLVMStyle();
> +  if (Name.equals_lower("chromium"))
> +    return getChromiumStyle();
> +  if (Name.equals_lower("mozilla"))
> +    return getMozillaStyle();
> +  if (Name.equals_lower("google"))
> +    return getGoogleStyle();
> +
> +  llvm::errs() << "Unknown style " << Name << ", using Google style.\n";
> +  return getGoogleStyle();
> +}
> +
> +llvm::error_code parseConfiguration(StringRef Text, FormatStyle *Style) {
> +  llvm::yaml::Input Input(Text);
> +  Input >> *Style;
> +  return Input.error();
> +}
> +
> +std::string configurationAsText(const FormatStyle &Style) {
> +  std::string Text;
> +  llvm::raw_string_ostream Stream(Text);
> +  llvm::yaml::Output Output(Stream);
> +  // We use the same mapping method for input and output, so we need a non-const
> +  // reference here.
> +  FormatStyle NonConstStyle = Style;
> +  Output << NonConstStyle;
> +  return Text;
> +}
> +
>  // Returns the length of everything up to the first possible line break after
>  // the ), ], } or > matching \c Tok.
>  static unsigned getLengthToMatchingParen(const AnnotatedToken &Tok) {
>
> Modified: cfe/trunk/unittests/Format/FormatTest.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/Format/FormatTest.cpp?rev=181326&r1=181325&r2=181326&view=diff
> ==============================================================================
> --- cfe/trunk/unittests/Format/FormatTest.cpp (original)
> +++ cfe/trunk/unittests/Format/FormatTest.cpp Tue May  7 10:32:14 2013
> @@ -3909,5 +3909,113 @@ TEST_F(FormatTest, DoNotCreateUnreasonab
>                 "}");
>  }
>
> +bool operator==(const FormatStyle &L, const FormatStyle &R) {
> +  return L.AccessModifierOffset == R.AccessModifierOffset &&
> +         L.AlignEscapedNewlinesLeft == R.AlignEscapedNewlinesLeft &&
> +         L.AllowAllParametersOfDeclarationOnNextLine ==
> +             R.AllowAllParametersOfDeclarationOnNextLine &&
> +         L.AllowShortIfStatementsOnASingleLine ==
> +             R.AllowShortIfStatementsOnASingleLine &&
> +         L.BinPackParameters == R.BinPackParameters &&
> +         L.ColumnLimit == R.ColumnLimit &&
> +         L.ConstructorInitializerAllOnOneLineOrOnePerLine ==
> +             R.ConstructorInitializerAllOnOneLineOrOnePerLine &&
> +         L.DerivePointerBinding == R.DerivePointerBinding &&
> +         L.IndentCaseLabels == R.IndentCaseLabels &&
> +         L.MaxEmptyLinesToKeep == R.MaxEmptyLinesToKeep &&
> +         L.ObjCSpaceBeforeProtocolList == R.ObjCSpaceBeforeProtocolList &&
> +         L.PenaltyExcessCharacter == R.PenaltyExcessCharacter &&
> +         L.PenaltyReturnTypeOnItsOwnLine == R.PenaltyReturnTypeOnItsOwnLine &&
> +         L.PointerBindsToType == R.PointerBindsToType &&
> +         L.SpacesBeforeTrailingComments == R.SpacesBeforeTrailingComments &&
> +         L.Standard == R.Standard;
> +}
> +
> +bool allStylesEqual(ArrayRef<FormatStyle> Styles) {
> +  for (size_t i = 1; i < Styles.size(); ++i)
> +    if (!(Styles[0] == Styles[i]))
> +      return false;
> +  return true;
> +}
> +
> +TEST_F(FormatTest, GetsPredefinedStyleByName) {
> +  FormatStyle LLVMStyles[] = { getLLVMStyle(), getPredefinedStyle("LLVM"),
> +                               getPredefinedStyle("llvm"),
> +                               getPredefinedStyle("lLvM") };
> +  EXPECT_TRUE(allStylesEqual(LLVMStyles));
> +
> +  FormatStyle GoogleStyles[] = { getGoogleStyle(), getPredefinedStyle("Google"),
> +                                 getPredefinedStyle("google"),
> +                                 getPredefinedStyle("gOOgle") };
> +  EXPECT_TRUE(allStylesEqual(GoogleStyles));
> +
> +  FormatStyle ChromiumStyles[] = { getChromiumStyle(),
> +                                   getPredefinedStyle("Chromium"),
> +                                   getPredefinedStyle("chromium"),
> +                                   getPredefinedStyle("chROmiUM") };
> +  EXPECT_TRUE(allStylesEqual(ChromiumStyles));
> +
> +  FormatStyle MozillaStyles[] = { getMozillaStyle(),
> +                                  getPredefinedStyle("Mozilla"),
> +                                  getPredefinedStyle("mozilla"),
> +                                  getPredefinedStyle("moZilla") };
> +  EXPECT_TRUE(allStylesEqual(MozillaStyles));
> +}
> +
> +TEST_F(FormatTest, ParsesConfiguration) {
> +  FormatStyle Style = {};
> +#define CHECK_PARSE(TEXT, FIELD, VALUE)                                        \
> +  EXPECT_NE(VALUE, Style.FIELD);                                               \
> +  EXPECT_EQ(0, parseConfiguration(TEXT, &Style).value());                      \
> +  EXPECT_EQ(VALUE, Style.FIELD)
> +
> +#define CHECK_PARSE_BOOL(FIELD)                                                \
> +  Style.FIELD = false;                                                         \
> +  EXPECT_EQ(0, parseConfiguration(#FIELD ": true", &Style).value());           \
> +  EXPECT_EQ(true, Style.FIELD);                                                \
> +  EXPECT_EQ(0, parseConfiguration(#FIELD ": false", &Style).value());          \
> +  EXPECT_EQ(false, Style.FIELD);
> +
> +  CHECK_PARSE_BOOL(AlignEscapedNewlinesLeft);
> +  CHECK_PARSE_BOOL(AllowAllParametersOfDeclarationOnNextLine);
> +  CHECK_PARSE_BOOL(AllowShortIfStatementsOnASingleLine);
> +  CHECK_PARSE_BOOL(BinPackParameters);
> +  CHECK_PARSE_BOOL(ConstructorInitializerAllOnOneLineOrOnePerLine);
> +  CHECK_PARSE_BOOL(DerivePointerBinding);
> +  CHECK_PARSE_BOOL(IndentCaseLabels);
> +  CHECK_PARSE_BOOL(ObjCSpaceBeforeProtocolList);
> +  CHECK_PARSE_BOOL(PointerBindsToType);
> +
> +  CHECK_PARSE("AccessModifierOffset: -1234", AccessModifierOffset, -1234);
> +  CHECK_PARSE("ColumnLimit: 1234", ColumnLimit, 1234u);
> +  CHECK_PARSE("MaxEmptyLinesToKeep: 1234", MaxEmptyLinesToKeep, 1234u);
> +  CHECK_PARSE("PenaltyExcessCharacter: 1234", PenaltyExcessCharacter, 1234u);
> +  CHECK_PARSE("PenaltyReturnTypeOnItsOwnLine: 1234",
> +              PenaltyReturnTypeOnItsOwnLine, 1234u);
> +  CHECK_PARSE("SpacesBeforeTrailingComments: 1234",
> +              SpacesBeforeTrailingComments, 1234u);
> +
> +  Style.Standard = FormatStyle::LS_Auto;
> +  CHECK_PARSE("Standard: C++03", Standard, FormatStyle::LS_Cpp03);
> +  CHECK_PARSE("Standard: C++11", Standard, FormatStyle::LS_Cpp11);
> +  CHECK_PARSE("Standard: Auto", Standard, FormatStyle::LS_Auto);
> +
> +  Style.ColumnLimit = 123;
> +  FormatStyle BaseStyle = getLLVMStyle();
> +  CHECK_PARSE("BasedOnStyle: LLVM", ColumnLimit, BaseStyle.ColumnLimit);
> +  CHECK_PARSE("BasedOnStyle: LLVM\nColumnLimit: 1234", ColumnLimit, 1234u);
> +
> +#undef CHECK_PARSE
> +#undef CHECK_PARSE_BOOL
> +}
> +
> +TEST_F(FormatTest, ConfigurationRoundTripTest) {
> +  FormatStyle Style = getLLVMStyle();
> +  std::string YAML = configurationAsText(Style);
> +  FormatStyle ParsedStyle = {};
> +  EXPECT_EQ(0, parseConfiguration(YAML, &ParsedStyle).value());

This unit test is failing because of Input::beginMapping in
YAMLTraits.cpp.  It uses dyn_cast, but CurrentNode is null (and so it
asserts). I don't know enough about YAML to know whether CurrentNode
being null is sensible or not.

~Aaron



More information about the cfe-commits mailing list