Index: include/clang/Format/Format.h =================================================================== --- include/clang/Format/Format.h +++ include/clang/Format/Format.h @@ -828,6 +828,15 @@ ArrayRef Ranges, StringRef FileName = ""); +/// \brief Fix namespace end comments in the given \p Ranges in \p Code. +/// +/// Returns the ``Replacements`` that fix the namespace comments in all +/// \p Ranges in \p Code. +tooling::Replacements fixNamespaceEndComments(const FormatStyle &Style, + StringRef Code, + ArrayRef Ranges, + StringRef FileName = ""); + /// \brief Returns the ``LangOpts`` that the formatter expects you to set. /// /// \param Style determines specific settings for lexing mode. Index: lib/Format/CMakeLists.txt =================================================================== --- lib/Format/CMakeLists.txt +++ lib/Format/CMakeLists.txt @@ -7,6 +7,7 @@ Format.cpp FormatToken.cpp FormatTokenLexer.cpp + NamespaceEndCommentsFixer.cpp SortJavaScriptImports.cpp TokenAnalyzer.cpp TokenAnnotator.cpp Index: lib/Format/Format.cpp =================================================================== --- lib/Format/Format.cpp +++ lib/Format/Format.cpp @@ -17,6 +17,7 @@ #include "AffectedRangeManager.h" #include "ContinuationIndenter.h" #include "FormatTokenLexer.h" +#include "NamespaceEndCommentsFixer.h" #include "SortJavaScriptImports.h" #include "TokenAnalyzer.h" #include "TokenAnnotator.h" @@ -896,6 +897,10 @@ } }; +static bool inputUsesCRLF(StringRef Text) { + return Text.count('\r') * 2 > Text.count('\n'); +} + class Formatter : public TokenAnalyzer { public: Formatter(const Environment &Env, const FormatStyle &Style, @@ -931,11 +936,6 @@ } private: - - static bool inputUsesCRLF(StringRef Text) { - return Text.count('\r') * 2 > Text.count('\n'); - } - bool hasCpp03IncompatibleFormat(const SmallVectorImpl &Lines) { for (const AnnotatedLine *Line : Lines) { @@ -1858,6 +1858,16 @@ return Clean.process(); } +tooling::Replacements fixNamespaceEndComments(const FormatStyle &Style, + StringRef Code, + ArrayRef Ranges, + StringRef FileName) { + std::unique_ptr Env = + Environment::CreateVirtualEnvironment(Code, FileName, Ranges); + NamespaceEndCommentsFixer Fix(*Env, Style, inputUsesCRLF(Code)); + return Fix.process(); +} + LangOptions getFormattingLangOpts(const FormatStyle &Style) { LangOptions LangOpts; LangOpts.CPlusPlus = 1; Index: lib/Format/NamespaceEndCommentsFixer.h =================================================================== --- /dev/null +++ lib/Format/NamespaceEndCommentsFixer.h @@ -0,0 +1,41 @@ +//===--- NamespaceEndCommentsFixer.h ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief This file declares NamespaceEndCommentsFixer, a TokenAnalyzer that +/// fixes namespace end comments. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_FORMAT_NAMESPACEENDCOMMENTSFIXER_H +#define LLVM_CLANG_LIB_FORMAT_NAMESPACEENDCOMMENTSFIXER_H + +#include "TokenAnalyzer.h" + +namespace clang { +namespace format { + +class NamespaceEndCommentsFixer : public TokenAnalyzer { +public: + NamespaceEndCommentsFixer(const Environment &Env, const FormatStyle &Style, + bool UsesCRLF); + + tooling::Replacements + analyze(TokenAnnotator &Annotator, + SmallVectorImpl &AnnotatedLines, + FormatTokenLexer &Tokens) override; + +private: + bool UsesCRLF; +}; + +} // end namespace format +} // end namespace clang + +#endif Index: lib/Format/NamespaceEndCommentsFixer.cpp =================================================================== --- /dev/null +++ lib/Format/NamespaceEndCommentsFixer.cpp @@ -0,0 +1,300 @@ +//===--- NamespaceEndCommentsFixer.cpp --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief This file implements NamespaceEndCommentsFixer, a TokenAnalyzer that +/// fixes namespace end comments. +/// +//===----------------------------------------------------------------------===// + +#include "NamespaceEndCommentsFixer.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Regex.h" + +#define DEBUG_TYPE "namespace-end-comments-fixer" + +namespace clang { +namespace format { + +namespace { + +// Given a namespace token, computes the sequence of tokens that constitute that +// namespace name. For example, for: +// +// namespace ::A::B { ... } +// +// this function returns [::, A, ::, B]. +static SmallVector +computeNamespaceNameTokens(const FormatToken *NamespaceTok) { + assert(NamespaceTok && NamespaceTok->is(tok::kw_namespace) && + "expecting a namespace token"); + // Matches (modulo comments) an optional '::', followed by a keyword, followed + // by a repetition of: ('::' followed by a keyword): + // (::)? kw (:: kw)* + SmallVector NameToks; + const FormatToken *LastColonColon = nullptr; + const FormatToken *Tok = NamespaceTok->getNextNonComment(); + // Match the first (optional) '::'. + if (Tok->is(tok::coloncolon)) { + LastColonColon = Tok; + Tok = Tok->getNextNonComment(); + } + while (Tok && Tok->is(tok::identifier)) { + // Match a keyword followed by '::'. + if (LastColonColon) { + NameToks.push_back(LastColonColon); + LastColonColon = nullptr; + } + NameToks.push_back(Tok); + Tok = Tok->getNextNonComment(); + if (Tok && Tok->is(tok::coloncolon)) { + LastColonColon = Tok; + Tok = Tok->getNextNonComment(); + } + } + return NameToks; +} + +// The maximal number of lines that a short namespace spans. +// Short namespaces don't need an end comment. +static const int kShortNamespaceMaxLines = 1; + +// Matches a valid namespace end comment. +// Valid namespace end comments don't need to be edited. +static llvm::Regex kNamespaceCommentPattern = + llvm::Regex("^/[/*] *(end (of )?)? *(anonymous|unnamed)? *" + "namespace( +([a-zA-Z0-9:_]+))?\\.? *(\\*/)?$", + llvm::Regex::IgnoreCase); + +static StringRef newline(bool UsesCRLF) { return UsesCRLF ? "\r\n" : "\n"; } + +struct Namespace { + Namespace(const FormatToken *NamespaceToken, const SourceManager &SourceMgr, + bool UsesCRLF) + : NamespaceToken(NamespaceToken), + NamespaceNameTokens(computeNamespaceNameTokens(NamespaceToken)), + SourceMgr(SourceMgr), UsesCRLF(UsesCRLF), RBrace(nullptr), + RBraceLine(nullptr), NextAfterEndComment(nullptr) {} + + bool isAnonymous() const { return NamespaceNameTokens.empty(); } + + bool isShort() const { + int StartLine = + SourceMgr.getSpellingLineNumber(NamespaceToken->Tok.getLocation()); + int EndLine = SourceMgr.getSpellingLineNumber(RBrace->Tok.getLocation()); + return EndLine - StartLine + 1 <= kShortNamespaceMaxLines; + } + + std::string getNamespaceName() const { + std::string name = ""; + for (const FormatToken *Tok : NamespaceNameTokens) { + name += Tok->TokenText; + } + return name; + } + + bool mustAddNewline() const { + assert(NextAfterEndComment); + return NextAfterEndComment->NewlinesBefore == 0 && + NextAfterEndComment->isNot(tok::eof); + } + + std::string getNamespaceEndLineCommentText(int SpacesBefore) const { + std::string text(SpacesBefore, ' '); + text += "// namespace"; + if (isAnonymous()) + return text; + text += ' '; + text += getNamespaceName(); + if (mustAddNewline()) { + text += newline(UsesCRLF); + } + return text; + } + + std::string getNamespaceEndBlockCommentText(int SpacesBefore) const { + std::string text(SpacesBefore, ' '); + text += "/* namespace"; + if (!isAnonymous()) { + text += ' '; + text += getNamespaceName(); + } + text += " */"; + if (mustAddNewline()) { + text += newline(UsesCRLF); + } + return text; + } + + const FormatToken *getNextTokenAfterNamespaceName() const { + const FormatToken *Last = NamespaceNameTokens.empty() + ? NamespaceToken + : NamespaceNameTokens.back(); + return Last->getNextNonComment(); + } + + bool hasEndComment() const { + return RBrace && RBrace->Next && RBrace->Next->is(tok::comment); + } + + bool validEndComment() const { + assert(hasEndComment()); + const FormatToken *Comment = RBrace->Next; + SmallVector Groups; + if (kNamespaceCommentPattern.match(Comment->TokenText, &Groups)) { + StringRef NamespaceNameInComment = Groups.size() > 5 ? Groups[5] : ""; + // Anonymous namespace comments must not mention a namespace name. + if (isAnonymous() && !NamespaceNameInComment.empty()) + return false; + StringRef AnonymousInComment = Groups.size() > 3 ? Groups[3] : ""; + // Named namespace comments must not mention anonymous namespace. + if (!isAnonymous() && !AnonymousInComment.empty()) + return false; + return NamespaceNameInComment == getNamespaceName(); + } + return false; + } + + void addEndComment(tooling::Replacements *Fixes) const { + auto Range = CharSourceRange::getCharRange(RBrace->Tok.getEndLoc(), + RBrace->Tok.getEndLoc()); + auto Err = Fixes->add(tooling::Replacement( + SourceMgr, Range, getNamespaceEndLineCommentText(/*SpacesBefore=*/1))); + if (Err) { + llvm::errs() << "Error while adding namespace end comment: " + << llvm::toString(std::move(Err)) << "\n"; + } + } + + void updateEndComment(tooling::Replacements *Fixes) const { + assert(hasEndComment()); + const FormatToken *Comment = RBrace->Next; + auto Range = CharSourceRange::getCharRange( + Comment->getStartOfNonWhitespace(), Comment->Tok.getEndLoc()); + std::string NewComment = + Comment->is(TT_LineComment) + ? getNamespaceEndLineCommentText(/*SpacesBefore=*/0) + : getNamespaceEndBlockCommentText(/*SpacesBefore=*/0); + auto Err = Fixes->add(tooling::Replacement(SourceMgr, Range, NewComment)); + if (Err) { + llvm::errs() << "Error while updating namespace end comment: " + << llvm::toString(std::move(Err)) << "\n"; + } + } + + void fixEndComment(tooling::Replacements *Fixes) const { + assert(RBrace); + assert(RBraceLine); + if (!RBraceLine->Affected || RBraceLine->InPPDirective) + return; + if (hasEndComment()) { + if (!validEndComment()) { + updateEndComment(Fixes); + } + } else if (!isShort()) { + addEndComment(Fixes); + } + } + + const FormatToken *NamespaceToken; + SmallVector NamespaceNameTokens; + const SourceManager &SourceMgr; + bool UsesCRLF; + const FormatToken *RBrace; + const AnnotatedLine *RBraceLine; + bool RBraceAffected; + const FormatToken *NextAfterEndComment; +}; + +static const FormatToken * +getNextTokenAfterEndComment(const FormatToken *RBrace) { + assert(RBrace); + const FormatToken *Tok = RBrace->Next; + if (Tok && Tok->is(tok::comment)) + Tok = Tok->Next; + return Tok; +} + +struct LBrace { + const FormatToken *LBraceToken; + Namespace *NamespaceOpened; +}; + +void appendNamespaces(SmallVectorImpl &AnnotatedLines, + const SourceManager &SourceMgr, bool UsesCRLF, + SmallVectorImpl> *Namespaces) { + assert(Namespaces); + SmallVector LBraceStack; + Namespace *NamespaceNeedingNext = nullptr; + for (const AnnotatedLine *Line : AnnotatedLines) { + const FormatToken *Tok = Line->First; + assert(Tok); + if (NamespaceNeedingNext) { + assert(!NamespaceNeedingNext->NextAfterEndComment); + NamespaceNeedingNext->NextAfterEndComment = Tok; + NamespaceNeedingNext = nullptr; + } + // Detect "(inline)? namespace" in the beginning of a line. + if (Tok->is(tok::kw_inline)) + Tok = Tok->getNextNonComment(); + if (Tok && Tok->is(tok::kw_namespace)) { + Namespaces->push_back( + llvm::make_unique(Tok, SourceMgr, UsesCRLF)); + Tok = Namespaces->back()->getNextTokenAfterNamespaceName(); + if (Tok && Tok->is(tok::l_brace)) { + // Attach this namespace to the left brace. + LBraceStack.push_back({Tok, Namespaces->back().get()}); + Tok = Tok->getNextNonComment(); + } + } + while (Tok) { + if (Tok->is(tok::l_brace)) { + LBraceStack.push_back({Tok, /*NamespaceOpened=*/nullptr}); + } else if (Tok->is(tok::r_brace) && !LBraceStack.empty()) { + Namespace *LastNamespaceOpenend = LBraceStack.back().NamespaceOpened; + if (LastNamespaceOpenend) { + LastNamespaceOpenend->RBrace = Tok; + LastNamespaceOpenend->RBraceLine = Line; + LastNamespaceOpenend->NextAfterEndComment = + getNextTokenAfterEndComment(Tok); + if (!LastNamespaceOpenend->NextAfterEndComment) { + NamespaceNeedingNext = LastNamespaceOpenend; + } + } + LBraceStack.pop_back(); + } + Tok = Tok->Next; + } + } +} + +} // end anonymous namespace + +NamespaceEndCommentsFixer::NamespaceEndCommentsFixer(const Environment &Env, + const FormatStyle &Style, + bool UsesCRLF) + : TokenAnalyzer(Env, Style), UsesCRLF(UsesCRLF) {} + +tooling::Replacements NamespaceEndCommentsFixer::analyze( + TokenAnnotator &Annotator, SmallVectorImpl &AnnotatedLines, + FormatTokenLexer &Tokens) { + AffectedRangeMgr.computeAffectedLines(AnnotatedLines.begin(), + AnnotatedLines.end()); + tooling::Replacements Fixes; + SmallVector, 2> Namespaces; + appendNamespaces(AnnotatedLines, Env.getSourceManager(), UsesCRLF, + &Namespaces); + for (unsigned I = 0, E = Namespaces.size(); I != E; ++I) { + Namespaces[I]->fixEndComment(&Fixes); + } + return Fixes; +} + +} // end namespace format +} // end namespace clang Index: unittests/Format/CMakeLists.txt =================================================================== --- unittests/Format/CMakeLists.txt +++ unittests/Format/CMakeLists.txt @@ -4,6 +4,7 @@ add_clang_unittest(FormatTests CleanupTest.cpp + FixNamespaceEndCommentsTest.cpp FormatTest.cpp FormatTestComments.cpp FormatTestJava.cpp Index: unittests/Format/FixNamespaceEndCommentsTest.cpp =================================================================== --- /dev/null +++ unittests/Format/FixNamespaceEndCommentsTest.cpp @@ -0,0 +1,324 @@ +//===- FixNamespaceEndCommentsTest.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 "clang/Frontend/TextDiagnosticPrinter.h" +#include "llvm/Support/Debug.h" +#include "gtest/gtest.h" + +#define DEBUG_TYPE "fix-namespace-end-comments-test" + +namespace clang { +namespace format { +namespace { + +class FixNamespaceEndCommentTest : public ::testing::Test { +protected: + std::string fixNamespaceEndComments(llvm::StringRef Code, + std::vector Ranges, + const FormatStyle &Style = getLLVMStyle()) { + DEBUG(llvm::errs() << "---\n"); + DEBUG(llvm::errs() << Code << "\n\n"); + tooling::Replacements Replaces = + clang::format::fixNamespaceEndComments(Style, Code, Ranges, ""); + auto Result = applyAllReplacements(Code, Replaces); + EXPECT_TRUE(static_cast(Result)); + DEBUG(llvm::errs() << "\n" << *Result << "\n\n"); + return *Result; + } + + std::string + fixNamespaceEndComments(llvm::StringRef Code, + const FormatStyle &Style = getLLVMStyle()) { + return fixNamespaceEndComments( + Code, + /*Ranges=*/{1, tooling::Range(0, Code.size())}, Style); + } +}; + +TEST_F(FixNamespaceEndCommentTest, AddsEndComment) { + EXPECT_EQ("namespace {\n" + " int i;\n" + "} // namespace", + fixNamespaceEndComments("namespace {\n" + " int i;\n" + "}")); + EXPECT_EQ("namespace {\n" + " int i;\n" + "} // namespace\n", + fixNamespaceEndComments("namespace {\n" + " int i;\n" + "}\n")); + EXPECT_EQ("namespace A {\n" + " int i;\n" + "} // namespace A", + fixNamespaceEndComments("namespace A {\n" + " int i;\n" + "}")); + EXPECT_EQ("inline namespace A {\n" + " int i;\n" + "} // namespace A", + fixNamespaceEndComments("inline namespace A {\n" + " int i;\n" + "}")); + EXPECT_EQ("namespace ::A {\n" + " int i;\n" + "} // namespace ::A", + fixNamespaceEndComments("namespace ::A {\n" + " int i;\n" + "}")); + EXPECT_EQ("namespace ::A::B {\n" + " int i;\n" + "} // namespace ::A::B", + fixNamespaceEndComments("namespace ::A::B {\n" + " int i;\n" + "}")); + EXPECT_EQ("namespace /**/::/**/A/**/::/**/B/**/ {\n" + " int i;\n" + "} // namespace ::A::B", + fixNamespaceEndComments("namespace /**/::/**/A/**/::/**/B/**/ {\n" + " int i;\n" + "}")); + EXPECT_EQ("namespace A {\n" + "namespace B {\n" + " int i;\n" + "} // namespace B\n" + "} // namespace A", + fixNamespaceEndComments("namespace A {\n" + "namespace B {\n" + " int i;\n" + "}\n" + "}")); + EXPECT_EQ("namespace A {\n" + " int a;\n" + "} // namespace A\n" + "namespace B {\n" + " int b;\n" + "} // namespace B", + fixNamespaceEndComments("namespace A {\n" + " int a;\n" + "}\n" + "namespace B {\n" + " int b;\n" + "}")); + EXPECT_EQ("namespace A {\n" + " int a1\n" + "} // namespace A\n" + "namespace A {\n" + " int a2;\n" + "} // namespace A", + fixNamespaceEndComments("namespace A {\n" + " int a1\n" + "} // namespace A\n" + "namespace A {\n" + " int a2;\n" + "} // namespace A")); + EXPECT_EQ("namespace A {\n" + " int a;\n" + "} // namespace A\n" + "// comment about b\n" + "int b;", + fixNamespaceEndComments("namespace A {\n" + " int a;\n" + "} // namespace A\n" + "// comment about b\n" + "int b;")); + + EXPECT_EQ("namespace A {\n" + "namespace B {\n" + "namespace C {\n" + "namespace D {\n" + "} // namespace D\n" + "} // namespace C\n" + "} // namespace B\n" + "} // namespace A", + fixNamespaceEndComments("namespace A {\n" + "namespace B {\n" + "namespace C {\n" + "namespace D {\n" + "}\n" + "}\n" + "}\n" + "}")); +} + +TEST_F(FixNamespaceEndCommentTest, AddsNewlineIfNeeded) { + EXPECT_EQ("namespace A {\n" + " int i;\n" + "} // namespace A\n" + " int j;", + fixNamespaceEndComments("namespace A {\n" + " int i;\n" + "} int j;")); + EXPECT_EQ("namespace A {\n" + " int i;\n" + "} // namespace A\n" + " namespace B {\n" + " int j;\n" + "} // namespace B", + fixNamespaceEndComments("namespace A {\n" + " int i;\n" + "} namespace B {\n" + " int j;\n" + "}")); + EXPECT_EQ("namespace A {\r\n" + " int i;\r\n" + "} // namespace A\r\n" + " int j;", + fixNamespaceEndComments("namespace A {\r\n" + " int i;\r\n" + "} int j;")); +} + + +TEST_F(FixNamespaceEndCommentTest, DoesNotAddEndCommentForShortNamespace) { + EXPECT_EQ("namespace {}", fixNamespaceEndComments("namespace {}")); + EXPECT_EQ("namespace A {}", fixNamespaceEndComments("namespace A {}")); + EXPECT_EQ("namespace A { int i; }", + fixNamespaceEndComments("namespace A { int i; }")); +} + +TEST_F(FixNamespaceEndCommentTest, DoesNotAddCommentAfterNonNamespaceRBrace) { + EXPECT_EQ("namespace {\n" + "int main() {\n" + " return 0;\n" + "}\n" + "} // namespace", + fixNamespaceEndComments("namespace {\n" + "int main() {\n" + " return 0;\n" + "}\n" + "}")); +} + +TEST_F(FixNamespaceEndCommentTest, DoesNotAddCommentAfterUnaffectedRBrace) { + EXPECT_EQ("namespace A {\n" + " int i;\n" + "}", + fixNamespaceEndComments("namespace A {\n" + " int i;\n" + "}", + // The range (16, 3) spans the 'int' above. + /*Ranges=*/{1, tooling::Range(16, 3)})); +} + +TEST_F(FixNamespaceEndCommentTest, DoesNotAddCommentAfterRBraceInPPDirective) { + EXPECT_EQ("#define SAD \\\n" + "namespace A { \\\n" + " int i; \\\n" + "}", + fixNamespaceEndComments("#define SAD \\\n" + "namespace A { \\\n" + " int i; \\\n" + "}")); +} + +TEST_F(FixNamespaceEndCommentTest, KeepsValidEndComment) { + EXPECT_EQ("namespace {\n" + " int i;\n" + "} // end anonymous namespace", + fixNamespaceEndComments("namespace {\n" + " int i;\n" + "} // end anonymous namespace")); + EXPECT_EQ("namespace A {\n" + " int i;\n" + "} /* end of namespace A */", + fixNamespaceEndComments("namespace A {\n" + " int i;\n" + "} /* end of namespace A */")); + EXPECT_EQ("namespace A {\n" + " int i;\n" + "} // namespace A", + fixNamespaceEndComments("namespace A {\n" + " int i;\n" + "} // namespace A")); + EXPECT_EQ("namespace A::B {\n" + " int i;\n" + "} // end namespace A::B", + fixNamespaceEndComments("namespace A::B {\n" + " int i;\n" + "} // end namespace A::B")); +} + +TEST_F(FixNamespaceEndCommentTest, UpdatesInvalidEndLineComment) { + EXPECT_EQ("namespace {\n" + " int i;\n" + "} // namespace", + fixNamespaceEndComments("namespace {\n" + " int i;\n" + "} // namespace A")); + EXPECT_EQ("namespace A {\n" + " int i;\n" + "} // namespace A", + fixNamespaceEndComments("namespace A {\n" + " int i;\n" + "} // namespace")); + EXPECT_EQ("namespace A {\n" + " int i;\n" + "} // namespace A", + fixNamespaceEndComments("namespace A {\n" + " int i;\n" + "} //")); + EXPECT_EQ("namespace A {\n" + " int i;\n" + "} // namespace A", + fixNamespaceEndComments("namespace A {\n" + " int i;\n" + "} //")); + EXPECT_EQ("namespace A {\n" + " int i;\n" + "} // namespace A", + fixNamespaceEndComments("namespace A {\n" + " int i;\n" + "} // banamespace A")); + + // Updates invalid line comments even for short namespaces. + EXPECT_EQ("namespace A {} // namespace A", + fixNamespaceEndComments("namespace A {} // namespace")); +} + +TEST_F(FixNamespaceEndCommentTest, UpdatesInvalidEndBlockComment) { + EXPECT_EQ("namespace {\n" + " int i;\n" + "} /* namespace */", + fixNamespaceEndComments("namespace {\n" + " int i;\n" + "} /* namespace A */")); + EXPECT_EQ("namespace A {\n" + " int i;\n" + "} /* namespace A */", + fixNamespaceEndComments("namespace A {\n" + " int i;\n" + "} /* end namespace */")); + EXPECT_EQ("namespace A {\n" + " int i;\n" + "} /* namespace A */", + fixNamespaceEndComments("namespace A {\n" + " int i;\n" + "} /**/")); + EXPECT_EQ("namespace A {\n" + " int i;\n" + "} /* namespace A */", + fixNamespaceEndComments("namespace A {\n" + " int i;\n" + "} /* end unnamed namespace */")); + EXPECT_EQ("namespace A {\n" + " int i;\n" + "} /* namespace A */", + fixNamespaceEndComments("namespace A {\n" + " int i;\n" + "} /* banamespace A */")); + EXPECT_EQ("namespace A {} /* namespace A */", + fixNamespaceEndComments("namespace A {} /**/")); +} + +} // end namespace +} // end namespace format +} // end namespace clang