SERVER-104800 Adjust clang tidy std bans (#39064)

GitOrigin-RevId: 7ea0cb3ba6d7ff5ff0c7f84cf47ba8bed310ff0d
This commit is contained in:
Alex Li 2025-08-06 21:45:22 -04:00 committed by MongoDB Bot
parent 18cfe7e7f2
commit b97884cb49
34 changed files with 348 additions and 631 deletions

View File

@ -65,6 +65,7 @@ Checks: '-*,
modernize-unary-static-assert,
modernize-use-override,
mongo-assert-check,
mongo-banned-names-check,
mongo-cctype-check,
mongo-collection-sharding-runtime-check,
mongo-config-header-check,
@ -76,10 +77,7 @@ Checks: '-*,
mongo-invariant-ddl-coordinator-check,
mongo-macro-definition-leaks-check,
mongo-mutex-check,
mongo-polyfill-check,
mongo-rand-check,
mongo-std-atomic-check,
mongo-std-optional-check,
mongo-stringdata-const-ref-check,
mongo-trace-check,
mongo-uninterruptible-lock-guard-check,

View File

@ -1769,7 +1769,7 @@ TEST_F(DConcurrencyTestFixture,
boost::optional<Lock::DBLock> dbIX = Lock::DBLock{opCtx1, dbName, LockMode::MODE_IX};
shard_role_details::getLocker(opCtx1)->releaseTicket();
stdx::packaged_task<void()> task{[opCtx2, &dbName] {
std::packaged_task<void()> task{[opCtx2, &dbName] { // NOLINT
Lock::GlobalLock globalIX{opCtx2, LockMode::MODE_IX};
Lock::DBLock dbX{opCtx2, dbName, LockMode::MODE_X};
}};

View File

@ -93,6 +93,7 @@ static const StringMap<MetaType> kMetaNameToMetaType = {
{changeStreamControlEventName, MetaType::kChangeStreamControlEvent},
};
// NOLINTNEXTLINE needs audit
static const std::unordered_map<MetaType, StringData> kMetaTypeToMetaName = {
{MetaType::kScore, scoreName},
{MetaType::kVectorSearchScore, vectorSearchScoreName},

View File

@ -770,7 +770,7 @@ public:
boost::optional<Date_t> maxTime,
WaitFn waitFn) {
auto barrier = std::make_shared<unittest::Barrier>(2);
task = stdx::packaged_task<bool()>([=, this] {
task = std::packaged_task<bool()>([=, this] { // NOLINT
if (maxTime)
opCtx->setDeadlineByDate(*maxTime, ErrorCodes::ExceededTimeLimit);
stdx::unique_lock<stdx::mutex> lk(mutex);
@ -795,7 +795,7 @@ public:
stdx::mutex mutex;
stdx::condition_variable cv;
bool isSignaled = false;
stdx::packaged_task<bool()> task;
std::packaged_task<bool()> task; // NOLINT
JoinThread waiter;
};

View File

@ -54,6 +54,7 @@ public:
};
TEST_F(ProfileFilterTest, FilterOnAllOpDebugFields) {
// NOLINTNEXTLINE
const std::unordered_set<std::string> allowedOpDebugFields = {"ts",
"client",
"appName",

View File

@ -50,6 +50,9 @@
namespace mongo {
namespace {
template <typename T>
using nolint_promise = std::promise<T>; // NOLINT
using unittest::assertGet;
class MoveChunkRegistration : public ShardServerTestFixture {
@ -177,9 +180,9 @@ TEST_F(MoveChunkRegistration, TestReceiveChunkIsRejectedWhenRegistryIsLocked) {
TEST_F(MoveChunkRegistration,
TestReceiveChunkWithWaitForConflictingOpsIsBlockedWhenRegistryIsLocked) {
stdx::promise<void> blockReceive;
stdx::promise<void> readyToLock;
stdx::promise<void> inLock;
nolint_promise<void> blockReceive;
nolint_promise<void> readyToLock;
nolint_promise<void> inLock;
// Registry thread.
auto result = stdx::async(stdx::launch::async, [&] {
@ -245,9 +248,9 @@ TEST_F(MoveChunkRegistration,
// in progress. The test will fail if any of the futures are not signalled indicating that some part
// of the sequence is not working correctly.
TEST_F(MoveChunkRegistration, TestBlockingWhileDonateInProgress) {
stdx::promise<void> blockDonate;
stdx::promise<void> readyToLock;
stdx::promise<void> inLock;
nolint_promise<void> blockDonate;
nolint_promise<void> readyToLock;
nolint_promise<void> inLock;
// Migration thread.
auto result = stdx::async(stdx::launch::async, [&] {
@ -309,9 +312,9 @@ TEST_F(MoveChunkRegistration, TestBlockingWhileDonateInProgress) {
// in progress. The test will fail if any of the futures are not signalled indicating that some part
// of the sequence is not working correctly.
TEST_F(MoveChunkRegistration, TestBlockingWhileReceiveInProgress) {
stdx::promise<void> blockReceive;
stdx::promise<void> readyToLock;
stdx::promise<void> inLock;
nolint_promise<void> blockReceive;
nolint_promise<void> readyToLock;
nolint_promise<void> inLock;
// Migration thread.
auto result = stdx::async(stdx::launch::async, [&] {

View File

@ -1484,7 +1484,7 @@ TEST_F(SessionCatalogTestWithDefaultOpCtx, KillSessionsThroughScanSessions) {
return {lsid0, lsid1, lsid2};
}();
std::vector<stdx::future<void>> futures;
std::vector<std::future<void>> futures; // NOLINT
unittest::Barrier firstUseOfTheSessionReachedBarrier(lsids.size() + 1);
for (const auto& lsid : lsids) {
@ -1563,7 +1563,7 @@ TEST_F(SessionCatalogTestWithDefaultOpCtx, ConcurrentCheckOutAndKill) {
auto opCtx = cc().makeOperationContext();
opCtx->setLogicalSessionId(lsid);
stdx::future<void> normalCheckOutFinish, killCheckOutFinish;
std::future<void> normalCheckOutFinish, killCheckOutFinish; // NOLINT
// This variable is protected by the session check-out.
std::string lastSessionCheckOut = "first session";
@ -1633,7 +1633,7 @@ TEST_F(SessionCatalogTest, CheckOutForKillTimeout) {
// Check out the session to block checkOutForKill.
OperationContextSession firstCheckOut(opCtx.get());
stdx::future<void> killCheckOutTimeout = stdx::async(stdx::launch::async, [&] {
std::future<void> killCheckOutTimeout = std::async(std::launch::async, [&] { // NOLINT
ThreadClient tc(getServiceContext()->getService());
auto sideOpCtx = Client::getCurrent()->makeOperationContext();
sideOpCtx->setLogicalSessionId(lsid);

View File

@ -148,7 +148,7 @@ public:
int jobs();
private:
stdx::recursive_mutex _timersMutex;
std::recursive_mutex _timersMutex; // NOLINT
stdx::unordered_set<std::shared_ptr<AsyncTimerMockImpl>> _timers;
Milliseconds _curTime;
};

View File

@ -95,7 +95,7 @@ public:
template <class T>
class FutureHandle {
public:
FutureHandle(stdx::future<T> future,
FutureHandle(std::future<T> future, // NOLINT
executor::TaskExecutor* executor,
executor::NetworkInterfaceMock* network)
: _future(std::move(future)), _executor(executor), _network(network) {}
@ -140,7 +140,7 @@ public:
}
private:
stdx::future<T> _future;
std::future<T> _future; // NOLINT
executor::TaskExecutor* _executor;
executor::NetworkInterfaceMock* _network;
};

View File

@ -157,6 +157,7 @@ private:
std::exception_ptr exception = nullptr;
};
// NOLINTNEXTLINE needs audit
static std::unordered_set<std::string> forbiddenKeywords{
"legacy", "cursor", "endSessions", "ok", "isWritablePrimary", "n"};

View File

@ -186,6 +186,7 @@ public:
// Do not destroy the cursor if the error is any of the errors in
// 'safeErrorCodes' since for those errors we can guarantee that the data
// has not been corrupted and it is safe to continue the execution.
// NOLINTNEXTLINE needs audit
static const std::unordered_set<ErrorCodes::Error> safeErrorCodes{
ErrorCodes::QueryExceededMemoryLimitNoDiskUseAllowed,
ErrorCodes::CursorInUse,

View File

@ -806,6 +806,7 @@ Status AsyncResultsMerger::releaseMemory() {
// 'safeErrorCodes' since for those errors we can guarantee that the data has not been
// corrupted and it is safe to continue the execution. We must wait for the other shards
// to be sure that none returned a fatal error.
// NOLINTNEXTLINE needs audit
static const std::unordered_set<ErrorCodes::Error> safeErrorCodes{
ErrorCodes::QueryExceededMemoryLimitNoDiskUseAllowed,
ErrorCodes::CursorInUse,

View File

@ -819,8 +819,8 @@ private:
}
private:
stdx::promise<void> _promise;
stdx::shared_future<void> _future;
std::promise<void> _promise; // NOLINT needs audit
std::shared_future<void> _future;
};
boost::optional<CompletePromiseFuture> _killCompleteInfo;

View File

@ -11,6 +11,7 @@ cc_library(
name = "mongo_tidy_checks_static",
srcs = [
"MongoAssertCheck.cpp",
"MongoBannedNamesCheck.cpp",
"MongoCctypeCheck.cpp",
"MongoCollectionShardingRuntimeCheck.cpp",
"MongoConfigHeaderCheck.cpp",
@ -22,11 +23,8 @@ cc_library(
"MongoInvariantStatusIsOKCheck.cpp",
"MongoMacroDefinitionLeaksCheck.cpp",
"MongoNoUniqueAddressCheck.cpp",
"MongoPolyFillCheck.cpp",
"MongoRWMutexCheck.cpp",
"MongoRandCheck.cpp",
"MongoStdAtomicCheck.cpp",
"MongoStdOptionalCheck.cpp",
"MongoStringDataConstRefCheck.cpp",
"MongoStringDataStringViewApi.cpp",
"MongoTidyModule.cpp",
@ -37,6 +35,7 @@ cc_library(
],
hdrs = [
"MongoAssertCheck.h",
"MongoBannedNamesCheck.h",
"MongoCctypeCheck.h",
"MongoCollectionShardingRuntimeCheck.h",
"MongoConfigHeaderCheck.h",
@ -48,11 +47,8 @@ cc_library(
"MongoInvariantStatusIsOKCheck.h",
"MongoMacroDefinitionLeaksCheck.h",
"MongoNoUniqueAddressCheck.h",
"MongoPolyFillCheck.h",
"MongoRWMutexCheck.h",
"MongoRandCheck.h",
"MongoStdAtomicCheck.h",
"MongoStdOptionalCheck.h",
"MongoStringDataConstRefCheck.h",
"MongoStringDataStringViewApi.h",
"MongoTidyUtils.h",

View File

@ -0,0 +1,249 @@
/**
* Copyright (C) 2025-present MongoDB, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the Server Side Public License in all respects for
* all of the code used other than as permitted herein. If you modify file(s)
* with this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version. If you delete this
* exception statement from all source files in the program, then also delete
* it in the license file.
*/
#include "MongoBannedNamesCheck.h"
#include <iostream>
#include <string>
#include <clang/Lex/Lexer.h>
namespace mongo::tidy {
using namespace clang;
using namespace clang::ast_matchers;
namespace {
enum Action {
kStdxReplacement,
kMongoReplacement,
kBoostReplacement,
kDoNotUse,
};
enum Namespace {
kStd,
kBoost,
};
struct BanInfo {
std::string produceBanMessage(const llvm::StringRef sourceText) const {
auto msg = [&]() -> std::string {
switch (action) {
case kStdxReplacement:
return "Consider using alternatives such as the polyfills from the "
"mongo::stdx:: namespace.";
case kMongoReplacement:
case kBoostReplacement:
return "Consider using " + message + " instead.";
case kDoNotUse:
return "Do not use. " + message;
}
return std::string();
}();
std::ostringstream err;
err << "Forbidden use of banned name in " << sourceText.str() << ". " << msg
<< " Use '// NOLINT' if usage is absolutely necessary."
<< " Be especially careful doing so outside of test code.";
return err.str();
}
bool fromBannedNamespace(const llvm::StringRef sourceText) const {
for (auto&& ns : namespaces) {
switch (ns) {
case kStd:
if (sourceText.find("std::" + name.str()) != llvm::StringRef::npos)
return true;
break;
case kBoost:
if (sourceText.find("boost::" + name.str()) != llvm::StringRef::npos)
return true;
break;
}
}
return false;
}
llvm::StringRef name;
Action action;
std::vector<Namespace> namespaces;
std::string message;
};
std::vector<llvm::StringRef> getNames(const std::vector<BanInfo>& infos) {
std::vector<llvm::StringRef> names;
for (auto&& info : infos) {
names.push_back(info.name);
}
return names;
}
// List of base type names from the std and boost namespaces to be checked
const std::vector<BanInfo> baseTypeInfos = {
{"atomic", kMongoReplacement, {kStd, kBoost}, "mongo::Atomic<T>"},
{"condition_variable", kStdxReplacement, {kStd, kBoost}},
{"condition_variable_any", kStdxReplacement, {kStd, kBoost}},
{"future", kMongoReplacement, {kStd, kBoost}, "mongo::Future"},
{"launch", kDoNotUse, {kStd}},
{"optional", kBoostReplacement, {kStd}, "boost::optional"},
{"packaged_task", kMongoReplacement, {kStd, kBoost}, "mongo::PackagedTask"},
{"promise", kMongoReplacement, {kStd, kBoost}, "mongo::Promise"},
{"recursive_mutex",
kDoNotUse,
{kStd, kBoost},
"A recursive mutex is often an indication of a design problem and is prone to deadlocks "
"because you don't know what code you are calling while holding the lock."},
{"shared_mutex",
kMongoReplacement,
{kStd, kBoost},
"a type from src/mongo/platform/rwmutex.h or a LockManager lock"},
{"shared_timed_mutex",
kMongoReplacement,
{kStd, kBoost},
"a type from src/mongo/platform/rwmutex.h or a LockManager lock"},
{"thread", kStdxReplacement, {kStd, kBoost}},
{"timed_mutex", kDoNotUse, {kStd, kBoost}, "timed_mutex acquisitions are not interruptible."},
{"unordered_map", kStdxReplacement, {kStd, kBoost}},
{"unordered_set", kStdxReplacement, {kStd, kBoost}},
};
// List of base enum names from the std and boost namespaces to be checked
const std::vector<BanInfo> baseEnumInfos = {};
// List of base function names from the std and boost namespaces to be checked
const std::vector<BanInfo> baseFuncInfos = {
{"async", kDoNotUse, {kStd, kBoost}},
{"get_terminate", kStdxReplacement, {kStd}},
{"notify_all_at_thread_exit", kDoNotUse, {kStd, kBoost}},
{"regex_search", kMongoReplacement, {kStd, kBoost}, "mongo::pcre::Regex"},
{"set_terminate", kStdxReplacement, {kStd}},
};
// List of base namespace names from the std and boost namespaces to be checked
const std::vector<BanInfo> baseNamespaceInfos = {};
const std::vector<BanInfo> allInfos = [] {
std::vector<BanInfo> infos;
auto add = [&](auto& r) {
infos.insert(infos.end(), r.begin(), r.end());
};
add(baseTypeInfos);
add(baseEnumInfos);
add(baseFuncInfos);
add(baseNamespaceInfos);
return infos;
}();
} // namespace
void MongoBannedNamesCheck::registerMatchers(ast_matchers::MatchFinder* Finder) {
// Register AST Matchers to find any use of the banned names.
Finder->addMatcher(declRefExpr(hasDeclaration(enumConstantDecl(
hasParent(enumDecl(hasAnyName(getNames(baseEnumInfos)))))))
.bind("enumConstants"),
this);
// The type matcher matches against DeclaratorDecl nodes, which means return types and
// references will not be matched here. Matching against only declarations should be
// sufficient and will not generate extra noise when developers decide to use a banned type.
Finder->addMatcher(
declaratorDecl(hasType(namedDecl(hasAnyName(getNames(baseTypeInfos))))).bind("typeNames"),
this);
Finder->addMatcher(
usingDecl(hasAnyUsingShadowDecl(hasTargetDecl(anyOf(hasAnyName(getNames(baseTypeInfos)),
hasAnyName(getNames(baseFuncInfos))))))
.bind("usingNames"),
this);
Finder->addMatcher(callExpr(callee(expr(hasDescendant(declRefExpr(hasDeclaration(
functionDecl(hasAnyName(getNames(baseFuncInfos)))))))))
.bind("functionNames"),
this);
Finder->addMatcher(declRefExpr(hasDeclaration(namedDecl(hasParent(
namespaceDecl(hasAnyName(getNames(baseNamespaceInfos)))))))
.bind("namespaceFromRef"),
this);
Finder->addMatcher(expr(hasType(qualType(hasDeclaration(namedDecl(hasParent(
namespaceDecl(hasAnyName(getNames(baseNamespaceInfos)))))))))
.bind("namespaceFromType"),
this);
Finder->addMatcher(
declaratorDecl(hasParent(namespaceDecl(hasAnyName(getNames(baseNamespaceInfos)))))
.bind("namespaceFromDecl"),
this);
}
void MongoBannedNamesCheck::checkNamespace(const SourceLocation loc,
const llvm::StringRef sourceText) {
if (!loc.isValid()) {
return;
}
for (auto&& info : allInfos) {
if (sourceText.contains(info.name.str()) && info.fromBannedNamespace(sourceText)) {
diag(loc, info.produceBanMessage(sourceText));
return;
}
}
}
// Get text as written in source from a clang node to ensure any alias makes it into the
// returned string.
template <typename Node>
llvm::StringRef getTextFromSource(const Node* node, const clang::ASTContext& Context) {
const clang::SourceManager& SM = Context.getSourceManager();
clang::SourceRange Range = node->getSourceRange();
return Lexer::getSourceText(CharSourceRange::getTokenRange(Range), SM, Context.getLangOpts());
}
void MongoBannedNamesCheck::check(const ast_matchers::MatchFinder::MatchResult& Result) {
auto chkAs = [&]<typename As>(std::type_identity<As>,
std::initializer_list<std::string> matchNames) {
for (auto&& name : matchNames) {
if (auto node = Result.Nodes.getNodeAs<As>(name)) {
checkNamespace(node->getBeginLoc(), getTextFromSource(node, *Result.Context));
}
}
};
chkAs(std::type_identity<Expr>{},
{
"functionNames",
"enumConstants",
"namespaceFromRef",
"namespaceFromType",
});
chkAs(std::type_identity<DeclaratorDecl>{},
{
"typeNames",
"namespaceFromDecl",
});
chkAs(std::type_identity<UsingDecl>{},
{
"usingNames",
});
}
} // namespace mongo::tidy

View File

@ -1,5 +1,5 @@
/**
* Copyright (C) 2023-present MongoDB, Inc.
* Copyright (C) 2025-present MongoDB, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
@ -34,17 +34,15 @@
namespace mongo::tidy {
/**
* check for usage of std::atomic
* Overrides the default registerMatchers function to add matcher to match the
* usage of std::atomic. overrides the default check function to
* flag the uses of std::atomic to enforce our commitment to boost::atomic
* MongoBannedNamesCheck is a custom clang-tidy check for detecting
* the usage of listed names from the std or boost libraries (std,
* boost, or global namespace) in the source code.
*/
class MongoStdAtomicCheck : public clang::tidy::ClangTidyCheck {
class MongoBannedNamesCheck : public clang::tidy::ClangTidyCheck {
public:
MongoStdAtomicCheck(clang::StringRef Name, clang::tidy::ClangTidyContext* Context);
using clang::tidy::ClangTidyCheck::ClangTidyCheck;
void registerMatchers(clang::ast_matchers::MatchFinder* Finder) override;
void check(const clang::ast_matchers::MatchFinder::MatchResult& Result) override;
void checkNamespace(clang::SourceLocation loc, llvm::StringRef name);
};
} // namespace mongo::tidy

View File

@ -1,187 +0,0 @@
/**
* Copyright (C) 2023-present MongoDB, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the Server Side Public License in all respects for
* all of the code used other than as permitted herein. If you modify file(s)
* with this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version. If you delete this
* exception statement from all source files in the program, then also delete
* it in the license file.
*/
#include "MongoPolyFillCheck.h"
#include <array>
#include <clang/Lex/Lexer.h>
namespace mongo::tidy {
using namespace clang;
using namespace clang::ast_matchers;
namespace {
// List of base polyfill type names from the std and boost namespaces to be checked
constexpr std::initializer_list<llvm::StringRef> basePolyfillTypeNames = {
"condition_variable",
"condition_variable_any",
"cv_status",
"future",
"future_status",
"launch",
"packaged_task",
"promise",
"recursive_mutex",
"shared_mutex",
"shared_timed_mutex",
"thread",
"timed_mutex",
"unordered_map",
"unordered_multimap",
"unordered_multiset",
"unordered_set",
};
// List of base polyfill enum names from the std and boost namespaces to be checked
constexpr std::initializer_list<llvm::StringRef> basePolyfillEnumNames = {
"cv_status",
"future_status",
"launch",
};
// List of base polyfill function names from the std and boost namespaces to be checked
constexpr std::initializer_list<llvm::StringRef> basePolyfillFuncNames = {
"async",
"get_terminate",
"notify_all_at_thread_exit",
"regex_search",
"set_terminate",
};
// List of base polyfill namespace names from the std and boost namespaces to be checked
constexpr std::initializer_list<llvm::StringRef> basePolyfillNamespaces = {
"this_thread",
"chrono",
};
} // namespace
// Generate a list of fully qualified polyfill names by prefixing each name
// in the input list with 'std::' and 'boost::'
std::vector<std::string> generateQualifiedPolyfillNames(
const std::vector<llvm::StringRef>& bannedNames) {
std::vector<std::string> fullyBannedNames;
for (auto&& name : bannedNames) {
fullyBannedNames.push_back("std::" + name.str());
fullyBannedNames.push_back("boost::" + name.str());
}
return fullyBannedNames;
}
MongoPolyFillCheck::MongoPolyFillCheck(StringRef Name, clang::tidy::ClangTidyContext* Context)
: ClangTidyCheck(Name, Context) {
std::vector<llvm::StringRef> basePolyfillNames;
basePolyfillNames.insert(
basePolyfillNames.end(), basePolyfillTypeNames.begin(), basePolyfillTypeNames.end());
basePolyfillNames.insert(
basePolyfillNames.end(), basePolyfillEnumNames.begin(), basePolyfillEnumNames.end());
basePolyfillNames.insert(
basePolyfillNames.end(), basePolyfillFuncNames.begin(), basePolyfillFuncNames.end());
basePolyfillNames.insert(
basePolyfillNames.end(), basePolyfillNamespaces.begin(), basePolyfillNamespaces.end());
// Generate a list of fully polyfill names
fullyQualifiedPolyfillNames = generateQualifiedPolyfillNames(basePolyfillNames);
}
void MongoPolyFillCheck::registerMatchers(ast_matchers::MatchFinder* Finder) {
// Register AST Matchers to find any use of the banned names.
Finder->addMatcher(declRefExpr(hasDeclaration(enumConstantDecl(
hasParent(enumDecl(hasAnyName(basePolyfillEnumNames))))))
.bind("bannedEnumConstants"),
this);
Finder->addMatcher(declaratorDecl(hasType(namedDecl(hasAnyName(basePolyfillTypeNames))))
.bind("bannedTypeNames"),
this);
Finder->addMatcher(callExpr(callee(expr(hasDescendant(declRefExpr(hasDeclaration(
functionDecl(hasAnyName(basePolyfillFuncNames))))))))
.bind("bannedFunctionNames"),
this);
Finder->addMatcher(declRefExpr(hasDeclaration(namedDecl(hasParent(
namespaceDecl(hasAnyName(basePolyfillNamespaces))))))
.bind("bannedNamespaceFromRef"),
this);
Finder->addMatcher(expr(hasType(qualType(hasDeclaration(namedDecl(
hasParent(namespaceDecl(hasAnyName(basePolyfillNamespaces))))))))
.bind("bannedNamespaceFromType"),
this);
Finder->addMatcher(declaratorDecl(hasParent(namespaceDecl(hasAnyName(basePolyfillNamespaces))))
.bind("bannedNamespaceFromDecl"),
this);
}
void MongoPolyFillCheck::checkBannedName(const SourceLocation loc, const llvm::StringRef name) {
// we catch this_thread but not this_thread::at_thread_exit
if (name.find("this_thread::at_thread_exit") != std::string::npos)
return;
// Check if the type string starts with 'std' or 'boost' and contains a banned name.
for (auto&& polyfillName : fullyQualifiedPolyfillNames) {
if ((name.starts_with("std::") || name.starts_with("boost::")) &&
name.find(polyfillName) != std::string::npos) {
if (loc.isValid())
diag(loc,
"Illegal use of banned name from std::/boost:: for %0. Consider using "
"alternatives such as the polyfills from the mongo::stdx:: namespace.")
<< name;
}
}
}
// Get full name as written in source from a clang node to ensure any alias makes it into the
// returned string.
template <typename Node>
llvm::StringRef getFullName(const Node* node, const clang::ASTContext& Context) {
const clang::SourceManager& SM = Context.getSourceManager();
clang::SourceRange Range = node->getSourceRange();
return Lexer::getSourceText(CharSourceRange::getTokenRange(Range), SM, Context.getLangOpts());
}
void MongoPolyFillCheck::check(const ast_matchers::MatchFinder::MatchResult& Result) {
for (auto&& matcher : {"bannedFunctionNames",
"bannedEnumConstants",
"bannedNamespaceFromRef",
"bannedNamespaceFromType"}) {
if (const auto* matched = Result.Nodes.getNodeAs<Expr>(matcher)) {
auto name = getFullName(matched, *Result.Context);
checkBannedName(matched->getBeginLoc(), std::move(name));
}
}
// DeclaratorDecl inherits from Decl instead of Expr, so it's extracted separately.
for (auto&& matcher : {"bannedTypeNames", "bannedNamespaceFromDecl"}) {
if (const auto* matched = Result.Nodes.getNodeAs<DeclaratorDecl>(matcher)) {
auto name = getFullName(matched, *Result.Context);
checkBannedName(matched->getBeginLoc(), std::move(name));
}
}
}
} // namespace mongo::tidy

View File

@ -1,55 +0,0 @@
/**
* Copyright (C) 2023-present MongoDB, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the Server Side Public License in all respects for
* all of the code used other than as permitted herein. If you modify file(s)
* with this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version. If you delete this
* exception statement from all source files in the program, then also delete
* it in the license file.
*/
#pragma once
#include <clang-tidy/ClangTidy.h>
#include <clang-tidy/ClangTidyCheck.h>
namespace mongo::tidy {
/**
* MongoPolyFillCheck is a custom clang-tidy check for detecting
* the usage of listed names from the std or boost namespace in the source code.
*
* It extends ClangTidyCheck and overrides the registerMatchers
* and check functions. The registerMatchers function adds matchers
* to identify the usage of banned names, while the check function
* flags the matched occurrences.
*/
class MongoPolyFillCheck : public clang::tidy::ClangTidyCheck {
public:
MongoPolyFillCheck(clang::StringRef Name, clang::tidy::ClangTidyContext* Context);
void registerMatchers(clang::ast_matchers::MatchFinder* Finder) override;
void check(const clang::ast_matchers::MatchFinder::MatchResult& Result) override;
void checkBannedName(clang::SourceLocation loc, llvm::StringRef name);
private:
std::vector<std::string> fullyQualifiedPolyfillNames;
};
} // namespace mongo::tidy

View File

@ -1,64 +0,0 @@
/**
* Copyright (C) 2023-present MongoDB, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the Server Side Public License in all respects for
* all of the code used other than as permitted herein. If you modify file(s)
* with this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version. If you delete this
* exception statement from all source files in the program, then also delete
* it in the license file.
*/
#include "MongoStdAtomicCheck.h"
#include <iostream>
#include "MongoTidyUtils.h"
namespace mongo::tidy {
using namespace clang;
using namespace clang::ast_matchers;
MongoStdAtomicCheck::MongoStdAtomicCheck(StringRef Name, clang::tidy::ClangTidyContext* Context)
: ClangTidyCheck(Name, Context) {}
void MongoStdAtomicCheck::registerMatchers(ast_matchers::MatchFinder* Finder) {
// match parameter decl, variable Decl, Field Decl, Reference Decl, Template Decl regarding
// std::atomic
Finder->addMatcher(loc(templateSpecializationType(
hasDeclaration(namedDecl(hasName("atomic"), isFromStdNamespace()))))
.bind("loc_atomic"),
this);
}
void MongoStdAtomicCheck::check(const ast_matchers::MatchFinder::MatchResult& Result) {
const auto* loc_match = Result.Nodes.getNodeAs<TypeLoc>("loc_atomic");
if (loc_match) {
diag(loc_match->getBeginLoc(),
"Illegal use of prohibited std::atomic<T>, use Atomic<T> or other types from "
"\"mongo/platform/atomic.h\"");
}
}
} // namespace mongo::tidy

View File

@ -1,73 +0,0 @@
/**
* Copyright (C) 2023-present MongoDB, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the Server Side Public License in all respects for
* all of the code used other than as permitted herein. If you modify file(s)
* with this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version. If you delete this
* exception statement from all source files in the program, then also delete
* it in the license file.
*/
#include "MongoStdOptionalCheck.h"
#include <iostream>
#include "MongoTidyUtils.h"
namespace mongo::tidy {
using namespace clang;
using namespace clang::ast_matchers;
MongoStdOptionalCheck::MongoStdOptionalCheck(StringRef Name, clang::tidy::ClangTidyContext* Context)
: ClangTidyCheck(Name, Context) {}
void MongoStdOptionalCheck::registerMatchers(ast_matchers::MatchFinder* Finder) {
// match using std::optional;
Finder->addMatcher(usingDecl(hasAnyUsingShadowDecl(hasTargetDecl(
allOf(hasName("optional"), isFromStdNamespace()))))
.bind("decl_optional"),
this);
// match parameter decl, variable Decl, Field Decl, Reference Decl, Template Decl regarding
// std::optional
Finder->addMatcher(loc(templateSpecializationType(hasDeclaration(
namedDecl(hasName("optional"), isFromStdNamespace()))))
.bind("loc_optional"),
this);
}
void MongoStdOptionalCheck::check(const ast_matchers::MatchFinder::MatchResult& Result) {
const auto* decl_match = Result.Nodes.getNodeAs<UsingDecl>("decl_optional");
const auto* loc_match = Result.Nodes.getNodeAs<TypeLoc>("loc_optional");
if (decl_match) {
diag(decl_match->getBeginLoc(), "Use of std::optional, use boost::optional instead. ");
}
if (loc_match && !loc_match->getBeginLoc().isInvalid()) {
diag(loc_match->getBeginLoc(), "Use of std::optional, use boost::optional instead. ");
}
}
} // namespace mongo::tidy

View File

@ -1,49 +0,0 @@
/**
* Copyright (C) 2023-present MongoDB, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the Server Side Public License in all respects for
* all of the code used other than as permitted herein. If you modify file(s)
* with this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version. If you delete this
* exception statement from all source files in the program, then also delete
* it in the license file.
*/
#pragma once
#include <clang-tidy/ClangTidy.h>
#include <clang-tidy/ClangTidyCheck.h>
namespace mongo::tidy {
/**
* check for usage of std::optional
* Overrides the default registerMatchers function to add matcher to match the
* usage of std::optional. overrides the default check function to
* flag the uses of std::optional to enforce our commitment to boost::optional
*/
class MongoStdOptionalCheck : public clang::tidy::ClangTidyCheck {
public:
MongoStdOptionalCheck(clang::StringRef Name, clang::tidy::ClangTidyContext* Context);
void registerMatchers(clang::ast_matchers::MatchFinder* Finder) override;
void check(const clang::ast_matchers::MatchFinder::MatchResult& Result) override;
};
} // namespace mongo::tidy

View File

@ -28,6 +28,7 @@
*/
#include "MongoAssertCheck.h"
#include "MongoBannedNamesCheck.h"
#include "MongoCctypeCheck.h"
#include "MongoCollectionShardingRuntimeCheck.h"
#include "MongoConfigHeaderCheck.h"
@ -39,11 +40,8 @@
#include "MongoInvariantStatusIsOKCheck.h"
#include "MongoMacroDefinitionLeaksCheck.h"
#include "MongoNoUniqueAddressCheck.h"
#include "MongoPolyFillCheck.h"
#include "MongoRWMutexCheck.h"
#include "MongoRandCheck.h"
#include "MongoStdAtomicCheck.h"
#include "MongoStdOptionalCheck.h"
#include "MongoStringDataConstRefCheck.h"
#include "MongoStringDataStringViewApi.h"
#include "MongoTraceCheck.h"
@ -70,10 +68,8 @@ public:
CheckFactories.registerCheck<MongoCxx20BannedIncludesCheck>(
"mongo-cxx20-banned-includes-check");
CheckFactories.registerCheck<MongoCxx20StdChronoCheck>("mongo-cxx20-std-chrono-check");
CheckFactories.registerCheck<MongoStdOptionalCheck>("mongo-std-optional-check");
CheckFactories.registerCheck<MongoVolatileCheck>("mongo-volatile-check");
CheckFactories.registerCheck<MongoTraceCheck>("mongo-trace-check");
CheckFactories.registerCheck<MongoStdAtomicCheck>("mongo-std-atomic-check");
CheckFactories.registerCheck<MongoAssertCheck>("mongo-assert-check");
CheckFactories.registerCheck<MongoFCVConstantCheck>("mongo-fcv-constant-check");
CheckFactories.registerCheck<MongoUnstructuredLogCheck>("mongo-unstructured-log-check");
@ -82,7 +78,7 @@ public:
CheckFactories.registerCheck<MongoMacroDefinitionLeaksCheck>(
"mongo-macro-definition-leaks-check");
CheckFactories.registerCheck<MongoRandCheck>("mongo-rand-check");
CheckFactories.registerCheck<MongoPolyFillCheck>("mongo-polyfill-check");
CheckFactories.registerCheck<MongoBannedNamesCheck>("mongo-banned-names-check");
CheckFactories.registerCheck<MongoNoUniqueAddressCheck>("mongo-no-unique-address-check");
CheckFactories.registerCheck<MongoStringDataConstRefCheck>(
"mongo-stringdata-const-ref-check");

View File

@ -26,9 +26,7 @@ tests = [
"test_MongoConfigHeaderCheck",
"test_MongoCxx20BannedIncludesCheck",
"test_MongoCxx20StdChronoCheck",
"test_MongoStdOptionalCheck",
"test_MongoTraceCheck",
"test_MongoStdAtomicCheck",
"test_MongoAssertCheck",
"test_MongoFCVConstantCheck",
"test_MongoUnstructuredLogCheck",
@ -36,7 +34,7 @@ tests = [
"test_MongoMacroDefinitionLeaksCheck",
"test_MongoRandCheck",
"test_MongoRWMutexCheck",
"test_MongoPolyFillCheck",
"test_MongoBannedNamesCheck",
"test_MongoNoUniqueAddressCheck",
"test_MongoStringDataConstRefCheck1",
"test_MongoStringDataConstRefCheck2",

View File

@ -114,24 +114,6 @@ class MongoTidyTests(unittest.TestCase):
]
self.run_clang_tidy()
def test_MongoStdOptionalCheck(self):
msg = "Use of std::optional, use boost::optional instead."
opt = "mongo-std-optional-check,-warnings-as-errors"
self.expected_output = [
f"{msg} [{opt}]\n{n:5} | {src}"
for n, src in [
(36, "void f(std::optional<std::string> parameterDeclTest) {"),
(37, " std::optional<std::string> variableDeclTest;"),
(42, " std::optional<int> fieldDeclTest = 5;"),
(46, "void functionName(const std::optional<int>& referenceDeclTest) {"),
(52, "std::optional<std::string> functionReturnTypeDeclTest(StringData name);"),
(55, "std::optional<T> templateDeclTest;"),
(57, "using std::optional;"),
]
]
self.run_clang_tidy()
def test_MongoVolatileCheck(self):
msg = 'Illegal use of the volatile storage keyword, use Atomic instead from "mongo/platform/atomic.h"'
opt = "mongo-volatile-check,-warnings-as-errors"
@ -159,19 +141,6 @@ class MongoTidyTests(unittest.TestCase):
self.run_clang_tidy()
def test_MongoStdAtomicCheck(self):
msg = 'Illegal use of prohibited std::atomic<T>, use Atomic<T> or other types from "mongo/platform/atomic.h"'
opt = "mongo-std-atomic-check,-warnings-as-errors"
self.expected_output = [
f"{msg} [{opt}]\n{n:5} | {src}"
for n, src in [
(6, "std::atomic<int> atomic_var;"),
(10, " std::atomic<int> field_decl;"),
]
]
self.run_clang_tidy()
def test_MongoAssertCheck(self):
self.expected_output = [
"error: Illegal use of the bare assert macro, use a macro function from assert_util.h instead",
@ -244,26 +213,29 @@ class MongoTidyTests(unittest.TestCase):
self.run_clang_tidy()
def test_MongoPolyFillCheck(self):
def test_MongoBannedNamesCheck(self):
stdx_replacement_str = "Consider using alternatives such as the polyfills from the mongo::stdx:: namespace."
test_names = [
"std::cv_status wait_result{std::cv_status::timeout}",
"std::cv_status::timeout",
"std::cv_status::timeout",
"std::this_thread::get_id",
"std::get_terminate()",
"std::chrono::seconds(1)",
"std::future<int> myFuture",
"std::condition_variable cv",
"std::unordered_map<int, int> myMap",
"boost::unordered_map<int, int> boostMap",
"std::regex_search(std::string(\"\"), std::regex(\"\"))",
("std::get_terminate()", stdx_replacement_str),
("std::future<int> myFuture", "Consider using mongo::Future instead."),
("std::recursive_mutex recursiveMut", "Do not use. A recursive mutex is often an indication of a design problem and is prone to deadlocks because you don't know what code you are calling while holding the lock."),
("const std::condition_variable cv", stdx_replacement_str),
("static std::unordered_map<int, int> myMap", stdx_replacement_str),
("boost::unordered_map<int, int> boostMap", stdx_replacement_str),
("std::regex_search(std::string(\"\"), std::regex(\"\"))", "Consider using mongo::pcre::Regex instead."),
("std::atomic<int> atomicVar", "Consider using mongo::Atomic<T> instead."),
("std::optional<std::string> strOpt", "Consider using boost::optional instead."),
("std::atomic<int> fieldDecl", "Consider using mongo::Atomic<T> instead."),
("std::optional<T> templateDecl", "Consider using boost::optional instead."),
("using std::optional", "Consider using boost::optional instead."),
]
self.expected_output = [
"error: Illegal use of banned name from std::/boost:: for "
+ name
+ ". Consider using alternatives such as the polyfills from the mongo::stdx:: namespace."
for name in test_names
"error: Forbidden use of banned name in "
+ name + ". " + msg
+ " Use '// NOLINT' if usage is absolutely necessary. Be especially careful doing so outside of test code."
for (name, msg) in test_names
]
self.run_clang_tidy()

View File

@ -0,0 +1,32 @@
#include <atomic>
#include <condition_variable>
#include <future>
#include <mutex>
#include <regex>
#include <thread>
#include <unordered_map>
#include <boost/unordered_map.hpp>
namespace mongo {
void mongoBannedNamesCheckTest() {
std::get_terminate();
std::future<int> myFuture;
std::recursive_mutex recursiveMut;
const std::condition_variable cv;
static std::unordered_map<int, int> myMap;
boost::unordered_map<int, int> boostMap;
std::atomic<int> atomicVar;
std::optional<std::string> strOpt;
std::regex_search(std::string(""), std::regex(""));
}
struct AtomicStruct {
std::atomic<int> fieldDecl;
};
template <typename T>
std::optional<T> templateDecl;
using std::optional;
} // namespace mongo

View File

@ -0,0 +1,2 @@
Checks: '-*,mongo-banned-names-check'
WarningsAsErrors: '*'

View File

@ -1,26 +0,0 @@
#include <condition_variable>
#include <future>
#include <mutex>
#include <regex>
#include <thread>
#include <unordered_map>
#include <boost/unordered_map.hpp>
namespace mongo {
void mongoPolyFillCheckTest() {
std::cv_status wait_result{std::cv_status::timeout};
if (wait_result == std::cv_status::timeout) {
}
std::this_thread::get_id();
std::get_terminate();
std::chrono::seconds(1);
std::chrono::seconds seconds(1);
std::future<int> myFuture;
std::condition_variable cv;
std::unordered_map<int, int> myMap;
boost::unordered_map<int, int> boostMap;
std::regex_search(std::string(""), std::regex(""));
}
} // namespace mongo

View File

@ -1,2 +0,0 @@
Checks: '-*,mongo-polyfill-check'
WarningsAsErrors: '*'

View File

@ -1,13 +0,0 @@
#include <atomic>
namespace mongo {
// Variable Decl
std::atomic<int> atomic_var;
// Field Decl
struct structName {
std::atomic<int> field_decl;
};
} // namespace mongo

View File

@ -1,2 +0,0 @@
Checks: '-*,mongo-std-atomic-check'
WarningsAsErrors: '*'

View File

@ -1,59 +0,0 @@
/**
* Copyright (C) 2023-present MongoDB, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the Server Side Public License in all respects for
* all of the code used other than as permitted herein. If you modify file(s)
* with this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version. If you delete this
* exception statement from all source files in the program, then also delete
* it in the license file.
*/
#include <optional>
#include <string>
namespace mongo {
// Variable Decl and Parameter Decl
void f(std::optional<std::string> parameterDeclTest) {
std::optional<std::string> variableDeclTest;
}
// Field Decl
struct CertInformationToLog {
std::optional<int> fieldDeclTest = 5;
};
// Reference Decl
void functionName(const std::optional<int>& referenceDeclTest) {
return;
}
class StringData {};
// Function Return type Decl
std::optional<std::string> functionReturnTypeDeclTest(StringData name);
template <typename T>
std::optional<T> templateDeclTest;
using std::optional;
} // namespace mongo

View File

@ -1,2 +0,0 @@
Checks: '-*,mongo-std-optional-check'
WarningsAsErrors: '*'

View File

@ -102,7 +102,7 @@ TEST(BasicLockableAdapter, TestWithMutexTypes) {
}
{
stdx::timed_mutex mut;
std::timed_mutex mut; // NOLINT
callUnderLock(mut);
}

View File

@ -53,13 +53,13 @@ public:
return _f.wait();
}
bool waitFor(stdx::chrono::milliseconds dur) const {
return _f.wait_for(dur) == stdx::future_status::ready;
bool waitFor(std::chrono::milliseconds dur) const {
return _f.wait_for(dur) == std::future_status::ready; // NOLINT
}
private:
stdx::promise<void> _p;
stdx::future<void> _f{_p.get_future()};
std::promise<void> _p; // NOLINT
std::future<void> _f{_p.get_future()}; // NOLINT
};
using OnPhdrFunc = std::function<int(dl_phdr_info*, size_t)>;