Imported Upstream version 6.10.0.49

Former-commit-id: 1d6753294b2993e1fbf92de9366bb9544db4189b
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2020-01-16 16:38:04 +00:00
parent d94e79959b
commit 468663ddbb
48518 changed files with 2789335 additions and 61176 deletions

View File

@ -0,0 +1,30 @@
set(LLVM_LINK_COMPONENTS support)
add_clang_library(clangTidyCppCoreGuidelinesModule
CppCoreGuidelinesTidyModule.cpp
InterfacesGlobalInitCheck.cpp
NoMallocCheck.cpp
OwningMemoryCheck.cpp
ProBoundsArrayToPointerDecayCheck.cpp
ProBoundsConstantArrayIndexCheck.cpp
ProBoundsPointerArithmeticCheck.cpp
ProTypeConstCastCheck.cpp
ProTypeCstyleCastCheck.cpp
ProTypeMemberInitCheck.cpp
ProTypeReinterpretCastCheck.cpp
ProTypeStaticCastDowncastCheck.cpp
ProTypeUnionAccessCheck.cpp
ProTypeVarargCheck.cpp
SpecialMemberFunctionsCheck.cpp
SlicingCheck.cpp
LINK_LIBS
clangAST
clangASTMatchers
clangBasic
clangLex
clangTidy
clangTidyMiscModule
clangTidyUtils
clangTooling
)

View File

@ -0,0 +1,82 @@
//===--- CppCoreGuidelinesModule.cpp - clang-tidy -------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "../ClangTidy.h"
#include "../ClangTidyModule.h"
#include "../ClangTidyModuleRegistry.h"
#include "../misc/UnconventionalAssignOperatorCheck.h"
#include "InterfacesGlobalInitCheck.h"
#include "NoMallocCheck.h"
#include "OwningMemoryCheck.h"
#include "ProBoundsArrayToPointerDecayCheck.h"
#include "ProBoundsConstantArrayIndexCheck.h"
#include "ProBoundsPointerArithmeticCheck.h"
#include "ProTypeConstCastCheck.h"
#include "ProTypeCstyleCastCheck.h"
#include "ProTypeMemberInitCheck.h"
#include "ProTypeReinterpretCastCheck.h"
#include "ProTypeStaticCastDowncastCheck.h"
#include "ProTypeUnionAccessCheck.h"
#include "ProTypeVarargCheck.h"
#include "SlicingCheck.h"
#include "SpecialMemberFunctionsCheck.h"
namespace clang {
namespace tidy {
namespace cppcoreguidelines {
/// A module containing checks of the C++ Core Guidelines
class CppCoreGuidelinesModule : public ClangTidyModule {
public:
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
CheckFactories.registerCheck<InterfacesGlobalInitCheck>(
"cppcoreguidelines-interfaces-global-init");
CheckFactories.registerCheck<NoMallocCheck>("cppcoreguidelines-no-malloc");
CheckFactories.registerCheck<OwningMemoryCheck>(
"cppcoreguidelines-owning-memory");
CheckFactories.registerCheck<ProBoundsArrayToPointerDecayCheck>(
"cppcoreguidelines-pro-bounds-array-to-pointer-decay");
CheckFactories.registerCheck<ProBoundsConstantArrayIndexCheck>(
"cppcoreguidelines-pro-bounds-constant-array-index");
CheckFactories.registerCheck<ProBoundsPointerArithmeticCheck>(
"cppcoreguidelines-pro-bounds-pointer-arithmetic");
CheckFactories.registerCheck<ProTypeConstCastCheck>(
"cppcoreguidelines-pro-type-const-cast");
CheckFactories.registerCheck<ProTypeCstyleCastCheck>(
"cppcoreguidelines-pro-type-cstyle-cast");
CheckFactories.registerCheck<ProTypeMemberInitCheck>(
"cppcoreguidelines-pro-type-member-init");
CheckFactories.registerCheck<ProTypeReinterpretCastCheck>(
"cppcoreguidelines-pro-type-reinterpret-cast");
CheckFactories.registerCheck<ProTypeStaticCastDowncastCheck>(
"cppcoreguidelines-pro-type-static-cast-downcast");
CheckFactories.registerCheck<ProTypeUnionAccessCheck>(
"cppcoreguidelines-pro-type-union-access");
CheckFactories.registerCheck<ProTypeVarargCheck>(
"cppcoreguidelines-pro-type-vararg");
CheckFactories.registerCheck<SpecialMemberFunctionsCheck>(
"cppcoreguidelines-special-member-functions");
CheckFactories.registerCheck<SlicingCheck>("cppcoreguidelines-slicing");
CheckFactories.registerCheck<misc::UnconventionalAssignOperatorCheck>(
"cppcoreguidelines-c-copy-assignment-signature");
}
};
// Register the LLVMTidyModule using this statically initialized variable.
static ClangTidyModuleRegistry::Add<CppCoreGuidelinesModule>
X("cppcoreguidelines-module", "Adds checks for the C++ Core Guidelines.");
} // namespace cppcoreguidelines
// This anchor is used to force the linker to link in the generated object file
// and thus register the CppCoreGuidelinesModule.
volatile int CppCoreGuidelinesModuleAnchorSource = 0;
} // namespace tidy
} // namespace clang

View File

@ -0,0 +1,59 @@
//===--- InterfacesGlobalInitCheck.cpp - clang-tidy------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "InterfacesGlobalInitCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace cppcoreguidelines {
void InterfacesGlobalInitCheck::registerMatchers(MatchFinder *Finder) {
const auto IsGlobal =
allOf(hasGlobalStorage(),
hasDeclContext(anyOf(translationUnitDecl(), // Global scope.
namespaceDecl(), // Namespace scope.
recordDecl())), // Class scope.
unless(isConstexpr()));
const auto ReferencesUndefinedGlobalVar = declRefExpr(hasDeclaration(
varDecl(IsGlobal, unless(isDefinition())).bind("referencee")));
Finder->addMatcher(
varDecl(IsGlobal, isDefinition(),
hasInitializer(expr(hasDescendant(ReferencesUndefinedGlobalVar))))
.bind("var"),
this);
}
void InterfacesGlobalInitCheck::check(const MatchFinder::MatchResult &Result) {
const auto *const Var = Result.Nodes.getNodeAs<VarDecl>("var");
// For now assume that people who write macros know what they're doing.
if (Var->getLocation().isMacroID())
return;
const auto *const Referencee = Result.Nodes.getNodeAs<VarDecl>("referencee");
// If the variable has been defined, we're good.
const auto *const ReferenceeDef = Referencee->getDefinition();
if (ReferenceeDef != nullptr &&
Result.SourceManager->isBeforeInTranslationUnit(
ReferenceeDef->getLocation(), Var->getLocation())) {
return;
}
diag(Var->getLocation(),
"initializing non-local variable with non-const expression depending on "
"uninitialized non-local variable %0")
<< Referencee;
}
} // namespace cppcoreguidelines
} // namespace tidy
} // namespace clang

View File

@ -0,0 +1,35 @@
//===--- InterfacesGlobalInitCheck.h - clang-tidy----------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_INTERFACES_GLOBAL_INIT_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_INTERFACES_GLOBAL_INIT_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace cppcoreguidelines {
/// Flags possible initialization order issues of static variables.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-interfaces-global-init.html
class InterfacesGlobalInitCheck : public ClangTidyCheck {
public:
InterfacesGlobalInitCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
} // namespace cppcoreguidelines
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_INTERFACES_GLOBAL_INIT_H

View File

