//===--- NonCopyableObjects.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 "NonCopyableObjects.h" #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include using namespace clang::ast_matchers; namespace clang { namespace { // FIXME: it would be good to make a list that is also user-configurable so that // users can add their own elements to the list. However, it may require some // extra thought since POSIX types and FILE types are usable in different ways. bool isPOSIXTypeName(StringRef ClassName) { static const char *const TypeNames[] = { "::pthread_cond_t", "::pthread_mutex_t", "pthread_cond_t", "pthread_mutex_t" }; return std::binary_search(std::begin(TypeNames), std::end(TypeNames), ClassName); } bool isFILETypeName(StringRef ClassName) { static const char *const TypeNames[] = { "::FILE", "FILE", "std::FILE" }; return std::binary_search(std::begin(TypeNames), std::end(TypeNames), ClassName); } AST_MATCHER(NamedDecl, isFILEType) { return isFILETypeName(Node.getQualifiedNameAsString()); } AST_MATCHER(NamedDecl, isPOSIXType) { return isPOSIXTypeName(Node.getQualifiedNameAsString()); } } // namespace namespace tidy { void NonCopyableObjectsCheck::registerMatchers(MatchFinder *Finder) { // There are two ways to get into trouble with objects like FILE *: // dereferencing the pointer type to be a non-pointer type, and declaring // the type as a non-pointer type in the first place. While the declaration // itself could technically be well-formed in the case where the type is not // an opaque type, it's highly suspicious behavior. // // POSIX types are a bit different in that it's reasonable to declare a // non-pointer variable or data member of the type, but it is not reasonable // to dereference a pointer to the type, or declare a parameter of non-pointer // type. auto BadFILEType = hasType(namedDecl(isFILEType()).bind("type_decl")); auto BadPOSIXType = hasType(namedDecl(isPOSIXType()).bind("type_decl")); auto BadEitherType = anyOf(BadFILEType, BadPOSIXType); Finder->addMatcher( namedDecl(anyOf(varDecl(BadFILEType), fieldDecl(BadFILEType))) .bind("decl"), this); Finder->addMatcher(parmVarDecl(BadPOSIXType).bind("decl"), this); Finder->addMatcher( expr(unaryOperator(hasOperatorName("*"), BadEitherType)).bind("expr"), this); } void NonCopyableObjectsCheck::check(const MatchFinder::MatchResult &Result) { const auto *D = Result.Nodes.getNodeAs("decl"); const auto *BD = Result.Nodes.getNodeAs("type_decl"); const auto *E = Result.Nodes.getNodeAs("expr"); if (D && BD) diag(D->getLocation(), "'%0' declared as type '%1', which is unsafe to copy" "; did you mean '%1 *'?") << D->getName() << BD->getName(); else if (E) diag(E->getExprLoc(), "expression has opaque data structure type '%0'; type should only be " "used as a pointer and not dereferenced") << BD->getName(); } } // namespace tidy } // namespace clang