Fix nested namespaces in google-readability-nested-namespace-comments.

Fixes PR34701.

Patch by Alexandru Octavian Buțiu.

llvm-svn: 315057
This commit is contained in:
Aaron Ballman 2017-10-06 12:57:28 +00:00
parent 4e1bae8136
commit 9549c1180f
3 changed files with 71 additions and 10 deletions

View File

@ -23,7 +23,7 @@ NamespaceCommentCheck::NamespaceCommentCheck(StringRef Name,
ClangTidyContext *Context) ClangTidyContext *Context)
: ClangTidyCheck(Name, Context), : ClangTidyCheck(Name, Context),
NamespaceCommentPattern("^/[/*] *(end (of )?)? *(anonymous|unnamed)? *" NamespaceCommentPattern("^/[/*] *(end (of )?)? *(anonymous|unnamed)? *"
"namespace( +([a-zA-Z0-9_]+))?\\.? *(\\*/)?$", "namespace( +([a-zA-Z0-9_:]+))?\\.? *(\\*/)?$",
llvm::Regex::IgnoreCase), llvm::Regex::IgnoreCase),
ShortNamespaceLines(Options.get("ShortNamespaceLines", 1u)), ShortNamespaceLines(Options.get("ShortNamespaceLines", 1u)),
SpacesBeforeComments(Options.get("SpacesBeforeComments", 1u)) {} SpacesBeforeComments(Options.get("SpacesBeforeComments", 1u)) {}
@ -56,6 +56,15 @@ static std::string getNamespaceComment(const NamespaceDecl *ND,
return Fix; return Fix;
} }
static std::string getNamespaceComment(const std::string &NameSpaceName,
bool InsertLineBreak) {
std::string Fix = "// namespace ";
Fix.append(NameSpaceName);
if (InsertLineBreak)
Fix.append("\n");
return Fix;
}
void NamespaceCommentCheck::check(const MatchFinder::MatchResult &Result) { void NamespaceCommentCheck::check(const MatchFinder::MatchResult &Result) {
const auto *ND = Result.Nodes.getNodeAs<NamespaceDecl>("namespace"); const auto *ND = Result.Nodes.getNodeAs<NamespaceDecl>("namespace");
const SourceManager &Sources = *Result.SourceManager; const SourceManager &Sources = *Result.SourceManager;
@ -74,11 +83,38 @@ void NamespaceCommentCheck::check(const MatchFinder::MatchResult &Result) {
SourceLocation AfterRBrace = ND->getRBraceLoc().getLocWithOffset(1); SourceLocation AfterRBrace = ND->getRBraceLoc().getLocWithOffset(1);
SourceLocation Loc = AfterRBrace; SourceLocation Loc = AfterRBrace;
Token Tok; Token Tok;
SourceLocation LBracketLocation = ND->getLocation();
SourceLocation NestedNamespaceBegin = LBracketLocation;
// Currently for nested namepsace (n1::n2::...) the AST matcher will match foo
// then bar instead of a single match. So if we got a nested namespace we have
// to skip the next ones.
for (const auto &EndOfNameLocation : Ends) {
if (Sources.isBeforeInTranslationUnit(NestedNamespaceBegin,
EndOfNameLocation))
return;
}
while (Lexer::getRawToken(LBracketLocation, Tok, Sources, getLangOpts()) ||
!Tok.is(tok::l_brace)) {
LBracketLocation = LBracketLocation.getLocWithOffset(1);
}
auto TextRange =
Lexer::getAsCharRange(SourceRange(NestedNamespaceBegin, LBracketLocation),
Sources, getLangOpts());
auto NestedNamespaceName =
Lexer::getSourceText(TextRange, Sources, getLangOpts()).rtrim();
bool IsNested = NestedNamespaceName.contains(':');
if (IsNested)
Ends.push_back(LBracketLocation);
// Skip whitespace until we find the next token. // Skip whitespace until we find the next token.
while (Lexer::getRawToken(Loc, Tok, Sources, getLangOpts()) || while (Lexer::getRawToken(Loc, Tok, Sources, getLangOpts()) ||
Tok.is(tok::semi)) { Tok.is(tok::semi)) {
Loc = Loc.getLocWithOffset(1); Loc = Loc.getLocWithOffset(1);
} }
if (!locationsInSameFile(Sources, ND->getRBraceLoc(), Loc)) if (!locationsInSameFile(Sources, ND->getRBraceLoc(), Loc))
return; return;
@ -98,10 +134,14 @@ void NamespaceCommentCheck::check(const MatchFinder::MatchResult &Result) {
StringRef NamespaceNameInComment = Groups.size() > 5 ? Groups[5] : ""; StringRef NamespaceNameInComment = Groups.size() > 5 ? Groups[5] : "";
StringRef Anonymous = Groups.size() > 3 ? Groups[3] : ""; StringRef Anonymous = Groups.size() > 3 ? Groups[3] : "";
// Check if the namespace in the comment is the same. if (IsNested && NestedNamespaceName == NamespaceNameInComment) {
if ((ND->isAnonymousNamespace() && NamespaceNameInComment.empty()) || // C++17 nested namespace.
(ND->getNameAsString() == NamespaceNameInComment && return;
Anonymous.empty())) { } else if ((ND->isAnonymousNamespace() &&
NamespaceNameInComment.empty()) ||
(ND->getNameAsString() == NamespaceNameInComment &&
Anonymous.empty())) {
// Check if the namespace in the comment is the same.
// FIXME: Maybe we need a strict mode, where we always fix namespace // FIXME: Maybe we need a strict mode, where we always fix namespace
// comments with different format. // comments with different format.
return; return;
@ -131,13 +171,16 @@ void NamespaceCommentCheck::check(const MatchFinder::MatchResult &Result) {
std::string NamespaceName = std::string NamespaceName =
ND->isAnonymousNamespace() ND->isAnonymousNamespace()
? "anonymous namespace" ? "anonymous namespace"
: ("namespace '" + ND->getNameAsString() + "'"); : ("namespace '" + NestedNamespaceName.str() + "'");
diag(AfterRBrace, Message) diag(AfterRBrace, Message)
<< NamespaceName << FixItHint::CreateReplacement( << NamespaceName
CharSourceRange::getCharRange(OldCommentRange), << FixItHint::CreateReplacement(
std::string(SpacesBeforeComments, ' ') + CharSourceRange::getCharRange(OldCommentRange),
getNamespaceComment(ND, NeedLineBreak)); std::string(SpacesBeforeComments, ' ') +
(IsNested
? getNamespaceComment(NestedNamespaceName, NeedLineBreak)
: getNamespaceComment(ND, NeedLineBreak)));
diag(ND->getLocation(), "%0 starts here", DiagnosticIDs::Note) diag(ND->getLocation(), "%0 starts here", DiagnosticIDs::Note)
<< NamespaceName; << NamespaceName;
} }

View File

@ -34,6 +34,7 @@ private:
llvm::Regex NamespaceCommentPattern; llvm::Regex NamespaceCommentPattern;
const unsigned ShortNamespaceLines; const unsigned ShortNamespaceLines;
const unsigned SpacesBeforeComments; const unsigned SpacesBeforeComments;
llvm::SmallVector<SourceLocation, 4> Ends;
}; };
} // namespace readability } // namespace readability

View File

@ -0,0 +1,17 @@
// RUN: %check_clang_tidy %s google-readability-namespace-comments %t -- -std=c++17
namespace n1::n2 {
namespace n3 {
// So that namespace is not empty.
void f();
// CHECK-MESSAGES: :[[@LINE+4]]:2: warning: namespace 'n3' not terminated with
// CHECK-MESSAGES: :[[@LINE-7]]:11: note: namespace 'n3' starts here
// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: namespace 'n1::n2' not terminated with a closing comment [google-readability-namespace-comments]
// CHECK-MESSAGES: :[[@LINE-10]]:11: note: namespace 'n1::n2' starts here
}}
// CHECK-FIXES: } // namespace n3
// CHECK-FIXES: } // namespace n1::n2