Skip to content

Commit

Permalink
[clang-tidy] support nested inline namespace in c++20 for modernize-c…
Browse files Browse the repository at this point in the history
…oncat-nested-namespaces

Fixed #56022
c++20 support namespace like `namespace a::inline b {}`.
If an inline namespace is not the first, it can be concatened.

Reviewed By: PiotrZSL

Differential Revision: https://reviews.llvm.org/D147946
  • Loading branch information
HerrCai0907 committed Apr 11, 2023
1 parent aa7aedd commit 32aaacc
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 93 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,30 +30,6 @@ static StringRef getRawStringRef(const SourceRange &Range,
return Lexer::getSourceText(TextRange, Sources, LangOpts);
}

static bool unsupportedNamespace(const NamespaceDecl &ND) {
return ND.isAnonymousNamespace() || ND.isInlineNamespace() ||
!ND.attrs().empty();
}

static bool singleNamedNamespaceChild(const NamespaceDecl &ND) {
NamespaceDecl::decl_range Decls = ND.decls();
if (std::distance(Decls.begin(), Decls.end()) != 1)
return false;

const auto *ChildNamespace = dyn_cast<const NamespaceDecl>(*Decls.begin());
return ChildNamespace && !unsupportedNamespace(*ChildNamespace);
}

template <class R, class F>
static void concatNamespace(NamespaceName &ConcatNameSpace, R &&Range,
F &&Stringify) {
for (auto const &V : Range) {
ConcatNameSpace.append(Stringify(V));
if (V != Range.back())
ConcatNameSpace.append("::");
}
}

std::optional<SourceRange>
NS::getCleanedNamespaceFrontRange(const SourceManager &SM,
const LangOptions &LangOpts) const {
Expand Down Expand Up @@ -90,19 +66,53 @@ SourceRange NS::getNamespaceBackRange(const SourceManager &SM,
return getDefaultNamespaceBackRange();
SourceRange TokRange = SourceRange{Tok->getLocation(), Tok->getEndLoc()};
StringRef TokText = getRawStringRef(TokRange, SM, LangOpts);
std::string CloseComment = ("namespace " + getName()).str();
NamespaceName CloseComment{"namespace "};
appendCloseComment(CloseComment);
// current fix hint in readability/NamespaceCommentCheck.cpp use single line
// comment
if (TokText != "// " + CloseComment && TokText != "//" + CloseComment)
constexpr size_t L = sizeof("//") - 1U;
if (TokText.take_front(L) == "//" &&
TokText.drop_front(L).trim() != CloseComment)
return getDefaultNamespaceBackRange();
return SourceRange{front()->getRBraceLoc(), Tok->getEndLoc()};
}

NamespaceName NS::getName() const {
NamespaceName Name{};
concatNamespace(Name, *this,
[](const NamespaceDecl *ND) { return ND->getName(); });
return Name;
void NS::appendName(NamespaceName &Str) const {
for (const NamespaceDecl *ND : *this) {
if (ND->isInlineNamespace())
Str.append("inline ");
Str.append(ND->getName());
if (ND != back())
Str.append("::");
}
}
void NS::appendCloseComment(NamespaceName &Str) const {
if (size() == 1)
Str.append(back()->getName());
else
appendName(Str);
}

bool ConcatNestedNamespacesCheck::unsupportedNamespace(const NamespaceDecl &ND,
bool IsChild) const {
if (ND.isAnonymousNamespace() || !ND.attrs().empty())
return true;
if (getLangOpts().CPlusPlus20) {
// C++20 support inline nested namespace
bool IsFirstNS = IsChild || !Namespaces.empty();
return ND.isInlineNamespace() && !IsFirstNS;
}
return ND.isInlineNamespace();
}

bool ConcatNestedNamespacesCheck::singleNamedNamespaceChild(
const NamespaceDecl &ND) const {
NamespaceDecl::decl_range Decls = ND.decls();
if (std::distance(Decls.begin(), Decls.end()) != 1)
return false;

const auto *ChildNamespace = dyn_cast<const NamespaceDecl>(*Decls.begin());
return ChildNamespace && !unsupportedNamespace(*ChildNamespace, true);
}

void ConcatNestedNamespacesCheck::registerMatchers(
Expand Down Expand Up @@ -137,8 +147,11 @@ void ConcatNestedNamespacesCheck::reportDiagnostic(
SourceRange LastRBrace = Backs.pop_back_val();

NamespaceName ConcatNameSpace{"namespace "};
concatNamespace(ConcatNameSpace, Namespaces,
[](const NS &NS) { return NS.getName(); });
for (const NS &NS : Namespaces) {
NS.appendName(ConcatNameSpace);
if (&NS != &Namespaces.back()) // compare address directly
ConcatNameSpace.append("::");
}

for (SourceRange const &Front : Fronts)
DB << FixItHint::CreateRemoval(Front);
Expand All @@ -159,12 +172,15 @@ void ConcatNestedNamespacesCheck::check(
if (!locationsInSameFile(Sources, ND.getBeginLoc(), ND.getRBraceLoc()))
return;

if (unsupportedNamespace(ND))
if (unsupportedNamespace(ND, false))
return;

if (!ND.isNested())
Namespaces.push_back(NS{});
Namespaces.back().push_back(&ND);
if (!Namespaces.empty())
// Otherwise it will crash with invalid input like `inline namespace
// a::b::c`.
Namespaces.back().push_back(&ND);

if (singleNamedNamespaceChild(ND))
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,16 @@ class NS : public llvm::SmallVector<const NamespaceDecl *, 6> {
SourceRange getNamespaceBackRange(const SourceManager &SM,
const LangOptions &LangOpts) const;
SourceRange getDefaultNamespaceBackRange() const;
NamespaceName getName() const;
void appendName(NamespaceName &Str) const;
void appendCloseComment(NamespaceName &Str) const;
};

class ConcatNestedNamespacesCheck : public ClangTidyCheck {
public:
ConcatNestedNamespacesCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
bool unsupportedNamespace(const NamespaceDecl &ND, bool IsChild) const;
bool singleNamedNamespaceChild(const NamespaceDecl &ND) const;
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
return LangOpts.CPlusPlus17;
}
Expand Down
4 changes: 2 additions & 2 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -314,8 +314,8 @@ Changes in existing checks

- Improved :doc:`modernize-concat-nested-namespaces
<clang-tidy/checks/modernize/concat-nested-namespaces>` to fix incorrect fixes when
using macro between namespace declarations and false positive when using namespace
with attributes.
using macro between namespace declarations, to fix false positive when using namespace
with attributes and to support nested inline namespace introduced in c++20.

- Fixed a false positive in :doc:`performance-no-automatic-move
<clang-tidy/checks/performance/no-automatic-move>` when warning would be
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ For example:
}
}

// in c++20
namespace n8 {
inline namespace n9 {
void t();
}
}

Will be modified to:

.. code-block:: c++
Expand All @@ -47,3 +54,8 @@ Will be modified to:
}
}

// in c++20
namespace n8::inline n9 {
void t();
}

0 comments on commit 32aaacc

Please sign in to comment.