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:
parent
4e1bae8136
commit
9549c1180f
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue