clang-tools 10.0.0

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