summaryrefslogtreecommitdiffstats
path: root/clang-tools-extra/clang-tidy/cppcoreguidelines
diff options
context:
space:
mode:
authorJonas Toth <jonas.toth@gmail.com>2017-09-12 16:20:51 +0000
committerJonas Toth <jonas.toth@gmail.com>2017-09-12 16:20:51 +0000
commita5d53274f34f33a9c1edc833072d4159c7e7ba92 (patch)
tree87f1996db178a8a2e746bad8062f969602ee28aa /clang-tools-extra/clang-tidy/cppcoreguidelines
parent7fac4b2f25885d515502391fcb0b54f78e2b6dde (diff)
downloadbcm5719-llvm-a5d53274f34f33a9c1edc833072d4159c7e7ba92.tar.gz
bcm5719-llvm-a5d53274f34f33a9c1edc833072d4159c7e7ba92.zip
[clang-tidy] Implement type-based check for `gsl::owner`
This check implements the typebased semantic of `gsl::owner`. Meaning, that - only `gsl::owner` is allowed to get `delete`d - `new` expression must be assigned to `gsl::owner` - function calls that expect `gsl::owner` as argument, must get either an owner or a newly created and recognized resource (in the moment only `new`ed memory) - assignment to `gsl::owner` must be either a resource or another owner - functions returning an `gsl::owner` are considered as factories, and their result must be assigned to an `gsl::owner` - classes that have an `gsl::owner`-member must declare a non-default destructor There are some problems that occur when typededuction is in place. For example `auto Var = function_that_returns_owner();` the type of `Var` will not be an `gsl::owner`. This case is catched, and explicitly noted. But cases like fully templated functions ``` template <typename T> void f(T t) { delete t; } // ... f(gsl::owner<int*>(new int(42))); ``` Will created false positive (the deletion is problematic), since the type deduction removes the wrapping `typeAlias`. Please give your comments :) llvm-svn: 313043
Diffstat (limited to 'clang-tools-extra/clang-tidy/cppcoreguidelines')
-rw-r--r--clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt1
-rw-r--r--clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp3
-rw-r--r--clang-tools-extra/clang-tidy/cppcoreguidelines/OwningMemoryCheck.cpp315
-rw-r--r--clang-tools-extra/clang-tidy/cppcoreguidelines/OwningMemoryCheck.h44
4 files changed, 363 insertions, 0 deletions
diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt b/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt
index 43ac55d1dd1..70ee599c69e 100644
--- a/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt
@@ -4,6 +4,7 @@ add_clang_library(clangTidyCppCoreGuidelinesModule
CppCoreGuidelinesTidyModule.cpp
InterfacesGlobalInitCheck.cpp
NoMallocCheck.cpp
+ OwningMemoryCheck.cpp
ProBoundsArrayToPointerDecayCheck.cpp
ProBoundsConstantArrayIndexCheck.cpp
ProBoundsPointerArithmeticCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp
index 6b9de8d3905..454adc9fd19 100644
--- a/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp
@@ -13,6 +13,7 @@
#include "../misc/UnconventionalAssignOperatorCheck.h"
#include "InterfacesGlobalInitCheck.h"
#include "NoMallocCheck.h"
+#include "OwningMemoryCheck.h"
#include "ProBoundsArrayToPointerDecayCheck.h"
#include "ProBoundsConstantArrayIndexCheck.h"
#include "ProBoundsPointerArithmeticCheck.h"
@@ -37,6 +38,8 @@ public:
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>(
diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/OwningMemoryCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/OwningMemoryCheck.cpp
new file mode 100644
index 00000000000..3227169409f
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/OwningMemoryCheck.cpp
@@ -0,0 +1,315 @@
+//===--- 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 {
+
+/// 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 CreatesOwner =
+ anyOf(cxxNewExpr(), callExpr(callee(functionDecl(
+ returns(qualType(hasDeclaration(OwnerDecl)))))));
+ const auto ConsideredOwner = anyOf(IsOwnerType, CreatesOwner);
+
+ // Find delete expressions that delete non-owners.
+ Finder->addMatcher(
+ cxxDeleteExpr(
+ hasDescendant(
+ declRefExpr(unless(ConsideredOwner)).bind("deleted_variable")))
+ .bind("delete_expr"),
+ 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);
+
+ // Match class member initialization that expects owners, but does not get
+ // them.
+ Finder->addMatcher(
+ cxxRecordDecl(has(cxxConstructorDecl(forEachConstructorInitializer(
+ cxxCtorInitializer(
+ allOf(
+ isMemberInitializer(), forField(IsOwnerType),
+ withInitializer(
+ // Avoid templatesdeclaration with excluding parenListExpr.
+ allOf(unless(ConsideredOwner), unless(parenListExpr())))))
+ .bind("owner_member_initializer"))))),
+ 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 |= 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();
+ 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 if possible.
+
+ // 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 of type owner, but not the declared return type.
+ 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();
+ // The returned value is a resource that was not annotated with owner<> and
+ // the function return type is not owner<>.
+ 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
diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/OwningMemoryCheck.h b/clang-tools-extra/clang-tidy/cppcoreguidelines/OwningMemoryCheck.h
new file mode 100644
index 00000000000..60c40bc710b
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/OwningMemoryCheck.h
@@ -0,0 +1,44 @@
+//===--- 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) {}
+ 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 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);
+};
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_OWNING_MEMORY_H
OpenPOWER on IntegriCloud