1 // Copyright (c) 2012 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 "FindBadConstructsConsumer.h"
6
7 #include "Util.h"
8 #include "clang/AST/Attr.h"
9 #include "clang/Frontend/CompilerInstance.h"
10 #include "clang/Lex/Lexer.h"
11 #include "clang/Sema/Sema.h"
12 #include "llvm/Support/raw_ostream.h"
13
14 using namespace clang;
15
16 namespace chrome_checker {
17
18 namespace {
19
20 // Returns the underlying Type for |type| by expanding typedefs and removing
21 // any namespace qualifiers. This is similar to desugaring, except that for
22 // ElaboratedTypes, desugar will unwrap too much.
UnwrapType(const Type * type)23 const Type* UnwrapType(const Type* type) {
24 if (const ElaboratedType* elaborated = dyn_cast<ElaboratedType>(type))
25 return UnwrapType(elaborated->getNamedType().getTypePtr());
26 if (const TypedefType* typedefed = dyn_cast<TypedefType>(type))
27 return UnwrapType(typedefed->desugar().getTypePtr());
28 return type;
29 }
30
InTestingNamespace(const Decl * record)31 bool InTestingNamespace(const Decl* record) {
32 return GetNamespace(record).find("testing") != std::string::npos;
33 }
34
IsGtestTestFixture(const CXXRecordDecl * decl)35 bool IsGtestTestFixture(const CXXRecordDecl* decl) {
36 return decl->getQualifiedNameAsString() == "testing::Test";
37 }
38
IsMethodInTestingNamespace(const CXXMethodDecl * method)39 bool IsMethodInTestingNamespace(const CXXMethodDecl* method) {
40 for (auto* overridden : method->overridden_methods()) {
41 if (IsMethodInTestingNamespace(overridden) ||
42 // Provide an exception for ::testing::Test. gtest itself uses some
43 // magic to try to make sure SetUp()/TearDown() aren't capitalized
44 // incorrectly, but having the plugin enforce override is also nice.
45 (InTestingNamespace(overridden) &&
46 !IsGtestTestFixture(overridden->getParent()))) {
47 return true;
48 }
49 }
50
51 return false;
52 }
53
IsGmockObject(const CXXRecordDecl * decl)54 bool IsGmockObject(const CXXRecordDecl* decl) {
55 // If |record| has member variables whose types are in the "testing" namespace
56 // (which is how gmock works behind the scenes), there's a really high chance
57 // that |record| is a gmock object.
58 for (auto* field : decl->fields()) {
59 CXXRecordDecl* record_type = field->getTypeSourceInfo()
60 ->getTypeLoc()
61 .getTypePtr()
62 ->getAsCXXRecordDecl();
63 if (record_type) {
64 if (InTestingNamespace(record_type)) {
65 return true;
66 }
67 }
68 }
69 return false;
70 }
71
IsPodOrTemplateType(const CXXRecordDecl & record)72 bool IsPodOrTemplateType(const CXXRecordDecl& record) {
73 return record.isPOD() ||
74 record.getDescribedClassTemplate() ||
75 record.getTemplateSpecializationKind() ||
76 record.isDependentType();
77 }
78
79 // Use a local RAV implementation to simply collect all FunctionDecls marked for
80 // late template parsing. This happens with the flag -fdelayed-template-parsing,
81 // which is on by default in MSVC-compatible mode.
GetLateParsedFunctionDecls(TranslationUnitDecl * decl)82 std::set<FunctionDecl*> GetLateParsedFunctionDecls(TranslationUnitDecl* decl) {
83 struct Visitor : public RecursiveASTVisitor<Visitor> {
84 bool VisitFunctionDecl(FunctionDecl* function_decl) {
85 if (function_decl->isLateTemplateParsed())
86 late_parsed_decls.insert(function_decl);
87 return true;
88 }
89
90 std::set<FunctionDecl*> late_parsed_decls;
91 } v;
92 v.TraverseDecl(decl);
93 return v.late_parsed_decls;
94 }
95
GetAutoReplacementTypeAsString(QualType type,StorageClass storage_class)96 std::string GetAutoReplacementTypeAsString(QualType type,
97 StorageClass storage_class) {
98 QualType non_reference_type = type.getNonReferenceType();
99 if (!non_reference_type->isPointerType())
100 return storage_class == SC_Static ? "static auto" : "auto";
101
102 std::string result = GetAutoReplacementTypeAsString(
103 non_reference_type->getPointeeType(), storage_class);
104 result += "*";
105 if (non_reference_type.isConstQualified())
106 result += " const";
107 if (non_reference_type.isVolatileQualified())
108 result += " volatile";
109 if (type->isReferenceType() && !non_reference_type.isConstQualified()) {
110 if (type->isLValueReferenceType())
111 result += "&";
112 else if (type->isRValueReferenceType())
113 result += "&&";
114 }
115 return result;
116 }
117
118 } // namespace
119
FindBadConstructsConsumer(CompilerInstance & instance,const Options & options)120 FindBadConstructsConsumer::FindBadConstructsConsumer(CompilerInstance& instance,
121 const Options& options)
122 : ChromeClassTester(instance, options) {
123 if (options.check_ipc) {
124 ipc_visitor_.reset(new CheckIPCVisitor(instance));
125 }
126
127 // Messages for virtual methods.
128 diag_method_requires_override_ = diagnostic().getCustomDiagID(
129 getErrorLevel(),
130 "[chromium-style] Overriding method must be marked with 'override' or "
131 "'final'.");
132 diag_redundant_virtual_specifier_ = diagnostic().getCustomDiagID(
133 getErrorLevel(), "[chromium-style] %0 is redundant; %1 implies %0.");
134 diag_will_be_redundant_virtual_specifier_ = diagnostic().getCustomDiagID(
135 getErrorLevel(), "[chromium-style] %0 will be redundant; %1 implies %0.");
136 // http://llvm.org/bugs/show_bug.cgi?id=21051 has been filed to make this a
137 // Clang warning.
138 diag_base_method_virtual_and_final_ = diagnostic().getCustomDiagID(
139 getErrorLevel(),
140 "[chromium-style] The virtual method does not override anything and is "
141 "final; consider making it non-virtual.");
142 diag_virtual_with_inline_body_ = diagnostic().getCustomDiagID(
143 getErrorLevel(),
144 "[chromium-style] virtual methods with non-empty bodies shouldn't be "
145 "declared inline.");
146
147 // Messages for constructors.
148 diag_no_explicit_ctor_ = diagnostic().getCustomDiagID(
149 getErrorLevel(),
150 "[chromium-style] Complex class/struct needs an explicit out-of-line "
151 "constructor.");
152 diag_no_explicit_copy_ctor_ = diagnostic().getCustomDiagID(
153 getErrorLevel(),
154 "[chromium-style] Complex class/struct needs an explicit out-of-line "
155 "copy constructor.");
156 diag_inline_complex_ctor_ = diagnostic().getCustomDiagID(
157 getErrorLevel(),
158 "[chromium-style] Complex constructor has an inlined body.");
159
160 // Messages for destructors.
161 diag_no_explicit_dtor_ = diagnostic().getCustomDiagID(
162 getErrorLevel(),
163 "[chromium-style] Complex class/struct needs an explicit out-of-line "
164 "destructor.");
165 diag_inline_complex_dtor_ = diagnostic().getCustomDiagID(
166 getErrorLevel(),
167 "[chromium-style] Complex destructor has an inline body.");
168
169 // Messages for refcounted objects.
170 diag_refcounted_needs_explicit_dtor_ = diagnostic().getCustomDiagID(
171 getErrorLevel(),
172 "[chromium-style] Classes that are ref-counted should have explicit "
173 "destructors that are declared protected or private.");
174 diag_refcounted_with_public_dtor_ = diagnostic().getCustomDiagID(
175 getErrorLevel(),
176 "[chromium-style] Classes that are ref-counted should have "
177 "destructors that are declared protected or private.");
178 diag_refcounted_with_protected_non_virtual_dtor_ =
179 diagnostic().getCustomDiagID(
180 getErrorLevel(),
181 "[chromium-style] Classes that are ref-counted and have non-private "
182 "destructors should declare their destructor virtual.");
183
184 // Miscellaneous messages.
185 diag_weak_ptr_factory_order_ = diagnostic().getCustomDiagID(
186 getErrorLevel(),
187 "[chromium-style] WeakPtrFactory members which refer to their outer "
188 "class must be the last member in the outer class definition.");
189 diag_bad_enum_max_value_ = diagnostic().getCustomDiagID(
190 getErrorLevel(),
191 "[chromium-style] kMaxValue enumerator does not match max value %0 of "
192 "other enumerators");
193 diag_enum_max_value_unique_ = diagnostic().getCustomDiagID(
194 getErrorLevel(),
195 "[chromium-style] kMaxValue enumerator should not have a unique value: "
196 "it should share the value of the highest enumerator");
197 diag_auto_deduced_to_a_pointer_type_ =
198 diagnostic().getCustomDiagID(getErrorLevel(),
199 "[chromium-style] auto variable type "
200 "must not deduce to a raw pointer "
201 "type.");
202
203 // Registers notes to make it easier to interpret warnings.
204 diag_note_inheritance_ = diagnostic().getCustomDiagID(
205 DiagnosticsEngine::Note, "[chromium-style] %0 inherits from %1 here");
206 diag_note_implicit_dtor_ = diagnostic().getCustomDiagID(
207 DiagnosticsEngine::Note,
208 "[chromium-style] No explicit destructor for %0 defined");
209 diag_note_public_dtor_ = diagnostic().getCustomDiagID(
210 DiagnosticsEngine::Note,
211 "[chromium-style] Public destructor declared here");
212 diag_note_protected_non_virtual_dtor_ = diagnostic().getCustomDiagID(
213 DiagnosticsEngine::Note,
214 "[chromium-style] Protected non-virtual destructor declared here");
215 }
216
Traverse(ASTContext & context)217 void FindBadConstructsConsumer::Traverse(ASTContext& context) {
218 if (ipc_visitor_) {
219 ipc_visitor_->set_context(&context);
220 ParseFunctionTemplates(context.getTranslationUnitDecl());
221 }
222 RecursiveASTVisitor::TraverseDecl(context.getTranslationUnitDecl());
223 if (ipc_visitor_) ipc_visitor_->set_context(nullptr);
224 }
225
TraverseDecl(Decl * decl)226 bool FindBadConstructsConsumer::TraverseDecl(Decl* decl) {
227 if (ipc_visitor_) ipc_visitor_->BeginDecl(decl);
228 bool result = RecursiveASTVisitor::TraverseDecl(decl);
229 if (ipc_visitor_) ipc_visitor_->EndDecl();
230 return result;
231 }
232
VisitEnumDecl(clang::EnumDecl * decl)233 bool FindBadConstructsConsumer::VisitEnumDecl(clang::EnumDecl* decl) {
234 CheckEnumMaxValue(decl);
235 return true;
236 }
237
VisitTagDecl(clang::TagDecl * tag_decl)238 bool FindBadConstructsConsumer::VisitTagDecl(clang::TagDecl* tag_decl) {
239 if (tag_decl->isCompleteDefinition())
240 CheckTag(tag_decl);
241 return true;
242 }
243
VisitTemplateSpecializationType(TemplateSpecializationType * spec)244 bool FindBadConstructsConsumer::VisitTemplateSpecializationType(
245 TemplateSpecializationType* spec) {
246 if (ipc_visitor_) ipc_visitor_->VisitTemplateSpecializationType(spec);
247 return true;
248 }
249
VisitCallExpr(CallExpr * call_expr)250 bool FindBadConstructsConsumer::VisitCallExpr(CallExpr* call_expr) {
251 if (ipc_visitor_) ipc_visitor_->VisitCallExpr(call_expr);
252 return true;
253 }
254
VisitVarDecl(clang::VarDecl * var_decl)255 bool FindBadConstructsConsumer::VisitVarDecl(clang::VarDecl* var_decl) {
256 CheckVarDecl(var_decl);
257 return true;
258 }
259
CheckChromeClass(LocationType location_type,SourceLocation record_location,CXXRecordDecl * record)260 void FindBadConstructsConsumer::CheckChromeClass(LocationType location_type,
261 SourceLocation record_location,
262 CXXRecordDecl* record) {
263 bool implementation_file = InImplementationFile(record_location);
264
265 if (!implementation_file) {
266 // Only check for "heavy" constructors/destructors in header files;
267 // within implementation files, there is no performance cost.
268
269 // If this is a POD or a class template or a type dependent on a
270 // templated class, assume there's no ctor/dtor/virtual method
271 // optimization that we should do.
272 if (!IsPodOrTemplateType(*record))
273 CheckCtorDtorWeight(record_location, record);
274 }
275
276 bool warn_on_inline_bodies = !implementation_file;
277 // Check that all virtual methods are annotated with override or final.
278 // Note this could also apply to templates, but for some reason Clang
279 // does not always see the "override", so we get false positives.
280 // See http://llvm.org/bugs/show_bug.cgi?id=18440 and
281 // http://llvm.org/bugs/show_bug.cgi?id=21942
282 if (!IsPodOrTemplateType(*record))
283 CheckVirtualMethods(record_location, record, warn_on_inline_bodies);
284
285 // TODO(dcheng): This is needed because some of the diagnostics for refcounted
286 // classes use DiagnosticsEngine::Report() directly, and there are existing
287 // violations in Blink. This should be removed once the checks are
288 // modularized.
289 if (location_type != LocationType::kBlink)
290 CheckRefCountedDtors(record_location, record);
291
292 CheckWeakPtrFactoryMembers(record_location, record);
293 }
294
CheckEnumMaxValue(EnumDecl * decl)295 void FindBadConstructsConsumer::CheckEnumMaxValue(EnumDecl* decl) {
296 if (!decl->isScoped())
297 return;
298
299 clang::EnumConstantDecl* max_value = nullptr;
300 std::set<clang::EnumConstantDecl*> max_enumerators;
301 llvm::APSInt max_seen;
302 for (clang::EnumConstantDecl* enumerator : decl->enumerators()) {
303 if (enumerator->getName() == "kMaxValue")
304 max_value = enumerator;
305
306 llvm::APSInt current_value = enumerator->getInitVal();
307 if (max_enumerators.empty()) {
308 max_enumerators.emplace(enumerator);
309 max_seen = current_value;
310 continue;
311 }
312
313 assert(max_seen.isSigned() == current_value.isSigned());
314
315 if (current_value < max_seen)
316 continue;
317
318 if (current_value == max_seen) {
319 max_enumerators.emplace(enumerator);
320 continue;
321 }
322
323 assert(current_value > max_seen);
324 max_enumerators.clear();
325 max_enumerators.emplace(enumerator);
326 max_seen = current_value;
327 }
328
329 if (!max_value)
330 return;
331
332 if (max_enumerators.find(max_value) == max_enumerators.end()) {
333 ReportIfSpellingLocNotIgnored(max_value->getLocation(),
334 diag_bad_enum_max_value_)
335 << max_seen.toString(10);
336 } else if (max_enumerators.size() < 2) {
337 ReportIfSpellingLocNotIgnored(decl->getLocation(),
338 diag_enum_max_value_unique_);
339 }
340 }
341
CheckCtorDtorWeight(SourceLocation record_location,CXXRecordDecl * record)342 void FindBadConstructsConsumer::CheckCtorDtorWeight(
343 SourceLocation record_location,
344 CXXRecordDecl* record) {
345 // We don't handle anonymous structs. If this record doesn't have a
346 // name, it's of the form:
347 //
348 // struct {
349 // ...
350 // } name_;
351 if (record->getIdentifier() == NULL)
352 return;
353
354 // We don't handle unions.
355 if (record->isUnion())
356 return;
357
358 // Skip records that derive from ignored base classes.
359 if (HasIgnoredBases(record))
360 return;
361
362 // Count the number of templated base classes as a feature of whether the
363 // destructor can be inlined.
364 int templated_base_classes = 0;
365 for (CXXRecordDecl::base_class_const_iterator it = record->bases_begin();
366 it != record->bases_end();
367 ++it) {
368 if (it->getTypeSourceInfo()->getTypeLoc().getTypeLocClass() ==
369 TypeLoc::TemplateSpecialization) {
370 ++templated_base_classes;
371 }
372 }
373
374 // Count the number of trivial and non-trivial member variables.
375 int trivial_member = 0;
376 int non_trivial_member = 0;
377 int templated_non_trivial_member = 0;
378 for (RecordDecl::field_iterator it = record->field_begin();
379 it != record->field_end();
380 ++it) {
381 CountType(it->getType().getTypePtr(),
382 &trivial_member,
383 &non_trivial_member,
384 &templated_non_trivial_member);
385 }
386
387 // Check to see if we need to ban inlined/synthesized constructors. Note
388 // that the cutoffs here are kind of arbitrary. Scores over 10 break.
389 int dtor_score = 0;
390 // Deriving from a templated base class shouldn't be enough to trigger
391 // the ctor warning, but if you do *anything* else, it should.
392 //
393 // TODO(erg): This is motivated by templated base classes that don't have
394 // any data members. Somehow detect when templated base classes have data
395 // members and treat them differently.
396 dtor_score += templated_base_classes * 9;
397 // Instantiating a template is an insta-hit.
398 dtor_score += templated_non_trivial_member * 10;
399 // The fourth normal class member should trigger the warning.
400 dtor_score += non_trivial_member * 3;
401
402 int ctor_score = dtor_score;
403 // You should be able to have 9 ints before we warn you.
404 ctor_score += trivial_member;
405
406 if (ctor_score >= 10) {
407 if (!record->hasUserDeclaredConstructor()) {
408 ReportIfSpellingLocNotIgnored(record_location, diag_no_explicit_ctor_);
409 } else {
410 // Iterate across all the constructors in this file and yell if we
411 // find one that tries to be inline.
412 for (CXXRecordDecl::ctor_iterator it = record->ctor_begin();
413 it != record->ctor_end();
414 ++it) {
415 // The current check is buggy. An implicit copy constructor does not
416 // have an inline body, so this check never fires for classes with a
417 // user-declared out-of-line constructor.
418 if (it->hasInlineBody()) {
419 if (it->isCopyConstructor() &&
420 !record->hasUserDeclaredCopyConstructor()) {
421 // In general, implicit constructors are generated on demand. But
422 // in the Windows component build, dllexport causes instantiation of
423 // the copy constructor which means that this fires on many more
424 // classes. For now, suppress this on dllexported classes.
425 // (This does mean that windows component builds will not emit this
426 // warning in some cases where it is emitted in other configs, but
427 // that's the better tradeoff at this point).
428 // TODO(dcheng): With the RecursiveASTVisitor, these warnings might
429 // be emitted on other platforms too, reevaluate if we want to keep
430 // surpressing this then http://crbug.com/467288
431 if (!record->hasAttr<DLLExportAttr>())
432 ReportIfSpellingLocNotIgnored(record_location,
433 diag_no_explicit_copy_ctor_);
434 } else {
435 // See the comment in the previous branch about copy constructors.
436 // This does the same for implicit move constructors.
437 bool is_likely_compiler_generated_dllexport_move_ctor =
438 it->isMoveConstructor() &&
439 !record->hasUserDeclaredMoveConstructor() &&
440 record->hasAttr<DLLExportAttr>();
441 if (!is_likely_compiler_generated_dllexport_move_ctor)
442 ReportIfSpellingLocNotIgnored(it->getInnerLocStart(),
443 diag_inline_complex_ctor_);
444 }
445 } else if (it->isInlined() && !it->isInlineSpecified() &&
446 !it->isDeleted() && (!it->isCopyOrMoveConstructor() ||
447 it->isExplicitlyDefaulted())) {
448 // isInlined() is a more reliable check than hasInlineBody(), but
449 // unfortunately, it results in warnings for implicit copy/move
450 // constructors in the previously mentioned situation. To preserve
451 // compatibility with existing Chromium code, only warn if it's an
452 // explicitly defaulted copy or move constructor.
453 ReportIfSpellingLocNotIgnored(it->getInnerLocStart(),
454 diag_inline_complex_ctor_);
455 }
456 }
457 }
458 }
459
460 // The destructor side is equivalent except that we don't check for
461 // trivial members; 20 ints don't need a destructor.
462 if (dtor_score >= 10 && !record->hasTrivialDestructor()) {
463 if (!record->hasUserDeclaredDestructor()) {
464 ReportIfSpellingLocNotIgnored(record_location, diag_no_explicit_dtor_);
465 } else if (CXXDestructorDecl* dtor = record->getDestructor()) {
466 if (dtor->isInlined() && !dtor->isInlineSpecified() &&
467 !dtor->isDeleted()) {
468 ReportIfSpellingLocNotIgnored(dtor->getInnerLocStart(),
469 diag_inline_complex_dtor_);
470 }
471 }
472 }
473 }
474
475 SuppressibleDiagnosticBuilder
ReportIfSpellingLocNotIgnored(SourceLocation loc,unsigned diagnostic_id)476 FindBadConstructsConsumer::ReportIfSpellingLocNotIgnored(
477 SourceLocation loc,
478 unsigned diagnostic_id) {
479 LocationType type =
480 ClassifyLocation(instance().getSourceManager().getSpellingLoc(loc));
481 bool ignored = type == LocationType::kThirdParty;
482 if (type == LocationType::kBlink) {
483 if (diagnostic_id == diag_no_explicit_ctor_ ||
484 diagnostic_id == diag_no_explicit_copy_ctor_ ||
485 diagnostic_id == diag_inline_complex_ctor_ ||
486 diagnostic_id == diag_no_explicit_dtor_ ||
487 diagnostic_id == diag_inline_complex_dtor_ ||
488 diagnostic_id == diag_refcounted_with_protected_non_virtual_dtor_ ||
489 diagnostic_id == diag_virtual_with_inline_body_) {
490 // Certain checks are ignored in Blink for historical reasons.
491 // TODO(dcheng): Make this list smaller.
492 ignored = true;
493 }
494 }
495 return SuppressibleDiagnosticBuilder(&diagnostic(), loc, diagnostic_id,
496 ignored);
497 }
498
499 // Checks that virtual methods are correctly annotated, and have no body in a
500 // header file.
CheckVirtualMethods(SourceLocation record_location,CXXRecordDecl * record,bool warn_on_inline_bodies)501 void FindBadConstructsConsumer::CheckVirtualMethods(
502 SourceLocation record_location,
503 CXXRecordDecl* record,
504 bool warn_on_inline_bodies) {
505 if (IsGmockObject(record)) {
506 if (!options_.check_gmock_objects)
507 return;
508 warn_on_inline_bodies = false;
509 }
510
511 for (CXXRecordDecl::method_iterator it = record->method_begin();
512 it != record->method_end();
513 ++it) {
514 if (it->isCopyAssignmentOperator() || isa<CXXConstructorDecl>(*it)) {
515 // Ignore constructors and assignment operators.
516 } else if (isa<CXXDestructorDecl>(*it) &&
517 !record->hasUserDeclaredDestructor()) {
518 // Ignore non-user-declared destructors.
519 } else if (!it->isVirtual()) {
520 continue;
521 } else {
522 CheckVirtualSpecifiers(*it);
523 if (warn_on_inline_bodies)
524 CheckVirtualBodies(*it);
525 }
526 }
527 }
528
529 // Makes sure that virtual methods use the most appropriate specifier. If a
530 // virtual method overrides a method from a base class, only the override
531 // specifier should be used. If the method should not be overridden by derived
532 // classes, only the final specifier should be used.
CheckVirtualSpecifiers(const CXXMethodDecl * method)533 void FindBadConstructsConsumer::CheckVirtualSpecifiers(
534 const CXXMethodDecl* method) {
535 bool is_override = method->size_overridden_methods() > 0;
536 bool has_virtual = method->isVirtualAsWritten();
537 OverrideAttr* override_attr = method->getAttr<OverrideAttr>();
538 FinalAttr* final_attr = method->getAttr<FinalAttr>();
539
540 if (IsMethodInTestingNamespace(method))
541 return;
542
543 SourceManager& manager = instance().getSourceManager();
544 const LangOptions& lang_opts = instance().getLangOpts();
545
546 // Grab the stream of tokens from the beginning of the method
547 bool remove_virtual = false;
548 bool add_override = false;
549
550 // Complain if a method is annotated virtual && (override || final).
551 if (has_virtual && (override_attr || final_attr))
552 remove_virtual = true;
553
554 // Complain if a method is an override and is not annotated with override or
555 // final.
556 if (is_override && !override_attr && !final_attr) {
557 add_override = true;
558 // Also remove the virtual in the same fixit if currently present.
559 if (has_virtual)
560 remove_virtual = true;
561 }
562
563 if (final_attr && override_attr) {
564 ReportIfSpellingLocNotIgnored(override_attr->getLocation(),
565 diag_redundant_virtual_specifier_)
566 << override_attr << final_attr
567 << FixItHint::CreateRemoval(override_attr->getRange());
568 }
569
570 if (!remove_virtual && !add_override)
571 return;
572
573 // Deletion of virtual and insertion of override are tricky. The AST does not
574 // expose the location of `virtual` or `=`: the former is useful when trying
575 // to remove `virtual, while the latter is useful when trying to insert
576 // `override`. Iterate over the tokens from |method->getLocStart()| until:
577 // 1. A `{` not nested inside parentheses is found or
578 // 2. A `=` not nested inside parentheses is found or
579 // 3. A `;` not nested inside parentheses is found or
580 // 4. The end of the file is found.
581 SourceLocation virtual_loc;
582 SourceLocation override_insertion_loc;
583 // Attempt to set up the lexer in raw mode.
584 std::pair<FileID, unsigned> decomposed_start =
585 manager.getDecomposedLoc(method->getLocStart());
586 bool invalid = false;
587 StringRef buffer = manager.getBufferData(decomposed_start.first, &invalid);
588 if (!invalid) {
589 int nested_parentheses = 0;
590 Lexer lexer(manager.getLocForStartOfFile(decomposed_start.first), lang_opts,
591 buffer.begin(), buffer.begin() + decomposed_start.second,
592 buffer.end());
593 Token token;
594 while (!lexer.LexFromRawLexer(token)) {
595 // Found '=', ';', or '{'. No need to scan any further, since an override
596 // fixit hint won't be inserted after any of these tokens.
597 if ((token.is(tok::equal) || token.is(tok::semi) ||
598 token.is(tok::l_brace)) &&
599 nested_parentheses == 0) {
600 override_insertion_loc = token.getLocation();
601 break;
602 }
603 if (token.is(tok::l_paren)) {
604 ++nested_parentheses;
605 } else if (token.is(tok::r_paren)) {
606 --nested_parentheses;
607 } else if (token.is(tok::raw_identifier)) {
608 // TODO(dcheng): Unclear if this needs to check for nested parentheses
609 // as well?
610 if (token.getRawIdentifier() == "virtual")
611 virtual_loc = token.getLocation();
612 }
613 }
614 }
615
616 if (add_override && override_insertion_loc.isValid()) {
617 ReportIfSpellingLocNotIgnored(override_insertion_loc,
618 diag_method_requires_override_)
619 << FixItHint::CreateInsertion(override_insertion_loc, " override");
620 }
621 if (remove_virtual && virtual_loc.isValid()) {
622 ReportIfSpellingLocNotIgnored(
623 virtual_loc, add_override ? diag_will_be_redundant_virtual_specifier_
624 : diag_redundant_virtual_specifier_)
625 << "'virtual'"
626 // Slightly subtle: the else case handles both the currently and the
627 // will be redundant case for override. Doing the check this way also
628 // lets the plugin prioritize keeping 'final' over 'override' when both
629 // are present.
630 << (final_attr ? "'final'" : "'override'")
631 << FixItHint::CreateRemoval(
632 CharSourceRange::getTokenRange(SourceRange(virtual_loc)));
633 }
634 }
635
CheckVirtualBodies(const CXXMethodDecl * method)636 void FindBadConstructsConsumer::CheckVirtualBodies(
637 const CXXMethodDecl* method) {
638 // Virtual methods should not have inline definitions beyond "{}". This
639 // only matters for header files.
640 if (method->hasBody() && method->hasInlineBody()) {
641 if (CompoundStmt* cs = dyn_cast<CompoundStmt>(method->getBody())) {
642 if (cs->size()) {
643 SourceLocation loc = cs->getLBracLoc();
644 // CR_BEGIN_MSG_MAP_EX and BEGIN_SAFE_MSG_MAP_EX try to be compatible
645 // to BEGIN_MSG_MAP(_EX). So even though they are in chrome code,
646 // we can't easily fix them, so explicitly whitelist them here.
647 bool emit = true;
648 if (loc.isMacroID()) {
649 SourceManager& manager = instance().getSourceManager();
650 LocationType type = ClassifyLocation(manager.getSpellingLoc(loc));
651 if (type == LocationType::kThirdParty || type == LocationType::kBlink)
652 emit = false;
653 else {
654 StringRef name = Lexer::getImmediateMacroName(
655 loc, manager, instance().getLangOpts());
656 if (name == "CR_BEGIN_MSG_MAP_EX" ||
657 name == "BEGIN_SAFE_MSG_MAP_EX")
658 emit = false;
659 }
660 }
661 if (emit)
662 ReportIfSpellingLocNotIgnored(loc, diag_virtual_with_inline_body_);
663 }
664 }
665 }
666 }
667
CountType(const Type * type,int * trivial_member,int * non_trivial_member,int * templated_non_trivial_member)668 void FindBadConstructsConsumer::CountType(const Type* type,
669 int* trivial_member,
670 int* non_trivial_member,
671 int* templated_non_trivial_member) {
672 switch (type->getTypeClass()) {
673 case Type::Record: {
674 auto* record_decl = type->getAsCXXRecordDecl();
675 // Simplifying; the whole class isn't trivial if the dtor is, but
676 // we use this as a signal about complexity.
677 // Note that if a record doesn't have a definition, it doesn't matter how
678 // it's counted, since the translation unit will fail to build. In that
679 // case, just count it as a trivial member to avoid emitting warnings that
680 // might be spurious.
681 if (!record_decl->hasDefinition() || record_decl->hasTrivialDestructor())
682 (*trivial_member)++;
683 else
684 (*non_trivial_member)++;
685 break;
686 }
687 case Type::TemplateSpecialization: {
688 TemplateName name =
689 dyn_cast<TemplateSpecializationType>(type)->getTemplateName();
690 bool whitelisted_template = false;
691
692 // HACK: I'm at a loss about how to get the syntax checker to get
693 // whether a template is externed or not. For the first pass here,
694 // just do simple string comparisons.
695 if (TemplateDecl* decl = name.getAsTemplateDecl()) {
696 std::string base_name = decl->getNameAsString();
697 if (base_name == "basic_string")
698 whitelisted_template = true;
699 }
700
701 if (whitelisted_template)
702 (*non_trivial_member)++;
703 else
704 (*templated_non_trivial_member)++;
705 break;
706 }
707 case Type::Elaborated: {
708 CountType(dyn_cast<ElaboratedType>(type)->getNamedType().getTypePtr(),
709 trivial_member,
710 non_trivial_member,
711 templated_non_trivial_member);
712 break;
713 }
714 case Type::Typedef: {
715 while (const TypedefType* TT = dyn_cast<TypedefType>(type)) {
716 if (auto* decl = TT->getDecl()) {
717 const std::string name = decl->getNameAsString();
718 auto* context = decl->getDeclContext();
719 if (name == "atomic_int" && context->isStdNamespace()) {
720 (*trivial_member)++;
721 return;
722 }
723 type = decl->getUnderlyingType().getTypePtr();
724 }
725 }
726 CountType(type,
727 trivial_member,
728 non_trivial_member,
729 templated_non_trivial_member);
730 break;
731 }
732 default: {
733 // Stupid assumption: anything we see that isn't the above is a POD
734 // or reference type.
735 (*trivial_member)++;
736 break;
737 }
738 }
739 }
740
741 // Check |record| for issues that are problematic for ref-counted types.
742 // Note that |record| may not be a ref-counted type, but a base class for
743 // a type that is.
744 // If there are issues, update |loc| with the SourceLocation of the issue
745 // and returns appropriately, or returns None if there are no issues.
746 // static
747 FindBadConstructsConsumer::RefcountIssue
CheckRecordForRefcountIssue(const CXXRecordDecl * record,SourceLocation & loc)748 FindBadConstructsConsumer::CheckRecordForRefcountIssue(
749 const CXXRecordDecl* record,
750 SourceLocation& loc) {
751 if (!record->hasUserDeclaredDestructor()) {
752 loc = record->getLocation();
753 return ImplicitDestructor;
754 }
755
756 if (CXXDestructorDecl* dtor = record->getDestructor()) {
757 if (dtor->getAccess() == AS_public) {
758 loc = dtor->getInnerLocStart();
759 return PublicDestructor;
760 }
761 }
762
763 return None;
764 }
765
766 // Returns true if |base| specifies one of the Chromium reference counted
767 // classes (base::RefCounted / base::RefCountedThreadSafe).
IsRefCounted(const CXXBaseSpecifier * base,CXXBasePath & path)768 bool FindBadConstructsConsumer::IsRefCounted(
769 const CXXBaseSpecifier* base,
770 CXXBasePath& path) {
771 const TemplateSpecializationType* base_type =
772 dyn_cast<TemplateSpecializationType>(
773 UnwrapType(base->getType().getTypePtr()));
774 if (!base_type) {
775 // Base-most definition is not a template, so this cannot derive from
776 // base::RefCounted. However, it may still be possible to use with a
777 // scoped_refptr<> and support ref-counting, so this is not a perfect
778 // guarantee of safety.
779 return false;
780 }
781
782 TemplateName name = base_type->getTemplateName();
783 if (TemplateDecl* decl = name.getAsTemplateDecl()) {
784 std::string base_name = decl->getNameAsString();
785
786 // Check for both base::RefCounted and base::RefCountedThreadSafe.
787 if (base_name.compare(0, 10, "RefCounted") == 0 &&
788 GetNamespace(decl) == "base") {
789 return true;
790 }
791 }
792
793 return false;
794 }
795
796 // Returns true if |base| specifies a class that has a public destructor,
797 // either explicitly or implicitly.
798 // static
HasPublicDtorCallback(const CXXBaseSpecifier * base,CXXBasePath & path,void * user_data)799 bool FindBadConstructsConsumer::HasPublicDtorCallback(
800 const CXXBaseSpecifier* base,
801 CXXBasePath& path,
802 void* user_data) {
803 // Only examine paths that have public inheritance, as they are the
804 // only ones which will result in the destructor potentially being
805 // exposed. This check is largely redundant, as Chromium code should be
806 // exclusively using public inheritance.
807 if (path.Access != AS_public)
808 return false;
809
810 CXXRecordDecl* record =
811 dyn_cast<CXXRecordDecl>(base->getType()->getAs<RecordType>()->getDecl());
812 SourceLocation unused;
813 return None != CheckRecordForRefcountIssue(record, unused);
814 }
815
816 // Outputs a C++ inheritance chain as a diagnostic aid.
PrintInheritanceChain(const CXXBasePath & path)817 void FindBadConstructsConsumer::PrintInheritanceChain(const CXXBasePath& path) {
818 for (CXXBasePath::const_iterator it = path.begin(); it != path.end(); ++it) {
819 diagnostic().Report(it->Base->getLocStart(), diag_note_inheritance_)
820 << it->Class << it->Base->getType();
821 }
822 }
823
DiagnosticForIssue(RefcountIssue issue)824 unsigned FindBadConstructsConsumer::DiagnosticForIssue(RefcountIssue issue) {
825 switch (issue) {
826 case ImplicitDestructor:
827 return diag_refcounted_needs_explicit_dtor_;
828 case PublicDestructor:
829 return diag_refcounted_with_public_dtor_;
830 case None:
831 assert(false && "Do not call DiagnosticForIssue with issue None");
832 return 0;
833 }
834 assert(false);
835 return 0;
836 }
837
838 // Check |record| to determine if it has any problematic refcounting
839 // issues and, if so, print them as warnings/errors based on the current
840 // value of getErrorLevel().
841 //
842 // If |record| is a C++ class, and if it inherits from one of the Chromium
843 // ref-counting classes (base::RefCounted / base::RefCountedThreadSafe),
844 // ensure that there are no public destructors in the class hierarchy. This
845 // is to guard against accidentally stack-allocating a RefCounted class or
846 // sticking it in a non-ref-counted container (like std::unique_ptr<>).
CheckRefCountedDtors(SourceLocation record_location,CXXRecordDecl * record)847 void FindBadConstructsConsumer::CheckRefCountedDtors(
848 SourceLocation record_location,
849 CXXRecordDecl* record) {
850 // Skip anonymous structs.
851 if (record->getIdentifier() == NULL)
852 return;
853
854 // Determine if the current type is even ref-counted.
855 CXXBasePaths refcounted_path;
856 if (!record->lookupInBases(
857 [this](const CXXBaseSpecifier* base, CXXBasePath& path) {
858 return IsRefCounted(base, path);
859 },
860 refcounted_path)) {
861 return; // Class does not derive from a ref-counted base class.
862 }
863
864 // Easy check: Check to see if the current type is problematic.
865 SourceLocation loc;
866 RefcountIssue issue = CheckRecordForRefcountIssue(record, loc);
867 if (issue != None) {
868 diagnostic().Report(loc, DiagnosticForIssue(issue));
869 PrintInheritanceChain(refcounted_path.front());
870 return;
871 }
872 if (CXXDestructorDecl* dtor =
873 refcounted_path.begin()->back().Class->getDestructor()) {
874 if (dtor->getAccess() == AS_protected && !dtor->isVirtual()) {
875 loc = dtor->getInnerLocStart();
876 ReportIfSpellingLocNotIgnored(
877 loc, diag_refcounted_with_protected_non_virtual_dtor_);
878 return;
879 }
880 }
881
882 // Long check: Check all possible base classes for problematic
883 // destructors. This checks for situations involving multiple
884 // inheritance, where the ref-counted class may be implementing an
885 // interface that has a public or implicit destructor.
886 //
887 // struct SomeInterface {
888 // virtual void DoFoo();
889 // };
890 //
891 // struct RefCountedInterface
892 // : public base::RefCounted<RefCountedInterface>,
893 // public SomeInterface {
894 // private:
895 // friend class base::Refcounted<RefCountedInterface>;
896 // virtual ~RefCountedInterface() {}
897 // };
898 //
899 // While RefCountedInterface is "safe", in that its destructor is
900 // private, it's possible to do the following "unsafe" code:
901 // scoped_refptr<RefCountedInterface> some_class(
902 // new RefCountedInterface);
903 // // Calls SomeInterface::~SomeInterface(), which is unsafe.
904 // delete static_cast<SomeInterface*>(some_class.get());
905 if (!options_.check_base_classes)
906 return;
907
908 // Find all public destructors. This will record the class hierarchy
909 // that leads to the public destructor in |dtor_paths|.
910 CXXBasePaths dtor_paths;
911 if (!record->lookupInBases(
912 [](const CXXBaseSpecifier* base, CXXBasePath& path) {
913 // TODO(thakis): Inline HasPublicDtorCallback() after clang roll.
914 return HasPublicDtorCallback(base, path, nullptr);
915 },
916 dtor_paths)) {
917 return;
918 }
919
920 for (CXXBasePaths::const_paths_iterator it = dtor_paths.begin();
921 it != dtor_paths.end();
922 ++it) {
923 // The record with the problem will always be the last record
924 // in the path, since it is the record that stopped the search.
925 const CXXRecordDecl* problem_record = dyn_cast<CXXRecordDecl>(
926 it->back().Base->getType()->getAs<RecordType>()->getDecl());
927
928 issue = CheckRecordForRefcountIssue(problem_record, loc);
929
930 if (issue == ImplicitDestructor) {
931 diagnostic().Report(record_location,
932 diag_refcounted_needs_explicit_dtor_);
933 PrintInheritanceChain(refcounted_path.front());
934 diagnostic().Report(loc, diag_note_implicit_dtor_) << problem_record;
935 PrintInheritanceChain(*it);
936 } else if (issue == PublicDestructor) {
937 diagnostic().Report(record_location, diag_refcounted_with_public_dtor_);
938 PrintInheritanceChain(refcounted_path.front());
939 diagnostic().Report(loc, diag_note_public_dtor_);
940 PrintInheritanceChain(*it);
941 }
942 }
943 }
944
945 // Check for any problems with WeakPtrFactory class members. This currently
946 // only checks that any WeakPtrFactory<T> member of T appears as the last
947 // data member in T. We could consider checking for bad uses of
948 // WeakPtrFactory to refer to other data members, but that would require
949 // looking at the initializer list in constructors to see what the factory
950 // points to.
951 // Note, if we later add other unrelated checks of data members, we should
952 // consider collapsing them in to one loop to avoid iterating over the data
953 // members more than once.
CheckWeakPtrFactoryMembers(SourceLocation record_location,CXXRecordDecl * record)954 void FindBadConstructsConsumer::CheckWeakPtrFactoryMembers(
955 SourceLocation record_location,
956 CXXRecordDecl* record) {
957 // Skip anonymous structs.
958 if (record->getIdentifier() == NULL)
959 return;
960
961 // Iterate through members of the class.
962 RecordDecl::field_iterator iter(record->field_begin()),
963 the_end(record->field_end());
964 SourceLocation weak_ptr_factory_location; // Invalid initially.
965 for (; iter != the_end; ++iter) {
966 const TemplateSpecializationType* template_spec_type =
967 iter->getType().getTypePtr()->getAs<TemplateSpecializationType>();
968 bool param_is_weak_ptr_factory_to_self = false;
969 if (template_spec_type) {
970 const TemplateDecl* template_decl =
971 template_spec_type->getTemplateName().getAsTemplateDecl();
972 if (template_decl && template_spec_type->getNumArgs() == 1) {
973 if (template_decl->getNameAsString().compare("WeakPtrFactory") == 0 &&
974 GetNamespace(template_decl) == "base") {
975 // Only consider WeakPtrFactory members which are specialized for the
976 // owning class.
977 const TemplateArgument& arg = template_spec_type->getArg(0);
978 if (arg.getAsType().getTypePtr()->getAsCXXRecordDecl() ==
979 record->getTypeForDecl()->getAsCXXRecordDecl()) {
980 if (!weak_ptr_factory_location.isValid()) {
981 // Save the first matching WeakPtrFactory member for the
982 // diagnostic.
983 weak_ptr_factory_location = iter->getLocation();
984 }
985 param_is_weak_ptr_factory_to_self = true;
986 }
987 }
988 }
989 }
990 // If we've already seen a WeakPtrFactory<OwningType> and this param is not
991 // one of those, it means there is at least one member after a factory.
992 if (weak_ptr_factory_location.isValid() &&
993 !param_is_weak_ptr_factory_to_self) {
994 ReportIfSpellingLocNotIgnored(weak_ptr_factory_location,
995 diag_weak_ptr_factory_order_);
996 }
997 }
998 }
999
1000 // Copied from BlinkGCPlugin, see crrev.com/1135333007
ParseFunctionTemplates(TranslationUnitDecl * decl)1001 void FindBadConstructsConsumer::ParseFunctionTemplates(
1002 TranslationUnitDecl* decl) {
1003 if (!instance().getLangOpts().DelayedTemplateParsing)
1004 return; // Nothing to do.
1005
1006 std::set<FunctionDecl*> late_parsed_decls = GetLateParsedFunctionDecls(decl);
1007 clang::Sema& sema = instance().getSema();
1008
1009 for (const FunctionDecl* fd : late_parsed_decls) {
1010 assert(fd->isLateTemplateParsed());
1011
1012 if (instance().getSourceManager().isInSystemHeader(
1013 instance().getSourceManager().getSpellingLoc(fd->getLocation())))
1014 continue;
1015
1016 // Parse and build AST for yet-uninstantiated template functions.
1017 clang::LateParsedTemplate* lpt = sema.LateParsedTemplateMap[fd].get();
1018 sema.LateTemplateParser(sema.OpaqueParser, *lpt);
1019 }
1020 }
1021
CheckVarDecl(clang::VarDecl * var_decl)1022 void FindBadConstructsConsumer::CheckVarDecl(clang::VarDecl* var_decl) {
1023 // Lambda init-captures should be ignored.
1024 if (var_decl->isInitCapture())
1025 return;
1026
1027 // Check whether auto deduces to a raw pointer.
1028 QualType non_reference_type = var_decl->getType().getNonReferenceType();
1029 // We might have a case where the type is written as auto*, but the actual
1030 // type is deduced to be an int**. For that reason, keep going down the
1031 // pointee type until we get an 'auto' or a non-pointer type.
1032 for (;;) {
1033 const clang::AutoType* auto_type =
1034 non_reference_type->getAs<clang::AutoType>();
1035 if (auto_type) {
1036 if (auto_type->isDeduced()) {
1037 QualType deduced_type = auto_type->getDeducedType();
1038 if (!deduced_type.isNull() && deduced_type->isPointerType() &&
1039 !deduced_type->isFunctionPointerType()) {
1040 // Check if we should even be considering this type (note that there
1041 // should be fewer auto types than banned namespace/directory types,
1042 // so check this last.
1043 LocationType location_type =
1044 ClassifyLocation(var_decl->getLocStart());
1045 if (location_type != LocationType::kThirdParty) {
1046 // The range starts from |var_decl|'s loc start, which is the
1047 // beginning of the full expression defining this |var_decl|. It
1048 // ends, however, where this |var_decl|'s type loc ends, since
1049 // that's the end of the type of |var_decl|.
1050 // Note that the beginning source location of type loc omits cv
1051 // qualifiers, which is why it's not a good candidate to use for the
1052 // start of the range.
1053 clang::SourceRange range(
1054 var_decl->getLocStart(),
1055 var_decl->getTypeSourceInfo()->getTypeLoc().getLocEnd());
1056 ReportIfSpellingLocNotIgnored(range.getBegin(),
1057 diag_auto_deduced_to_a_pointer_type_)
1058 << FixItHint::CreateReplacement(
1059 range,
1060 GetAutoReplacementTypeAsString(
1061 var_decl->getType(), var_decl->getStorageClass()));
1062 }
1063 }
1064 }
1065 } else if (non_reference_type->isPointerType()) {
1066 non_reference_type = non_reference_type->getPointeeType();
1067 continue;
1068 }
1069 break;
1070 }
1071 }
1072
1073 } // namespace chrome_checker
1074