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

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

Issue 1141793003: Update from https://crrev.com/329939 (Closed) Base URL: git@github.com:domokit/mojo.git@master
Patch Set: Created 5 years, 7 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/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 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
71 } 71 }
72 72
73 // Generates a fixit hint to remove the 'virtual' keyword. 73 // Generates a fixit hint to remove the 'virtual' keyword.
74 // Unfortunately, there doesn't seem to be a good way to determine the source 74 // Unfortunately, there doesn't seem to be a good way to determine the source
75 // location of the 'virtual' keyword. It's available in Declarator, but that 75 // location of the 'virtual' keyword. It's available in Declarator, but that
76 // isn't accessible from the AST. So instead, make an educated guess that the 76 // isn't accessible from the AST. So instead, make an educated guess that the
77 // first token is probably the virtual keyword. Strictly speaking, this doesn't 77 // first token is probably the virtual keyword. Strictly speaking, this doesn't
78 // have to be true, but it probably will be. 78 // have to be true, but it probably will be.
79 // TODO(dcheng): Add a warning to force virtual to always appear first ;-) 79 // TODO(dcheng): Add a warning to force virtual to always appear first ;-)
80 FixItHint FixItRemovalForVirtual(const SourceManager& manager, 80 FixItHint FixItRemovalForVirtual(const SourceManager& manager,
81 const LangOptions& lang_opts,
81 const CXXMethodDecl* method) { 82 const CXXMethodDecl* method) {
82 SourceRange range(method->getLocStart()); 83 SourceRange range(method->getLocStart());
83 // Get the spelling loc just in case it was expanded from a macro. 84 // Get the spelling loc just in case it was expanded from a macro.
84 SourceRange spelling_range(manager.getSpellingLoc(range.getBegin())); 85 SourceRange spelling_range(manager.getSpellingLoc(range.getBegin()));
85 // Sanity check that the text looks like virtual. 86 // Sanity check that the text looks like virtual.
86 StringRef text = clang::Lexer::getSourceText( 87 StringRef text = clang::Lexer::getSourceText(
87 CharSourceRange::getTokenRange(spelling_range), manager, LangOptions()); 88 CharSourceRange::getTokenRange(spelling_range), manager, lang_opts);
88 if (text.trim() != "virtual") 89 if (text.trim() != "virtual")
89 return FixItHint(); 90 return FixItHint();
90 return FixItHint::CreateRemoval(range); 91 return FixItHint::CreateRemoval(range);
91 } 92 }
92 93
93 bool IsPodOrTemplateType(const CXXRecordDecl& record) { 94 bool IsPodOrTemplateType(const CXXRecordDecl& record) {
94 return record.isPOD() || 95 return record.isPOD() ||
95 record.getDescribedClassTemplate() || 96 record.getDescribedClassTemplate() ||
96 record.getTemplateSpecializationKind() || 97 record.getTemplateSpecializationKind() ||
97 record.isDependentType(); 98 record.isDependentType();
(...skipping 185 matching lines...) Expand 10 before | Expand all | Expand 10 after
283 // find one that tries to be inline. 284 // find one that tries to be inline.
284 for (CXXRecordDecl::ctor_iterator it = record->ctor_begin(); 285 for (CXXRecordDecl::ctor_iterator it = record->ctor_begin();
285 it != record->ctor_end(); 286 it != record->ctor_end();
286 ++it) { 287 ++it) {
287 // The current check is buggy. An implicit copy constructor does not 288 // The current check is buggy. An implicit copy constructor does not
288 // have an inline body, so this check never fires for classes with a 289 // have an inline body, so this check never fires for classes with a
289 // user-declared out-of-line constructor. 290 // user-declared out-of-line constructor.
290 if (it->hasInlineBody()) { 291 if (it->hasInlineBody()) {
291 if (it->isCopyConstructor() && 292 if (it->isCopyConstructor() &&
292 !record->hasUserDeclaredCopyConstructor()) { 293 !record->hasUserDeclaredCopyConstructor()) {
293 emitWarning(record_location, 294 // In general, implicit constructors are generated on demand. But
294 "Complex class/struct needs an explicit out-of-line " 295 // in the Windows component build, dllexport causes instantiation of
295 "copy constructor."); 296 // the copy constructor which means that this fires on many more
297 // classes. For now, suppress this on dllexported classes.
298 // (This does mean that windows component builds will not emit this
299 // warning in some cases where it is emitted in other configs, but
300 // that's the better tradeoff at this point).
301 // TODO(dcheng): With the RecursiveASTVisitor, these warnings might
302 // be emitted on other platforms too, reevaluate if we want to keep
303 // surpressing this then http://crbug.com/467288
304 if (!record->hasAttr<DLLExportAttr>())
305 emitWarning(record_location,
306 "Complex class/struct needs an explicit out-of-line "
307 "copy constructor.");
296 } else { 308 } else {
297 emitWarning(it->getInnerLocStart(), 309 // See the comment in the previous branch about copy constructors.
298 "Complex constructor has an inlined body."); 310 // This does the same for implicit move constructors.
311 bool is_likely_compiler_generated_dllexport_move_ctor =
312 it->isMoveConstructor() &&
313 !record->hasUserDeclaredMoveConstructor() &&
314 record->hasAttr<DLLExportAttr>();
315 if (!is_likely_compiler_generated_dllexport_move_ctor)
316 emitWarning(it->getInnerLocStart(),
317 "Complex constructor has an inlined body.");
299 } 318 }
300 } else if (it->isInlined() && !it->isInlineSpecified() && 319 } else if (it->isInlined() && !it->isInlineSpecified() &&
301 !it->isDeleted() && (!it->isCopyOrMoveConstructor() || 320 !it->isDeleted() && (!it->isCopyOrMoveConstructor() ||
302 it->isExplicitlyDefaulted())) { 321 it->isExplicitlyDefaulted())) {
303 // isInlined() is a more reliable check than hasInlineBody(), but 322 // isInlined() is a more reliable check than hasInlineBody(), but
304 // unfortunately, it results in warnings for implicit copy/move 323 // unfortunately, it results in warnings for implicit copy/move
305 // constructors in the previously mentioned situation. To preserve 324 // constructors in the previously mentioned situation. To preserve
306 // compatibility with existing Chromium code, only warn if it's an 325 // compatibility with existing Chromium code, only warn if it's an
307 // explicitly defaulted copy or move constructor. 326 // explicitly defaulted copy or move constructor.
308 emitWarning(it->getInnerLocStart(), 327 emitWarning(it->getInnerLocStart(),
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
347 // incorrectly, but having the plugin enforce override is also nice. 366 // incorrectly, but having the plugin enforce override is also nice.
348 (InTestingNamespace(overridden) && 367 (InTestingNamespace(overridden) &&
349 !IsGtestTestFixture(overridden->getParent()))) { 368 !IsGtestTestFixture(overridden->getParent()))) {
350 return true; 369 return true;
351 } 370 }
352 } 371 }
353 372
354 return false; 373 return false;
355 } 374 }
356 375
376 SuppressibleDiagnosticBuilder
377 FindBadConstructsConsumer::ReportIfSpellingLocNotIgnored(
378 SourceLocation loc,
379 unsigned diagnostic_id) {
380 return SuppressibleDiagnosticBuilder(
381 &diagnostic(), loc, diagnostic_id,
382 InBannedDirectory(instance().getSourceManager().getSpellingLoc(loc)));
383 }
384
357 // Checks that virtual methods are correctly annotated, and have no body in a 385 // Checks that virtual methods are correctly annotated, and have no body in a
358 // header file. 386 // header file.
359 void FindBadConstructsConsumer::CheckVirtualMethods( 387 void FindBadConstructsConsumer::CheckVirtualMethods(
360 SourceLocation record_location, 388 SourceLocation record_location,
361 CXXRecordDecl* record, 389 CXXRecordDecl* record,
362 bool warn_on_inline_bodies) { 390 bool warn_on_inline_bodies) {
363 // Gmock objects trigger these for each MOCK_BLAH() macro used. So we have a 391 // Gmock objects trigger these for each MOCK_BLAH() macro used. So we have a
364 // trick to get around that. If a class has member variables whose types are 392 // trick to get around that. If a class has member variables whose types are
365 // in the "testing" namespace (which is how gmock works behind the scenes), 393 // in the "testing" namespace (which is how gmock works behind the scenes),
366 // there's a really high chance we won't care about these errors 394 // there's a really high chance we won't care about these errors
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
404 const CXXMethodDecl* method) { 432 const CXXMethodDecl* method) {
405 bool is_override = method->size_overridden_methods() > 0; 433 bool is_override = method->size_overridden_methods() > 0;
406 bool has_virtual = method->isVirtualAsWritten(); 434 bool has_virtual = method->isVirtualAsWritten();
407 OverrideAttr* override_attr = method->getAttr<OverrideAttr>(); 435 OverrideAttr* override_attr = method->getAttr<OverrideAttr>();
408 FinalAttr* final_attr = method->getAttr<FinalAttr>(); 436 FinalAttr* final_attr = method->getAttr<FinalAttr>();
409 437
410 if (IsMethodInBannedOrTestingNamespace(method)) 438 if (IsMethodInBannedOrTestingNamespace(method))
411 return; 439 return;
412 440
413 SourceManager& manager = instance().getSourceManager(); 441 SourceManager& manager = instance().getSourceManager();
442 const LangOptions& lang_opts = instance().getLangOpts();
414 443
415 // Complain if a method is annotated virtual && (override || final). 444 // Complain if a method is annotated virtual && (override || final).
416 if (has_virtual && (override_attr || final_attr)) { 445 if (has_virtual && (override_attr || final_attr)) {
417 // ... but only if virtual does not originate in a macro from a banned file. 446 // ... but only if virtual does not originate in a macro from a banned file.
418 // Note this is just an educated guess: the assumption here is that any 447 // Note this is just an educated guess: the assumption here is that any
419 // macro for declaring methods will probably be at the start of the method's 448 // macro for declaring methods will probably be at the start of the method's
420 // source range. 449 // source range.
421 if (!InBannedDirectory(manager.getSpellingLoc(method->getLocStart()))) { 450 ReportIfSpellingLocNotIgnored(method->getLocStart(),
422 diagnostic().Report(method->getLocStart(), 451 diag_redundant_virtual_specifier_)
423 diag_redundant_virtual_specifier_) 452 << "'virtual'"
424 << "'virtual'" 453 << (override_attr ? static_cast<Attr*>(override_attr) : final_attr)
425 << (override_attr ? static_cast<Attr*>(override_attr) : final_attr) 454 << FixItRemovalForVirtual(manager, lang_opts, method);
426 << FixItRemovalForVirtual(manager, method);
427 }
428 } 455 }
429 456
430 // Complain if a method is an override and is not annotated with override or 457 // Complain if a method is an override and is not annotated with override or
431 // final. 458 // final.
432 if (is_override && !override_attr && !final_attr) { 459 if (is_override && !override_attr && !final_attr) {
433 SourceRange range = method->getSourceRange(); 460 SourceRange range = method->getSourceRange();
434 SourceLocation loc; 461 SourceLocation loc;
435 if (method->hasInlineBody()) { 462 if (method->hasInlineBody()) {
436 loc = method->getBody()->getSourceRange().getBegin(); 463 loc = method->getBody()->getSourceRange().getBegin();
437 } else { 464 } else {
438 // TODO(dcheng): We should probably use ASTContext's LangOptions here. 465 loc = Lexer::getLocForEndOfToken(manager.getSpellingLoc(range.getEnd()),
439 LangOptions lang_options; 466 0, manager, lang_opts);
440 loc = Lexer::getLocForEndOfToken(
441 manager.getSpellingLoc(range.getEnd()), 0,
442 manager, lang_options);
443 // The original code used the ending source loc of TypeSourceInfo's 467 // The original code used the ending source loc of TypeSourceInfo's
444 // TypeLoc. Unfortunately, this breaks down in the presence of attributes. 468 // TypeLoc. Unfortunately, this breaks down in the presence of attributes.
445 // Attributes often appear at the end of a TypeLoc, e.g. 469 // Attributes often appear at the end of a TypeLoc, e.g.
446 // virtual ULONG __stdcall AddRef() 470 // virtual ULONG __stdcall AddRef()
447 // has a TypeSourceInfo that looks something like: 471 // has a TypeSourceInfo that looks something like:
448 // ULONG AddRef() __attribute(stdcall) 472 // ULONG AddRef() __attribute(stdcall)
449 // so a fix-it insertion would be generated to insert 'override' after 473 // so a fix-it insertion would be generated to insert 'override' after
450 // __stdcall in the code as written. 474 // __stdcall in the code as written.
451 // While using the spelling loc of the CXXMethodDecl fixes attribute 475 // While using the spelling loc of the CXXMethodDecl fixes attribute
452 // handling, it breaks handling of "= 0" and similar constructs.. To work 476 // handling, it breaks handling of "= 0" and similar constructs.. To work
453 // around this, scan backwards in the source text for a '=' or ')' token 477 // around this, scan backwards in the source text for a '=' or ')' token
454 // and adjust the location as needed... 478 // and adjust the location as needed...
455 for (SourceLocation l = loc.getLocWithOffset(-1); 479 for (SourceLocation l = loc.getLocWithOffset(-1);
456 l != manager.getLocForStartOfFile(manager.getFileID(loc)); 480 l != manager.getLocForStartOfFile(manager.getFileID(loc));
457 l = l.getLocWithOffset(-1)) { 481 l = l.getLocWithOffset(-1)) {
458 l = Lexer::GetBeginningOfToken(l, manager, lang_options); 482 l = Lexer::GetBeginningOfToken(l, manager, lang_opts);
459 Token token; 483 Token token;
460 // getRawToken() returns *true* on failure. In that case, just give up 484 // getRawToken() returns *true* on failure. In that case, just give up
461 // and don't bother generating a possibly incorrect fix-it. 485 // and don't bother generating a possibly incorrect fix-it.
462 if (Lexer::getRawToken(l, token, manager, lang_options, true)) { 486 if (Lexer::getRawToken(l, token, manager, lang_opts, true)) {
463 loc = SourceLocation(); 487 loc = SourceLocation();
464 break; 488 break;
465 } 489 }
466 if (token.is(tok::r_paren)) { 490 if (token.is(tok::r_paren)) {
467 break; 491 break;
468 } else if (token.is(tok::equal)) { 492 } else if (token.is(tok::equal)) {
469 loc = l; 493 loc = l;
470 break; 494 break;
471 } 495 }
472 } 496 }
473 } 497 }
498 // Again, only emit the warning if it doesn't originate from a macro in
499 // a system header.
474 if (loc.isValid()) { 500 if (loc.isValid()) {
475 diagnostic().Report(loc, diag_method_requires_override_) 501 ReportIfSpellingLocNotIgnored(loc, diag_method_requires_override_)
476 << FixItHint::CreateInsertion(loc, " override"); 502 << FixItHint::CreateInsertion(loc, " override");
477 } else { 503 } else {
478 diagnostic().Report(range.getBegin(), diag_method_requires_override_); 504 ReportIfSpellingLocNotIgnored(range.getBegin(),
505 diag_method_requires_override_);
479 } 506 }
480 } 507 }
481 508
482 if (final_attr && override_attr) { 509 if (final_attr && override_attr) {
483 diagnostic().Report(override_attr->getLocation(), 510 ReportIfSpellingLocNotIgnored(override_attr->getLocation(),
484 diag_redundant_virtual_specifier_) 511 diag_redundant_virtual_specifier_)
485 << override_attr << final_attr 512 << override_attr << final_attr
486 << FixItHint::CreateRemoval(override_attr->getRange()); 513 << FixItHint::CreateRemoval(override_attr->getRange());
487 } 514 }
488 515
489 if (final_attr && !is_override) { 516 if (final_attr && !is_override) {
490 diagnostic().Report(method->getLocStart(), 517 ReportIfSpellingLocNotIgnored(method->getLocStart(),
491 diag_base_method_virtual_and_final_) 518 diag_base_method_virtual_and_final_)
492 << FixItRemovalForVirtual(manager, method) 519 << FixItRemovalForVirtual(manager, lang_opts, method)
493 << FixItHint::CreateRemoval(final_attr->getRange()); 520 << FixItHint::CreateRemoval(final_attr->getRange());
494 } 521 }
495 } 522 }
496 523
497 void FindBadConstructsConsumer::CheckVirtualBodies( 524 void FindBadConstructsConsumer::CheckVirtualBodies(
498 const CXXMethodDecl* method) { 525 const CXXMethodDecl* method) {
499 // Virtual methods should not have inline definitions beyond "{}". This 526 // Virtual methods should not have inline definitions beyond "{}". This
500 // only matters for header files. 527 // only matters for header files.
501 if (method->hasBody() && method->hasInlineBody()) { 528 if (method->hasBody() && method->hasInlineBody()) {
502 if (CompoundStmt* cs = dyn_cast<CompoundStmt>(method->getBody())) { 529 if (CompoundStmt* cs = dyn_cast<CompoundStmt>(method->getBody())) {
503 if (cs->size()) { 530 if (cs->size()) {
504 emitWarning(cs->getLBracLoc(), 531 SourceLocation loc = cs->getLBracLoc();
505 "virtual methods with non-empty bodies shouldn't be " 532 // CR_BEGIN_MSG_MAP_EX and BEGIN_SAFE_MSG_MAP_EX try to be compatible
506 "declared inline."); 533 // to BEGIN_MSG_MAP(_EX). So even though they are in chrome code,
534 // we can't easily fix them, so explicitly whitelist them here.
535 bool emit = true;
536 if (loc.isMacroID()) {
537 SourceManager& manager = instance().getSourceManager();
538 if (InBannedDirectory(manager.getSpellingLoc(loc)))
539 emit = false;
540 else {
541 StringRef name = Lexer::getImmediateMacroName(
542 loc, manager, instance().getLangOpts());
543 if (name == "CR_BEGIN_MSG_MAP_EX" ||
544 name == "BEGIN_SAFE_MSG_MAP_EX")
545 emit = false;
546 }
547 }
548 if (emit)
549 emitWarning(loc,
550 "virtual methods with non-empty bodies shouldn't be "
551 "declared inline.");
507 } 552 }
508 } 553 }
509 } 554 }
510 } 555 }
511 556
512 void FindBadConstructsConsumer::CountType(const Type* type, 557 void FindBadConstructsConsumer::CountType(const Type* type,
513 int* trivial_member, 558 int* trivial_member,
514 int* non_trivial_member, 559 int* non_trivial_member,
515 int* templated_non_trivial_member) { 560 int* templated_non_trivial_member) {
516 switch (type->getTypeClass()) { 561 switch (type->getTypeClass()) {
517 case Type::Record: { 562 case Type::Record: {
518 // Simplifying; the whole class isn't trivial if the dtor is, but 563 // Simplifying; the whole class isn't trivial if the dtor is, but
519 // we use this as a signal about complexity. 564 // we use this as a signal about complexity.
520 if (TypeHasNonTrivialDtor(type)) 565 if (TypeHasNonTrivialDtor(type))
521 (*trivial_member)++; 566 (*trivial_member)++;
522 else 567 else
523 (*non_trivial_member)++; 568 (*non_trivial_member)++;
524 break; 569 break;
525 } 570 }
526 case Type::TemplateSpecialization: { 571 case Type::TemplateSpecialization: {
527 TemplateName name = 572 TemplateName name =
528 dyn_cast<TemplateSpecializationType>(type)->getTemplateName(); 573 dyn_cast<TemplateSpecializationType>(type)->getTemplateName();
529 bool whitelisted_template = false; 574 bool whitelisted_template = false;
530 575
531 // HACK: I'm at a loss about how to get the syntax checker to get 576 // HACK: I'm at a loss about how to get the syntax checker to get
532 // whether a template is exterened or not. For the first pass here, 577 // whether a template is externed or not. For the first pass here,
533 // just do retarded string comparisons. 578 // just do retarded string comparisons.
534 if (TemplateDecl* decl = name.getAsTemplateDecl()) { 579 if (TemplateDecl* decl = name.getAsTemplateDecl()) {
535 std::string base_name = decl->getNameAsString(); 580 std::string base_name = decl->getNameAsString();
536 if (base_name == "basic_string") 581 if (base_name == "basic_string")
537 whitelisted_template = true; 582 whitelisted_template = true;
538 } 583 }
539 584
540 if (whitelisted_template) 585 if (whitelisted_template)
541 (*non_trivial_member)++; 586 (*non_trivial_member)++;
542 else 587 else
(...skipping 274 matching lines...) Expand 10 before | Expand all | Expand 10 after
817 // one of those, it means there is at least one member after a factory. 862 // one of those, it means there is at least one member after a factory.
818 if (weak_ptr_factory_location.isValid() && 863 if (weak_ptr_factory_location.isValid() &&
819 !param_is_weak_ptr_factory_to_self) { 864 !param_is_weak_ptr_factory_to_self) {
820 diagnostic().Report(weak_ptr_factory_location, 865 diagnostic().Report(weak_ptr_factory_location,
821 diag_weak_ptr_factory_order_); 866 diag_weak_ptr_factory_order_);
822 } 867 }
823 } 868 }
824 } 869 }
825 870
826 } // namespace chrome_checker 871 } // namespace chrome_checker
OLDNEW
« no previous file with comments | « tools/clang/plugins/FindBadConstructsConsumer.h ('k') | tools/clang/plugins/SuppressibleDiagnosticBuilder.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698