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,66 @@ +//===--- 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; + + const SourceManager &SourceMgr; + + struct NamespaceBlock; + + std::string computeName(const NamespaceBlock &Namespace) const; + + std::string computeEndCommentText(const NamespaceBlock &Namespace) const; + + bool isAnonymous(const NamespaceBlock &Namespace) const; + + bool isShort(const NamespaceBlock &Namespace) const; + + bool hasEndComment(const NamespaceBlock &Namespace) const; + + bool validEndComment(const NamespaceBlock &Namespace) const; + + void addEndComment(const NamespaceBlock &Namespace, + tooling::Replacements *Fixes) const; + + void updateEndComment(const NamespaceBlock &Namespace, + tooling::Replacements *Fixes) const; + + void fixEndComment(const NamespaceBlock &Namespace, + tooling::Replacements *Fixes) const; +}; + +} // end namespace format +} // end namespace clang + +#endif Index: lib/Format/NamespaceEndCommentsFixer.cpp =================================================================== --- /dev/null +++ lib/Format/NamespaceEndCommentsFixer.cpp @@ -0,0 +1,219 @@ +//===--- 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"); + // Collects all the non-comment tokens between 'namespace' and '{'. + SmallVector NameToks; + const FormatToken *Tok = NamespaceTok->getNextNonComment(); + while (Tok && !Tok->is(tok::l_brace)) { + NameToks.push_back(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"; } + +} // namespace + +struct NamespaceEndCommentsFixer::NamespaceBlock { + const FormatToken *NamespaceToken; + SmallVector NamespaceNameTokens; + const FormatToken *RBrace; + const AnnotatedLine *RBraceLine; + const FormatToken *NextAfterEndComment; +}; + +std::string +NamespaceEndCommentsFixer::computeName(const NamespaceBlock &Namespace) const { + std::string name = ""; + for (const FormatToken *Tok : Namespace.NamespaceNameTokens) { + name += Tok->TokenText; + } + return name; +} + +std::string NamespaceEndCommentsFixer::computeEndCommentText( + const NamespaceBlock &Namespace) const { + std::string text = "// namespace"; + if (isAnonymous(Namespace)) + return text; + text += ' '; + text += computeName(Namespace); + const FormatToken *Next = Namespace.NextAfterEndComment; + bool MustAddNewline = + Next && Next->NewlinesBefore == 0 && Next->isNot(tok::eof); + if (MustAddNewline) + text += newline(UsesCRLF); + return text; +} + +bool NamespaceEndCommentsFixer::isAnonymous( + const NamespaceBlock &Namespace) const { + return Namespace.NamespaceNameTokens.empty(); +} + +bool NamespaceEndCommentsFixer::isShort(const NamespaceBlock &Namespace) const { + int StartLine = SourceMgr.getSpellingLineNumber( + Namespace.NamespaceToken->Tok.getLocation()); + int EndLine = + SourceMgr.getSpellingLineNumber(Namespace.RBrace->Tok.getLocation()); + return EndLine - StartLine + 1 <= kShortNamespaceMaxLines; +} + +bool NamespaceEndCommentsFixer::hasEndComment( + const NamespaceBlock &Namespace) const { + return Namespace.RBrace && Namespace.RBrace->Next && + Namespace.RBrace->Next->is(tok::comment); +} + +bool NamespaceEndCommentsFixer::validEndComment( + const NamespaceBlock &Namespace) const { + assert(hasEndComment(Namespace)); + const FormatToken *Comment = Namespace.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(Namespace) && !NamespaceNameInComment.empty()) + return false; + StringRef AnonymousInComment = Groups.size() > 3 ? Groups[3] : ""; + // Named namespace comments must not mention anonymous namespace. + if (!isAnonymous(Namespace) && !AnonymousInComment.empty()) + return false; + return NamespaceNameInComment == computeName(Namespace); + } + return false; +} + +void NamespaceEndCommentsFixer::addEndComment( + const NamespaceBlock &Namespace, tooling::Replacements *Fixes) const { + auto EndLoc = Namespace.RBrace->Tok.getEndLoc(); + auto Range = CharSourceRange::getCharRange(EndLoc, EndLoc); + auto Err = Fixes->add( + tooling::Replacement(SourceMgr, Range, computeEndCommentText(Namespace))); + if (Err) { + llvm::errs() << "Error while adding namespace end comment: " + << llvm::toString(std::move(Err)) << "\n"; + } +} + +void NamespaceEndCommentsFixer::updateEndComment( + const NamespaceBlock &Namespace, tooling::Replacements *Fixes) const { + assert(hasEndComment(Namespace)); + const FormatToken *Comment = Namespace.RBrace->Next; + auto Range = CharSourceRange::getCharRange(Comment->getStartOfNonWhitespace(), + Comment->Tok.getEndLoc()); + auto Err = Fixes->add( + tooling::Replacement(SourceMgr, Range, computeEndCommentText(Namespace))); + if (Err) { + llvm::errs() << "Error while updating namespace end comment: " + << llvm::toString(std::move(Err)) << "\n"; + } +} + +void NamespaceEndCommentsFixer::fixEndComment( + const NamespaceBlock &Namespace, tooling::Replacements *Fixes) const { + assert(Namespace.RBrace); + assert(Namespace.RBraceLine); + if (!Namespace.RBraceLine->Affected || Namespace.RBraceLine->InPPDirective) + return; + if (!hasEndComment(Namespace)) { + if (!isShort(Namespace)) + addEndComment(Namespace, Fixes); + return; + } + if (!validEndComment(Namespace)) + updateEndComment(Namespace, Fixes); +} + +NamespaceEndCommentsFixer::NamespaceEndCommentsFixer(const Environment &Env, + const FormatStyle &Style, + bool UsesCRLF) + : TokenAnalyzer(Env, Style), UsesCRLF(UsesCRLF), + SourceMgr(Env.getSourceManager()) {} + +tooling::Replacements NamespaceEndCommentsFixer::analyze( + TokenAnnotator &Annotator, SmallVectorImpl &AnnotatedLines, + FormatTokenLexer &Tokens) { + AffectedRangeMgr.computeAffectedLines(AnnotatedLines.begin(), + AnnotatedLines.end()); + tooling::Replacements Fixes; + for (size_t I = 0, E = AnnotatedLines.size(); I != E; ++I) { + const AnnotatedLine *ClosingBlockLine = AnnotatedLines[I]; + if (!ClosingBlockLine->Affected) + continue; + size_t OpeningBlockLineIndex = + ClosingBlockLine->MatchingOpeningBlockLineIndex; + if (OpeningBlockLineIndex == UnwrappedLine::kInvalidIndex) + continue; + assert(OpeningBlockLineIndex < E); + const AnnotatedLine *OpeningBlockLine = + AnnotatedLines[OpeningBlockLineIndex]; + const FormatToken *NamespaceTok = OpeningBlockLine->First; + // Detect "(inline)? namespace" in the beginning of a line. + if (NamespaceTok->is(tok::kw_inline)) + NamespaceTok = NamespaceTok->getNextNonComment(); + if (NamespaceTok->isNot(tok::kw_namespace)) + continue; + const FormatToken *RBraceTok = ClosingBlockLine->First; + if (RBraceTok->isNot(tok::r_brace)) + continue; + NamespaceBlock Namespace; + Namespace.NamespaceToken = NamespaceTok; + Namespace.NamespaceNameTokens = computeNamespaceNameTokens(NamespaceTok); + Namespace.RBrace = RBraceTok; + Namespace.RBraceLine = ClosingBlockLine; + if (I + 1 < E) { + Namespace.NextAfterEndComment = AnnotatedLines[I + 1]->First; + } + fixEndComment(Namespace, &Fixes); + } + return Fixes; +} + +} // end namespace format +} // end namespace clang Index: lib/Format/TokenAnnotator.h =================================================================== --- lib/Format/TokenAnnotator.h +++ lib/Format/TokenAnnotator.h @@ -39,6 +39,7 @@ public: AnnotatedLine(const UnwrappedLine &Line) : First(Line.Tokens.front().Tok), Level(Line.Level), + MatchingOpeningBlockLineIndex(Line.MatchingOpeningBlockLineIndex), InPPDirective(Line.InPPDirective), MustBeDeclaration(Line.MustBeDeclaration), MightBeFunctionDecl(false), IsMultiVariableDeclStmt(false), Affected(false), @@ -109,6 +110,7 @@ LineType Type; unsigned Level; + size_t MatchingOpeningBlockLineIndex; bool InPPDirective; bool MustBeDeclaration; bool MightBeFunctionDecl; Index: lib/Format/UnwrappedLineParser.h =================================================================== --- lib/Format/UnwrappedLineParser.h +++ lib/Format/UnwrappedLineParser.h @@ -48,6 +48,14 @@ bool InPPDirective; bool MustBeDeclaration; + + /// \brief If this \c UnwrappedLine closes a block in a sequence of lines, + /// \c MatchingOpeningBlockLineIndex stores the index of the corresponding + /// opening line. Otherwise, \c MatchingOpeningBlockLineIndex must be + /// \c kInvalidIndex. + size_t MatchingOpeningBlockLineIndex; + + static const size_t kInvalidIndex = -1; }; class UnwrappedLineConsumer { @@ -234,8 +242,8 @@ SmallVector Children; }; -inline UnwrappedLine::UnwrappedLine() - : Level(0), InPPDirective(false), MustBeDeclaration(false) {} +inline UnwrappedLine::UnwrappedLine() : Level(0), InPPDirective(false), + MustBeDeclaration(false), MatchingOpeningBlockLineIndex(kInvalidIndex) {} } // end namespace format } // end namespace clang Index: lib/Format/UnwrappedLineParser.cpp =================================================================== --- lib/Format/UnwrappedLineParser.cpp +++ lib/Format/UnwrappedLineParser.cpp @@ -428,6 +428,8 @@ parseParens(); addUnwrappedLine(); + size_t OpeningLineIndex = + Lines.empty() ? (UnwrappedLine::kInvalidIndex) : (Lines.size() - 1); ScopedDeclarationState DeclarationState(*Line, DeclarationScopeStack, MustBeDeclaration); @@ -453,6 +455,7 @@ if (MunchSemi && FormatTok->Tok.is(tok::semi)) nextToken(); Line->Level = InitialLevel; + Line->MatchingOpeningBlockLineIndex = OpeningLineIndex; } static bool isGoogScope(const UnwrappedLine &Line) { Index: unittests/Format/CMakeLists.txt =================================================================== --- unittests/Format/CMakeLists.txt +++ unittests/Format/CMakeLists.txt @@ -6,11 +6,12 @@ CleanupTest.cpp FormatTest.cpp FormatTestComments.cpp - FormatTestJava.cpp FormatTestJS.cpp + FormatTestJava.cpp FormatTestObjC.cpp FormatTestProto.cpp FormatTestSelective.cpp + NamespaceEndCommentsFixerTest.cpp SortImportsTestJS.cpp SortIncludesTest.cpp ) Index: unittests/Format/NamespaceEndCommentsFixerTest.cpp =================================================================== --- /dev/null +++ unittests/Format/NamespaceEndCommentsFixerTest.cpp @@ -0,0 +1,350 @@ +//===- NamespaceEndCommentsFixerTest.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 "namespace-end-comments-fixer-test" + +namespace clang { +namespace format { +namespace { + +class NamespaceEndCommentsFixerTest : 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(NamespaceEndCommentsFixerTest, 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" + "}\n" + "namespace A {\n" + " int a2;\n" + "}")); + EXPECT_EQ("namespace A {\n" + " int a;\n" + "}// namespace A\n" + "// comment about b\n" + "int b;", + fixNamespaceEndComments("namespace A {\n" + " int a;\n" + "}\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(NamespaceEndCommentsFixerTest, 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(NamespaceEndCommentsFixerTest, 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(NamespaceEndCommentsFixerTest, 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(NamespaceEndCommentsFixerTest, DoesNotAddCommentAfterRBraceInPPDirective) { + EXPECT_EQ("#define SAD \\\n" + "namespace A { \\\n" + " int i; \\\n" + "}", + fixNamespaceEndComments("#define SAD \\\n" + "namespace A { \\\n" + " int i; \\\n" + "}")); +} + +TEST_F(NamespaceEndCommentsFixerTest, 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(NamespaceEndCommentsFixerTest, 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(NamespaceEndCommentsFixerTest, 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 {} /**/")); +} + +TEST_F(NamespaceEndCommentsFixerTest, + DoesNotAddEndCommentForNamespacesControlledByMacros) { + EXPECT_EQ("#ifdef 1\n" + "namespace A {\n" + "#elseif\n" + "namespace B {\n" + "#endif\n" + " int i;\n" + "}\n" + "}\n", + fixNamespaceEndComments("#ifdef 1\n" + "namespace A {\n" + "#elseif\n" + "namespace B {\n" + "#endif\n" + " int i;\n" + "}\n" + "}\n")); +} + +TEST_F(NamespaceEndCommentsFixerTest, + DoesNotAddEndCommentForNamespacesInMacroDeclarations) { + EXPECT_EQ("#ifdef 1\n" + "namespace A {\n" + "#elseif\n" + "namespace B {\n" + "#endif\n" + " int i;\n" + "}\n" + "}\n", + fixNamespaceEndComments("#ifdef 1\n" + "namespace A {\n" + "#elseif\n" + "namespace B {\n" + "#endif\n" + " int i;\n" + "}\n" + "}\n")); +} +} // end namespace +} // end namespace format +} // end namespace clang