diff options
author | Alexander Kornienko <alexfh@google.com> | 2014-07-14 07:37:05 +0000 |
---|---|---|
committer | Alexander Kornienko <alexfh@google.com> | 2014-07-14 07:37:05 +0000 |
commit | 7366616a9370430a55fd96621180b423c2252193 (patch) | |
tree | 8ee72f953f9186600c1129ba09027c15d7c3d69e /clang-tools-extra/clang-tidy/google/AvoidCStyleCastsCheck.cpp | |
parent | 24189d4c863e72772f08cdedac77f4a7816ee2f3 (diff) | |
download | bcm5719-llvm-7366616a9370430a55fd96621180b423c2252193.tar.gz bcm5719-llvm-7366616a9370430a55fd96621180b423c2252193.zip |
Suggest automated replacements of C-style casts with C++ casts.
Summary:
This patch implements a subset of possible replacements of C-style
casts with const_cast/static_cast/reinterpret_cast. This should cover a large
portion of cases in real code. Handling of a few more cases may be implemented
eventually.
Reviewers: sbenza, djasper
Reviewed By: djasper
Subscribers: cfe-commits
Differential Revision: http://reviews.llvm.org/D4478
llvm-svn: 212924
Diffstat (limited to 'clang-tools-extra/clang-tidy/google/AvoidCStyleCastsCheck.cpp')
-rw-r--r-- | clang-tools-extra/clang-tidy/google/AvoidCStyleCastsCheck.cpp | 102 |
1 files changed, 92 insertions, 10 deletions
diff --git a/clang-tools-extra/clang-tidy/google/AvoidCStyleCastsCheck.cpp b/clang-tools-extra/clang-tidy/google/AvoidCStyleCastsCheck.cpp index 59914e02fb9..7f8a965a11f 100644 --- a/clang-tools-extra/clang-tidy/google/AvoidCStyleCastsCheck.cpp +++ b/clang-tools-extra/clang-tidy/google/AvoidCStyleCastsCheck.cpp @@ -8,8 +8,10 @@ //===----------------------------------------------------------------------===// #include "AvoidCStyleCastsCheck.h" +#include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/Lex/Lexer.h" using namespace clang::ast_matchers; @@ -24,27 +26,107 @@ AvoidCStyleCastsCheck::registerMatchers(ast_matchers::MatchFinder *Finder) { // Filter out (EnumType)IntegerLiteral construct, which is generated // for non-type template arguments of enum types. // FIXME: Remove this once this is fixed in the AST. - unless(hasParent(substNonTypeTemplateParmExpr()))).bind("cast"), + unless(hasParent(substNonTypeTemplateParmExpr())), + // Avoid matches in template instantiations. + unless(hasAncestor(decl( + anyOf(recordDecl(ast_matchers::isTemplateInstantiation()), + functionDecl(ast_matchers::isTemplateInstantiation())))))) + .bind("cast"), this); } +bool needsConstCast(QualType SourceType, QualType DestType) { + SourceType = SourceType.getNonReferenceType(); + DestType = DestType.getNonReferenceType(); + while (SourceType->isPointerType() && DestType->isPointerType()) { + SourceType = SourceType->getPointeeType(); + DestType = DestType->getPointeeType(); + if (SourceType.isConstQualified() && !DestType.isConstQualified()) + return true; + } + return false; +} + +bool pointedTypesAreEqual(QualType SourceType, QualType DestType) { + SourceType = SourceType.getNonReferenceType(); + DestType = DestType.getNonReferenceType(); + while (SourceType->isPointerType() && DestType->isPointerType()) { + SourceType = SourceType->getPointeeType(); + DestType = DestType->getPointeeType(); + } + return SourceType.getUnqualifiedType() == DestType.getUnqualifiedType(); +} void AvoidCStyleCastsCheck::check(const MatchFinder::MatchResult &Result) { const auto *CastExpr = Result.Nodes.getNodeAs<CStyleCastExpr>("cast"); - // Ignore casts in macros for now. - if (CastExpr->getLocStart().isMacroID()) - return; - // Casting to void is an idiomatic way to mute "unused variable" and similar // warnings. if (CastExpr->getTypeAsWritten()->isVoidType()) return; - diag(CastExpr->getLocStart(), "C-style casts are discouraged. Use " - "static_cast/const_cast/reinterpret_cast " - "instead."); - // FIXME: Suggest appropriate C++ cast. See [expr.cast] for cast notation - // semantics. + QualType SourceType = + CastExpr->getSubExprAsWritten()->getType().getCanonicalType(); + QualType DestType = CastExpr->getTypeAsWritten().getCanonicalType(); + + auto ParenRange = CharSourceRange::getTokenRange(CastExpr->getLParenLoc(), + CastExpr->getRParenLoc()); + if (SourceType == DestType) { + diag(CastExpr->getLocStart(), "Redundant cast to the same type.") + << FixItHint::CreateRemoval(ParenRange); + return; + } + + std::string DestTypeString = CastExpr->getTypeAsWritten().getAsString(); + + auto diag_builder = + diag(CastExpr->getLocStart(), "C-style casts are discouraged. %0"); + + auto ReplaceWithCast = [&](StringRef CastType) { + diag_builder << ("Use " + CastType + ".").str(); + if (ParenRange.getBegin().isMacroID() || ParenRange.getEnd().isMacroID()) + return; + diag_builder << FixItHint::CreateReplacement( + ParenRange, + (CastType + "<" + DestTypeString + ">(").str()) + << FixItHint::CreateInsertion( + Lexer::getLocForEndOfToken( + CastExpr->getSubExprAsWritten()->getLocEnd(), 0, + *Result.SourceManager, + Result.Context->getLangOpts()), + ")"); + }; + // Suggest appropriate C++ cast. See [expr.cast] for cast notation semantics. + switch (CastExpr->getCastKind()) { + case CK_NoOp: + if (needsConstCast(SourceType, DestType) && + pointedTypesAreEqual(SourceType, DestType)) { + ReplaceWithCast("const_cast"); + return; + } + if (DestType->isReferenceType() && + (SourceType.getNonReferenceType() == + DestType.getNonReferenceType().withConst() || + SourceType.getNonReferenceType() == DestType.getNonReferenceType())) { + ReplaceWithCast("const_cast"); + return; + } + if (SourceType->isBuiltinType() && DestType->isBuiltinType()) { + ReplaceWithCast("static_cast"); + return; + } + break; + case CK_BitCast: + // FIXME: Suggest const_cast<...>(reinterpret_cast<...>(...)) replacement. + if (!needsConstCast(SourceType, DestType)) { + ReplaceWithCast("reinterpret_cast"); + return; + } + break; + default: + break; + } + + diag_builder << "Use static_cast/const_cast/reinterpret_cast."; } } // namespace readability |