@ -0,0 +1,82 @@
//===--- NoMallocCheck.cpp - clang-tidy------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "NoMallocCheck.h"
#include "../utils/Matchers.h"
#include "../utils/OptionsUtils.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include <algorithm>
#include <string>
#include <vector>
using namespace clang::ast_matchers;
using namespace clang::ast_matchers::internal;
namespace clang {
namespace tidy {
namespace cppcoreguidelines {
namespace {
Matcher<FunctionDecl> hasAnyListedName(const std::string &FunctionNames) {
const std::vector<std::string> NameList =
utils::options::parseStringList(FunctionNames);
return hasAnyName(std::vector<StringRef>(NameList.begin(), NameList.end()));
}
} // namespace
void NoMallocCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "Allocations", AllocList);
Options.store(Opts, "Reallocations", ReallocList);
Options.store(Opts, "Deallocations", DeallocList);
}
void NoMallocCheck::registerMatchers(MatchFinder *Finder) {
// C-style memory management is only problematic in C++.
if (!getLangOpts().CPlusPlus)
return;
// Registering malloc, will suggest RAII.
Finder->addMatcher(callExpr(callee(functionDecl(hasAnyListedName(AllocList))))
.bind("allocation"),
this);
// Registering realloc calls, suggest std::vector or std::string.
Finder->addMatcher(
callExpr(callee(functionDecl(hasAnyListedName(ReallocList))))
.bind("realloc"),
this);
// Registering free calls, will suggest RAII instead.
Finder->addMatcher(
callExpr(callee(functionDecl(hasAnyListedName(DeallocList))))
.bind("free"),
this);
}
void NoMallocCheck::check(const MatchFinder::MatchResult &Result) {
const CallExpr *Call = nullptr;
StringRef Recommendation;
if ((Call = Result.Nodes.getNodeAs<CallExpr>("allocation")))
Recommendation = "consider a container or a smart pointer";
else if ((Call = Result.Nodes.getNodeAs<CallExpr>("realloc")))
Recommendation = "consider std::vector or std::string";
else if ((Call = Result.Nodes.getNodeAs<CallExpr>("free")))
Recommendation = "use RAII";
assert(Call && "Unhandled binding in the Matcher");
diag(Call->getLocStart(), "do not manage memory manually; %0")
<< Recommendation << SourceRange(Call->getLocStart(), Call->getLocEnd());
}
} // namespace cppcoreguidelines
} // namespace tidy
} // namespace clang

View File

