diff options
| author | Eric Fiselier <eric@efcs.ca> | 2018-12-07 20:03:03 +0000 |
|---|---|---|
| committer | Eric Fiselier <eric@efcs.ca> | 2018-12-07 20:03:03 +0000 |
| commit | 2bbbd8be24731d485b56a5e8495b2d873eb63d4b (patch) | |
| tree | 3de27468ecd601e4341b0a4de5d74be5acf0d46b /clang-tools-extra/clang-tidy | |
| parent | f7254a698b79a326dffcb8a562205139f2b33d2e (diff) | |
| download | bcm5719-llvm-2bbbd8be24731d485b56a5e8495b2d873eb63d4b.tar.gz bcm5719-llvm-2bbbd8be24731d485b56a5e8495b2d873eb63d4b.zip | |
[clang-tidy]: Abseil: new check 'abseil-upgrade-duration-conversions'
Patch by Alex Strelnikov.
Reviewed as D53830
Introduce a new check to upgrade user code based on upcoming API breaking changes to absl::Duration.
The check finds calls to arithmetic operators and factory functions for absl::Duration that rely on
an implicit user defined conversion to int64_t. These cases will no longer compile after proposed
changes are released. Suggested fixes explicitly cast the argument int64_t.
llvm-svn: 348633
Diffstat (limited to 'clang-tools-extra/clang-tidy')
4 files changed, 202 insertions, 0 deletions
diff --git a/clang-tools-extra/clang-tidy/abseil/AbseilTidyModule.cpp b/clang-tools-extra/clang-tidy/abseil/AbseilTidyModule.cpp index a559ac5f8fe..23fc2cf2770 100644 --- a/clang-tools-extra/clang-tidy/abseil/AbseilTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/abseil/AbseilTidyModule.cpp @@ -20,6 +20,7 @@ #include "RedundantStrcatCallsCheck.h" #include "StringFindStartswithCheck.h" #include "StrCatAppendCheck.h" +#include "UpgradeDurationConversionsCheck.h" namespace clang { namespace tidy { @@ -47,6 +48,8 @@ public: "abseil-str-cat-append"); CheckFactories.registerCheck<StringFindStartswithCheck>( "abseil-string-find-startswith"); + CheckFactories.registerCheck<UpgradeDurationConversionsCheck>( + "abseil-upgrade-duration-conversions"); } }; diff --git a/clang-tools-extra/clang-tidy/abseil/CMakeLists.txt b/clang-tools-extra/clang-tidy/abseil/CMakeLists.txt index 1139f2e1797..34640318a22 100644 --- a/clang-tools-extra/clang-tidy/abseil/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/abseil/CMakeLists.txt @@ -13,6 +13,7 @@ add_clang_library(clangTidyAbseilModule RedundantStrcatCallsCheck.cpp StrCatAppendCheck.cpp StringFindStartswithCheck.cpp + UpgradeDurationConversionsCheck.cpp LINK_LIBS clangAST diff --git a/clang-tools-extra/clang-tidy/abseil/UpgradeDurationConversionsCheck.cpp b/clang-tools-extra/clang-tidy/abseil/UpgradeDurationConversionsCheck.cpp new file mode 100644 index 00000000000..bdf32bccc37 --- /dev/null +++ b/clang-tools-extra/clang-tidy/abseil/UpgradeDurationConversionsCheck.cpp @@ -0,0 +1,158 @@ +//===--- UpgradeDurationConversionsCheck.cpp - clang-tidy -----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "UpgradeDurationConversionsCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace abseil { + +void UpgradeDurationConversionsCheck::registerMatchers(MatchFinder *Finder) { + if (!getLangOpts().CPlusPlus) + return; + + // For the arithmetic calls, we match only the uses of the templated operators + // where the template parameter is not a built-in type. This means the + // instantiation makes use of an available user defined conversion to + // `int64_t`. + // + // The implementation of these templates will be updated to fail SFINAE for + // non-integral types. We match them to suggest an explicit cast. + + // Match expressions like `a *= b` and `a /= b` where `a` has type + // `absl::Duration` and `b` is not of a built-in type. + Finder->addMatcher( + cxxOperatorCallExpr( + argumentCountIs(2), + hasArgument( + 0, expr(hasType(cxxRecordDecl(hasName("::absl::Duration"))))), + hasArgument(1, expr().bind("arg")), + callee(functionDecl( + hasParent(functionTemplateDecl()), + unless(hasTemplateArgument(0, refersToType(builtinType()))), + hasAnyName("operator*=", "operator/=")))), + this); + + // Match expressions like `a.operator*=(b)` and `a.operator/=(b)` where `a` + // has type `absl::Duration` and `b` is not of a built-in type. + Finder->addMatcher( + cxxMemberCallExpr( + callee(cxxMethodDecl( + ofClass(cxxRecordDecl(hasName("::absl::Duration"))), + hasParent(functionTemplateDecl()), + unless(hasTemplateArgument(0, refersToType(builtinType()))), + hasAnyName("operator*=", "operator/="))), + argumentCountIs(1), hasArgument(0, expr().bind("arg"))), + this); + + // Match expressions like `a * b`, `a / b`, `operator*(a, b)`, and + // `operator/(a, b)` where `a` has type `absl::Duration` and `b` is not of a + // built-in type. + Finder->addMatcher( + callExpr(callee(functionDecl( + hasParent(functionTemplateDecl()), + unless(hasTemplateArgument(0, refersToType(builtinType()))), + hasAnyName("::absl::operator*", "::absl::operator/"))), + argumentCountIs(2), + hasArgument(0, expr(hasType( + cxxRecordDecl(hasName("::absl::Duration"))))), + hasArgument(1, expr().bind("arg"))), + this); + + // Match expressions like `a * b` and `operator*(a, b)` where `a` is not of a + // built-in type and `b` has type `absl::Duration`. + Finder->addMatcher( + callExpr(callee(functionDecl( + hasParent(functionTemplateDecl()), + unless(hasTemplateArgument(0, refersToType(builtinType()))), + hasName("::absl::operator*"))), + argumentCountIs(2), hasArgument(0, expr().bind("arg")), + hasArgument(1, expr(hasType(cxxRecordDecl( + hasName("::absl::Duration")))))), + this); + + // For the factory functions, we match only the non-templated overloads that + // take an `int64_t` parameter. Within these calls, we care about implicit + // casts through a user defined conversion to `int64_t`. + // + // The factory functions will be updated to be templated and SFINAE on whether + // the template parameter is an integral type. This complements the already + // existing templated overloads that only accept floating point types. + + // Match calls like: + // `absl::Nanoseconds(x)` + // `absl::Microseconds(x)` + // `absl::Milliseconds(x)` + // `absl::Seconds(x)` + // `absl::Minutes(x)` + // `absl::Hours(x)` + // where `x` is not of a built-in type. + Finder->addMatcher( + implicitCastExpr( + anyOf(hasCastKind(CK_UserDefinedConversion), + has(implicitCastExpr(hasCastKind(CK_UserDefinedConversion)))), + hasParent(callExpr( + callee(functionDecl( + hasAnyName("::absl::Nanoseconds", "::absl::Microseconds", + "::absl::Milliseconds", "::absl::Seconds", + "::absl::Minutes", "::absl::Hours"), + unless(hasParent(functionTemplateDecl())))), + hasArgument(0, expr().bind("arg"))))), + this); +} + +void UpgradeDurationConversionsCheck::check( + const MatchFinder::MatchResult &Result) { + const llvm::StringRef Message = + "implicit conversion to 'int64_t' is deprecated in this context; use an " + "explicit cast instead"; + + const auto *ArgExpr = Result.Nodes.getNodeAs<Expr>("arg"); + SourceLocation Loc = ArgExpr->getBeginLoc(); + + if (!match(isInTemplateInstantiation(), *ArgExpr, *Result.Context).empty()) { + if (MatchedTemplateLocations.count(Loc.getRawEncoding()) == 0) { + // For each location matched in a template instantiation, we check if the + // location can also be found in `MatchedTemplateLocations`. If it is not + // found, that means the expression did not create a match without the + // instantiation and depends on template parameters. A manual fix is + // probably required so we provide only a warning. + diag(Loc, Message); + } + return; + } + + // We gather source locations from template matches not in template + // instantiations for future matches. + internal::Matcher<Stmt> IsInsideTemplate = + hasAncestor(decl(anyOf(classTemplateDecl(), functionTemplateDecl()))); + if (!match(IsInsideTemplate, *ArgExpr, *Result.Context).empty()) + MatchedTemplateLocations.insert(Loc.getRawEncoding()); + + DiagnosticBuilder Diag = diag(Loc, Message); + CharSourceRange SourceRange = Lexer::makeFileCharRange( + CharSourceRange::getTokenRange(ArgExpr->getSourceRange()), + *Result.SourceManager, Result.Context->getLangOpts()); + if (SourceRange.isInvalid()) + // An invalid source range likely means we are inside a macro body. A manual + // fix is likely needed so we do not create a fix-it hint. + return; + + Diag << FixItHint::CreateInsertion(SourceRange.getBegin(), + "static_cast<int64_t>(") + << FixItHint::CreateInsertion(SourceRange.getEnd(), ")"); +} + +} // namespace abseil +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/clang-tidy/abseil/UpgradeDurationConversionsCheck.h b/clang-tools-extra/clang-tidy/abseil/UpgradeDurationConversionsCheck.h new file mode 100644 index 00000000000..63712e2caef --- /dev/null +++ b/clang-tools-extra/clang-tidy/abseil/UpgradeDurationConversionsCheck.h @@ -0,0 +1,40 @@ +//===--- UpgradeDurationConversionsCheck.h - clang-tidy ---------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_UPGRADEDURATIONCONVERSIONSCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_UPGRADEDURATIONCONVERSIONSCHECK_H + +#include "../ClangTidy.h" + +#include <unordered_set> + +namespace clang { +namespace tidy { +namespace abseil { + +/// Finds deprecated uses of `absl::Duration` arithmetic operators and factories. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/abseil-upgrade-duration-conversions.html +class UpgradeDurationConversionsCheck : public ClangTidyCheck { +public: + UpgradeDurationConversionsCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + +private: + std::unordered_set<unsigned> MatchedTemplateLocations; +}; + +} // namespace abseil +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_UPGRADEDURATIONCONVERSIONSCHECK_H |

