Bug Summary

File:build/source/clang-tools-extra/clangd/refactor/tweaks/ObjCMemberwiseInitializer.cpp
Warning:line 110, column 27
Called C++ object pointer is null

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name ObjCMemberwiseInitializer.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -mframe-pointer=none -relaxed-aliasing -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -ffunction-sections -fdata-sections -fcoverage-compilation-dir=/build/source/build-llvm/tools/clang/stage2-bins -resource-dir /usr/lib/llvm-17/lib/clang/17 -D CLANG_REPOSITORY_STRING="++20230510111145+7df43bdb42ae-1~exp1~20230510111303.1288" -D _DEBUG -D _GLIBCXX_ASSERTIONS -D _GNU_SOURCE -D _LIBCPP_ENABLE_ASSERTIONS -D __STDC_CONSTANT_MACROS -D __STDC_FORMAT_MACROS -D __STDC_LIMIT_MACROS -I tools/clang/tools/extra/clangd/refactor/tweaks -I /build/source/clang-tools-extra/clangd/refactor/tweaks -I /build/source/clang-tools-extra/clangd/../include-cleaner/include -I tools/clang/tools/extra/clangd/../clang-tidy -I /build/source/clang/include -I tools/clang/include -I include -I /build/source/llvm/include -I /build/source/clang-tools-extra/clangd -I tools/clang/tools/extra/clangd -I /build/source/clang-tools-extra/clangd/refactor/tweaks/../.. -D _FORTIFY_SOURCE=2 -D NDEBUG -U NDEBUG -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/x86_64-linux-gnu/c++/10 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/backward -internal-isystem /usr/lib/llvm-17/lib/clang/17/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fmacro-prefix-map=/build/source/build-llvm/tools/clang/stage2-bins=build-llvm/tools/clang/stage2-bins -fmacro-prefix-map=/build/source/= -fcoverage-prefix-map=/build/source/build-llvm/tools/clang/stage2-bins=build-llvm/tools/clang/stage2-bins -fcoverage-prefix-map=/build/source/= -source-date-epoch 1683717183 -O2 -Wno-unused-command-line-argument -Wno-unused-parameter -Wwrite-strings -Wno-missing-field-initializers -Wno-long-long -Wno-maybe-uninitialized -Wno-class-memaccess -Wno-redundant-move -Wno-pessimizing-move -Wno-noexcept-type -Wno-comment -Wno-misleading-indentation -std=c++17 -fdeprecated-macro -fdebug-compilation-dir=/build/source/build-llvm/tools/clang/stage2-bins -fdebug-prefix-map=/build/source/build-llvm/tools/clang/stage2-bins=build-llvm/tools/clang/stage2-bins -fdebug-prefix-map=/build/source/= -ferror-limit 19 -fvisibility-inlines-hidden -stack-protector 2 -fgnuc-version=4.2.1 -fcolor-diagnostics -vectorize-loops -vectorize-slp -analyzer-output=html -analyzer-config stable-report-filename=true -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2023-05-10-133810-16478-1 -x c++ /build/source/clang-tools-extra/clangd/refactor/tweaks/ObjCMemberwiseInitializer.cpp

/build/source/clang-tools-extra/clangd/refactor/tweaks/ObjCMemberwiseInitializer.cpp