@ -0,0 +1,62 @@
//===--- NoMallocCheck.h - clang-tidy----------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_NO_MALLOC_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_NO_MALLOC_H
#include "../ClangTidy.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
namespace clang {
namespace tidy {
namespace cppcoreguidelines {
/// This checker is concerned with C-style memory management and suggest modern
/// alternatives to it.
/// The check is only enabled in C++. For analyzing malloc calls see Clang
/// Static Analyzer - unix.Malloc.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-no-malloc.html
class NoMallocCheck : public ClangTidyCheck {
public:
/// Construct Checker and read in configuration for function names.
NoMallocCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
AllocList(Options.get("Allocations", "::malloc;::calloc")),
ReallocList(Options.get("Reallocations", "::realloc")),
DeallocList(Options.get("Deallocations", "::free")) {}
/// Make configuration of checker discoverable.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
/// Registering for malloc, calloc, realloc and free calls.
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
/// Checks matched function calls and gives suggestion to modernize the code.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
private:
/// Semicolon-separated list of fully qualified names of memory allocation
/// functions the check warns about. Defaults to `::malloc;::calloc`.
const std::string AllocList;
/// Semicolon-separated list of fully qualified names of memory reallocation
/// functions the check warns about. Defaults to `::realloc`.
const std::string ReallocList;
/// Semicolon-separated list of fully qualified names of memory deallocation
/// functions the check warns about. Defaults to `::free`.
const std::string DeallocList;
};
} // namespace cppcoreguidelines
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_NO_MALLOC_H

View File

@ -0,0 +1,394 @@
//===--- OwningMemoryCheck.cpp - clang-tidy--------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "OwningMemoryCheck.h"
#include "../utils/Matchers.h"
#include "../utils/OptionsUtils.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include <string>
#include <vector>
using namespace clang::ast_matchers;
using namespace clang::ast_matchers::internal;
namespace clang {
namespace tidy {
namespace cppcoreguidelines {
// FIXME: Copied from 'NoMallocCheck.cpp'. Has to be refactored into 'util' or
// something like that.
namespace {
Matcher<FunctionDecl> hasAnyListedName(const std::string &FunctionNames) {
const std::vector<std::string> NameList =
utils::options::parseStringList(FunctionNames);
return hasAnyName(std::vector<StringRef>(NameList.begin(), NameList.end()));
}
} // namespace
void OwningMemoryCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "LegacyResourceProducers", LegacyResourceProducers);
Options.store(Opts, "LegacyResourceConsumers", LegacyResourceConsumers);
}
/// Match common cases, where the owner semantic is relevant, like function
/// calls, delete expressions and others.
void OwningMemoryCheck::registerMatchers(MatchFinder *Finder) {
if (!getLangOpts().CPlusPlus11)
return;
const auto OwnerDecl = typeAliasTemplateDecl(hasName("::gsl::owner"));
const auto IsOwnerType = hasType(OwnerDecl);
const auto LegacyCreatorFunctions = hasAnyListedName(LegacyResourceProducers);
const auto LegacyConsumerFunctions =
hasAnyListedName(LegacyResourceConsumers);
// Legacy functions that are use for resource management but cannot be
// updated to use `gsl::owner<>`, like standard C memory management.
const auto CreatesLegacyOwner =
callExpr(callee(functionDecl(LegacyCreatorFunctions)));
// C-style functions like `::malloc()` sometimes create owners as void*
// which is expected to be cast to the correct type in C++. This case
// must be catched explicitly.
const auto LegacyOwnerCast =
castExpr(hasSourceExpression(CreatesLegacyOwner));
// Functions that do manual resource management but cannot be updated to use
// owner. Best example is `::free()`.
const auto LegacyOwnerConsumers = functionDecl(LegacyConsumerFunctions);
const auto CreatesOwner =
anyOf(cxxNewExpr(),
callExpr(callee(
functionDecl(returns(qualType(hasDeclaration(OwnerDecl)))))),
CreatesLegacyOwner, LegacyOwnerCast);
const auto ConsideredOwner = eachOf(IsOwnerType, CreatesOwner);
// Find delete expressions that delete non-owners.
Finder->addMatcher(
cxxDeleteExpr(
hasDescendant(
declRefExpr(unless(ConsideredOwner)).bind("deleted_variable")))
.bind("delete_expr"),
this);
// Ignoring the implicit casts is vital because the legacy owners do not work
// with the 'owner<>' annotation and therefore always implicitly cast to the
// legacy type (even 'void *').
//
// Furthermore, legacy owner functions are assumed to use raw pointers for
// resources. This check assumes that all pointer arguments of a legacy
// functions shall be 'gsl::owner<>'.
Finder->addMatcher(
callExpr(
allOf(callee(LegacyOwnerConsumers),
hasAnyArgument(allOf(unless(ignoringImpCasts(ConsideredOwner)),
hasType(pointerType())))))
.bind("legacy_consumer"),
this);
// Matching assignment to owners, with the rhs not being an owner nor creating
// one.
Finder->addMatcher(binaryOperator(allOf(matchers::isAssignmentOperator(),
hasLHS(IsOwnerType),
hasRHS(unless(ConsideredOwner))))
.bind("owner_assignment"),
this);
// Matching initialization of owners with non-owners, nor creating owners.
Finder->addMatcher(
namedDecl(
varDecl(allOf(hasInitializer(unless(ConsideredOwner)), IsOwnerType))
.bind("owner_initialization")),
this);
const auto HasConstructorInitializerForOwner =
has(cxxConstructorDecl(forEachConstructorInitializer(
cxxCtorInitializer(allOf(isMemberInitializer(), forField(IsOwnerType),
withInitializer(
// Avoid templatesdeclaration with
// excluding parenListExpr.
allOf(unless(ConsideredOwner),
unless(parenListExpr())))))
.bind("owner_member_initializer"))));
// Match class member initialization that expects owners, but does not get
// them.
Finder->addMatcher(cxxRecordDecl(HasConstructorInitializerForOwner), this);
// Matching on assignment operations where the RHS is a newly created owner,
// but the LHS is not an owner.
Finder->addMatcher(
binaryOperator(allOf(matchers::isAssignmentOperator(),
hasLHS(unless(IsOwnerType)), hasRHS(CreatesOwner)))
.bind("bad_owner_creation_assignment"),
this);
// Matching on initialization operations where the initial value is a newly
// created owner, but the LHS is not an owner.
Finder->addMatcher(
namedDecl(varDecl(eachOf(allOf(hasInitializer(CreatesOwner),
unless(IsOwnerType)),
allOf(hasInitializer(ConsideredOwner),
hasType(autoType().bind("deduced_type")))))
.bind("bad_owner_creation_variable")),
this);
// Match on all function calls that expect owners as arguments, but didn't
// get them.
Finder->addMatcher(
callExpr(forEachArgumentWithParam(
expr(unless(ConsideredOwner)).bind("expected_owner_argument"),
parmVarDecl(IsOwnerType))),
this);
// Matching for function calls where one argument is a created owner, but the
// parameter type is not an owner.
Finder->addMatcher(callExpr(forEachArgumentWithParam(
expr(CreatesOwner).bind("bad_owner_creation_argument"),
parmVarDecl(unless(IsOwnerType))
.bind("bad_owner_creation_parameter"))),
this);
// Matching on functions, that return an owner/resource, but don't declare
// their return type as owner.
Finder->addMatcher(
functionDecl(
allOf(hasDescendant(returnStmt(hasReturnValue(ConsideredOwner))
.bind("bad_owner_return")),
unless(returns(qualType(hasDeclaration(OwnerDecl))))))
.bind("function_decl"),
this);
// Match on classes that have an owner as member, but don't declare a
// destructor to properly release the owner.
Finder->addMatcher(
cxxRecordDecl(
allOf(
has(fieldDecl(IsOwnerType).bind("undestructed_owner_member")),
anyOf(unless(has(cxxDestructorDecl())),
has(cxxDestructorDecl(anyOf(isDefaulted(), isDeleted()))))))
.bind("non_destructor_class"),
this);
}
void OwningMemoryCheck::check(const MatchFinder::MatchResult &Result) {
const auto &Nodes = Result.Nodes;
bool CheckExecuted = false;
CheckExecuted |= handleDeletion(Nodes);
CheckExecuted |= handleLegacyConsumers(Nodes);
CheckExecuted |= handleExpectedOwner(Nodes);
CheckExecuted |= handleAssignmentAndInit(Nodes);
CheckExecuted |= handleAssignmentFromNewOwner(Nodes);
CheckExecuted |= handleReturnValues(Nodes);
CheckExecuted |= handleOwnerMembers(Nodes);
assert(CheckExecuted &&
"None of the subroutines executed, logic error in matcher!");
}
bool OwningMemoryCheck::handleDeletion(const BoundNodes &Nodes) {
// Result of delete matchers.
const auto *DeleteStmt = Nodes.getNodeAs<CXXDeleteExpr>("delete_expr");
const auto *DeletedVariable =
Nodes.getNodeAs<DeclRefExpr>("deleted_variable");
// Deletion of non-owners, with `delete variable;`
if (DeleteStmt) {
diag(DeleteStmt->getLocStart(),
"deleting a pointer through a type that is "
"not marked 'gsl::owner<>'; consider using a "
"smart pointer instead")
<< DeletedVariable->getSourceRange();
// FIXME: The declaration of the variable that was deleted can be
// rewritten.
const ValueDecl *Decl = DeletedVariable->getDecl();
diag(Decl->getLocStart(), "variable declared here", DiagnosticIDs::Note)
<< Decl->getSourceRange();
return true;
}
return false;
}
bool OwningMemoryCheck::handleLegacyConsumers(const BoundNodes &Nodes) {
// Result of matching for legacy consumer-functions like `::free()`.
const auto *LegacyConsumer = Nodes.getNodeAs<CallExpr>("legacy_consumer");
// FIXME: `freopen` should be handled seperately because it takes the filename
// as a pointer, which should not be an owner. The argument that is an owner
// is known and the false positive coming from the filename can be avoided.
if (LegacyConsumer) {
diag(LegacyConsumer->getLocStart(),
"calling legacy resource function without passing a 'gsl::owner<>'")
<< LegacyConsumer->getSourceRange();
return true;
}
return false;
}
bool OwningMemoryCheck::handleExpectedOwner(const BoundNodes &Nodes) {
// Result of function call matchers.
const auto *ExpectedOwner = Nodes.getNodeAs<Expr>("expected_owner_argument");
// Expected function argument to be owner.
if (ExpectedOwner) {
diag(ExpectedOwner->getLocStart(),
"expected argument of type 'gsl::owner<>'; got %0")
<< ExpectedOwner->getType() << ExpectedOwner->getSourceRange();
return true;
}
return false;
}
/// Assignment and initialization of owner variables.
bool OwningMemoryCheck::handleAssignmentAndInit(const BoundNodes &Nodes) {
const auto *OwnerAssignment =
Nodes.getNodeAs<BinaryOperator>("owner_assignment");
const auto *OwnerInitialization =
Nodes.getNodeAs<VarDecl>("owner_initialization");
const auto *OwnerInitializer =
Nodes.getNodeAs<CXXCtorInitializer>("owner_member_initializer");
// Assignments to owners.
if (OwnerAssignment) {
diag(OwnerAssignment->getLocStart(),
"expected assignment source to be of type 'gsl::owner<>'; got %0")
<< OwnerAssignment->getRHS()->getType()
<< OwnerAssignment->getSourceRange();
return true;
}
// Initialization of owners.
if (OwnerInitialization) {
diag(OwnerInitialization->getLocStart(),
"expected initialization with value of type 'gsl::owner<>'; got %0")
<< OwnerInitialization->getAnyInitializer()->getType()
<< OwnerInitialization->getSourceRange();
return true;
}
// Initializer of class constructors that initialize owners.
if (OwnerInitializer) {
diag(OwnerInitializer->getSourceLocation(),
"expected initialization of owner member variable with value of type "
"'gsl::owner<>'; got %0")
// FIXME: the expression from getInit has type 'void', but the type
// of the supplied argument would be of interest.
<< OwnerInitializer->getInit()->getType()
<< OwnerInitializer->getSourceRange();
return true;
}
return false;
}
/// Problematic assignment and initializations, since the assigned value is a
/// newly created owner.
bool OwningMemoryCheck::handleAssignmentFromNewOwner(const BoundNodes &Nodes) {
const auto *BadOwnerAssignment =
Nodes.getNodeAs<BinaryOperator>("bad_owner_creation_assignment");
const auto *BadOwnerInitialization =
Nodes.getNodeAs<VarDecl>("bad_owner_creation_variable");
const auto *BadOwnerArgument =
Nodes.getNodeAs<Expr>("bad_owner_creation_argument");
const auto *BadOwnerParameter =
Nodes.getNodeAs<ParmVarDecl>("bad_owner_creation_parameter");
// Bad assignments to non-owners, where the RHS is a newly created owner.
if (BadOwnerAssignment) {
diag(BadOwnerAssignment->getLocStart(),
"assigning newly created 'gsl::owner<>' to non-owner %0")
<< BadOwnerAssignment->getLHS()->getType()
<< BadOwnerAssignment->getSourceRange();
return true;
}
// Bad initialization of non-owners, where the RHS is a newly created owner.
if (BadOwnerInitialization) {
diag(BadOwnerInitialization->getLocStart(),
"initializing non-owner %0 with a newly created 'gsl::owner<>'")
<< BadOwnerInitialization->getType()
<< BadOwnerInitialization->getSourceRange();
// FIXME: FixitHint to rewrite the type of the initialized variable
// as 'gsl::owner<OriginalType>'
// If the type of the variable was deduced, the wrapping owner typedef is
// eliminated, therefore the check emits a special note for that case.
if (Nodes.getNodeAs<AutoType>("deduced_type")) {
diag(BadOwnerInitialization->getLocStart(),
"type deduction did not result in an owner", DiagnosticIDs::Note);
}
return true;
}
// Function call, where one arguments is a newly created owner, but the
// parameter type is not.
if (BadOwnerArgument) {
assert(BadOwnerParameter &&
"parameter for the problematic argument not found");
diag(BadOwnerArgument->getLocStart(), "initializing non-owner argument of "
"type %0 with a newly created "
"'gsl::owner<>'")
<< BadOwnerParameter->getType() << BadOwnerArgument->getSourceRange();
return true;
}
return false;
}
bool OwningMemoryCheck::handleReturnValues(const BoundNodes &Nodes) {
// Function return statements, that are owners/resources, but the function
// declaration does not declare its return value as owner.
const auto *BadReturnType = Nodes.getNodeAs<ReturnStmt>("bad_owner_return");
const auto *Function = Nodes.getNodeAs<FunctionDecl>("function_decl");
// Function return values, that should be owners but aren't.
if (BadReturnType) {
// The returned value is a resource or variable that was not annotated with
// owner<> and the function return type is not owner<>.
diag(BadReturnType->getLocStart(),
"returning a newly created resource of "
"type %0 or 'gsl::owner<>' from a "
"function whose return type is not 'gsl::owner<>'")
<< Function->getReturnType() << BadReturnType->getSourceRange();
// FIXME: Rewrite the return type as 'gsl::owner<OriginalType>'
return true;
}
return false;
}
bool OwningMemoryCheck::handleOwnerMembers(const BoundNodes &Nodes) {
// Classes, that have owners as member, but do not declare destructors
// accordingly.
const auto *BadClass = Nodes.getNodeAs<CXXRecordDecl>("non_destructor_class");
// Classes, that contains owners, but do not declare destructors.
if (BadClass) {
const auto *DeclaredOwnerMember =
Nodes.getNodeAs<FieldDecl>("undestructed_owner_member");
assert(DeclaredOwnerMember &&
"match on class with bad destructor but without a declared owner");
diag(DeclaredOwnerMember->getLocStart(),
"member variable of type 'gsl::owner<>' requires the class %0 to "
"implement a destructor to release the owned resource")
<< BadClass;
return true;
}
return false;
}
} // namespace cppcoreguidelines
} // namespace tidy
} // namespace clang

