| //===--- 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 <algorithm> |
| |
| 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<NamedDecl>("decl"); |
| const auto *BD = Result.Nodes.getNodeAs<NamedDecl>("type_decl"); |
| const auto *E = Result.Nodes.getNodeAs<Expr>("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 |
| |