mirror of https://github.com/mongodb/mongo
SERVER-71741 Move macro definition leaks check to clang-tidy
This commit is contained in:
parent
24af688c58
commit
d981535c05
|
|
@ -39,6 +39,7 @@ Checks: '-*,
|
|||
mongo-cxx20-banned-includes-check,
|
||||
mongo-header-bracket-check,
|
||||
mongo-std-atomic-check,
|
||||
mongo-macro-definition-leaks-check,
|
||||
mongo-mutex-check,
|
||||
mongo-assert-check,
|
||||
mongo-std-optional-check,
|
||||
|
|
|
|||
|
|
@ -141,9 +141,6 @@ class Linter:
|
|||
self._check_newlines()
|
||||
self._check_and_strip_comments()
|
||||
|
||||
# File-level checks
|
||||
self._check_macro_definition_leaks()
|
||||
|
||||
# Line-level checks
|
||||
for linenum in range(start_line, len(self.clean_lines)):
|
||||
if not self.clean_lines[linenum]:
|
||||
|
|
@ -209,26 +206,6 @@ class Linter:
|
|||
|
||||
self.clean_lines.append(clean_line)
|
||||
|
||||
def _check_macro_definition_leaks(self):
|
||||
"""Some header macros should appear in define/undef pairs."""
|
||||
if not _RE_HEADER.search(self.file_name):
|
||||
return
|
||||
# Naive check: doesn't consider `#if` scoping.
|
||||
# Assumes an #undef matches the nearest #define.
|
||||
for macro in ['MONGO_LOGV2_DEFAULT_COMPONENT']:
|
||||
re_define = re.compile(fr"^\s*#\s*define\s+{macro}\b")
|
||||
re_undef = re.compile(fr"^\s*#\s*undef\s+{macro}\b")
|
||||
def_line = None
|
||||
for idx, line in enumerate(self.clean_lines):
|
||||
if def_line is None:
|
||||
if re_define.match(line):
|
||||
def_line = idx
|
||||
else:
|
||||
if re_undef.match(line):
|
||||
def_line = None
|
||||
if def_line is not None:
|
||||
self._error(def_line, 'mongodb/undefmacro', f'Missing "#undef {macro}"')
|
||||
|
||||
def _check_for_mongo_polyfill(self, linenum):
|
||||
line = self.clean_lines[linenum]
|
||||
match = _RE_PATTERN_MONGO_POLYFILL.search(line)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,114 @@
|
|||
/**
|
||||
* 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 "MongoMacroDefinitionLeaksCheck.h"
|
||||
|
||||
#include <clang/Lex/PPCallbacks.h>
|
||||
#include <clang/Lex/Preprocessor.h>
|
||||
|
||||
namespace mongo::tidy {
|
||||
|
||||
using namespace clang;
|
||||
using namespace clang::ast_matchers;
|
||||
|
||||
// Callbacks for handling preprocessor events
|
||||
class MongoMacroPPCallbacks : public clang::PPCallbacks {
|
||||
public:
|
||||
explicit MongoMacroPPCallbacks(MongoMacroDefinitionLeaksCheck& Check,
|
||||
clang::LangOptions LangOpts,
|
||||
const clang::SourceManager& SM)
|
||||
: Check(Check), LangOpts(LangOpts), SM(SM) {}
|
||||
|
||||
// Callback for when a macro is defined
|
||||
void MacroDefined(const clang::Token& MacroNameTok, const clang::MacroDirective* MD) override {
|
||||
llvm::StringRef macroName = MacroNameTok.getIdentifierInfo()->getName();
|
||||
if (macroName == "MONGO_LOGV2_DEFAULT_COMPONENT") {
|
||||
defineUndefDiff += 1;
|
||||
lastMacroLocation = MD->getLocation();
|
||||
}
|
||||
}
|
||||
|
||||
// Callback for when a macro is undefined
|
||||
void MacroUndefined(const clang::Token& MacroNameTok,
|
||||
const clang::MacroDefinition& MD,
|
||||
const clang::MacroDirective* Undef) override {
|
||||
llvm::StringRef macroName = MacroNameTok.getIdentifierInfo()->getName();
|
||||
|
||||
if (macroName == "MONGO_LOGV2_DEFAULT_COMPONENT") {
|
||||
defineUndefDiff -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Callback for when a file is included or excluded
|
||||
void FileChanged(SourceLocation Loc,
|
||||
FileChangeReason Reason,
|
||||
SrcMgr::CharacteristicKind FileType,
|
||||
FileID PrevFID) override {
|
||||
if (Reason != EnterFile && Reason != ExitFile)
|
||||
return;
|
||||
|
||||
const FileEntry* CurrentFile = SM.getFileEntryForID(SM.getFileID(Loc));
|
||||
if (!CurrentFile)
|
||||
return;
|
||||
|
||||
if (Reason == EnterFile) {
|
||||
// Push the file to the stack
|
||||
fileStack.push(CurrentFile->getName().str());
|
||||
defineUndefDiff = 0;
|
||||
} else if (Reason == ExitFile && !fileStack.empty()) {
|
||||
// Get the top file from the stack
|
||||
std::string currentFileName = fileStack.top();
|
||||
fileStack.pop();
|
||||
if (defineUndefDiff != 0) {
|
||||
Check.diag(lastMacroLocation, "Missing #undef 'MONGO_LOGV2_DEFAULT_COMPONENT'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
MongoMacroDefinitionLeaksCheck& Check;
|
||||
clang::LangOptions LangOpts;
|
||||
const clang::SourceManager& SM;
|
||||
int defineUndefDiff = 0;
|
||||
clang::SourceLocation lastMacroLocation;
|
||||
std::stack<std::string> fileStack;
|
||||
};
|
||||
|
||||
MongoMacroDefinitionLeaksCheck::MongoMacroDefinitionLeaksCheck(
|
||||
llvm::StringRef Name, clang::tidy::ClangTidyContext* Context)
|
||||
: ClangTidyCheck(Name, Context) {}
|
||||
|
||||
void MongoMacroDefinitionLeaksCheck::registerPPCallbacks(const clang::SourceManager& SM,
|
||||
clang::Preprocessor* PP,
|
||||
clang::Preprocessor* ModuleExpanderPP) {
|
||||
PP->addPPCallbacks(::std::make_unique<MongoMacroPPCallbacks>(*this, getLangOpts(), SM));
|
||||
}
|
||||
|
||||
} // namespace mongo::tidy
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
/**
|
||||
* 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 {
|
||||
|
||||
/**
|
||||
* MongoMacroDefinitionLeaksCheck is a custom clang-tidy check for detecting
|
||||
* the imbalance between the definitions and undefinitions of the macro
|
||||
* "MONGO_LOGV2_DEFAULT_COMPONENT" in the same file.
|
||||
*
|
||||
* It extends ClangTidyCheck and overrides the registerPPCallbacks function. The registerPPCallbacks
|
||||
* function adds a custom Preprocessor callback class (MongoMacroPPCallbacks) to handle
|
||||
* preprocessor events and detect an imbalance in the definitions and undefinitions of the
|
||||
* "MONGO_LOGV2_DEFAULT_COMPONENT" macro within each file.
|
||||
*
|
||||
* If a .h or .hpp file is found to have a non-zero difference between definitions and undefinitions
|
||||
* of the "MONGO_LOGV2_DEFAULT_COMPONENT" macro, it's considered a leak and the check raises a
|
||||
* diagnostic message pointing out the location of the last macro definition.
|
||||
*/
|
||||
class MongoMacroDefinitionLeaksCheck : public clang::tidy::ClangTidyCheck {
|
||||
public:
|
||||
MongoMacroDefinitionLeaksCheck(clang::StringRef Name, clang::tidy::ClangTidyContext* Context);
|
||||
void registerPPCallbacks(const clang::SourceManager& SM,
|
||||
clang::Preprocessor* PP,
|
||||
clang::Preprocessor* ModuleExpanderPP) override;
|
||||
};
|
||||
|
||||
} // namespace mongo::tidy
|
||||
|
|
@ -34,6 +34,7 @@
|
|||
#include "MongoCxx20BannedIncludesCheck.h"
|
||||
#include "MongoFCVConstantCheck.h"
|
||||
#include "MongoHeaderBracketCheck.h"
|
||||
#include "MongoMacroDefinitionLeaksCheck.h"
|
||||
#include "MongoMutexCheck.h"
|
||||
#include "MongoStdAtomicCheck.h"
|
||||
#include "MongoStdOptionalCheck.h"
|
||||
|
|
@ -70,6 +71,8 @@ public:
|
|||
CheckFactories.registerCheck<MongoUnstructuredLogCheck>("mongo-unstructured-log-check");
|
||||
CheckFactories.registerCheck<MongoCollectionShardingRuntimeCheck>(
|
||||
"mongo-collection-sharding-runtime-check");
|
||||
CheckFactories.registerCheck<MongoMacroDefinitionLeaksCheck>(
|
||||
"mongo-macro-definition-leaks-check");
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -136,6 +136,7 @@ mongo_custom_check = env.SharedLibrary(
|
|||
"MongoFCVConstantCheck.cpp",
|
||||
"MongoUnstructuredLogCheck.cpp",
|
||||
"MongoCollectionShardingRuntimeCheck.cpp",
|
||||
"MongoMacroDefinitionLeaksCheck.cpp",
|
||||
],
|
||||
LIBDEPS_NO_INHERIT=[
|
||||
'$BUILD_DIR/third_party/shim_allocator',
|
||||
|
|
|
|||
|
|
@ -312,6 +312,21 @@ class MongoTidyTests(unittest.TestCase):
|
|||
|
||||
self.run_clang_tidy()
|
||||
|
||||
def test_MongoMacroDefinitionLeaksCheck(self):
|
||||
self.write_config(
|
||||
textwrap.dedent("""\
|
||||
Checks: '-*,mongo-macro-definition-leaks-check'
|
||||
WarningsAsErrors: '*'
|
||||
HeaderFilterRegex: '(mongo/.*)'
|
||||
"""))
|
||||
|
||||
self.expected_output = [
|
||||
"Missing #undef 'MONGO_LOGV2_DEFAULT_COMPONENT'",
|
||||
]
|
||||
|
||||
self.run_clang_tidy()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ if env.GetOption('ninja') == 'disabled':
|
|||
'test_MongoFCVConstantCheck.cpp',
|
||||
'test_MongoUnstructuredLogCheck.cpp',
|
||||
'test_MongoCollectionShardingRuntimeCheck.cpp',
|
||||
'test_MongoMacroDefinitionLeaksCheck.cpp',
|
||||
]
|
||||
|
||||
# So that we can do fast runs, we will generate a separate compilation database file for each
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
#include "test_MongoMacroDefinitionLeaksCheck.h"
|
||||
|
|
@ -0,0 +1 @@
|
|||
#define MONGO_LOGV2_DEFAULT_COMPONENT 1
|
||||
Loading…
Reference in New Issue