View File

@ -0,0 +1,63 @@
//===--- OwningMemoryCheck.h - clang-tidy------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_OWNING_MEMORY_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_OWNING_MEMORY_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace cppcoreguidelines {
/// Checks for common use cases for gsl::owner and enforces the unique owner
/// nature of it whenever possible.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-owning-memory.html
class OwningMemoryCheck : public ClangTidyCheck {
public:
OwningMemoryCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
LegacyResourceProducers(Options.get(
"LegacyResourceProducers", "::malloc;::aligned_alloc;::realloc;"
"::calloc;::fopen;::freopen;::tmpfile")),
LegacyResourceConsumers(Options.get(
"LegacyResourceConsumers", "::free;::realloc;::freopen;::fclose")) {
}
/// Make configuration of checker discoverable.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
private:
bool handleDeletion(const ast_matchers::BoundNodes &Nodes);
bool handleLegacyConsumers(const ast_matchers::BoundNodes &Nodes);
bool handleExpectedOwner(const ast_matchers::BoundNodes &Nodes);
bool handleAssignmentAndInit(const ast_matchers::BoundNodes &Nodes);
bool handleAssignmentFromNewOwner(const ast_matchers::BoundNodes &Nodes);
bool handleReturnValues(const ast_matchers::BoundNodes &Nodes);
bool handleOwnerMembers(const ast_matchers::BoundNodes &Nodes);
/// List of old C-style functions that create resources.
/// Defaults to
/// `::malloc;::aligned_alloc;::realloc;::calloc;::fopen;::freopen;::tmpfile`.
const std::string LegacyResourceProducers;
/// List of old C-style functions that consume or release resources.
/// Defaults to `::free;::realloc;::freopen;::fclose`.
const std::string LegacyResourceConsumers;
};
} // namespace cppcoreguidelines
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_OWNING_MEMORY_H

View File

@ -0,0 +1,80 @@
//===--- ProBoundsArrayToPointerDecayCheck.cpp - clang-tidy----------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "ProBoundsArrayToPointerDecayCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace cppcoreguidelines {
AST_MATCHER_P(CXXForRangeStmt, hasRangeBeginEndStmt,
ast_matchers::internal::Matcher<DeclStmt>, InnerMatcher) {
for (const DeclStmt *Stmt : {Node.getBeginStmt(), Node.getEndStmt()})
if (Stmt != nullptr && InnerMatcher.matches(*Stmt, Finder, Builder))
return true;
return false;
}
AST_MATCHER(Stmt, isInsideOfRangeBeginEndStmt) {
return stmt(hasAncestor(cxxForRangeStmt(
hasRangeBeginEndStmt(hasDescendant(equalsNode(&Node))))))
.matches(Node, Finder, Builder);
}
AST_MATCHER_P(Expr, hasParentIgnoringImpCasts,
ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
const Expr *E = &Node;
do {
ASTContext::DynTypedNodeList Parents =
Finder->getASTContext().getParents(*E);
if (Parents.size() != 1)
return false;
E = Parents[0].get<Expr>();
if (!E)
return false;
} while (isa<ImplicitCastExpr>(E));
return InnerMatcher.matches(*E, Finder, Builder);
}
void ProBoundsArrayToPointerDecayCheck::registerMatchers(MatchFinder *Finder) {
if (!getLangOpts().CPlusPlus)
return;
// The only allowed array to pointer decay
// 1) just before array subscription
// 2) inside a range-for over an array
// 3) if it converts a string literal to a pointer
Finder->addMatcher(
implicitCastExpr(unless(hasParent(arraySubscriptExpr())),
unless(hasParentIgnoringImpCasts(explicitCastExpr())),
unless(isInsideOfRangeBeginEndStmt()),
unless(hasSourceExpression(stringLiteral())))
.bind("cast"),
this);
}
void ProBoundsArrayToPointerDecayCheck::check(
const MatchFinder::MatchResult &Result) {
const auto *MatchedCast = Result.Nodes.getNodeAs<ImplicitCastExpr>("cast");
if (MatchedCast->getCastKind() != CK_ArrayToPointerDecay)
return;
diag(MatchedCast->getExprLoc(), "do not implicitly decay an array into a "
"pointer; consider using gsl::array_view or "
"an explicit cast instead");
}
} // namespace cppcoreguidelines
} // namespace tidy
} // namespace clang

View File

@ -0,0 +1,35 @@
//===--- ProBoundsArrayToPointerDecayCheck.h - clang-tidy--------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_BOUNDS_ARRAY_TO_POINTER_DECAY_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_BOUNDS_ARRAY_TO_POINTER_DECAY_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace cppcoreguidelines {
/// This check flags all array to pointer decays
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-pro-bounds-array-to-pointer-decay.html
class ProBoundsArrayToPointerDecayCheck : public ClangTidyCheck {
public:
ProBoundsArrayToPointerDecayCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
} // namespace cppcoreguidelines
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_BOUNDS_ARRAY_TO_POINTER_DECAY_H

View File

