forked from OSchip/llvm-project
[clang-tidy] Check for dynamically initialized statics in headers.
Finds instances where variables with static storage are initialized dynamically in header files. Reviewed By: aaron.ballman, alexfh Patch by Charles Zhang! Differential Revision: https://reviews.llvm.org/D62829 llvm-svn: 369568
This commit is contained in:
parent
ed18e70c86
commit
f24c1e6b51
|
@ -16,6 +16,7 @@
|
|||
#include "BranchCloneCheck.h"
|
||||
#include "CopyConstructorInitCheck.h"
|
||||
#include "DanglingHandleCheck.h"
|
||||
#include "DynamicStaticInitializersCheck.h"
|
||||
#include "ExceptionEscapeCheck.h"
|
||||
#include "FoldInitTypeCheck.h"
|
||||
#include "ForwardDeclarationNamespaceCheck.h"
|
||||
|
@ -73,6 +74,8 @@ public:
|
|||
"bugprone-copy-constructor-init");
|
||||
CheckFactories.registerCheck<DanglingHandleCheck>(
|
||||
"bugprone-dangling-handle");
|
||||
CheckFactories.registerCheck<DynamicStaticInitializersCheck>(
|
||||
"bugprone-dynamic-static-initializers");
|
||||
CheckFactories.registerCheck<ExceptionEscapeCheck>(
|
||||
"bugprone-exception-escape");
|
||||
CheckFactories.registerCheck<FoldInitTypeCheck>(
|
||||
|
|
|
@ -8,6 +8,7 @@ add_clang_library(clangTidyBugproneModule
|
|||
BugproneTidyModule.cpp
|
||||
CopyConstructorInitCheck.cpp
|
||||
DanglingHandleCheck.cpp
|
||||
DynamicStaticInitializersCheck.cpp
|
||||
ExceptionEscapeCheck.cpp
|
||||
FoldInitTypeCheck.cpp
|
||||
ForwardDeclarationNamespaceCheck.cpp
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
//===--- DynamicStaticInitializersCheck.cpp - clang-tidy ------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "DynamicStaticInitializersCheck.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
||||
|
||||
using namespace clang::ast_matchers;
|
||||
|
||||
namespace clang {
|
||||
namespace tidy {
|
||||
namespace bugprone {
|
||||
|
||||
AST_MATCHER(clang::VarDecl, hasConstantDeclaration) {
|
||||
const Expr *Init = Node.getInit();
|
||||
if (Init && !Init->isValueDependent()) {
|
||||
if (Node.isConstexpr())
|
||||
return true;
|
||||
return Node.checkInitIsICE();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
DynamicStaticInitializersCheck::DynamicStaticInitializersCheck(StringRef Name,
|
||||
ClangTidyContext *Context)
|
||||
: ClangTidyCheck(Name, Context),
|
||||
RawStringHeaderFileExtensions(Options.getLocalOrGlobal(
|
||||
"HeaderFileExtensions", utils::defaultHeaderFileExtensions())) {
|
||||
if (!utils::parseHeaderFileExtensions(RawStringHeaderFileExtensions,
|
||||
HeaderFileExtensions, ',')) {
|
||||
llvm::errs() << "Invalid header file extension: "
|
||||
<< RawStringHeaderFileExtensions << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void DynamicStaticInitializersCheck::storeOptions(
|
||||
ClangTidyOptions::OptionMap &Opts) {
|
||||
Options.store(Opts, "HeaderFileExtensions", RawStringHeaderFileExtensions);
|
||||
}
|
||||
|
||||
void DynamicStaticInitializersCheck::registerMatchers(MatchFinder *Finder) {
|
||||
if (!getLangOpts().CPlusPlus || getLangOpts().ThreadsafeStatics)
|
||||
return;
|
||||
Finder->addMatcher(
|
||||
varDecl(hasGlobalStorage(), unless(hasConstantDeclaration())).bind("var"),
|
||||
this);
|
||||
}
|
||||
|
||||
void DynamicStaticInitializersCheck::check(const MatchFinder::MatchResult &Result) {
|
||||
const auto *Var = Result.Nodes.getNodeAs<VarDecl>("var");
|
||||
SourceLocation Loc = Var->getLocation();
|
||||
if (!Loc.isValid() || !utils::isPresumedLocInHeaderFile(Loc, *Result.SourceManager,
|
||||
HeaderFileExtensions))
|
||||
return;
|
||||
// If the initializer is a constant expression, then the compiler
|
||||
// doesn't have to dynamically initialize it.
|
||||
diag(Loc, "static variable %0 may be dynamically initialized in this header file")
|
||||
<< Var;
|
||||
}
|
||||
|
||||
} // namespace bugprone
|
||||
} // namespace tidy
|
||||
} // namespace clang
|
|
@ -0,0 +1,43 @@
|
|||
//===--- DynamicStaticInitializersCheck.h - clang-tidy ----------*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_DYNAMIC_STATIC_INITIALIZERS_CHECK_H
|
||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_DYNAMIC_STATIC_INITIALIZERS_CHECK_H
|
||||
|
||||
#include "../ClangTidyCheck.h"
|
||||
#include "../utils/HeaderFileExtensionsUtils.h"
|
||||
|
||||
namespace clang {
|
||||
namespace tidy {
|
||||
namespace bugprone {
|
||||
|
||||
/// Finds dynamically initialized static variables in header files.
|
||||
///
|
||||
/// The check supports these options:
|
||||
/// - `HeaderFileExtensions`: a comma-separated list of filename extensions of
|
||||
/// header files (The filename extensions should not contain "." prefix).
|
||||
/// "h,hh,hpp,hxx" by default.
|
||||
/// For extension-less header files, using an empty string or leaving an
|
||||
/// empty string between "," if there are other filename extensions.
|
||||
class DynamicStaticInitializersCheck : public ClangTidyCheck {
|
||||
public:
|
||||
DynamicStaticInitializersCheck(StringRef Name, ClangTidyContext *Context);
|
||||
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
|
||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||
|
||||
private:
|
||||
const std::string RawStringHeaderFileExtensions;
|
||||
utils::HeaderFileExtensionsSet HeaderFileExtensions;
|
||||
};
|
||||
|
||||
} // namespace bugprone
|
||||
} // namespace tidy
|
||||
} // namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_DYNAMIC_STATIC_INITIALIZERS_CHECK_H
|
|
@ -67,6 +67,12 @@ The improvements are...
|
|||
Improvements to clang-tidy
|
||||
--------------------------
|
||||
|
||||
- New :doc:`bugprone-dynamic-static-initializers
|
||||
<clang-tidy/checks/bugprone-dynamic-static-initializers>` check.
|
||||
|
||||
Finds instances where variables with static storage are initialized
|
||||
dynamically in header files.
|
||||
|
||||
- New :doc:`linuxkernel-must-use-errs
|
||||
<clang-tidy/checks/linuxkernel-must-use-errs>` check.
|
||||
|
||||
|
@ -79,7 +85,6 @@ Improvements to clang-tidy
|
|||
Finds uses of deprecated Googletest APIs with names containing ``case`` and
|
||||
replaces them with equivalent APIs with ``suite``.
|
||||
|
||||
|
||||
Improvements to include-fixer
|
||||
-----------------------------
|
||||
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
.. title:: clang-tidy - bugprone-dynamic-static-initializers
|
||||
|
||||
bugprone-dynamic-static-initializers
|
||||
====================================
|
||||
|
||||
Finds instances of static variables that are dynamically initialized
|
||||
in header files.
|
||||
|
||||
This can pose problems in certain multithreaded contexts. For example,
|
||||
when disabling compiler generated synchronization instructions for
|
||||
static variables initialized at runtime (e.g. by ``-fno-threadsafe-statics``), even if a particular project
|
||||
takes the necessary precautions to prevent race conditions during
|
||||
initialization by providing their own synchronization, header files included from other projects may
|
||||
not. Therefore, such a check is helpful for ensuring that disabling
|
||||
compiler generated synchronization for static variable initialization will not cause
|
||||
problems.
|
||||
|
||||
Consider the following code:
|
||||
|
||||
-- code-block:: c
|
||||
|
||||
int foo() {
|
||||
static int k = bar();
|
||||
return k;
|
||||
}
|
||||
|
||||
When synchronization of static initialization is disabled, if two threads both call `foo` for the first time, there is the possibility that `k` will be double initialized, creating a race condition.
|
|
@ -44,6 +44,7 @@ Clang-Tidy Checks
|
|||
bugprone-branch-clone
|
||||
bugprone-copy-constructor-init
|
||||
bugprone-dangling-handle
|
||||
bugprone-dynamic-static-initializers
|
||||
bugprone-exception-escape
|
||||
bugprone-fold-init-type
|
||||
bugprone-forward-declaration-namespace
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
// RUN: %check_clang_tidy %s bugprone-dynamic-static-initializers %t -- -- -fno-threadsafe-statics
|
||||
|
||||
int fact(int n) {
|
||||
return (n == 0) ? 1 : n * fact(n - 1);
|
||||
}
|
||||
|
||||
int static_thing = fact(5);
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: static variable 'static_thing' may be dynamically initialized in this header file [bugprone-dynamic-static-initializers]
|
||||
|
||||
int sample() {
|
||||
int x;
|
||||
return x;
|
||||
}
|
||||
|
||||
int dynamic_thing = sample();
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: static variable 'dynamic_thing' may be dynamically initialized in this header file [bugprone-dynamic-static-initializers]
|
||||
|
||||
int not_so_bad = 12 + 4942; // no warning
|
||||
|
||||
extern int bar();
|
||||
|
||||
int foo() {
|
||||
static int k = bar();
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: static variable 'k' may be dynamically initialized in this header file [bugprone-dynamic-static-initializers]
|
||||
return k;
|
||||
}
|
||||
|
||||
int bar2() {
|
||||
return 7;
|
||||
}
|
||||
|
||||
int foo2() {
|
||||
// This may work fine when optimization is enabled because bar() can
|
||||
// be turned into a constant 7. But without optimization, it can
|
||||
// cause problems. Therefore, we must err on the side of conservatism.
|
||||
static int x = bar2();
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: static variable 'x' may be dynamically initialized in this header file [bugprone-dynamic-static-initializers]
|
||||
return x;
|
||||
}
|
||||
|
||||
int foo3() {
|
||||
static int p = 7 + 83; // no warning
|
||||
return p;
|
||||
}
|
Loading…
Reference in New Issue