1//===--- ObjCMemberwiseInitializer.cpp ---------------------------*- C++-*-===//
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 "ParsedAST.h"
10#include "SourceCode.h"
11#include "refactor/InsertionPoint.h"
12#include "refactor/Tweak.h"
13#include "support/Logger.h"
14#include "clang/AST/DeclObjC.h"
15#include "clang/AST/PrettyPrinter.h"
16#include "clang/Basic/LLVM.h"
17#include "clang/Basic/LangOptions.h"
18#include "clang/Basic/SourceLocation.h"
19#include "clang/Basic/SourceManager.h"
20#include "clang/Tooling/Core/Replacement.h"
21#include "llvm/ADT/StringRef.h"
22#include "llvm/ADT/iterator_range.h"
23#include "llvm/Support/Casting.h"
24#include "llvm/Support/Error.h"
25#include <optional>
26
27namespace clang {
28namespace clangd {
29namespace {
30
31static std::string capitalize(std::string Message) {
32 if (!Message.empty())
33 Message[0] = llvm::toUpper(Message[0]);
34 return Message;
35}
36
37static std::string getTypeStr(const QualType &OrigT, const Decl &D,
38 unsigned PropertyAttributes) {
39 QualType T = OrigT;
40 PrintingPolicy Policy(D.getASTContext().getLangOpts());
41 Policy.SuppressStrongLifetime = true;
42 std::string Prefix;
43 // If the nullability is specified via a property attribute, use the shorter
44 // `nullable` form for the method parameter.
45 if (PropertyAttributes & ObjCPropertyAttribute::kind_nullability) {
46 if (auto Kind = AttributedType::stripOuterNullability(T)) {
47 switch (*Kind) {
48 case NullabilityKind::Nullable:
49 Prefix = "nullable ";
50 break;
51 case NullabilityKind::NonNull:
52 Prefix = "nonnull ";
53 break;
54 case NullabilityKind::Unspecified:
55 Prefix = "null_unspecified ";
56 break;
57 case NullabilityKind::NullableResult:
58 T = OrigT;
59 break;
60 }
61 }
62 }
63 return Prefix + T.getAsString(Policy);
64}
65
66struct MethodParameter {
67 // Parameter name.
68 llvm::StringRef Name;
69
70 // Type of the parameter.
71 std::string Type;
72
73 // Assignment target (LHS).
74 std::string Assignee;
75
76 MethodParameter(const ObjCIvarDecl &ID) {
77 // Convention maps `@property int foo` to ivar `int _foo`, so drop the
78 // leading `_` if there is one.
79 Name = ID.getName();
80 Name.consume_front("_");
81 Type = getTypeStr(ID.getType(), ID, ObjCPropertyAttribute::kind_noattr);
82 Assignee = ID.getName().str();
83 }
84 MethodParameter(const ObjCPropertyDecl &PD) {
85 Name = PD.getName();
86 Type = getTypeStr(PD.getType(), PD, PD.getPropertyAttributes());
87 if (const auto *ID = PD.getPropertyIvarDecl())
88 Assignee = ID->getName().str();
89 else // Could be a dynamic property or a property in a header.
90 Assignee = ("self." + Name).str();
91 }
92 static std::optional<MethodParameter> parameterFor(const Decl &D) {
93 if (const auto *ID = dyn_cast<ObjCIvarDecl>(&D))
94 return MethodParameter(*ID);
95 if (const auto *PD = dyn_cast<ObjCPropertyDecl>(&D))
96 if (PD->isInstanceProperty())
97 return MethodParameter(*PD);
98 return std::nullopt;
99 }
100};
101
102static SmallVector<MethodParameter, 8>
103getAllParams(const ObjCInterfaceDecl *ID) {
104 SmallVector<MethodParameter, 8> Params;
105 // Currently we only generate based on the ivars and properties declared
106 // in the interface. We could consider expanding this to include visible
107 // categories + class extensions in the future (see
108 // all_declared_ivar_begin).
109 llvm::DenseSet<llvm::StringRef> Names;
110 for (const auto *Ivar : ID->ivars()) {
19
Called C++ object pointer is null
111 MethodParameter P(*Ivar);
112 if (Names.insert(P.Name).second)
113 Params.push_back(P);
114 }
115 for (const auto *Prop : ID->properties()) {
116 MethodParameter P(*Prop);
117 if (Names.insert(P.Name).second)
118 Params.push_back(P);
119 }
120 return Params;
121}
122
123static std::string
124initializerForParams(const SmallVector<MethodParameter, 8> &Params,
125 bool GenerateImpl) {
126 std::string Code;
127 llvm::raw_string_ostream Stream(Code);
128
129 if (Params.empty()) {
130 if (GenerateImpl) {
131 Stream <<
132 R"cpp(- (instancetype)init {
133 self = [super init];
134 if (self) {
135
136 }
137 return self;
138})cpp";
139 } else {
140 Stream << "- (instancetype)init;";
141 }
142 } else {
143 const auto &First = Params.front();
144 Stream << llvm::formatv("- (instancetype)initWith{0}:({1}){2}",
145 capitalize(First.Name.trim().str()), First.Type,
146 First.Name);
147 for (const auto &It : llvm::drop_begin(Params))
148 Stream << llvm::formatv(" {0}:({1}){0}", It.Name, It.Type);
149
150 if (GenerateImpl) {
151 Stream <<
152 R"cpp( {
153 self = [super init];
154 if (self) {)cpp";
155 for (const auto &Param : Params)
156 Stream << llvm::formatv("\n {0} = {1};", Param.Assignee, Param.Name);
157 Stream <<
158 R"cpp(
159 }
160 return self;
161})cpp";
162 } else {
163 Stream << ";";
164 }
165 }
166 Stream << "\n\n";
167 return Code;
168}
169
170/// Generate an initializer for an Objective-C class based on selected
171/// properties and instance variables.
172class ObjCMemberwiseInitializer : public Tweak {
173public:
174 const char *id() const final;
175 llvm::StringLiteral kind() const override {
176 return CodeAction::REFACTOR_KIND;
177 }
178
179 bool prepare(const Selection &Inputs) override;
180 Expected<Tweak::Effect> apply(const Selection &Inputs) override;
181 std::string title() const override;
182
183private:
184 SmallVector<MethodParameter, 8>
185 paramsForSelection(const SelectionTree::Node *N);
186
187 const ObjCInterfaceDecl *Interface = nullptr;
188
189 // Will be nullptr if running on an interface.
190 const ObjCImplementationDecl *Impl = nullptr;
191};
192
193REGISTER_TWEAK(ObjCMemberwiseInitializer)::llvm::Registry<::clang::clangd::Tweak>::Add<ObjCMemberwiseInitializer
> TweakRegistrationForObjCMemberwiseInitializer("ObjCMemberwiseInitializer"
, ""); const char *ObjCMemberwiseInitializer::id() const { return
"ObjCMemberwiseInitializer"; }
194
195bool ObjCMemberwiseInitializer::prepare(const Selection &Inputs) {
196 const SelectionTree::Node *N = Inputs.ASTSelection.commonAncestor();
197 if (!N)
198 return false;
199 const Decl *D = N->ASTNode.get<Decl>();
200 if (!D)
201 return false;
202 const auto &LangOpts = Inputs.AST->getLangOpts();
203 // Require ObjC w/ arc enabled since we don't emit retains.
204 if (!LangOpts.ObjC || !LangOpts.ObjCAutoRefCount)
205 return false;
206
207 // We support the following selected decls:
208 // - ObjCInterfaceDecl/ObjCImplementationDecl only - generate for all
209 // properties and ivars
210 //
211 // - Specific ObjCPropertyDecl(s)/ObjCIvarDecl(s) - generate only for those
212 // selected. Note that if only one is selected, the common ancestor will be
213 // the ObjCPropertyDecl/ObjCIvarDecl itself instead of the container.
214 if (const auto *ID = dyn_cast<ObjCInterfaceDecl>(D)) {
215 // Ignore forward declarations (@class Name;).
216 if (!ID->isThisDeclarationADefinition())
217 return false;
218 Interface = ID;
219 } else if (const auto *ID = dyn_cast<ObjCImplementationDecl>(D)) {
220 Interface = ID->getClassInterface();
221 Impl = ID;
222 } else if (isa<ObjCPropertyDecl, ObjCIvarDecl>(D)) {
223 const auto *DC = D->getDeclContext();
224 if (const auto *ID = dyn_cast<ObjCInterfaceDecl>(DC)) {
225 Interface = ID;
226 } else if (const auto *ID = dyn_cast<ObjCImplementationDecl>(DC)) {
227 Interface = ID->getClassInterface();
228 Impl = ID;
229 }
230 }
231 return Interface != nullptr;
232}
233
234SmallVector<MethodParameter, 8>
235ObjCMemberwiseInitializer::paramsForSelection(const SelectionTree::Node *N) {
236 SmallVector<MethodParameter, 8> Params;
237 // Base case: selected a single ivar or property.
238 if (const auto *D
11.1
'D' is null
11.1
'D' is null
= N->ASTNode.get<Decl>()) {
4
Calling 'DynTypedNode::get'
11
Returning from 'DynTypedNode::get'
239 if (auto Param = MethodParameter::parameterFor(*D)) {
240 Params.push_back(*Param);
241 return Params;
242 }
243 }
244 const ObjCContainerDecl *Container =
12
Taking false branch
245 Impl ? static_cast<const ObjCContainerDecl *>(Impl)
13
Assuming field 'Impl' is null
14
'?' condition is false
246 : static_cast<const ObjCContainerDecl *>(Interface);
247 if (Container == N->ASTNode.get<ObjCContainerDecl>() && N->Children.empty())
15
Assuming pointer value is null
16
Taking true branch
248 return getAllParams(Interface);
17
Passing null pointer value via 1st parameter 'ID'
18
Calling 'getAllParams'
249
250 llvm::DenseSet<llvm::StringRef> Names;
251 // Check for selecting multiple ivars/properties.
252 for (const auto *CNode : N->Children) {
253 const Decl *D = CNode->ASTNode.get<Decl>();
254 if (!D)
255 continue;
256 if (auto P = MethodParameter::parameterFor(*D))
257 if (Names.insert(P->Name).second)
258 Params.push_back(*P);
259 }
260 return Params;
261}
262
263Expected<Tweak::Effect>
264ObjCMemberwiseInitializer::apply(const Selection &Inputs) {
265 const auto &SM = Inputs.AST->getASTContext().getSourceManager();
266 const SelectionTree::Node *N = Inputs.ASTSelection.commonAncestor();
267 if (!N)
1
Assuming 'N' is non-null
2
Taking false branch
268 return error("Invalid selection");
269
270 SmallVector<MethodParameter, 8> Params = paramsForSelection(N);
3
Calling 'ObjCMemberwiseInitializer::paramsForSelection'
271
272 // Insert before the first non-init instance method.
273 std::vector<Anchor> Anchors = {
274 {[](const Decl *D) {
275 if (const auto *MD = llvm::dyn_cast<ObjCMethodDecl>(D)) {
276 return MD->getMethodFamily() != OMF_init && MD->isInstanceMethod();
277 }
278 return false;
279 },
280 Anchor::Above}};
281 Effect E;
282
283 auto InterfaceReplacement =
284 insertDecl(initializerForParams(Params, /*GenerateImpl=*/false),
285 *Interface, Anchors);
286 if (!InterfaceReplacement)
287 return InterfaceReplacement.takeError();
288 auto FE = Effect::fileEdit(SM, SM.getFileID(Interface->getLocation()),
289 tooling::Replacements(*InterfaceReplacement));
290 if (!FE)
291 return FE.takeError();
292 E.ApplyEdits.insert(std::move(*FE));
293
294 if (Impl) {
295 // If we see the class implementation, add the initializer there too.
296 // FIXME: merging the edits is awkward, do this elsewhere.
297 auto ImplReplacement = insertDecl(
298 initializerForParams(Params, /*GenerateImpl=*/true), *Impl, Anchors);
299 if (!ImplReplacement)
300 return ImplReplacement.takeError();
301
302 if (SM.isWrittenInSameFile(Interface->getLocation(), Impl->getLocation())) {
303 // Merge with previous edit if they are in the same file.
304 if (auto Err =
305 E.ApplyEdits.begin()->second.Replacements.add(*ImplReplacement))
306 return std::move(Err);
307 } else {
308 // Generate a new edit if the interface and implementation are in
309 // different files.
310 auto FE = Effect::fileEdit(SM, SM.getFileID(Impl->getLocation()),
311 tooling::Replacements(*ImplReplacement));
312 if (!FE)
313 return FE.takeError();
314 E.ApplyEdits.insert(std::move(*FE));
315 }
316 }
317 return E;
318}
319
320std::string ObjCMemberwiseInitializer::title() const {
321 if (Impl)
322 return "Generate memberwise initializer";
323 return "Declare memberwise initializer";
324}
325
326} // namespace
327} // namespace clangd
328} // namespace clang

