clang-tools  8.0.0
UpgradeDurationConversionsCheck.cpp
Go to the documentation of this file.
1 //===--- UpgradeDurationConversionsCheck.cpp - clang-tidy -----------------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
11 #include "DurationRewriter.h"
12 #include "clang/AST/ASTContext.h"
13 #include "clang/ASTMatchers/ASTMatchFinder.h"
14 
15 using namespace clang::ast_matchers;
16 
17 namespace clang {
18 namespace tidy {
19 namespace abseil {
20 
21 void UpgradeDurationConversionsCheck::registerMatchers(MatchFinder *Finder) {
22  if (!getLangOpts().CPlusPlus)
23  return;
24 
25  // For the arithmetic calls, we match only the uses of the templated operators
26  // where the template parameter is not a built-in type. This means the
27  // instantiation makes use of an available user defined conversion to
28  // `int64_t`.
29  //
30  // The implementation of these templates will be updated to fail SFINAE for
31  // non-integral types. We match them to suggest an explicit cast.
32 
33  // Match expressions like `a *= b` and `a /= b` where `a` has type
34  // `absl::Duration` and `b` is not of a built-in type.
35  Finder->addMatcher(
36  cxxOperatorCallExpr(
37  argumentCountIs(2),
38  hasArgument(
39  0, expr(hasType(cxxRecordDecl(hasName("::absl::Duration"))))),
40  hasArgument(1, expr().bind("arg")),
41  callee(functionDecl(
42  hasParent(functionTemplateDecl()),
43  unless(hasTemplateArgument(0, refersToType(builtinType()))),
44  hasAnyName("operator*=", "operator/=")))),
45  this);
46 
47  // Match expressions like `a.operator*=(b)` and `a.operator/=(b)` where `a`
48  // has type `absl::Duration` and `b` is not of a built-in type.
49  Finder->addMatcher(
50  cxxMemberCallExpr(
51  callee(cxxMethodDecl(
52  ofClass(cxxRecordDecl(hasName("::absl::Duration"))),
53  hasParent(functionTemplateDecl()),
54  unless(hasTemplateArgument(0, refersToType(builtinType()))),
55  hasAnyName("operator*=", "operator/="))),
56  argumentCountIs(1), hasArgument(0, expr().bind("arg"))),
57  this);
58 
59  // Match expressions like `a * b`, `a / b`, `operator*(a, b)`, and
60  // `operator/(a, b)` where `a` has type `absl::Duration` and `b` is not of a
61  // built-in type.
62  Finder->addMatcher(
63  callExpr(callee(functionDecl(
64  hasParent(functionTemplateDecl()),
65  unless(hasTemplateArgument(0, refersToType(builtinType()))),
66  hasAnyName("::absl::operator*", "::absl::operator/"))),
67  argumentCountIs(2),
68  hasArgument(0, expr(hasType(
69  cxxRecordDecl(hasName("::absl::Duration"))))),
70  hasArgument(1, expr().bind("arg"))),
71  this);
72 
73  // Match expressions like `a * b` and `operator*(a, b)` where `a` is not of a
74  // built-in type and `b` has type `absl::Duration`.
75  Finder->addMatcher(
76  callExpr(callee(functionDecl(
77  hasParent(functionTemplateDecl()),
78  unless(hasTemplateArgument(0, refersToType(builtinType()))),
79  hasName("::absl::operator*"))),
80  argumentCountIs(2), hasArgument(0, expr().bind("arg")),
81  hasArgument(1, expr(hasType(cxxRecordDecl(
82  hasName("::absl::Duration")))))),
83  this);
84 
85  // For the factory functions, we match only the non-templated overloads that
86  // take an `int64_t` parameter. Within these calls, we care about implicit
87  // casts through a user defined conversion to `int64_t`.
88  //
89  // The factory functions will be updated to be templated and SFINAE on whether
90  // the template parameter is an integral type. This complements the already
91  // existing templated overloads that only accept floating point types.
92 
93  // Match calls like:
94  // `absl::Nanoseconds(x)`
95  // `absl::Microseconds(x)`
96  // `absl::Milliseconds(x)`
97  // `absl::Seconds(x)`
98  // `absl::Minutes(x)`
99  // `absl::Hours(x)`
100  // where `x` is not of a built-in type.
101  Finder->addMatcher(
102  implicitCastExpr(
103  anyOf(hasCastKind(CK_UserDefinedConversion),
104  has(implicitCastExpr(hasCastKind(CK_UserDefinedConversion)))),
105  hasParent(callExpr(
106  callee(functionDecl(DurationFactoryFunction(),
107  unless(hasParent(functionTemplateDecl())))),
108  hasArgument(0, expr().bind("arg"))))),
109  this);
110 }
111 
112 void UpgradeDurationConversionsCheck::check(
113  const MatchFinder::MatchResult &Result) {
114  const llvm::StringRef Message =
115  "implicit conversion to 'int64_t' is deprecated in this context; use an "
116  "explicit cast instead";
117 
118  const auto *ArgExpr = Result.Nodes.getNodeAs<Expr>("arg");
119  SourceLocation Loc = ArgExpr->getBeginLoc();
120 
121  if (!match(isInTemplateInstantiation(), *ArgExpr, *Result.Context).empty()) {
122  if (MatchedTemplateLocations.count(Loc.getRawEncoding()) == 0) {
123  // For each location matched in a template instantiation, we check if the
124  // location can also be found in `MatchedTemplateLocations`. If it is not
125  // found, that means the expression did not create a match without the
126  // instantiation and depends on template parameters. A manual fix is
127  // probably required so we provide only a warning.
128  diag(Loc, Message);
129  }
130  return;
131  }
132 
133  // We gather source locations from template matches not in template
134  // instantiations for future matches.
135  internal::Matcher<Stmt> IsInsideTemplate =
136  hasAncestor(decl(anyOf(classTemplateDecl(), functionTemplateDecl())));
137  if (!match(IsInsideTemplate, *ArgExpr, *Result.Context).empty())
138  MatchedTemplateLocations.insert(Loc.getRawEncoding());
139 
140  DiagnosticBuilder Diag = diag(Loc, Message);
141  CharSourceRange SourceRange = Lexer::makeFileCharRange(
142  CharSourceRange::getTokenRange(ArgExpr->getSourceRange()),
143  *Result.SourceManager, Result.Context->getLangOpts());
144  if (SourceRange.isInvalid())
145  // An invalid source range likely means we are inside a macro body. A manual
146  // fix is likely needed so we do not create a fix-it hint.
147  return;
148 
149  Diag << FixItHint::CreateInsertion(SourceRange.getBegin(),
150  "static_cast<int64_t>(")
151  << FixItHint::CreateInsertion(SourceRange.getEnd(), ")");
152 }
153 
154 } // namespace abseil
155 } // namespace tidy
156 } // namespace clang
SourceLocation Loc
&#39;#&#39; location in the include directive
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//