Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,31 +14,41 @@ using namespace clang::ast_matchers;

namespace clang::tidy::llvm_libc {

const static StringRef RequiredNamespace = "__llvm_libc";
const static StringRef RequiredNamespaceStart = "__llvm_libc";
const static StringRef RequiredNamespaceMacroName = "LIBC_NAMESPACE";

void ImplementationInNamespaceCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
decl(hasParent(translationUnitDecl()), unless(linkageSpecDecl()))
.bind("child_of_translation_unit"),
translationUnitDecl(
forEach(decl(isExpansionInMainFile(), unless(linkageSpecDecl()),
// anonymous namespaces generate usingDirective
unless(usingDirectiveDecl(isImplicit())))
.bind("child_of_translation_unit"))),
this);
}

void ImplementationInNamespaceCheck::check(
const MatchFinder::MatchResult &Result) {
const auto *MatchedDecl =
Result.Nodes.getNodeAs<Decl>("child_of_translation_unit");
if (!Result.SourceManager->isInMainFile(MatchedDecl->getLocation()))
MatchedDecl->dump();
const auto *NS = dyn_cast<NamespaceDecl>(MatchedDecl);
if (NS == nullptr || NS->isAnonymousNamespace()) {
diag(MatchedDecl->getLocation(),
"declaration must be enclosed within the '%0' namespace")
<< RequiredNamespaceMacroName;
return;

if (const auto *NS = dyn_cast<NamespaceDecl>(MatchedDecl)) {
if (NS->getName() != RequiredNamespace) {
diag(NS->getLocation(), "'%0' needs to be the outermost namespace")
<< RequiredNamespace;
}
}
if (Result.SourceManager->isMacroBodyExpansion(NS->getLocation()) == false) {
diag(NS->getLocation(), "the outermost namespace should be the '%0' macro")
<< RequiredNamespaceMacroName;
return;
}
if (NS->getName().starts_with(RequiredNamespaceStart) == false) {
diag(NS->getLocation(), "the '%0' macro should start with '%1'")
<< RequiredNamespaceMacroName << RequiredNamespaceStart;
return;
}
diag(MatchedDecl->getLocation(),
"declaration must be declared within the '%0' namespace")
<< RequiredNamespace;
}

} // namespace clang::tidy::llvm_libc
5 changes: 5 additions & 0 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,11 @@ Changes in existing checks
<clang-tidy/checks/llvm/namespace-comment>` check to provide fixes for
``inline`` namespaces in the same format as :program:`clang-format`.

- Improved :doc:`llvmlibc-implementation-in-namespace
<clang-tidy/checks/llvmlibc/implementation-in-namespace>` to support
customizable namespace. This further allows for testing the libc when the
system-libc is also LLVM's libc.

- Improved :doc:`misc-include-cleaner
<clang-tidy/checks/misc/include-cleaner>` check by adding option
`DeduplicateFindings` to output one finding per symbol occurrence.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,30 @@ correct namespace.

.. code-block:: c++

// Correct: implementation inside the correct namespace.
namespace __llvm_libc {
// Implementation inside the LIBC_NAMESPACE namespace.
// Correct if:
// - LIBC_NAMESPACE is a macro
// - LIBC_NAMESPACE expansion starts with `__llvm_libc`
namespace LIBC_NAMESPACE {
void LLVM_LIBC_ENTRYPOINT(strcpy)(char *dest, const char *src) {}
// Namespaces within __llvm_libc namespace are allowed.
namespace inner{
// Namespaces within LIBC_NAMESPACE namespace are allowed.
namespace inner {
int localVar = 0;
}
// Functions with C linkage are allowed.
extern "C" void str_fuzz(){}
extern "C" void str_fuzz() {}
}

// Incorrect: implementation not in a namespace.
// Incorrect: implementation not in the LIBC_NAMESPACE namespace.
void LLVM_LIBC_ENTRYPOINT(strcpy)(char *dest, const char *src) {}

// Incorrect: outer most namespace is not correct.
// Incorrect: outer most namespace is not the LIBC_NAMESPACE macro.
namespace something_else {
void LLVM_LIBC_ENTRYPOINT(strcpy)(char *dest, const char *src) {}
}

// Incorrect: outer most namespace expansion does not start with `__llvm_libc`.
#define LIBC_NAMESPACE custom_namespace
namespace LIBC_NAMESPACE {
void LLVM_LIBC_ENTRYPOINT(strcpy)(char *dest, const char *src) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,30 @@
#define MACRO_A "defining macros outside namespace is valid"

class ClassB;
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration must be declared within the '__llvm_libc' namespace
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration must be enclosed within the 'LIBC_NAMESPACE' namespace
struct StructC {};
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: declaration must be declared within the '__llvm_libc' namespace
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: declaration must be enclosed within the 'LIBC_NAMESPACE' namespace
char *VarD = MACRO_A;
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration must be declared within the '__llvm_libc' namespace
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration must be enclosed within the 'LIBC_NAMESPACE' namespace
typedef int typeE;
// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: declaration must be declared within the '__llvm_libc' namespace
// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: declaration must be enclosed within the 'LIBC_NAMESPACE' namespace
void funcF() {}
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: declaration must be declared within the '__llvm_libc' namespace
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: declaration must be enclosed within the 'LIBC_NAMESPACE' namespace

namespace outer_most {
// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: the outermost namespace should be the 'LIBC_NAMESPACE' macro
class A {};
}

// Wrapped in anonymous namespace.
namespace {
// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: declaration must be enclosed within the 'LIBC_NAMESPACE' namespace
class A {};
}

namespace namespaceG {
// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: '__llvm_libc' needs to be the outermost namespace
namespace __llvm_libc{
// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: the outermost namespace should be the 'LIBC_NAMESPACE' macro
namespace __llvm_libc {
namespace namespaceH {
class ClassB;
} // namespace namespaceH
Expand All @@ -26,9 +37,20 @@ typedef int typeE;
void funcF() {}
} // namespace namespaceG

// Wrapped in correct namespace.
namespace __llvm_libc {
// Namespaces within __llvim_libc namespace allowed.
// Wrapped in macro namespace but with an incorrect name
#define LIBC_NAMESPACE custom_namespace
namespace LIBC_NAMESPACE {
// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: the 'LIBC_NAMESPACE' macro should start with '__llvm_libc'
namespace namespaceH {
class ClassB;
} // namespace namespaceH
} // namespace LIBC_NAMESPACE


// Wrapped in macro namespace with a valid name, LIBC_NAMESPACE starts with '__llvm_libc'
#undef LIBC_NAMESPACE
#define LIBC_NAMESPACE __llvm_libc_xyz
namespace LIBC_NAMESPACE {
namespace namespaceI {
class ClassB;
} // namespace namespaceI
Expand All @@ -37,4 +59,4 @@ char *VarD = MACRO_A;
typedef int typeE;
void funcF() {}
extern "C" void extern_funcJ() {}
} // namespace __llvm_libc
} // namespace LIBC_NAMESPACE