clang-tools 23.0.0git
UseStdBitCheck.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
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 "UseStdBitCheck.h"
10#include "clang/ASTMatchers/ASTMatchFinder.h"
11#include "llvm/Support/FormatVariadic.h"
12
13using namespace clang::ast_matchers;
14
15namespace clang::tidy::modernize {
16
18 : ClangTidyCheck(Name, Context),
19 IncludeInserter(Options.getLocalOrGlobal("IncludeStyle",
20 utils::IncludeSorter::IS_LLVM),
21 areDiagsSelfContained()),
22 HonorIntPromotion(Options.get("HonorIntPromotion", false)) {}
23
24void UseStdBitCheck::registerMatchers(MatchFinder *Finder) {
25 const auto MakeBinaryOperatorMatcher = [](auto Op) {
26 return [=](const auto &LHS, const auto &RHS) {
27 return binaryOperator(hasOperatorName(Op),
28 hasLHS(ignoringParenImpCasts(LHS)),
29 hasRHS(ignoringParenImpCasts(RHS)));
30 };
31 };
32 const auto MakeCommutativeBinaryOperatorMatcher = [](auto Op) {
33 return [=](const auto &LHS, const auto &RHS) {
34 return binaryOperator(
35 hasOperatorName(Op),
36 hasOperands(ignoringParenImpCasts(LHS), ignoringParenImpCasts(RHS)));
37 };
38 };
39
40 const auto LogicalAnd = MakeCommutativeBinaryOperatorMatcher("&&");
41 const auto Sub = MakeBinaryOperatorMatcher("-");
42 const auto ShiftLeft = MakeBinaryOperatorMatcher("<<");
43 const auto ShiftRight = MakeBinaryOperatorMatcher(">>");
44 const auto BitwiseAnd = MakeCommutativeBinaryOperatorMatcher("&");
45 const auto BitwiseOr = MakeCommutativeBinaryOperatorMatcher("|");
46 const auto CmpNot = MakeCommutativeBinaryOperatorMatcher("!=");
47 const auto CmpGt = MakeBinaryOperatorMatcher(">");
48 const auto CmpGte = MakeBinaryOperatorMatcher(">=");
49 const auto CmpLt = MakeBinaryOperatorMatcher("<");
50 const auto CmpLte = MakeBinaryOperatorMatcher("<=");
51
52 const auto Literal0 = integerLiteral(equals(0));
53 const auto Literal1 = integerLiteral(equals(1));
54
55 const auto LogicalNot = [](const auto &Expr) {
56 return unaryOperator(hasOperatorName("!"),
57 hasUnaryOperand(ignoringParenImpCasts(Expr)));
58 };
59
60 const auto IsNonNull = [=](const auto &Expr) {
61 return anyOf(Expr, CmpNot(Expr, Literal0), CmpGt(Expr, Literal0),
62 CmpGte(Expr, Literal1), CmpLt(Literal0, Expr),
63 CmpLte(Literal1, Expr));
64 };
65 const auto BindDeclRef = [](StringRef Name) {
66 return declRefExpr(
67 to(varDecl(hasType(isUnsignedInteger())).bind(Name.str())));
68 };
69 const auto BoundDeclRef = [](StringRef Name) {
70 return declRefExpr(to(varDecl(equalsBoundNode(Name.str()))));
71 };
72
73 // Determining if an integer is a power of 2 with following pattern:
74 // has_one_bit(v) = v && !(v & (v - 1));
75 Finder->addMatcher(
76 LogicalAnd(IsNonNull(BindDeclRef("v")),
77 LogicalNot(BitwiseAnd(
78 BoundDeclRef("v"),
79 Sub(BoundDeclRef("v"), integerLiteral(equals(1))))))
80 .bind("has_one_bit_expr"),
81 this);
82
83 // Computing popcount with following pattern:
84 // std::bitset<N>(val).count()
85 Finder->addMatcher(
86 cxxMemberCallExpr(
87 argumentCountIs(0),
88 callee(cxxMethodDecl(
89 hasName("count"),
90 ofClass(cxxRecordDecl(hasName("bitset"), isInStdNamespace())))),
91 on(cxxConstructExpr(
92 hasArgument(0, expr(hasType(isUnsignedInteger())).bind("v")))))
93 .bind("popcount_expr"),
94 this);
95
96 // Rotating an integer by a fixed amount
97 Finder->addMatcher(
98 expr(BitwiseOr(ShiftLeft(BindDeclRef("v"),
99 integerLiteral().bind("shift_left_amount")),
100 ShiftRight(BoundDeclRef("v"),
101 integerLiteral().bind("shift_right_amount"))),
102 optionally(hasParent(castExpr(hasType(isInteger())).bind("cast"))))
103 .bind("rotate_expr"),
104 this);
105}
106
107void UseStdBitCheck::registerPPCallbacks(const SourceManager &SM,
108 Preprocessor *PP,
109 Preprocessor *ModuleExpanderPP) {
110 IncludeInserter.registerPreprocessor(PP);
111}
112
114 Options.store(Opts, "IncludeStyle", IncludeInserter.getStyle());
115 Options.store(Opts, "HonorIntPromotion", HonorIntPromotion);
116}
117
118void UseStdBitCheck::check(const MatchFinder::MatchResult &Result) {
119 ASTContext &Context = *Result.Context;
120 const SourceManager &Source = Context.getSourceManager();
121
122 if (const auto *MatchedExpr =
123 Result.Nodes.getNodeAs<BinaryOperator>("has_one_bit_expr")) {
124 const auto *MatchedVarDecl = Result.Nodes.getNodeAs<VarDecl>("v");
125
126 auto Diag =
127 diag(MatchedExpr->getBeginLoc(), "use 'std::has_one_bit' instead");
128 if (auto R = MatchedExpr->getSourceRange();
129 !R.getBegin().isMacroID() && !R.getEnd().isMacroID()) {
130 Diag << FixItHint::CreateReplacement(
131 MatchedExpr->getSourceRange(),
132 ("std::has_one_bit(" + MatchedVarDecl->getName() + ")").str())
133 << IncludeInserter.createIncludeInsertion(
134 Source.getFileID(MatchedExpr->getBeginLoc()), "<bit>");
135 }
136 } else if (const auto *MatchedExpr =
137 Result.Nodes.getNodeAs<CXXMemberCallExpr>("popcount_expr")) {
138 const auto *BitsetInstantiatedDecl =
139 cast<ClassTemplateSpecializationDecl>(MatchedExpr->getRecordDecl());
140 const llvm::APSInt BitsetSize =
141 BitsetInstantiatedDecl->getTemplateArgs()[0].getAsIntegral();
142 const auto *MatchedArg = Result.Nodes.getNodeAs<Expr>("v");
143 const uint64_t MatchedVarSize = Context.getTypeSize(MatchedArg->getType());
144 if (BitsetSize < MatchedVarSize)
145 return;
146 auto Diag = diag(MatchedExpr->getBeginLoc(), "use 'std::popcount' instead");
147 if (auto R = MatchedExpr->getSourceRange();
148 !R.getBegin().isMacroID() && !R.getEnd().isMacroID()) {
149 Diag << FixItHint::CreateRemoval(CharSourceRange::getTokenRange(
150 MatchedArg->getEndLoc().getLocWithOffset(1),
151 MatchedExpr->getRParenLoc().getLocWithOffset(-1)))
152 << FixItHint::CreateReplacement(
153 CharSourceRange::getTokenRange(
154 MatchedExpr->getBeginLoc(),
155 MatchedArg->getBeginLoc().getLocWithOffset(-1)),
156 "std::popcount(")
157 << IncludeInserter.createIncludeInsertion(
158 Source.getFileID(MatchedExpr->getBeginLoc()), "<bit>");
159 }
160 } else if (const auto *MatchedExpr =
161 Result.Nodes.getNodeAs<Expr>("rotate_expr")) {
162 // Detect if the expression is an explicit cast. If that's the case we don't
163 // need to insert a cast.
164
165 bool HasExplicitIntegerCast = false;
166 if (const Expr *CE = Result.Nodes.getNodeAs<CastExpr>("cast"))
167 HasExplicitIntegerCast = !isa<ImplicitCastExpr>(CE);
168
169 const auto *MatchedVarDecl = Result.Nodes.getNodeAs<VarDecl>("v");
170 const llvm::APInt ShiftLeftAmount =
171 Result.Nodes.getNodeAs<IntegerLiteral>("shift_left_amount")->getValue();
172 const llvm::APInt ShiftRightAmount =
173 Result.Nodes.getNodeAs<IntegerLiteral>("shift_right_amount")
174 ->getValue();
175 const uint64_t MatchedVarSize =
176 Context.getTypeSize(MatchedVarDecl->getType());
177
178 // Overflowing shifts
179 if (ShiftLeftAmount.sge(MatchedVarSize))
180 return;
181 if (ShiftRightAmount.sge(MatchedVarSize))
182 return;
183 // Not a rotation.
184 if (MatchedVarSize != (ShiftLeftAmount + ShiftRightAmount))
185 return;
186
187 // Only insert cast if the operand is not subject to cast and
188 // some implicit promotion happened.
189 const bool NeedsIntCast =
190 HonorIntPromotion && !HasExplicitIntegerCast &&
191 Context.getTypeSize(MatchedExpr->getType()) > MatchedVarSize;
192 const bool IsRotl = ShiftRightAmount.sge(ShiftLeftAmount);
193
194 const StringRef ReplacementFuncName = IsRotl ? "rotl" : "rotr";
195 const uint64_t ReplacementShiftAmount =
196 (IsRotl ? ShiftLeftAmount : ShiftRightAmount).getZExtValue();
197 auto Diag = diag(MatchedExpr->getBeginLoc(), "use 'std::%0' instead")
198 << ReplacementFuncName;
199 if (auto R = MatchedExpr->getSourceRange();
200 R.getBegin().isMacroID() || R.getEnd().isMacroID())
201 return;
202
203 Diag << FixItHint::CreateReplacement(
204 MatchedExpr->getSourceRange(),
205 llvm::formatv("{3}std::{0}({1}, {2}){4}", ReplacementFuncName,
206 MatchedVarDecl->getName(), ReplacementShiftAmount,
207 NeedsIntCast ? "static_cast<int>(" : "",
208 NeedsIntCast ? ")" : "")
209 .str())
210 << IncludeInserter.createIncludeInsertion(
211 Source.getFileID(MatchedExpr->getBeginLoc()), "<bit>");
212
213 } else {
214 llvm_unreachable("unexpected match");
215 }
216}
217
218} // namespace clang::tidy::modernize
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
UseStdBitCheck(StringRef Name, ClangTidyContext *Context)
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
void registerMatchers(ast_matchers::MatchFinder *Finder) override
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) override
llvm::StringMap< ClangTidyValue > OptionMap