Skip to content

Commit 5a65e67

Browse files
committedJan 11, 2018
[clang-tidy] Fix google-readability-namespace-comments handling of C++17 nested namespaces
Summary: Fixes bug 34701 When we encounter a namespace find the location of the left bracket. Then if the text between the name and the left bracket contains a ':' then it's a C++17 nested namespace. Reviewers: #clang-tools-extra, alexfh, aaron.ballman Reviewed By: aaron.ballman Subscribers: curdeius, cfe-commits, krasimir, JonasToth, JDevlieghere, xazax.hun Tags: #clang-tools-extra Patch by Alexandru Octavian Buțiu! Differential Revision: https://reviews.llvm.org/D38284 llvm-svn: 322274
1 parent 7488d3a commit 5a65e67

File tree

3 files changed

+77
-10
lines changed

3 files changed

+77
-10
lines changed
 

‎clang-tools-extra/clang-tidy/readability/NamespaceCommentCheck.cpp

+59-10
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ NamespaceCommentCheck::NamespaceCommentCheck(StringRef Name,
2323
ClangTidyContext *Context)
2424
: ClangTidyCheck(Name, Context),
2525
NamespaceCommentPattern("^/[/*] *(end (of )?)? *(anonymous|unnamed)? *"
26-
"namespace( +([a-zA-Z0-9_]+))?\\.? *(\\*/)?$",
26+
"namespace( +([a-zA-Z0-9_:]+))?\\.? *(\\*/)?$",
2727
llvm::Regex::IgnoreCase),
2828
ShortNamespaceLines(Options.get("ShortNamespaceLines", 1u)),
2929
SpacesBeforeComments(Options.get("SpacesBeforeComments", 1u)) {}
@@ -56,6 +56,15 @@ static std::string getNamespaceComment(const NamespaceDecl *ND,
5656
return Fix;
5757
}
5858

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+
5968
void NamespaceCommentCheck::check(const MatchFinder::MatchResult &Result) {
6069
const auto *ND = Result.Nodes.getNodeAs<NamespaceDecl>("namespace");
6170
const SourceManager &Sources = *Result.SourceManager;
@@ -74,11 +83,44 @@ void NamespaceCommentCheck::check(const MatchFinder::MatchResult &Result) {
7483
SourceLocation AfterRBrace = ND->getRBraceLoc().getLocWithOffset(1);
7584
SourceLocation Loc = AfterRBrace;
7685
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+
77118
// Skip whitespace until we find the next token.
78119
while (Lexer::getRawToken(Loc, Tok, Sources, getLangOpts()) ||
79120
Tok.is(tok::semi)) {
80121
Loc = Loc.getLocWithOffset(1);
81122
}
123+
82124
if (!locationsInSameFile(Sources, ND->getRBraceLoc(), Loc))
83125
return;
84126

@@ -98,10 +140,14 @@ void NamespaceCommentCheck::check(const MatchFinder::MatchResult &Result) {
98140
StringRef NamespaceNameInComment = Groups.size() > 5 ? Groups[5] : "";
99141
StringRef Anonymous = Groups.size() > 3 ? Groups[3] : "";
100142

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.
105151
// FIXME: Maybe we need a strict mode, where we always fix namespace
106152
// comments with different format.
107153
return;
@@ -131,13 +177,16 @@ void NamespaceCommentCheck::check(const MatchFinder::MatchResult &Result) {
131177
std::string NamespaceName =
132178
ND->isAnonymousNamespace()
133179
? "anonymous namespace"
134-
: ("namespace '" + ND->getNameAsString() + "'");
180+
: ("namespace '" + NestedNamespaceName.str() + "'");
135181

136182
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)));
141190
diag(ND->getLocation(), "%0 starts here", DiagnosticIDs::Note)
142191
<< NamespaceName;
143192
}

‎clang-tools-extra/clang-tidy/readability/NamespaceCommentCheck.h

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class NamespaceCommentCheck : public ClangTidyCheck {
3434
llvm::Regex NamespaceCommentPattern;
3535
const unsigned ShortNamespaceLines;
3636
const unsigned SpacesBeforeComments;
37+
llvm::SmallVector<SourceLocation, 4> Ends;
3738
};
3839

3940
} // namespace readability
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// RUN: %check_clang_tidy %s google-readability-namespace-comments %t
2+
3+
namespace n1::n2 {
4+
namespace n3 {
5+
6+
// So that namespace is not empty.
7+
void f();
8+
9+
10+
// CHECK-MESSAGES: :[[@LINE+4]]:2: warning: namespace 'n3' not terminated with
11+
// CHECK-MESSAGES: :[[@LINE-7]]:11: note: namespace 'n3' starts here
12+
// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: namespace 'n1::n2' not terminated with a closing comment [google-readability-namespace-comments]
13+
// CHECK-MESSAGES: :[[@LINE-10]]:11: note: namespace 'n1::n2' starts here
14+
}}
15+
// CHECK-FIXES: } // namespace n3
16+
// CHECK-FIXES: } // namespace n1::n2
17+

0 commit comments

Comments
 (0)
Please sign in to comment.