@ -0,0 +1,141 @@
//===--- ProBoundsConstantArrayIndexCheck.cpp - clang-tidy-----------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "ProBoundsConstantArrayIndexCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Lex/Preprocessor.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace cppcoreguidelines {
ProBoundsConstantArrayIndexCheck::ProBoundsConstantArrayIndexCheck(
StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context), GslHeader(Options.get("GslHeader", "")),
IncludeStyle(utils::IncludeSorter::parseIncludeStyle(
Options.getLocalOrGlobal("IncludeStyle", "llvm"))) {}
void ProBoundsConstantArrayIndexCheck::storeOptions(
ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "GslHeader", GslHeader);
Options.store(Opts, "IncludeStyle", IncludeStyle);
}
void ProBoundsConstantArrayIndexCheck::registerPPCallbacks(
CompilerInstance &Compiler) {
if (!getLangOpts().CPlusPlus)
return;
Inserter.reset(new utils::IncludeInserter(
Compiler.getSourceManager(), Compiler.getLangOpts(), IncludeStyle));
Compiler.getPreprocessor().addPPCallbacks(Inserter->CreatePPCallbacks());
}
void ProBoundsConstantArrayIndexCheck::registerMatchers(MatchFinder *Finder) {
if (!getLangOpts().CPlusPlus)
return;
// Note: if a struct contains an array member, the compiler-generated
// constructor has an arraySubscriptExpr.
Finder->addMatcher(
arraySubscriptExpr(
hasBase(ignoringImpCasts(hasType(constantArrayType().bind("type")))),
hasIndex(expr().bind("index")), unless(hasAncestor(isImplicit())))
.bind("expr"),
this);
Finder->addMatcher(
cxxOperatorCallExpr(
hasOverloadedOperatorName("[]"),
hasArgument(
0, hasType(cxxRecordDecl(hasName("::std::array")).bind("type"))),
hasArgument(1, expr().bind("index")))
.bind("expr"),
this);
}
void ProBoundsConstantArrayIndexCheck::check(
const MatchFinder::MatchResult &Result) {
const auto *Matched = Result.Nodes.getNodeAs<Expr>("expr");
const auto *IndexExpr = Result.Nodes.getNodeAs<Expr>("index");
if (IndexExpr->isValueDependent())
return; // We check in the specialization.
llvm::APSInt Index;
if (!IndexExpr->isIntegerConstantExpr(Index, *Result.Context, nullptr,
/*isEvaluated=*/true)) {
SourceRange BaseRange;
if (const auto *ArraySubscriptE = dyn_cast<ArraySubscriptExpr>(Matched))
BaseRange = ArraySubscriptE->getBase()->getSourceRange();
else
BaseRange =
dyn_cast<CXXOperatorCallExpr>(Matched)->getArg(0)->getSourceRange();
SourceRange IndexRange = IndexExpr->getSourceRange();
auto Diag = diag(Matched->getExprLoc(),
"do not use array subscript when the index is "
"not an integer constant expression; use gsl::at() "
"instead");
if (!GslHeader.empty()) {
Diag << FixItHint::CreateInsertion(BaseRange.getBegin(), "gsl::at(")
<< FixItHint::CreateReplacement(
SourceRange(BaseRange.getEnd().getLocWithOffset(1),
IndexRange.getBegin().getLocWithOffset(-1)),
", ")
<< FixItHint::CreateReplacement(Matched->getLocEnd(), ")");
Optional<FixItHint> Insertion = Inserter->CreateIncludeInsertion(
Result.SourceManager->getMainFileID(), GslHeader,
/*IsAngled=*/false);
if (Insertion)
Diag << Insertion.getValue();
}
return;
}
const auto *StdArrayDecl =
Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("type");
// For static arrays, this is handled in clang-diagnostic-array-bounds.
if (!StdArrayDecl)
return;
if (Index.isSigned() && Index.isNegative()) {
diag(Matched->getExprLoc(), "std::array<> index %0 is negative")
<< Index.toString(10);
return;
}
const TemplateArgumentList &TemplateArgs = StdArrayDecl->getTemplateArgs();
if (TemplateArgs.size() < 2)
return;
// First template arg of std::array is the type, second arg is the size.
const auto &SizeArg = TemplateArgs[1];
if (SizeArg.getKind() != TemplateArgument::Integral)
return;
llvm::APInt ArraySize = SizeArg.getAsIntegral();
// Get uint64_t values, because different bitwidths would lead to an assertion
// in APInt::uge.
if (Index.getZExtValue() >= ArraySize.getZExtValue()) {
diag(Matched->getExprLoc(),
"std::array<> index %0 is past the end of the array "
"(which contains %1 elements)")
<< Index.toString(10) << ArraySize.toString(10, false);
}
}
} // namespace cppcoreguidelines
} // namespace tidy
} // namespace clang

View File

@ -0,0 +1,42 @@
//===--- ProBoundsConstantArrayIndexCheck.h - clang-tidy---------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_BOUNDS_CONSTANT_ARRAY_INDEX_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_BOUNDS_CONSTANT_ARRAY_INDEX_H
#include "../ClangTidy.h"
#include "../utils/IncludeInserter.h"
namespace clang {
namespace tidy {
namespace cppcoreguidelines {
/// This checks that all array subscriptions on static arrays and std::arrays
/// have a constant index and are within bounds
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-pro-bounds-constant-array-index.html
class ProBoundsConstantArrayIndexCheck : public ClangTidyCheck {
const std::string GslHeader;
const utils::IncludeSorter::IncludeStyle IncludeStyle;
std::unique_ptr<utils::IncludeInserter> Inserter;
public:
ProBoundsConstantArrayIndexCheck(StringRef Name, ClangTidyContext *Context);
void registerPPCallbacks(CompilerInstance &Compiler) override;
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
} // namespace cppcoreguidelines
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_BOUNDS_CONSTANT_ARRAY_INDEX_H

View File

@ -0,0 +1,59 @@
//===--- ProBoundsPointerArithmeticCheck.cpp - clang-tidy------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "ProBoundsPointerArithmeticCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace cppcoreguidelines {
void ProBoundsPointerArithmeticCheck::registerMatchers(MatchFinder *Finder) {
if (!getLangOpts().CPlusPlus)
return;
// Flag all operators +, -, +=, -=, ++, -- that result in a pointer
Finder->addMatcher(
binaryOperator(
anyOf(hasOperatorName("+"), hasOperatorName("-"),
hasOperatorName("+="), hasOperatorName("-=")),
hasType(pointerType()),
unless(hasLHS(ignoringImpCasts(declRefExpr(to(isImplicit()))))))
.bind("expr"),
this);
Finder->addMatcher(
unaryOperator(anyOf(hasOperatorName("++"), hasOperatorName("--")),
hasType(pointerType()))
.bind("expr"),
this);
// Array subscript on a pointer (not an array) is also pointer arithmetic
Finder->addMatcher(
arraySubscriptExpr(
hasBase(ignoringImpCasts(
anyOf(hasType(pointerType()),
hasType(decayedType(hasDecayedType(pointerType())))))))
.bind("expr"),
this);
}
void ProBoundsPointerArithmeticCheck::check(
const MatchFinder::MatchResult &Result) {
const auto *MatchedExpr = Result.Nodes.getNodeAs<Expr>("expr");
diag(MatchedExpr->getExprLoc(), "do not use pointer arithmetic");
}
} // namespace cppcoreguidelines
} // namespace tidy
} // namespace clang

View File

@ -0,0 +1,37 @@
//===--- ProBoundsPointerArithmeticCheck.h - clang-tidy----------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_BOUNDS_POINTER_ARITHMETIC_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_BOUNDS_POINTER_ARITHMETIC_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace cppcoreguidelines {
/// Flags all kinds of pointer arithmetic that have result of pointer type, i.e.
/// +, -, +=, -=, ++, --. In addition, the [] operator on pointers (not on
/// arrays) is flagged.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-pro-bounds-pointer-arithmetic.html
class ProBoundsPointerArithmeticCheck : public ClangTidyCheck {
public:
ProBoundsPointerArithmeticCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
} // namespace cppcoreguidelines
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_BOUNDS_POINTER_ARITHMETIC_H

View File

@ -0,0 +1,34 @@
//===--- ProTypeConstCastCheck.cpp - clang-tidy----------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "ProTypeConstCastCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace cppcoreguidelines {
void ProTypeConstCastCheck::registerMatchers(MatchFinder *Finder) {
if (!getLangOpts().CPlusPlus)
return;
Finder->addMatcher(cxxConstCastExpr().bind("cast"), this);
}
void ProTypeConstCastCheck::check(const MatchFinder::MatchResult &Result) {
const auto *MatchedCast = Result.Nodes.getNodeAs<CXXConstCastExpr>("cast");
diag(MatchedCast->getOperatorLoc(), "do not use const_cast");
}
} // namespace cppcoreguidelines
} // namespace tidy
} // namespace clang

View File

