Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(152)

Side by Side Diff: tools/clang/plugins/FindBadConstructsConsumer.cpp

Issue 597863002: Update plugin to handle new style rules for virtual annotations. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: And one more thing... Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "FindBadConstructsConsumer.h" 5 #include "FindBadConstructsConsumer.h"
6 6
7 #include "clang/Frontend/CompilerInstance.h" 7 #include "clang/Frontend/CompilerInstance.h"
8 #include "clang/AST/Attr.h"
8 #include "clang/Lex/Lexer.h" 9 #include "clang/Lex/Lexer.h"
9 #include "llvm/Support/raw_ostream.h" 10 #include "llvm/Support/raw_ostream.h"
10 11
11 using namespace clang; 12 using namespace clang;
12 13
13 namespace chrome_checker { 14 namespace chrome_checker {
14 15
15 namespace { 16 namespace {
16 17
17 const char kMethodRequiresOverride[] = 18 const char kMethodRequiresOverride[] =
18 "[chromium-style] Overriding method must be marked with OVERRIDE."; 19 "[chromium-style] Overriding method must be marked with 'override' or "
19 const char kMethodRequiresVirtual[] = 20 "'final'.";
20 "[chromium-style] Overriding method must have \"virtual\" keyword."; 21 const char kRedundantVirtualSpecifier[] =
22 "[chromium-style] %0 is redundant; %1 implies %0.";
23 // http://llvm.org/bugs/show_bug.cgi?id=21051 has been filed to make this a
24 // Clang warning.
25 const char kBaseMethodVirtualAndFinal[] =
26 "[chromium-style] The virtual method does not override anything and is "
27 "final; consider making it non-virtual.";
21 const char kNoExplicitDtor[] = 28 const char kNoExplicitDtor[] =
22 "[chromium-style] Classes that are ref-counted should have explicit " 29 "[chromium-style] Classes that are ref-counted should have explicit "
23 "destructors that are declared protected or private."; 30 "destructors that are declared protected or private.";
24 const char kPublicDtor[] = 31 const char kPublicDtor[] =
25 "[chromium-style] Classes that are ref-counted should have " 32 "[chromium-style] Classes that are ref-counted should have "
26 "destructors that are declared protected or private."; 33 "destructors that are declared protected or private.";
27 const char kProtectedNonVirtualDtor[] = 34 const char kProtectedNonVirtualDtor[] =
28 "[chromium-style] Classes that are ref-counted and have non-private " 35 "[chromium-style] Classes that are ref-counted and have non-private "
29 "destructors should declare their destructor virtual."; 36 "destructors should declare their destructor virtual.";
30 const char kWeakPtrFactoryOrder[] = 37 const char kWeakPtrFactoryOrder[] =
(...skipping 21 matching lines...) Expand all
52 // any namespace qualifiers. This is similar to desugaring, except that for 59 // any namespace qualifiers. This is similar to desugaring, except that for
53 // ElaboratedTypes, desugar will unwrap too much. 60 // ElaboratedTypes, desugar will unwrap too much.
54 const Type* UnwrapType(const Type* type) { 61 const Type* UnwrapType(const Type* type) {
55 if (const ElaboratedType* elaborated = dyn_cast<ElaboratedType>(type)) 62 if (const ElaboratedType* elaborated = dyn_cast<ElaboratedType>(type))
56 return UnwrapType(elaborated->getNamedType().getTypePtr()); 63 return UnwrapType(elaborated->getNamedType().getTypePtr());
57 if (const TypedefType* typedefed = dyn_cast<TypedefType>(type)) 64 if (const TypedefType* typedefed = dyn_cast<TypedefType>(type))
58 return UnwrapType(typedefed->desugar().getTypePtr()); 65 return UnwrapType(typedefed->desugar().getTypePtr());
59 return type; 66 return type;
60 } 67 }
61 68
69 FixItHint FixItRemovalForVirtual(const SourceManager& manager,
70 const CXXMethodDecl* method) {
71 // Unfortunately, there doesn't seem to be a good way to determine the
72 // location
73 // of the 'virtual' keyword. It's available in Declarator, but that isn't
74 // accessible from the AST. So instead, make an educated guess that the first
75 // token is probably the virtual keyword. Strictly speaking, this doesn't have
76 // to be true, but it probably will be.
77 // TODO(dcheng): Add a warning to force virtual to always appear first ;-)
78 SourceRange range(method->getLocStart(),
79 Lexer::getLocForEndOfToken(
80 method->getLocStart(), 0, manager, LangOptions()));
81 // Get the spelling loc just in case it was expanded from a macro...
82 SourceRange spelling_range(manager.getSpellingLoc(range.getBegin()),
83 manager.getSpellingLoc(range.getEnd()));
84 // Sanity check that the text looks like virtual.
85 StringRef text = clang::Lexer::getSourceText(
86 CharSourceRange::getCharRange(spelling_range), manager, LangOptions());
87 if (!text.startswith("virtual"))
hans 2014/09/27 00:36:10 Any reason not to check if (text != "virtual") ?
dcheng 2014/09/28 18:35:39 I was worried about whitespace, but I forgot about
88 return FixItHint();
89 return FixItHint::CreateRemoval(range);
90 }
91
62 } // namespace 92 } // namespace
63 93
64 FindBadConstructsConsumer::FindBadConstructsConsumer(CompilerInstance& instance, 94 FindBadConstructsConsumer::FindBadConstructsConsumer(CompilerInstance& instance,
65 const Options& options) 95 const Options& options)
66 : ChromeClassTester(instance), options_(options) { 96 : ChromeClassTester(instance), options_(options) {
67 // Register warning/error messages. 97 // Messages for virtual method specifiers.
68 diag_method_requires_override_ = 98 diag_method_requires_override_ =
69 diagnostic().getCustomDiagID(getErrorLevel(), kMethodRequiresOverride); 99 diagnostic().getCustomDiagID(getErrorLevel(), kMethodRequiresOverride);
70 diag_method_requires_virtual_ = 100 diag_redundant_virtual_specifier_ =
71 diagnostic().getCustomDiagID(getErrorLevel(), kMethodRequiresVirtual); 101 diagnostic().getCustomDiagID(getErrorLevel(), kRedundantVirtualSpecifier);
102 diag_base_method_virtual_and_final_ =
103 diagnostic().getCustomDiagID(getErrorLevel(), kBaseMethodVirtualAndFinal);
104
105 // Messages for destructors.
72 diag_no_explicit_dtor_ = 106 diag_no_explicit_dtor_ =
73 diagnostic().getCustomDiagID(getErrorLevel(), kNoExplicitDtor); 107 diagnostic().getCustomDiagID(getErrorLevel(), kNoExplicitDtor);
74 diag_public_dtor_ = 108 diag_public_dtor_ =
75 diagnostic().getCustomDiagID(getErrorLevel(), kPublicDtor); 109 diagnostic().getCustomDiagID(getErrorLevel(), kPublicDtor);
76 diag_protected_non_virtual_dtor_ = 110 diag_protected_non_virtual_dtor_ =
77 diagnostic().getCustomDiagID(getErrorLevel(), kProtectedNonVirtualDtor); 111 diagnostic().getCustomDiagID(getErrorLevel(), kProtectedNonVirtualDtor);
112
113 // Miscellaneous messages.
78 diag_weak_ptr_factory_order_ = 114 diag_weak_ptr_factory_order_ =
79 diagnostic().getCustomDiagID(getErrorLevel(), kWeakPtrFactoryOrder); 115 diagnostic().getCustomDiagID(getErrorLevel(), kWeakPtrFactoryOrder);
80 diag_bad_enum_last_value_ = 116 diag_bad_enum_last_value_ =
81 diagnostic().getCustomDiagID(getErrorLevel(), kBadLastEnumValue); 117 diagnostic().getCustomDiagID(getErrorLevel(), kBadLastEnumValue);
82 118
83 // Registers notes to make it easier to interpret warnings. 119 // Registers notes to make it easier to interpret warnings.
84 diag_note_inheritance_ = 120 diag_note_inheritance_ =
85 diagnostic().getCustomDiagID(DiagnosticsEngine::Note, kNoteInheritance); 121 diagnostic().getCustomDiagID(DiagnosticsEngine::Note, kNoteInheritance);
86 diag_note_implicit_dtor_ = 122 diag_note_implicit_dtor_ =
87 diagnostic().getCustomDiagID(DiagnosticsEngine::Note, kNoteImplicitDtor); 123 diagnostic().getCustomDiagID(DiagnosticsEngine::Note, kNoteImplicitDtor);
88 diag_note_public_dtor_ = 124 diag_note_public_dtor_ =
89 diagnostic().getCustomDiagID(DiagnosticsEngine::Note, kNotePublicDtor); 125 diagnostic().getCustomDiagID(DiagnosticsEngine::Note, kNotePublicDtor);
90 diag_note_protected_non_virtual_dtor_ = diagnostic().getCustomDiagID( 126 diag_note_protected_non_virtual_dtor_ = diagnostic().getCustomDiagID(
91 DiagnosticsEngine::Note, kNoteProtectedNonVirtualDtor); 127 DiagnosticsEngine::Note, kNoteProtectedNonVirtualDtor);
92 } 128 }
93 129
94 void FindBadConstructsConsumer::CheckChromeClass(SourceLocation record_location, 130 void FindBadConstructsConsumer::CheckChromeClass(SourceLocation record_location,
95 CXXRecordDecl* record) { 131 CXXRecordDecl* record) {
96 bool implementation_file = InImplementationFile(record_location); 132 bool implementation_file = InImplementationFile(record_location);
97 133
98 if (!implementation_file) { 134 if (!implementation_file) {
99 // Only check for "heavy" constructors/destructors in header files; 135 // Only check for "heavy" constructors/destructors in header files;
100 // within implementation files, there is no performance cost. 136 // within implementation files, there is no performance cost.
101 CheckCtorDtorWeight(record_location, record); 137 CheckCtorDtorWeight(record_location, record);
102 } 138 }
103 139
104 bool warn_on_inline_bodies = !implementation_file; 140 bool warn_on_inline_bodies = !implementation_file;
105 141
106 // Check that all virtual methods are marked accordingly with both 142 // Check that all virtual methods are annotated with override or final.
107 // virtual and OVERRIDE.
108 CheckVirtualMethods(record_location, record, warn_on_inline_bodies); 143 CheckVirtualMethods(record_location, record, warn_on_inline_bodies);
109 144
110 CheckRefCountedDtors(record_location, record); 145 CheckRefCountedDtors(record_location, record);
111 146
112 if (options_.check_weak_ptr_factory_order) 147 if (options_.check_weak_ptr_factory_order)
113 CheckWeakPtrFactoryMembers(record_location, record); 148 CheckWeakPtrFactoryMembers(record_location, record);
114 } 149 }
115 150
116 void FindBadConstructsConsumer::CheckChromeEnum(SourceLocation enum_location, 151 void FindBadConstructsConsumer::CheckChromeEnum(SourceLocation enum_location,
117 EnumDecl* enum_decl) { 152 EnumDecl* enum_decl) {
(...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after
243 "destructor."); 278 "destructor.");
244 } else if (CXXDestructorDecl* dtor = record->getDestructor()) { 279 } else if (CXXDestructorDecl* dtor = record->getDestructor()) {
245 if (dtor->hasInlineBody()) { 280 if (dtor->hasInlineBody()) {
246 emitWarning(dtor->getInnerLocStart(), 281 emitWarning(dtor->getInnerLocStart(),
247 "Complex destructor has an inline body."); 282 "Complex destructor has an inline body.");
248 } 283 }
249 } 284 }
250 } 285 }
251 } 286 }
252 287
253 void FindBadConstructsConsumer::CheckVirtualMethod(const CXXMethodDecl* method,
254 bool warn_on_inline_bodies) {
255 if (!method->isVirtual())
256 return;
257
258 if (!method->isVirtualAsWritten()) {
259 SourceLocation loc = method->getTypeSpecStartLoc();
260 if (isa<CXXDestructorDecl>(method))
261 loc = method->getInnerLocStart();
262 SourceManager& manager = instance().getSourceManager();
263 FullSourceLoc full_loc(loc, manager);
264 SourceLocation spelling_loc = manager.getSpellingLoc(loc);
265 diagnostic().Report(full_loc, diag_method_requires_virtual_)
266 << FixItHint::CreateInsertion(spelling_loc, "virtual ");
267 }
268
269 // Virtual methods should not have inline definitions beyond "{}". This
270 // only matters for header files.
271 if (warn_on_inline_bodies && method->hasBody() && method->hasInlineBody()) {
272 if (CompoundStmt* cs = dyn_cast<CompoundStmt>(method->getBody())) {
273 if (cs->size()) {
274 emitWarning(cs->getLBracLoc(),
275 "virtual methods with non-empty bodies shouldn't be "
276 "declared inline.");
277 }
278 }
279 }
280 }
281
282 bool FindBadConstructsConsumer::InTestingNamespace(const Decl* record) { 288 bool FindBadConstructsConsumer::InTestingNamespace(const Decl* record) {
283 return GetNamespace(record).find("testing") != std::string::npos; 289 return GetNamespace(record).find("testing") != std::string::npos;
284 } 290 }
285 291
286 bool FindBadConstructsConsumer::IsMethodInBannedOrTestingNamespace( 292 bool FindBadConstructsConsumer::IsMethodInBannedOrTestingNamespace(
287 const CXXMethodDecl* method) { 293 const CXXMethodDecl* method) {
288 if (InBannedNamespace(method)) 294 if (InBannedNamespace(method))
289 return true; 295 return true;
290 for (CXXMethodDecl::method_iterator i = method->begin_overridden_methods(); 296 for (CXXMethodDecl::method_iterator i = method->begin_overridden_methods();
291 i != method->end_overridden_methods(); 297 i != method->end_overridden_methods();
292 ++i) { 298 ++i) {
293 const CXXMethodDecl* overridden = *i; 299 const CXXMethodDecl* overridden = *i;
294 if (IsMethodInBannedOrTestingNamespace(overridden) || 300 if (IsMethodInBannedOrTestingNamespace(overridden) ||
295 InTestingNamespace(overridden)) { 301 InTestingNamespace(overridden)) {
296 return true; 302 return true;
297 } 303 }
298 } 304 }
299 305
300 return false; 306 return false;
301 } 307 }
302 308
303 void FindBadConstructsConsumer::CheckOverriddenMethod( 309 // Checks that virtual methods are correctly annotated, and have no body in a
304 const CXXMethodDecl* method) { 310 // header file.
305 if (!method->size_overridden_methods() || method->getAttr<OverrideAttr>())
306 return;
307
308 if (isa<CXXDestructorDecl>(method) || method->isPure())
309 return;
310
311 if (IsMethodInBannedOrTestingNamespace(method))
312 return;
313
314 SourceManager& manager = instance().getSourceManager();
315 SourceRange type_info_range =
316 method->getTypeSourceInfo()->getTypeLoc().getSourceRange();
317 FullSourceLoc loc(type_info_range.getBegin(), manager);
318
319 // Build the FixIt insertion point after the end of the method definition,
320 // including any const-qualifiers and attributes, and before the opening
321 // of the l-curly-brace (if inline) or the semi-color (if a declaration).
322 SourceLocation spelling_end =
323 manager.getSpellingLoc(type_info_range.getEnd());
324 if (spelling_end.isValid()) {
325 SourceLocation token_end =
326 Lexer::getLocForEndOfToken(spelling_end, 0, manager, LangOptions());
327 diagnostic().Report(token_end, diag_method_requires_override_)
328 << FixItHint::CreateInsertion(token_end, " OVERRIDE");
329 } else {
330 diagnostic().Report(loc, diag_method_requires_override_);
331 }
332 }
333
334 // Makes sure there is a "virtual" keyword on virtual methods.
335 //
336 // Gmock objects trigger these for each MOCK_BLAH() macro used. So we have a
337 // trick to get around that. If a class has member variables whose types are
338 // in the "testing" namespace (which is how gmock works behind the scenes),
339 // there's a really high chance we won't care about these errors
340 void FindBadConstructsConsumer::CheckVirtualMethods( 311 void FindBadConstructsConsumer::CheckVirtualMethods(
341 SourceLocation record_location, 312 SourceLocation record_location,
342 CXXRecordDecl* record, 313 CXXRecordDecl* record,
343 bool warn_on_inline_bodies) { 314 bool warn_on_inline_bodies) {
315 // Gmock objects trigger these for each MOCK_BLAH() macro used. So we have a
316 // trick to get around that. If a class has member variables whose types are
317 // in the "testing" namespace (which is how gmock works behind the scenes),
318 // there's a really high chance we won't care about these errors
344 for (CXXRecordDecl::field_iterator it = record->field_begin(); 319 for (CXXRecordDecl::field_iterator it = record->field_begin();
345 it != record->field_end(); 320 it != record->field_end();
346 ++it) { 321 ++it) {
347 CXXRecordDecl* record_type = it->getTypeSourceInfo() 322 CXXRecordDecl* record_type = it->getTypeSourceInfo()
348 ->getTypeLoc() 323 ->getTypeLoc()
349 .getTypePtr() 324 .getTypePtr()
350 ->getAsCXXRecordDecl(); 325 ->getAsCXXRecordDecl();
351 if (record_type) { 326 if (record_type) {
352 if (InTestingNamespace(record_type)) { 327 if (InTestingNamespace(record_type)) {
353 return; 328 return;
354 } 329 }
355 } 330 }
356 } 331 }
357 332
358 for (CXXRecordDecl::method_iterator it = record->method_begin(); 333 for (CXXRecordDecl::method_iterator it = record->method_begin();
359 it != record->method_end(); 334 it != record->method_end();
360 ++it) { 335 ++it) {
361 if (it->isCopyAssignmentOperator() || isa<CXXConstructorDecl>(*it)) { 336 if (it->isCopyAssignmentOperator() || isa<CXXConstructorDecl>(*it)) {
362 // Ignore constructors and assignment operators. 337 // Ignore constructors and assignment operators.
363 } else if (isa<CXXDestructorDecl>(*it) && 338 } else if (isa<CXXDestructorDecl>(*it) &&
364 !record->hasUserDeclaredDestructor()) { 339 !record->hasUserDeclaredDestructor()) {
365 // Ignore non-user-declared destructors. 340 // Ignore non-user-declared destructors.
341 } else if (!it->isVirtual()) {
342 continue;
366 } else { 343 } else {
367 CheckVirtualMethod(*it, warn_on_inline_bodies); 344 CheckVirtualSpecifiers(*it);
368 CheckOverriddenMethod(*it); 345 if (warn_on_inline_bodies)
346 CheckVirtualBodies(*it);
369 } 347 }
370 } 348 }
371 } 349 }
350
351 // Makes sure that virtual methods use the most appropriate specifier. If a
352 // virtual method overrides a method from a base class, only the override
353 // specifier should be used. If the method should not be overridden by derived
354 // classes, only the final specifier should be used.
355 void FindBadConstructsConsumer::CheckVirtualSpecifiers(
356 const CXXMethodDecl* method) {
357 bool is_override = method->size_overridden_methods() > 0;
358 bool has_virtual = method->isVirtualAsWritten();
359 OverrideAttr* override_attr = method->getAttr<OverrideAttr>();
360 FinalAttr* final_attr = method->getAttr<FinalAttr>();
361
362 if (method->isPure())
363 return;
364
365 if (IsMethodInBannedOrTestingNamespace(method))
366 return;
367
368 if (isa<CXXDestructorDecl>(method) && !options_.strict_virtual_specifiers)
369 return;
370
371 SourceManager& manager = instance().getSourceManager();
372
373 // Complain if a method is annotated virtual && (override || final).
374 if (has_virtual && (override_attr || final_attr) &&
375 options_.strict_virtual_specifiers) {
376 diagnostic().Report(method->getLocStart(),
377 diag_redundant_virtual_specifier_)
378 << "'virtual'"
379 << (override_attr ? static_cast<Attr*>(override_attr) : final_attr)
380 << FixItRemovalForVirtual(manager, method);
381 }
382
383 // Complain if a method is an override and is not annotated with override or
384 // final.
385 if (is_override && !override_attr && !final_attr) {
386 SourceRange type_info_range =
387 method->getTypeSourceInfo()->getTypeLoc().getSourceRange();
388 FullSourceLoc loc(type_info_range.getBegin(), manager);
389
390 // Build the FixIt insertion point after the end of the method definition,
391 // including any const-qualifiers and attributes, and before the opening
392 // of the l-curly-brace (if inline) or the semi-color (if a declaration).
393 SourceLocation spelling_end =
394 manager.getSpellingLoc(type_info_range.getEnd());
395 if (spelling_end.isValid()) {
396 SourceLocation token_end =
397 Lexer::getLocForEndOfToken(spelling_end, 0, manager, LangOptions());
398 diagnostic().Report(token_end, diag_method_requires_override_)
399 << FixItHint::CreateInsertion(token_end, " override");
400 } else {
401 diagnostic().Report(loc, diag_method_requires_override_);
402 }
403 }
404
405 if (final_attr && override_attr && options_.strict_virtual_specifiers) {
406 diagnostic().Report(override_attr->getLocation(),
407 diag_redundant_virtual_specifier_)
408 << override_attr << final_attr
409 << FixItHint::CreateRemoval(override_attr->getRange());
410 }
411
412 if (final_attr && !is_override && options_.strict_virtual_specifiers) {
413 diagnostic().Report(method->getLocStart(),
414 diag_base_method_virtual_and_final_)
415 << FixItRemovalForVirtual(manager, method)
416 << FixItHint::CreateRemoval(final_attr->getRange());
417 }
418 }
419
420 void FindBadConstructsConsumer::CheckVirtualBodies(
421 const CXXMethodDecl* method) {
422 // Virtual methods should not have inline definitions beyond "{}". This
423 // only matters for header files.
424 if (method->hasBody() && method->hasInlineBody()) {
425 if (CompoundStmt* cs = dyn_cast<CompoundStmt>(method->getBody())) {
426 if (cs->size()) {
427 emitWarning(cs->getLBracLoc(),
428 "virtual methods with non-empty bodies shouldn't be "
429 "declared inline.");
430 }
431 }
432 }
433 }
372 434
373 void FindBadConstructsConsumer::CountType(const Type* type, 435 void FindBadConstructsConsumer::CountType(const Type* type,
374 int* trivial_member, 436 int* trivial_member,
375 int* non_trivial_member, 437 int* non_trivial_member,
376 int* templated_non_trivial_member) { 438 int* templated_non_trivial_member) {
377 switch (type->getTypeClass()) { 439 switch (type->getTypeClass()) {
378 case Type::Record: { 440 case Type::Record: {
379 // Simplifying; the whole class isn't trivial if the dtor is, but 441 // Simplifying; the whole class isn't trivial if the dtor is, but
380 // we use this as a signal about complexity. 442 // we use this as a signal about complexity.
381 if (TypeHasNonTrivialDtor(type)) 443 if (TypeHasNonTrivialDtor(type))
(...skipping 294 matching lines...) Expand 10 before | Expand all | Expand 10 after
676 record->getTypeForDecl()->getAsCXXRecordDecl()) { 738 record->getTypeForDecl()->getAsCXXRecordDecl()) {
677 weak_ptr_factory_location = iter->getLocation(); 739 weak_ptr_factory_location = iter->getLocation();
678 } 740 }
679 } 741 }
680 } 742 }
681 } 743 }
682 } 744 }
683 } 745 }
684 746
685 } // namespace chrome_checker 747 } // namespace chrome_checker
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698