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.
4
5#include "BlinkGCPluginConsumer.h"
6
7#include <algorithm>
8#include <set>
9
10#include "CheckDispatchVisitor.h"
11#include "CheckFieldsVisitor.h"
12#include "CheckFinalizerVisitor.h"
13#include "CheckGCRootsVisitor.h"
14#include "CheckTraceVisitor.h"
15#include "CollectVisitor.h"
16#include "JsonWriter.h"
17#include "RecordInfo.h"
18#include "clang/AST/RecursiveASTVisitor.h"
19#include "clang/Sema/Sema.h"
20
21using namespace clang;
22
23namespace {
24
25// Use a local RAV implementation to simply collect all FunctionDecls marked for
26// late template parsing. This happens with the flag -fdelayed-template-parsing,
27// which is on by default in MSVC-compatible mode.
28std::set<FunctionDecl*> GetLateParsedFunctionDecls(TranslationUnitDecl* decl) {
29  struct Visitor : public RecursiveASTVisitor<Visitor> {
30    bool VisitFunctionDecl(FunctionDecl* function_decl) {
31      if (function_decl->isLateTemplateParsed())
32        late_parsed_decls.insert(function_decl);
33      return true;
34    }
35
36    std::set<FunctionDecl*> late_parsed_decls;
37  } v;
38  v.TraverseDecl(decl);
39  return v.late_parsed_decls;
40}
41
42class EmptyStmtVisitor : public RecursiveASTVisitor<EmptyStmtVisitor> {
43 public:
44  static bool isEmpty(Stmt* stmt) {
45    EmptyStmtVisitor visitor;
46    visitor.TraverseStmt(stmt);
47    return visitor.empty_;
48  }
49
50  bool WalkUpFromCompoundStmt(CompoundStmt* stmt) {
51    empty_ = stmt->body_empty();
52    return false;
53  }
54  bool VisitStmt(Stmt*) {
55    empty_ = false;
56    return false;
57  }
58 private:
59  EmptyStmtVisitor() : empty_(true) {}
60  bool empty_;
61};
62
63}  // namespace
64
65BlinkGCPluginConsumer::BlinkGCPluginConsumer(
66    clang::CompilerInstance& instance,
67    const BlinkGCPluginOptions& options)
68    : instance_(instance),
69      reporter_(instance),
70      options_(options),
71      cache_(instance),
72      json_(0) {
73  // Only check structures in the blink and WebKit namespaces.
74  options_.checked_namespaces.insert("blink");
75
76  // Ignore GC implementation files.
77  options_.ignored_directories.push_back("/heap/");
78
79  if (!options_.use_chromium_style_naming)
80    Config::UseLegacyNames();
81}
82
83void BlinkGCPluginConsumer::HandleTranslationUnit(ASTContext& context) {
84  // Don't run the plugin if the compilation unit is already invalid.
85  if (reporter_.hasErrorOccurred())
86    return;
87
88  ParseFunctionTemplates(context.getTranslationUnitDecl());
89
90  CollectVisitor visitor;
91  visitor.TraverseDecl(context.getTranslationUnitDecl());
92
93  if (options_.dump_graph) {
94    std::error_code err;
95    // TODO: Make createDefaultOutputFile or a shorter createOutputFile work.
96    json_ = JsonWriter::from(instance_.createOutputFile(
97        "",                                      // OutputPath
98        err,                                     // Errors
99        true,                                    // Binary
100        true,                                    // RemoveFileOnSignal
101        instance_.getFrontendOpts().OutputFile,  // BaseInput
102        "graph.json",                            // Extension
103        false,                                   // UseTemporary
104        false,                                   // CreateMissingDirectories
105        0,                                       // ResultPathName
106        0));                                     // TempPathName
107    if (!err && json_) {
108      json_->OpenList();
109    } else {
110      json_ = 0;
111      llvm::errs()
112          << "[blink-gc] "
113          << "Failed to create an output file for the object graph.\n";
114    }
115  }
116
117  for (const auto& record : visitor.record_decls())
118    CheckRecord(cache_.Lookup(record));
119
120  for (const auto& method : visitor.trace_decls())
121    CheckTracingMethod(method);
122
123  if (json_) {
124    json_->CloseList();
125    delete json_;
126    json_ = 0;
127  }
128}
129
130void BlinkGCPluginConsumer::ParseFunctionTemplates(TranslationUnitDecl* decl) {
131  if (!instance_.getLangOpts().DelayedTemplateParsing)
132    return;  // Nothing to do.
133
134  std::set<FunctionDecl*> late_parsed_decls = GetLateParsedFunctionDecls(decl);
135  clang::Sema& sema = instance_.getSema();
136
137  for (const FunctionDecl* fd : late_parsed_decls) {
138    assert(fd->isLateTemplateParsed());
139
140    if (!Config::IsTraceMethod(fd))
141      continue;
142
143    if (instance_.getSourceManager().isInSystemHeader(
144            instance_.getSourceManager().getSpellingLoc(fd->getLocation())))
145      continue;
146
147    // Force parsing and AST building of the yet-uninstantiated function
148    // template trace method bodies.
149    clang::LateParsedTemplate* lpt = sema.LateParsedTemplateMap[fd].get();
150    sema.LateTemplateParser(sema.OpaqueParser, *lpt);
151  }
152}
153
154void BlinkGCPluginConsumer::CheckRecord(RecordInfo* info) {
155  if (IsIgnored(info))
156    return;
157
158  CXXRecordDecl* record = info->record();
159
160  // TODO: what should we do to check unions?
161  if (record->isUnion())
162    return;
163
164  // If this is the primary template declaration, check its specializations.
165  if (record->isThisDeclarationADefinition() &&
166      record->getDescribedClassTemplate()) {
167    ClassTemplateDecl* tmpl = record->getDescribedClassTemplate();
168    for (ClassTemplateDecl::spec_iterator it = tmpl->spec_begin();
169         it != tmpl->spec_end();
170         ++it) {
171      CheckClass(cache_.Lookup(*it));
172    }
173    return;
174  }
175
176  CheckClass(info);
177}
178
179void BlinkGCPluginConsumer::CheckClass(RecordInfo* info) {
180  if (!info)
181    return;
182
183  if (CXXMethodDecl* trace = info->GetTraceMethod()) {
184    if (trace->isPure())
185      reporter_.ClassDeclaresPureVirtualTrace(info, trace);
186  } else if (info->RequiresTraceMethod()) {
187    reporter_.ClassRequiresTraceMethod(info);
188  }
189
190  // Check polymorphic classes that are GC-derived or have a trace method.
191  if (info->record()->hasDefinition() && info->record()->isPolymorphic()) {
192    // TODO: Check classes that inherit a trace method.
193    CXXMethodDecl* trace = info->GetTraceMethod();
194    if (trace || info->IsGCDerived())
195      CheckPolymorphicClass(info, trace);
196  }
197
198  {
199    CheckFieldsVisitor visitor;
200    if (visitor.ContainsInvalidFields(info))
201      reporter_.ClassContainsInvalidFields(info, visitor.invalid_fields());
202  }
203
204  if (info->IsGCDerived()) {
205    // It is illegal for a class to be both stack allocated and garbage
206    // collected.
207    if (info->IsStackAllocated()) {
208      for (auto& base : info->GetBases()) {
209        RecordInfo* base_info = base.second.info();
210        if (Config::IsGCBase(base_info->name()) || base_info->IsGCDerived()) {
211          reporter_.StackAllocatedDerivesGarbageCollected(info, &base.second);
212        }
213      }
214    }
215
216    if (!info->IsGCMixin()) {
217      CheckLeftMostDerived(info);
218      CheckDispatch(info);
219      if (CXXMethodDecl* newop = info->DeclaresNewOperator())
220        if (!Config::IsIgnoreAnnotated(newop))
221          reporter_.ClassOverridesNew(info, newop);
222    }
223
224    {
225      CheckGCRootsVisitor visitor;
226      if (visitor.ContainsGCRoots(info))
227        reporter_.ClassContainsGCRoots(info, visitor.gc_roots());
228    }
229
230    if (info->NeedsFinalization())
231      CheckFinalization(info);
232
233    if (options_.warn_unneeded_finalizer && info->IsGCFinalized())
234      CheckUnneededFinalization(info);
235  }
236
237  DumpClass(info);
238}
239
240CXXRecordDecl* BlinkGCPluginConsumer::GetDependentTemplatedDecl(
241    const Type& type) {
242  const TemplateSpecializationType* tmpl_type =
243      type.getAs<TemplateSpecializationType>();
244  if (!tmpl_type)
245    return 0;
246
247  TemplateDecl* tmpl_decl = tmpl_type->getTemplateName().getAsTemplateDecl();
248  if (!tmpl_decl)
249    return 0;
250
251  return dyn_cast<CXXRecordDecl>(tmpl_decl->getTemplatedDecl());
252}
253
254// The GC infrastructure assumes that if the vtable of a polymorphic
255// base-class is not initialized for a given object (ie, it is partially
256// initialized) then the object does not need to be traced. Thus, we must
257// ensure that any polymorphic class with a trace method does not have any
258// tractable fields that are initialized before we are sure that the vtable
259// and the trace method are both defined.  There are two cases that need to
260// hold to satisfy that assumption:
261//
262// 1. If trace is virtual, then it must be defined in the left-most base.
263// This ensures that if the vtable is initialized then it contains a pointer
264// to the trace method.
265//
266// 2. If trace is non-virtual, then the trace method is defined and we must
267// ensure that the left-most base defines a vtable. This ensures that the
268// first thing to be initialized when constructing the object is the vtable
269// itself.
270void BlinkGCPluginConsumer::CheckPolymorphicClass(
271    RecordInfo* info,
272    CXXMethodDecl* trace) {
273  CXXRecordDecl* left_most = info->record();
274  CXXRecordDecl::base_class_iterator it = left_most->bases_begin();
275  CXXRecordDecl* left_most_base = 0;
276  while (it != left_most->bases_end()) {
277    left_most_base = it->getType()->getAsCXXRecordDecl();
278    if (!left_most_base && it->getType()->isDependentType())
279      left_most_base = RecordInfo::GetDependentTemplatedDecl(*it->getType());
280
281    // TODO: Find a way to correctly check actual instantiations
282    // for dependent types. The escape below will be hit, eg, when
283    // we have a primary template with no definition and
284    // specializations for each case (such as SupplementBase) in
285    // which case we don't succeed in checking the required
286    // properties.
287    if (!left_most_base || !left_most_base->hasDefinition())
288      return;
289
290    StringRef name = left_most_base->getName();
291    // We know GCMixin base defines virtual trace.
292    if (Config::IsGCMixinBase(name))
293      return;
294
295    // Stop with the left-most prior to a safe polymorphic base (a safe base
296    // is non-polymorphic and contains no fields).
297    if (Config::IsSafePolymorphicBase(name))
298      break;
299
300    left_most = left_most_base;
301    it = left_most->bases_begin();
302  }
303
304  if (RecordInfo* left_most_info = cache_.Lookup(left_most)) {
305    // Check condition (1):
306    if (trace && trace->isVirtual()) {
307      if (CXXMethodDecl* trace = left_most_info->GetTraceMethod()) {
308        if (trace->isVirtual())
309          return;
310      }
311      reporter_.BaseClassMustDeclareVirtualTrace(info, left_most);
312      return;
313    }
314
315    // Check condition (2):
316    if (DeclaresVirtualMethods(left_most))
317      return;
318    if (left_most_base) {
319      // Get the base next to the "safe polymorphic base"
320      if (it != left_most->bases_end())
321        ++it;
322      if (it != left_most->bases_end()) {
323        if (CXXRecordDecl* next_base = it->getType()->getAsCXXRecordDecl()) {
324          if (CXXRecordDecl* next_left_most = GetLeftMostBase(next_base)) {
325            if (DeclaresVirtualMethods(next_left_most))
326              return;
327            reporter_.LeftMostBaseMustBePolymorphic(info, next_left_most);
328            return;
329          }
330        }
331      }
332    }
333    reporter_.LeftMostBaseMustBePolymorphic(info, left_most);
334  }
335}
336
337CXXRecordDecl* BlinkGCPluginConsumer::GetLeftMostBase(
338    CXXRecordDecl* left_most) {
339  CXXRecordDecl::base_class_iterator it = left_most->bases_begin();
340  while (it != left_most->bases_end()) {
341    if (it->getType()->isDependentType())
342      left_most = RecordInfo::GetDependentTemplatedDecl(*it->getType());
343    else
344      left_most = it->getType()->getAsCXXRecordDecl();
345    if (!left_most || !left_most->hasDefinition())
346      return 0;
347    it = left_most->bases_begin();
348  }
349  return left_most;
350}
351
352bool BlinkGCPluginConsumer::DeclaresVirtualMethods(CXXRecordDecl* decl) {
353  CXXRecordDecl::method_iterator it = decl->method_begin();
354  for (; it != decl->method_end(); ++it)
355    if (it->isVirtual() && !it->isPure())
356      return true;
357  return false;
358}
359
360void BlinkGCPluginConsumer::CheckLeftMostDerived(RecordInfo* info) {
361  CXXRecordDecl* left_most = GetLeftMostBase(info->record());
362  if (!left_most)
363    return;
364  if (!Config::IsGCBase(left_most->getName()))
365    reporter_.ClassMustLeftMostlyDeriveGC(info);
366}
367
368void BlinkGCPluginConsumer::CheckDispatch(RecordInfo* info) {
369  bool finalized = info->IsGCFinalized();
370  CXXMethodDecl* trace_dispatch = info->GetTraceDispatchMethod();
371  CXXMethodDecl* finalize_dispatch = info->GetFinalizeDispatchMethod();
372  if (!trace_dispatch && !finalize_dispatch)
373    return;
374
375  CXXRecordDecl* base = trace_dispatch ? trace_dispatch->getParent()
376                                       : finalize_dispatch->getParent();
377
378  // Check that dispatch methods are defined at the base.
379  if (base == info->record()) {
380    if (!trace_dispatch)
381      reporter_.MissingTraceDispatchMethod(info);
382    if (finalized && !finalize_dispatch)
383      reporter_.MissingFinalizeDispatchMethod(info);
384    if (!finalized && finalize_dispatch) {
385      reporter_.ClassRequiresFinalization(info);
386      reporter_.NoteUserDeclaredFinalizer(finalize_dispatch);
387    }
388  }
389
390  // Check that classes implementing manual dispatch do not have vtables.
391  if (info->record()->isPolymorphic()) {
392    reporter_.VirtualAndManualDispatch(
393        info, trace_dispatch ? trace_dispatch : finalize_dispatch);
394  }
395
396  // If this is a non-abstract class check that it is dispatched to.
397  // TODO: Create a global variant of this local check. We can only check if
398  // the dispatch body is known in this compilation unit.
399  if (info->IsConsideredAbstract())
400    return;
401
402  const FunctionDecl* defn;
403
404  if (trace_dispatch && trace_dispatch->isDefined(defn)) {
405    CheckDispatchVisitor visitor(info);
406    visitor.TraverseStmt(defn->getBody());
407    if (!visitor.dispatched_to_receiver())
408      reporter_.MissingTraceDispatch(defn, info);
409  }
410
411  if (finalized && finalize_dispatch && finalize_dispatch->isDefined(defn)) {
412    CheckDispatchVisitor visitor(info);
413    visitor.TraverseStmt(defn->getBody());
414    if (!visitor.dispatched_to_receiver())
415      reporter_.MissingFinalizeDispatch(defn, info);
416  }
417}
418
419// TODO: Should we collect destructors similar to trace methods?
420void BlinkGCPluginConsumer::CheckFinalization(RecordInfo* info) {
421  CXXDestructorDecl* dtor = info->record()->getDestructor();
422
423  // For finalized classes, check the finalization method if possible.
424  if (info->IsGCFinalized()) {
425    if (dtor && dtor->hasBody()) {
426      CheckFinalizerVisitor visitor(&cache_, info->IsEagerlyFinalized());
427      visitor.TraverseCXXMethodDecl(dtor);
428      if (!visitor.finalized_fields().empty()) {
429        reporter_.FinalizerAccessesFinalizedFields(
430            dtor, visitor.finalized_fields());
431      }
432    }
433    return;
434  }
435
436  // Don't require finalization of a mixin that has not yet been "mixed in".
437  if (info->IsGCMixin())
438    return;
439
440  // Report the finalization error, and proceed to print possible causes for
441  // the finalization requirement.
442  reporter_.ClassRequiresFinalization(info);
443
444  if (dtor && dtor->isUserProvided())
445    reporter_.NoteUserDeclaredDestructor(dtor);
446
447  for (auto& base : info->GetBases())
448    if (base.second.info()->NeedsFinalization())
449      reporter_.NoteBaseRequiresFinalization(&base.second);
450
451  for (auto& field : info->GetFields())
452    if (field.second.edge()->NeedsFinalization())
453      reporter_.NoteFieldRequiresFinalization(&field.second);
454}
455
456void BlinkGCPluginConsumer::CheckUnneededFinalization(RecordInfo* info) {
457  if (!HasNonEmptyFinalizer(info))
458    reporter_.ClassDoesNotRequireFinalization(info);
459}
460
461bool BlinkGCPluginConsumer::HasNonEmptyFinalizer(RecordInfo* info) {
462  CXXDestructorDecl* dtor = info->record()->getDestructor();
463
464  // If the destructor is virtual (or one of the bases are by way of the
465  // recursive call below), consider this class as having a non-empty
466  // finalizer. Not doing so runs counter to standard C++ reflexes like
467  //
468  //   class A : public GarbageCollectedMixin {
469  //   public:
470  //     virtual ~A() { };
471  //     virtual void f() = 0;
472  //   };
473  //   class B : public GarbageCollectedFinalized<B>, public A {
474  //     USING_GARBAGE_COLLECTED_MIXIN(B);
475  //   public:
476  //     ~B() override { }
477  //     void f() override { }
478  //   };
479  //
480  // and it is considered a step too far to report a warning for such
481  // explicit usage of empty destructors.
482  if (dtor && dtor->isVirtual())
483      return true;
484
485  if (dtor && dtor->isUserProvided()) {
486    if (!dtor->hasBody() || !EmptyStmtVisitor::isEmpty(dtor->getBody()))
487      return true;
488  }
489
490  if (info->GetFinalizeDispatchMethod())
491    return true;
492
493  for (auto& base : info->GetBases())
494    if (HasNonEmptyFinalizer(base.second.info()))
495      return true;
496
497  for (auto& field : info->GetFields())
498    if (field.second.edge()->NeedsFinalization())
499      return true;
500
501  return false;
502}
503
504void BlinkGCPluginConsumer::CheckTracingMethod(CXXMethodDecl* method) {
505  RecordInfo* parent = cache_.Lookup(method->getParent());
506  if (IsIgnored(parent))
507    return;
508
509  // Check templated tracing methods by checking the template instantiations.
510  // Specialized templates are handled as ordinary classes.
511  if (ClassTemplateDecl* tmpl =
512      parent->record()->getDescribedClassTemplate()) {
513    for (ClassTemplateDecl::spec_iterator it = tmpl->spec_begin();
514         it != tmpl->spec_end();
515         ++it) {
516      // Check trace using each template instantiation as the holder.
517      if (Config::IsTemplateInstantiation(*it))
518        CheckTraceOrDispatchMethod(cache_.Lookup(*it), method);
519    }
520    return;
521  }
522
523  CheckTraceOrDispatchMethod(parent, method);
524}
525
526void BlinkGCPluginConsumer::CheckTraceOrDispatchMethod(
527    RecordInfo* parent,
528    CXXMethodDecl* method) {
529  Config::TraceMethodType trace_type = Config::GetTraceMethodType(method);
530  if (trace_type == Config::TRACE_AFTER_DISPATCH_METHOD ||
531      trace_type == Config::TRACE_AFTER_DISPATCH_IMPL_METHOD ||
532      !parent->GetTraceDispatchMethod()) {
533    CheckTraceMethod(parent, method, trace_type);
534  }
535  // Dispatch methods are checked when we identify subclasses.
536}
537
538void BlinkGCPluginConsumer::CheckTraceMethod(
539    RecordInfo* parent,
540    CXXMethodDecl* trace,
541    Config::TraceMethodType trace_type) {
542  // A trace method must not override any non-virtual trace methods.
543  if (trace_type == Config::TRACE_METHOD) {
544    for (auto& base : parent->GetBases())
545      if (CXXMethodDecl* other = base.second.info()->InheritsNonVirtualTrace())
546        reporter_.OverriddenNonVirtualTrace(parent, trace, other);
547  }
548
549  CheckTraceVisitor visitor(trace, parent, &cache_);
550  visitor.TraverseCXXMethodDecl(trace);
551
552  // Skip reporting if this trace method is a just delegate to
553  // traceImpl (or traceAfterDispatchImpl) method. We will report on
554  // CheckTraceMethod on traceImpl method.
555  if (visitor.delegates_to_traceimpl())
556    return;
557
558  for (auto& base : parent->GetBases())
559    if (!base.second.IsProperlyTraced())
560      reporter_.BaseRequiresTracing(parent, trace, base.first);
561
562  for (auto& field : parent->GetFields()) {
563    if (!field.second.IsProperlyTraced() ||
564        field.second.IsInproperlyTraced()) {
565      // Report one or more tracing-related field errors.
566      reporter_.FieldsImproperlyTraced(parent, trace);
567      break;
568    }
569  }
570}
571
572void BlinkGCPluginConsumer::DumpClass(RecordInfo* info) {
573  if (!json_)
574    return;
575
576  json_->OpenObject();
577  json_->Write("name", info->record()->getQualifiedNameAsString());
578  json_->Write("loc", GetLocString(info->record()->getLocStart()));
579  json_->CloseObject();
580
581  class DumpEdgeVisitor : public RecursiveEdgeVisitor {
582   public:
583    DumpEdgeVisitor(JsonWriter* json) : json_(json) {}
584    void DumpEdge(RecordInfo* src,
585                  RecordInfo* dst,
586                  const std::string& lbl,
587                  const Edge::LivenessKind& kind,
588                  const std::string& loc) {
589      json_->OpenObject();
590      json_->Write("src", src->record()->getQualifiedNameAsString());
591      json_->Write("dst", dst->record()->getQualifiedNameAsString());
592      json_->Write("lbl", lbl);
593      json_->Write("kind", kind);
594      json_->Write("loc", loc);
595      json_->Write("ptr",
596                   !Parent() ? "val" :
597                   Parent()->IsRawPtr() ?
598                       (static_cast<RawPtr*>(Parent())->HasReferenceType() ?
599                        "reference" : "raw") :
600                   Parent()->IsRefPtr() ? "ref" :
601                   Parent()->IsOwnPtr() ? "own" :
602                   Parent()->IsUniquePtr() ? "unique" :
603                   (Parent()->IsMember() || Parent()->IsWeakMember()) ? "mem" :
604                   "val");
605      json_->CloseObject();
606    }
607
608    void DumpField(RecordInfo* src, FieldPoint* point, const std::string& loc) {
609      src_ = src;
610      point_ = point;
611      loc_ = loc;
612      point_->edge()->Accept(this);
613    }
614
615    void AtValue(Value* e) override {
616      // The liveness kind of a path from the point to this value
617      // is given by the innermost place that is non-strong.
618      Edge::LivenessKind kind = Edge::kStrong;
619      if (Config::IsIgnoreCycleAnnotated(point_->field())) {
620        kind = Edge::kWeak;
621      } else {
622        for (Context::iterator it = context().begin();
623             it != context().end();
624             ++it) {
625          Edge::LivenessKind pointer_kind = (*it)->Kind();
626          if (pointer_kind != Edge::kStrong) {
627            kind = pointer_kind;
628            break;
629          }
630        }
631      }
632      DumpEdge(
633          src_, e->value(), point_->field()->getNameAsString(), kind, loc_);
634    }
635
636   private:
637    JsonWriter* json_;
638    RecordInfo* src_;
639    FieldPoint* point_;
640    std::string loc_;
641  };
642
643  DumpEdgeVisitor visitor(json_);
644
645  for (auto& base : info->GetBases())
646    visitor.DumpEdge(info,
647                     base.second.info(),
648                     "<super>",
649                     Edge::kStrong,
650                     GetLocString(base.second.spec().getLocStart()));
651
652  for (auto& field : info->GetFields())
653    visitor.DumpField(info,
654                      &field.second,
655                      GetLocString(field.second.field()->getLocStart()));
656}
657
658std::string BlinkGCPluginConsumer::GetLocString(SourceLocation loc) {
659  const SourceManager& source_manager = instance_.getSourceManager();
660  PresumedLoc ploc = source_manager.getPresumedLoc(loc);
661  if (ploc.isInvalid())
662    return "";
663  std::string loc_str;
664  llvm::raw_string_ostream os(loc_str);
665  os << ploc.getFilename()
666     << ":" << ploc.getLine()
667     << ":" << ploc.getColumn();
668  return os.str();
669}
670
671bool BlinkGCPluginConsumer::IsIgnored(RecordInfo* record) {
672  return (!record ||
673          !InCheckedNamespace(record) ||
674          IsIgnoredClass(record) ||
675          InIgnoredDirectory(record));
676}
677
678bool BlinkGCPluginConsumer::IsIgnoredClass(RecordInfo* info) {
679  // Ignore any class prefixed by SameSizeAs. These are used in
680  // Blink to verify class sizes and don't need checking.
681  const std::string SameSizeAs = "SameSizeAs";
682  if (info->name().compare(0, SameSizeAs.size(), SameSizeAs) == 0)
683    return true;
684  return (options_.ignored_classes.find(info->name()) !=
685          options_.ignored_classes.end());
686}
687
688bool BlinkGCPluginConsumer::InIgnoredDirectory(RecordInfo* info) {
689  std::string filename;
690  if (!GetFilename(info->record()->getLocStart(), &filename))
691    return false;  // TODO: should we ignore non-existing file locations?
692#if defined(LLVM_ON_WIN32)
693  std::replace(filename.begin(), filename.end(), '\\', '/');
694#endif
695  for (const auto& dir : options_.ignored_directories)
696    if (filename.find(dir) != std::string::npos)
697      return true;
698  return false;
699}
700
701bool BlinkGCPluginConsumer::InCheckedNamespace(RecordInfo* info) {
702  if (!info)
703    return false;
704  for (DeclContext* context = info->record()->getDeclContext();
705       !context->isTranslationUnit();
706       context = context->getParent()) {
707    if (NamespaceDecl* decl = dyn_cast<NamespaceDecl>(context)) {
708      if (decl->isAnonymousNamespace())
709        return true;
710      if (options_.checked_namespaces.find(decl->getNameAsString()) !=
711          options_.checked_namespaces.end()) {
712        return true;
713      }
714    }
715  }
716  return false;
717}
718
719bool BlinkGCPluginConsumer::GetFilename(SourceLocation loc,
720                                        std::string* filename) {
721  const SourceManager& source_manager = instance_.getSourceManager();
722  SourceLocation spelling_location = source_manager.getSpellingLoc(loc);
723  PresumedLoc ploc = source_manager.getPresumedLoc(spelling_location);
724  if (ploc.isInvalid()) {
725    // If we're in an invalid location, we're looking at things that aren't
726    // actually stated in the source.
727    return false;
728  }
729  *filename = ploc.getFilename();
730  return true;
731}
732