1 //===--- TypeTraits.cpp - clang-tidy---------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "TypeTraits.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/AST/DeclCXX.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13
14 namespace clang {
15 namespace tidy {
16 namespace utils {
17 namespace type_traits {
18
19 namespace {
20
classHasTrivialCopyAndDestroy(QualType Type)21 bool classHasTrivialCopyAndDestroy(QualType Type) {
22 auto *Record = Type->getAsCXXRecordDecl();
23 return Record && Record->hasDefinition() &&
24 !Record->hasNonTrivialCopyConstructor() &&
25 !Record->hasNonTrivialDestructor();
26 }
27
hasDeletedCopyConstructor(QualType Type)28 bool hasDeletedCopyConstructor(QualType Type) {
29 auto *Record = Type->getAsCXXRecordDecl();
30 if (!Record || !Record->hasDefinition())
31 return false;
32 for (const auto *Constructor : Record->ctors()) {
33 if (Constructor->isCopyConstructor() && Constructor->isDeleted())
34 return true;
35 }
36 return false;
37 }
38
39 } // namespace
40
isExpensiveToCopy(QualType Type,const ASTContext & Context)41 llvm::Optional<bool> isExpensiveToCopy(QualType Type,
42 const ASTContext &Context) {
43 if (Type->isDependentType() || Type->isIncompleteType())
44 return llvm::None;
45 return !Type.isTriviallyCopyableType(Context) &&
46 !classHasTrivialCopyAndDestroy(Type) &&
47 !hasDeletedCopyConstructor(Type) &&
48 !Type->isObjCLifetimeType();
49 }
50
recordIsTriviallyDefaultConstructible(const RecordDecl & RecordDecl,const ASTContext & Context)51 bool recordIsTriviallyDefaultConstructible(const RecordDecl &RecordDecl,
52 const ASTContext &Context) {
53 const auto *ClassDecl = dyn_cast<CXXRecordDecl>(&RecordDecl);
54 // Non-C++ records are always trivially constructible.
55 if (!ClassDecl)
56 return true;
57 // It is impossible to determine whether an ill-formed decl is trivially
58 // constructible.
59 if (RecordDecl.isInvalidDecl())
60 return false;
61 // A class with a user-provided default constructor is not trivially
62 // constructible.
63 if (ClassDecl->hasUserProvidedDefaultConstructor())
64 return false;
65 // A polymorphic class is not trivially constructible
66 if (ClassDecl->isPolymorphic())
67 return false;
68 // A class is trivially constructible if it has a trivial default constructor.
69 if (ClassDecl->hasTrivialDefaultConstructor())
70 return true;
71
72 // If all its fields are trivially constructible and have no default
73 // initializers.
74 for (const FieldDecl *Field : ClassDecl->fields()) {
75 if (Field->hasInClassInitializer())
76 return false;
77 if (!isTriviallyDefaultConstructible(Field->getType(), Context))
78 return false;
79 }
80 // If all its direct bases are trivially constructible.
81 for (const CXXBaseSpecifier &Base : ClassDecl->bases()) {
82 if (!isTriviallyDefaultConstructible(Base.getType(), Context))
83 return false;
84 if (Base.isVirtual())
85 return false;
86 }
87
88 return true;
89 }
90
91 // Based on QualType::isTrivial.
isTriviallyDefaultConstructible(QualType Type,const ASTContext & Context)92 bool isTriviallyDefaultConstructible(QualType Type, const ASTContext &Context) {
93 if (Type.isNull())
94 return false;
95
96 if (Type->isArrayType())
97 return isTriviallyDefaultConstructible(Context.getBaseElementType(Type),
98 Context);
99
100 // Return false for incomplete types after skipping any incomplete array
101 // types which are expressly allowed by the standard and thus our API.
102 if (Type->isIncompleteType())
103 return false;
104
105 if (Context.getLangOpts().ObjCAutoRefCount) {
106 switch (Type.getObjCLifetime()) {
107 case Qualifiers::OCL_ExplicitNone:
108 return true;
109
110 case Qualifiers::OCL_Strong:
111 case Qualifiers::OCL_Weak:
112 case Qualifiers::OCL_Autoreleasing:
113 return false;
114
115 case Qualifiers::OCL_None:
116 if (Type->isObjCLifetimeType())
117 return false;
118 break;
119 }
120 }
121
122 QualType CanonicalType = Type.getCanonicalType();
123 if (CanonicalType->isDependentType())
124 return false;
125
126 // As an extension, Clang treats vector types as Scalar types.
127 if (CanonicalType->isScalarType() || CanonicalType->isVectorType())
128 return true;
129
130 if (const auto *RT = CanonicalType->getAs<RecordType>()) {
131 return recordIsTriviallyDefaultConstructible(*RT->getDecl(), Context);
132 }
133
134 // No other types can match.
135 return false;
136 }
137
138 // Based on QualType::isDestructedType.
isTriviallyDestructible(QualType Type)139 bool isTriviallyDestructible(QualType Type) {
140 if (Type.isNull())
141 return false;
142
143 if (Type->isIncompleteType())
144 return false;
145
146 if (Type.getCanonicalType()->isDependentType())
147 return false;
148
149 return Type.isDestructedType() == QualType::DK_none;
150 }
151
hasNonTrivialMoveConstructor(QualType Type)152 bool hasNonTrivialMoveConstructor(QualType Type) {
153 auto *Record = Type->getAsCXXRecordDecl();
154 return Record && Record->hasDefinition() &&
155 Record->hasNonTrivialMoveConstructor();
156 }
157
hasNonTrivialMoveAssignment(QualType Type)158 bool hasNonTrivialMoveAssignment(QualType Type) {
159 auto *Record = Type->getAsCXXRecordDecl();
160 return Record && Record->hasDefinition() &&
161 Record->hasNonTrivialMoveAssignment();
162 }
163
164 } // namespace type_traits
165 } // namespace utils
166 } // namespace tidy
167 } // namespace clang
168