Remove linux_chromium_gn_dbg from the chromium CQ.
[chromium-blink-merge.git] / tools / clang / blink_gc_plugin / BlinkGCPlugin.cpp
blob52b8ed16fb9205de92f917e75139ff79b61d1f13
1 // Copyright 2014 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 // This clang plugin checks various invariants of the Blink garbage
6 // collection infrastructure.
7 //
8 // Errors are described at:
9 // http://www.chromium.org/developers/blink-gc-plugin-errors
11 #include <algorithm>
13 #include "BlinkGCPluginOptions.h"
14 #include "CheckFieldsVisitor.h"
15 #include "CheckFinalizerVisitor.h"
16 #include "CheckGCRootsVisitor.h"
17 #include "CheckTraceVisitor.h"
18 #include "CollectVisitor.h"
19 #include "Config.h"
20 #include "JsonWriter.h"
21 #include "RecordInfo.h"
23 #include "clang/AST/AST.h"
24 #include "clang/AST/ASTConsumer.h"
25 #include "clang/AST/RecursiveASTVisitor.h"
26 #include "clang/Frontend/CompilerInstance.h"
27 #include "clang/Frontend/FrontendPluginRegistry.h"
28 #include "clang/Sema/Sema.h"
30 using namespace clang;
31 using std::string;
33 namespace {
35 const char kClassMustLeftMostlyDeriveGC[] =
36 "[blink-gc] Class %0 must derive its GC base in the left-most position.";
38 const char kClassRequiresTraceMethod[] =
39 "[blink-gc] Class %0 requires a trace method.";
41 const char kBaseRequiresTracing[] =
42 "[blink-gc] Base class %0 of derived class %1 requires tracing.";
44 const char kBaseRequiresTracingNote[] =
45 "[blink-gc] Untraced base class %0 declared here:";
47 const char kFieldsRequireTracing[] =
48 "[blink-gc] Class %0 has untraced fields that require tracing.";
50 const char kFieldRequiresTracingNote[] =
51 "[blink-gc] Untraced field %0 declared here:";
53 const char kClassContainsInvalidFields[] =
54 "[blink-gc] Class %0 contains invalid fields.";
56 const char kClassContainsGCRoot[] =
57 "[blink-gc] Class %0 contains GC root in field %1.";
59 const char kClassRequiresFinalization[] =
60 "[blink-gc] Class %0 requires finalization.";
62 const char kClassDoesNotRequireFinalization[] =
63 "[blink-gc] Class %0 may not require finalization.";
65 const char kFinalizerAccessesFinalizedField[] =
66 "[blink-gc] Finalizer %0 accesses potentially finalized field %1.";
68 const char kFinalizerAccessesEagerlyFinalizedField[] =
69 "[blink-gc] Finalizer %0 accesses eagerly finalized field %1.";
71 const char kRawPtrToGCManagedClassNote[] =
72 "[blink-gc] Raw pointer field %0 to a GC managed class declared here:";
74 const char kRefPtrToGCManagedClassNote[] =
75 "[blink-gc] RefPtr field %0 to a GC managed class declared here:";
77 const char kReferencePtrToGCManagedClassNote[] =
78 "[blink-gc] Reference pointer field %0 to a GC managed class"
79 " declared here:";
81 const char kOwnPtrToGCManagedClassNote[] =
82 "[blink-gc] OwnPtr field %0 to a GC managed class declared here:";
84 const char kMemberToGCUnmanagedClassNote[] =
85 "[blink-gc] Member field %0 to non-GC managed class declared here:";
87 const char kStackAllocatedFieldNote[] =
88 "[blink-gc] Stack-allocated field %0 declared here:";
90 const char kMemberInUnmanagedClassNote[] =
91 "[blink-gc] Member field %0 in unmanaged class declared here:";
93 const char kPartObjectToGCDerivedClassNote[] =
94 "[blink-gc] Part-object field %0 to a GC derived class declared here:";
96 const char kPartObjectContainsGCRootNote[] =
97 "[blink-gc] Field %0 with embedded GC root in %1 declared here:";
99 const char kFieldContainsGCRootNote[] =
100 "[blink-gc] Field %0 defining a GC root declared here:";
102 const char kOverriddenNonVirtualTrace[] =
103 "[blink-gc] Class %0 overrides non-virtual trace of base class %1.";
105 const char kOverriddenNonVirtualTraceNote[] =
106 "[blink-gc] Non-virtual trace method declared here:";
108 const char kMissingTraceDispatchMethod[] =
109 "[blink-gc] Class %0 is missing manual trace dispatch.";
111 const char kMissingFinalizeDispatchMethod[] =
112 "[blink-gc] Class %0 is missing manual finalize dispatch.";
114 const char kVirtualAndManualDispatch[] =
115 "[blink-gc] Class %0 contains or inherits virtual methods"
116 " but implements manual dispatching.";
118 const char kMissingTraceDispatch[] =
119 "[blink-gc] Missing dispatch to class %0 in manual trace dispatch.";
121 const char kMissingFinalizeDispatch[] =
122 "[blink-gc] Missing dispatch to class %0 in manual finalize dispatch.";
124 const char kFinalizedFieldNote[] =
125 "[blink-gc] Potentially finalized field %0 declared here:";
127 const char kEagerlyFinalizedFieldNote[] =
128 "[blink-gc] Field %0 having eagerly finalized value, declared here:";
130 const char kUserDeclaredDestructorNote[] =
131 "[blink-gc] User-declared destructor declared here:";
133 const char kUserDeclaredFinalizerNote[] =
134 "[blink-gc] User-declared finalizer declared here:";
136 const char kBaseRequiresFinalizationNote[] =
137 "[blink-gc] Base class %0 requiring finalization declared here:";
139 const char kFieldRequiresFinalizationNote[] =
140 "[blink-gc] Field %0 requiring finalization declared here:";
142 const char kManualDispatchMethodNote[] =
143 "[blink-gc] Manual dispatch %0 declared here:";
145 const char kDerivesNonStackAllocated[] =
146 "[blink-gc] Stack-allocated class %0 derives class %1"
147 " which is not stack allocated.";
149 const char kClassOverridesNew[] =
150 "[blink-gc] Garbage collected class %0"
151 " is not permitted to override its new operator.";
153 const char kClassDeclaresPureVirtualTrace[] =
154 "[blink-gc] Garbage collected class %0"
155 " is not permitted to declare a pure-virtual trace method.";
157 const char kLeftMostBaseMustBePolymorphic[] =
158 "[blink-gc] Left-most base class %0 of derived class %1"
159 " must be polymorphic.";
161 const char kBaseClassMustDeclareVirtualTrace[] =
162 "[blink-gc] Left-most base class %0 of derived class %1"
163 " must define a virtual trace method.";
165 const char kClassMustDeclareGCMixinTraceMethod[] =
166 "[blink-gc] Class %0 which inherits from GarbageCollectedMixin must"
167 " locally declare and override trace(Visitor*)";
169 // Use a local RAV implementation to simply collect all FunctionDecls marked for
170 // late template parsing. This happens with the flag -fdelayed-template-parsing,
171 // which is on by default in MSVC-compatible mode.
172 std::set<FunctionDecl*> GetLateParsedFunctionDecls(TranslationUnitDecl* decl) {
173 struct Visitor : public RecursiveASTVisitor<Visitor> {
174 bool VisitFunctionDecl(FunctionDecl* function_decl) {
175 if (function_decl->isLateTemplateParsed())
176 late_parsed_decls.insert(function_decl);
177 return true;
180 std::set<FunctionDecl*> late_parsed_decls;
181 } v;
182 v.TraverseDecl(decl);
183 return v.late_parsed_decls;
186 // This visitor checks that a method contains within its body, a call to a
187 // method on the provided receiver class. This is used to check manual
188 // dispatching for trace and finalize methods.
189 class CheckDispatchVisitor : public RecursiveASTVisitor<CheckDispatchVisitor> {
190 public:
191 CheckDispatchVisitor(RecordInfo* receiver)
192 : receiver_(receiver), dispatched_to_receiver_(false) {}
194 bool dispatched_to_receiver() { return dispatched_to_receiver_; }
196 bool VisitMemberExpr(MemberExpr* member) {
197 if (CXXMethodDecl* fn = dyn_cast<CXXMethodDecl>(member->getMemberDecl())) {
198 if (fn->getParent() == receiver_->record())
199 dispatched_to_receiver_ = true;
201 return true;
204 bool VisitUnresolvedMemberExpr(UnresolvedMemberExpr* member) {
205 for (Decl* decl : member->decls()) {
206 if (CXXMethodDecl* method = dyn_cast<CXXMethodDecl>(decl)) {
207 if (method->getParent() == receiver_->record() &&
208 Config::GetTraceMethodType(method) ==
209 Config::TRACE_AFTER_DISPATCH_METHOD) {
210 dispatched_to_receiver_ = true;
211 return true;
215 return true;
218 private:
219 RecordInfo* receiver_;
220 bool dispatched_to_receiver_;
223 class EmptyStmtVisitor
224 : public RecursiveASTVisitor<EmptyStmtVisitor> {
225 public:
226 static bool isEmpty(Stmt* stmt) {
227 EmptyStmtVisitor visitor;
228 visitor.TraverseStmt(stmt);
229 return visitor.empty_;
232 bool WalkUpFromCompoundStmt(CompoundStmt* stmt) {
233 empty_ = stmt->body_empty();
234 return false;
236 bool VisitStmt(Stmt*) {
237 empty_ = false;
238 return false;
240 private:
241 EmptyStmtVisitor() : empty_(true) {}
242 bool empty_;
245 // Main class containing checks for various invariants of the Blink
246 // garbage collection infrastructure.
247 class BlinkGCPluginConsumer : public ASTConsumer {
248 public:
249 BlinkGCPluginConsumer(CompilerInstance& instance,
250 const BlinkGCPluginOptions& options)
251 : instance_(instance),
252 diagnostic_(instance.getDiagnostics()),
253 options_(options),
254 json_(0) {
256 // Only check structures in the blink and WebKit namespaces.
257 options_.checked_namespaces.insert("blink");
259 // Ignore GC implementation files.
260 options_.ignored_directories.push_back("/heap/");
262 // Register warning/error messages.
263 diag_class_must_left_mostly_derive_gc_ = diagnostic_.getCustomDiagID(
264 getErrorLevel(), kClassMustLeftMostlyDeriveGC);
265 diag_class_requires_trace_method_ =
266 diagnostic_.getCustomDiagID(getErrorLevel(), kClassRequiresTraceMethod);
267 diag_base_requires_tracing_ =
268 diagnostic_.getCustomDiagID(getErrorLevel(), kBaseRequiresTracing);
269 diag_fields_require_tracing_ =
270 diagnostic_.getCustomDiagID(getErrorLevel(), kFieldsRequireTracing);
271 diag_class_contains_invalid_fields_ = diagnostic_.getCustomDiagID(
272 getErrorLevel(), kClassContainsInvalidFields);
273 diag_class_contains_invalid_fields_warning_ = diagnostic_.getCustomDiagID(
274 DiagnosticsEngine::Warning, kClassContainsInvalidFields);
275 diag_class_contains_gc_root_ =
276 diagnostic_.getCustomDiagID(getErrorLevel(), kClassContainsGCRoot);
277 diag_class_requires_finalization_ = diagnostic_.getCustomDiagID(
278 getErrorLevel(), kClassRequiresFinalization);
279 diag_class_does_not_require_finalization_ = diagnostic_.getCustomDiagID(
280 DiagnosticsEngine::Warning, kClassDoesNotRequireFinalization);
281 diag_finalizer_accesses_finalized_field_ = diagnostic_.getCustomDiagID(
282 getErrorLevel(), kFinalizerAccessesFinalizedField);
283 diag_finalizer_eagerly_finalized_field_ = diagnostic_.getCustomDiagID(
284 getErrorLevel(), kFinalizerAccessesEagerlyFinalizedField);
285 diag_overridden_non_virtual_trace_ = diagnostic_.getCustomDiagID(
286 getErrorLevel(), kOverriddenNonVirtualTrace);
287 diag_missing_trace_dispatch_method_ = diagnostic_.getCustomDiagID(
288 getErrorLevel(), kMissingTraceDispatchMethod);
289 diag_missing_finalize_dispatch_method_ = diagnostic_.getCustomDiagID(
290 getErrorLevel(), kMissingFinalizeDispatchMethod);
291 diag_virtual_and_manual_dispatch_ =
292 diagnostic_.getCustomDiagID(getErrorLevel(), kVirtualAndManualDispatch);
293 diag_missing_trace_dispatch_ =
294 diagnostic_.getCustomDiagID(getErrorLevel(), kMissingTraceDispatch);
295 diag_missing_finalize_dispatch_ =
296 diagnostic_.getCustomDiagID(getErrorLevel(), kMissingFinalizeDispatch);
297 diag_derives_non_stack_allocated_ =
298 diagnostic_.getCustomDiagID(getErrorLevel(), kDerivesNonStackAllocated);
299 diag_class_overrides_new_ =
300 diagnostic_.getCustomDiagID(getErrorLevel(), kClassOverridesNew);
301 diag_class_declares_pure_virtual_trace_ = diagnostic_.getCustomDiagID(
302 getErrorLevel(), kClassDeclaresPureVirtualTrace);
303 diag_left_most_base_must_be_polymorphic_ = diagnostic_.getCustomDiagID(
304 getErrorLevel(), kLeftMostBaseMustBePolymorphic);
305 diag_base_class_must_declare_virtual_trace_ = diagnostic_.getCustomDiagID(
306 getErrorLevel(), kBaseClassMustDeclareVirtualTrace);
307 diag_class_must_declare_gc_mixin_trace_method_ =
308 diagnostic_.getCustomDiagID(getErrorLevel(),
309 kClassMustDeclareGCMixinTraceMethod);
311 // Register note messages.
312 diag_base_requires_tracing_note_ = diagnostic_.getCustomDiagID(
313 DiagnosticsEngine::Note, kBaseRequiresTracingNote);
314 diag_field_requires_tracing_note_ = diagnostic_.getCustomDiagID(
315 DiagnosticsEngine::Note, kFieldRequiresTracingNote);
316 diag_raw_ptr_to_gc_managed_class_note_ = diagnostic_.getCustomDiagID(
317 DiagnosticsEngine::Note, kRawPtrToGCManagedClassNote);
318 diag_ref_ptr_to_gc_managed_class_note_ = diagnostic_.getCustomDiagID(
319 DiagnosticsEngine::Note, kRefPtrToGCManagedClassNote);
320 diag_reference_ptr_to_gc_managed_class_note_ = diagnostic_.getCustomDiagID(
321 DiagnosticsEngine::Note, kReferencePtrToGCManagedClassNote);
322 diag_own_ptr_to_gc_managed_class_note_ = diagnostic_.getCustomDiagID(
323 DiagnosticsEngine::Note, kOwnPtrToGCManagedClassNote);
324 diag_member_to_gc_unmanaged_class_note_ = diagnostic_.getCustomDiagID(
325 DiagnosticsEngine::Note, kMemberToGCUnmanagedClassNote);
326 diag_stack_allocated_field_note_ = diagnostic_.getCustomDiagID(
327 DiagnosticsEngine::Note, kStackAllocatedFieldNote);
328 diag_member_in_unmanaged_class_note_ = diagnostic_.getCustomDiagID(
329 DiagnosticsEngine::Note, kMemberInUnmanagedClassNote);
330 diag_part_object_to_gc_derived_class_note_ = diagnostic_.getCustomDiagID(
331 DiagnosticsEngine::Note, kPartObjectToGCDerivedClassNote);
332 diag_part_object_contains_gc_root_note_ = diagnostic_.getCustomDiagID(
333 DiagnosticsEngine::Note, kPartObjectContainsGCRootNote);
334 diag_field_contains_gc_root_note_ = diagnostic_.getCustomDiagID(
335 DiagnosticsEngine::Note, kFieldContainsGCRootNote);
336 diag_finalized_field_note_ = diagnostic_.getCustomDiagID(
337 DiagnosticsEngine::Note, kFinalizedFieldNote);
338 diag_eagerly_finalized_field_note_ = diagnostic_.getCustomDiagID(
339 DiagnosticsEngine::Note, kEagerlyFinalizedFieldNote);
340 diag_user_declared_destructor_note_ = diagnostic_.getCustomDiagID(
341 DiagnosticsEngine::Note, kUserDeclaredDestructorNote);
342 diag_user_declared_finalizer_note_ = diagnostic_.getCustomDiagID(
343 DiagnosticsEngine::Note, kUserDeclaredFinalizerNote);
344 diag_base_requires_finalization_note_ = diagnostic_.getCustomDiagID(
345 DiagnosticsEngine::Note, kBaseRequiresFinalizationNote);
346 diag_field_requires_finalization_note_ = diagnostic_.getCustomDiagID(
347 DiagnosticsEngine::Note, kFieldRequiresFinalizationNote);
348 diag_overridden_non_virtual_trace_note_ = diagnostic_.getCustomDiagID(
349 DiagnosticsEngine::Note, kOverriddenNonVirtualTraceNote);
350 diag_manual_dispatch_method_note_ = diagnostic_.getCustomDiagID(
351 DiagnosticsEngine::Note, kManualDispatchMethodNote);
354 void HandleTranslationUnit(ASTContext& context) override {
355 // Don't run the plugin if the compilation unit is already invalid.
356 if (diagnostic_.hasErrorOccurred())
357 return;
359 ParseFunctionTemplates(context.getTranslationUnitDecl());
361 CollectVisitor visitor;
362 visitor.TraverseDecl(context.getTranslationUnitDecl());
364 if (options_.dump_graph) {
365 std::error_code err;
366 // TODO: Make createDefaultOutputFile or a shorter createOutputFile work.
367 json_ = JsonWriter::from(instance_.createOutputFile(
368 "", // OutputPath
369 err, // Errors
370 true, // Binary
371 true, // RemoveFileOnSignal
372 instance_.getFrontendOpts().OutputFile, // BaseInput
373 "graph.json", // Extension
374 false, // UseTemporary
375 false, // CreateMissingDirectories
376 0, // ResultPathName
377 0)); // TempPathName
378 if (!err && json_) {
379 json_->OpenList();
380 } else {
381 json_ = 0;
382 llvm::errs()
383 << "[blink-gc] "
384 << "Failed to create an output file for the object graph.\n";
388 for (CollectVisitor::RecordVector::iterator it =
389 visitor.record_decls().begin();
390 it != visitor.record_decls().end();
391 ++it) {
392 CheckRecord(cache_.Lookup(*it));
395 for (CollectVisitor::MethodVector::iterator it =
396 visitor.trace_decls().begin();
397 it != visitor.trace_decls().end();
398 ++it) {
399 CheckTracingMethod(*it);
402 if (json_) {
403 json_->CloseList();
404 delete json_;
405 json_ = 0;
409 void ParseFunctionTemplates(TranslationUnitDecl* decl) {
410 if (!instance_.getLangOpts().DelayedTemplateParsing)
411 return; // Nothing to do.
413 std::set<FunctionDecl*> late_parsed_decls =
414 GetLateParsedFunctionDecls(decl);
415 clang::Sema& sema = instance_.getSema();
417 for (const FunctionDecl* fd : late_parsed_decls) {
418 assert(fd->isLateTemplateParsed());
420 if (!Config::IsTraceMethod(fd))
421 continue;
423 if (instance_.getSourceManager().isInSystemHeader(
424 instance_.getSourceManager().getSpellingLoc(fd->getLocation())))
425 continue;
427 // Force parsing and AST building of the yet-uninstantiated function
428 // template trace method bodies.
429 clang::LateParsedTemplate* lpt = sema.LateParsedTemplateMap[fd];
430 sema.LateTemplateParser(sema.OpaqueParser, *lpt);
434 // Main entry for checking a record declaration.
435 void CheckRecord(RecordInfo* info) {
436 if (IsIgnored(info))
437 return;
439 CXXRecordDecl* record = info->record();
441 // TODO: what should we do to check unions?
442 if (record->isUnion())
443 return;
445 // If this is the primary template declaration, check its specializations.
446 if (record->isThisDeclarationADefinition() &&
447 record->getDescribedClassTemplate()) {
448 ClassTemplateDecl* tmpl = record->getDescribedClassTemplate();
449 for (ClassTemplateDecl::spec_iterator it = tmpl->spec_begin();
450 it != tmpl->spec_end();
451 ++it) {
452 CheckClass(cache_.Lookup(*it));
454 return;
457 CheckClass(info);
460 // Check a class-like object (eg, class, specialization, instantiation).
461 void CheckClass(RecordInfo* info) {
462 if (!info)
463 return;
465 // Check consistency of stack-allocated hierarchies.
466 if (info->IsStackAllocated()) {
467 for (RecordInfo::Bases::iterator it = info->GetBases().begin();
468 it != info->GetBases().end();
469 ++it) {
470 if (!it->second.info()->IsStackAllocated())
471 ReportDerivesNonStackAllocated(info, &it->second);
475 if (CXXMethodDecl* trace = info->GetTraceMethod()) {
476 if (trace->isPure())
477 ReportClassDeclaresPureVirtualTrace(info, trace);
478 } else if (info->RequiresTraceMethod()) {
479 ReportClassRequiresTraceMethod(info);
482 // Check polymorphic classes that are GC-derived or have a trace method.
483 if (info->record()->hasDefinition() && info->record()->isPolymorphic()) {
484 // TODO: Check classes that inherit a trace method.
485 CXXMethodDecl* trace = info->GetTraceMethod();
486 if (trace || info->IsGCDerived())
487 CheckPolymorphicClass(info, trace);
491 CheckFieldsVisitor visitor(options_);
492 if (visitor.ContainsInvalidFields(info))
493 ReportClassContainsInvalidFields(info, &visitor.invalid_fields());
496 if (info->IsGCDerived()) {
498 if (!info->IsGCMixin()) {
499 CheckLeftMostDerived(info);
500 CheckDispatch(info);
501 if (CXXMethodDecl* newop = info->DeclaresNewOperator())
502 if (!Config::IsIgnoreAnnotated(newop))
503 ReportClassOverridesNew(info, newop);
504 if (info->IsGCMixinInstance()) {
505 // Require that declared GCMixin implementations
506 // also provide a trace() override.
507 if (info->DeclaresGCMixinMethods()
508 && !info->DeclaresLocalTraceMethod())
509 ReportClassMustDeclareGCMixinTraceMethod(info);
514 CheckGCRootsVisitor visitor;
515 if (visitor.ContainsGCRoots(info))
516 ReportClassContainsGCRoots(info, &visitor.gc_roots());
519 if (info->NeedsFinalization())
520 CheckFinalization(info);
522 if (options_.warn_unneeded_finalizer && info->IsGCFinalized())
523 CheckUnneededFinalization(info);
526 DumpClass(info);
529 CXXRecordDecl* GetDependentTemplatedDecl(const Type& type) {
530 const TemplateSpecializationType* tmpl_type =
531 type.getAs<TemplateSpecializationType>();
532 if (!tmpl_type)
533 return 0;
535 TemplateDecl* tmpl_decl = tmpl_type->getTemplateName().getAsTemplateDecl();
536 if (!tmpl_decl)
537 return 0;
539 return dyn_cast<CXXRecordDecl>(tmpl_decl->getTemplatedDecl());
542 // The GC infrastructure assumes that if the vtable of a polymorphic
543 // base-class is not initialized for a given object (ie, it is partially
544 // initialized) then the object does not need to be traced. Thus, we must
545 // ensure that any polymorphic class with a trace method does not have any
546 // tractable fields that are initialized before we are sure that the vtable
547 // and the trace method are both defined. There are two cases that need to
548 // hold to satisfy that assumption:
550 // 1. If trace is virtual, then it must be defined in the left-most base.
551 // This ensures that if the vtable is initialized then it contains a pointer
552 // to the trace method.
554 // 2. If trace is non-virtual, then the trace method is defined and we must
555 // ensure that the left-most base defines a vtable. This ensures that the
556 // first thing to be initialized when constructing the object is the vtable
557 // itself.
558 void CheckPolymorphicClass(RecordInfo* info, CXXMethodDecl* trace) {
559 CXXRecordDecl* left_most = info->record();
560 CXXRecordDecl::base_class_iterator it = left_most->bases_begin();
561 CXXRecordDecl* left_most_base = 0;
562 while (it != left_most->bases_end()) {
563 left_most_base = it->getType()->getAsCXXRecordDecl();
564 if (!left_most_base && it->getType()->isDependentType())
565 left_most_base = RecordInfo::GetDependentTemplatedDecl(*it->getType());
567 // TODO: Find a way to correctly check actual instantiations
568 // for dependent types. The escape below will be hit, eg, when
569 // we have a primary template with no definition and
570 // specializations for each case (such as SupplementBase) in
571 // which case we don't succeed in checking the required
572 // properties.
573 if (!left_most_base || !left_most_base->hasDefinition())
574 return;
576 StringRef name = left_most_base->getName();
577 // We know GCMixin base defines virtual trace.
578 if (Config::IsGCMixinBase(name))
579 return;
581 // Stop with the left-most prior to a safe polymorphic base (a safe base
582 // is non-polymorphic and contains no fields).
583 if (Config::IsSafePolymorphicBase(name))
584 break;
586 left_most = left_most_base;
587 it = left_most->bases_begin();
590 if (RecordInfo* left_most_info = cache_.Lookup(left_most)) {
592 // Check condition (1):
593 if (trace && trace->isVirtual()) {
594 if (CXXMethodDecl* trace = left_most_info->GetTraceMethod()) {
595 if (trace->isVirtual())
596 return;
598 ReportBaseClassMustDeclareVirtualTrace(info, left_most);
599 return;
602 // Check condition (2):
603 if (DeclaresVirtualMethods(left_most))
604 return;
605 if (left_most_base) {
606 // Get the base next to the "safe polymorphic base"
607 if (it != left_most->bases_end())
608 ++it;
609 if (it != left_most->bases_end()) {
610 if (CXXRecordDecl* next_base = it->getType()->getAsCXXRecordDecl()) {
611 if (CXXRecordDecl* next_left_most = GetLeftMostBase(next_base)) {
612 if (DeclaresVirtualMethods(next_left_most))
613 return;
614 ReportLeftMostBaseMustBePolymorphic(info, next_left_most);
615 return;
620 ReportLeftMostBaseMustBePolymorphic(info, left_most);
624 CXXRecordDecl* GetLeftMostBase(CXXRecordDecl* left_most) {
625 CXXRecordDecl::base_class_iterator it = left_most->bases_begin();
626 while (it != left_most->bases_end()) {
627 if (it->getType()->isDependentType())
628 left_most = RecordInfo::GetDependentTemplatedDecl(*it->getType());
629 else
630 left_most = it->getType()->getAsCXXRecordDecl();
631 if (!left_most || !left_most->hasDefinition())
632 return 0;
633 it = left_most->bases_begin();
635 return left_most;
638 bool DeclaresVirtualMethods(CXXRecordDecl* decl) {
639 CXXRecordDecl::method_iterator it = decl->method_begin();
640 for (; it != decl->method_end(); ++it)
641 if (it->isVirtual() && !it->isPure())
642 return true;
643 return false;
646 void CheckLeftMostDerived(RecordInfo* info) {
647 CXXRecordDecl* left_most = GetLeftMostBase(info->record());
648 if (!left_most)
649 return;
650 if (!Config::IsGCBase(left_most->getName()))
651 ReportClassMustLeftMostlyDeriveGC(info);
654 void CheckDispatch(RecordInfo* info) {
655 bool finalized = info->IsGCFinalized();
656 CXXMethodDecl* trace_dispatch = info->GetTraceDispatchMethod();
657 CXXMethodDecl* finalize_dispatch = info->GetFinalizeDispatchMethod();
658 if (!trace_dispatch && !finalize_dispatch)
659 return;
661 CXXRecordDecl* base = trace_dispatch ? trace_dispatch->getParent()
662 : finalize_dispatch->getParent();
664 // Check that dispatch methods are defined at the base.
665 if (base == info->record()) {
666 if (!trace_dispatch)
667 ReportMissingTraceDispatchMethod(info);
668 if (finalized && !finalize_dispatch)
669 ReportMissingFinalizeDispatchMethod(info);
670 if (!finalized && finalize_dispatch) {
671 ReportClassRequiresFinalization(info);
672 NoteUserDeclaredFinalizer(finalize_dispatch);
676 // Check that classes implementing manual dispatch do not have vtables.
677 if (info->record()->isPolymorphic())
678 ReportVirtualAndManualDispatch(
679 info, trace_dispatch ? trace_dispatch : finalize_dispatch);
681 // If this is a non-abstract class check that it is dispatched to.
682 // TODO: Create a global variant of this local check. We can only check if
683 // the dispatch body is known in this compilation unit.
684 if (info->IsConsideredAbstract())
685 return;
687 const FunctionDecl* defn;
689 if (trace_dispatch && trace_dispatch->isDefined(defn)) {
690 CheckDispatchVisitor visitor(info);
691 visitor.TraverseStmt(defn->getBody());
692 if (!visitor.dispatched_to_receiver())
693 ReportMissingTraceDispatch(defn, info);
696 if (finalized && finalize_dispatch && finalize_dispatch->isDefined(defn)) {
697 CheckDispatchVisitor visitor(info);
698 visitor.TraverseStmt(defn->getBody());
699 if (!visitor.dispatched_to_receiver())
700 ReportMissingFinalizeDispatch(defn, info);
704 // TODO: Should we collect destructors similar to trace methods?
705 void CheckFinalization(RecordInfo* info) {
706 CXXDestructorDecl* dtor = info->record()->getDestructor();
708 // For finalized classes, check the finalization method if possible.
709 if (info->IsGCFinalized()) {
710 if (dtor && dtor->hasBody()) {
711 CheckFinalizerVisitor visitor(&cache_, info->IsEagerlyFinalized());
712 visitor.TraverseCXXMethodDecl(dtor);
713 if (!visitor.finalized_fields().empty()) {
714 ReportFinalizerAccessesFinalizedFields(
715 dtor, &visitor.finalized_fields());
718 return;
721 // Don't require finalization of a mixin that has not yet been "mixed in".
722 if (info->IsGCMixin())
723 return;
725 // Report the finalization error, and proceed to print possible causes for
726 // the finalization requirement.
727 ReportClassRequiresFinalization(info);
729 if (dtor && dtor->isUserProvided())
730 NoteUserDeclaredDestructor(dtor);
732 for (RecordInfo::Bases::iterator it = info->GetBases().begin();
733 it != info->GetBases().end();
734 ++it) {
735 if (it->second.info()->NeedsFinalization())
736 NoteBaseRequiresFinalization(&it->second);
739 for (RecordInfo::Fields::iterator it = info->GetFields().begin();
740 it != info->GetFields().end();
741 ++it) {
742 if (it->second.edge()->NeedsFinalization())
743 NoteField(&it->second, diag_field_requires_finalization_note_);
747 void CheckUnneededFinalization(RecordInfo* info) {
748 if (!HasNonEmptyFinalizer(info))
749 ReportClassDoesNotRequireFinalization(info);
752 bool HasNonEmptyFinalizer(RecordInfo* info) {
753 CXXDestructorDecl* dtor = info->record()->getDestructor();
754 if (dtor && dtor->isUserProvided()) {
755 if (!dtor->hasBody() || !EmptyStmtVisitor::isEmpty(dtor->getBody()))
756 return true;
758 for (RecordInfo::Bases::iterator it = info->GetBases().begin();
759 it != info->GetBases().end();
760 ++it) {
761 if (HasNonEmptyFinalizer(it->second.info()))
762 return true;
764 for (RecordInfo::Fields::iterator it = info->GetFields().begin();
765 it != info->GetFields().end();
766 ++it) {
767 if (it->second.edge()->NeedsFinalization())
768 return true;
770 return false;
773 // This is the main entry for tracing method definitions.
774 void CheckTracingMethod(CXXMethodDecl* method) {
775 RecordInfo* parent = cache_.Lookup(method->getParent());
776 if (IsIgnored(parent))
777 return;
779 // Check templated tracing methods by checking the template instantiations.
780 // Specialized templates are handled as ordinary classes.
781 if (ClassTemplateDecl* tmpl =
782 parent->record()->getDescribedClassTemplate()) {
783 for (ClassTemplateDecl::spec_iterator it = tmpl->spec_begin();
784 it != tmpl->spec_end();
785 ++it) {
786 // Check trace using each template instantiation as the holder.
787 if (Config::IsTemplateInstantiation(*it))
788 CheckTraceOrDispatchMethod(cache_.Lookup(*it), method);
790 return;
793 CheckTraceOrDispatchMethod(parent, method);
796 // Determine what type of tracing method this is (dispatch or trace).
797 void CheckTraceOrDispatchMethod(RecordInfo* parent, CXXMethodDecl* method) {
798 Config::TraceMethodType trace_type = Config::GetTraceMethodType(method);
799 if (trace_type == Config::TRACE_AFTER_DISPATCH_METHOD ||
800 trace_type == Config::TRACE_AFTER_DISPATCH_IMPL_METHOD ||
801 !parent->GetTraceDispatchMethod()) {
802 CheckTraceMethod(parent, method, trace_type);
804 // Dispatch methods are checked when we identify subclasses.
807 // Check an actual trace method.
808 void CheckTraceMethod(RecordInfo* parent,
809 CXXMethodDecl* trace,
810 Config::TraceMethodType trace_type) {
811 // A trace method must not override any non-virtual trace methods.
812 if (trace_type == Config::TRACE_METHOD) {
813 for (RecordInfo::Bases::iterator it = parent->GetBases().begin();
814 it != parent->GetBases().end();
815 ++it) {
816 RecordInfo* base = it->second.info();
817 if (CXXMethodDecl* other = base->InheritsNonVirtualTrace())
818 ReportOverriddenNonVirtualTrace(parent, trace, other);
822 CheckTraceVisitor visitor(trace, parent, &cache_);
823 visitor.TraverseCXXMethodDecl(trace);
825 // Skip reporting if this trace method is a just delegate to
826 // traceImpl (or traceAfterDispatchImpl) method. We will report on
827 // CheckTraceMethod on traceImpl method.
828 if (visitor.delegates_to_traceimpl())
829 return;
831 for (RecordInfo::Bases::iterator it = parent->GetBases().begin();
832 it != parent->GetBases().end();
833 ++it) {
834 if (!it->second.IsProperlyTraced())
835 ReportBaseRequiresTracing(parent, trace, it->first);
838 for (RecordInfo::Fields::iterator it = parent->GetFields().begin();
839 it != parent->GetFields().end();
840 ++it) {
841 if (!it->second.IsProperlyTraced()) {
842 // Discontinue once an untraced-field error is found.
843 ReportFieldsRequireTracing(parent, trace);
844 break;
849 void DumpClass(RecordInfo* info) {
850 if (!json_)
851 return;
853 json_->OpenObject();
854 json_->Write("name", info->record()->getQualifiedNameAsString());
855 json_->Write("loc", GetLocString(info->record()->getLocStart()));
856 json_->CloseObject();
858 class DumpEdgeVisitor : public RecursiveEdgeVisitor {
859 public:
860 DumpEdgeVisitor(JsonWriter* json) : json_(json) {}
861 void DumpEdge(RecordInfo* src,
862 RecordInfo* dst,
863 const string& lbl,
864 const Edge::LivenessKind& kind,
865 const string& loc) {
866 json_->OpenObject();
867 json_->Write("src", src->record()->getQualifiedNameAsString());
868 json_->Write("dst", dst->record()->getQualifiedNameAsString());
869 json_->Write("lbl", lbl);
870 json_->Write("kind", kind);
871 json_->Write("loc", loc);
872 json_->Write("ptr",
873 !Parent() ? "val" :
874 Parent()->IsRawPtr() ?
875 (static_cast<RawPtr*>(Parent())->HasReferenceType() ?
876 "reference" : "raw") :
877 Parent()->IsRefPtr() ? "ref" :
878 Parent()->IsOwnPtr() ? "own" :
879 (Parent()->IsMember() ||
880 Parent()->IsWeakMember()) ? "mem" :
881 "val");
882 json_->CloseObject();
885 void DumpField(RecordInfo* src, FieldPoint* point, const string& loc) {
886 src_ = src;
887 point_ = point;
888 loc_ = loc;
889 point_->edge()->Accept(this);
892 void AtValue(Value* e) override {
893 // The liveness kind of a path from the point to this value
894 // is given by the innermost place that is non-strong.
895 Edge::LivenessKind kind = Edge::kStrong;
896 if (Config::IsIgnoreCycleAnnotated(point_->field())) {
897 kind = Edge::kWeak;
898 } else {
899 for (Context::iterator it = context().begin();
900 it != context().end();
901 ++it) {
902 Edge::LivenessKind pointer_kind = (*it)->Kind();
903 if (pointer_kind != Edge::kStrong) {
904 kind = pointer_kind;
905 break;
909 DumpEdge(
910 src_, e->value(), point_->field()->getNameAsString(), kind, loc_);
913 private:
914 JsonWriter* json_;
915 RecordInfo* src_;
916 FieldPoint* point_;
917 string loc_;
920 DumpEdgeVisitor visitor(json_);
922 RecordInfo::Bases& bases = info->GetBases();
923 for (RecordInfo::Bases::iterator it = bases.begin();
924 it != bases.end();
925 ++it) {
926 visitor.DumpEdge(info,
927 it->second.info(),
928 "<super>",
929 Edge::kStrong,
930 GetLocString(it->second.spec().getLocStart()));
933 RecordInfo::Fields& fields = info->GetFields();
934 for (RecordInfo::Fields::iterator it = fields.begin();
935 it != fields.end();
936 ++it) {
937 visitor.DumpField(info,
938 &it->second,
939 GetLocString(it->second.field()->getLocStart()));
943 // Adds either a warning or error, based on the current handling of -Werror.
944 DiagnosticsEngine::Level getErrorLevel() {
945 return diagnostic_.getWarningsAsErrors() ? DiagnosticsEngine::Error
946 : DiagnosticsEngine::Warning;
949 const string GetLocString(SourceLocation loc) {
950 const SourceManager& source_manager = instance_.getSourceManager();
951 PresumedLoc ploc = source_manager.getPresumedLoc(loc);
952 if (ploc.isInvalid())
953 return "";
954 string loc_str;
955 llvm::raw_string_ostream OS(loc_str);
956 OS << ploc.getFilename()
957 << ":" << ploc.getLine()
958 << ":" << ploc.getColumn();
959 return OS.str();
962 bool IsIgnored(RecordInfo* record) {
963 return !record ||
964 !InCheckedNamespace(record) ||
965 IsIgnoredClass(record) ||
966 InIgnoredDirectory(record);
969 bool IsIgnoredClass(RecordInfo* info) {
970 // Ignore any class prefixed by SameSizeAs. These are used in
971 // Blink to verify class sizes and don't need checking.
972 const string SameSizeAs = "SameSizeAs";
973 if (info->name().compare(0, SameSizeAs.size(), SameSizeAs) == 0)
974 return true;
975 return options_.ignored_classes.find(info->name()) !=
976 options_.ignored_classes.end();
979 bool InIgnoredDirectory(RecordInfo* info) {
980 string filename;
981 if (!GetFilename(info->record()->getLocStart(), &filename))
982 return false; // TODO: should we ignore non-existing file locations?
983 #if defined(LLVM_ON_WIN32)
984 std::replace(filename.begin(), filename.end(), '\\', '/');
985 #endif
986 std::vector<string>::iterator it = options_.ignored_directories.begin();
987 for (; it != options_.ignored_directories.end(); ++it)
988 if (filename.find(*it) != string::npos)
989 return true;
990 return false;
993 bool InCheckedNamespace(RecordInfo* info) {
994 if (!info)
995 return false;
996 for (DeclContext* context = info->record()->getDeclContext();
997 !context->isTranslationUnit();
998 context = context->getParent()) {
999 if (NamespaceDecl* decl = dyn_cast<NamespaceDecl>(context)) {
1000 if (decl->isAnonymousNamespace())
1001 return true;
1002 if (options_.checked_namespaces.find(decl->getNameAsString()) !=
1003 options_.checked_namespaces.end()) {
1004 return true;
1008 return false;
1011 bool GetFilename(SourceLocation loc, string* filename) {
1012 const SourceManager& source_manager = instance_.getSourceManager();
1013 SourceLocation spelling_location = source_manager.getSpellingLoc(loc);
1014 PresumedLoc ploc = source_manager.getPresumedLoc(spelling_location);
1015 if (ploc.isInvalid()) {
1016 // If we're in an invalid location, we're looking at things that aren't
1017 // actually stated in the source.
1018 return false;
1020 *filename = ploc.getFilename();
1021 return true;
1024 void ReportClassMustLeftMostlyDeriveGC(RecordInfo* info) {
1025 SourceLocation loc = info->record()->getInnerLocStart();
1026 SourceManager& manager = instance_.getSourceManager();
1027 FullSourceLoc full_loc(loc, manager);
1028 diagnostic_.Report(full_loc, diag_class_must_left_mostly_derive_gc_)
1029 << info->record();
1032 void ReportClassRequiresTraceMethod(RecordInfo* info) {
1033 SourceLocation loc = info->record()->getInnerLocStart();
1034 SourceManager& manager = instance_.getSourceManager();
1035 FullSourceLoc full_loc(loc, manager);
1036 diagnostic_.Report(full_loc, diag_class_requires_trace_method_)
1037 << info->record();
1039 for (RecordInfo::Bases::iterator it = info->GetBases().begin();
1040 it != info->GetBases().end();
1041 ++it) {
1042 if (it->second.NeedsTracing().IsNeeded())
1043 NoteBaseRequiresTracing(&it->second);
1046 for (RecordInfo::Fields::iterator it = info->GetFields().begin();
1047 it != info->GetFields().end();
1048 ++it) {
1049 if (!it->second.IsProperlyTraced())
1050 NoteFieldRequiresTracing(info, it->first);
1054 void ReportBaseRequiresTracing(RecordInfo* derived,
1055 CXXMethodDecl* trace,
1056 CXXRecordDecl* base) {
1057 SourceLocation loc = trace->getLocStart();
1058 SourceManager& manager = instance_.getSourceManager();
1059 FullSourceLoc full_loc(loc, manager);
1060 diagnostic_.Report(full_loc, diag_base_requires_tracing_)
1061 << base << derived->record();
1064 void ReportFieldsRequireTracing(RecordInfo* info, CXXMethodDecl* trace) {
1065 SourceLocation loc = trace->getLocStart();
1066 SourceManager& manager = instance_.getSourceManager();
1067 FullSourceLoc full_loc(loc, manager);
1068 diagnostic_.Report(full_loc, diag_fields_require_tracing_)
1069 << info->record();
1070 for (RecordInfo::Fields::iterator it = info->GetFields().begin();
1071 it != info->GetFields().end();
1072 ++it) {
1073 if (!it->second.IsProperlyTraced())
1074 NoteFieldRequiresTracing(info, it->first);
1078 void ReportClassContainsInvalidFields(RecordInfo* info,
1079 CheckFieldsVisitor::Errors* errors) {
1080 SourceLocation loc = info->record()->getLocStart();
1081 SourceManager& manager = instance_.getSourceManager();
1082 FullSourceLoc full_loc(loc, manager);
1083 bool only_warnings = options_.warn_raw_ptr;
1084 for (CheckFieldsVisitor::Errors::iterator it = errors->begin();
1085 only_warnings && it != errors->end();
1086 ++it) {
1087 if (!CheckFieldsVisitor::IsWarning(it->second))
1088 only_warnings = false;
1090 diagnostic_.Report(full_loc, only_warnings ?
1091 diag_class_contains_invalid_fields_warning_ :
1092 diag_class_contains_invalid_fields_)
1093 << info->record();
1094 for (CheckFieldsVisitor::Errors::iterator it = errors->begin();
1095 it != errors->end();
1096 ++it) {
1097 unsigned error;
1098 if (CheckFieldsVisitor::IsRawPtrError(it->second)) {
1099 error = diag_raw_ptr_to_gc_managed_class_note_;
1100 } else if (CheckFieldsVisitor::IsReferencePtrError(it->second)) {
1101 error = diag_reference_ptr_to_gc_managed_class_note_;
1102 } else if (it->second == CheckFieldsVisitor::kRefPtrToGCManaged) {
1103 error = diag_ref_ptr_to_gc_managed_class_note_;
1104 } else if (it->second == CheckFieldsVisitor::kOwnPtrToGCManaged) {
1105 error = diag_own_ptr_to_gc_managed_class_note_;
1106 } else if (it->second == CheckFieldsVisitor::kMemberToGCUnmanaged) {
1107 error = diag_member_to_gc_unmanaged_class_note_;
1108 } else if (it->second == CheckFieldsVisitor::kMemberInUnmanaged) {
1109 error = diag_member_in_unmanaged_class_note_;
1110 } else if (it->second == CheckFieldsVisitor::kPtrFromHeapToStack) {
1111 error = diag_stack_allocated_field_note_;
1112 } else if (it->second == CheckFieldsVisitor::kGCDerivedPartObject) {
1113 error = diag_part_object_to_gc_derived_class_note_;
1114 } else {
1115 assert(false && "Unknown field error");
1117 NoteField(it->first, error);
1121 void ReportClassContainsGCRoots(RecordInfo* info,
1122 CheckGCRootsVisitor::Errors* errors) {
1123 SourceLocation loc = info->record()->getLocStart();
1124 SourceManager& manager = instance_.getSourceManager();
1125 FullSourceLoc full_loc(loc, manager);
1126 for (CheckGCRootsVisitor::Errors::iterator it = errors->begin();
1127 it != errors->end();
1128 ++it) {
1129 CheckGCRootsVisitor::RootPath::iterator path = it->begin();
1130 FieldPoint* point = *path;
1131 diagnostic_.Report(full_loc, diag_class_contains_gc_root_)
1132 << info->record() << point->field();
1133 while (++path != it->end()) {
1134 NotePartObjectContainsGCRoot(point);
1135 point = *path;
1137 NoteFieldContainsGCRoot(point);
1141 void ReportFinalizerAccessesFinalizedFields(
1142 CXXMethodDecl* dtor,
1143 CheckFinalizerVisitor::Errors* fields) {
1144 for (CheckFinalizerVisitor::Errors::iterator it = fields->begin();
1145 it != fields->end();
1146 ++it) {
1147 SourceLocation loc = it->member->getLocStart();
1148 SourceManager& manager = instance_.getSourceManager();
1149 bool as_eagerly_finalized = it->as_eagerly_finalized;
1150 unsigned diag_error = as_eagerly_finalized ?
1151 diag_finalizer_eagerly_finalized_field_ :
1152 diag_finalizer_accesses_finalized_field_;
1153 unsigned diag_note = as_eagerly_finalized ?
1154 diag_eagerly_finalized_field_note_ :
1155 diag_finalized_field_note_;
1156 FullSourceLoc full_loc(loc, manager);
1157 diagnostic_.Report(full_loc, diag_error)
1158 << dtor << it->field->field();
1159 NoteField(it->field, diag_note);
1163 void ReportClassRequiresFinalization(RecordInfo* info) {
1164 SourceLocation loc = info->record()->getInnerLocStart();
1165 SourceManager& manager = instance_.getSourceManager();
1166 FullSourceLoc full_loc(loc, manager);
1167 diagnostic_.Report(full_loc, diag_class_requires_finalization_)
1168 << info->record();
1171 void ReportClassDoesNotRequireFinalization(RecordInfo* info) {
1172 SourceLocation loc = info->record()->getInnerLocStart();
1173 SourceManager& manager = instance_.getSourceManager();
1174 FullSourceLoc full_loc(loc, manager);
1175 diagnostic_.Report(full_loc, diag_class_does_not_require_finalization_)
1176 << info->record();
1179 void ReportClassMustDeclareGCMixinTraceMethod(RecordInfo* info) {
1180 SourceLocation loc = info->record()->getInnerLocStart();
1181 SourceManager& manager = instance_.getSourceManager();
1182 FullSourceLoc full_loc(loc, manager);
1183 diagnostic_.Report(
1184 full_loc, diag_class_must_declare_gc_mixin_trace_method_)
1185 << info->record();
1188 void ReportOverriddenNonVirtualTrace(RecordInfo* info,
1189 CXXMethodDecl* trace,
1190 CXXMethodDecl* overridden) {
1191 SourceLocation loc = trace->getLocStart();
1192 SourceManager& manager = instance_.getSourceManager();
1193 FullSourceLoc full_loc(loc, manager);
1194 diagnostic_.Report(full_loc, diag_overridden_non_virtual_trace_)
1195 << info->record() << overridden->getParent();
1196 NoteOverriddenNonVirtualTrace(overridden);
1199 void ReportMissingTraceDispatchMethod(RecordInfo* info) {
1200 ReportMissingDispatchMethod(info, diag_missing_trace_dispatch_method_);
1203 void ReportMissingFinalizeDispatchMethod(RecordInfo* info) {
1204 ReportMissingDispatchMethod(info, diag_missing_finalize_dispatch_method_);
1207 void ReportMissingDispatchMethod(RecordInfo* info, unsigned error) {
1208 SourceLocation loc = info->record()->getInnerLocStart();
1209 SourceManager& manager = instance_.getSourceManager();
1210 FullSourceLoc full_loc(loc, manager);
1211 diagnostic_.Report(full_loc, error) << info->record();
1214 void ReportVirtualAndManualDispatch(RecordInfo* info,
1215 CXXMethodDecl* dispatch) {
1216 SourceLocation loc = info->record()->getInnerLocStart();
1217 SourceManager& manager = instance_.getSourceManager();
1218 FullSourceLoc full_loc(loc, manager);
1219 diagnostic_.Report(full_loc, diag_virtual_and_manual_dispatch_)
1220 << info->record();
1221 NoteManualDispatchMethod(dispatch);
1224 void ReportMissingTraceDispatch(const FunctionDecl* dispatch,
1225 RecordInfo* receiver) {
1226 ReportMissingDispatch(dispatch, receiver, diag_missing_trace_dispatch_);
1229 void ReportMissingFinalizeDispatch(const FunctionDecl* dispatch,
1230 RecordInfo* receiver) {
1231 ReportMissingDispatch(dispatch, receiver, diag_missing_finalize_dispatch_);
1234 void ReportMissingDispatch(const FunctionDecl* dispatch,
1235 RecordInfo* receiver,
1236 unsigned error) {
1237 SourceLocation loc = dispatch->getLocStart();
1238 SourceManager& manager = instance_.getSourceManager();
1239 FullSourceLoc full_loc(loc, manager);
1240 diagnostic_.Report(full_loc, error) << receiver->record();
1243 void ReportDerivesNonStackAllocated(RecordInfo* info, BasePoint* base) {
1244 SourceLocation loc = base->spec().getLocStart();
1245 SourceManager& manager = instance_.getSourceManager();
1246 FullSourceLoc full_loc(loc, manager);
1247 diagnostic_.Report(full_loc, diag_derives_non_stack_allocated_)
1248 << info->record() << base->info()->record();
1251 void ReportClassOverridesNew(RecordInfo* info, CXXMethodDecl* newop) {
1252 SourceLocation loc = newop->getLocStart();
1253 SourceManager& manager = instance_.getSourceManager();
1254 FullSourceLoc full_loc(loc, manager);
1255 diagnostic_.Report(full_loc, diag_class_overrides_new_) << info->record();
1258 void ReportClassDeclaresPureVirtualTrace(RecordInfo* info,
1259 CXXMethodDecl* trace) {
1260 SourceLocation loc = trace->getLocStart();
1261 SourceManager& manager = instance_.getSourceManager();
1262 FullSourceLoc full_loc(loc, manager);
1263 diagnostic_.Report(full_loc, diag_class_declares_pure_virtual_trace_)
1264 << info->record();
1267 void ReportLeftMostBaseMustBePolymorphic(RecordInfo* derived,
1268 CXXRecordDecl* base) {
1269 SourceLocation loc = base->getLocStart();
1270 SourceManager& manager = instance_.getSourceManager();
1271 FullSourceLoc full_loc(loc, manager);
1272 diagnostic_.Report(full_loc, diag_left_most_base_must_be_polymorphic_)
1273 << base << derived->record();
1276 void ReportBaseClassMustDeclareVirtualTrace(RecordInfo* derived,
1277 CXXRecordDecl* base) {
1278 SourceLocation loc = base->getLocStart();
1279 SourceManager& manager = instance_.getSourceManager();
1280 FullSourceLoc full_loc(loc, manager);
1281 diagnostic_.Report(full_loc, diag_base_class_must_declare_virtual_trace_)
1282 << base << derived->record();
1285 void NoteManualDispatchMethod(CXXMethodDecl* dispatch) {
1286 SourceLocation loc = dispatch->getLocStart();
1287 SourceManager& manager = instance_.getSourceManager();
1288 FullSourceLoc full_loc(loc, manager);
1289 diagnostic_.Report(full_loc, diag_manual_dispatch_method_note_) << dispatch;
1292 void NoteBaseRequiresTracing(BasePoint* base) {
1293 SourceLocation loc = base->spec().getLocStart();
1294 SourceManager& manager = instance_.getSourceManager();
1295 FullSourceLoc full_loc(loc, manager);
1296 diagnostic_.Report(full_loc, diag_base_requires_tracing_note_)
1297 << base->info()->record();
1300 void NoteFieldRequiresTracing(RecordInfo* holder, FieldDecl* field) {
1301 NoteField(field, diag_field_requires_tracing_note_);
1304 void NotePartObjectContainsGCRoot(FieldPoint* point) {
1305 FieldDecl* field = point->field();
1306 SourceLocation loc = field->getLocStart();
1307 SourceManager& manager = instance_.getSourceManager();
1308 FullSourceLoc full_loc(loc, manager);
1309 diagnostic_.Report(full_loc, diag_part_object_contains_gc_root_note_)
1310 << field << field->getParent();
1313 void NoteFieldContainsGCRoot(FieldPoint* point) {
1314 NoteField(point, diag_field_contains_gc_root_note_);
1317 void NoteUserDeclaredDestructor(CXXMethodDecl* dtor) {
1318 SourceLocation loc = dtor->getLocStart();
1319 SourceManager& manager = instance_.getSourceManager();
1320 FullSourceLoc full_loc(loc, manager);
1321 diagnostic_.Report(full_loc, diag_user_declared_destructor_note_);
1324 void NoteUserDeclaredFinalizer(CXXMethodDecl* dtor) {
1325 SourceLocation loc = dtor->getLocStart();
1326 SourceManager& manager = instance_.getSourceManager();
1327 FullSourceLoc full_loc(loc, manager);
1328 diagnostic_.Report(full_loc, diag_user_declared_finalizer_note_);
1331 void NoteBaseRequiresFinalization(BasePoint* base) {
1332 SourceLocation loc = base->spec().getLocStart();
1333 SourceManager& manager = instance_.getSourceManager();
1334 FullSourceLoc full_loc(loc, manager);
1335 diagnostic_.Report(full_loc, diag_base_requires_finalization_note_)
1336 << base->info()->record();
1339 void NoteField(FieldPoint* point, unsigned note) {
1340 NoteField(point->field(), note);
1343 void NoteField(FieldDecl* field, unsigned note) {
1344 SourceLocation loc = field->getLocStart();
1345 SourceManager& manager = instance_.getSourceManager();
1346 FullSourceLoc full_loc(loc, manager);
1347 diagnostic_.Report(full_loc, note) << field;
1350 void NoteOverriddenNonVirtualTrace(CXXMethodDecl* overridden) {
1351 SourceLocation loc = overridden->getLocStart();
1352 SourceManager& manager = instance_.getSourceManager();
1353 FullSourceLoc full_loc(loc, manager);
1354 diagnostic_.Report(full_loc, diag_overridden_non_virtual_trace_note_)
1355 << overridden;
1358 unsigned diag_class_must_left_mostly_derive_gc_;
1359 unsigned diag_class_requires_trace_method_;
1360 unsigned diag_base_requires_tracing_;
1361 unsigned diag_fields_require_tracing_;
1362 unsigned diag_class_contains_invalid_fields_;
1363 unsigned diag_class_contains_invalid_fields_warning_;
1364 unsigned diag_class_contains_gc_root_;
1365 unsigned diag_class_requires_finalization_;
1366 unsigned diag_class_does_not_require_finalization_;
1367 unsigned diag_finalizer_accesses_finalized_field_;
1368 unsigned diag_finalizer_eagerly_finalized_field_;
1369 unsigned diag_overridden_non_virtual_trace_;
1370 unsigned diag_missing_trace_dispatch_method_;
1371 unsigned diag_missing_finalize_dispatch_method_;
1372 unsigned diag_virtual_and_manual_dispatch_;
1373 unsigned diag_missing_trace_dispatch_;
1374 unsigned diag_missing_finalize_dispatch_;
1375 unsigned diag_derives_non_stack_allocated_;
1376 unsigned diag_class_overrides_new_;
1377 unsigned diag_class_declares_pure_virtual_trace_;
1378 unsigned diag_left_most_base_must_be_polymorphic_;
1379 unsigned diag_base_class_must_declare_virtual_trace_;
1380 unsigned diag_class_must_declare_gc_mixin_trace_method_;
1382 unsigned diag_base_requires_tracing_note_;
1383 unsigned diag_field_requires_tracing_note_;
1384 unsigned diag_raw_ptr_to_gc_managed_class_note_;
1385 unsigned diag_ref_ptr_to_gc_managed_class_note_;
1386 unsigned diag_reference_ptr_to_gc_managed_class_note_;
1387 unsigned diag_own_ptr_to_gc_managed_class_note_;
1388 unsigned diag_member_to_gc_unmanaged_class_note_;
1389 unsigned diag_stack_allocated_field_note_;
1390 unsigned diag_member_in_unmanaged_class_note_;
1391 unsigned diag_part_object_to_gc_derived_class_note_;
1392 unsigned diag_part_object_contains_gc_root_note_;
1393 unsigned diag_field_contains_gc_root_note_;
1394 unsigned diag_finalized_field_note_;
1395 unsigned diag_eagerly_finalized_field_note_;
1396 unsigned diag_user_declared_destructor_note_;
1397 unsigned diag_user_declared_finalizer_note_;
1398 unsigned diag_base_requires_finalization_note_;
1399 unsigned diag_field_requires_finalization_note_;
1400 unsigned diag_overridden_non_virtual_trace_note_;
1401 unsigned diag_manual_dispatch_method_note_;
1403 CompilerInstance& instance_;
1404 DiagnosticsEngine& diagnostic_;
1405 BlinkGCPluginOptions options_;
1406 RecordCache cache_;
1407 JsonWriter* json_;
1410 class BlinkGCPluginAction : public PluginASTAction {
1411 public:
1412 BlinkGCPluginAction() {}
1414 protected:
1415 // Overridden from PluginASTAction:
1416 virtual std::unique_ptr<ASTConsumer> CreateASTConsumer(
1417 CompilerInstance& instance,
1418 llvm::StringRef ref) {
1419 return llvm::make_unique<BlinkGCPluginConsumer>(instance, options_);
1422 virtual bool ParseArgs(const CompilerInstance& instance,
1423 const std::vector<string>& args) {
1424 bool parsed = true;
1426 for (size_t i = 0; i < args.size() && parsed; ++i) {
1427 if (args[i] == "enable-oilpan") {
1428 options_.enable_oilpan = true;
1429 } else if (args[i] == "dump-graph") {
1430 options_.dump_graph = true;
1431 } else if (args[i] == "warn-raw-ptr") {
1432 options_.warn_raw_ptr = true;
1433 } else if (args[i] == "warn-unneeded-finalizer") {
1434 options_.warn_unneeded_finalizer = true;
1435 } else {
1436 parsed = false;
1437 llvm::errs() << "Unknown blink-gc-plugin argument: " << args[i] << "\n";
1441 return parsed;
1444 private:
1445 BlinkGCPluginOptions options_;
1448 } // namespace
1450 static FrontendPluginRegistry::Add<BlinkGCPluginAction> X(
1451 "blink-gc-plugin",
1452 "Check Blink GC invariants");