xref: /llvm-project/clang/lib/Format/NamespaceEndCommentsFixer.cpp (revision b2082a98175b0e5356f23bf21d3dc5b76edea390)
17cb267afSKrasimir Georgiev //===--- NamespaceEndCommentsFixer.cpp --------------------------*- C++ -*-===//
27cb267afSKrasimir Georgiev //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
67cb267afSKrasimir Georgiev //
77cb267afSKrasimir Georgiev //===----------------------------------------------------------------------===//
87cb267afSKrasimir Georgiev ///
97cb267afSKrasimir Georgiev /// \file
109fc8faf9SAdrian Prantl /// This file implements NamespaceEndCommentsFixer, a TokenAnalyzer that
117cb267afSKrasimir Georgiev /// fixes namespace end comments.
127cb267afSKrasimir Georgiev ///
137cb267afSKrasimir Georgiev //===----------------------------------------------------------------------===//
147cb267afSKrasimir Georgiev 
157cb267afSKrasimir Georgiev #include "NamespaceEndCommentsFixer.h"
16*b2082a98SOwen Pan #include "clang/Basic/TokenKinds.h"
17*b2082a98SOwen Pan #include "llvm/Support/Debug.h"
18*b2082a98SOwen Pan #include "llvm/Support/Regex.h"
197cb267afSKrasimir Georgiev 
207cb267afSKrasimir Georgiev #define DEBUG_TYPE "namespace-end-comments-fixer"
217cb267afSKrasimir Georgiev 
227cb267afSKrasimir Georgiev namespace clang {
237cb267afSKrasimir Georgiev namespace format {
247cb267afSKrasimir Georgiev 
257cb267afSKrasimir Georgiev namespace {
26d54c4df3SZequan Wu // Iterates all tokens starting from StartTok to EndTok and apply Fn to all
27d54c4df3SZequan Wu // tokens between them including StartTok and EndTok. Returns the token after
28d54c4df3SZequan Wu // EndTok.
29d54c4df3SZequan Wu const FormatToken *
processTokens(const FormatToken * Tok,tok::TokenKind StartTok,tok::TokenKind EndTok,llvm::function_ref<void (const FormatToken *)> Fn)30d54c4df3SZequan Wu processTokens(const FormatToken *Tok, tok::TokenKind StartTok,
31d54c4df3SZequan Wu               tok::TokenKind EndTok,
32d54c4df3SZequan Wu               llvm::function_ref<void(const FormatToken *)> Fn) {
33d54c4df3SZequan Wu   if (!Tok || Tok->isNot(StartTok))
34d54c4df3SZequan Wu     return Tok;
35d54c4df3SZequan Wu   int NestLevel = 0;
36d54c4df3SZequan Wu   do {
37d54c4df3SZequan Wu     if (Tok->is(StartTok))
38d54c4df3SZequan Wu       ++NestLevel;
39d54c4df3SZequan Wu     else if (Tok->is(EndTok))
40d54c4df3SZequan Wu       --NestLevel;
41d54c4df3SZequan Wu     if (Fn)
42d54c4df3SZequan Wu       Fn(Tok);
43d54c4df3SZequan Wu     Tok = Tok->getNextNonComment();
44d54c4df3SZequan Wu   } while (Tok && NestLevel > 0);
45d54c4df3SZequan Wu   return Tok;
46d54c4df3SZequan Wu }
47d54c4df3SZequan Wu 
skipAttribute(const FormatToken * Tok)48d54c4df3SZequan Wu const FormatToken *skipAttribute(const FormatToken *Tok) {
49d54c4df3SZequan Wu   if (!Tok)
50d54c4df3SZequan Wu     return nullptr;
51151d0a4dSOwen Pan   if (Tok->isAttribute()) {
52d54c4df3SZequan Wu     Tok = Tok->getNextNonComment();
53d54c4df3SZequan Wu     Tok = processTokens(Tok, tok::l_paren, tok::r_paren, nullptr);
54d54c4df3SZequan Wu   } else if (Tok->is(tok::l_square)) {
55d54c4df3SZequan Wu     Tok = processTokens(Tok, tok::l_square, tok::r_square, nullptr);
56d54c4df3SZequan Wu   }
57d54c4df3SZequan Wu   return Tok;
58d54c4df3SZequan Wu }
59d54c4df3SZequan Wu 
607cb267afSKrasimir Georgiev // Computes the name of a namespace given the namespace token.
617cb267afSKrasimir Georgiev // Returns "" for anonymous namespace.
computeName(const FormatToken * NamespaceTok)627cb267afSKrasimir Georgiev std::string computeName(const FormatToken *NamespaceTok) {
63e8a301f8SFrancois Ferrand   assert(NamespaceTok &&
64e8a301f8SFrancois Ferrand          NamespaceTok->isOneOf(tok::kw_namespace, TT_NamespaceMacro) &&
657cb267afSKrasimir Georgiev          "expecting a namespace token");
660542d152SKazu Hirata   std::string name;
677cb267afSKrasimir Georgiev   const FormatToken *Tok = NamespaceTok->getNextNonComment();
68e8a301f8SFrancois Ferrand   if (NamespaceTok->is(TT_NamespaceMacro)) {
69e8a301f8SFrancois Ferrand     // Collects all the non-comment tokens between opening parenthesis
7037944130SNico Weber     // and closing parenthesis or comma.
71e8a301f8SFrancois Ferrand     assert(Tok && Tok->is(tok::l_paren) && "expected an opening parenthesis");
72e8a301f8SFrancois Ferrand     Tok = Tok->getNextNonComment();
73e8a301f8SFrancois Ferrand     while (Tok && !Tok->isOneOf(tok::r_paren, tok::comma)) {
74e8a301f8SFrancois Ferrand       name += Tok->TokenText;
75e8a301f8SFrancois Ferrand       Tok = Tok->getNextNonComment();
76e8a301f8SFrancois Ferrand     }
77d54c4df3SZequan Wu     return name;
78be570576SZequan Wu   }
79d54c4df3SZequan Wu   Tok = skipAttribute(Tok);
80be570576SZequan Wu 
81be570576SZequan Wu   std::string FirstNSName;
8237944130SNico Weber   // For `namespace [[foo]] A::B::inline C {` or
8337944130SNico Weber   // `namespace MACRO1 MACRO2 A::B::inline C {`, returns "A::B::inline C".
84be570576SZequan Wu   // Peek for the first '::' (or '{' or '(')) and then return all tokens from
85be570576SZequan Wu   // one token before that up until the '{'. A '(' might be a macro with
86be570576SZequan Wu   // arguments.
87d54c4df3SZequan Wu   const FormatToken *FirstNSTok = nullptr;
88be570576SZequan Wu   while (Tok && !Tok->isOneOf(tok::l_brace, tok::coloncolon, tok::l_paren)) {
89d54c4df3SZequan Wu     if (FirstNSTok)
90be570576SZequan Wu       FirstNSName += FirstNSTok->TokenText;
9137944130SNico Weber     FirstNSTok = Tok;
9237944130SNico Weber     Tok = Tok->getNextNonComment();
9337944130SNico Weber   }
9437944130SNico Weber 
95d54c4df3SZequan Wu   if (FirstNSTok)
9637944130SNico Weber     Tok = FirstNSTok;
97d54c4df3SZequan Wu   Tok = skipAttribute(Tok);
98d54c4df3SZequan Wu 
99d54c4df3SZequan Wu   FirstNSTok = nullptr;
100d54c4df3SZequan Wu   // Add everything from '(' to ')'.
101d54c4df3SZequan Wu   auto AddToken = [&name](const FormatToken *Tok) { name += Tok->TokenText; };
102d54c4df3SZequan Wu   bool IsPrevColoncolon = false;
103d54c4df3SZequan Wu   bool HasColoncolon = false;
104d54c4df3SZequan Wu   bool IsPrevInline = false;
105d54c4df3SZequan Wu   bool NameFinished = false;
106d54c4df3SZequan Wu   // If we found '::' in name, then it's the name. Otherwise, we can't tell
107d54c4df3SZequan Wu   // which one is name. For example, `namespace A B {`.
108d54c4df3SZequan Wu   while (Tok && Tok->isNot(tok::l_brace)) {
109d54c4df3SZequan Wu     if (FirstNSTok) {
110d54c4df3SZequan Wu       if (!IsPrevInline && HasColoncolon && !IsPrevColoncolon) {
111d54c4df3SZequan Wu         if (FirstNSTok->is(tok::l_paren)) {
112d54c4df3SZequan Wu           FirstNSTok = Tok =
113d54c4df3SZequan Wu               processTokens(FirstNSTok, tok::l_paren, tok::r_paren, AddToken);
114d54c4df3SZequan Wu           continue;
115d54c4df3SZequan Wu         }
116d54c4df3SZequan Wu         if (FirstNSTok->isNot(tok::coloncolon)) {
117d54c4df3SZequan Wu           NameFinished = true;
118d54c4df3SZequan Wu           break;
119d54c4df3SZequan Wu         }
120d54c4df3SZequan Wu       }
121d54c4df3SZequan Wu       name += FirstNSTok->TokenText;
122d54c4df3SZequan Wu       IsPrevColoncolon = FirstNSTok->is(tok::coloncolon);
123d54c4df3SZequan Wu       HasColoncolon = HasColoncolon || IsPrevColoncolon;
124d54c4df3SZequan Wu       if (FirstNSTok->is(tok::kw_inline)) {
12537944130SNico Weber         name += " ";
126d54c4df3SZequan Wu         IsPrevInline = true;
127d54c4df3SZequan Wu       }
128d54c4df3SZequan Wu     }
129d54c4df3SZequan Wu     FirstNSTok = Tok;
1307cb267afSKrasimir Georgiev     Tok = Tok->getNextNonComment();
131d54c4df3SZequan Wu     const FormatToken *TokAfterAttr = skipAttribute(Tok);
132d54c4df3SZequan Wu     if (TokAfterAttr != Tok)
133d54c4df3SZequan Wu       FirstNSTok = Tok = TokAfterAttr;
1347cb267afSKrasimir Georgiev   }
135d54c4df3SZequan Wu   if (!NameFinished && FirstNSTok && FirstNSTok->isNot(tok::l_brace))
136d54c4df3SZequan Wu     name += FirstNSTok->TokenText;
137d54c4df3SZequan Wu   if (FirstNSName.empty() || HasColoncolon)
1387cb267afSKrasimir Georgiev     return name;
139d54c4df3SZequan Wu   return name.empty() ? FirstNSName : FirstNSName + " " + name;
1407cb267afSKrasimir Georgiev }
1417cb267afSKrasimir Georgiev 
computeEndCommentText(StringRef NamespaceName,bool AddNewline,const FormatToken * NamespaceTok,unsigned SpacesToAdd)142e8a301f8SFrancois Ferrand std::string computeEndCommentText(StringRef NamespaceName, bool AddNewline,
143772eb24eSBjörn Schäpers                                   const FormatToken *NamespaceTok,
144772eb24eSBjörn Schäpers                                   unsigned SpacesToAdd) {
145e8a301f8SFrancois Ferrand   std::string text = "//";
146772eb24eSBjörn Schäpers   text.append(SpacesToAdd, ' ');
147e8a301f8SFrancois Ferrand   text += NamespaceTok->TokenText;
148e8a301f8SFrancois Ferrand   if (NamespaceTok->is(TT_NamespaceMacro))
149e8a301f8SFrancois Ferrand     text += "(";
150e8a301f8SFrancois Ferrand   else if (!NamespaceName.empty())
1517cb267afSKrasimir Georgiev     text += ' ';
1527cb267afSKrasimir Georgiev   text += NamespaceName;
153e8a301f8SFrancois Ferrand   if (NamespaceTok->is(TT_NamespaceMacro))
154e8a301f8SFrancois Ferrand     text += ")";
1557cb267afSKrasimir Georgiev   if (AddNewline)
1567cb267afSKrasimir Georgiev     text += '\n';
1577cb267afSKrasimir Georgiev   return text;
1587cb267afSKrasimir Georgiev }
1597cb267afSKrasimir Georgiev 
hasEndComment(const FormatToken * RBraceTok)1607cb267afSKrasimir Georgiev bool hasEndComment(const FormatToken *RBraceTok) {
1617cb267afSKrasimir Georgiev   return RBraceTok->Next && RBraceTok->Next->is(tok::comment);
1627cb267afSKrasimir Georgiev }
1637cb267afSKrasimir Georgiev 
validEndComment(const FormatToken * RBraceTok,StringRef NamespaceName,const FormatToken * NamespaceTok)164e8a301f8SFrancois Ferrand bool validEndComment(const FormatToken *RBraceTok, StringRef NamespaceName,
165e8a301f8SFrancois Ferrand                      const FormatToken *NamespaceTok) {
1667cb267afSKrasimir Georgiev   assert(hasEndComment(RBraceTok));
1677cb267afSKrasimir Georgiev   const FormatToken *Comment = RBraceTok->Next;
1688ef820a6SBenjamin Kramer 
1698ef820a6SBenjamin Kramer   // Matches a valid namespace end comment.
1708ef820a6SBenjamin Kramer   // Valid namespace end comments don't need to be edited.
171b81cc603SThomas Preud'homme   static const llvm::Regex NamespaceCommentPattern =
172b81cc603SThomas Preud'homme       llvm::Regex("^/[/*] *(end (of )?)? *(anonymous|unnamed)? *"
1738ef820a6SBenjamin Kramer                   "namespace( +([a-zA-Z0-9:_ ]+))?\\.? *(\\*/)?$",
1748ef820a6SBenjamin Kramer                   llvm::Regex::IgnoreCase);
175b81cc603SThomas Preud'homme   static const llvm::Regex NamespaceMacroCommentPattern =
176b81cc603SThomas Preud'homme       llvm::Regex("^/[/*] *(end (of )?)? *(anonymous|unnamed)? *"
177063c42e9SOwen Pan                   "([a-zA-Z0-9_]+)\\(([a-zA-Z0-9:_]*|\".+\")\\)\\.? *(\\*/)?$",
178e8a301f8SFrancois Ferrand                   llvm::Regex::IgnoreCase);
179e8a301f8SFrancois Ferrand 
180e8a301f8SFrancois Ferrand   SmallVector<StringRef, 8> Groups;
181e8a301f8SFrancois Ferrand   if (NamespaceTok->is(TT_NamespaceMacro) &&
182b81cc603SThomas Preud'homme       NamespaceMacroCommentPattern.match(Comment->TokenText, &Groups)) {
183e8a301f8SFrancois Ferrand     StringRef NamespaceTokenText = Groups.size() > 4 ? Groups[4] : "";
184e8a301f8SFrancois Ferrand     // The name of the macro must be used.
185e8a301f8SFrancois Ferrand     if (NamespaceTokenText != NamespaceTok->TokenText)
186e8a301f8SFrancois Ferrand       return false;
187e8a301f8SFrancois Ferrand   } else if (NamespaceTok->isNot(tok::kw_namespace) ||
188b81cc603SThomas Preud'homme              !NamespaceCommentPattern.match(Comment->TokenText, &Groups)) {
189e8a301f8SFrancois Ferrand     // Comment does not match regex.
190e8a301f8SFrancois Ferrand     return false;
191e8a301f8SFrancois Ferrand   }
192f2c97ffeSOwen Pan   StringRef NamespaceNameInComment = Groups.size() > 5 ? Groups[5].rtrim() : "";
1937cb267afSKrasimir Georgiev   // Anonymous namespace comments must not mention a namespace name.
1947cb267afSKrasimir Georgiev   if (NamespaceName.empty() && !NamespaceNameInComment.empty())
1957cb267afSKrasimir Georgiev     return false;
1967cb267afSKrasimir Georgiev   StringRef AnonymousInComment = Groups.size() > 3 ? Groups[3] : "";
1977cb267afSKrasimir Georgiev   // Named namespace comments must not mention anonymous namespace.
1987cb267afSKrasimir Georgiev   if (!NamespaceName.empty() && !AnonymousInComment.empty())
1997cb267afSKrasimir Georgiev     return false;
200e8ea35e6Smydeveloperday   if (NamespaceNameInComment == NamespaceName)
201e8ea35e6Smydeveloperday     return true;
202e8ea35e6Smydeveloperday 
203e8ea35e6Smydeveloperday   // Has namespace comment flowed onto the next line.
204e8ea35e6Smydeveloperday   // } // namespace
205e8ea35e6Smydeveloperday   //   // verylongnamespacenamethatdidnotfitonthepreviouscommentline
206e8ea35e6Smydeveloperday   if (!(Comment->Next && Comment->Next->is(TT_LineComment)))
207e8ea35e6Smydeveloperday     return false;
208e8ea35e6Smydeveloperday 
209e8ea35e6Smydeveloperday   static const llvm::Regex CommentPattern = llvm::Regex(
210e8ea35e6Smydeveloperday       "^/[/*] *( +([a-zA-Z0-9:_]+))?\\.? *(\\*/)?$", llvm::Regex::IgnoreCase);
211e8ea35e6Smydeveloperday 
212e8ea35e6Smydeveloperday   // Pull out just the comment text.
213d079995dSMarek Kurdej   if (!CommentPattern.match(Comment->Next->TokenText, &Groups))
214e8ea35e6Smydeveloperday     return false;
215e8ea35e6Smydeveloperday   NamespaceNameInComment = Groups.size() > 2 ? Groups[2] : "";
216e8ea35e6Smydeveloperday 
2177d5062c6SMarek Kurdej   return NamespaceNameInComment == NamespaceName;
2187cb267afSKrasimir Georgiev }
2197cb267afSKrasimir Georgiev 
addEndComment(const FormatToken * RBraceTok,StringRef EndCommentText,const SourceManager & SourceMgr,tooling::Replacements * Fixes)2207cb267afSKrasimir Georgiev void addEndComment(const FormatToken *RBraceTok, StringRef EndCommentText,
2217cb267afSKrasimir Georgiev                    const SourceManager &SourceMgr,
2227cb267afSKrasimir Georgiev                    tooling::Replacements *Fixes) {
2237cb267afSKrasimir Georgiev   auto EndLoc = RBraceTok->Tok.getEndLoc();
2247cb267afSKrasimir Georgiev   auto Range = CharSourceRange::getCharRange(EndLoc, EndLoc);
2257cb267afSKrasimir Georgiev   auto Err = Fixes->add(tooling::Replacement(SourceMgr, Range, EndCommentText));
2267cb267afSKrasimir Georgiev   if (Err) {
2277cb267afSKrasimir Georgiev     llvm::errs() << "Error while adding namespace end comment: "
2287cb267afSKrasimir Georgiev                  << llvm::toString(std::move(Err)) << "\n";
2297cb267afSKrasimir Georgiev   }
2307cb267afSKrasimir Georgiev }
2317cb267afSKrasimir Georgiev 
updateEndComment(const FormatToken * RBraceTok,StringRef EndCommentText,const SourceManager & SourceMgr,tooling::Replacements * Fixes)2327cb267afSKrasimir Georgiev void updateEndComment(const FormatToken *RBraceTok, StringRef EndCommentText,
2337cb267afSKrasimir Georgiev                       const SourceManager &SourceMgr,
2347cb267afSKrasimir Georgiev                       tooling::Replacements *Fixes) {
2357cb267afSKrasimir Georgiev   assert(hasEndComment(RBraceTok));
2367cb267afSKrasimir Georgiev   const FormatToken *Comment = RBraceTok->Next;
2377cb267afSKrasimir Georgiev   auto Range = CharSourceRange::getCharRange(Comment->getStartOfNonWhitespace(),
2387cb267afSKrasimir Georgiev                                              Comment->Tok.getEndLoc());
2397cb267afSKrasimir Georgiev   auto Err = Fixes->add(tooling::Replacement(SourceMgr, Range, EndCommentText));
2407cb267afSKrasimir Georgiev   if (Err) {
2417cb267afSKrasimir Georgiev     llvm::errs() << "Error while updating namespace end comment: "
2427cb267afSKrasimir Georgiev                  << llvm::toString(std::move(Err)) << "\n";
2437cb267afSKrasimir Georgiev   }
2447cb267afSKrasimir Georgiev }
24562103052SKrasimir Georgiev } // namespace
246e56a829eSFrancois Ferrand 
247e56a829eSFrancois Ferrand const FormatToken *
getNamespaceToken(const AnnotatedLine * Line,const SmallVectorImpl<AnnotatedLine * > & AnnotatedLines)2486bbc7069SKrasimir Georgiev getNamespaceToken(const AnnotatedLine *Line,
249e56a829eSFrancois Ferrand                   const SmallVectorImpl<AnnotatedLine *> &AnnotatedLines) {
2506bbc7069SKrasimir Georgiev   if (!Line->Affected || Line->InPPDirective || !Line->startsWith(tok::r_brace))
251e56a829eSFrancois Ferrand     return nullptr;
2526bbc7069SKrasimir Georgiev   size_t StartLineIndex = Line->MatchingOpeningBlockLineIndex;
253e56a829eSFrancois Ferrand   if (StartLineIndex == UnwrappedLine::kInvalidIndex)
254e56a829eSFrancois Ferrand     return nullptr;
255e56a829eSFrancois Ferrand   assert(StartLineIndex < AnnotatedLines.size());
256e56a829eSFrancois Ferrand   const FormatToken *NamespaceTok = AnnotatedLines[StartLineIndex]->First;
257caf6fd51SMarek Kurdej   if (NamespaceTok->is(tok::l_brace)) {
258caf6fd51SMarek Kurdej     // "namespace" keyword can be on the line preceding '{', e.g. in styles
259caf6fd51SMarek Kurdej     // where BraceWrapping.AfterNamespace is true.
2606482383eSmydeveloperday     if (StartLineIndex > 0) {
261caf6fd51SMarek Kurdej       NamespaceTok = AnnotatedLines[StartLineIndex - 1]->First;
2626482383eSmydeveloperday       if (AnnotatedLines[StartLineIndex - 1]->endsWith(tok::semi))
2636482383eSmydeveloperday         return nullptr;
264caf6fd51SMarek Kurdej     }
2656482383eSmydeveloperday   }
2666482383eSmydeveloperday 
2676f3778c3SSam McCall   return NamespaceTok->getNamespaceToken();
268e56a829eSFrancois Ferrand }
2697cb267afSKrasimir Georgiev 
270e8a301f8SFrancois Ferrand StringRef
getNamespaceTokenText(const AnnotatedLine * Line,const SmallVectorImpl<AnnotatedLine * > & AnnotatedLines)271e8a301f8SFrancois Ferrand getNamespaceTokenText(const AnnotatedLine *Line,
272e8a301f8SFrancois Ferrand                       const SmallVectorImpl<AnnotatedLine *> &AnnotatedLines) {
273e8a301f8SFrancois Ferrand   const FormatToken *NamespaceTok = getNamespaceToken(Line, AnnotatedLines);
274e8a301f8SFrancois Ferrand   return NamespaceTok ? NamespaceTok->TokenText : StringRef();
275e8a301f8SFrancois Ferrand }
276e8a301f8SFrancois Ferrand 
NamespaceEndCommentsFixer(const Environment & Env,const FormatStyle & Style)2777cb267afSKrasimir Georgiev NamespaceEndCommentsFixer::NamespaceEndCommentsFixer(const Environment &Env,
2787cb267afSKrasimir Georgiev                                                      const FormatStyle &Style)
2797cb267afSKrasimir Georgiev     : TokenAnalyzer(Env, Style) {}
2807cb267afSKrasimir Georgiev 
analyze(TokenAnnotator & Annotator,SmallVectorImpl<AnnotatedLine * > & AnnotatedLines,FormatTokenLexer & Tokens)2819ad83fe7SKrasimir Georgiev std::pair<tooling::Replacements, unsigned> NamespaceEndCommentsFixer::analyze(
2827cb267afSKrasimir Georgiev     TokenAnnotator &Annotator, SmallVectorImpl<AnnotatedLine *> &AnnotatedLines,
2837cb267afSKrasimir Georgiev     FormatTokenLexer &Tokens) {
2847cb267afSKrasimir Georgiev   const SourceManager &SourceMgr = Env.getSourceManager();
2850dddcf78SManuel Klimek   AffectedRangeMgr.computeAffectedLines(AnnotatedLines);
2867cb267afSKrasimir Georgiev   tooling::Replacements Fixes;
28750bdd607Smydeveloperday 
28850bdd607Smydeveloperday   // Spin through the lines and ensure we have balanced braces.
28950bdd607Smydeveloperday   int Braces = 0;
290545317cbSMarek Kurdej   for (AnnotatedLine *Line : AnnotatedLines) {
291545317cbSMarek Kurdej     FormatToken *Tok = Line->First;
29250bdd607Smydeveloperday     while (Tok) {
29350bdd607Smydeveloperday       Braces += Tok->is(tok::l_brace) ? 1 : Tok->is(tok::r_brace) ? -1 : 0;
29450bdd607Smydeveloperday       Tok = Tok->Next;
29550bdd607Smydeveloperday     }
29650bdd607Smydeveloperday   }
29750bdd607Smydeveloperday   // Don't attempt to comment unbalanced braces or this can
29850bdd607Smydeveloperday   // lead to comments being placed on the closing brace which isn't
29950bdd607Smydeveloperday   // the matching brace of the namespace. (occurs during incomplete editing).
300d079995dSMarek Kurdej   if (Braces != 0)
30150bdd607Smydeveloperday     return {Fixes, 0};
30250bdd607Smydeveloperday 
3030542d152SKazu Hirata   std::string AllNamespaceNames;
304e56a829eSFrancois Ferrand   size_t StartLineIndex = SIZE_MAX;
305e8a301f8SFrancois Ferrand   StringRef NamespaceTokenText;
306e56a829eSFrancois Ferrand   unsigned int CompactedNamespacesCount = 0;
3077cb267afSKrasimir Georgiev   for (size_t I = 0, E = AnnotatedLines.size(); I != E; ++I) {
3087cb267afSKrasimir Georgiev     const AnnotatedLine *EndLine = AnnotatedLines[I];
309e56a829eSFrancois Ferrand     const FormatToken *NamespaceTok =
310e56a829eSFrancois Ferrand         getNamespaceToken(EndLine, AnnotatedLines);
311e56a829eSFrancois Ferrand     if (!NamespaceTok)
3127cb267afSKrasimir Georgiev       continue;
313bda77397SKrasimir Georgiev     FormatToken *RBraceTok = EndLine->First;
314bda77397SKrasimir Georgiev     if (RBraceTok->Finalized)
315bda77397SKrasimir Georgiev       continue;
316bda77397SKrasimir Georgiev     RBraceTok->Finalized = true;
317eb62118eSKrasimir Georgiev     const FormatToken *EndCommentPrevTok = RBraceTok;
318eb62118eSKrasimir Georgiev     // Namespaces often end with '};'. In that case, attach namespace end
319eb62118eSKrasimir Georgiev     // comments to the semicolon tokens.
320d079995dSMarek Kurdej     if (RBraceTok->Next && RBraceTok->Next->is(tok::semi))
321eb62118eSKrasimir Georgiev       EndCommentPrevTok = RBraceTok->Next;
322e56a829eSFrancois Ferrand     if (StartLineIndex == SIZE_MAX)
323e56a829eSFrancois Ferrand       StartLineIndex = EndLine->MatchingOpeningBlockLineIndex;
324e56a829eSFrancois Ferrand     std::string NamespaceName = computeName(NamespaceTok);
325e56a829eSFrancois Ferrand     if (Style.CompactNamespaces) {
326e8a301f8SFrancois Ferrand       if (CompactedNamespacesCount == 0)
327e8a301f8SFrancois Ferrand         NamespaceTokenText = NamespaceTok->TokenText;
328e56a829eSFrancois Ferrand       if ((I + 1 < E) &&
329e8a301f8SFrancois Ferrand           NamespaceTokenText ==
330e8a301f8SFrancois Ferrand               getNamespaceTokenText(AnnotatedLines[I + 1], AnnotatedLines) &&
331e56a829eSFrancois Ferrand           StartLineIndex - CompactedNamespacesCount - 1 ==
332e56a829eSFrancois Ferrand               AnnotatedLines[I + 1]->MatchingOpeningBlockLineIndex &&
333e56a829eSFrancois Ferrand           !AnnotatedLines[I + 1]->First->Finalized) {
334e56a829eSFrancois Ferrand         if (hasEndComment(EndCommentPrevTok)) {
335e56a829eSFrancois Ferrand           // remove end comment, it will be merged in next one
336e56a829eSFrancois Ferrand           updateEndComment(EndCommentPrevTok, std::string(), SourceMgr, &Fixes);
337e56a829eSFrancois Ferrand         }
338359b4e6cSMarek Kurdej         ++CompactedNamespacesCount;
339717cd16eSMarek Kurdej         if (!NamespaceName.empty())
340e56a829eSFrancois Ferrand           AllNamespaceNames = "::" + NamespaceName + AllNamespaceNames;
341e56a829eSFrancois Ferrand         continue;
342e56a829eSFrancois Ferrand       }
3433d4c8127SKrasimir Georgiev       NamespaceName += AllNamespaceNames;
344e56a829eSFrancois Ferrand       CompactedNamespacesCount = 0;
345e56a829eSFrancois Ferrand       AllNamespaceNames = std::string();
346e56a829eSFrancois Ferrand     }
347eb62118eSKrasimir Georgiev     // The next token in the token stream after the place where the end comment
348eb62118eSKrasimir Georgiev     // token must be. This is either the next token on the current line or the
349eb62118eSKrasimir Georgiev     // first token on the next line.
350eb62118eSKrasimir Georgiev     const FormatToken *EndCommentNextTok = EndCommentPrevTok->Next;
351eb62118eSKrasimir Georgiev     if (EndCommentNextTok && EndCommentNextTok->is(tok::comment))
352eb62118eSKrasimir Georgiev       EndCommentNextTok = EndCommentNextTok->Next;
353eb62118eSKrasimir Georgiev     if (!EndCommentNextTok && I + 1 < E)
354eb62118eSKrasimir Georgiev       EndCommentNextTok = AnnotatedLines[I + 1]->First;
355eb62118eSKrasimir Georgiev     bool AddNewline = EndCommentNextTok &&
356eb62118eSKrasimir Georgiev                       EndCommentNextTok->NewlinesBefore == 0 &&
357eb62118eSKrasimir Georgiev                       EndCommentNextTok->isNot(tok::eof);
3587cb267afSKrasimir Georgiev     const std::string EndCommentText =
359772eb24eSBjörn Schäpers         computeEndCommentText(NamespaceName, AddNewline, NamespaceTok,
360772eb24eSBjörn Schäpers                               Style.SpacesInLineCommentPrefix.Minimum);
361eb62118eSKrasimir Georgiev     if (!hasEndComment(EndCommentPrevTok)) {
3622b8542ceSOwen Pan       unsigned LineCount = 0;
3632b8542ceSOwen Pan       for (auto J = StartLineIndex + 1; J < I; ++J)
3642b8542ceSOwen Pan         LineCount += AnnotatedLines[J]->size();
3652b8542ceSOwen Pan       if (LineCount > Style.ShortNamespaceLines) {
3662f6b2dafSOwen Pan         addEndComment(EndCommentPrevTok,
3672f6b2dafSOwen Pan                       std::string(Style.SpacesBeforeTrailingComments, ' ') +
3682f6b2dafSOwen Pan                           EndCommentText,
3692f6b2dafSOwen Pan                       SourceMgr, &Fixes);
3702f6b2dafSOwen Pan       }
371e8a301f8SFrancois Ferrand     } else if (!validEndComment(EndCommentPrevTok, NamespaceName,
372e8a301f8SFrancois Ferrand                                 NamespaceTok)) {
373eb62118eSKrasimir Georgiev       updateEndComment(EndCommentPrevTok, EndCommentText, SourceMgr, &Fixes);
3747cb267afSKrasimir Georgiev     }
375e56a829eSFrancois Ferrand     StartLineIndex = SIZE_MAX;
376e56a829eSFrancois Ferrand   }
3779ad83fe7SKrasimir Georgiev   return {Fixes, 0};
3787cb267afSKrasimir Georgiev }
3797cb267afSKrasimir Georgiev 
3807cb267afSKrasimir Georgiev } // namespace format
3817cb267afSKrasimir Georgiev } // namespace clang
382