Commit 993e04f7 authored by dcheng's avatar dcheng Committed by Commit bot

Update plugin to handle new style rules for virtual specifiers.

Implements several new checks in the plugin, gated behind a flag:
- Only one of {virtual,override,final} should be ever used.
- Destructors must also be annotated correctly.
- A virtual final method that doesn't override anything should be
  devirtualized.

The test harness for the Chrome plugin has also been updated to stop
it from littering the source tree with object files, and to make the
golden files easier to update.

BUG=417463

Review URL: https://codereview.chromium.org/597863002

Cr-Commit-Position: refs/heads/master@{#297129}
parent a0fd0993
...@@ -36,6 +36,8 @@ bool FindBadConstructsAction::ParseArgs(const CompilerInstance& instance, ...@@ -36,6 +36,8 @@ bool FindBadConstructsAction::ParseArgs(const CompilerInstance& instance,
// TODO(tsepez): Enable this by default once http://crbug.com/356815 // TODO(tsepez): Enable this by default once http://crbug.com/356815
// and http://crbug.com/356816 are fixed. // and http://crbug.com/356816 are fixed.
options_.check_enum_last_value = true; options_.check_enum_last_value = true;
} else if (args[i] == "strict-virtual-specifiers") {
options_.strict_virtual_specifiers = true;
} else { } else {
parsed = false; parsed = false;
llvm::errs() << "Unknown clang plugin argument: " << args[i] << "\n"; llvm::errs() << "Unknown clang plugin argument: " << args[i] << "\n";
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include "FindBadConstructsConsumer.h" #include "FindBadConstructsConsumer.h"
#include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/CompilerInstance.h"
#include "clang/AST/Attr.h"
#include "clang/Lex/Lexer.h" #include "clang/Lex/Lexer.h"
#include "llvm/Support/raw_ostream.h" #include "llvm/Support/raw_ostream.h"
...@@ -15,9 +16,15 @@ namespace chrome_checker { ...@@ -15,9 +16,15 @@ namespace chrome_checker {
namespace { namespace {
const char kMethodRequiresOverride[] = const char kMethodRequiresOverride[] =
"[chromium-style] Overriding method must be marked with OVERRIDE."; "[chromium-style] Overriding method must be marked with 'override' or "
const char kMethodRequiresVirtual[] = "'final'.";
"[chromium-style] Overriding method must have \"virtual\" keyword."; const char kRedundantVirtualSpecifier[] =
"[chromium-style] %0 is redundant; %1 implies %0.";
// http://llvm.org/bugs/show_bug.cgi?id=21051 has been filed to make this a
// Clang warning.
const char kBaseMethodVirtualAndFinal[] =
"[chromium-style] The virtual method does not override anything and is "
"final; consider making it non-virtual.";
const char kNoExplicitDtor[] = const char kNoExplicitDtor[] =
"[chromium-style] Classes that are ref-counted should have explicit " "[chromium-style] Classes that are ref-counted should have explicit "
"destructors that are declared protected or private."; "destructors that are declared protected or private.";
...@@ -59,22 +66,47 @@ const Type* UnwrapType(const Type* type) { ...@@ -59,22 +66,47 @@ const Type* UnwrapType(const Type* type) {
return type; return type;
} }
FixItHint FixItRemovalForVirtual(const SourceManager& manager,
const CXXMethodDecl* method) {
// Unfortunately, there doesn't seem to be a good way to determine the
// location of the 'virtual' keyword. It's available in Declarator, but that
// isn't accessible from the AST. So instead, make an educated guess that the
// first token is probably the virtual keyword. Strictly speaking, this
// doesn't have to be true, but it probably will be.
// TODO(dcheng): Add a warning to force virtual to always appear first ;-)
SourceRange range(method->getLocStart());
// Get the spelling loc just in case it was expanded from a macro.
SourceRange spelling_range(manager.getSpellingLoc(range.getBegin()));
// Sanity check that the text looks like virtual.
StringRef text = clang::Lexer::getSourceText(
CharSourceRange::getTokenRange(spelling_range), manager, LangOptions());
if (text.trim() != "virtual")
return FixItHint();
return FixItHint::CreateRemoval(range);
}
} // namespace } // namespace
FindBadConstructsConsumer::FindBadConstructsConsumer(CompilerInstance& instance, FindBadConstructsConsumer::FindBadConstructsConsumer(CompilerInstance& instance,
const Options& options) const Options& options)
: ChromeClassTester(instance), options_(options) { : ChromeClassTester(instance), options_(options) {
// Register warning/error messages. // Messages for virtual method specifiers.
diag_method_requires_override_ = diag_method_requires_override_ =
diagnostic().getCustomDiagID(getErrorLevel(), kMethodRequiresOverride); diagnostic().getCustomDiagID(getErrorLevel(), kMethodRequiresOverride);
diag_method_requires_virtual_ = diag_redundant_virtual_specifier_ =
diagnostic().getCustomDiagID(getErrorLevel(), kMethodRequiresVirtual); diagnostic().getCustomDiagID(getErrorLevel(), kRedundantVirtualSpecifier);
diag_base_method_virtual_and_final_ =
diagnostic().getCustomDiagID(getErrorLevel(), kBaseMethodVirtualAndFinal);
// Messages for destructors.
diag_no_explicit_dtor_ = diag_no_explicit_dtor_ =
diagnostic().getCustomDiagID(getErrorLevel(), kNoExplicitDtor); diagnostic().getCustomDiagID(getErrorLevel(), kNoExplicitDtor);
diag_public_dtor_ = diag_public_dtor_ =
diagnostic().getCustomDiagID(getErrorLevel(), kPublicDtor); diagnostic().getCustomDiagID(getErrorLevel(), kPublicDtor);
diag_protected_non_virtual_dtor_ = diag_protected_non_virtual_dtor_ =
diagnostic().getCustomDiagID(getErrorLevel(), kProtectedNonVirtualDtor); diagnostic().getCustomDiagID(getErrorLevel(), kProtectedNonVirtualDtor);
// Miscellaneous messages.
diag_weak_ptr_factory_order_ = diag_weak_ptr_factory_order_ =
diagnostic().getCustomDiagID(getErrorLevel(), kWeakPtrFactoryOrder); diagnostic().getCustomDiagID(getErrorLevel(), kWeakPtrFactoryOrder);
diag_bad_enum_last_value_ = diag_bad_enum_last_value_ =
...@@ -103,8 +135,7 @@ void FindBadConstructsConsumer::CheckChromeClass(SourceLocation record_location, ...@@ -103,8 +135,7 @@ void FindBadConstructsConsumer::CheckChromeClass(SourceLocation record_location,
bool warn_on_inline_bodies = !implementation_file; bool warn_on_inline_bodies = !implementation_file;
// Check that all virtual methods are marked accordingly with both // Check that all virtual methods are annotated with override or final.
// virtual and OVERRIDE.
CheckVirtualMethods(record_location, record, warn_on_inline_bodies); CheckVirtualMethods(record_location, record, warn_on_inline_bodies);
CheckRefCountedDtors(record_location, record); CheckRefCountedDtors(record_location, record);
...@@ -250,35 +281,6 @@ void FindBadConstructsConsumer::CheckCtorDtorWeight( ...@@ -250,35 +281,6 @@ void FindBadConstructsConsumer::CheckCtorDtorWeight(
} }
} }
void FindBadConstructsConsumer::CheckVirtualMethod(const CXXMethodDecl* method,
bool warn_on_inline_bodies) {
if (!method->isVirtual())
return;
if (!method->isVirtualAsWritten()) {
SourceLocation loc = method->getTypeSpecStartLoc();
if (isa<CXXDestructorDecl>(method))
loc = method->getInnerLocStart();
SourceManager& manager = instance().getSourceManager();
FullSourceLoc full_loc(loc, manager);
SourceLocation spelling_loc = manager.getSpellingLoc(loc);
diagnostic().Report(full_loc, diag_method_requires_virtual_)
<< FixItHint::CreateInsertion(spelling_loc, "virtual ");
}
// Virtual methods should not have inline definitions beyond "{}". This
// only matters for header files.
if (warn_on_inline_bodies && method->hasBody() && method->hasInlineBody()) {
if (CompoundStmt* cs = dyn_cast<CompoundStmt>(method->getBody())) {
if (cs->size()) {
emitWarning(cs->getLBracLoc(),
"virtual methods with non-empty bodies shouldn't be "
"declared inline.");
}
}
}
}
bool FindBadConstructsConsumer::InTestingNamespace(const Decl* record) { bool FindBadConstructsConsumer::InTestingNamespace(const Decl* record) {
return GetNamespace(record).find("testing") != std::string::npos; return GetNamespace(record).find("testing") != std::string::npos;
} }
...@@ -300,47 +302,16 @@ bool FindBadConstructsConsumer::IsMethodInBannedOrTestingNamespace( ...@@ -300,47 +302,16 @@ bool FindBadConstructsConsumer::IsMethodInBannedOrTestingNamespace(
return false; return false;
} }
void FindBadConstructsConsumer::CheckOverriddenMethod( // Checks that virtual methods are correctly annotated, and have no body in a
const CXXMethodDecl* method) { // header file.
if (!method->size_overridden_methods() || method->getAttr<OverrideAttr>())
return;
if (isa<CXXDestructorDecl>(method) || method->isPure())
return;
if (IsMethodInBannedOrTestingNamespace(method))
return;
SourceManager& manager = instance().getSourceManager();
SourceRange type_info_range =
method->getTypeSourceInfo()->getTypeLoc().getSourceRange();
FullSourceLoc loc(type_info_range.getBegin(), manager);
// Build the FixIt insertion point after the end of the method definition,
// including any const-qualifiers and attributes, and before the opening
// of the l-curly-brace (if inline) or the semi-color (if a declaration).
SourceLocation spelling_end =
manager.getSpellingLoc(type_info_range.getEnd());
if (spelling_end.isValid()) {
SourceLocation token_end =
Lexer::getLocForEndOfToken(spelling_end, 0, manager, LangOptions());
diagnostic().Report(token_end, diag_method_requires_override_)
<< FixItHint::CreateInsertion(token_end, " OVERRIDE");
} else {
diagnostic().Report(loc, diag_method_requires_override_);
}
}
// Makes sure there is a "virtual" keyword on virtual methods.
//
// Gmock objects trigger these for each MOCK_BLAH() macro used. So we have a
// trick to get around that. If a class has member variables whose types are
// in the "testing" namespace (which is how gmock works behind the scenes),
// there's a really high chance we won't care about these errors
void FindBadConstructsConsumer::CheckVirtualMethods( void FindBadConstructsConsumer::CheckVirtualMethods(
SourceLocation record_location, SourceLocation record_location,
CXXRecordDecl* record, CXXRecordDecl* record,
bool warn_on_inline_bodies) { bool warn_on_inline_bodies) {
// Gmock objects trigger these for each MOCK_BLAH() macro used. So we have a
// trick to get around that. If a class has member variables whose types are
// in the "testing" namespace (which is how gmock works behind the scenes),
// there's a really high chance we won't care about these errors
for (CXXRecordDecl::field_iterator it = record->field_begin(); for (CXXRecordDecl::field_iterator it = record->field_begin();
it != record->field_end(); it != record->field_end();
++it) { ++it) {
...@@ -363,9 +334,96 @@ void FindBadConstructsConsumer::CheckVirtualMethods( ...@@ -363,9 +334,96 @@ void FindBadConstructsConsumer::CheckVirtualMethods(
} else if (isa<CXXDestructorDecl>(*it) && } else if (isa<CXXDestructorDecl>(*it) &&
!record->hasUserDeclaredDestructor()) { !record->hasUserDeclaredDestructor()) {
// Ignore non-user-declared destructors. // Ignore non-user-declared destructors.
} else if (!it->isVirtual()) {
continue;
} else {
CheckVirtualSpecifiers(*it);
if (warn_on_inline_bodies)
CheckVirtualBodies(*it);
}
}
}
// Makes sure that virtual methods use the most appropriate specifier. If a
// virtual method overrides a method from a base class, only the override
// specifier should be used. If the method should not be overridden by derived
// classes, only the final specifier should be used.
void FindBadConstructsConsumer::CheckVirtualSpecifiers(
const CXXMethodDecl* method) {
bool is_override = method->size_overridden_methods() > 0;
bool has_virtual = method->isVirtualAsWritten();
OverrideAttr* override_attr = method->getAttr<OverrideAttr>();
FinalAttr* final_attr = method->getAttr<FinalAttr>();
if (method->isPure())
return;
if (IsMethodInBannedOrTestingNamespace(method))
return;
if (isa<CXXDestructorDecl>(method) && !options_.strict_virtual_specifiers)
return;
SourceManager& manager = instance().getSourceManager();
// Complain if a method is annotated virtual && (override || final).
if (has_virtual && (override_attr || final_attr) &&
options_.strict_virtual_specifiers) {
diagnostic().Report(method->getLocStart(),
diag_redundant_virtual_specifier_)
<< "'virtual'"
<< (override_attr ? static_cast<Attr*>(override_attr) : final_attr)
<< FixItRemovalForVirtual(manager, method);
}
// Complain if a method is an override and is not annotated with override or
// final.
if (is_override && !override_attr && !final_attr) {
SourceRange type_info_range =
method->getTypeSourceInfo()->getTypeLoc().getSourceRange();
FullSourceLoc loc(type_info_range.getBegin(), manager);
// Build the FixIt insertion point after the end of the method definition,
// including any const-qualifiers and attributes, and before the opening
// of the l-curly-brace (if inline) or the semi-color (if a declaration).
SourceLocation spelling_end =
manager.getSpellingLoc(type_info_range.getEnd());
if (spelling_end.isValid()) {
SourceLocation token_end =
Lexer::getLocForEndOfToken(spelling_end, 0, manager, LangOptions());
diagnostic().Report(token_end, diag_method_requires_override_)
<< FixItHint::CreateInsertion(token_end, " override");
} else { } else {
CheckVirtualMethod(*it, warn_on_inline_bodies); diagnostic().Report(loc, diag_method_requires_override_);
CheckOverriddenMethod(*it); }
}
if (final_attr && override_attr && options_.strict_virtual_specifiers) {
diagnostic().Report(override_attr->getLocation(),
diag_redundant_virtual_specifier_)
<< override_attr << final_attr
<< FixItHint::CreateRemoval(override_attr->getRange());
}
if (final_attr && !is_override && options_.strict_virtual_specifiers) {
diagnostic().Report(method->getLocStart(),
diag_base_method_virtual_and_final_)
<< FixItRemovalForVirtual(manager, method)
<< FixItHint::CreateRemoval(final_attr->getRange());
}
}
void FindBadConstructsConsumer::CheckVirtualBodies(
const CXXMethodDecl* method) {
// Virtual methods should not have inline definitions beyond "{}". This
// only matters for header files.
if (method->hasBody() && method->hasInlineBody()) {
if (CompoundStmt* cs = dyn_cast<CompoundStmt>(method->getBody())) {
if (cs->size()) {
emitWarning(cs->getLBracLoc(),
"virtual methods with non-empty bodies shouldn't be "
"declared inline.");
}
} }
} }
} }
......
...@@ -36,10 +36,10 @@ class FindBadConstructsConsumer : public ChromeClassTester { ...@@ -36,10 +36,10 @@ class FindBadConstructsConsumer : public ChromeClassTester {
const Options& options); const Options& options);
// ChromeClassTester overrides: // ChromeClassTester overrides:
virtual void CheckChromeClass(clang::SourceLocation record_location, void CheckChromeClass(clang::SourceLocation record_location,
clang::CXXRecordDecl* record); clang::CXXRecordDecl* record) override;
virtual void CheckChromeEnum(clang::SourceLocation enum_location, void CheckChromeEnum(clang::SourceLocation enum_location,
clang::EnumDecl* enum_decl); clang::EnumDecl* enum_decl) override;
private: private:
// The type of problematic ref-counting pattern that was encountered. // The type of problematic ref-counting pattern that was encountered.
...@@ -48,16 +48,14 @@ class FindBadConstructsConsumer : public ChromeClassTester { ...@@ -48,16 +48,14 @@ class FindBadConstructsConsumer : public ChromeClassTester {
void CheckCtorDtorWeight(clang::SourceLocation record_location, void CheckCtorDtorWeight(clang::SourceLocation record_location,
clang::CXXRecordDecl* record); clang::CXXRecordDecl* record);
void CheckVirtualMethod(const clang::CXXMethodDecl* method,
bool warn_on_inline_bodies);
bool InTestingNamespace(const clang::Decl* record); bool InTestingNamespace(const clang::Decl* record);
bool IsMethodInBannedOrTestingNamespace(const clang::CXXMethodDecl* method); bool IsMethodInBannedOrTestingNamespace(const clang::CXXMethodDecl* method);
void CheckOverriddenMethod(const clang::CXXMethodDecl* method);
void CheckVirtualMethods(clang::SourceLocation record_location, void CheckVirtualMethods(clang::SourceLocation record_location,
clang::CXXRecordDecl* record, clang::CXXRecordDecl* record,
bool warn_on_inline_bodies); bool warn_on_inline_bodies);
void CheckVirtualSpecifiers(const clang::CXXMethodDecl* method);
void CheckVirtualBodies(const clang::CXXMethodDecl* method);
void CountType(const clang::Type* type, void CountType(const clang::Type* type,
int* trivial_member, int* trivial_member,
...@@ -85,7 +83,8 @@ class FindBadConstructsConsumer : public ChromeClassTester { ...@@ -85,7 +83,8 @@ class FindBadConstructsConsumer : public ChromeClassTester {
const Options options_; const Options options_;
unsigned diag_method_requires_override_; unsigned diag_method_requires_override_;
unsigned diag_method_requires_virtual_; unsigned diag_redundant_virtual_specifier_;
unsigned diag_base_method_virtual_and_final_;
unsigned diag_no_explicit_dtor_; unsigned diag_no_explicit_dtor_;
unsigned diag_public_dtor_; unsigned diag_public_dtor_;
unsigned diag_protected_non_virtual_dtor_; unsigned diag_protected_non_virtual_dtor_;
......
...@@ -11,11 +11,13 @@ struct Options { ...@@ -11,11 +11,13 @@ struct Options {
Options() Options()
: check_base_classes(false), : check_base_classes(false),
check_weak_ptr_factory_order(false), check_weak_ptr_factory_order(false),
check_enum_last_value(false) {} check_enum_last_value(false),
strict_virtual_specifiers(false) {}
bool check_base_classes; bool check_base_classes;
bool check_weak_ptr_factory_order; bool check_weak_ptr_factory_order;
bool check_enum_last_value; bool check_enum_last_value;
bool strict_virtual_specifiers;
}; };
} // namespace chrome_checker } // namespace chrome_checker
......
/src/chromium/src/myheader.h:2:21: warning: [chromium-style] Overriding method must be marked with OVERRIDE. /src/chromium/src/myheader.h:2:21: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
virtual void foo(); // Should warn about missing 'override'. virtual void foo(); // Should warn about missing 'override'.
^ ^
OVERRIDE override
/src/chrome-breakpad/src/myheader.h:124:21: warning: [chromium-style] Overriding method must be marked with OVERRIDE. /src/chrome-breakpad/src/myheader.h:124:21: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
virtual void foo(); // Should warn about missing 'override'. virtual void foo(); // Should warn about missing 'override'.
^ ^
OVERRIDE override
2 warnings generated. 2 warnings generated.
In file included from overridden_methods.cpp:5: In file included from overridden_methods.cpp:5:
./overridden_methods.h:48:28: warning: [chromium-style] Overriding method must be marked with OVERRIDE. ./overridden_methods.h:48:28: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
virtual void SomeMethod(); virtual void SomeMethod();
^ ^
OVERRIDE override
./overridden_methods.h:52:34: warning: [chromium-style] Overriding method must be marked with OVERRIDE. ./overridden_methods.h:52:34: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
virtual void SomeInlineMethod() {} virtual void SomeInlineMethod() {}
^ ^
OVERRIDE override
./overridden_methods.h:56:39: warning: [chromium-style] Overriding method must be marked with OVERRIDE. ./overridden_methods.h:56:39: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
virtual void SomeConstMethod() const {} virtual void SomeConstMethod() const {}
^ ^
OVERRIDE override
./overridden_methods.h:58:53: warning: [chromium-style] Overriding method must be marked with OVERRIDE. ./overridden_methods.h:58:53: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
virtual void SomeMethodWithExceptionSpec() throw() {} virtual void SomeMethodWithExceptionSpec() throw() {}
^ ^
OVERRIDE override
./overridden_methods.h:61:67: warning: [chromium-style] Overriding method must be marked with OVERRIDE. ./overridden_methods.h:61:67: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
virtual void SomeConstMethodWithExceptionSpec() const throw(int) {} virtual void SomeConstMethodWithExceptionSpec() const throw(int) {}
^ ^
OVERRIDE override
./overridden_methods.h:63:39: warning: [chromium-style] Overriding method must be marked with OVERRIDE. ./overridden_methods.h:63:39: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
virtual void SomeNonPureBaseMethod() {} virtual void SomeNonPureBaseMethod() {}
^ ^
OVERRIDE override
./overridden_methods.h:65:39: warning: [chromium-style] Overriding method must be marked with OVERRIDE. ./overridden_methods.h:65:39: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
virtual void SomeMethodWithComment(); // This is a comment. virtual void SomeMethodWithComment(); // This is a comment.
^ ^
OVERRIDE override
./overridden_methods.h:67:46: warning: [chromium-style] Overriding method must be marked with OVERRIDE. ./overridden_methods.h:67:46: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
virtual void SomeMethodWithCommentAndBody() {} // This is a comment. virtual void SomeMethodWithCommentAndBody() {} // This is a comment.
^ ^
OVERRIDE override
overridden_methods.cpp:24:28: warning: [chromium-style] Overriding method must be marked with OVERRIDE. overridden_methods.cpp:24:28: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
virtual void SomeMethod(); virtual void SomeMethod();
^ ^
OVERRIDE override
overridden_methods.cpp:28:34: warning: [chromium-style] Overriding method must be marked with OVERRIDE. overridden_methods.cpp:28:34: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
virtual void SomeInlineMethod() {} virtual void SomeInlineMethod() {}
^ ^
OVERRIDE override
overridden_methods.cpp:32:39: warning: [chromium-style] Overriding method must be marked with OVERRIDE. overridden_methods.cpp:32:39: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
virtual void SomeConstMethod() const {} virtual void SomeConstMethod() const {}
^ ^
OVERRIDE override
overridden_methods.cpp:34:53: warning: [chromium-style] Overriding method must be marked with OVERRIDE. overridden_methods.cpp:34:53: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
virtual void SomeMethodWithExceptionSpec() throw() {} virtual void SomeMethodWithExceptionSpec() throw() {}
^ ^
OVERRIDE override
overridden_methods.cpp:37:67: warning: [chromium-style] Overriding method must be marked with OVERRIDE. overridden_methods.cpp:37:67: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
virtual void SomeConstMethodWithExceptionSpec() const throw(int) {} virtual void SomeConstMethodWithExceptionSpec() const throw(int) {}
^ ^
OVERRIDE override
overridden_methods.cpp:39:39: warning: [chromium-style] Overriding method must be marked with OVERRIDE. overridden_methods.cpp:39:39: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
virtual void SomeNonPureBaseMethod() {} virtual void SomeNonPureBaseMethod() {}
^ ^
OVERRIDE override
overridden_methods.cpp:41:39: warning: [chromium-style] Overriding method must be marked with OVERRIDE. overridden_methods.cpp:41:39: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
virtual void SomeMethodWithComment(); // This is a comment. virtual void SomeMethodWithComment(); // This is a comment.
^ ^
OVERRIDE override
overridden_methods.cpp:43:46: warning: [chromium-style] Overriding method must be marked with OVERRIDE. overridden_methods.cpp:43:46: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
virtual void SomeMethodWithCommentAndBody() {} // This is a comment. virtual void SomeMethodWithCommentAndBody() {} // This is a comment.
^ ^
OVERRIDE override
16 warnings generated. 16 warnings generated.
...@@ -33,7 +33,7 @@ do_testcase() { ...@@ -33,7 +33,7 @@ do_testcase() {
flags="${flags} -isysroot $(xcrun --show-sdk-path) -stdlib=libstdc++" flags="${flags} -isysroot $(xcrun --show-sdk-path) -stdlib=libstdc++"
fi fi
local output="$("${CLANG_PATH}" -c -Wno-c++11-extensions \ local output="$("${CLANG_PATH}" -fsyntax-only -Wno-c++11-extensions \
-Xclang -load -Xclang "${PLUGIN_PATH}" \ -Xclang -load -Xclang "${PLUGIN_PATH}" \
-Xclang -add-plugin -Xclang find-bad-constructs ${flags} ${1} 2>&1)" -Xclang -add-plugin -Xclang find-bad-constructs ${flags} ${1} 2>&1)"
local diffout="$(echo "${output}" | diff - "${2}")" local diffout="$(echo "${output}" | diff - "${2}")"
...@@ -44,6 +44,10 @@ do_testcase() { ...@@ -44,6 +44,10 @@ do_testcase() {
echo "FAIL: ${1}" echo "FAIL: ${1}"
echo "Output of compiler:" echo "Output of compiler:"
echo "${output}" echo "${output}"
cat > ${2}-actual << EOF
${output}
EOF
echo "Expected output:" echo "Expected output:"
cat "${2}" cat "${2}"
echo echo
......
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#define VIRTUAL virtual
#define VIRTUAL_VOID virtual void
class A {
public:
VIRTUAL void F() final {}
// Make sure an out-of-place virtual doesn't cause an incorrect fixit removal
// to be emitted.
void VIRTUAL G() final {}
// Make sure a fixit removal isn't generated for macros that expand to more
// than just 'virtual'.
VIRTUAL_VOID H() final {}
};
-Xclang -plugin-arg-find-bad-constructs -Xclang strict-virtual-specifiers
virtual_base_method_also_final.cpp:10:3: warning: [chromium-style] 'virtual' is redundant; 'final' implies 'virtual'.
VIRTUAL void F() final {}
^~~~~~~~
virtual_base_method_also_final.cpp:5:17: note: expanded from macro 'VIRTUAL'
#define VIRTUAL virtual
^
virtual_base_method_also_final.cpp:10:3: warning: [chromium-style] The virtual method does not override anything and is final; consider making it non-virtual.
VIRTUAL void F() final {}
^~~~~~~~ ~~~~~~
virtual_base_method_also_final.cpp:5:17: note: expanded from macro 'VIRTUAL'
#define VIRTUAL virtual
^
virtual_base_method_also_final.cpp:13:3: warning: [chromium-style] 'virtual' is redundant; 'final' implies 'virtual'.
void VIRTUAL G() final {}
^
virtual_base_method_also_final.cpp:13:3: warning: [chromium-style] The virtual method does not override anything and is final; consider making it non-virtual.
void VIRTUAL G() final {}
^ ~~~~~~
virtual_base_method_also_final.cpp:16:3: warning: [chromium-style] 'virtual' is redundant; 'final' implies 'virtual'.
VIRTUAL_VOID H() final {}
^
virtual_base_method_also_final.cpp:6:22: note: expanded from macro 'VIRTUAL_VOID'
#define VIRTUAL_VOID virtual void
^
virtual_base_method_also_final.cpp:16:3: warning: [chromium-style] The virtual method does not override anything and is final; consider making it non-virtual.
virtual_base_method_also_final.cpp:6:22: note: expanded from macro 'VIRTUAL_VOID'
#define VIRTUAL_VOID virtual void
^
6 warnings generated.
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "virtual_methods.h" #include "virtual_bodies.h"
// Shouldn't warn about method usage in the implementation file. // Shouldn't warn about method usage in the implementation file.
class VirtualMethodsInImplementation { class VirtualMethodsInImplementation {
...@@ -26,9 +26,12 @@ class ConcreteVirtualMethodsInImplementation ...@@ -26,9 +26,12 @@ class ConcreteVirtualMethodsInImplementation
}; };
// Fill in the implementations // Fill in the implementations
void VirtualMethodsInHeaders::MethodHasNoArguments() {} void VirtualMethodsInHeaders::MethodHasNoArguments() {
void WarnOnMissingVirtual::MethodHasNoArguments() {} }
void VirtualMethodsInImplementation::MethodHasNoArguments() {} void WarnOnMissingVirtual::MethodHasNoArguments() {
}
void VirtualMethodsInImplementation::MethodHasNoArguments() {
}
int main() { int main() {
ConcreteVirtualMethodsInHeaders one; ConcreteVirtualMethodsInHeaders one;
......
...@@ -32,6 +32,7 @@ class VirtualMethodsInHeadersTesting : public VirtualMethodsInHeaders { ...@@ -32,6 +32,7 @@ class VirtualMethodsInHeadersTesting : public VirtualMethodsInHeaders {
public: public:
// Don't complain about no virtual testing methods. // Don't complain about no virtual testing methods.
void MethodHasNoArguments(); void MethodHasNoArguments();
private: private:
testing::TestStruct tester_; testing::TestStruct tester_;
}; };
......
In file included from virtual_bodies.cpp:5:
./virtual_bodies.h:17:36: warning: [chromium-style] virtual methods with non-empty bodies shouldn't be declared inline.
virtual bool ComplainAboutThis() { return true; }
^
1 warning generated.
In file included from virtual_methods.cpp:5:
./virtual_methods.h:17:36: warning: [chromium-style] virtual methods with non-empty bodies shouldn't be declared inline.
virtual bool ComplainAboutThis() { return true; }
^
./virtual_methods.h:23:3: warning: [chromium-style] Overriding method must have "virtual" keyword.
void MethodHasNoArguments() override;
^
virtual
2 warnings generated.
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Tests for chromium style checks for virtual/override/final specifiers on
// virtual methods.
// Purposely use macros to test that the FixIt hints don't try to remove the
// macro body.
#define OVERRIDE override
#define FINAL final
// Base class can only use virtual.
class Base {
public:
virtual ~Base() {}
virtual void F() = 0;
};
// Derived classes correctly use only override or final specifier.
class CorrectOverride : public Base {
public:
~CorrectOverride() OVERRIDE {}
void F() OVERRIDE {}
};
class CorrectFinal : public CorrectOverride {
public:
~CorrectFinal() FINAL {}
void F() FINAL {}
};
// No override on an overridden method should trigger a diagnostic.
class MissingOverride : public Base {
public:
~MissingOverride() {}
void F() {}
};
// Redundant specifiers should trigger a diagnostic.
class VirtualAndOverride : public Base {
public:
virtual ~VirtualAndOverride() OVERRIDE {}
virtual void F() OVERRIDE {}
};
class VirtualAndFinal : public Base {
public:
virtual ~VirtualAndFinal() FINAL {}
virtual void F() FINAL {}
};
class VirtualAndOverrideFinal : public Base {
public:
virtual ~VirtualAndOverrideFinal() OVERRIDE FINAL {}
virtual void F() OVERRIDE FINAL {}
};
class OverrideAndFinal : public Base {
public:
~OverrideAndFinal() OVERRIDE FINAL {}
void F() OVERRIDE FINAL {}
};
-Xclang -plugin-arg-find-bad-constructs -Xclang strict-virtual-specifiers
virtual_specifiers.cpp:36:21: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
~MissingOverride() {}
^
override
virtual_specifiers.cpp:37:11: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
void F() {}
^
override
virtual_specifiers.cpp:43:3: warning: [chromium-style] 'virtual' is redundant; 'override' implies 'virtual'.
virtual ~VirtualAndOverride() OVERRIDE {}
^~~~~~~~
virtual_specifiers.cpp:44:3: warning: [chromium-style] 'virtual' is redundant; 'override' implies 'virtual'.
virtual void F() OVERRIDE {}
^~~~~~~~
virtual_specifiers.cpp:49:3: warning: [chromium-style] 'virtual' is redundant; 'final' implies 'virtual'.
virtual ~VirtualAndFinal() FINAL {}
^~~~~~~~
virtual_specifiers.cpp:50:3: warning: [chromium-style] 'virtual' is redundant; 'final' implies 'virtual'.
virtual void F() FINAL {}
^~~~~~~~
virtual_specifiers.cpp:55:3: warning: [chromium-style] 'virtual' is redundant; 'override' implies 'virtual'.
virtual ~VirtualAndOverrideFinal() OVERRIDE FINAL {}
^~~~~~~~
virtual_specifiers.cpp:55:38: warning: [chromium-style] 'override' is redundant; 'final' implies 'override'.
virtual ~VirtualAndOverrideFinal() OVERRIDE FINAL {}
^~~~~~~~~
virtual_specifiers.cpp:10:18: note: expanded from macro 'OVERRIDE'
#define OVERRIDE override
^
virtual_specifiers.cpp:56:3: warning: [chromium-style] 'virtual' is redundant; 'override' implies 'virtual'.
virtual void F() OVERRIDE FINAL {}
^~~~~~~~
virtual_specifiers.cpp:56:20: warning: [chromium-style] 'override' is redundant; 'final' implies 'override'.
virtual void F() OVERRIDE FINAL {}
^~~~~~~~~
virtual_specifiers.cpp:10:18: note: expanded from macro 'OVERRIDE'
#define OVERRIDE override
^
virtual_specifiers.cpp:61:23: warning: [chromium-style] 'override' is redundant; 'final' implies 'override'.
~OverrideAndFinal() OVERRIDE FINAL {}
^~~~~~~~~
virtual_specifiers.cpp:10:18: note: expanded from macro 'OVERRIDE'
#define OVERRIDE override
^
virtual_specifiers.cpp:62:12: warning: [chromium-style] 'override' is redundant; 'final' implies 'override'.
void F() OVERRIDE FINAL {}
^~~~~~~~~
virtual_specifiers.cpp:10:18: note: expanded from macro 'OVERRIDE'
#define OVERRIDE override
^
12 warnings generated.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment