| OLD | NEW |
| 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/AST/Attr.h" |
| 9 #include "clang/Lex/Lexer.h" | 9 #include "clang/Lex/Lexer.h" |
| 10 #include "llvm/Support/raw_ostream.h" | 10 #include "llvm/Support/raw_ostream.h" |
| (...skipping 265 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 276 if (!record->hasUserDeclaredConstructor()) { | 276 if (!record->hasUserDeclaredConstructor()) { |
| 277 emitWarning(record_location, | 277 emitWarning(record_location, |
| 278 "Complex class/struct needs an explicit out-of-line " | 278 "Complex class/struct needs an explicit out-of-line " |
| 279 "constructor."); | 279 "constructor."); |
| 280 } else { | 280 } else { |
| 281 // Iterate across all the constructors in this file and yell if we | 281 // Iterate across all the constructors in this file and yell if we |
| 282 // find one that tries to be inline. | 282 // find one that tries to be inline. |
| 283 for (CXXRecordDecl::ctor_iterator it = record->ctor_begin(); | 283 for (CXXRecordDecl::ctor_iterator it = record->ctor_begin(); |
| 284 it != record->ctor_end(); | 284 it != record->ctor_end(); |
| 285 ++it) { | 285 ++it) { |
| 286 // The current check is buggy. An implicit copy constructor does not | |
| 287 // have an inline body, so this check never fires for classes with a | |
| 288 // user-declared out-of-line constructor. | |
| 289 if (it->hasInlineBody()) { | 286 if (it->hasInlineBody()) { |
| 290 if (it->isCopyConstructor() && | 287 if (it->isCopyConstructor() && |
| 291 !record->hasUserDeclaredCopyConstructor()) { | 288 !record->hasUserDeclaredCopyConstructor()) { |
| 292 emitWarning(record_location, | 289 emitWarning(record_location, |
| 293 "Complex class/struct needs an explicit out-of-line " | 290 "Complex class/struct needs an explicit out-of-line " |
| 294 "copy constructor."); | 291 "copy constructor."); |
| 295 } else { | 292 } else { |
| 296 emitWarning(it->getInnerLocStart(), | 293 emitWarning(it->getInnerLocStart(), |
| 297 "Complex constructor has an inlined body."); | 294 "Complex constructor has an inlined body."); |
| 298 } | 295 } |
| 299 } else if (it->isInlined() && (!it->isCopyOrMoveConstructor() || | |
| 300 it->isExplicitlyDefaulted())) { | |
| 301 // isInlined() is a more reliable check than hasInlineBody(), but | |
| 302 // unfortunately, it results in warnings for implicit copy/move | |
| 303 // constructors in the previously mentioned situation. To preserve | |
| 304 // compatibility with existing Chromium code, only warn if it's an | |
| 305 // explicitly defaulted copy or move constructor. | |
| 306 emitWarning(it->getInnerLocStart(), | |
| 307 "Complex constructor has an inlined body."); | |
| 308 } | 296 } |
| 309 } | 297 } |
| 310 } | 298 } |
| 311 } | 299 } |
| 312 | 300 |
| 313 // The destructor side is equivalent except that we don't check for | 301 // The destructor side is equivalent except that we don't check for |
| 314 // trivial members; 20 ints don't need a destructor. | 302 // trivial members; 20 ints don't need a destructor. |
| 315 if (dtor_score >= 10 && !record->hasTrivialDestructor()) { | 303 if (dtor_score >= 10 && !record->hasTrivialDestructor()) { |
| 316 if (!record->hasUserDeclaredDestructor()) { | 304 if (!record->hasUserDeclaredDestructor()) { |
| 317 emitWarning(record_location, | 305 emitWarning(record_location, |
| 318 "Complex class/struct needs an explicit out-of-line " | 306 "Complex class/struct needs an explicit out-of-line " |
| 319 "destructor."); | 307 "destructor."); |
| 320 } else if (CXXDestructorDecl* dtor = record->getDestructor()) { | 308 } else if (CXXDestructorDecl* dtor = record->getDestructor()) { |
| 321 if (dtor->isInlined()) { | 309 if (dtor->hasInlineBody()) { |
| 322 emitWarning(dtor->getInnerLocStart(), | 310 emitWarning(dtor->getInnerLocStart(), |
| 323 "Complex destructor has an inline body."); | 311 "Complex destructor has an inline body."); |
| 324 } | 312 } |
| 325 } | 313 } |
| 326 } | 314 } |
| 327 } | 315 } |
| 328 | 316 |
| 329 bool FindBadConstructsConsumer::InTestingNamespace(const Decl* record) { | 317 bool FindBadConstructsConsumer::InTestingNamespace(const Decl* record) { |
| 330 return GetNamespace(record).find("testing") != std::string::npos; | 318 return GetNamespace(record).find("testing") != std::string::npos; |
| 331 } | 319 } |
| 332 | 320 |
| 333 bool FindBadConstructsConsumer::IsMethodInBannedOrTestingNamespace( | 321 bool FindBadConstructsConsumer::IsMethodInBannedOrTestingNamespace( |
| 334 const CXXMethodDecl* method) { | 322 const CXXMethodDecl* method) { |
| 335 if (InBannedNamespace(method)) | 323 if (InBannedNamespace(method)) |
| 336 return true; | 324 return true; |
| 337 for (CXXMethodDecl::method_iterator i = method->begin_overridden_methods(); | 325 for (CXXMethodDecl::method_iterator i = method->begin_overridden_methods(); |
| 338 i != method->end_overridden_methods(); | 326 i != method->end_overridden_methods(); |
| 339 ++i) { | 327 ++i) { |
| 340 const CXXMethodDecl* overridden = *i; | 328 const CXXMethodDecl* overridden = *i; |
| 341 if (IsMethodInBannedOrTestingNamespace(overridden) || | 329 if (IsMethodInBannedOrTestingNamespace(overridden) || |
| 342 // Provide an exception for ::testing::Test. gtest itself uses some | 330 // Provide an exception for ::testing::Test. gtest itself uses some |
| 343 // magic to try to make sure SetUp()/TearDown() aren't capitalized | 331 // magic to try to make sure SetUp()/TearDown() aren't capitalized |
| 344 // incorrectly, but having the plugin enforce override is also nice. | 332 // incorrectly, but having the plugin enforce override is also nice. |
| 345 (InTestingNamespace(overridden) && | 333 (InTestingNamespace(overridden) && |
| 346 !IsGtestTestFixture(overridden->getParent()))) { | 334 (!options_.strict_virtual_specifiers || |
| 335 !IsGtestTestFixture(overridden->getParent())))) { |
| 347 return true; | 336 return true; |
| 348 } | 337 } |
| 349 } | 338 } |
| 350 | 339 |
| 351 return false; | 340 return false; |
| 352 } | 341 } |
| 353 | 342 |
| 354 // Checks that virtual methods are correctly annotated, and have no body in a | 343 // Checks that virtual methods are correctly annotated, and have no body in a |
| 355 // header file. | 344 // header file. |
| 356 void FindBadConstructsConsumer::CheckVirtualMethods( | 345 void FindBadConstructsConsumer::CheckVirtualMethods( |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 397 // virtual method overrides a method from a base class, only the override | 386 // virtual method overrides a method from a base class, only the override |
| 398 // specifier should be used. If the method should not be overridden by derived | 387 // specifier should be used. If the method should not be overridden by derived |
| 399 // classes, only the final specifier should be used. | 388 // classes, only the final specifier should be used. |
| 400 void FindBadConstructsConsumer::CheckVirtualSpecifiers( | 389 void FindBadConstructsConsumer::CheckVirtualSpecifiers( |
| 401 const CXXMethodDecl* method) { | 390 const CXXMethodDecl* method) { |
| 402 bool is_override = method->size_overridden_methods() > 0; | 391 bool is_override = method->size_overridden_methods() > 0; |
| 403 bool has_virtual = method->isVirtualAsWritten(); | 392 bool has_virtual = method->isVirtualAsWritten(); |
| 404 OverrideAttr* override_attr = method->getAttr<OverrideAttr>(); | 393 OverrideAttr* override_attr = method->getAttr<OverrideAttr>(); |
| 405 FinalAttr* final_attr = method->getAttr<FinalAttr>(); | 394 FinalAttr* final_attr = method->getAttr<FinalAttr>(); |
| 406 | 395 |
| 396 if (method->isPure() && !options_.strict_virtual_specifiers) |
| 397 return; |
| 398 |
| 407 if (IsMethodInBannedOrTestingNamespace(method)) | 399 if (IsMethodInBannedOrTestingNamespace(method)) |
| 408 return; | 400 return; |
| 409 | 401 |
| 402 if (isa<CXXDestructorDecl>(method) && !options_.strict_virtual_specifiers) |
| 403 return; |
| 404 |
| 410 SourceManager& manager = instance().getSourceManager(); | 405 SourceManager& manager = instance().getSourceManager(); |
| 411 | 406 |
| 412 // Complain if a method is annotated virtual && (override || final). | 407 // Complain if a method is annotated virtual && (override || final). |
| 413 if (has_virtual && (override_attr || final_attr)) { | 408 if (has_virtual && (override_attr || final_attr) && |
| 409 options_.strict_virtual_specifiers) { |
| 414 diagnostic().Report(method->getLocStart(), | 410 diagnostic().Report(method->getLocStart(), |
| 415 diag_redundant_virtual_specifier_) | 411 diag_redundant_virtual_specifier_) |
| 416 << "'virtual'" | 412 << "'virtual'" |
| 417 << (override_attr ? static_cast<Attr*>(override_attr) : final_attr) | 413 << (override_attr ? static_cast<Attr*>(override_attr) : final_attr) |
| 418 << FixItRemovalForVirtual(manager, method); | 414 << FixItRemovalForVirtual(manager, method); |
| 419 } | 415 } |
| 420 | 416 |
| 421 // Complain if a method is an override and is not annotated with override or | 417 // Complain if a method is an override and is not annotated with override or |
| 422 // final. | 418 // final. |
| 423 if (is_override && !override_attr && !final_attr) { | 419 if (is_override && !override_attr && !final_attr) { |
| 424 SourceRange type_info_range = | 420 SourceRange type_info_range = |
| 425 method->getTypeSourceInfo()->getTypeLoc().getSourceRange(); | 421 method->getTypeSourceInfo()->getTypeLoc().getSourceRange(); |
| 426 FullSourceLoc loc(type_info_range.getBegin(), manager); | 422 FullSourceLoc loc(type_info_range.getBegin(), manager); |
| 427 | 423 |
| 428 // Build the FixIt insertion point after the end of the method definition, | 424 // Build the FixIt insertion point after the end of the method definition, |
| 429 // including any const-qualifiers and attributes, and before the opening | 425 // including any const-qualifiers and attributes, and before the opening |
| 430 // of the l-curly-brace (if inline) or the semi-color (if a declaration). | 426 // of the l-curly-brace (if inline) or the semi-color (if a declaration). |
| 431 SourceLocation spelling_end = | 427 SourceLocation spelling_end = |
| 432 manager.getSpellingLoc(type_info_range.getEnd()); | 428 manager.getSpellingLoc(type_info_range.getEnd()); |
| 433 if (spelling_end.isValid()) { | 429 if (spelling_end.isValid()) { |
| 434 SourceLocation token_end = | 430 SourceLocation token_end = |
| 435 Lexer::getLocForEndOfToken(spelling_end, 0, manager, LangOptions()); | 431 Lexer::getLocForEndOfToken(spelling_end, 0, manager, LangOptions()); |
| 436 diagnostic().Report(token_end, diag_method_requires_override_) | 432 diagnostic().Report(token_end, diag_method_requires_override_) |
| 437 << FixItHint::CreateInsertion(token_end, " override"); | 433 << FixItHint::CreateInsertion(token_end, " override"); |
| 438 } else { | 434 } else { |
| 439 diagnostic().Report(loc, diag_method_requires_override_); | 435 diagnostic().Report(loc, diag_method_requires_override_); |
| 440 } | 436 } |
| 441 } | 437 } |
| 442 | 438 |
| 443 if (final_attr && override_attr) { | 439 if (final_attr && override_attr && options_.strict_virtual_specifiers) { |
| 444 diagnostic().Report(override_attr->getLocation(), | 440 diagnostic().Report(override_attr->getLocation(), |
| 445 diag_redundant_virtual_specifier_) | 441 diag_redundant_virtual_specifier_) |
| 446 << override_attr << final_attr | 442 << override_attr << final_attr |
| 447 << FixItHint::CreateRemoval(override_attr->getRange()); | 443 << FixItHint::CreateRemoval(override_attr->getRange()); |
| 448 } | 444 } |
| 449 | 445 |
| 450 if (final_attr && !is_override) { | 446 if (final_attr && !is_override && options_.strict_virtual_specifiers) { |
| 451 diagnostic().Report(method->getLocStart(), | 447 diagnostic().Report(method->getLocStart(), |
| 452 diag_base_method_virtual_and_final_) | 448 diag_base_method_virtual_and_final_) |
| 453 << FixItRemovalForVirtual(manager, method) | 449 << FixItRemovalForVirtual(manager, method) |
| 454 << FixItHint::CreateRemoval(final_attr->getRange()); | 450 << FixItHint::CreateRemoval(final_attr->getRange()); |
| 455 } | 451 } |
| 456 } | 452 } |
| 457 | 453 |
| 458 void FindBadConstructsConsumer::CheckVirtualBodies( | 454 void FindBadConstructsConsumer::CheckVirtualBodies( |
| 459 const CXXMethodDecl* method) { | 455 const CXXMethodDecl* method) { |
| 460 // Virtual methods should not have inline definitions beyond "{}". This | 456 // Virtual methods should not have inline definitions beyond "{}". This |
| (...skipping 324 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 785 // one of those, it means there is at least one member after a factory. | 781 // one of those, it means there is at least one member after a factory. |
| 786 if (weak_ptr_factory_location.isValid() && | 782 if (weak_ptr_factory_location.isValid() && |
| 787 !param_is_weak_ptr_factory_to_self) { | 783 !param_is_weak_ptr_factory_to_self) { |
| 788 diagnostic().Report(weak_ptr_factory_location, | 784 diagnostic().Report(weak_ptr_factory_location, |
| 789 diag_weak_ptr_factory_order_); | 785 diag_weak_ptr_factory_order_); |
| 790 } | 786 } |
| 791 } | 787 } |
| 792 } | 788 } |
| 793 | 789 |
| 794 } // namespace chrome_checker | 790 } // namespace chrome_checker |
| OLD | NEW |