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
10 #include "UpgradeDurationConversionsCheck.h"
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
DurationRewriter.h
Loc
SourceLocation Loc
&#39;#&#39; location in the include directive
Definition: IncludeOrderCheck.cpp:37
clang::ast_matchers
Definition: AbseilMatcher.h:15
clang::tidy::modernize::Message
static const StringRef Message
Definition: UseTransparentFunctorsCheck.cpp:71
UpgradeDurationConversionsCheck.h
Result
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
Definition: ClangdServer.cpp:60
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ChangeNamespace.cpp:18