xref: /llvm-project/clang-tools-extra/clang-tidy/misc/NonCopyableObjects.cpp (revision 456177b98fa3d807007ccfe671aae02691480e5a)
1 //===--- NonCopyableObjects.cpp - clang-tidy-------------------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "NonCopyableObjects.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include <algorithm>
14 
15 using namespace clang::ast_matchers;
16 
17 namespace clang {
18 namespace tidy {
19 namespace misc {
20 
21 namespace {
22 // FIXME: it would be good to make a list that is also user-configurable so that
23 // users can add their own elements to the list. However, it may require some
24 // extra thought since POSIX types and FILE types are usable in different ways.
25 bool isPOSIXTypeName(StringRef ClassName) {
26   static const char *const TypeNames[] = {
27     "::pthread_cond_t",
28     "::pthread_mutex_t",
29     "pthread_cond_t",
30     "pthread_mutex_t"
31   };
32   return std::binary_search(std::begin(TypeNames), std::end(TypeNames),
33                             ClassName);
34 }
35 
36 bool isFILETypeName(StringRef ClassName) {
37   static const char *const TypeNames[] = {
38     "::FILE",
39     "FILE",
40     "std::FILE"
41   };
42   return std::binary_search(std::begin(TypeNames), std::end(TypeNames),
43                             ClassName);
44 }
45 
46 AST_MATCHER(NamedDecl, isFILEType) {
47   return isFILETypeName(Node.getQualifiedNameAsString());
48 }
49 
50 AST_MATCHER(NamedDecl, isPOSIXType) {
51   return isPOSIXTypeName(Node.getQualifiedNameAsString());
52 }
53 } // namespace
54 
55 void NonCopyableObjectsCheck::registerMatchers(MatchFinder *Finder) {
56   // There are two ways to get into trouble with objects like FILE *:
57   // dereferencing the pointer type to be a non-pointer type, and declaring
58   // the type as a non-pointer type in the first place. While the declaration
59   // itself could technically be well-formed in the case where the type is not
60   // an opaque type, it's highly suspicious behavior.
61   //
62   // POSIX types are a bit different in that it's reasonable to declare a
63   // non-pointer variable or data member of the type, but it is not reasonable
64   // to dereference a pointer to the type, or declare a parameter of non-pointer
65   // type.
66   auto BadFILEType = hasType(namedDecl(isFILEType()).bind("type_decl"));
67   auto BadPOSIXType = hasType(namedDecl(isPOSIXType()).bind("type_decl"));
68   auto BadEitherType = anyOf(BadFILEType, BadPOSIXType);
69 
70   Finder->addMatcher(
71       namedDecl(anyOf(varDecl(BadFILEType), fieldDecl(BadFILEType)))
72           .bind("decl"),
73       this);
74   Finder->addMatcher(parmVarDecl(BadPOSIXType).bind("decl"), this);
75   Finder->addMatcher(
76       expr(unaryOperator(hasOperatorName("*"), BadEitherType)).bind("expr"),
77       this);
78 }
79 
80 void NonCopyableObjectsCheck::check(const MatchFinder::MatchResult &Result) {
81   const auto *D = Result.Nodes.getNodeAs<NamedDecl>("decl");
82   const auto *BD = Result.Nodes.getNodeAs<NamedDecl>("type_decl");
83   const auto *E = Result.Nodes.getNodeAs<Expr>("expr");
84 
85   if (D && BD)
86     diag(D->getLocation(), "%0 declared as type '%1', which is unsafe to copy"
87                            "; did you mean '%1 *'?")
88         << D << BD->getName();
89   else if (E)
90     diag(E->getExprLoc(),
91          "expression has opaque data structure type %0; type should only be "
92          "used as a pointer and not dereferenced")
93         << BD;
94 }
95 
96 } // namespace misc
97 } // namespace tidy
98 } // namespace clang
99 
100