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