1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "CheckTraceVisitor.h"
11 using namespace clang
;
13 CheckTraceVisitor::CheckTraceVisitor(CXXMethodDecl
* trace
,
19 delegates_to_traceimpl_(false) {
22 bool CheckTraceVisitor::delegates_to_traceimpl() const {
23 return delegates_to_traceimpl_
;
26 bool CheckTraceVisitor::VisitMemberExpr(MemberExpr
* member
) {
27 // In weak callbacks, consider any occurrence as a correct usage.
28 // TODO: We really want to require that isAlive is checked on manually
29 // processed weak fields.
30 if (IsWeakCallback()) {
31 if (FieldDecl
* field
= dyn_cast
<FieldDecl
>(member
->getMemberDecl()))
37 bool CheckTraceVisitor::VisitCallExpr(CallExpr
* call
) {
38 // In weak callbacks we don't check calls (see VisitMemberExpr).
42 Expr
* callee
= call
->getCallee();
44 // Trace calls from a templated derived class result in a
45 // DependentScopeMemberExpr because the concrete trace call depends on the
46 // instantiation of any shared template parameters. In this case the call is
47 // "unresolved" and we resort to comparing the syntactic type names.
48 if (CXXDependentScopeMemberExpr
* expr
=
49 dyn_cast
<CXXDependentScopeMemberExpr
>(callee
)) {
50 CheckCXXDependentScopeMemberExpr(call
, expr
);
54 // A tracing call will have either a |visitor| or a |m_field| argument.
55 // A registerWeakMembers call will have a |this| argument.
56 if (call
->getNumArgs() != 1)
58 Expr
* arg
= call
->getArg(0);
60 if (UnresolvedMemberExpr
* expr
= dyn_cast
<UnresolvedMemberExpr
>(callee
)) {
61 // This could be a trace call of a base class, as explained in the
62 // comments of CheckTraceBaseCall().
63 if (CheckTraceBaseCall(call
))
66 if (expr
->getMemberName().getAsString() == kRegisterWeakMembersName
)
67 MarkAllWeakMembersTraced();
69 QualType base
= expr
->getBaseType();
70 if (!base
->isPointerType())
72 CXXRecordDecl
* decl
= base
->getPointeeType()->getAsCXXRecordDecl();
74 CheckTraceFieldCall(expr
->getMemberName().getAsString(), decl
, arg
);
75 if (Config::IsTraceImplName(expr
->getMemberName().getAsString()))
76 delegates_to_traceimpl_
= true;
80 if (CXXMemberCallExpr
* expr
= dyn_cast
<CXXMemberCallExpr
>(call
)) {
81 if (CheckTraceFieldMemberCall(expr
) || CheckRegisterWeakMembers(expr
))
84 if (Config::IsTraceImplName(expr
->getMethodDecl()->getNameAsString())) {
85 delegates_to_traceimpl_
= true;
90 CheckTraceBaseCall(call
);
94 bool CheckTraceVisitor::IsTraceCallName(const std::string
& name
) {
95 if (trace_
->getName() == kTraceImplName
)
96 return name
== kTraceName
;
97 if (trace_
->getName() == kTraceAfterDispatchImplName
)
98 return name
== kTraceAfterDispatchName
;
99 // Currently, a manually dispatched class cannot have mixin bases (having
100 // one would add a vtable which we explicitly check against). This means
101 // that we can only make calls to a trace method of the same name. Revisit
102 // this if our mixin/vtable assumption changes.
103 return name
== trace_
->getName();
106 CXXRecordDecl
* CheckTraceVisitor::GetDependentTemplatedDecl(
107 CXXDependentScopeMemberExpr
* expr
) {
108 NestedNameSpecifier
* qual
= expr
->getQualifier();
112 const Type
* type
= qual
->getAsType();
116 return RecordInfo::GetDependentTemplatedDecl(*type
);
121 class FindFieldVisitor
: public RecursiveASTVisitor
<FindFieldVisitor
> {
124 MemberExpr
* member() const;
125 FieldDecl
* field() const;
126 bool TraverseMemberExpr(MemberExpr
* member
);
133 FindFieldVisitor::FindFieldVisitor()
138 MemberExpr
* FindFieldVisitor::member() const {
142 FieldDecl
* FindFieldVisitor::field() const {
146 bool FindFieldVisitor::TraverseMemberExpr(MemberExpr
* member
) {
147 if (FieldDecl
* field
= dyn_cast
<FieldDecl
>(member
->getMemberDecl())) {
157 void CheckTraceVisitor::CheckCXXDependentScopeMemberExpr(
159 CXXDependentScopeMemberExpr
* expr
) {
160 std::string fn_name
= expr
->getMember().getAsString();
162 // Check for VisitorDispatcher::trace(field) and
163 // VisitorDispatcher::registerWeakMembers.
164 if (!expr
->isImplicitAccess()) {
165 if (DeclRefExpr
* base_decl
= dyn_cast
<DeclRefExpr
>(expr
->getBase())) {
166 if (Config::IsVisitorDispatcherType(base_decl
->getType())) {
167 if (call
->getNumArgs() == 1 && fn_name
== kTraceName
) {
168 FindFieldVisitor finder
;
169 finder
.TraverseStmt(call
->getArg(0));
171 FoundField(finder
.field());
174 } else if (call
->getNumArgs() == 1 &&
175 fn_name
== kRegisterWeakMembersName
) {
176 MarkAllWeakMembersTraced();
182 CXXRecordDecl
* tmpl
= GetDependentTemplatedDecl(expr
);
186 // Check for Super<T>::trace(visitor)
187 if (call
->getNumArgs() == 1 && IsTraceCallName(fn_name
)) {
188 RecordInfo::Bases::iterator it
= info_
->GetBases().begin();
189 for (; it
!= info_
->GetBases().end(); ++it
) {
190 if (it
->first
->getName() == tmpl
->getName())
191 it
->second
.MarkTraced();
195 // Check for TraceIfNeeded<T>::trace(visitor, &field)
196 if (call
->getNumArgs() == 2 && fn_name
== kTraceName
&&
197 tmpl
->getName() == kTraceIfNeededName
) {
198 FindFieldVisitor finder
;
199 finder
.TraverseStmt(call
->getArg(1));
201 FoundField(finder
.field());
205 bool CheckTraceVisitor::CheckTraceBaseCall(CallExpr
* call
) {
206 // Checks for "Base::trace(visitor)"-like calls.
208 // Checking code for these two variables is shared among MemberExpr* case
209 // and UnresolvedMemberCase* case below.
211 // For example, if we've got "Base::trace(visitor)" as |call|,
212 // callee_record will be "Base", and func_name will be "trace".
213 CXXRecordDecl
* callee_record
= nullptr;
214 std::string func_name
;
216 if (MemberExpr
* callee
= dyn_cast
<MemberExpr
>(call
->getCallee())) {
217 if (!callee
->hasQualifier())
220 FunctionDecl
* trace_decl
=
221 dyn_cast
<FunctionDecl
>(callee
->getMemberDecl());
222 if (!trace_decl
|| !Config::IsTraceMethod(trace_decl
))
225 const Type
* type
= callee
->getQualifier()->getAsType();
229 callee_record
= type
->getAsCXXRecordDecl();
230 func_name
= trace_decl
->getName();
231 } else if (UnresolvedMemberExpr
* callee
=
232 dyn_cast
<UnresolvedMemberExpr
>(call
->getCallee())) {
233 // Callee part may become unresolved if the type of the argument
234 // ("visitor") is a template parameter and the called function is
235 // overloaded (i.e. trace(Visitor*) and
236 // trace(InlinedGlobalMarkingVisitor)).
238 // Here, we try to find a function that looks like trace() from the
239 // candidate overloaded functions, and if we find one, we assume it is
242 CXXMethodDecl
* trace_decl
= nullptr;
243 for (NamedDecl
* named_decl
: callee
->decls()) {
244 if (CXXMethodDecl
* method_decl
= dyn_cast
<CXXMethodDecl
>(named_decl
)) {
245 if (Config::IsTraceMethod(method_decl
)) {
246 trace_decl
= method_decl
;
254 // Check if the passed argument is named "visitor".
255 if (call
->getNumArgs() != 1)
257 DeclRefExpr
* arg
= dyn_cast
<DeclRefExpr
>(call
->getArg(0));
258 if (!arg
|| arg
->getNameInfo().getAsString() != kVisitorVarName
)
261 callee_record
= trace_decl
->getParent();
262 func_name
= callee
->getMemberName().getAsString();
268 if (!IsTraceCallName(func_name
))
271 for (auto& base
: info_
->GetBases()) {
272 // We want to deal with omitted trace() function in an intermediary
273 // class in the class hierarchy, e.g.:
274 // class A : public GarbageCollected<A> { trace() { ... } };
275 // class B : public A { /* No trace(); have nothing to trace. */ };
276 // class C : public B { trace() { B::trace(visitor); } }
277 // where, B::trace() is actually A::trace(), and in some cases we get
278 // A as |callee_record| instead of B. We somehow need to mark B as
279 // traced if we find A::trace() call.
281 // To solve this, here we keep going up the class hierarchy as long as
282 // they are not required to have a trace method. The implementation is
283 // a simple DFS, where |base_records| represents the set of base classes
286 std::vector
<CXXRecordDecl
*> base_records
;
287 base_records
.push_back(base
.first
);
289 while (!base_records
.empty()) {
290 CXXRecordDecl
* base_record
= base_records
.back();
291 base_records
.pop_back();
293 if (base_record
== callee_record
) {
294 // If we find a matching trace method, pretend the user has written
295 // a correct trace() method of the base; in the example above, we
296 // find A::trace() here and mark B as correctly traced.
297 base
.second
.MarkTraced();
301 if (RecordInfo
* base_info
= cache_
->Lookup(base_record
)) {
302 if (!base_info
->RequiresTraceMethod()) {
303 // If this base class is not required to have a trace method, then
304 // the actual trace method may be defined in an ancestor.
305 for (auto& inner_base
: base_info
->GetBases())
306 base_records
.push_back(inner_base
.first
);
315 bool CheckTraceVisitor::CheckTraceFieldMemberCall(CXXMemberCallExpr
* call
) {
316 return CheckTraceFieldCall(call
->getMethodDecl()->getNameAsString(),
317 call
->getRecordDecl(),
321 bool CheckTraceVisitor::CheckTraceFieldCall(
322 const std::string
& name
,
323 CXXRecordDecl
* callee
,
325 if (name
!= kTraceName
|| !Config::IsVisitor(callee
->getName()))
328 FindFieldVisitor finder
;
329 finder
.TraverseStmt(arg
);
331 FoundField(finder
.field());
336 bool CheckTraceVisitor::CheckRegisterWeakMembers(CXXMemberCallExpr
* call
) {
337 CXXMethodDecl
* fn
= call
->getMethodDecl();
338 if (fn
->getName() != kRegisterWeakMembersName
)
341 if (fn
->isTemplateInstantiation()) {
342 const TemplateArgumentList
& args
=
343 *fn
->getTemplateSpecializationInfo()->TemplateArguments
;
344 // The second template argument is the callback method.
345 if (args
.size() > 1 &&
346 args
[1].getKind() == TemplateArgument::Declaration
) {
347 if (FunctionDecl
* callback
=
348 dyn_cast
<FunctionDecl
>(args
[1].getAsDecl())) {
349 if (callback
->hasBody()) {
350 CheckTraceVisitor
nested_visitor(nullptr, info_
, nullptr);
351 nested_visitor
.TraverseStmt(callback
->getBody());
359 bool CheckTraceVisitor::IsWeakCallback() const {
363 void CheckTraceVisitor::MarkTraced(RecordInfo::Fields::iterator it
) {
364 // In a weak callback we can't mark strong fields as traced.
365 if (IsWeakCallback() && !it
->second
.edge()->IsWeakMember())
367 it
->second
.MarkTraced();
370 void CheckTraceVisitor::FoundField(FieldDecl
* field
) {
371 if (Config::IsTemplateInstantiation(info_
->record())) {
372 // Pointer equality on fields does not work for template instantiations.
373 // The trace method refers to fields of the template definition which
374 // are different from the instantiated fields that need to be traced.
375 const std::string
& name
= field
->getNameAsString();
376 for (RecordInfo::Fields::iterator it
= info_
->GetFields().begin();
377 it
!= info_
->GetFields().end();
379 if (it
->first
->getNameAsString() == name
) {
385 RecordInfo::Fields::iterator it
= info_
->GetFields().find(field
);
386 if (it
!= info_
->GetFields().end())
391 void CheckTraceVisitor::MarkAllWeakMembersTraced() {
392 // If we find a call to registerWeakMembers which is unresolved we
393 // unsoundly consider all weak members as traced.
394 // TODO: Find out how to validate weak member tracing for unresolved call.
395 for (auto& field
: info_
->GetFields()) {
396 if (field
.second
.edge()->IsWeakMember())
397 field
.second
.MarkTraced();