OLD | NEW |
(Empty) | |
| 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 "CheckTraceVisitor.h" |
| 12 #include "CollectVisitor.h" |
| 13 #include "JsonWriter.h" |
| 14 #include "RecordInfo.h" |
| 15 #include "clang/AST/RecursiveASTVisitor.h" |
| 16 #include "clang/Sema/Sema.h" |
| 17 |
| 18 using namespace clang; |
| 19 |
| 20 namespace { |
| 21 |
| 22 const char kClassMustLeftMostlyDeriveGC[] = |
| 23 "[blink-gc] Class %0 must derive its GC base in the left-most position."; |
| 24 |
| 25 const char kClassRequiresTraceMethod[] = |
| 26 "[blink-gc] Class %0 requires a trace method."; |
| 27 |
| 28 const char kBaseRequiresTracing[] = |
| 29 "[blink-gc] Base class %0 of derived class %1 requires tracing."; |
| 30 |
| 31 const char kBaseRequiresTracingNote[] = |
| 32 "[blink-gc] Untraced base class %0 declared here:"; |
| 33 |
| 34 const char kFieldsRequireTracing[] = |
| 35 "[blink-gc] Class %0 has untraced fields that require tracing."; |
| 36 |
| 37 const char kFieldRequiresTracingNote[] = |
| 38 "[blink-gc] Untraced field %0 declared here:"; |
| 39 |
| 40 const char kClassContainsInvalidFields[] = |
| 41 "[blink-gc] Class %0 contains invalid fields."; |
| 42 |
| 43 const char kClassContainsGCRoot[] = |
| 44 "[blink-gc] Class %0 contains GC root in field %1."; |
| 45 |
| 46 const char kClassRequiresFinalization[] = |
| 47 "[blink-gc] Class %0 requires finalization."; |
| 48 |
| 49 const char kClassDoesNotRequireFinalization[] = |
| 50 "[blink-gc] Class %0 may not require finalization."; |
| 51 |
| 52 const char kFinalizerAccessesFinalizedField[] = |
| 53 "[blink-gc] Finalizer %0 accesses potentially finalized field %1."; |
| 54 |
| 55 const char kFinalizerAccessesEagerlyFinalizedField[] = |
| 56 "[blink-gc] Finalizer %0 accesses eagerly finalized field %1."; |
| 57 |
| 58 const char kRawPtrToGCManagedClassNote[] = |
| 59 "[blink-gc] Raw pointer field %0 to a GC managed class declared here:"; |
| 60 |
| 61 const char kRefPtrToGCManagedClassNote[] = |
| 62 "[blink-gc] RefPtr field %0 to a GC managed class declared here:"; |
| 63 |
| 64 const char kReferencePtrToGCManagedClassNote[] = |
| 65 "[blink-gc] Reference pointer field %0 to a GC managed class" |
| 66 " declared here:"; |
| 67 |
| 68 const char kOwnPtrToGCManagedClassNote[] = |
| 69 "[blink-gc] OwnPtr field %0 to a GC managed class declared here:"; |
| 70 |
| 71 const char kMemberToGCUnmanagedClassNote[] = |
| 72 "[blink-gc] Member field %0 to non-GC managed class declared here:"; |
| 73 |
| 74 const char kStackAllocatedFieldNote[] = |
| 75 "[blink-gc] Stack-allocated field %0 declared here:"; |
| 76 |
| 77 const char kMemberInUnmanagedClassNote[] = |
| 78 "[blink-gc] Member field %0 in unmanaged class declared here:"; |
| 79 |
| 80 const char kPartObjectToGCDerivedClassNote[] = |
| 81 "[blink-gc] Part-object field %0 to a GC derived class declared here:"; |
| 82 |
| 83 const char kPartObjectContainsGCRootNote[] = |
| 84 "[blink-gc] Field %0 with embedded GC root in %1 declared here:"; |
| 85 |
| 86 const char kFieldContainsGCRootNote[] = |
| 87 "[blink-gc] Field %0 defining a GC root declared here:"; |
| 88 |
| 89 const char kOverriddenNonVirtualTrace[] = |
| 90 "[blink-gc] Class %0 overrides non-virtual trace of base class %1."; |
| 91 |
| 92 const char kOverriddenNonVirtualTraceNote[] = |
| 93 "[blink-gc] Non-virtual trace method declared here:"; |
| 94 |
| 95 const char kMissingTraceDispatchMethod[] = |
| 96 "[blink-gc] Class %0 is missing manual trace dispatch."; |
| 97 |
| 98 const char kMissingFinalizeDispatchMethod[] = |
| 99 "[blink-gc] Class %0 is missing manual finalize dispatch."; |
| 100 |
| 101 const char kVirtualAndManualDispatch[] = |
| 102 "[blink-gc] Class %0 contains or inherits virtual methods" |
| 103 " but implements manual dispatching."; |
| 104 |
| 105 const char kMissingTraceDispatch[] = |
| 106 "[blink-gc] Missing dispatch to class %0 in manual trace dispatch."; |
| 107 |
| 108 const char kMissingFinalizeDispatch[] = |
| 109 "[blink-gc] Missing dispatch to class %0 in manual finalize dispatch."; |
| 110 |
| 111 const char kFinalizedFieldNote[] = |
| 112 "[blink-gc] Potentially finalized field %0 declared here:"; |
| 113 |
| 114 const char kEagerlyFinalizedFieldNote[] = |
| 115 "[blink-gc] Field %0 having eagerly finalized value, declared here:"; |
| 116 |
| 117 const char kUserDeclaredDestructorNote[] = |
| 118 "[blink-gc] User-declared destructor declared here:"; |
| 119 |
| 120 const char kUserDeclaredFinalizerNote[] = |
| 121 "[blink-gc] User-declared finalizer declared here:"; |
| 122 |
| 123 const char kBaseRequiresFinalizationNote[] = |
| 124 "[blink-gc] Base class %0 requiring finalization declared here:"; |
| 125 |
| 126 const char kFieldRequiresFinalizationNote[] = |
| 127 "[blink-gc] Field %0 requiring finalization declared here:"; |
| 128 |
| 129 const char kManualDispatchMethodNote[] = |
| 130 "[blink-gc] Manual dispatch %0 declared here:"; |
| 131 |
| 132 const char kDerivesNonStackAllocated[] = |
| 133 "[blink-gc] Stack-allocated class %0 derives class %1" |
| 134 " which is not stack allocated."; |
| 135 |
| 136 const char kClassOverridesNew[] = |
| 137 "[blink-gc] Garbage collected class %0" |
| 138 " is not permitted to override its new operator."; |
| 139 |
| 140 const char kClassDeclaresPureVirtualTrace[] = |
| 141 "[blink-gc] Garbage collected class %0" |
| 142 " is not permitted to declare a pure-virtual trace method."; |
| 143 |
| 144 const char kLeftMostBaseMustBePolymorphic[] = |
| 145 "[blink-gc] Left-most base class %0 of derived class %1" |
| 146 " must be polymorphic."; |
| 147 |
| 148 const char kBaseClassMustDeclareVirtualTrace[] = |
| 149 "[blink-gc] Left-most base class %0 of derived class %1" |
| 150 " must define a virtual trace method."; |
| 151 |
| 152 const char kClassMustDeclareGCMixinTraceMethod[] = |
| 153 "[blink-gc] Class %0 which inherits from GarbageCollectedMixin must" |
| 154 " locally declare and override trace(Visitor*)"; |
| 155 |
| 156 // Use a local RAV implementation to simply collect all FunctionDecls marked for |
| 157 // late template parsing. This happens with the flag -fdelayed-template-parsing, |
| 158 // which is on by default in MSVC-compatible mode. |
| 159 std::set<FunctionDecl*> GetLateParsedFunctionDecls(TranslationUnitDecl* decl) { |
| 160 struct Visitor : public RecursiveASTVisitor<Visitor> { |
| 161 bool VisitFunctionDecl(FunctionDecl* function_decl) { |
| 162 if (function_decl->isLateTemplateParsed()) |
| 163 late_parsed_decls.insert(function_decl); |
| 164 return true; |
| 165 } |
| 166 |
| 167 std::set<FunctionDecl*> late_parsed_decls; |
| 168 } v; |
| 169 v.TraverseDecl(decl); |
| 170 return v.late_parsed_decls; |
| 171 } |
| 172 |
| 173 class EmptyStmtVisitor : public RecursiveASTVisitor<EmptyStmtVisitor> { |
| 174 public: |
| 175 static bool isEmpty(Stmt* stmt) { |
| 176 EmptyStmtVisitor visitor; |
| 177 visitor.TraverseStmt(stmt); |
| 178 return visitor.empty_; |
| 179 } |
| 180 |
| 181 bool WalkUpFromCompoundStmt(CompoundStmt* stmt) { |
| 182 empty_ = stmt->body_empty(); |
| 183 return false; |
| 184 } |
| 185 bool VisitStmt(Stmt*) { |
| 186 empty_ = false; |
| 187 return false; |
| 188 } |
| 189 private: |
| 190 EmptyStmtVisitor() : empty_(true) {} |
| 191 bool empty_; |
| 192 }; |
| 193 |
| 194 } // namespace |
| 195 |
| 196 BlinkGCPluginConsumer::BlinkGCPluginConsumer( |
| 197 clang::CompilerInstance& instance, |
| 198 const BlinkGCPluginOptions& options) |
| 199 : instance_(instance), |
| 200 diagnostic_(instance.getDiagnostics()), |
| 201 options_(options), |
| 202 json_(0) { |
| 203 // Only check structures in the blink and WebKit namespaces. |
| 204 options_.checked_namespaces.insert("blink"); |
| 205 |
| 206 // Ignore GC implementation files. |
| 207 options_.ignored_directories.push_back("/heap/"); |
| 208 |
| 209 // Register warning/error messages. |
| 210 diag_class_must_left_mostly_derive_gc_ = diagnostic_.getCustomDiagID( |
| 211 getErrorLevel(), kClassMustLeftMostlyDeriveGC); |
| 212 diag_class_requires_trace_method_ = |
| 213 diagnostic_.getCustomDiagID(getErrorLevel(), kClassRequiresTraceMethod); |
| 214 diag_base_requires_tracing_ = |
| 215 diagnostic_.getCustomDiagID(getErrorLevel(), kBaseRequiresTracing); |
| 216 diag_fields_require_tracing_ = |
| 217 diagnostic_.getCustomDiagID(getErrorLevel(), kFieldsRequireTracing); |
| 218 diag_class_contains_invalid_fields_ = diagnostic_.getCustomDiagID( |
| 219 getErrorLevel(), kClassContainsInvalidFields); |
| 220 diag_class_contains_invalid_fields_warning_ = diagnostic_.getCustomDiagID( |
| 221 DiagnosticsEngine::Warning, kClassContainsInvalidFields); |
| 222 diag_class_contains_gc_root_ = |
| 223 diagnostic_.getCustomDiagID(getErrorLevel(), kClassContainsGCRoot); |
| 224 diag_class_requires_finalization_ = diagnostic_.getCustomDiagID( |
| 225 getErrorLevel(), kClassRequiresFinalization); |
| 226 diag_class_does_not_require_finalization_ = diagnostic_.getCustomDiagID( |
| 227 DiagnosticsEngine::Warning, kClassDoesNotRequireFinalization); |
| 228 diag_finalizer_accesses_finalized_field_ = diagnostic_.getCustomDiagID( |
| 229 getErrorLevel(), kFinalizerAccessesFinalizedField); |
| 230 diag_finalizer_eagerly_finalized_field_ = diagnostic_.getCustomDiagID( |
| 231 getErrorLevel(), kFinalizerAccessesEagerlyFinalizedField); |
| 232 diag_overridden_non_virtual_trace_ = diagnostic_.getCustomDiagID( |
| 233 getErrorLevel(), kOverriddenNonVirtualTrace); |
| 234 diag_missing_trace_dispatch_method_ = diagnostic_.getCustomDiagID( |
| 235 getErrorLevel(), kMissingTraceDispatchMethod); |
| 236 diag_missing_finalize_dispatch_method_ = diagnostic_.getCustomDiagID( |
| 237 getErrorLevel(), kMissingFinalizeDispatchMethod); |
| 238 diag_virtual_and_manual_dispatch_ = |
| 239 diagnostic_.getCustomDiagID(getErrorLevel(), kVirtualAndManualDispatch); |
| 240 diag_missing_trace_dispatch_ = |
| 241 diagnostic_.getCustomDiagID(getErrorLevel(), kMissingTraceDispatch); |
| 242 diag_missing_finalize_dispatch_ = |
| 243 diagnostic_.getCustomDiagID(getErrorLevel(), kMissingFinalizeDispatch); |
| 244 diag_derives_non_stack_allocated_ = |
| 245 diagnostic_.getCustomDiagID(getErrorLevel(), kDerivesNonStackAllocated); |
| 246 diag_class_overrides_new_ = |
| 247 diagnostic_.getCustomDiagID(getErrorLevel(), kClassOverridesNew); |
| 248 diag_class_declares_pure_virtual_trace_ = diagnostic_.getCustomDiagID( |
| 249 getErrorLevel(), kClassDeclaresPureVirtualTrace); |
| 250 diag_left_most_base_must_be_polymorphic_ = diagnostic_.getCustomDiagID( |
| 251 getErrorLevel(), kLeftMostBaseMustBePolymorphic); |
| 252 diag_base_class_must_declare_virtual_trace_ = diagnostic_.getCustomDiagID( |
| 253 getErrorLevel(), kBaseClassMustDeclareVirtualTrace); |
| 254 diag_class_must_declare_gc_mixin_trace_method_ = |
| 255 diagnostic_.getCustomDiagID(getErrorLevel(), |
| 256 kClassMustDeclareGCMixinTraceMethod); |
| 257 |
| 258 // Register note messages. |
| 259 diag_base_requires_tracing_note_ = diagnostic_.getCustomDiagID( |
| 260 DiagnosticsEngine::Note, kBaseRequiresTracingNote); |
| 261 diag_field_requires_tracing_note_ = diagnostic_.getCustomDiagID( |
| 262 DiagnosticsEngine::Note, kFieldRequiresTracingNote); |
| 263 diag_raw_ptr_to_gc_managed_class_note_ = diagnostic_.getCustomDiagID( |
| 264 DiagnosticsEngine::Note, kRawPtrToGCManagedClassNote); |
| 265 diag_ref_ptr_to_gc_managed_class_note_ = diagnostic_.getCustomDiagID( |
| 266 DiagnosticsEngine::Note, kRefPtrToGCManagedClassNote); |
| 267 diag_reference_ptr_to_gc_managed_class_note_ = diagnostic_.getCustomDiagID( |
| 268 DiagnosticsEngine::Note, kReferencePtrToGCManagedClassNote); |
| 269 diag_own_ptr_to_gc_managed_class_note_ = diagnostic_.getCustomDiagID( |
| 270 DiagnosticsEngine::Note, kOwnPtrToGCManagedClassNote); |
| 271 diag_member_to_gc_unmanaged_class_note_ = diagnostic_.getCustomDiagID( |
| 272 DiagnosticsEngine::Note, kMemberToGCUnmanagedClassNote); |
| 273 diag_stack_allocated_field_note_ = diagnostic_.getCustomDiagID( |
| 274 DiagnosticsEngine::Note, kStackAllocatedFieldNote); |
| 275 diag_member_in_unmanaged_class_note_ = diagnostic_.getCustomDiagID( |
| 276 DiagnosticsEngine::Note, kMemberInUnmanagedClassNote); |
| 277 diag_part_object_to_gc_derived_class_note_ = diagnostic_.getCustomDiagID( |
| 278 DiagnosticsEngine::Note, kPartObjectToGCDerivedClassNote); |
| 279 diag_part_object_contains_gc_root_note_ = diagnostic_.getCustomDiagID( |
| 280 DiagnosticsEngine::Note, kPartObjectContainsGCRootNote); |
| 281 diag_field_contains_gc_root_note_ = diagnostic_.getCustomDiagID( |
| 282 DiagnosticsEngine::Note, kFieldContainsGCRootNote); |
| 283 diag_finalized_field_note_ = diagnostic_.getCustomDiagID( |
| 284 DiagnosticsEngine::Note, kFinalizedFieldNote); |
| 285 diag_eagerly_finalized_field_note_ = diagnostic_.getCustomDiagID( |
| 286 DiagnosticsEngine::Note, kEagerlyFinalizedFieldNote); |
| 287 diag_user_declared_destructor_note_ = diagnostic_.getCustomDiagID( |
| 288 DiagnosticsEngine::Note, kUserDeclaredDestructorNote); |
| 289 diag_user_declared_finalizer_note_ = diagnostic_.getCustomDiagID( |
| 290 DiagnosticsEngine::Note, kUserDeclaredFinalizerNote); |
| 291 diag_base_requires_finalization_note_ = diagnostic_.getCustomDiagID( |
| 292 DiagnosticsEngine::Note, kBaseRequiresFinalizationNote); |
| 293 diag_field_requires_finalization_note_ = diagnostic_.getCustomDiagID( |
| 294 DiagnosticsEngine::Note, kFieldRequiresFinalizationNote); |
| 295 diag_overridden_non_virtual_trace_note_ = diagnostic_.getCustomDiagID( |
| 296 DiagnosticsEngine::Note, kOverriddenNonVirtualTraceNote); |
| 297 diag_manual_dispatch_method_note_ = diagnostic_.getCustomDiagID( |
| 298 DiagnosticsEngine::Note, kManualDispatchMethodNote); |
| 299 } |
| 300 |
| 301 void BlinkGCPluginConsumer::HandleTranslationUnit(ASTContext& context) { |
| 302 // Don't run the plugin if the compilation unit is already invalid. |
| 303 if (diagnostic_.hasErrorOccurred()) |
| 304 return; |
| 305 |
| 306 ParseFunctionTemplates(context.getTranslationUnitDecl()); |
| 307 |
| 308 CollectVisitor visitor; |
| 309 visitor.TraverseDecl(context.getTranslationUnitDecl()); |
| 310 |
| 311 if (options_.dump_graph) { |
| 312 std::error_code err; |
| 313 // TODO: Make createDefaultOutputFile or a shorter createOutputFile work. |
| 314 json_ = JsonWriter::from(instance_.createOutputFile( |
| 315 "", // OutputPath |
| 316 err, // Errors |
| 317 true, // Binary |
| 318 true, // RemoveFileOnSignal |
| 319 instance_.getFrontendOpts().OutputFile, // BaseInput |
| 320 "graph.json", // Extension |
| 321 false, // UseTemporary |
| 322 false, // CreateMissingDirectories |
| 323 0, // ResultPathName |
| 324 0)); // TempPathName |
| 325 if (!err && json_) { |
| 326 json_->OpenList(); |
| 327 } else { |
| 328 json_ = 0; |
| 329 llvm::errs() |
| 330 << "[blink-gc] " |
| 331 << "Failed to create an output file for the object graph.\n"; |
| 332 } |
| 333 } |
| 334 |
| 335 for (CollectVisitor::RecordVector::iterator it = |
| 336 visitor.record_decls().begin(); |
| 337 it != visitor.record_decls().end(); |
| 338 ++it) { |
| 339 CheckRecord(cache_.Lookup(*it)); |
| 340 } |
| 341 |
| 342 for (CollectVisitor::MethodVector::iterator it = |
| 343 visitor.trace_decls().begin(); |
| 344 it != visitor.trace_decls().end(); |
| 345 ++it) { |
| 346 CheckTracingMethod(*it); |
| 347 } |
| 348 |
| 349 if (json_) { |
| 350 json_->CloseList(); |
| 351 delete json_; |
| 352 json_ = 0; |
| 353 } |
| 354 } |
| 355 |
| 356 void BlinkGCPluginConsumer::ParseFunctionTemplates(TranslationUnitDecl* decl) { |
| 357 if (!instance_.getLangOpts().DelayedTemplateParsing) |
| 358 return; // Nothing to do. |
| 359 |
| 360 std::set<FunctionDecl*> late_parsed_decls = GetLateParsedFunctionDecls(decl); |
| 361 clang::Sema& sema = instance_.getSema(); |
| 362 |
| 363 for (const FunctionDecl* fd : late_parsed_decls) { |
| 364 assert(fd->isLateTemplateParsed()); |
| 365 |
| 366 if (!Config::IsTraceMethod(fd)) |
| 367 continue; |
| 368 |
| 369 if (instance_.getSourceManager().isInSystemHeader( |
| 370 instance_.getSourceManager().getSpellingLoc(fd->getLocation()))) |
| 371 continue; |
| 372 |
| 373 // Force parsing and AST building of the yet-uninstantiated function |
| 374 // template trace method bodies. |
| 375 clang::LateParsedTemplate* lpt = sema.LateParsedTemplateMap[fd]; |
| 376 sema.LateTemplateParser(sema.OpaqueParser, *lpt); |
| 377 } |
| 378 } |
| 379 |
| 380 void BlinkGCPluginConsumer::CheckRecord(RecordInfo* info) { |
| 381 if (IsIgnored(info)) |
| 382 return; |
| 383 |
| 384 CXXRecordDecl* record = info->record(); |
| 385 |
| 386 // TODO: what should we do to check unions? |
| 387 if (record->isUnion()) |
| 388 return; |
| 389 |
| 390 // If this is the primary template declaration, check its specializations. |
| 391 if (record->isThisDeclarationADefinition() && |
| 392 record->getDescribedClassTemplate()) { |
| 393 ClassTemplateDecl* tmpl = record->getDescribedClassTemplate(); |
| 394 for (ClassTemplateDecl::spec_iterator it = tmpl->spec_begin(); |
| 395 it != tmpl->spec_end(); |
| 396 ++it) { |
| 397 CheckClass(cache_.Lookup(*it)); |
| 398 } |
| 399 return; |
| 400 } |
| 401 |
| 402 CheckClass(info); |
| 403 } |
| 404 |
| 405 void BlinkGCPluginConsumer::CheckClass(RecordInfo* info) { |
| 406 if (!info) |
| 407 return; |
| 408 |
| 409 // Check consistency of stack-allocated hierarchies. |
| 410 if (info->IsStackAllocated()) { |
| 411 for (RecordInfo::Bases::iterator it = info->GetBases().begin(); |
| 412 it != info->GetBases().end(); |
| 413 ++it) { |
| 414 if (!it->second.info()->IsStackAllocated()) |
| 415 ReportDerivesNonStackAllocated(info, &it->second); |
| 416 } |
| 417 } |
| 418 |
| 419 if (CXXMethodDecl* trace = info->GetTraceMethod()) { |
| 420 if (trace->isPure()) |
| 421 ReportClassDeclaresPureVirtualTrace(info, trace); |
| 422 } else if (info->RequiresTraceMethod()) { |
| 423 ReportClassRequiresTraceMethod(info); |
| 424 } |
| 425 |
| 426 // Check polymorphic classes that are GC-derived or have a trace method. |
| 427 if (info->record()->hasDefinition() && info->record()->isPolymorphic()) { |
| 428 // TODO: Check classes that inherit a trace method. |
| 429 CXXMethodDecl* trace = info->GetTraceMethod(); |
| 430 if (trace || info->IsGCDerived()) |
| 431 CheckPolymorphicClass(info, trace); |
| 432 } |
| 433 |
| 434 { |
| 435 CheckFieldsVisitor visitor(options_); |
| 436 if (visitor.ContainsInvalidFields(info)) |
| 437 ReportClassContainsInvalidFields(info, &visitor.invalid_fields()); |
| 438 } |
| 439 |
| 440 if (info->IsGCDerived()) { |
| 441 if (!info->IsGCMixin()) { |
| 442 CheckLeftMostDerived(info); |
| 443 CheckDispatch(info); |
| 444 if (CXXMethodDecl* newop = info->DeclaresNewOperator()) |
| 445 if (!Config::IsIgnoreAnnotated(newop)) |
| 446 ReportClassOverridesNew(info, newop); |
| 447 if (info->IsGCMixinInstance()) { |
| 448 // Require that declared GCMixin implementations |
| 449 // also provide a trace() override. |
| 450 if (info->DeclaresGCMixinMethods() |
| 451 && !info->DeclaresLocalTraceMethod()) |
| 452 ReportClassMustDeclareGCMixinTraceMethod(info); |
| 453 } |
| 454 } |
| 455 |
| 456 { |
| 457 CheckGCRootsVisitor visitor; |
| 458 if (visitor.ContainsGCRoots(info)) |
| 459 ReportClassContainsGCRoots(info, &visitor.gc_roots()); |
| 460 } |
| 461 |
| 462 if (info->NeedsFinalization()) |
| 463 CheckFinalization(info); |
| 464 |
| 465 if (options_.warn_unneeded_finalizer && info->IsGCFinalized()) |
| 466 CheckUnneededFinalization(info); |
| 467 } |
| 468 |
| 469 DumpClass(info); |
| 470 } |
| 471 |
| 472 CXXRecordDecl* BlinkGCPluginConsumer::GetDependentTemplatedDecl( |
| 473 const Type& type) { |
| 474 const TemplateSpecializationType* tmpl_type = |
| 475 type.getAs<TemplateSpecializationType>(); |
| 476 if (!tmpl_type) |
| 477 return 0; |
| 478 |
| 479 TemplateDecl* tmpl_decl = tmpl_type->getTemplateName().getAsTemplateDecl(); |
| 480 if (!tmpl_decl) |
| 481 return 0; |
| 482 |
| 483 return dyn_cast<CXXRecordDecl>(tmpl_decl->getTemplatedDecl()); |
| 484 } |
| 485 |
| 486 // The GC infrastructure assumes that if the vtable of a polymorphic |
| 487 // base-class is not initialized for a given object (ie, it is partially |
| 488 // initialized) then the object does not need to be traced. Thus, we must |
| 489 // ensure that any polymorphic class with a trace method does not have any |
| 490 // tractable fields that are initialized before we are sure that the vtable |
| 491 // and the trace method are both defined. There are two cases that need to |
| 492 // hold to satisfy that assumption: |
| 493 // |
| 494 // 1. If trace is virtual, then it must be defined in the left-most base. |
| 495 // This ensures that if the vtable is initialized then it contains a pointer |
| 496 // to the trace method. |
| 497 // |
| 498 // 2. If trace is non-virtual, then the trace method is defined and we must |
| 499 // ensure that the left-most base defines a vtable. This ensures that the |
| 500 // first thing to be initialized when constructing the object is the vtable |
| 501 // itself. |
| 502 void BlinkGCPluginConsumer::CheckPolymorphicClass( |
| 503 RecordInfo* info, |
| 504 CXXMethodDecl* trace) { |
| 505 CXXRecordDecl* left_most = info->record(); |
| 506 CXXRecordDecl::base_class_iterator it = left_most->bases_begin(); |
| 507 CXXRecordDecl* left_most_base = 0; |
| 508 while (it != left_most->bases_end()) { |
| 509 left_most_base = it->getType()->getAsCXXRecordDecl(); |
| 510 if (!left_most_base && it->getType()->isDependentType()) |
| 511 left_most_base = RecordInfo::GetDependentTemplatedDecl(*it->getType()); |
| 512 |
| 513 // TODO: Find a way to correctly check actual instantiations |
| 514 // for dependent types. The escape below will be hit, eg, when |
| 515 // we have a primary template with no definition and |
| 516 // specializations for each case (such as SupplementBase) in |
| 517 // which case we don't succeed in checking the required |
| 518 // properties. |
| 519 if (!left_most_base || !left_most_base->hasDefinition()) |
| 520 return; |
| 521 |
| 522 StringRef name = left_most_base->getName(); |
| 523 // We know GCMixin base defines virtual trace. |
| 524 if (Config::IsGCMixinBase(name)) |
| 525 return; |
| 526 |
| 527 // Stop with the left-most prior to a safe polymorphic base (a safe base |
| 528 // is non-polymorphic and contains no fields). |
| 529 if (Config::IsSafePolymorphicBase(name)) |
| 530 break; |
| 531 |
| 532 left_most = left_most_base; |
| 533 it = left_most->bases_begin(); |
| 534 } |
| 535 |
| 536 if (RecordInfo* left_most_info = cache_.Lookup(left_most)) { |
| 537 // Check condition (1): |
| 538 if (trace && trace->isVirtual()) { |
| 539 if (CXXMethodDecl* trace = left_most_info->GetTraceMethod()) { |
| 540 if (trace->isVirtual()) |
| 541 return; |
| 542 } |
| 543 ReportBaseClassMustDeclareVirtualTrace(info, left_most); |
| 544 return; |
| 545 } |
| 546 |
| 547 // Check condition (2): |
| 548 if (DeclaresVirtualMethods(left_most)) |
| 549 return; |
| 550 if (left_most_base) { |
| 551 // Get the base next to the "safe polymorphic base" |
| 552 if (it != left_most->bases_end()) |
| 553 ++it; |
| 554 if (it != left_most->bases_end()) { |
| 555 if (CXXRecordDecl* next_base = it->getType()->getAsCXXRecordDecl()) { |
| 556 if (CXXRecordDecl* next_left_most = GetLeftMostBase(next_base)) { |
| 557 if (DeclaresVirtualMethods(next_left_most)) |
| 558 return; |
| 559 ReportLeftMostBaseMustBePolymorphic(info, next_left_most); |
| 560 return; |
| 561 } |
| 562 } |
| 563 } |
| 564 } |
| 565 ReportLeftMostBaseMustBePolymorphic(info, left_most); |
| 566 } |
| 567 } |
| 568 |
| 569 CXXRecordDecl* BlinkGCPluginConsumer::GetLeftMostBase( |
| 570 CXXRecordDecl* left_most) { |
| 571 CXXRecordDecl::base_class_iterator it = left_most->bases_begin(); |
| 572 while (it != left_most->bases_end()) { |
| 573 if (it->getType()->isDependentType()) |
| 574 left_most = RecordInfo::GetDependentTemplatedDecl(*it->getType()); |
| 575 else |
| 576 left_most = it->getType()->getAsCXXRecordDecl(); |
| 577 if (!left_most || !left_most->hasDefinition()) |
| 578 return 0; |
| 579 it = left_most->bases_begin(); |
| 580 } |
| 581 return left_most; |
| 582 } |
| 583 |
| 584 bool BlinkGCPluginConsumer::DeclaresVirtualMethods(CXXRecordDecl* decl) { |
| 585 CXXRecordDecl::method_iterator it = decl->method_begin(); |
| 586 for (; it != decl->method_end(); ++it) |
| 587 if (it->isVirtual() && !it->isPure()) |
| 588 return true; |
| 589 return false; |
| 590 } |
| 591 |
| 592 void BlinkGCPluginConsumer::CheckLeftMostDerived(RecordInfo* info) { |
| 593 CXXRecordDecl* left_most = GetLeftMostBase(info->record()); |
| 594 if (!left_most) |
| 595 return; |
| 596 if (!Config::IsGCBase(left_most->getName())) |
| 597 ReportClassMustLeftMostlyDeriveGC(info); |
| 598 } |
| 599 |
| 600 void BlinkGCPluginConsumer::CheckDispatch(RecordInfo* info) { |
| 601 bool finalized = info->IsGCFinalized(); |
| 602 CXXMethodDecl* trace_dispatch = info->GetTraceDispatchMethod(); |
| 603 CXXMethodDecl* finalize_dispatch = info->GetFinalizeDispatchMethod(); |
| 604 if (!trace_dispatch && !finalize_dispatch) |
| 605 return; |
| 606 |
| 607 CXXRecordDecl* base = trace_dispatch ? trace_dispatch->getParent() |
| 608 : finalize_dispatch->getParent(); |
| 609 |
| 610 // Check that dispatch methods are defined at the base. |
| 611 if (base == info->record()) { |
| 612 if (!trace_dispatch) |
| 613 ReportMissingTraceDispatchMethod(info); |
| 614 if (finalized && !finalize_dispatch) |
| 615 ReportMissingFinalizeDispatchMethod(info); |
| 616 if (!finalized && finalize_dispatch) { |
| 617 ReportClassRequiresFinalization(info); |
| 618 NoteUserDeclaredFinalizer(finalize_dispatch); |
| 619 } |
| 620 } |
| 621 |
| 622 // Check that classes implementing manual dispatch do not have vtables. |
| 623 if (info->record()->isPolymorphic()) { |
| 624 ReportVirtualAndManualDispatch( |
| 625 info, trace_dispatch ? trace_dispatch : finalize_dispatch); |
| 626 } |
| 627 |
| 628 // If this is a non-abstract class check that it is dispatched to. |
| 629 // TODO: Create a global variant of this local check. We can only check if |
| 630 // the dispatch body is known in this compilation unit. |
| 631 if (info->IsConsideredAbstract()) |
| 632 return; |
| 633 |
| 634 const FunctionDecl* defn; |
| 635 |
| 636 if (trace_dispatch && trace_dispatch->isDefined(defn)) { |
| 637 CheckDispatchVisitor visitor(info); |
| 638 visitor.TraverseStmt(defn->getBody()); |
| 639 if (!visitor.dispatched_to_receiver()) |
| 640 ReportMissingTraceDispatch(defn, info); |
| 641 } |
| 642 |
| 643 if (finalized && finalize_dispatch && finalize_dispatch->isDefined(defn)) { |
| 644 CheckDispatchVisitor visitor(info); |
| 645 visitor.TraverseStmt(defn->getBody()); |
| 646 if (!visitor.dispatched_to_receiver()) |
| 647 ReportMissingFinalizeDispatch(defn, info); |
| 648 } |
| 649 } |
| 650 |
| 651 // TODO: Should we collect destructors similar to trace methods? |
| 652 void BlinkGCPluginConsumer::CheckFinalization(RecordInfo* info) { |
| 653 CXXDestructorDecl* dtor = info->record()->getDestructor(); |
| 654 |
| 655 // For finalized classes, check the finalization method if possible. |
| 656 if (info->IsGCFinalized()) { |
| 657 if (dtor && dtor->hasBody()) { |
| 658 CheckFinalizerVisitor visitor(&cache_, info->IsEagerlyFinalized()); |
| 659 visitor.TraverseCXXMethodDecl(dtor); |
| 660 if (!visitor.finalized_fields().empty()) { |
| 661 ReportFinalizerAccessesFinalizedFields( |
| 662 dtor, &visitor.finalized_fields()); |
| 663 } |
| 664 } |
| 665 return; |
| 666 } |
| 667 |
| 668 // Don't require finalization of a mixin that has not yet been "mixed in". |
| 669 if (info->IsGCMixin()) |
| 670 return; |
| 671 |
| 672 // Report the finalization error, and proceed to print possible causes for |
| 673 // the finalization requirement. |
| 674 ReportClassRequiresFinalization(info); |
| 675 |
| 676 if (dtor && dtor->isUserProvided()) |
| 677 NoteUserDeclaredDestructor(dtor); |
| 678 |
| 679 for (RecordInfo::Bases::iterator it = info->GetBases().begin(); |
| 680 it != info->GetBases().end(); |
| 681 ++it) { |
| 682 if (it->second.info()->NeedsFinalization()) |
| 683 NoteBaseRequiresFinalization(&it->second); |
| 684 } |
| 685 |
| 686 for (RecordInfo::Fields::iterator it = info->GetFields().begin(); |
| 687 it != info->GetFields().end(); |
| 688 ++it) { |
| 689 if (it->second.edge()->NeedsFinalization()) |
| 690 NoteField(&it->second, diag_field_requires_finalization_note_); |
| 691 } |
| 692 } |
| 693 |
| 694 void BlinkGCPluginConsumer::CheckUnneededFinalization(RecordInfo* info) { |
| 695 if (!HasNonEmptyFinalizer(info)) |
| 696 ReportClassDoesNotRequireFinalization(info); |
| 697 } |
| 698 |
| 699 bool BlinkGCPluginConsumer::HasNonEmptyFinalizer(RecordInfo* info) { |
| 700 CXXDestructorDecl* dtor = info->record()->getDestructor(); |
| 701 if (dtor && dtor->isUserProvided()) { |
| 702 if (!dtor->hasBody() || !EmptyStmtVisitor::isEmpty(dtor->getBody())) |
| 703 return true; |
| 704 } |
| 705 for (RecordInfo::Bases::iterator it = info->GetBases().begin(); |
| 706 it != info->GetBases().end(); |
| 707 ++it) { |
| 708 if (HasNonEmptyFinalizer(it->second.info())) |
| 709 return true; |
| 710 } |
| 711 for (RecordInfo::Fields::iterator it = info->GetFields().begin(); |
| 712 it != info->GetFields().end(); |
| 713 ++it) { |
| 714 if (it->second.edge()->NeedsFinalization()) |
| 715 return true; |
| 716 } |
| 717 return false; |
| 718 } |
| 719 |
| 720 void BlinkGCPluginConsumer::CheckTracingMethod(CXXMethodDecl* method) { |
| 721 RecordInfo* parent = cache_.Lookup(method->getParent()); |
| 722 if (IsIgnored(parent)) |
| 723 return; |
| 724 |
| 725 // Check templated tracing methods by checking the template instantiations. |
| 726 // Specialized templates are handled as ordinary classes. |
| 727 if (ClassTemplateDecl* tmpl = |
| 728 parent->record()->getDescribedClassTemplate()) { |
| 729 for (ClassTemplateDecl::spec_iterator it = tmpl->spec_begin(); |
| 730 it != tmpl->spec_end(); |
| 731 ++it) { |
| 732 // Check trace using each template instantiation as the holder. |
| 733 if (Config::IsTemplateInstantiation(*it)) |
| 734 CheckTraceOrDispatchMethod(cache_.Lookup(*it), method); |
| 735 } |
| 736 return; |
| 737 } |
| 738 |
| 739 CheckTraceOrDispatchMethod(parent, method); |
| 740 } |
| 741 |
| 742 void BlinkGCPluginConsumer::CheckTraceOrDispatchMethod( |
| 743 RecordInfo* parent, |
| 744 CXXMethodDecl* method) { |
| 745 Config::TraceMethodType trace_type = Config::GetTraceMethodType(method); |
| 746 if (trace_type == Config::TRACE_AFTER_DISPATCH_METHOD || |
| 747 trace_type == Config::TRACE_AFTER_DISPATCH_IMPL_METHOD || |
| 748 !parent->GetTraceDispatchMethod()) { |
| 749 CheckTraceMethod(parent, method, trace_type); |
| 750 } |
| 751 // Dispatch methods are checked when we identify subclasses. |
| 752 } |
| 753 |
| 754 void BlinkGCPluginConsumer::CheckTraceMethod( |
| 755 RecordInfo* parent, |
| 756 CXXMethodDecl* trace, |
| 757 Config::TraceMethodType trace_type) { |
| 758 // A trace method must not override any non-virtual trace methods. |
| 759 if (trace_type == Config::TRACE_METHOD) { |
| 760 for (RecordInfo::Bases::iterator it = parent->GetBases().begin(); |
| 761 it != parent->GetBases().end(); |
| 762 ++it) { |
| 763 RecordInfo* base = it->second.info(); |
| 764 if (CXXMethodDecl* other = base->InheritsNonVirtualTrace()) |
| 765 ReportOverriddenNonVirtualTrace(parent, trace, other); |
| 766 } |
| 767 } |
| 768 |
| 769 CheckTraceVisitor visitor(trace, parent, &cache_); |
| 770 visitor.TraverseCXXMethodDecl(trace); |
| 771 |
| 772 // Skip reporting if this trace method is a just delegate to |
| 773 // traceImpl (or traceAfterDispatchImpl) method. We will report on |
| 774 // CheckTraceMethod on traceImpl method. |
| 775 if (visitor.delegates_to_traceimpl()) |
| 776 return; |
| 777 |
| 778 for (RecordInfo::Bases::iterator it = parent->GetBases().begin(); |
| 779 it != parent->GetBases().end(); |
| 780 ++it) { |
| 781 if (!it->second.IsProperlyTraced()) |
| 782 ReportBaseRequiresTracing(parent, trace, it->first); |
| 783 } |
| 784 |
| 785 for (RecordInfo::Fields::iterator it = parent->GetFields().begin(); |
| 786 it != parent->GetFields().end(); |
| 787 ++it) { |
| 788 if (!it->second.IsProperlyTraced()) { |
| 789 // Discontinue once an untraced-field error is found. |
| 790 ReportFieldsRequireTracing(parent, trace); |
| 791 break; |
| 792 } |
| 793 } |
| 794 } |
| 795 |
| 796 void BlinkGCPluginConsumer::DumpClass(RecordInfo* info) { |
| 797 if (!json_) |
| 798 return; |
| 799 |
| 800 json_->OpenObject(); |
| 801 json_->Write("name", info->record()->getQualifiedNameAsString()); |
| 802 json_->Write("loc", GetLocString(info->record()->getLocStart())); |
| 803 json_->CloseObject(); |
| 804 |
| 805 class DumpEdgeVisitor : public RecursiveEdgeVisitor { |
| 806 public: |
| 807 DumpEdgeVisitor(JsonWriter* json) : json_(json) {} |
| 808 void DumpEdge(RecordInfo* src, |
| 809 RecordInfo* dst, |
| 810 const std::string& lbl, |
| 811 const Edge::LivenessKind& kind, |
| 812 const std::string& loc) { |
| 813 json_->OpenObject(); |
| 814 json_->Write("src", src->record()->getQualifiedNameAsString()); |
| 815 json_->Write("dst", dst->record()->getQualifiedNameAsString()); |
| 816 json_->Write("lbl", lbl); |
| 817 json_->Write("kind", kind); |
| 818 json_->Write("loc", loc); |
| 819 json_->Write("ptr", |
| 820 !Parent() ? "val" : |
| 821 Parent()->IsRawPtr() ? |
| 822 (static_cast<RawPtr*>(Parent())->HasReferenceType() ? |
| 823 "reference" : "raw") : |
| 824 Parent()->IsRefPtr() ? "ref" : |
| 825 Parent()->IsOwnPtr() ? "own" : |
| 826 (Parent()->IsMember() || Parent()->IsWeakMember()) ? "mem" : |
| 827 "val"); |
| 828 json_->CloseObject(); |
| 829 } |
| 830 |
| 831 void DumpField(RecordInfo* src, FieldPoint* point, const std::string& loc) { |
| 832 src_ = src; |
| 833 point_ = point; |
| 834 loc_ = loc; |
| 835 point_->edge()->Accept(this); |
| 836 } |
| 837 |
| 838 void AtValue(Value* e) override { |
| 839 // The liveness kind of a path from the point to this value |
| 840 // is given by the innermost place that is non-strong. |
| 841 Edge::LivenessKind kind = Edge::kStrong; |
| 842 if (Config::IsIgnoreCycleAnnotated(point_->field())) { |
| 843 kind = Edge::kWeak; |
| 844 } else { |
| 845 for (Context::iterator it = context().begin(); |
| 846 it != context().end(); |
| 847 ++it) { |
| 848 Edge::LivenessKind pointer_kind = (*it)->Kind(); |
| 849 if (pointer_kind != Edge::kStrong) { |
| 850 kind = pointer_kind; |
| 851 break; |
| 852 } |
| 853 } |
| 854 } |
| 855 DumpEdge( |
| 856 src_, e->value(), point_->field()->getNameAsString(), kind, loc_); |
| 857 } |
| 858 |
| 859 private: |
| 860 JsonWriter* json_; |
| 861 RecordInfo* src_; |
| 862 FieldPoint* point_; |
| 863 std::string loc_; |
| 864 }; |
| 865 |
| 866 DumpEdgeVisitor visitor(json_); |
| 867 |
| 868 RecordInfo::Bases& bases = info->GetBases(); |
| 869 for (RecordInfo::Bases::iterator it = bases.begin(); |
| 870 it != bases.end(); |
| 871 ++it) { |
| 872 visitor.DumpEdge(info, |
| 873 it->second.info(), |
| 874 "<super>", |
| 875 Edge::kStrong, |
| 876 GetLocString(it->second.spec().getLocStart())); |
| 877 } |
| 878 |
| 879 RecordInfo::Fields& fields = info->GetFields(); |
| 880 for (RecordInfo::Fields::iterator it = fields.begin(); |
| 881 it != fields.end(); |
| 882 ++it) { |
| 883 visitor.DumpField(info, |
| 884 &it->second, |
| 885 GetLocString(it->second.field()->getLocStart())); |
| 886 } |
| 887 } |
| 888 |
| 889 DiagnosticsEngine::Level BlinkGCPluginConsumer::getErrorLevel() { |
| 890 return diagnostic_.getWarningsAsErrors() ? DiagnosticsEngine::Error |
| 891 : DiagnosticsEngine::Warning; |
| 892 } |
| 893 |
| 894 std::string BlinkGCPluginConsumer::GetLocString(SourceLocation loc) { |
| 895 const SourceManager& source_manager = instance_.getSourceManager(); |
| 896 PresumedLoc ploc = source_manager.getPresumedLoc(loc); |
| 897 if (ploc.isInvalid()) |
| 898 return ""; |
| 899 std::string loc_str; |
| 900 llvm::raw_string_ostream os(loc_str); |
| 901 os << ploc.getFilename() |
| 902 << ":" << ploc.getLine() |
| 903 << ":" << ploc.getColumn(); |
| 904 return os.str(); |
| 905 } |
| 906 |
| 907 bool BlinkGCPluginConsumer::IsIgnored(RecordInfo* record) { |
| 908 return (!record || |
| 909 !InCheckedNamespace(record) || |
| 910 IsIgnoredClass(record) || |
| 911 InIgnoredDirectory(record)); |
| 912 } |
| 913 |
| 914 bool BlinkGCPluginConsumer::IsIgnoredClass(RecordInfo* info) { |
| 915 // Ignore any class prefixed by SameSizeAs. These are used in |
| 916 // Blink to verify class sizes and don't need checking. |
| 917 const std::string SameSizeAs = "SameSizeAs"; |
| 918 if (info->name().compare(0, SameSizeAs.size(), SameSizeAs) == 0) |
| 919 return true; |
| 920 return (options_.ignored_classes.find(info->name()) != |
| 921 options_.ignored_classes.end()); |
| 922 } |
| 923 |
| 924 bool BlinkGCPluginConsumer::InIgnoredDirectory(RecordInfo* info) { |
| 925 std::string filename; |
| 926 if (!GetFilename(info->record()->getLocStart(), &filename)) |
| 927 return false; // TODO: should we ignore non-existing file locations? |
| 928 #if defined(LLVM_ON_WIN32) |
| 929 std::replace(filename.begin(), filename.end(), '\\', '/'); |
| 930 #endif |
| 931 std::vector<std::string>::iterator it = options_.ignored_directories.begin(); |
| 932 for (; it != options_.ignored_directories.end(); ++it) |
| 933 if (filename.find(*it) != std::string::npos) |
| 934 return true; |
| 935 return false; |
| 936 } |
| 937 |
| 938 bool BlinkGCPluginConsumer::InCheckedNamespace(RecordInfo* info) { |
| 939 if (!info) |
| 940 return false; |
| 941 for (DeclContext* context = info->record()->getDeclContext(); |
| 942 !context->isTranslationUnit(); |
| 943 context = context->getParent()) { |
| 944 if (NamespaceDecl* decl = dyn_cast<NamespaceDecl>(context)) { |
| 945 if (decl->isAnonymousNamespace()) |
| 946 return true; |
| 947 if (options_.checked_namespaces.find(decl->getNameAsString()) != |
| 948 options_.checked_namespaces.end()) { |
| 949 return true; |
| 950 } |
| 951 } |
| 952 } |
| 953 return false; |
| 954 } |
| 955 |
| 956 bool BlinkGCPluginConsumer::GetFilename(SourceLocation loc, |
| 957 std::string* filename) { |
| 958 const SourceManager& source_manager = instance_.getSourceManager(); |
| 959 SourceLocation spelling_location = source_manager.getSpellingLoc(loc); |
| 960 PresumedLoc ploc = source_manager.getPresumedLoc(spelling_location); |
| 961 if (ploc.isInvalid()) { |
| 962 // If we're in an invalid location, we're looking at things that aren't |
| 963 // actually stated in the source. |
| 964 return false; |
| 965 } |
| 966 *filename = ploc.getFilename(); |
| 967 return true; |
| 968 } |
| 969 |
| 970 void BlinkGCPluginConsumer::ReportClassMustLeftMostlyDeriveGC( |
| 971 RecordInfo* info) { |
| 972 SourceLocation loc = info->record()->getInnerLocStart(); |
| 973 SourceManager& manager = instance_.getSourceManager(); |
| 974 FullSourceLoc full_loc(loc, manager); |
| 975 diagnostic_.Report(full_loc, diag_class_must_left_mostly_derive_gc_) |
| 976 << info->record(); |
| 977 } |
| 978 |
| 979 void BlinkGCPluginConsumer::ReportClassRequiresTraceMethod(RecordInfo* info) { |
| 980 SourceLocation loc = info->record()->getInnerLocStart(); |
| 981 SourceManager& manager = instance_.getSourceManager(); |
| 982 FullSourceLoc full_loc(loc, manager); |
| 983 diagnostic_.Report(full_loc, diag_class_requires_trace_method_) |
| 984 << info->record(); |
| 985 |
| 986 for (RecordInfo::Bases::iterator it = info->GetBases().begin(); |
| 987 it != info->GetBases().end(); |
| 988 ++it) { |
| 989 if (it->second.NeedsTracing().IsNeeded()) |
| 990 NoteBaseRequiresTracing(&it->second); |
| 991 } |
| 992 |
| 993 for (RecordInfo::Fields::iterator it = info->GetFields().begin(); |
| 994 it != info->GetFields().end(); |
| 995 ++it) { |
| 996 if (!it->second.IsProperlyTraced()) |
| 997 NoteFieldRequiresTracing(info, it->first); |
| 998 } |
| 999 } |
| 1000 |
| 1001 void BlinkGCPluginConsumer::ReportBaseRequiresTracing( |
| 1002 RecordInfo* derived, |
| 1003 CXXMethodDecl* trace, |
| 1004 CXXRecordDecl* base) { |
| 1005 SourceLocation loc = trace->getLocStart(); |
| 1006 SourceManager& manager = instance_.getSourceManager(); |
| 1007 FullSourceLoc full_loc(loc, manager); |
| 1008 diagnostic_.Report(full_loc, diag_base_requires_tracing_) |
| 1009 << base << derived->record(); |
| 1010 } |
| 1011 |
| 1012 void BlinkGCPluginConsumer::ReportFieldsRequireTracing( |
| 1013 RecordInfo* info, |
| 1014 CXXMethodDecl* trace) { |
| 1015 SourceLocation loc = trace->getLocStart(); |
| 1016 SourceManager& manager = instance_.getSourceManager(); |
| 1017 FullSourceLoc full_loc(loc, manager); |
| 1018 diagnostic_.Report(full_loc, diag_fields_require_tracing_) |
| 1019 << info->record(); |
| 1020 for (RecordInfo::Fields::iterator it = info->GetFields().begin(); |
| 1021 it != info->GetFields().end(); |
| 1022 ++it) { |
| 1023 if (!it->second.IsProperlyTraced()) |
| 1024 NoteFieldRequiresTracing(info, it->first); |
| 1025 } |
| 1026 } |
| 1027 |
| 1028 void BlinkGCPluginConsumer::ReportClassContainsInvalidFields( |
| 1029 RecordInfo* info, |
| 1030 CheckFieldsVisitor::Errors* errors) { |
| 1031 SourceLocation loc = info->record()->getLocStart(); |
| 1032 SourceManager& manager = instance_.getSourceManager(); |
| 1033 FullSourceLoc full_loc(loc, manager); |
| 1034 bool only_warnings = options_.warn_raw_ptr; |
| 1035 for (CheckFieldsVisitor::Errors::iterator it = errors->begin(); |
| 1036 only_warnings && it != errors->end(); |
| 1037 ++it) { |
| 1038 if (!CheckFieldsVisitor::IsWarning(it->second)) |
| 1039 only_warnings = false; |
| 1040 } |
| 1041 diagnostic_.Report(full_loc, |
| 1042 only_warnings ? |
| 1043 diag_class_contains_invalid_fields_warning_ : |
| 1044 diag_class_contains_invalid_fields_) |
| 1045 << info->record(); |
| 1046 for (CheckFieldsVisitor::Errors::iterator it = errors->begin(); |
| 1047 it != errors->end(); |
| 1048 ++it) { |
| 1049 unsigned error; |
| 1050 if (CheckFieldsVisitor::IsRawPtrError(it->second)) { |
| 1051 error = diag_raw_ptr_to_gc_managed_class_note_; |
| 1052 } else if (CheckFieldsVisitor::IsReferencePtrError(it->second)) { |
| 1053 error = diag_reference_ptr_to_gc_managed_class_note_; |
| 1054 } else if (it->second == CheckFieldsVisitor::kRefPtrToGCManaged) { |
| 1055 error = diag_ref_ptr_to_gc_managed_class_note_; |
| 1056 } else if (it->second == CheckFieldsVisitor::kOwnPtrToGCManaged) { |
| 1057 error = diag_own_ptr_to_gc_managed_class_note_; |
| 1058 } else if (it->second == CheckFieldsVisitor::kMemberToGCUnmanaged) { |
| 1059 error = diag_member_to_gc_unmanaged_class_note_; |
| 1060 } else if (it->second == CheckFieldsVisitor::kMemberInUnmanaged) { |
| 1061 error = diag_member_in_unmanaged_class_note_; |
| 1062 } else if (it->second == CheckFieldsVisitor::kPtrFromHeapToStack) { |
| 1063 error = diag_stack_allocated_field_note_; |
| 1064 } else if (it->second == CheckFieldsVisitor::kGCDerivedPartObject) { |
| 1065 error = diag_part_object_to_gc_derived_class_note_; |
| 1066 } else { |
| 1067 assert(false && "Unknown field error"); |
| 1068 } |
| 1069 NoteField(it->first, error); |
| 1070 } |
| 1071 } |
| 1072 |
| 1073 void BlinkGCPluginConsumer::ReportClassContainsGCRoots( |
| 1074 RecordInfo* info, |
| 1075 CheckGCRootsVisitor::Errors* errors) { |
| 1076 SourceLocation loc = info->record()->getLocStart(); |
| 1077 SourceManager& manager = instance_.getSourceManager(); |
| 1078 FullSourceLoc full_loc(loc, manager); |
| 1079 for (CheckGCRootsVisitor::Errors::iterator it = errors->begin(); |
| 1080 it != errors->end(); |
| 1081 ++it) { |
| 1082 CheckGCRootsVisitor::RootPath::iterator path = it->begin(); |
| 1083 FieldPoint* point = *path; |
| 1084 diagnostic_.Report(full_loc, diag_class_contains_gc_root_) |
| 1085 << info->record() << point->field(); |
| 1086 while (++path != it->end()) { |
| 1087 NotePartObjectContainsGCRoot(point); |
| 1088 point = *path; |
| 1089 } |
| 1090 NoteFieldContainsGCRoot(point); |
| 1091 } |
| 1092 } |
| 1093 |
| 1094 void BlinkGCPluginConsumer::ReportFinalizerAccessesFinalizedFields( |
| 1095 CXXMethodDecl* dtor, |
| 1096 CheckFinalizerVisitor::Errors* fields) { |
| 1097 for (CheckFinalizerVisitor::Errors::iterator it = fields->begin(); |
| 1098 it != fields->end(); |
| 1099 ++it) { |
| 1100 SourceLocation loc = it->member->getLocStart(); |
| 1101 SourceManager& manager = instance_.getSourceManager(); |
| 1102 bool as_eagerly_finalized = it->as_eagerly_finalized; |
| 1103 unsigned diag_error = as_eagerly_finalized ? |
| 1104 diag_finalizer_eagerly_finalized_field_ : |
| 1105 diag_finalizer_accesses_finalized_field_; |
| 1106 unsigned diag_note = as_eagerly_finalized ? |
| 1107 diag_eagerly_finalized_field_note_ : |
| 1108 diag_finalized_field_note_; |
| 1109 FullSourceLoc full_loc(loc, manager); |
| 1110 diagnostic_.Report(full_loc, diag_error) |
| 1111 << dtor << it->field->field(); |
| 1112 NoteField(it->field, diag_note); |
| 1113 } |
| 1114 } |
| 1115 |
| 1116 void BlinkGCPluginConsumer::ReportClassRequiresFinalization(RecordInfo* info) { |
| 1117 SourceLocation loc = info->record()->getInnerLocStart(); |
| 1118 SourceManager& manager = instance_.getSourceManager(); |
| 1119 FullSourceLoc full_loc(loc, manager); |
| 1120 diagnostic_.Report(full_loc, diag_class_requires_finalization_) |
| 1121 << info->record(); |
| 1122 } |
| 1123 |
| 1124 void BlinkGCPluginConsumer::ReportClassDoesNotRequireFinalization( |
| 1125 RecordInfo* info) { |
| 1126 SourceLocation loc = info->record()->getInnerLocStart(); |
| 1127 SourceManager& manager = instance_.getSourceManager(); |
| 1128 FullSourceLoc full_loc(loc, manager); |
| 1129 diagnostic_.Report(full_loc, diag_class_does_not_require_finalization_) |
| 1130 << info->record(); |
| 1131 } |
| 1132 |
| 1133 void BlinkGCPluginConsumer::ReportClassMustDeclareGCMixinTraceMethod( |
| 1134 RecordInfo* info) { |
| 1135 SourceLocation loc = info->record()->getInnerLocStart(); |
| 1136 SourceManager& manager = instance_.getSourceManager(); |
| 1137 FullSourceLoc full_loc(loc, manager); |
| 1138 diagnostic_.Report(full_loc, diag_class_must_declare_gc_mixin_trace_method_) |
| 1139 << info->record(); |
| 1140 } |
| 1141 |
| 1142 void BlinkGCPluginConsumer::ReportOverriddenNonVirtualTrace( |
| 1143 RecordInfo* info, |
| 1144 CXXMethodDecl* trace, |
| 1145 CXXMethodDecl* overridden) { |
| 1146 SourceLocation loc = trace->getLocStart(); |
| 1147 SourceManager& manager = instance_.getSourceManager(); |
| 1148 FullSourceLoc full_loc(loc, manager); |
| 1149 diagnostic_.Report(full_loc, diag_overridden_non_virtual_trace_) |
| 1150 << info->record() << overridden->getParent(); |
| 1151 NoteOverriddenNonVirtualTrace(overridden); |
| 1152 } |
| 1153 |
| 1154 void BlinkGCPluginConsumer::ReportMissingTraceDispatchMethod(RecordInfo* info) { |
| 1155 ReportMissingDispatchMethod(info, diag_missing_trace_dispatch_method_); |
| 1156 } |
| 1157 |
| 1158 void BlinkGCPluginConsumer::ReportMissingFinalizeDispatchMethod( |
| 1159 RecordInfo* info) { |
| 1160 ReportMissingDispatchMethod(info, diag_missing_finalize_dispatch_method_); |
| 1161 } |
| 1162 |
| 1163 void BlinkGCPluginConsumer::ReportMissingDispatchMethod( |
| 1164 RecordInfo* info, |
| 1165 unsigned error) { |
| 1166 SourceLocation loc = info->record()->getInnerLocStart(); |
| 1167 SourceManager& manager = instance_.getSourceManager(); |
| 1168 FullSourceLoc full_loc(loc, manager); |
| 1169 diagnostic_.Report(full_loc, error) << info->record(); |
| 1170 } |
| 1171 |
| 1172 void BlinkGCPluginConsumer::ReportVirtualAndManualDispatch( |
| 1173 RecordInfo* info, |
| 1174 CXXMethodDecl* dispatch) { |
| 1175 SourceLocation loc = info->record()->getInnerLocStart(); |
| 1176 SourceManager& manager = instance_.getSourceManager(); |
| 1177 FullSourceLoc full_loc(loc, manager); |
| 1178 diagnostic_.Report(full_loc, diag_virtual_and_manual_dispatch_) |
| 1179 << info->record(); |
| 1180 NoteManualDispatchMethod(dispatch); |
| 1181 } |
| 1182 |
| 1183 void BlinkGCPluginConsumer::ReportMissingTraceDispatch( |
| 1184 const FunctionDecl* dispatch, |
| 1185 RecordInfo* receiver) { |
| 1186 ReportMissingDispatch(dispatch, receiver, diag_missing_trace_dispatch_); |
| 1187 } |
| 1188 |
| 1189 void BlinkGCPluginConsumer::ReportMissingFinalizeDispatch( |
| 1190 const FunctionDecl* dispatch, |
| 1191 RecordInfo* receiver) { |
| 1192 ReportMissingDispatch(dispatch, receiver, diag_missing_finalize_dispatch_); |
| 1193 } |
| 1194 |
| 1195 void BlinkGCPluginConsumer::ReportMissingDispatch( |
| 1196 const FunctionDecl* dispatch, |
| 1197 RecordInfo* receiver, |
| 1198 unsigned error) { |
| 1199 SourceLocation loc = dispatch->getLocStart(); |
| 1200 SourceManager& manager = instance_.getSourceManager(); |
| 1201 FullSourceLoc full_loc(loc, manager); |
| 1202 diagnostic_.Report(full_loc, error) << receiver->record(); |
| 1203 } |
| 1204 |
| 1205 void BlinkGCPluginConsumer::ReportDerivesNonStackAllocated( |
| 1206 RecordInfo* info, |
| 1207 BasePoint* base) { |
| 1208 SourceLocation loc = base->spec().getLocStart(); |
| 1209 SourceManager& manager = instance_.getSourceManager(); |
| 1210 FullSourceLoc full_loc(loc, manager); |
| 1211 diagnostic_.Report(full_loc, diag_derives_non_stack_allocated_) |
| 1212 << info->record() << base->info()->record(); |
| 1213 } |
| 1214 |
| 1215 void BlinkGCPluginConsumer::ReportClassOverridesNew( |
| 1216 RecordInfo* info, |
| 1217 CXXMethodDecl* newop) { |
| 1218 SourceLocation loc = newop->getLocStart(); |
| 1219 SourceManager& manager = instance_.getSourceManager(); |
| 1220 FullSourceLoc full_loc(loc, manager); |
| 1221 diagnostic_.Report(full_loc, diag_class_overrides_new_) << info->record(); |
| 1222 } |
| 1223 |
| 1224 void BlinkGCPluginConsumer::ReportClassDeclaresPureVirtualTrace( |
| 1225 RecordInfo* info, |
| 1226 CXXMethodDecl* trace) { |
| 1227 SourceLocation loc = trace->getLocStart(); |
| 1228 SourceManager& manager = instance_.getSourceManager(); |
| 1229 FullSourceLoc full_loc(loc, manager); |
| 1230 diagnostic_.Report(full_loc, diag_class_declares_pure_virtual_trace_) |
| 1231 << info->record(); |
| 1232 } |
| 1233 |
| 1234 void BlinkGCPluginConsumer::ReportLeftMostBaseMustBePolymorphic( |
| 1235 RecordInfo* derived, |
| 1236 CXXRecordDecl* base) { |
| 1237 SourceLocation loc = base->getLocStart(); |
| 1238 SourceManager& manager = instance_.getSourceManager(); |
| 1239 FullSourceLoc full_loc(loc, manager); |
| 1240 diagnostic_.Report(full_loc, diag_left_most_base_must_be_polymorphic_) |
| 1241 << base << derived->record(); |
| 1242 } |
| 1243 |
| 1244 void BlinkGCPluginConsumer::ReportBaseClassMustDeclareVirtualTrace( |
| 1245 RecordInfo* derived, |
| 1246 CXXRecordDecl* base) { |
| 1247 SourceLocation loc = base->getLocStart(); |
| 1248 SourceManager& manager = instance_.getSourceManager(); |
| 1249 FullSourceLoc full_loc(loc, manager); |
| 1250 diagnostic_.Report(full_loc, diag_base_class_must_declare_virtual_trace_) |
| 1251 << base << derived->record(); |
| 1252 } |
| 1253 |
| 1254 void BlinkGCPluginConsumer::NoteManualDispatchMethod(CXXMethodDecl* dispatch) { |
| 1255 SourceLocation loc = dispatch->getLocStart(); |
| 1256 SourceManager& manager = instance_.getSourceManager(); |
| 1257 FullSourceLoc full_loc(loc, manager); |
| 1258 diagnostic_.Report(full_loc, diag_manual_dispatch_method_note_) << dispatch; |
| 1259 } |
| 1260 |
| 1261 void BlinkGCPluginConsumer::NoteBaseRequiresTracing(BasePoint* base) { |
| 1262 SourceLocation loc = base->spec().getLocStart(); |
| 1263 SourceManager& manager = instance_.getSourceManager(); |
| 1264 FullSourceLoc full_loc(loc, manager); |
| 1265 diagnostic_.Report(full_loc, diag_base_requires_tracing_note_) |
| 1266 << base->info()->record(); |
| 1267 } |
| 1268 |
| 1269 void BlinkGCPluginConsumer::NoteFieldRequiresTracing( |
| 1270 RecordInfo* holder, |
| 1271 FieldDecl* field) { |
| 1272 NoteField(field, diag_field_requires_tracing_note_); |
| 1273 } |
| 1274 |
| 1275 void BlinkGCPluginConsumer::NotePartObjectContainsGCRoot(FieldPoint* point) { |
| 1276 FieldDecl* field = point->field(); |
| 1277 SourceLocation loc = field->getLocStart(); |
| 1278 SourceManager& manager = instance_.getSourceManager(); |
| 1279 FullSourceLoc full_loc(loc, manager); |
| 1280 diagnostic_.Report(full_loc, diag_part_object_contains_gc_root_note_) |
| 1281 << field << field->getParent(); |
| 1282 } |
| 1283 |
| 1284 void BlinkGCPluginConsumer::NoteFieldContainsGCRoot(FieldPoint* point) { |
| 1285 NoteField(point, diag_field_contains_gc_root_note_); |
| 1286 } |
| 1287 |
| 1288 void BlinkGCPluginConsumer::NoteUserDeclaredDestructor(CXXMethodDecl* dtor) { |
| 1289 SourceLocation loc = dtor->getLocStart(); |
| 1290 SourceManager& manager = instance_.getSourceManager(); |
| 1291 FullSourceLoc full_loc(loc, manager); |
| 1292 diagnostic_.Report(full_loc, diag_user_declared_destructor_note_); |
| 1293 } |
| 1294 |
| 1295 void BlinkGCPluginConsumer::NoteUserDeclaredFinalizer(CXXMethodDecl* dtor) { |
| 1296 SourceLocation loc = dtor->getLocStart(); |
| 1297 SourceManager& manager = instance_.getSourceManager(); |
| 1298 FullSourceLoc full_loc(loc, manager); |
| 1299 diagnostic_.Report(full_loc, diag_user_declared_finalizer_note_); |
| 1300 } |
| 1301 |
| 1302 void BlinkGCPluginConsumer::NoteBaseRequiresFinalization(BasePoint* base) { |
| 1303 SourceLocation loc = base->spec().getLocStart(); |
| 1304 SourceManager& manager = instance_.getSourceManager(); |
| 1305 FullSourceLoc full_loc(loc, manager); |
| 1306 diagnostic_.Report(full_loc, diag_base_requires_finalization_note_) |
| 1307 << base->info()->record(); |
| 1308 } |
| 1309 |
| 1310 void BlinkGCPluginConsumer::NoteField(FieldPoint* point, unsigned note) { |
| 1311 NoteField(point->field(), note); |
| 1312 } |
| 1313 |
| 1314 void BlinkGCPluginConsumer::NoteField(FieldDecl* field, unsigned note) { |
| 1315 SourceLocation loc = field->getLocStart(); |
| 1316 SourceManager& manager = instance_.getSourceManager(); |
| 1317 FullSourceLoc full_loc(loc, manager); |
| 1318 diagnostic_.Report(full_loc, note) << field; |
| 1319 } |
| 1320 |
| 1321 void BlinkGCPluginConsumer::NoteOverriddenNonVirtualTrace( |
| 1322 CXXMethodDecl* overridden) { |
| 1323 SourceLocation loc = overridden->getLocStart(); |
| 1324 SourceManager& manager = instance_.getSourceManager(); |
| 1325 FullSourceLoc full_loc(loc, manager); |
| 1326 diagnostic_.Report(full_loc, diag_overridden_non_virtual_trace_note_) |
| 1327 << overridden; |
| 1328 } |
OLD | NEW |