diff options
author | Jonas Toth <jonas.toth@gmail.com> | 2017-10-18 16:14:15 +0000 |
---|---|---|
committer | Jonas Toth <jonas.toth@gmail.com> | 2017-10-18 16:14:15 +0000 |
commit | c9aea86e6af2bc1f7414f69f31428cf49273bf62 (patch) | |
tree | eb9d59a48d895e2799a488c3b416a79a7d66bc89 /clang-tools-extra/clang-tidy/cppcoreguidelines | |
parent | 13ce95b77f54aa4a7ff01a46a5faaa85001755a6 (diff) | |
download | bcm5719-llvm-c9aea86e6af2bc1f7414f69f31428cf49273bf62.tar.gz bcm5719-llvm-c9aea86e6af2bc1f7414f69f31428cf49273bf62.zip |
[clang-tidy] introduce legacy resource functions to 'cppcoreguidelines-owning-memory'
Summary:
This patch introduces support for legacy C-style resource functions that must obey
the 'owner<>' semantics.
- added legacy creators like malloc,fopen,...
- added legacy consumers like free,fclose,...
This helps codes that mostly benefit from owner:
Legacy, C-Style code that isn't feasable to port directly to RAII but needs a step in between
to identify actual resource management and just using the resources.
Reviewers: aaron.ballman, alexfh, hokein
Reviewed By: aaron.ballman
Subscribers: nemanjai, JDevlieghere, xazax.hun, kbarton
Differential Revision: https://reviews.llvm.org/D38396
llvm-svn: 316092
Diffstat (limited to 'clang-tools-extra/clang-tidy/cppcoreguidelines')
-rw-r--r-- | clang-tools-extra/clang-tidy/cppcoreguidelines/OwningMemoryCheck.cpp | 74 | ||||
-rw-r--r-- | clang-tools-extra/clang-tidy/cppcoreguidelines/OwningMemoryCheck.h | 21 |
2 files changed, 91 insertions, 4 deletions
diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/OwningMemoryCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/OwningMemoryCheck.cpp index ac3d92c98ac..6c86e053b14 100644 --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/OwningMemoryCheck.cpp +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/OwningMemoryCheck.cpp @@ -22,6 +22,21 @@ 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) { @@ -30,10 +45,31 @@ void OwningMemoryCheck::registerMatchers(MatchFinder *Finder) { 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))))))); - const auto ConsideredOwner = anyOf(IsOwnerType, 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( @@ -43,6 +79,21 @@ void OwningMemoryCheck::registerMatchers(MatchFinder *Finder) { .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(), @@ -133,6 +184,7 @@ void OwningMemoryCheck::check(const MatchFinder::MatchResult &Result) { bool CheckExecuted = false; CheckExecuted |= handleDeletion(Nodes); + CheckExecuted |= handleLegacyConsumers(Nodes); CheckExecuted |= handleExpectedOwner(Nodes); CheckExecuted |= handleAssignmentAndInit(Nodes); CheckExecuted |= handleAssignmentFromNewOwner(Nodes); @@ -168,6 +220,22 @@ bool OwningMemoryCheck::handleDeletion(const BoundNodes &Nodes) { 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"); diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/OwningMemoryCheck.h b/clang-tools-extra/clang-tidy/cppcoreguidelines/OwningMemoryCheck.h index 60c40bc710b..e68e4c09b12 100644 --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/OwningMemoryCheck.h +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/OwningMemoryCheck.h @@ -24,17 +24,36 @@ namespace cppcoreguidelines { class OwningMemoryCheck : public ClangTidyCheck { public: OwningMemoryCheck(StringRef Name, ClangTidyContext *Context) - : ClangTidyCheck(Name, 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 |