@ -0,0 +1,35 @@
//===--- ProTypeConstCastCheck.h - clang-tidy--------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_TYPE_CONST_CAST_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_TYPE_CONST_CAST_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace cppcoreguidelines {
/// This check flags all instances of const_cast
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-pro-type-const-cast.html
class ProTypeConstCastCheck : public ClangTidyCheck {
public:
ProTypeConstCastCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
} // namespace cppcoreguidelines
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_TYPE_CONST_CAST_H

View File

@ -0,0 +1,108 @@
//===--- ProTypeCstyleCastCheck.cpp - clang-tidy---------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "ProTypeCstyleCastCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Lex/Lexer.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace cppcoreguidelines {
static bool needsConstCast(QualType SourceType, QualType DestType) {
SourceType = SourceType.getNonReferenceType();
DestType = DestType.getNonReferenceType();
while (SourceType->isPointerType() && DestType->isPointerType()) {
SourceType = SourceType->getPointeeType();
DestType = DestType->getPointeeType();
if (SourceType.isConstQualified() && !DestType.isConstQualified())
return true;
}
return false;
}
void ProTypeCstyleCastCheck::registerMatchers(MatchFinder *Finder) {
if (!getLangOpts().CPlusPlus)
return;
Finder->addMatcher(
cStyleCastExpr(unless(isInTemplateInstantiation())).bind("cast"), this);
}
void ProTypeCstyleCastCheck::check(const MatchFinder::MatchResult &Result) {
const auto *MatchedCast = Result.Nodes.getNodeAs<CStyleCastExpr>("cast");
if (MatchedCast->getCastKind() == CK_BitCast ||
MatchedCast->getCastKind() == CK_LValueBitCast ||
MatchedCast->getCastKind() == CK_IntegralToPointer ||
MatchedCast->getCastKind() == CK_PointerToIntegral ||
MatchedCast->getCastKind() == CK_ReinterpretMemberPointer) {
diag(MatchedCast->getLocStart(),
"do not use C-style cast to convert between unrelated types");
return;
}
QualType SourceType = MatchedCast->getSubExpr()->getType();
if (MatchedCast->getCastKind() == CK_BaseToDerived) {
const auto *SourceDecl = SourceType->getPointeeCXXRecordDecl();
if (!SourceDecl) // The cast is from object to reference.
SourceDecl = SourceType->getAsCXXRecordDecl();
if (!SourceDecl)
return;
if (SourceDecl->isPolymorphic()) {
// Leave type spelling exactly as it was (unlike
// getTypeAsWritten().getAsString() which would spell enum types 'enum
// X').
StringRef DestTypeString = Lexer::getSourceText(
CharSourceRange::getTokenRange(
MatchedCast->getLParenLoc().getLocWithOffset(1),
MatchedCast->getRParenLoc().getLocWithOffset(-1)),
*Result.SourceManager, getLangOpts());
auto diag_builder = diag(
MatchedCast->getLocStart(),
"do not use C-style cast to downcast from a base to a derived class; "
"use dynamic_cast instead");
const Expr *SubExpr =
MatchedCast->getSubExprAsWritten()->IgnoreImpCasts();
std::string CastText = ("dynamic_cast<" + DestTypeString + ">").str();
if (!isa<ParenExpr>(SubExpr)) {
CastText.push_back('(');
diag_builder << FixItHint::CreateInsertion(
Lexer::getLocForEndOfToken(SubExpr->getLocEnd(), 0,
*Result.SourceManager, getLangOpts()),
")");
}
auto ParenRange = CharSourceRange::getTokenRange(
MatchedCast->getLParenLoc(), MatchedCast->getRParenLoc());
diag_builder << FixItHint::CreateReplacement(ParenRange, CastText);
} else {
diag(
MatchedCast->getLocStart(),
"do not use C-style cast to downcast from a base to a derived class");
}
return;
}
if (MatchedCast->getCastKind() == CK_NoOp &&
needsConstCast(SourceType, MatchedCast->getType())) {
diag(MatchedCast->getLocStart(),
"do not use C-style cast to cast away constness");
}
}
} // namespace cppcoreguidelines
} // namespace tidy
} // namespace clang

View File

@ -0,0 +1,36 @@
//===--- ProTypeCstyleCastCheck.h - clang-tidy-------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_TYPE_CSTYLE_CAST_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_TYPE_CSTYLE_CAST_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace cppcoreguidelines {
/// This check flags all use of C-style casts that perform a static_cast
/// downcast, const_cast, or reinterpret_cast.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-pro-type-cstyle-cast.html
class ProTypeCstyleCastCheck : public ClangTidyCheck {
public:
ProTypeCstyleCastCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
} // namespace cppcoreguidelines
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_TYPE_CSTYLE_CAST_H

View File

