@@ -23,7 +23,7 @@ NamespaceCommentCheck::NamespaceCommentCheck(StringRef Name,
23
23
ClangTidyContext *Context)
24
24
: ClangTidyCheck(Name, Context),
25
25
NamespaceCommentPattern (" ^/[/*] *(end (of )?)? *(anonymous|unnamed)? *"
26
- " namespace( +([a-zA-Z0-9_]+))?\\ .? *(\\ */)?$" ,
26
+ " namespace( +([a-zA-Z0-9_: ]+))?\\ .? *(\\ */)?$" ,
27
27
llvm::Regex::IgnoreCase),
28
28
ShortNamespaceLines(Options.get(" ShortNamespaceLines" , 1u )),
29
29
SpacesBeforeComments(Options.get(" SpacesBeforeComments" , 1u )) {}
@@ -56,6 +56,15 @@ static std::string getNamespaceComment(const NamespaceDecl *ND,
56
56
return Fix;
57
57
}
58
58
59
+ static std::string getNamespaceComment (const std::string &NameSpaceName,
60
+ bool InsertLineBreak) {
61
+ std::string Fix = " // namespace " ;
62
+ Fix.append (NameSpaceName);
63
+ if (InsertLineBreak)
64
+ Fix.append (" \n " );
65
+ return Fix;
66
+ }
67
+
59
68
void NamespaceCommentCheck::check (const MatchFinder::MatchResult &Result) {
60
69
const auto *ND = Result.Nodes .getNodeAs <NamespaceDecl>(" namespace" );
61
70
const SourceManager &Sources = *Result.SourceManager ;
@@ -74,11 +83,44 @@ void NamespaceCommentCheck::check(const MatchFinder::MatchResult &Result) {
74
83
SourceLocation AfterRBrace = ND->getRBraceLoc ().getLocWithOffset (1 );
75
84
SourceLocation Loc = AfterRBrace;
76
85
Token Tok;
86
+ SourceLocation LBracketLocation = ND->getLocation ();
87
+ SourceLocation NestedNamespaceBegin = LBracketLocation;
88
+
89
+ // Currently for nested namepsace (n1::n2::...) the AST matcher will match foo
90
+ // then bar instead of a single match. So if we got a nested namespace we have
91
+ // to skip the next ones.
92
+ for (const auto &EndOfNameLocation : Ends) {
93
+ if (Sources.isBeforeInTranslationUnit (NestedNamespaceBegin,
94
+ EndOfNameLocation))
95
+ return ;
96
+ }
97
+
98
+ // Ignore macros
99
+ if (!ND->getLocation ().isMacroID ()) {
100
+ while (Lexer::getRawToken (LBracketLocation, Tok, Sources, getLangOpts ()) ||
101
+ !Tok.is (tok::l_brace)) {
102
+ LBracketLocation = LBracketLocation.getLocWithOffset (1 );
103
+ }
104
+ }
105
+
106
+ auto TextRange =
107
+ Lexer::getAsCharRange (SourceRange (NestedNamespaceBegin, LBracketLocation),
108
+ Sources, getLangOpts ());
109
+ StringRef NestedNamespaceName =
110
+ Lexer::getSourceText (TextRange, Sources, getLangOpts ()).rtrim ();
111
+ bool IsNested = NestedNamespaceName.contains (' :' );
112
+
113
+ if (IsNested)
114
+ Ends.push_back (LBracketLocation);
115
+ else
116
+ NestedNamespaceName = ND->getName ();
117
+
77
118
// Skip whitespace until we find the next token.
78
119
while (Lexer::getRawToken (Loc, Tok, Sources, getLangOpts ()) ||
79
120
Tok.is (tok::semi)) {
80
121
Loc = Loc.getLocWithOffset (1 );
81
122
}
123
+
82
124
if (!locationsInSameFile (Sources, ND->getRBraceLoc (), Loc))
83
125
return ;
84
126
@@ -98,10 +140,14 @@ void NamespaceCommentCheck::check(const MatchFinder::MatchResult &Result) {
98
140
StringRef NamespaceNameInComment = Groups.size () > 5 ? Groups[5 ] : " " ;
99
141
StringRef Anonymous = Groups.size () > 3 ? Groups[3 ] : " " ;
100
142
101
- // Check if the namespace in the comment is the same.
102
- if ((ND->isAnonymousNamespace () && NamespaceNameInComment.empty ()) ||
103
- (ND->getNameAsString () == NamespaceNameInComment &&
104
- Anonymous.empty ())) {
143
+ if (IsNested && NestedNamespaceName == NamespaceNameInComment) {
144
+ // C++17 nested namespace.
145
+ return ;
146
+ } else if ((ND->isAnonymousNamespace () &&
147
+ NamespaceNameInComment.empty ()) ||
148
+ (ND->getNameAsString () == NamespaceNameInComment &&
149
+ Anonymous.empty ())) {
150
+ // Check if the namespace in the comment is the same.
105
151
// FIXME: Maybe we need a strict mode, where we always fix namespace
106
152
// comments with different format.
107
153
return ;
@@ -131,13 +177,16 @@ void NamespaceCommentCheck::check(const MatchFinder::MatchResult &Result) {
131
177
std::string NamespaceName =
132
178
ND->isAnonymousNamespace ()
133
179
? " anonymous namespace"
134
- : (" namespace '" + ND-> getNameAsString () + " '" );
180
+ : (" namespace '" + NestedNamespaceName. str () + " '" );
135
181
136
182
diag (AfterRBrace, Message)
137
- << NamespaceName << FixItHint::CreateReplacement (
138
- CharSourceRange::getCharRange (OldCommentRange),
139
- std::string (SpacesBeforeComments, ' ' ) +
140
- getNamespaceComment (ND, NeedLineBreak));
183
+ << NamespaceName
184
+ << FixItHint::CreateReplacement (
185
+ CharSourceRange::getCharRange (OldCommentRange),
186
+ std::string (SpacesBeforeComments, ' ' ) +
187
+ (IsNested
188
+ ? getNamespaceComment (NestedNamespaceName, NeedLineBreak)
189
+ : getNamespaceComment (ND, NeedLineBreak)));
141
190
diag (ND->getLocation (), " %0 starts here" , DiagnosticIDs::Note)
142
191
<< NamespaceName;
143
192
}
0 commit comments