Index: docs/ClangFormatStyleOptions.rst =================================================================== --- docs/ClangFormatStyleOptions.rst +++ docs/ClangFormatStyleOptions.rst @@ -150,6 +150,9 @@ **AccessModifierOffset** (``int``) The extra indent or outdent of access modifiers, e.g. ``public:``. +**AccessModifierStandardIndent** (``bool``) + Indent accesses modifier in the normal method. + **AlignAfterOpenBracket** (``BracketAlignmentStyle``) If ``true``, horizontally aligns arguments after an open bracket. Index: include/clang/Format/Format.h =================================================================== --- include/clang/Format/Format.h +++ include/clang/Format/Format.h @@ -46,6 +46,7 @@ struct FormatStyle { /// \brief The extra indent or outdent of access modifiers, e.g. ``public:``. int AccessModifierOffset; + bool AccessModifierStandardIndent; /// \brief Different styles for aligning after open brackets. enum BracketAlignmentStyle { @@ -632,6 +633,7 @@ bool operator==(const FormatStyle &R) const { return AccessModifierOffset == R.AccessModifierOffset && + AccessModifierStandardIndent == R.AccessModifierStandardIndent && AlignAfterOpenBracket == R.AlignAfterOpenBracket && AlignConsecutiveAssignments == R.AlignConsecutiveAssignments && AlignConsecutiveDeclarations == R.AlignConsecutiveDeclarations && Index: lib/Format/Format.cpp =================================================================== --- lib/Format/Format.cpp +++ lib/Format/Format.cpp @@ -240,6 +240,8 @@ } IO.mapOptional("AccessModifierOffset", Style.AccessModifierOffset); + IO.mapOptional("AccessModifierStandardIndent", + Style.AccessModifierStandardIndent); IO.mapOptional("AlignAfterOpenBracket", Style.AlignAfterOpenBracket); IO.mapOptional("AlignConsecutiveAssignments", Style.AlignConsecutiveAssignments); @@ -487,6 +489,7 @@ FormatStyle LLVMStyle; LLVMStyle.Language = FormatStyle::LK_Cpp; LLVMStyle.AccessModifierOffset = -2; + LLVMStyle.AccessModifierStandardIndent = false; LLVMStyle.AlignEscapedNewlinesLeft = false; LLVMStyle.AlignAfterOpenBracket = FormatStyle::BAS_Align; LLVMStyle.AlignOperands = true; @@ -1213,9 +1216,9 @@ // necessary replacement to 'Replaces'. 'Includes' must be in strict source // order. static void sortCppIncludes(const FormatStyle &Style, - const SmallVectorImpl &Includes, - ArrayRef Ranges, StringRef FileName, - tooling::Replacements &Replaces, unsigned *Cursor) { + const SmallVectorImpl &Includes, + ArrayRef Ranges, StringRef FileName, + tooling::Replacements &Replaces, unsigned *Cursor) { if (!affectsRange(Ranges, Includes.front().Offset, Includes.back().Offset + Includes.back().Text.size())) return; Index: lib/Format/TokenAnnotator.h =================================================================== --- lib/Format/TokenAnnotator.h +++ lib/Format/TokenAnnotator.h @@ -39,7 +39,7 @@ public: AnnotatedLine(const UnwrappedLine &Line) : First(Line.Tokens.front().Tok), Level(Line.Level), - InPPDirective(Line.InPPDirective), + accessLevelMarker(Line.accessLevelMarker), InPPDirective(Line.InPPDirective), MustBeDeclaration(Line.MustBeDeclaration), MightBeFunctionDecl(false), IsMultiVariableDeclStmt(false), Affected(false), LeadingEmptyLinesAffected(false), ChildrenAffected(false) { @@ -109,6 +109,7 @@ LineType Type; unsigned Level; + std::vector accessLevelMarker; bool InPPDirective; bool MustBeDeclaration; bool MightBeFunctionDecl; Index: lib/Format/UnwrappedLineFormatter.cpp =================================================================== --- lib/Format/UnwrappedLineFormatter.cpp +++ lib/Format/UnwrappedLineFormatter.cpp @@ -10,6 +10,7 @@ #include "UnwrappedLineFormatter.h" #include "WhitespaceManager.h" #include "llvm/Support/Debug.h" +#include #include #define DEBUG_TYPE "format-formatter" @@ -56,11 +57,19 @@ // having the right size in adjustToUnmodifiedline. while (IndentForLevel.size() <= Line.Level) IndentForLevel.push_back(-1); + unsigned currentLevel = Line.Level; + if (Style.AccessModifierStandardIndent) { + currentLevel += std::accumulate(std::begin(Line.accessLevelMarker), + std::end(Line.accessLevelMarker), 0); + } if (Line.InPPDirective) { - Indent = Line.Level * Style.IndentWidth + AdditionalIndent; + Indent = currentLevel * Style.IndentWidth + AdditionalIndent; } else { - IndentForLevel.resize(Line.Level + 1); - Indent = getIndent(IndentForLevel, Line.Level); + std::size_t oldSize = IndentForLevel.size(); + IndentForLevel.resize(currentLevel + 1); + std::fill(std::begin(IndentForLevel) + oldSize, std::end(IndentForLevel), + -1); + Indent = getIndent(IndentForLevel, currentLevel); } if (static_cast(Indent) + Offset >= 0) Indent += Offset; Index: lib/Format/UnwrappedLineParser.h =================================================================== --- lib/Format/UnwrappedLineParser.h +++ lib/Format/UnwrappedLineParser.h @@ -42,6 +42,8 @@ /// \brief The indent level of the \c UnwrappedLine. unsigned Level; + /// \brief If an accesses specifier has been used at a particular level + std::vector accessLevelMarker; /// \brief Whether this \c UnwrappedLine is part of a preprocessor directive. bool InPPDirective; @@ -72,7 +74,7 @@ void parseFile(); void parseLevel(bool HasOpeningBrace); void parseBlock(bool MustBeDeclaration, bool AddLevel = true, - bool MunchSemi = true); + bool MunchSemi = true, bool ClassBlock = false); void parseChildBlock(); void parsePPDirective(); void parsePPDefine(); @@ -214,7 +216,9 @@ }; inline UnwrappedLine::UnwrappedLine() - : Level(0), InPPDirective(false), MustBeDeclaration(false) {} + : Level(0), InPPDirective(false), MustBeDeclaration(false) { + accessLevelMarker.push_back(0); +} } // end namespace format } // end namespace clang Index: lib/Format/UnwrappedLineParser.cpp =================================================================== --- lib/Format/UnwrappedLineParser.cpp +++ lib/Format/UnwrappedLineParser.cpp @@ -410,13 +410,14 @@ } void UnwrappedLineParser::parseBlock(bool MustBeDeclaration, bool AddLevel, - bool MunchSemi) { + bool MunchSemi, bool ClassBlock) { assert(FormatTok->isOneOf(tok::l_brace, TT_MacroBlockBegin) && "'{' or macro block token expected"); const bool MacroBlock = FormatTok->is(TT_MacroBlockBegin); FormatTok->BlockKind = BK_Block; unsigned InitialLevel = Line->Level; + std::vector InitialAccessUsed = Line->accessLevelMarker; nextToken(); if (MacroBlock && FormatTok->is(tok::l_paren)) @@ -426,8 +427,12 @@ ScopedDeclarationState DeclarationState(*Line, DeclarationScopeStack, MustBeDeclaration); - if (AddLevel) + if (AddLevel) { + if (ClassBlock) { + Line->accessLevelMarker.push_back(0); + } ++Line->Level; + } parseLevel(/*HasOpeningBrace=*/true); if (eof()) @@ -436,6 +441,7 @@ if (MacroBlock ? !FormatTok->is(TT_MacroBlockEnd) : !FormatTok->is(tok::r_brace)) { Line->Level = InitialLevel; + Line->accessLevelMarker = InitialAccessUsed; FormatTok->BlockKind = BK_Block; return; } @@ -448,6 +454,7 @@ if (MunchSemi && FormatTok->Tok.is(tok::semi)) nextToken(); Line->Level = InitialLevel; + Line->accessLevelMarker = InitialAccessUsed; } static bool isGoogScope(const UnwrappedLine &Line) { @@ -1119,7 +1126,7 @@ nextToken(); return false; } - const FormatToken* Previous = getPreviousToken(); + const FormatToken *Previous = getPreviousToken(); if (Previous && (Previous->isOneOf(tok::identifier, tok::kw_operator, tok::kw_new, tok::kw_delete) || @@ -1678,7 +1685,10 @@ // Otherwise, we don't know what it is, and we'd better keep the next token. if (FormatTok->Tok.is(tok::colon)) nextToken(); + Line->accessLevelMarker.back() = 0; + addUnwrappedLine(); addUnwrappedLine(); + Line->accessLevelMarker.back() = 1; } bool UnwrappedLineParser::parseEnum() { @@ -1853,7 +1863,7 @@ addUnwrappedLine(); parseBlock(/*MustBeDeclaration=*/true, /*AddLevel=*/true, - /*MunchSemi=*/false); + /*MunchSemi=*/false, /*ClassBlock=*/true); } // There is no addUnwrappedLine() here so that we fall through to parsing a // structural element afterwards. Thus, in "class A {} n, m;", @@ -2003,6 +2013,7 @@ }); CurrentLines->push_back(std::move(*Line)); Line->Tokens.clear(); + Line->accessLevelMarker = CurrentLines->back().accessLevelMarker; if (CurrentLines == &Lines && !PreprocessorDirectives.empty()) { CurrentLines->append( std::make_move_iterator(PreprocessorDirectives.begin()), Index: unittests/Format/CMakeLists.txt =================================================================== --- unittests/Format/CMakeLists.txt +++ unittests/Format/CMakeLists.txt @@ -5,6 +5,7 @@ add_clang_unittest(FormatTests CleanupTest.cpp FormatTest.cpp + FormatTestAccess.cpp FormatTestJava.cpp FormatTestJS.cpp FormatTestProto.cpp Index: unittests/Format/FormatTestAccess.cpp =================================================================== --- unittests/Format/FormatTestAccess.cpp +++ unittests/Format/FormatTestAccess.cpp @@ -0,0 +1,150 @@ +//===- unittest/Format/FormatTestAccess.cpp - Formatting unit tests +//-------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Format/Format.h" + +#include "../Tooling/RewriterTestContext.h" +#include "FormatTestUtils.h" + +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/MemoryBuffer.h" +#include "gtest/gtest.h" + +#define DEBUG_TYPE "format-test" + +namespace clang { +namespace format { +namespace { + +class FormatTestAccess : public ::testing::Test { +protected: + std::string format(llvm::StringRef Code, bool normalFormat) { + const FormatStyle &Style = getGoogleStyleWithColumns(normalFormat); + DEBUG(llvm::errs() << "---\n"); + DEBUG(llvm::errs() << Code << "\n\n"); + std::vector Ranges(1, tooling::Range(0, Code.size())); + bool IncompleteFormat = false; + tooling::Replacements Replaces = + reformat(Style, Code, Ranges, "", &IncompleteFormat); + EXPECT_FALSE(IncompleteFormat) << Code << "\n\n"; + auto Result = applyAllReplacements(Code, Replaces); + EXPECT_TRUE(static_cast(Result)); + DEBUG(llvm::errs() << "\n" << *Result << "\n\n"); + return *Result; + } + + FormatStyle getGoogleStyleWithColumns(bool normalFormat) { + FormatStyle Style = getLLVMStyle(); + Style.AccessModifierOffset = normalFormat ? 0 : -2; + Style.AccessModifierStandardIndent = normalFormat; + return Style; + } + + void verifyFormat(llvm::StringRef Code, bool normalFormat) { + EXPECT_EQ(Code.str(), format(test::messUp(Code), normalFormat)); + } +}; + +TEST_F(FormatTestAccess, NoChangeWithoutAccess) { + verifyFormat("class A {\n" + " int a1;\n" + "};", + false); + verifyFormat("class B {\n" + " int b1;\n" + "};", + true); +} + +TEST_F(FormatTestAccess, ChangeWithAccess) { + verifyFormat("class C {\n" + " int c1;\n" + "public:\n" + " int c2;\n" + "};", + false); + verifyFormat("class D {\n" + " int d1;\n" + " public:\n" + " int d2;\n" + "};", + true); +} + +TEST_F(FormatTestAccess, MultipleAccessLevels) { + verifyFormat("class E {\n" + " int e1;\n" + "public:\n" + " int e2;\n" + "private:\n" + " int e3;\n" + "protected:\n" + " int e4;\n" + "};", + false); + verifyFormat("class F {\n" + " int f1;\n" + " public:\n" + " int f2;\n" + " private:\n" + " int f3;\n" + " protected:\n" + " int f4;\n" + "};", + true); +} + +TEST_F(FormatTestAccess, NestedClass) { + verifyFormat("class G {\n" + " int g1;\n" + " class GGA {\n" + " int gg1;\n" + " public:\n" + " int gg2;\n" + " };\n" + "public:\n" + " class GGB {\n" + " int gg1;\n" + " public:\n" + " int gg2;\n" + " };\n" + " int g2;\n" + "private:\n" + " int g3;\n" + "protected:\n" + " int g4;\n" + "};", + false); + verifyFormat("class H {\n" + " int h1;\n" + " class HHA {\n" + " int hh1;\n" + " public:\n" + " int hh2;\n" + " };\n" + " public:\n" + " class HHB {\n" + " int hh1;\n" + " public:\n" + " int hh2;\n" + " };\n" + " int h2;\n" + " private:\n" + " int h3;\n" + " protected:\n" + " int h4;\n" + "};", + true); +} + +} // end namespace +} // end namespace format +} // end namespace clang