@ -0,0 +1,484 @@
//===--- ProTypeMemberInitCheck.cpp - clang-tidy---------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "ProTypeMemberInitCheck.h"
#include "../utils/LexerUtils.h"
#include "../utils/Matchers.h"
#include "../utils/TypeTraits.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Lex/Lexer.h"
#include "llvm/ADT/SmallPtrSet.h"
using namespace clang::ast_matchers;
using namespace clang::tidy::matchers;
using llvm::SmallPtrSet;
using llvm::SmallPtrSetImpl;
namespace clang {
namespace tidy {
namespace cppcoreguidelines {
namespace {
AST_MATCHER(CXXRecordDecl, hasDefaultConstructor) {
return Node.hasDefaultConstructor();
}
// Iterate over all the fields in a record type, both direct and indirect (e.g.
// if the record contains an anonmyous struct).
template <typename T, typename Func>
void forEachField(const RecordDecl &Record, const T &Fields, Func &&Fn) {
for (const FieldDecl *F : Fields) {
if (F->isAnonymousStructOrUnion()) {
if (const CXXRecordDecl *R = F->getType()->getAsCXXRecordDecl())
forEachField(*R, R->fields(), Fn);
} else {
Fn(F);
}
}
}
void removeFieldsInitializedInBody(
const Stmt &Stmt, ASTContext &Context,
SmallPtrSetImpl<const FieldDecl *> &FieldDecls) {
auto Matches =
match(findAll(binaryOperator(
hasOperatorName("="),
hasLHS(memberExpr(member(fieldDecl().bind("fieldDecl")))))),
Stmt, Context);
for (const auto &Match : Matches)
FieldDecls.erase(Match.getNodeAs<FieldDecl>("fieldDecl"));
}
StringRef getName(const FieldDecl *Field) { return Field->getName(); }
StringRef getName(const RecordDecl *Record) {
// Get the typedef name if this is a C-style anonymous struct and typedef.
if (const TypedefNameDecl *Typedef = Record->getTypedefNameForAnonDecl())
return Typedef->getName();
return Record->getName();
}
// Creates comma separated list of decls requiring initialization in order of
// declaration.
template <typename R, typename T>
std::string
toCommaSeparatedString(const R &OrderedDecls,
const SmallPtrSetImpl<const T *> &DeclsToInit) {
SmallVector<StringRef, 16> Names;
for (const T *Decl : OrderedDecls) {
if (DeclsToInit.count(Decl))
Names.emplace_back(getName(Decl));
}
return llvm::join(Names.begin(), Names.end(), ", ");
}
SourceLocation getLocationForEndOfToken(const ASTContext &Context,
SourceLocation Location) {
return Lexer::getLocForEndOfToken(Location, 0, Context.getSourceManager(),
Context.getLangOpts());
}
// There are 3 kinds of insertion placements:
enum class InitializerPlacement {
// 1. The fields are inserted after an existing CXXCtorInitializer stored in
// Where. This will be the case whenever there is a written initializer before
// the fields available.
After,
// 2. The fields are inserted before the first existing initializer stored in
// Where.
Before,
// 3. There are no written initializers and the fields will be inserted before
// the constructor's body creating a new initializer list including the ':'.
New
};
// An InitializerInsertion contains a list of fields and/or base classes to
// insert into the initializer list of a constructor. We use this to ensure
// proper absolute ordering according to the class declaration relative to the
// (perhaps improper) ordering in the existing initializer list, if any.
struct IntializerInsertion {
IntializerInsertion(InitializerPlacement Placement,
const CXXCtorInitializer *Where)
: Placement(Placement), Where(Where) {}
SourceLocation getLocation(const ASTContext &Context,
const CXXConstructorDecl &Constructor) const {
assert((Where != nullptr || Placement == InitializerPlacement::New) &&
"Location should be relative to an existing initializer or this "
"insertion represents a new initializer list.");
SourceLocation Location;
switch (Placement) {
case InitializerPlacement::New:
Location = utils::lexer::getPreviousToken(
Context, Constructor.getBody()->getLocStart())
.getLocation();
break;
case InitializerPlacement::Before:
Location = utils::lexer::getPreviousToken(
Context, Where->getSourceRange().getBegin())
.getLocation();
break;
case InitializerPlacement::After:
Location = Where->getRParenLoc();
break;
}
return getLocationForEndOfToken(Context, Location);
}
std::string codeToInsert() const {
assert(!Initializers.empty() && "No initializers to insert");
std::string Code;
llvm::raw_string_ostream Stream(Code);
std::string joined =
llvm::join(Initializers.begin(), Initializers.end(), "(), ");
switch (Placement) {
case InitializerPlacement::New:
Stream << " : " << joined << "()";
break;
case InitializerPlacement::Before:
Stream << " " << joined << "(),";
break;
case InitializerPlacement::After:
Stream << ", " << joined << "()";
break;
}
return Stream.str();
}
InitializerPlacement Placement;
const CXXCtorInitializer *Where;
SmallVector<std::string, 4> Initializers;
};
// Convenience utility to get a RecordDecl from a QualType.
const RecordDecl *getCanonicalRecordDecl(const QualType &Type) {
if (const auto *RT = Type.getCanonicalType()->getAs<RecordType>())
return RT->getDecl();
return nullptr;
}
template <typename R, typename T>
SmallVector<IntializerInsertion, 16>
computeInsertions(const CXXConstructorDecl::init_const_range &Inits,
const R &OrderedDecls,
const SmallPtrSetImpl<const T *> &DeclsToInit) {
SmallVector<IntializerInsertion, 16> Insertions;
Insertions.emplace_back(InitializerPlacement::New, nullptr);
typename R::const_iterator Decl = std::begin(OrderedDecls);
for (const CXXCtorInitializer *Init : Inits) {
if (Init->isWritten()) {
if (Insertions.size() == 1)
Insertions.emplace_back(InitializerPlacement::Before, Init);
// Gets either the field or base class being initialized by the provided
// initializer.
const auto *InitDecl =
Init->isAnyMemberInitializer()
? static_cast<const NamedDecl *>(Init->getAnyMember())
: Init->getBaseClass()->getAsCXXRecordDecl();
// Add all fields between current field up until the next intializer.
for (; Decl != std::end(OrderedDecls) && *Decl != InitDecl; ++Decl) {
if (const auto *D = dyn_cast<T>(*Decl)) {
if (DeclsToInit.count(D) > 0)
Insertions.back().Initializers.emplace_back(getName(D));
}
}
Insertions.emplace_back(InitializerPlacement::After, Init);
}
}
// Add remaining decls that require initialization.
for (; Decl != std::end(OrderedDecls); ++Decl) {
if (const auto *D = dyn_cast<T>(*Decl)) {
if (DeclsToInit.count(D) > 0)
Insertions.back().Initializers.emplace_back(getName(D));
}
}
return Insertions;
}
// Gets the list of bases and members that could possibly be initialized, in
// order as they appear in the class declaration.
void getInitializationsInOrder(const CXXRecordDecl &ClassDecl,
SmallVectorImpl<const NamedDecl *> &Decls) {
Decls.clear();
for (const auto &Base : ClassDecl.bases()) {
// Decl may be null if the base class is a template parameter.
if (const NamedDecl *Decl = getCanonicalRecordDecl(Base.getType())) {
Decls.emplace_back(Decl);
}
}
forEachField(ClassDecl, ClassDecl.fields(),
[&](const FieldDecl *F) { Decls.push_back(F); });
}
template <typename T>
void fixInitializerList(const ASTContext &Context, DiagnosticBuilder &Diag,
const CXXConstructorDecl *Ctor,
const SmallPtrSetImpl<const T *> &DeclsToInit) {
// Do not propose fixes in macros since we cannot place them correctly.
if (Ctor->getLocStart().isMacroID())
return;
SmallVector<const NamedDecl *, 16> OrderedDecls;
getInitializationsInOrder(*Ctor->getParent(), OrderedDecls);
for (const auto &Insertion :
computeInsertions(Ctor->inits(), OrderedDecls, DeclsToInit)) {
if (!Insertion.Initializers.empty())
Diag << FixItHint::CreateInsertion(Insertion.getLocation(Context, *Ctor),
Insertion.codeToInsert());
}
}
} // anonymous namespace
ProTypeMemberInitCheck::ProTypeMemberInitCheck(StringRef Name,
ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
IgnoreArrays(Options.get("IgnoreArrays", false)) {}
void ProTypeMemberInitCheck::registerMatchers(MatchFinder *Finder) {
if (!getLangOpts().CPlusPlus)
return;
auto IsUserProvidedNonDelegatingConstructor =
allOf(isUserProvided(),
unless(anyOf(isInstantiated(), isDelegatingConstructor())));
auto IsNonTrivialDefaultConstructor = allOf(
isDefaultConstructor(), unless(isUserProvided()),
hasParent(cxxRecordDecl(unless(isTriviallyDefaultConstructible()))));
Finder->addMatcher(
cxxConstructorDecl(isDefinition(),
anyOf(IsUserProvidedNonDelegatingConstructor,
IsNonTrivialDefaultConstructor))
.bind("ctor"),
this);
// Match classes with a default constructor that is defaulted or is not in the
// AST.
Finder->addMatcher(
cxxRecordDecl(
isDefinition(), unless(isInstantiated()), hasDefaultConstructor(),
anyOf(has(cxxConstructorDecl(isDefaultConstructor(), isDefaulted(),
unless(isImplicit()))),
unless(has(cxxConstructorDecl()))),
unless(isTriviallyDefaultConstructible()))
.bind("record"),
this);
auto HasDefaultConstructor = hasInitializer(
cxxConstructExpr(unless(requiresZeroInitialization()),
hasDeclaration(cxxConstructorDecl(
isDefaultConstructor(), unless(isUserProvided())))));
Finder->addMatcher(
varDecl(isDefinition(), HasDefaultConstructor,
hasAutomaticStorageDuration(),
hasType(recordDecl(has(fieldDecl()),
isTriviallyDefaultConstructible())))
.bind("var"),
this);
}
void ProTypeMemberInitCheck::check(const MatchFinder::MatchResult &Result) {
if (const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor")) {
// Skip declarations delayed by late template parsing without a body.
if (!Ctor->getBody())
return;
checkMissingMemberInitializer(*Result.Context, *Ctor->getParent(), Ctor);
checkMissingBaseClassInitializer(*Result.Context, *Ctor->getParent(), Ctor);
} else if (const auto *Record =
Result.Nodes.getNodeAs<CXXRecordDecl>("record")) {
assert(Record->hasDefaultConstructor() &&
"Matched record should have a default constructor");
checkMissingMemberInitializer(*Result.Context, *Record, nullptr);
checkMissingBaseClassInitializer(*Result.Context, *Record, nullptr);
} else if (const auto *Var = Result.Nodes.getNodeAs<VarDecl>("var")) {
checkUninitializedTrivialType(*Result.Context, Var);
}
}
void ProTypeMemberInitCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "IgnoreArrays", IgnoreArrays);
}
// FIXME: Copied from clang/lib/Sema/SemaDeclCXX.cpp.
static bool isIncompleteOrZeroLengthArrayType(ASTContext &Context, QualType T) {
if (T->isIncompleteArrayType())
return true;
while (const ConstantArrayType *ArrayT = Context.getAsConstantArrayType(T)) {
if (!ArrayT->getSize())
return true;
T = ArrayT->getElementType();
}
return false;
}
static bool isEmpty(ASTContext &Context, const QualType &Type) {
if (const CXXRecordDecl *ClassDecl = Type->getAsCXXRecordDecl()) {
return ClassDecl->isEmpty();
}
return isIncompleteOrZeroLengthArrayType(Context, Type);
}
void ProTypeMemberInitCheck::checkMissingMemberInitializer(
ASTContext &Context, const CXXRecordDecl &ClassDecl,
const CXXConstructorDecl *Ctor) {
bool IsUnion = ClassDecl.isUnion();
if (IsUnion && ClassDecl.hasInClassInitializer())
return;
// Gather all fields (direct and indirect) that need to be initialized.
SmallPtrSet<const FieldDecl *, 16> FieldsToInit;
forEachField(ClassDecl, ClassDecl.fields(), [&](const FieldDecl *F) {
if (!F->hasInClassInitializer() &&
utils::type_traits::isTriviallyDefaultConstructible(F->getType(),
Context) &&
!isEmpty(Context, F->getType()) && !F->isUnnamedBitfield())
FieldsToInit.insert(F);
});
if (FieldsToInit.empty())
return;
if (Ctor) {
for (const CXXCtorInitializer *Init : Ctor->inits()) {
// Remove any fields that were explicitly written in the initializer list
// or in-class.
if (Init->isAnyMemberInitializer() && Init->isWritten()) {
if (IsUnion)
return; // We can only initialize one member of a union.
FieldsToInit.erase(Init->getAnyMember());
}
}
removeFieldsInitializedInBody(*Ctor->getBody(), Context, FieldsToInit);
}
// Collect all fields in order, both direct fields and indirect fields from
// anonmyous record types.
SmallVector<const FieldDecl *, 16> OrderedFields;
forEachField(ClassDecl, ClassDecl.fields(),
[&](const FieldDecl *F) { OrderedFields.push_back(F); });
// Collect all the fields we need to initialize, including indirect fields.
SmallPtrSet<const FieldDecl *, 16> AllFieldsToInit;
forEachField(ClassDecl, FieldsToInit,
[&](const FieldDecl *F) { AllFieldsToInit.insert(F); });
if (AllFieldsToInit.empty())
return;
DiagnosticBuilder Diag =
diag(Ctor ? Ctor->getLocStart() : ClassDecl.getLocation(),
IsUnion
? "union constructor should initialize one of these fields: %0"
: "constructor does not initialize these fields: %0")
<< toCommaSeparatedString(OrderedFields, AllFieldsToInit);
// Do not propose fixes for constructors in macros since we cannot place them
// correctly.
if (Ctor && Ctor->getLocStart().isMacroID())
return;
// Collect all fields but only suggest a fix for the first member of unions,
// as initializing more than one union member is an error.
SmallPtrSet<const FieldDecl *, 16> FieldsToFix;
SmallPtrSet<const RecordDecl *, 4> UnionsSeen;
forEachField(ClassDecl, OrderedFields, [&](const FieldDecl *F) {
if (!FieldsToInit.count(F))
return;
// Don't suggest fixes for enums because we don't know a good default.
// Don't suggest fixes for bitfields because in-class initialization is not
// possible.
if (F->getType()->isEnumeralType() || F->isBitField())
return;
if (!F->getParent()->isUnion() || UnionsSeen.insert(F->getParent()).second)
FieldsToFix.insert(F);
});
if (FieldsToFix.empty())
return;
// Use in-class initialization if possible.
if (Context.getLangOpts().CPlusPlus11) {
for (const FieldDecl *Field : FieldsToFix) {
Diag << FixItHint::CreateInsertion(
getLocationForEndOfToken(Context, Field->getSourceRange().getEnd()),
"{}");
}
} else if (Ctor) {
// Otherwise, rewrite the constructor's initializer list.
fixInitializerList(Context, Diag, Ctor, FieldsToFix);
}
}
void ProTypeMemberInitCheck::checkMissingBaseClassInitializer(
const ASTContext &Context, const CXXRecordDecl &ClassDecl,
const CXXConstructorDecl *Ctor) {
// Gather any base classes that need to be initialized.
SmallVector<const RecordDecl *, 4> AllBases;
SmallPtrSet<const RecordDecl *, 4> BasesToInit;
for (const CXXBaseSpecifier &Base : ClassDecl.bases()) {
if (const auto *BaseClassDecl = getCanonicalRecordDecl(Base.getType())) {
AllBases.emplace_back(BaseClassDecl);
if (!BaseClassDecl->field_empty() &&
utils::type_traits::isTriviallyDefaultConstructible(Base.getType(),
Context))
BasesToInit.insert(BaseClassDecl);
}
}
if (BasesToInit.empty())
return;
// Remove any bases that were explicitly written in the initializer list.
if (Ctor) {
if (Ctor->isImplicit())
return;
for (const CXXCtorInitializer *Init : Ctor->inits()) {
if (Init->isBaseInitializer() && Init->isWritten())
BasesToInit.erase(Init->getBaseClass()->getAsCXXRecordDecl());
}
}
if (BasesToInit.empty())
return;
DiagnosticBuilder Diag =
diag(Ctor ? Ctor->getLocStart() : ClassDecl.getLocation(),
"constructor does not initialize these bases: %0")
<< toCommaSeparatedString(AllBases, BasesToInit);
if (Ctor)
fixInitializerList(Context, Diag, Ctor, BasesToInit);
}
void ProTypeMemberInitCheck::checkUninitializedTrivialType(
const ASTContext &Context, const VarDecl *Var) {
DiagnosticBuilder Diag =
diag(Var->getLocStart(), "uninitialized record type: %0") << Var;
Diag << FixItHint::CreateInsertion(
getLocationForEndOfToken(Context, Var->getSourceRange().getEnd()),
Context.getLangOpts().CPlusPlus11 ? "{}" : " = {}");
}
} // namespace cppcoreguidelines
} // namespace tidy
} // namespace clang

