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)));
30 const auto MakeCommutativeBinaryOperatorMatcher = [](
auto Op) {
31 return [=](
const auto &LHS,
const auto &RHS) {
32 return binaryOperator(
34 hasOperands(ignoringParenImpCasts(LHS), ignoringParenImpCasts(RHS)));
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(
"<=");
47 const auto Literal0 = integerLiteral(equals(0));
48 const auto Literal1 = integerLiteral(equals(1));
50 const auto LogicalNot = [](
const auto &Expr) {
51 return unaryOperator(hasOperatorName(
"!"),
52 hasUnaryOperand(ignoringParenImpCasts(Expr)));
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));
60 const auto BindDeclRef = [](StringRef Name) {
62 to(varDecl(hasType(isUnsignedInteger())).bind(Name.str())));
64 const auto BoundDeclRef = [](StringRef Name) {
65 return declRefExpr(to(varDecl(equalsBoundNode(Name.str()))));
71 LogicalAnd(IsNonNull(BindDeclRef(
"v")),
72 LogicalNot(BitwiseAnd(
74 Sub(BoundDeclRef(
"v"), integerLiteral(equals(1))))))
75 .bind(
"has_one_bit_expr"),
85 ofClass(cxxRecordDecl(hasName(
"bitset"), isInStdNamespace())))),
87 hasArgument(0, expr(hasType(isUnsignedInteger())).bind(
"v")))))
88 .bind(
"popcount_expr"),
103 const ASTContext &Context = *Result.Context;
104 const SourceManager &Source = Context.getSourceManager();
106 if (
const auto *MatchedExpr =
107 Result.Nodes.getNodeAs<BinaryOperator>(
"has_one_bit_expr")) {
108 const auto *MatchedVarDecl = Result.Nodes.getNodeAs<VarDecl>(
"v");
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>");
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)
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)),
141 << IncludeInserter.createIncludeInsertion(
142 Source.getFileID(MatchedExpr->getBeginLoc()),
"<bit>");
145 llvm_unreachable(
"unexpected match");
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.