Index: tools/clang/plugins/FindBadConstructsConsumer.cpp |
diff --git a/tools/clang/plugins/FindBadConstructsConsumer.cpp b/tools/clang/plugins/FindBadConstructsConsumer.cpp |
index 770e2551d6e7c92f12032f850251ba5b8af738d1..20919a41afd7afb98a90211160b36d23cccacac7 100644 |
--- a/tools/clang/plugins/FindBadConstructsConsumer.cpp |
+++ b/tools/clang/plugins/FindBadConstructsConsumer.cpp |
@@ -70,14 +70,15 @@ bool IsGtestTestFixture(const CXXRecordDecl* decl) { |
return decl->getQualifiedNameAsString() == "testing::Test"; |
} |
+// Generates a fixit hint to remove the 'virtual' keyword. |
+// Unfortunately, there doesn't seem to be a good way to determine the source |
+// 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 ;-) |
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())); |
@@ -413,32 +414,68 @@ void FindBadConstructsConsumer::CheckVirtualSpecifiers( |
// Complain if a method is annotated virtual && (override || final). |
if (has_virtual && (override_attr || final_attr)) { |
- diagnostic().Report(method->getLocStart(), |
- diag_redundant_virtual_specifier_) |
- << "'virtual'" |
- << (override_attr ? static_cast<Attr*>(override_attr) : final_attr) |
- << FixItRemovalForVirtual(manager, method); |
+ // ... but only if virtual does not originate in a macro from a banned file. |
+ // Note this is just an educated guess: the assumption here is that any |
+ // macro for declaring methods will probably be at the start of the method's |
+ // source range. |
+ if (!InBannedDirectory(manager.getSpellingLoc(method->getLocStart()))) { |
+ 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"); |
+ SourceRange range = method->getSourceRange(); |
+ SourceLocation loc; |
+ if (method->hasInlineBody()) { |
+ loc = method->getBody()->getSourceRange().getBegin(); |
+ } else { |
+ // TODO(dcheng): We should probably use ASTContext's LangOptions here. |
+ LangOptions lang_options; |
+ loc = Lexer::getLocForEndOfToken( |
+ manager.getSpellingLoc(range.getEnd()), 0, |
+ manager, lang_options); |
+ // The original code used the ending source loc of TypeSourceInfo's |
+ // TypeLoc. Unfortunately, this breaks down in the presence of attributes. |
+ // Attributes often appear at the end of a TypeLoc, e.g. |
+ // virtual ULONG __stdcall AddRef() |
+ // has a TypeSourceInfo that looks something like: |
+ // ULONG AddRef() __attribute(stdcall) |
+ // so a fix-it insertion would be generated to insert 'override' after |
+ // __stdcall in the code as written. |
+ // While using the spelling loc of the CXXMethodDecl fixes attribute |
+ // handling, it breaks handling of "= 0" and similar constructs.. To work |
+ // around this, scan backwards in the source text for a '=' or ')' token |
+ // and adjust the location as needed... |
+ for (SourceLocation l = loc.getLocWithOffset(-1); |
+ l != manager.getLocForStartOfFile(manager.getFileID(loc)); |
+ l = l.getLocWithOffset(-1)) { |
+ l = Lexer::GetBeginningOfToken(l, manager, lang_options); |
+ Token token; |
+ // getRawToken() returns *true* on failure. In that case, just give up |
+ // and don't bother generating a possibly incorrect fix-it. |
+ if (Lexer::getRawToken(l, token, manager, lang_options, true)) { |
+ loc = SourceLocation(); |
+ break; |
+ } |
+ if (token.is(tok::r_paren)) { |
+ break; |
+ } else if (token.is(tok::equal)) { |
+ loc = l; |
+ break; |
+ } |
+ } |
+ } |
+ if (loc.isValid()) { |
+ diagnostic().Report(loc, diag_method_requires_override_) |
+ << FixItHint::CreateInsertion(loc, " override"); |
} else { |
- diagnostic().Report(loc, diag_method_requires_override_); |
+ diagnostic().Report(range.getBegin(), diag_method_requires_override_); |
} |
} |
@@ -559,8 +596,14 @@ FindBadConstructsConsumer::CheckRecordForRefcountIssue( |
// Adds either a warning or error, based on the current handling of |
// -Werror. |
DiagnosticsEngine::Level FindBadConstructsConsumer::getErrorLevel() { |
+#if defined(LLVM_ON_WIN32) |
+ // TODO(dcheng): Re-enable -Werror for these diagnostics on Windows once all |
+ // the pre-existing warnings are cleaned up. https://crbug.com/467287 |
+ return DiagnosticsEngine::Warning; |
+#else |
return diagnostic().getWarningsAsErrors() ? DiagnosticsEngine::Error |
: DiagnosticsEngine::Warning; |
+#endif |
} |
// Returns true if |base| specifies one of the Chromium reference counted |