View File

@ -0,0 +1,74 @@
//===--- ProTypeMemberInitCheck.h - clang-tidy-------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_TYPE_MEMBER_INIT_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_TYPE_MEMBER_INIT_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace cppcoreguidelines {
/// \brief Implements C++ Core Guidelines Type.6.
///
/// Checks that every user-provided constructor value-initializes all class
/// members and base classes that would have undefined behavior otherwise. Also
/// check that any record types without user-provided default constructors are
/// value-initialized where used.
///
/// Members initialized through function calls in the body of the constructor
/// will result in false positives.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-pro-type-member-init.html
/// TODO: See if 'fixes' for false positives are optimized away by the compiler.
/// TODO: For classes with multiple constructors, make sure that we don't offer
/// multiple in-class initializer fixits for the same member.
class ProTypeMemberInitCheck : public ClangTidyCheck {
public:
ProTypeMemberInitCheck(StringRef Name, ClangTidyContext *Context);
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
private:
// Checks Type.6 part 1:
// Issue a diagnostic for any constructor of a non-trivially-constructible
// type that does not initialize all member variables.
//
// To fix: Write a data member initializer, or mention it in the member
// initializer list.
void checkMissingMemberInitializer(ASTContext &Context,
const CXXRecordDecl &ClassDecl,
const CXXConstructorDecl *Ctor);
// A subtle side effect of Type.6 part 2:
// Make sure to initialize trivially constructible base classes.
void checkMissingBaseClassInitializer(const ASTContext &Context,
const CXXRecordDecl &ClassDecl,
const CXXConstructorDecl *Ctor);
// Checks Type.6 part 2:
// Issue a diagnostic when constructing an object of a trivially constructible
// type without () or {} to initialize its members.
//
// To fix: Add () or {}.
void checkUninitializedTrivialType(const ASTContext &Context,
const VarDecl *Var);
// Whether arrays need to be initialized or not. Default is false.
bool IgnoreArrays;
};
} // namespace cppcoreguidelines
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_TYPE_MEMBER_INIT_H

Some files were not shown because too many files have changed in this diff Show More