/build/source/clang/include/clang/AST/ASTTypeTraits.h

1//===--- ASTTypeTraits.h ----------------------------------------*- C++ -*-===//
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// Provides a dynamic type identifier and a dynamically typed node container
10// that can be used to store an AST base node at runtime in the same storage in
11// a type safe way.
12//
13//===----------------------------------------------------------------------===//
14
15#ifndef LLVM_CLANG_AST_ASTTYPETRAITS_H
16#define LLVM_CLANG_AST_ASTTYPETRAITS_H
17
18#include "clang/AST/ASTFwd.h"
19#include "clang/AST/DeclCXX.h"
20#include "clang/AST/LambdaCapture.h"
21#include "clang/AST/NestedNameSpecifier.h"
22#include "clang/AST/TemplateBase.h"
23#include "clang/AST/TypeLoc.h"
24#include "clang/Basic/LLVM.h"
25#include "llvm/ADT/DenseMapInfo.h"
26#include "llvm/Support/AlignOf.h"
27
28namespace llvm {
29class raw_ostream;
30} // namespace llvm
31
32namespace clang {
33
34struct PrintingPolicy;
35
36/// Defines how we descend a level in the AST when we pass
37/// through expressions.
38enum TraversalKind {
39 /// Will traverse all child nodes.
40 TK_AsIs,
41
42 /// Ignore AST nodes not written in the source
43 TK_IgnoreUnlessSpelledInSource
44};
45
46/// Kind identifier.
47///
48/// It can be constructed from any node kind and allows for runtime type
49/// hierarchy checks.
50/// Use getFromNodeKind<T>() to construct them.
51class ASTNodeKind {
52public:
53 /// Empty identifier. It matches nothing.
54 constexpr ASTNodeKind() : KindId(NKI_None) {}
55
56 /// Construct an identifier for T.
57 template <class T> static constexpr ASTNodeKind getFromNodeKind() {
58 return ASTNodeKind(KindToKindId<T>::Id);
59 }
60
61 /// \{
62 /// Construct an identifier for the dynamic type of the node
63 static ASTNodeKind getFromNode(const Decl &D);
64 static ASTNodeKind getFromNode(const Stmt &S);
65 static ASTNodeKind getFromNode(const Type &T);
66 static ASTNodeKind getFromNode(const TypeLoc &T);
67 static ASTNodeKind getFromNode(const LambdaCapture &L);
68 static ASTNodeKind getFromNode(const OMPClause &C);
69 static ASTNodeKind getFromNode(const Attr &A);
70 /// \}
71
72 /// Returns \c true if \c this and \c Other represent the same kind.
73 constexpr bool isSame(ASTNodeKind Other) const {
74 return KindId != NKI_None && KindId == Other.KindId;
75 }
76
77 /// Returns \c true only for the default \c ASTNodeKind()
78 constexpr bool isNone() const { return KindId == NKI_None; }
79
80 /// Returns \c true if \c this is a base kind of (or same as) \c Other.
81 bool isBaseOf(ASTNodeKind Other) const;
82
83 /// Returns \c true if \c this is a base kind of (or same as) \c Other.
84 /// \param Distance If non-null, used to return the distance between \c this
85 /// and \c Other in the class hierarchy.
86 bool isBaseOf(ASTNodeKind Other, unsigned *Distance) const;
87
88 /// String representation of the kind.
89 StringRef asStringRef() const;
90
91 /// Strict weak ordering for ASTNodeKind.
92 constexpr bool operator<(const ASTNodeKind &Other) const {
93 return KindId < Other.KindId;
94 }
95
96 /// Return the most derived type between \p Kind1 and \p Kind2.
97 ///
98 /// Return ASTNodeKind() if they are not related.
99 static ASTNodeKind getMostDerivedType(ASTNodeKind Kind1, ASTNodeKind Kind2);
100
101 /// Return the most derived common ancestor between Kind1 and Kind2.
102 ///
103 /// Return ASTNodeKind() if they are not related.
104 static ASTNodeKind getMostDerivedCommonAncestor(ASTNodeKind Kind1,
105 ASTNodeKind Kind2);
106
107 ASTNodeKind getCladeKind() const;
108
109 /// Hooks for using ASTNodeKind as a key in a DenseMap.
110 struct DenseMapInfo {
111 // ASTNodeKind() is a good empty key because it is represented as a 0.
112 static inline ASTNodeKind getEmptyKey() { return ASTNodeKind(); }
113 // NKI_NumberOfKinds is not a valid value, so it is good for a
114 // tombstone key.
115 static inline ASTNodeKind getTombstoneKey() {
116 return ASTNodeKind(NKI_NumberOfKinds);
117 }
118 static unsigned getHashValue(const ASTNodeKind &Val) { return Val.KindId; }
119 static bool isEqual(const ASTNodeKind &LHS, const ASTNodeKind &RHS) {
120 return LHS.KindId == RHS.KindId;
121 }
122 };
123
124 /// Check if the given ASTNodeKind identifies a type that offers pointer
125 /// identity. This is useful for the fast path in DynTypedNode.
126 constexpr bool hasPointerIdentity() const {
127 return KindId > NKI_LastKindWithoutPointerIdentity;
128 }
129
130private:
131 /// Kind ids.
132 ///
133 /// Includes all possible base and derived kinds.
134 enum NodeKindId {
135 NKI_None,
136 NKI_TemplateArgument,
137 NKI_TemplateArgumentLoc,
138 NKI_LambdaCapture,
139 NKI_TemplateName,
140 NKI_NestedNameSpecifierLoc,
141 NKI_QualType,
142#define TYPELOC(CLASS, PARENT) NKI_##CLASS##TypeLoc,
143#include "clang/AST/TypeLocNodes.def"
144 NKI_TypeLoc,
145 NKI_LastKindWithoutPointerIdentity = NKI_TypeLoc,
146 NKI_CXXBaseSpecifier,
147 NKI_CXXCtorInitializer,
148 NKI_NestedNameSpecifier,
149 NKI_Decl,
150#define DECL(DERIVED, BASE) NKI_##DERIVED##Decl,
151#include "clang/AST/DeclNodes.inc"
152 NKI_Stmt,
153#define STMT(DERIVED, BASE) NKI_##DERIVED,
154#include "clang/AST/StmtNodes.inc"
155 NKI_Type,
156#define TYPE(DERIVED, BASE) NKI_##DERIVED##Type,
157#include "clang/AST/TypeNodes.inc"
158 NKI_OMPClause,
159#define GEN_CLANG_CLAUSE_CLASS
160#define CLAUSE_CLASS(Enum, Str, Class) NKI_##Class,
161#include "llvm/Frontend/OpenMP/OMP.inc"
162 NKI_Attr,
163#define ATTR(A) NKI_##A##Attr,
164#include "clang/Basic/AttrList.inc"
165 NKI_ObjCProtocolLoc,
166 NKI_NumberOfKinds
167 };
168
169 /// Use getFromNodeKind<T>() to construct the kind.
170 constexpr ASTNodeKind(NodeKindId KindId) : KindId(KindId) {}
171
172 /// Returns \c true if \c Base is a base kind of (or same as) \c
173 /// Derived.
174 static bool isBaseOf(NodeKindId Base, NodeKindId Derived);
175
176 /// Returns \c true if \c Base is a base kind of (or same as) \c
177 /// Derived.
178 /// \param Distance If non-null, used to return the distance between \c Base
179 /// and \c Derived in the class hierarchy.
180 static bool isBaseOf(NodeKindId Base, NodeKindId Derived, unsigned *Distance);
181
182 /// Helper meta-function to convert a kind T to its enum value.
183 ///
184 /// This struct is specialized below for all known kinds.
185 template <class T> struct KindToKindId {
186 static const NodeKindId Id = NKI_None;
187 };
188 template <class T>
189 struct KindToKindId<const T> : KindToKindId<T> {};
190
191 /// Per kind info.
192 struct KindInfo {
193 /// The id of the parent kind, or None if it has no parent.
194 NodeKindId ParentId;
195 /// Name of the kind.
196 const char *Name;
197 };
198 static const KindInfo AllKindInfo[NKI_NumberOfKinds];
199
200 NodeKindId KindId;
201};
202
203#define KIND_TO_KIND_ID(Class) \
204 template <> struct ASTNodeKind::KindToKindId<Class> { \
205 static const NodeKindId Id = NKI_##Class; \
206 };
207KIND_TO_KIND_ID(CXXCtorInitializer)
208KIND_TO_KIND_ID(TemplateArgument)
209KIND_TO_KIND_ID(TemplateArgumentLoc)
210KIND_TO_KIND_ID(LambdaCapture)
211KIND_TO_KIND_ID(TemplateName)
212KIND_TO_KIND_ID(NestedNameSpecifier)
213KIND_TO_KIND_ID(NestedNameSpecifierLoc)
214KIND_TO_KIND_ID(QualType)
215#define TYPELOC(CLASS, PARENT) KIND_TO_KIND_ID(CLASS##TypeLoc)
216#include "clang/AST/TypeLocNodes.def"
217KIND_TO_KIND_ID(TypeLoc)
218KIND_TO_KIND_ID(Decl)
219KIND_TO_KIND_ID(Stmt)
220KIND_TO_KIND_ID(Type)
221KIND_TO_KIND_ID(OMPClause)
222KIND_TO_KIND_ID(Attr)
223KIND_TO_KIND_ID(ObjCProtocolLoc)
224KIND_TO_KIND_ID(CXXBaseSpecifier)
225#define DECL(DERIVED, BASE) KIND_TO_KIND_ID(DERIVED##Decl)
226#include "clang/AST/DeclNodes.inc"
227#define STMT(DERIVED, BASE) KIND_TO_KIND_ID(DERIVED)
228#include "clang/AST/StmtNodes.inc"
229#define TYPE(DERIVED, BASE) KIND_TO_KIND_ID(DERIVED##Type)
230#include "clang/AST/TypeNodes.inc"
231#define GEN_CLANG_CLAUSE_CLASS
232#define CLAUSE_CLASS(Enum, Str, Class) KIND_TO_KIND_ID(Class)
233#include "llvm/Frontend/OpenMP/OMP.inc"
234#define ATTR(A) KIND_TO_KIND_ID(A##Attr)
235#include "clang/Basic/AttrList.inc"
236#undef KIND_TO_KIND_ID
237
238inline raw_ostream &operator<<(raw_ostream &OS, ASTNodeKind K) {
239 OS << K.asStringRef();
240 return OS;
241}
242
243/// A dynamically typed AST node container.
244///
245/// Stores an AST node in a type safe way. This allows writing code that
246/// works with different kinds of AST nodes, despite the fact that they don't
247/// have a common base class.
248///
249/// Use \c create(Node) to create a \c DynTypedNode from an AST node,
250/// and \c get<T>() to retrieve the node as type T if the types match.
251///
252/// See \c ASTNodeKind for which node base types are currently supported;
253/// You can create DynTypedNodes for all nodes in the inheritance hierarchy of
254/// the supported base types.
255class DynTypedNode {
256public:
257 /// Creates a \c DynTypedNode from \c Node.
258 template <typename T>
259 static DynTypedNode create(const T &Node) {
260 return BaseConverter<T>::create(Node);
261 }
262
263 /// Retrieve the stored node as type \c T.
264 ///
265 /// Returns NULL if the stored node does not have a type that is
266 /// convertible to \c T.
267 ///
268 /// For types that have identity via their pointer in the AST
269 /// (like \c Stmt, \c Decl, \c Type and \c NestedNameSpecifier) the returned
270 /// pointer points to the referenced AST node.
271 /// For other types (like \c QualType) the value is stored directly
272 /// in the \c DynTypedNode, and the returned pointer points at
273 /// the storage inside DynTypedNode. For those nodes, do not
274 /// use the pointer outside the scope of the DynTypedNode.
275 template <typename T> const T *get() const {
276 return BaseConverter<T>::get(NodeKind, &Storage);
5
Calling 'DynCastPtrConverter::get'
9
Returning from 'DynCastPtrConverter::get'
10
Returning null pointer, which participates in a condition later
277 }
278
279 /// Retrieve the stored node as type \c T.
280 ///
281 /// Similar to \c get(), but asserts that the type is what we are expecting.
282 template <typename T>
283 const T &getUnchecked() const {
284 return BaseConverter<T>::getUnchecked(NodeKind, &Storage);
285 }
286
287 ASTNodeKind getNodeKind() const { return NodeKind; }
288
289 /// Returns a pointer that identifies the stored AST node.
290 ///
291 /// Note that this is not supported by all AST nodes. For AST nodes
292 /// that don't have a pointer-defined identity inside the AST, this
293 /// method returns NULL.
294 const void *getMemoizationData() const {
295 return NodeKind.hasPointerIdentity()
296 ? *reinterpret_cast<void *const *>(&Storage)
297 : nullptr;
298 }
299
300 /// Prints the node to the given output stream.
301 void print(llvm::raw_ostream &OS, const PrintingPolicy &PP) const;
302
303 /// Dumps the node to the given output stream.
304 void dump(llvm::raw_ostream &OS, const ASTContext &Context) const;
305
306 /// For nodes which represent textual entities in the source code,
307 /// return their SourceRange. For all other nodes, return SourceRange().
308 SourceRange getSourceRange() const;
309
310 /// @{
311 /// Imposes an order on \c DynTypedNode.
312 ///
313 /// Supports comparison of nodes that support memoization.
314 /// FIXME: Implement comparison for other node types (currently
315 /// only Stmt, Decl, Type and NestedNameSpecifier return memoization data).
316 bool operator<(const DynTypedNode &Other) const {
317 if (!NodeKind.isSame(Other.NodeKind))
318 return NodeKind < Other.NodeKind;
319
320 if (ASTNodeKind::getFromNodeKind<QualType>().isSame(NodeKind))
321 return getUnchecked<QualType>().getAsOpaquePtr() <
322 Other.getUnchecked<QualType>().getAsOpaquePtr();
323
324 if (ASTNodeKind::getFromNodeKind<TypeLoc>().isBaseOf(NodeKind)) {
325 auto TLA = getUnchecked<TypeLoc>();
326 auto TLB = Other.getUnchecked<TypeLoc>();
327 return std::make_pair(TLA.getType().getAsOpaquePtr(),
328 TLA.getOpaqueData()) <
329 std::make_pair(TLB.getType().getAsOpaquePtr(),
330 TLB.getOpaqueData());
331 }
332
333 if (ASTNodeKind::getFromNodeKind<NestedNameSpecifierLoc>().isSame(
334 NodeKind)) {
335 auto NNSLA = getUnchecked<NestedNameSpecifierLoc>();
336 auto NNSLB = Other.getUnchecked<NestedNameSpecifierLoc>();
337 return std::make_pair(NNSLA.getNestedNameSpecifier(),
338 NNSLA.getOpaqueData()) <
339 std::make_pair(NNSLB.getNestedNameSpecifier(),
340 NNSLB.getOpaqueData());
341 }
342
343 assert(getMemoizationData() && Other.getMemoizationData())(static_cast <bool> (getMemoizationData() && Other
.getMemoizationData()) ? void (0) : __assert_fail ("getMemoizationData() && Other.getMemoizationData()"
, "clang/include/clang/AST/ASTTypeTraits.h", 343, __extension__
__PRETTY_FUNCTION__))
;
344 return getMemoizationData() < Other.getMemoizationData();
345 }
346 bool operator==(const DynTypedNode &Other) const {
347 // DynTypedNode::create() stores the exact kind of the node in NodeKind.
348 // If they contain the same node, their NodeKind must be the same.
349 if (!NodeKind.isSame(Other.NodeKind))
350 return false;
351
352 // FIXME: Implement for other types.
353 if (ASTNodeKind::getFromNodeKind<QualType>().isSame(NodeKind))
354 return getUnchecked<QualType>() == Other.getUnchecked<QualType>();
355
356 if (ASTNodeKind::getFromNodeKind<TypeLoc>().isBaseOf(NodeKind))
357 return getUnchecked<TypeLoc>() == Other.getUnchecked<TypeLoc>();
358
359 if (ASTNodeKind::getFromNodeKind<NestedNameSpecifierLoc>().isSame(NodeKind))
360 return getUnchecked<NestedNameSpecifierLoc>() ==
361 Other.getUnchecked<NestedNameSpecifierLoc>();
362
363 assert(getMemoizationData() && Other.getMemoizationData())(static_cast <bool> (getMemoizationData() && Other
.getMemoizationData()) ? void (0) : __assert_fail ("getMemoizationData() && Other.getMemoizationData()"
, "clang/include/clang/AST/ASTTypeTraits.h", 363, __extension__
__PRETTY_FUNCTION__))
;
364 return getMemoizationData() == Other.getMemoizationData();
365 }
366 bool operator!=(const DynTypedNode &Other) const {
367 return !operator==(Other);
368 }
369 /// @}
370
371 /// Hooks for using DynTypedNode as a key in a DenseMap.
372 struct DenseMapInfo {
373 static inline DynTypedNode getEmptyKey() {
374 DynTypedNode Node;
375 Node.NodeKind = ASTNodeKind::DenseMapInfo::getEmptyKey();
376 return Node;
377 }
378 static inline DynTypedNode getTombstoneKey() {
379 DynTypedNode Node;
380 Node.NodeKind = ASTNodeKind::DenseMapInfo::getTombstoneKey();
381 return Node;
382 }
383 static unsigned getHashValue(const DynTypedNode &Val) {
384 // FIXME: Add hashing support for the remaining types.
385 if (ASTNodeKind::getFromNodeKind<TypeLoc>().isBaseOf(Val.NodeKind)) {
386 auto TL = Val.getUnchecked<TypeLoc>();
387 return llvm::hash_combine(TL.getType().getAsOpaquePtr(),
388 TL.getOpaqueData());
389 }
390
391 if (ASTNodeKind::getFromNodeKind<NestedNameSpecifierLoc>().isSame(
392 Val.NodeKind)) {
393 auto NNSL = Val.getUnchecked<NestedNameSpecifierLoc>();
394 return llvm::hash_combine(NNSL.getNestedNameSpecifier(),
395 NNSL.getOpaqueData());
396 }
397
398 assert(Val.getMemoizationData())(static_cast <bool> (Val.getMemoizationData()) ? void (
0) : __assert_fail ("Val.getMemoizationData()", "clang/include/clang/AST/ASTTypeTraits.h"
, 398, __extension__ __PRETTY_FUNCTION__))
;
399 return llvm::hash_value(Val.getMemoizationData());
400 }
401 static bool isEqual(const DynTypedNode &LHS, const DynTypedNode &RHS) {
402 auto Empty = ASTNodeKind::DenseMapInfo::getEmptyKey();
403 auto TombStone = ASTNodeKind::DenseMapInfo::getTombstoneKey();
404 return (ASTNodeKind::DenseMapInfo::isEqual(LHS.NodeKind, Empty) &&
405 ASTNodeKind::DenseMapInfo::isEqual(RHS.NodeKind, Empty)) ||
406 (ASTNodeKind::DenseMapInfo::isEqual(LHS.NodeKind, TombStone) &&
407 ASTNodeKind::DenseMapInfo::isEqual(RHS.NodeKind, TombStone)) ||
408 LHS == RHS;
409 }
410 };
411
412private:
413 /// Takes care of converting from and to \c T.
414 template <typename T, typename EnablerT = void> struct BaseConverter;
415
416 /// Converter that uses dyn_cast<T> from a stored BaseT*.
417 template <typename T, typename BaseT> struct DynCastPtrConverter {
418 static const T *get(ASTNodeKind NodeKind, const void *Storage) {
419 if (ASTNodeKind::getFromNodeKind<T>().isBaseOf(NodeKind))
6
Assuming the condition is false
7
Taking false branch
420 return &getUnchecked(NodeKind, Storage);
421 return nullptr;
8
Returning null pointer, which participates in a condition later
422 }
423 static const T &getUnchecked(ASTNodeKind NodeKind, const void *Storage) {
424 assert(ASTNodeKind::getFromNodeKind<T>().isBaseOf(NodeKind))(static_cast <bool> (ASTNodeKind::getFromNodeKind<T>
().isBaseOf(NodeKind)) ? void (0) : __assert_fail ("ASTNodeKind::getFromNodeKind<T>().isBaseOf(NodeKind)"
, "clang/include/clang/AST/ASTTypeTraits.h", 424, __extension__
__PRETTY_FUNCTION__))
;
425 return *cast<T>(static_cast<const BaseT *>(
426 *reinterpret_cast<const void *const *>(Storage)));
427 }
428 static DynTypedNode create(const BaseT &Node) {
429 DynTypedNode Result;
430 Result.NodeKind = ASTNodeKind::getFromNode(Node);
431 new (&Result.Storage) const void *(&Node);
432 return Result;
433 }
434 };
435
436 /// Converter that stores T* (by pointer).
437 template <typename T> struct PtrConverter {
438 static const T *get(ASTNodeKind NodeKind, const void *Storage) {
439 if (ASTNodeKind::getFromNodeKind<T>().isSame(NodeKind))
440 return &getUnchecked(NodeKind, Storage);
441 return nullptr;
442 }
443 static const T &getUnchecked(ASTNodeKind NodeKind, const void *Storage) {
444 assert(ASTNodeKind::getFromNodeKind<T>().isSame(NodeKind))(static_cast <bool> (ASTNodeKind::getFromNodeKind<T>
().isSame(NodeKind)) ? void (0) : __assert_fail ("ASTNodeKind::getFromNodeKind<T>().isSame(NodeKind)"
, "clang/include/clang/AST/ASTTypeTraits.h", 444, __extension__
__PRETTY_FUNCTION__))
;
445 return *static_cast<const T *>(
446 *reinterpret_cast<const void *const *>(Storage));
447 }
448 static DynTypedNode create(const T &Node) {
449 DynTypedNode Result;
450 Result.NodeKind = ASTNodeKind::getFromNodeKind<T>();
451 new (&Result.Storage) const void *(&Node);
452 return Result;
453 }
454 };
455
456 /// Converter that stores T (by value).
457 template <typename T> struct ValueConverter {
458 static const T *get(ASTNodeKind NodeKind, const void *Storage) {
459 if (ASTNodeKind::getFromNodeKind<T>().isSame(NodeKind))
460 return reinterpret_cast<const T *>(Storage);
461 return nullptr;
462 }
463 static const T &getUnchecked(ASTNodeKind NodeKind, const void *Storage) {
464 assert(ASTNodeKind::getFromNodeKind<T>().isSame(NodeKind))(static_cast <bool> (ASTNodeKind::getFromNodeKind<T>
().isSame(NodeKind)) ? void (0) : __assert_fail ("ASTNodeKind::getFromNodeKind<T>().isSame(NodeKind)"
, "clang/include/clang/AST/ASTTypeTraits.h", 464, __extension__
__PRETTY_FUNCTION__))
;
465 return *reinterpret_cast<const T *>(Storage);
466 }
467 static DynTypedNode create(const T &Node) {
468 DynTypedNode Result;
469 Result.NodeKind = ASTNodeKind::getFromNodeKind<T>();
470 new (&Result.Storage) T(Node);
471 return Result;
472 }
473 };
474
475 /// Converter that stores nodes by value. It must be possible to dynamically
476 /// cast the stored node within a type hierarchy without breaking (especially
477 /// through slicing).
478 template <typename T, typename BaseT,
479 typename = std::enable_if_t<(sizeof(T) == sizeof(BaseT))>>
480 struct DynCastValueConverter {
481 static const T *get(ASTNodeKind NodeKind, const void *Storage) {
482 if (ASTNodeKind::getFromNodeKind<T>().isBaseOf(NodeKind))
483 return &getUnchecked(NodeKind, Storage);
484 return nullptr;
485 }
486 static const T &getUnchecked(ASTNodeKind NodeKind, const void *Storage) {
487 assert(ASTNodeKind::getFromNodeKind<T>().isBaseOf(NodeKind))(static_cast <bool> (ASTNodeKind::getFromNodeKind<T>
().isBaseOf(NodeKind)) ? void (0) : __assert_fail ("ASTNodeKind::getFromNodeKind<T>().isBaseOf(NodeKind)"
, "clang/include/clang/AST/ASTTypeTraits.h", 487, __extension__
__PRETTY_FUNCTION__))
;
488 return *static_cast<const T *>(reinterpret_cast<const BaseT *>(Storage));
489 }
490 static DynTypedNode create(const T &Node) {
491 DynTypedNode Result;
492 Result.NodeKind = ASTNodeKind::getFromNode(Node);
493 new (&Result.Storage) T(Node);
494 return Result;
495 }
496 };
497
498 ASTNodeKind NodeKind;
499
500 /// Stores the data of the node.
501 ///
502 /// Note that we can store \c Decls, \c Stmts, \c Types,
503 /// \c NestedNameSpecifiers and \c CXXCtorInitializer by pointer as they are
504 /// guaranteed to be unique pointers pointing to dedicated storage in the AST.
505 /// \c QualTypes, \c NestedNameSpecifierLocs, \c TypeLocs,
506 /// \c TemplateArguments and \c TemplateArgumentLocs on the other hand do not
507 /// have storage or unique pointers and thus need to be stored by value.
508 llvm::AlignedCharArrayUnion<const void *, TemplateArgument,
509 TemplateArgumentLoc, NestedNameSpecifierLoc,
510 QualType, TypeLoc, ObjCProtocolLoc>
511 Storage;
512};
513
514template <typename T>
515struct DynTypedNode::BaseConverter<
516 T, std::enable_if_t<std::is_base_of<Decl, T>::value>>
517 : public DynCastPtrConverter<T, Decl> {};
518
519template <typename T>
520struct DynTypedNode::BaseConverter<
521 T, std::enable_if_t<std::is_base_of<Stmt, T>::value>>
522 : public DynCastPtrConverter<T, Stmt> {};
523
524template <typename T>
525struct DynTypedNode::BaseConverter<
526 T, std::enable_if_t<std::is_base_of<Type, T>::value>>
527 : public DynCastPtrConverter<T, Type> {};
528
529template <typename T>
530struct DynTypedNode::BaseConverter<
531 T, std::enable_if_t<std::is_base_of<OMPClause, T>::value>>
532 : public DynCastPtrConverter<T, OMPClause> {};
533
534template <typename T>
535struct DynTypedNode::BaseConverter<
536 T, std::enable_if_t<std::is_base_of<Attr, T>::value>>
537 : public DynCastPtrConverter<T, Attr> {};
538
539template <>
540struct DynTypedNode::BaseConverter<
541 NestedNameSpecifier, void> : public PtrConverter<NestedNameSpecifier> {};
542
543template <>
544struct DynTypedNode::BaseConverter<
545 CXXCtorInitializer, void> : public PtrConverter<CXXCtorInitializer> {};
546
547template <>
548struct DynTypedNode::BaseConverter<
549 TemplateArgument, void> : public ValueConverter<TemplateArgument> {};
550
551template <>
552struct DynTypedNode::BaseConverter<TemplateArgumentLoc, void>
553 : public ValueConverter<TemplateArgumentLoc> {};
554
555template <>
556struct DynTypedNode::BaseConverter<LambdaCapture, void>
557 : public ValueConverter<LambdaCapture> {};
558
559template <>
560struct DynTypedNode::BaseConverter<
561 TemplateName, void> : public ValueConverter<TemplateName> {};
562
563template <>
564struct DynTypedNode::BaseConverter<
565 NestedNameSpecifierLoc,
566 void> : public ValueConverter<NestedNameSpecifierLoc> {};
567
568template <>
569struct DynTypedNode::BaseConverter<QualType,
570 void> : public ValueConverter<QualType> {};
571
572template <typename T>
573struct DynTypedNode::BaseConverter<
574 T, std::enable_if_t<std::is_base_of<TypeLoc, T>::value>>
575 : public DynCastValueConverter<T, TypeLoc> {};
576
577template <>
578struct DynTypedNode::BaseConverter<CXXBaseSpecifier, void>
579 : public PtrConverter<CXXBaseSpecifier> {};
580
581template <>
582struct DynTypedNode::BaseConverter<ObjCProtocolLoc, void>
583 : public ValueConverter<ObjCProtocolLoc> {};
584
585// The only operation we allow on unsupported types is \c get.
586// This allows to conveniently use \c DynTypedNode when having an arbitrary
587// AST node that is not supported, but prevents misuse - a user cannot create
588// a DynTypedNode from arbitrary types.
589template <typename T, typename EnablerT> struct DynTypedNode::BaseConverter {
590 static const T *get(ASTNodeKind NodeKind, const char Storage[]) {
591 return NULL__null;
592 }
593};
594
595} // end namespace clang
596
597namespace llvm {
598
599template <>
600struct DenseMapInfo<clang::ASTNodeKind> : clang::ASTNodeKind::DenseMapInfo {};
601
602template <>
603struct DenseMapInfo<clang::DynTypedNode> : clang::DynTypedNode::DenseMapInfo {};
604
605} // end namespace llvm
606
607#endif