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

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: s/annotation/specifier/g 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 final.";
hans 2014/09/26 01:29:29 pretty nitty, and we probably haven't done this so
dcheng 2014/09/26 07:07:13 Done.
19 const char kMethodRequiresVirtual[] = 20 const char kRedundantVirtualSpecifier[] =
20 "[chromium-style] Overriding method must have \"virtual\" keyword."; 21 "[chromium-style] %0 is redundant; %1 implies %0.";
hans 2014/09/26 01:29:29 as per above, I'd put single quotes around the %0'
dcheng 2014/09/26 07:07:14 Done, though I opted to just fix the instance wher
22 // http://llvm.org/bugs/show_bug.cgi?id=21051 has been filed to make this a
23 // Clang warning.
24 const char kBaseMethodVirtualAndFinal[] =
25 "[chromium-style] a virtual method on a base class should not also be "
26 "final; "
27 "method should be devirtualized";
hans 2014/09/26 01:29:29 s/on a base class/in a base class/ I'm not sure i
dcheng 2014/09/26 07:07:13 Updated with your text and weakened the suggestion
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 // Unfortunately, there doesn't seem to be a good way to determine the location
70 // of the 'virtual' keyword. It's available in Declarator, but that isn't
71 // accessible from the AST. So instead, make an educated guess that the first
72 // token is probably the virtual keyword. Strictly speaking, this doesn't have
73 // to be true, but it probably will be.
74 // TODO(dcheng): Add a warning to force virtual to always appear first ;-)
75 SourceRange GuessSourceRangeForVirtual(const SourceManager& manager,
76 const CXXMethodDecl* method) {
hans 2014/09/26 01:29:29 It might be overkill, but you could reach into the
dcheng 2014/09/26 07:07:13 Heh. Done.
77 return SourceRange(method->getLocStart(),
78 Lexer::getLocForEndOfToken(
79 method->getLocStart(), 0, manager, LangOptions()));
80 }
81
62 } // namespace 82 } // namespace
63 83
64 FindBadConstructsConsumer::FindBadConstructsConsumer(CompilerInstance& instance, 84 FindBadConstructsConsumer::FindBadConstructsConsumer(CompilerInstance& instance,
65 const Options& options) 85 const Options& options)
66 : ChromeClassTester(instance), options_(options) { 86 : ChromeClassTester(instance), options_(options) {
67 // Register warning/error messages. 87 // Messages for virtual method specifiers.
68 diag_method_requires_override_ = 88 diag_method_requires_override_ =
69 diagnostic().getCustomDiagID(getErrorLevel(), kMethodRequiresOverride); 89 diagnostic().getCustomDiagID(getErrorLevel(), kMethodRequiresOverride);
70 diag_method_requires_virtual_ = 90 diag_redundant_virtual_specifier_ = diagnostic().getCustomDiagID(
71 diagnostic().getCustomDiagID(getErrorLevel(), kMethodRequiresVirtual); 91 getErrorLevel(), kRedundantVirtualSpecifier);
92 diag_base_method_virtual_and_final_ =
93 diagnostic().getCustomDiagID(getErrorLevel(), kBaseMethodVirtualAndFinal);
94
95 // Messages for destructors.
72 diag_no_explicit_dtor_ = 96 diag_no_explicit_dtor_ =
73 diagnostic().getCustomDiagID(getErrorLevel(), kNoExplicitDtor); 97 diagnostic().getCustomDiagID(getErrorLevel(), kNoExplicitDtor);
74 diag_public_dtor_ = 98 diag_public_dtor_ =
75 diagnostic().getCustomDiagID(getErrorLevel(), kPublicDtor); 99 diagnostic().getCustomDiagID(getErrorLevel(), kPublicDtor);
76 diag_protected_non_virtual_dtor_ = 100 diag_protected_non_virtual_dtor_ =
77 diagnostic().getCustomDiagID(getErrorLevel(), kProtectedNonVirtualDtor); 101 diagnostic().getCustomDiagID(getErrorLevel(), kProtectedNonVirtualDtor);
102
103 // Miscellaneous messages.
78 diag_weak_ptr_factory_order_ = 104 diag_weak_ptr_factory_order_ =
79 diagnostic().getCustomDiagID(getErrorLevel(), kWeakPtrFactoryOrder); 105 diagnostic().getCustomDiagID(getErrorLevel(), kWeakPtrFactoryOrder);
80 diag_bad_enum_last_value_ = 106 diag_bad_enum_last_value_ =
81 diagnostic().getCustomDiagID(getErrorLevel(), kBadLastEnumValue); 107 diagnostic().getCustomDiagID(getErrorLevel(), kBadLastEnumValue);
82 108
83 // Registers notes to make it easier to interpret warnings. 109 // Registers notes to make it easier to interpret warnings.
84 diag_note_inheritance_ = 110 diag_note_inheritance_ =
85 diagnostic().getCustomDiagID(DiagnosticsEngine::Note, kNoteInheritance); 111 diagnostic().getCustomDiagID(DiagnosticsEngine::Note, kNoteInheritance);
86 diag_note_implicit_dtor_ = 112 diag_note_implicit_dtor_ =
87 diagnostic().getCustomDiagID(DiagnosticsEngine::Note, kNoteImplicitDtor); 113 diagnostic().getCustomDiagID(DiagnosticsEngine::Note, kNoteImplicitDtor);
88 diag_note_public_dtor_ = 114 diag_note_public_dtor_ =
89 diagnostic().getCustomDiagID(DiagnosticsEngine::Note, kNotePublicDtor); 115 diagnostic().getCustomDiagID(DiagnosticsEngine::Note, kNotePublicDtor);
90 diag_note_protected_non_virtual_dtor_ = diagnostic().getCustomDiagID( 116 diag_note_protected_non_virtual_dtor_ = diagnostic().getCustomDiagID(
91 DiagnosticsEngine::Note, kNoteProtectedNonVirtualDtor); 117 DiagnosticsEngine::Note, kNoteProtectedNonVirtualDtor);
92 } 118 }
93 119
94 void FindBadConstructsConsumer::CheckChromeClass(SourceLocation record_location, 120 void FindBadConstructsConsumer::CheckChromeClass(SourceLocation record_location,
95 CXXRecordDecl* record) { 121 CXXRecordDecl* record) {
96 bool implementation_file = InImplementationFile(record_location); 122 bool implementation_file = InImplementationFile(record_location);
97 123
98 if (!implementation_file) { 124 if (!implementation_file) {
99 // Only check for "heavy" constructors/destructors in header files; 125 // Only check for "heavy" constructors/destructors in header files;
100 // within implementation files, there is no performance cost. 126 // within implementation files, there is no performance cost.
101 CheckCtorDtorWeight(record_location, record); 127 CheckCtorDtorWeight(record_location, record);
102 } 128 }
103 129
104 bool warn_on_inline_bodies = !implementation_file; 130 bool warn_on_inline_bodies = !implementation_file;
105 131
106 // Check that all virtual methods are marked accordingly with both 132 // Check that all virtual methods are annotated with override or final.
107 // virtual and OVERRIDE.
108 CheckVirtualMethods(record_location, record, warn_on_inline_bodies); 133 CheckVirtualMethods(record_location, record, warn_on_inline_bodies);
109 134
110 CheckRefCountedDtors(record_location, record); 135 CheckRefCountedDtors(record_location, record);
111 136
112 if (options_.check_weak_ptr_factory_order) 137 if (options_.check_weak_ptr_factory_order)
113 CheckWeakPtrFactoryMembers(record_location, record); 138 CheckWeakPtrFactoryMembers(record_location, record);
114 } 139 }
115 140
116 void FindBadConstructsConsumer::CheckChromeEnum(SourceLocation enum_location, 141 void FindBadConstructsConsumer::CheckChromeEnum(SourceLocation enum_location,
117 EnumDecl* enum_decl) { 142 EnumDecl* enum_decl) {
(...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after
243 "destructor."); 268 "destructor.");
244 } else if (CXXDestructorDecl* dtor = record->getDestructor()) { 269 } else if (CXXDestructorDecl* dtor = record->getDestructor()) {
245 if (dtor->hasInlineBody()) { 270 if (dtor->hasInlineBody()) {
246 emitWarning(dtor->getInnerLocStart(), 271 emitWarning(dtor->getInnerLocStart(),
247 "Complex destructor has an inline body."); 272 "Complex destructor has an inline body.");
248 } 273 }
249 } 274 }
250 } 275 }
251 } 276 }
252 277
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) { 278 bool FindBadConstructsConsumer::InTestingNamespace(const Decl* record) {
283 return GetNamespace(record).find("testing") != std::string::npos; 279 return GetNamespace(record).find("testing") != std::string::npos;
284 } 280 }
285 281
286 bool FindBadConstructsConsumer::IsMethodInBannedOrTestingNamespace( 282 bool FindBadConstructsConsumer::IsMethodInBannedOrTestingNamespace(
287 const CXXMethodDecl* method) { 283 const CXXMethodDecl* method) {
288 if (InBannedNamespace(method)) 284 if (InBannedNamespace(method))
289 return true; 285 return true;
290 for (CXXMethodDecl::method_iterator i = method->begin_overridden_methods(); 286 for (CXXMethodDecl::method_iterator i = method->begin_overridden_methods();
291 i != method->end_overridden_methods(); 287 i != method->end_overridden_methods();
292 ++i) { 288 ++i) {
293 const CXXMethodDecl* overridden = *i; 289 const CXXMethodDecl* overridden = *i;
294 if (IsMethodInBannedOrTestingNamespace(overridden) || 290 if (IsMethodInBannedOrTestingNamespace(overridden) ||
295 InTestingNamespace(overridden)) { 291 InTestingNamespace(overridden)) {
296 return true; 292 return true;
297 } 293 }
298 } 294 }
299 295
300 return false; 296 return false;
301 } 297 }
302 298
303 void FindBadConstructsConsumer::CheckOverriddenMethod( 299 // Checks that virtual methods are correctly annotated, and have no body in a
304 const CXXMethodDecl* method) { 300 // 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( 301 void FindBadConstructsConsumer::CheckVirtualMethods(
341 SourceLocation record_location, 302 SourceLocation record_location,
342 CXXRecordDecl* record, 303 CXXRecordDecl* record,
343 bool warn_on_inline_bodies) { 304 bool warn_on_inline_bodies) {
305 // Gmock objects trigger these for each MOCK_BLAH() macro used. So we have a
306 // trick to get around that. If a class has member variables whose types are
307 // in the "testing" namespace (which is how gmock works behind the scenes),
308 // there's a really high chance we won't care about these errors
344 for (CXXRecordDecl::field_iterator it = record->field_begin(); 309 for (CXXRecordDecl::field_iterator it = record->field_begin();
345 it != record->field_end(); 310 it != record->field_end();
346 ++it) { 311 ++it) {
347 CXXRecordDecl* record_type = it->getTypeSourceInfo() 312 CXXRecordDecl* record_type = it->getTypeSourceInfo()
348 ->getTypeLoc() 313 ->getTypeLoc()
349 .getTypePtr() 314 .getTypePtr()
350 ->getAsCXXRecordDecl(); 315 ->getAsCXXRecordDecl();
351 if (record_type) { 316 if (record_type) {
352 if (InTestingNamespace(record_type)) { 317 if (InTestingNamespace(record_type)) {
353 return; 318 return;
354 } 319 }
355 } 320 }
356 } 321 }
357 322
358 for (CXXRecordDecl::method_iterator it = record->method_begin(); 323 for (CXXRecordDecl::method_iterator it = record->method_begin();
359 it != record->method_end(); 324 it != record->method_end();
360 ++it) { 325 ++it) {
361 if (it->isCopyAssignmentOperator() || isa<CXXConstructorDecl>(*it)) { 326 if (it->isCopyAssignmentOperator() || isa<CXXConstructorDecl>(*it)) {
362 // Ignore constructors and assignment operators. 327 // Ignore constructors and assignment operators.
363 } else if (isa<CXXDestructorDecl>(*it) && 328 } else if (isa<CXXDestructorDecl>(*it) &&
364 !record->hasUserDeclaredDestructor()) { 329 !record->hasUserDeclaredDestructor()) {
365 // Ignore non-user-declared destructors. 330 // Ignore non-user-declared destructors.
331 } else if (!it->isVirtual()) {
332 continue;
366 } else { 333 } else {
367 CheckVirtualMethod(*it, warn_on_inline_bodies); 334 CheckVirtualSpecifiers(*it);
368 CheckOverriddenMethod(*it); 335 if (warn_on_inline_bodies)
336 CheckVirtualBodies(*it);
369 } 337 }
370 } 338 }
371 } 339 }
340
341 // Makes sure that virtual methods use the most appropriate specifier. If a
342 // virtual method overrides a method from a base class, the method must use only
343 // the override specifier. Furthermore, if the method is not meant to be further
344 // subclasses, only the final specifier may be used.
hans 2014/09/26 01:29:29 "not meant to be further subclasses" sounds weird.
dcheng 2014/09/26 07:07:13 Oops, that's a typo. Fixed.
345 void FindBadConstructsConsumer::CheckVirtualSpecifiers(
346 const CXXMethodDecl* method) {
347 bool is_override = method->size_overridden_methods() > 0;
348 bool has_virtual = method->isVirtualAsWritten();
349 OverrideAttr* override_attr = method->getAttr<OverrideAttr>();
350 FinalAttr* final_attr = method->getAttr<FinalAttr>();
351
352 if (method->isPure())
353 return;
354
355 if (IsMethodInBannedOrTestingNamespace(method))
356 return;
357
358 if (isa<CXXDestructorDecl>(method) && !options_.strict_virtual_specifiers)
359 return;
360
361 SourceManager& manager = instance().getSourceManager();
362
363 // Complain if a method is annotated virtual && (override || final).
364 if (has_virtual && (override_attr || final_attr) &&
365 options_.strict_virtual_specifiers) {
366 diagnostic().Report(method->getLocStart(),
367 diag_redundant_virtual_specifier_)
368 << "virtual"
369 << (override_attr ? static_cast<Attr*>(override_attr) : final_attr)
370 << FixItHint::CreateRemoval(
371 GuessSourceRangeForVirtual(manager, method));
372 }
373
374 // Complain if a method is an override and is not annotated with override or
375 // final.
376 if (is_override && !override_attr && !final_attr) {
377 SourceRange type_info_range =
378 method->getTypeSourceInfo()->getTypeLoc().getSourceRange();
379 FullSourceLoc loc(type_info_range.getBegin(), manager);
380
381 // Build the FixIt insertion point after the end of the method definition,
382 // including any const-qualifiers and attributes, and before the opening
383 // of the l-curly-brace (if inline) or the semi-color (if a declaration).
384 SourceLocation spelling_end =
385 manager.getSpellingLoc(type_info_range.getEnd());
386 if (spelling_end.isValid()) {
387 SourceLocation token_end =
388 Lexer::getLocForEndOfToken(spelling_end, 0, manager, LangOptions());
389 diagnostic().Report(token_end, diag_method_requires_override_)
390 << FixItHint::CreateInsertion(token_end, " override");
391 } else {
392 diagnostic().Report(loc, diag_method_requires_override_);
393 }
394 }
395
396 if (final_attr && override_attr && options_.strict_virtual_specifiers) {
397 diagnostic().Report(override_attr->getLocation(),
398 diag_redundant_virtual_specifier_)
399 << override_attr << final_attr
400 << FixItHint::CreateRemoval(override_attr->getRange());
401 }
402
403 if (final_attr && !is_override && options_.strict_virtual_specifiers) {
404 diagnostic().Report(method->getLocStart(),
405 diag_base_method_virtual_and_final_)
406 << FixItHint::CreateRemoval(GuessSourceRangeForVirtual(manager, method))
hans 2014/09/26 01:29:29 Removing the virtual specifier should probably be
dcheng 2014/09/26 07:07:13 It's not needed, because the following does not co
407 << FixItHint::CreateRemoval(final_attr->getRange());
408 }
409 }
410
411 void FindBadConstructsConsumer::CheckVirtualBodies(
412 const CXXMethodDecl* method) {
413 // Virtual methods should not have inline definitions beyond "{}". This
414 // only matters for header files.
415 if (method->hasBody() && method->hasInlineBody()) {
416 if (CompoundStmt* cs = dyn_cast<CompoundStmt>(method->getBody())) {
417 if (cs->size()) {
418 emitWarning(cs->getLBracLoc(),
419 "virtual methods with non-empty bodies shouldn't be "
420 "declared inline.");
421 }
422 }
423 }
424 }
372 425
373 void FindBadConstructsConsumer::CountType(const Type* type, 426 void FindBadConstructsConsumer::CountType(const Type* type,
374 int* trivial_member, 427 int* trivial_member,
375 int* non_trivial_member, 428 int* non_trivial_member,
376 int* templated_non_trivial_member) { 429 int* templated_non_trivial_member) {
377 switch (type->getTypeClass()) { 430 switch (type->getTypeClass()) {
378 case Type::Record: { 431 case Type::Record: {
379 // Simplifying; the whole class isn't trivial if the dtor is, but 432 // Simplifying; the whole class isn't trivial if the dtor is, but
380 // we use this as a signal about complexity. 433 // we use this as a signal about complexity.
381 if (TypeHasNonTrivialDtor(type)) 434 if (TypeHasNonTrivialDtor(type))
(...skipping 294 matching lines...) Expand 10 before | Expand all | Expand 10 after
676 record->getTypeForDecl()->getAsCXXRecordDecl()) { 729 record->getTypeForDecl()->getAsCXXRecordDecl()) {
677 weak_ptr_factory_location = iter->getLocation(); 730 weak_ptr_factory_location = iter->getLocation();
678 } 731 }
679 } 732 }
680 } 733 }
681 } 734 }
682 } 735 }
683 } 736 }
684 737
685 } // namespace chrome_checker 738 } // namespace chrome_checker
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698