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
12using namespace clang::ast_matchers;
13
14namespace clang::tidy::modernize {
15
17 : ClangTidyCheck(Name, Context),
18 IncludeInserter(Options.getLocalOrGlobal("IncludeStyle",
19 utils::IncludeSorter::IS_LLVM),
20 areDiagsSelfContained()) {}
21
22void UseStdBitCheck::registerMatchers(MatchFinder *Finder) {
23 const auto MakeBinaryOperatorMatcher = [](auto Op) {
24 return [=](const auto &LHS, const auto &RHS) {
25 return binaryOperator(hasOperatorName(Op),
26 hasLHS(ignoringParenImpCasts(LHS)),
27 hasRHS(ignoringParenImpCasts(RHS)));
28 };
29 };
30 const auto MakeCommutativeBinaryOperatorMatcher = [](auto Op) {
31 return [=](const auto &LHS, const auto &RHS) {
32 return binaryOperator(
33 hasOperatorName(Op),
34 hasOperands(ignoringParenImpCasts(LHS), ignoringParenImpCasts(RHS)));
35 };
36 };
37
38 const auto LogicalAnd = MakeCommutativeBinaryOperatorMatcher("&&");
39 const auto Sub = MakeBinaryOperatorMatcher("-");
40 const auto BitwiseAnd = MakeCommutativeBinaryOperatorMatcher("&");
41 const auto CmpNot = MakeCommutativeBinaryOperatorMatcher("!=");
42 const auto CmpGt = MakeBinaryOperatorMatcher(">");
43 const auto CmpGte = MakeBinaryOperatorMatcher(">=");
44 const auto CmpLt = MakeBinaryOperatorMatcher("<");
45 const auto CmpLte = MakeBinaryOperatorMatcher("<=");
46
47 const auto Literal0 = integerLiteral(equals(0));
48 const auto Literal1 = integerLiteral(equals(1));
49
50 const auto LogicalNot = [](const auto &Expr) {
51 return unaryOperator(hasOperatorName("!"),
52 hasUnaryOperand(ignoringParenImpCasts(Expr)));
53 };
54
55 const auto IsNonNull = [=](const auto &Expr) {
56 return anyOf(Expr, CmpNot(Expr, Literal0), CmpGt(Expr, Literal0),
57 CmpGte(Expr, Literal1), CmpLt(Literal0, Expr),
58 CmpLte(Literal1, Expr));
59 };
60 const auto BindDeclRef = [](StringRef Name) {
61 return declRefExpr(
62 to(varDecl(hasType(isUnsignedInteger())).bind(Name.str())));
63 };
64 const auto BoundDeclRef = [](StringRef Name) {
65 return declRefExpr(to(varDecl(equalsBoundNode(Name.str()))));
66 };
67
68 // Determining if an integer is a power of 2 with following pattern:
69 // has_one_bit(v) = v && !(v & (v - 1));
70 Finder->addMatcher(
71 LogicalAnd(IsNonNull(BindDeclRef("v")),
72 LogicalNot(BitwiseAnd(
73 BoundDeclRef("v"),
74 Sub(BoundDeclRef("v"), integerLiteral(equals(1))))))
75 .bind("has_one_bit_expr"),
76 this);
77
78 // Computing popcount with following pattern:
79 // std::bitset<N>(val).count()
80 Finder->addMatcher(
81 cxxMemberCallExpr(
82 argumentCountIs(0),
83 callee(cxxMethodDecl(
84 hasName("count"),
85 ofClass(cxxRecordDecl(hasName("bitset"), isInStdNamespace())))),
86 on(cxxConstructExpr(
87 hasArgument(0, expr(hasType(isUnsignedInteger())).bind("v")))))
88 .bind("popcount_expr"),
89 this);
90}
91
92void UseStdBitCheck::registerPPCallbacks(const SourceManager &SM,
93 Preprocessor *PP,
94 Preprocessor *ModuleExpanderPP) {
95 IncludeInserter.registerPreprocessor(PP);
96}
97
99 Options.store(Opts, "IncludeStyle", IncludeInserter.getStyle());
100}
101
102void UseStdBitCheck::check(const MatchFinder::MatchResult &Result) {
103 const ASTContext &Context = *Result.Context;
104 const SourceManager &Source = Context.getSourceManager();
105
106 if (const auto *MatchedExpr =
107 Result.Nodes.getNodeAs<BinaryOperator>("has_one_bit_expr")) {
108 const auto *MatchedVarDecl = Result.Nodes.getNodeAs<VarDecl>("v");
109
110 auto Diag =
111 diag(MatchedExpr->getBeginLoc(), "use 'std::has_one_bit' instead");
112 if (auto R = MatchedExpr->getSourceRange();
113 !R.getBegin().isMacroID() && !R.getEnd().isMacroID()) {
114 Diag << FixItHint::CreateReplacement(
115 MatchedExpr->getSourceRange(),
116 ("std::has_one_bit(" + MatchedVarDecl->getName() + ")").str())
117 << IncludeInserter.createIncludeInsertion(
118 Source.getFileID(MatchedExpr->getBeginLoc()), "<bit>");
119 }
120 } else if (const auto *MatchedExpr =
121 Result.Nodes.getNodeAs<CXXMemberCallExpr>("popcount_expr")) {
122 const auto *BitsetInstantiatedDecl =
123 cast<ClassTemplateSpecializationDecl>(MatchedExpr->getRecordDecl());
124 const llvm::APSInt BitsetSize =
125 BitsetInstantiatedDecl->getTemplateArgs()[0].getAsIntegral();
126 const auto *MatchedArg = Result.Nodes.getNodeAs<Expr>("v");
127 const uint64_t MatchedVarSize = Context.getTypeSize(MatchedArg->getType());
128 if (BitsetSize < MatchedVarSize)
129 return;
130 auto Diag = diag(MatchedExpr->getBeginLoc(), "use 'std::popcount' instead");
131 if (auto R = MatchedExpr->getSourceRange();
132 !R.getBegin().isMacroID() && !R.getEnd().isMacroID()) {
133 Diag << FixItHint::CreateRemoval(CharSourceRange::getTokenRange(
134 MatchedArg->getEndLoc().getLocWithOffset(1),
135 MatchedExpr->getRParenLoc().getLocWithOffset(-1)))
136 << FixItHint::CreateReplacement(
137 CharSourceRange::getTokenRange(
138 MatchedExpr->getBeginLoc(),
139 MatchedArg->getBeginLoc().getLocWithOffset(-1)),
140 "std::popcount(")
141 << IncludeInserter.createIncludeInsertion(
142 Source.getFileID(MatchedExpr->getBeginLoc()), "<bit>");
143 }
144 } else {
145 llvm_unreachable("unexpected match");
146 }
147}
148
149} // 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