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

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: Add copyright blurb 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
« no previous file with comments | « tools/clang/plugins/FindBadConstructsConsumer.h ('k') | tools/clang/plugins/Options.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 of the 'virtual' keyword. It's available in Declarator, but that
73 // isn't accessible from the AST. So instead, make an educated guess that the
74 // first token is probably the virtual keyword. Strictly speaking, this
75 // doesn't have to be true, but it probably will be.
76 // TODO(dcheng): Add a warning to force virtual to always appear first ;-)
77 SourceRange range(method->getLocStart());
78 // Get the spelling loc just in case it was expanded from a macro.
79 SourceRange spelling_range(manager.getSpellingLoc(range.getBegin()));
80 // Sanity check that the text looks like virtual.
81 StringRef text = clang::Lexer::getSourceText(
82 CharSourceRange::getTokenRange(spelling_range), manager, LangOptions());
83 if (text.trim() != "virtual")
84 return FixItHint();
85 return FixItHint::CreateRemoval(range);
86 }
87
62 } // namespace 88 } // namespace
63 89
64 FindBadConstructsConsumer::FindBadConstructsConsumer(CompilerInstance& instance, 90 FindBadConstructsConsumer::FindBadConstructsConsumer(CompilerInstance& instance,
65 const Options& options) 91 const Options& options)
66 : ChromeClassTester(instance), options_(options) { 92 : ChromeClassTester(instance), options_(options) {
67 // Register warning/error messages. 93 // Messages for virtual method specifiers.
68 diag_method_requires_override_ = 94 diag_method_requires_override_ =
69 diagnostic().getCustomDiagID(getErrorLevel(), kMethodRequiresOverride); 95 diagnostic().getCustomDiagID(getErrorLevel(), kMethodRequiresOverride);
70 diag_method_requires_virtual_ = 96 diag_redundant_virtual_specifier_ =
71 diagnostic().getCustomDiagID(getErrorLevel(), kMethodRequiresVirtual); 97 diagnostic().getCustomDiagID(getErrorLevel(), kRedundantVirtualSpecifier);
98 diag_base_method_virtual_and_final_ =
99 diagnostic().getCustomDiagID(getErrorLevel(), kBaseMethodVirtualAndFinal);
100
101 // Messages for destructors.
72 diag_no_explicit_dtor_ = 102 diag_no_explicit_dtor_ =
73 diagnostic().getCustomDiagID(getErrorLevel(), kNoExplicitDtor); 103 diagnostic().getCustomDiagID(getErrorLevel(), kNoExplicitDtor);
74 diag_public_dtor_ = 104 diag_public_dtor_ =
75 diagnostic().getCustomDiagID(getErrorLevel(), kPublicDtor); 105 diagnostic().getCustomDiagID(getErrorLevel(), kPublicDtor);
76 diag_protected_non_virtual_dtor_ = 106 diag_protected_non_virtual_dtor_ =
77 diagnostic().getCustomDiagID(getErrorLevel(), kProtectedNonVirtualDtor); 107 diagnostic().getCustomDiagID(getErrorLevel(), kProtectedNonVirtualDtor);
108
109 // Miscellaneous messages.
78 diag_weak_ptr_factory_order_ = 110 diag_weak_ptr_factory_order_ =
79 diagnostic().getCustomDiagID(getErrorLevel(), kWeakPtrFactoryOrder); 111 diagnostic().getCustomDiagID(getErrorLevel(), kWeakPtrFactoryOrder);
80 diag_bad_enum_last_value_ = 112 diag_bad_enum_last_value_ =
81 diagnostic().getCustomDiagID(getErrorLevel(), kBadLastEnumValue); 113 diagnostic().getCustomDiagID(getErrorLevel(), kBadLastEnumValue);
82 114
83 // Registers notes to make it easier to interpret warnings. 115 // Registers notes to make it easier to interpret warnings.
84 diag_note_inheritance_ = 116 diag_note_inheritance_ =
85 diagnostic().getCustomDiagID(DiagnosticsEngine::Note, kNoteInheritance); 117 diagnostic().getCustomDiagID(DiagnosticsEngine::Note, kNoteInheritance);
86 diag_note_implicit_dtor_ = 118 diag_note_implicit_dtor_ =
87 diagnostic().getCustomDiagID(DiagnosticsEngine::Note, kNoteImplicitDtor); 119 diagnostic().getCustomDiagID(DiagnosticsEngine::Note, kNoteImplicitDtor);
88 diag_note_public_dtor_ = 120 diag_note_public_dtor_ =
89 diagnostic().getCustomDiagID(DiagnosticsEngine::Note, kNotePublicDtor); 121 diagnostic().getCustomDiagID(DiagnosticsEngine::Note, kNotePublicDtor);
90 diag_note_protected_non_virtual_dtor_ = diagnostic().getCustomDiagID( 122 diag_note_protected_non_virtual_dtor_ = diagnostic().getCustomDiagID(
91 DiagnosticsEngine::Note, kNoteProtectedNonVirtualDtor); 123 DiagnosticsEngine::Note, kNoteProtectedNonVirtualDtor);
92 } 124 }
93 125
94 void FindBadConstructsConsumer::CheckChromeClass(SourceLocation record_location, 126 void FindBadConstructsConsumer::CheckChromeClass(SourceLocation record_location,
95 CXXRecordDecl* record) { 127 CXXRecordDecl* record) {
96 bool implementation_file = InImplementationFile(record_location); 128 bool implementation_file = InImplementationFile(record_location);
97 129
98 if (!implementation_file) { 130 if (!implementation_file) {
99 // Only check for "heavy" constructors/destructors in header files; 131 // Only check for "heavy" constructors/destructors in header files;
100 // within implementation files, there is no performance cost. 132 // within implementation files, there is no performance cost.
101 CheckCtorDtorWeight(record_location, record); 133 CheckCtorDtorWeight(record_location, record);
102 } 134 }
103 135
104 bool warn_on_inline_bodies = !implementation_file; 136 bool warn_on_inline_bodies = !implementation_file;
105 137
106 // Check that all virtual methods are marked accordingly with both 138 // Check that all virtual methods are annotated with override or final.
107 // virtual and OVERRIDE.
108 CheckVirtualMethods(record_location, record, warn_on_inline_bodies); 139 CheckVirtualMethods(record_location, record, warn_on_inline_bodies);
109 140
110 CheckRefCountedDtors(record_location, record); 141 CheckRefCountedDtors(record_location, record);
111 142
112 if (options_.check_weak_ptr_factory_order) 143 if (options_.check_weak_ptr_factory_order)
113 CheckWeakPtrFactoryMembers(record_location, record); 144 CheckWeakPtrFactoryMembers(record_location, record);
114 } 145 }
115 146
116 void FindBadConstructsConsumer::CheckChromeEnum(SourceLocation enum_location, 147 void FindBadConstructsConsumer::CheckChromeEnum(SourceLocation enum_location,
117 EnumDecl* enum_decl) { 148 EnumDecl* enum_decl) {
(...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after
243 "destructor."); 274 "destructor.");
244 } else if (CXXDestructorDecl* dtor = record->getDestructor()) { 275 } else if (CXXDestructorDecl* dtor = record->getDestructor()) {
245 if (dtor->hasInlineBody()) { 276 if (dtor->hasInlineBody()) {
246 emitWarning(dtor->getInnerLocStart(), 277 emitWarning(dtor->getInnerLocStart(),
247 "Complex destructor has an inline body."); 278 "Complex destructor has an inline body.");
248 } 279 }
249 } 280 }
250 } 281 }
251 } 282 }
252 283
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) { 284 bool FindBadConstructsConsumer::InTestingNamespace(const Decl* record) {
283 return GetNamespace(record).find("testing") != std::string::npos; 285 return GetNamespace(record).find("testing") != std::string::npos;
284 } 286 }
285 287
286 bool FindBadConstructsConsumer::IsMethodInBannedOrTestingNamespace( 288 bool FindBadConstructsConsumer::IsMethodInBannedOrTestingNamespace(
287 const CXXMethodDecl* method) { 289 const CXXMethodDecl* method) {
288 if (InBannedNamespace(method)) 290 if (InBannedNamespace(method))
289 return true; 291 return true;
290 for (CXXMethodDecl::method_iterator i = method->begin_overridden_methods(); 292 for (CXXMethodDecl::method_iterator i = method->begin_overridden_methods();
291 i != method->end_overridden_methods(); 293 i != method->end_overridden_methods();
292 ++i) { 294 ++i) {
293 const CXXMethodDecl* overridden = *i; 295 const CXXMethodDecl* overridden = *i;
294 if (IsMethodInBannedOrTestingNamespace(overridden) || 296 if (IsMethodInBannedOrTestingNamespace(overridden) ||
295 InTestingNamespace(overridden)) { 297 InTestingNamespace(overridden)) {
296 return true; 298 return true;
297 } 299 }
298 } 300 }
299 301
300 return false; 302 return false;
301 } 303 }
302 304
303 void FindBadConstructsConsumer::CheckOverriddenMethod( 305 // Checks that virtual methods are correctly annotated, and have no body in a
304 const CXXMethodDecl* method) { 306 // 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( 307 void FindBadConstructsConsumer::CheckVirtualMethods(
341 SourceLocation record_location, 308 SourceLocation record_location,
342 CXXRecordDecl* record, 309 CXXRecordDecl* record,
343 bool warn_on_inline_bodies) { 310 bool warn_on_inline_bodies) {
311 // Gmock objects trigger these for each MOCK_BLAH() macro used. So we have a
312 // trick to get around that. If a class has member variables whose types are
313 // in the "testing" namespace (which is how gmock works behind the scenes),
314 // there's a really high chance we won't care about these errors
344 for (CXXRecordDecl::field_iterator it = record->field_begin(); 315 for (CXXRecordDecl::field_iterator it = record->field_begin();
345 it != record->field_end(); 316 it != record->field_end();
346 ++it) { 317 ++it) {
347 CXXRecordDecl* record_type = it->getTypeSourceInfo() 318 CXXRecordDecl* record_type = it->getTypeSourceInfo()
348 ->getTypeLoc() 319 ->getTypeLoc()
349 .getTypePtr() 320 .getTypePtr()
350 ->getAsCXXRecordDecl(); 321 ->getAsCXXRecordDecl();
351 if (record_type) { 322 if (record_type) {
352 if (InTestingNamespace(record_type)) { 323 if (InTestingNamespace(record_type)) {
353 return; 324 return;
354 } 325 }
355 } 326 }
356 } 327 }
357 328
358 for (CXXRecordDecl::method_iterator it = record->method_begin(); 329 for (CXXRecordDecl::method_iterator it = record->method_begin();
359 it != record->method_end(); 330 it != record->method_end();
360 ++it) { 331 ++it) {
361 if (it->isCopyAssignmentOperator() || isa<CXXConstructorDecl>(*it)) { 332 if (it->isCopyAssignmentOperator() || isa<CXXConstructorDecl>(*it)) {
362 // Ignore constructors and assignment operators. 333 // Ignore constructors and assignment operators.
363 } else if (isa<CXXDestructorDecl>(*it) && 334 } else if (isa<CXXDestructorDecl>(*it) &&
364 !record->hasUserDeclaredDestructor()) { 335 !record->hasUserDeclaredDestructor()) {
365 // Ignore non-user-declared destructors. 336 // Ignore non-user-declared destructors.
337 } else if (!it->isVirtual()) {
338 continue;
366 } else { 339 } else {
367 CheckVirtualMethod(*it, warn_on_inline_bodies); 340 CheckVirtualSpecifiers(*it);
368 CheckOverriddenMethod(*it); 341 if (warn_on_inline_bodies)
342 CheckVirtualBodies(*it);
369 } 343 }
370 } 344 }
371 } 345 }
346
347 // Makes sure that virtual methods use the most appropriate specifier. If a
348 // virtual method overrides a method from a base class, only the override
349 // specifier should be used. If the method should not be overridden by derived
350 // classes, only the final specifier should be used.
351 void FindBadConstructsConsumer::CheckVirtualSpecifiers(
352 const CXXMethodDecl* method) {
353 bool is_override = method->size_overridden_methods() > 0;
354 bool has_virtual = method->isVirtualAsWritten();
355 OverrideAttr* override_attr = method->getAttr<OverrideAttr>();
356 FinalAttr* final_attr = method->getAttr<FinalAttr>();
357
358 if (method->isPure())
359 return;
360
361 if (IsMethodInBannedOrTestingNamespace(method))
362 return;
363
364 if (isa<CXXDestructorDecl>(method) && !options_.strict_virtual_specifiers)
365 return;
366
367 SourceManager& manager = instance().getSourceManager();
368
369 // Complain if a method is annotated virtual && (override || final).
370 if (has_virtual && (override_attr || final_attr) &&
371 options_.strict_virtual_specifiers) {
372 diagnostic().Report(method->getLocStart(),
373 diag_redundant_virtual_specifier_)
374 << "'virtual'"
375 << (override_attr ? static_cast<Attr*>(override_attr) : final_attr)
376 << FixItRemovalForVirtual(manager, method);
377 }
378
379 // Complain if a method is an override and is not annotated with override or
380 // final.
381 if (is_override && !override_attr && !final_attr) {
382 SourceRange type_info_range =
383 method->getTypeSourceInfo()->getTypeLoc().getSourceRange();
384 FullSourceLoc loc(type_info_range.getBegin(), manager);
385
386 // Build the FixIt insertion point after the end of the method definition,
387 // including any const-qualifiers and attributes, and before the opening
388 // of the l-curly-brace (if inline) or the semi-color (if a declaration).
389 SourceLocation spelling_end =
390 manager.getSpellingLoc(type_info_range.getEnd());
391 if (spelling_end.isValid()) {
392 SourceLocation token_end =
393 Lexer::getLocForEndOfToken(spelling_end, 0, manager, LangOptions());
394 diagnostic().Report(token_end, diag_method_requires_override_)
395 << FixItHint::CreateInsertion(token_end, " override");
396 } else {
397 diagnostic().Report(loc, diag_method_requires_override_);
398 }
399 }
400
401 if (final_attr && override_attr && options_.strict_virtual_specifiers) {
402 diagnostic().Report(override_attr->getLocation(),
403 diag_redundant_virtual_specifier_)
404 << override_attr << final_attr
405 << FixItHint::CreateRemoval(override_attr->getRange());
406 }
407
408 if (final_attr && !is_override && options_.strict_virtual_specifiers) {
409 diagnostic().Report(method->getLocStart(),
410 diag_base_method_virtual_and_final_)
411 << FixItRemovalForVirtual(manager, method)
412 << FixItHint::CreateRemoval(final_attr->getRange());
413 }
414 }
415
416 void FindBadConstructsConsumer::CheckVirtualBodies(
417 const CXXMethodDecl* method) {
418 // Virtual methods should not have inline definitions beyond "{}". This
419 // only matters for header files.
420 if (method->hasBody() && method->hasInlineBody()) {
421 if (CompoundStmt* cs = dyn_cast<CompoundStmt>(method->getBody())) {
422 if (cs->size()) {
423 emitWarning(cs->getLBracLoc(),
424 "virtual methods with non-empty bodies shouldn't be "
425 "declared inline.");
426 }
427 }
428 }
429 }
372 430
373 void FindBadConstructsConsumer::CountType(const Type* type, 431 void FindBadConstructsConsumer::CountType(const Type* type,
374 int* trivial_member, 432 int* trivial_member,
375 int* non_trivial_member, 433 int* non_trivial_member,
376 int* templated_non_trivial_member) { 434 int* templated_non_trivial_member) {
377 switch (type->getTypeClass()) { 435 switch (type->getTypeClass()) {
378 case Type::Record: { 436 case Type::Record: {
379 // Simplifying; the whole class isn't trivial if the dtor is, but 437 // Simplifying; the whole class isn't trivial if the dtor is, but
380 // we use this as a signal about complexity. 438 // we use this as a signal about complexity.
381 if (TypeHasNonTrivialDtor(type)) 439 if (TypeHasNonTrivialDtor(type))
(...skipping 294 matching lines...) Expand 10 before | Expand all | Expand 10 after
676 record->getTypeForDecl()->getAsCXXRecordDecl()) { 734 record->getTypeForDecl()->getAsCXXRecordDecl()) {
677 weak_ptr_factory_location = iter->getLocation(); 735 weak_ptr_factory_location = iter->getLocation();
678 } 736 }
679 } 737 }
680 } 738 }
681 } 739 }
682 } 740 }
683 } 741 }
684 742
685 } // namespace chrome_checker 743 } // namespace chrome_checker
OLDNEW
« no previous file with comments | « tools/clang/plugins/FindBadConstructsConsumer.h ('k') | tools/clang/plugins/Options.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698