IdenticalExprChecker.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520
  1. //== IdenticalExprChecker.cpp - Identical expression checker----------------==//
  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. /// \file
  10. /// This defines IdenticalExprChecker, a check that warns about
  11. /// unintended use of identical expressions.
  12. ///
  13. /// It checks for use of identical expressions with comparison operators and
  14. /// inside conditional expressions.
  15. ///
  16. //===----------------------------------------------------------------------===//
  17. #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
  18. #include "clang/AST/RecursiveASTVisitor.h"
  19. #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
  20. #include "clang/StaticAnalyzer/Core/Checker.h"
  21. #include "clang/StaticAnalyzer/Core/CheckerManager.h"
  22. #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
  23. using namespace clang;
  24. using namespace ento;
  25. static bool isIdenticalStmt(const ASTContext &Ctx, const Stmt *Stmt1,
  26. const Stmt *Stmt2, bool IgnoreSideEffects = false);
  27. //===----------------------------------------------------------------------===//
  28. // FindIdenticalExprVisitor - Identify nodes using identical expressions.
  29. //===----------------------------------------------------------------------===//
  30. namespace {
  31. class FindIdenticalExprVisitor
  32. : public RecursiveASTVisitor<FindIdenticalExprVisitor> {
  33. BugReporter &BR;
  34. const CheckerBase *Checker;
  35. AnalysisDeclContext *AC;
  36. public:
  37. explicit FindIdenticalExprVisitor(BugReporter &B,
  38. const CheckerBase *Checker,
  39. AnalysisDeclContext *A)
  40. : BR(B), Checker(Checker), AC(A) {}
  41. // FindIdenticalExprVisitor only visits nodes
  42. // that are binary operators, if statements or
  43. // conditional operators.
  44. bool VisitBinaryOperator(const BinaryOperator *B);
  45. bool VisitIfStmt(const IfStmt *I);
  46. bool VisitConditionalOperator(const ConditionalOperator *C);
  47. private:
  48. void reportIdenticalExpr(const BinaryOperator *B, bool CheckBitwise,
  49. ArrayRef<SourceRange> Sr);
  50. void checkBitwiseOrLogicalOp(const BinaryOperator *B, bool CheckBitwise);
  51. void checkComparisonOp(const BinaryOperator *B);
  52. };
  53. } // end anonymous namespace
  54. void FindIdenticalExprVisitor::reportIdenticalExpr(const BinaryOperator *B,
  55. bool CheckBitwise,
  56. ArrayRef<SourceRange> Sr) {
  57. StringRef Message;
  58. if (CheckBitwise)
  59. Message = "identical expressions on both sides of bitwise operator";
  60. else
  61. Message = "identical expressions on both sides of logical operator";
  62. PathDiagnosticLocation ELoc =
  63. PathDiagnosticLocation::createOperatorLoc(B, BR.getSourceManager());
  64. BR.EmitBasicReport(AC->getDecl(), Checker,
  65. "Use of identical expressions",
  66. categories::LogicError,
  67. Message, ELoc, Sr);
  68. }
  69. void FindIdenticalExprVisitor::checkBitwiseOrLogicalOp(const BinaryOperator *B,
  70. bool CheckBitwise) {
  71. SourceRange Sr[2];
  72. const Expr *LHS = B->getLHS();
  73. const Expr *RHS = B->getRHS();
  74. // Split operators as long as we still have operators to split on. We will
  75. // get called for every binary operator in an expression so there is no need
  76. // to check every one against each other here, just the right most one with
  77. // the others.
  78. while (const BinaryOperator *B2 = dyn_cast<BinaryOperator>(LHS)) {
  79. if (B->getOpcode() != B2->getOpcode())
  80. break;
  81. if (isIdenticalStmt(AC->getASTContext(), RHS, B2->getRHS())) {
  82. Sr[0] = RHS->getSourceRange();
  83. Sr[1] = B2->getRHS()->getSourceRange();
  84. reportIdenticalExpr(B, CheckBitwise, Sr);
  85. }
  86. LHS = B2->getLHS();
  87. }
  88. if (isIdenticalStmt(AC->getASTContext(), RHS, LHS)) {
  89. Sr[0] = RHS->getSourceRange();
  90. Sr[1] = LHS->getSourceRange();
  91. reportIdenticalExpr(B, CheckBitwise, Sr);
  92. }
  93. }
  94. bool FindIdenticalExprVisitor::VisitIfStmt(const IfStmt *I) {
  95. const Stmt *Stmt1 = I->getThen();
  96. const Stmt *Stmt2 = I->getElse();
  97. // Check for identical inner condition:
  98. //
  99. // if (x<10) {
  100. // if (x<10) {
  101. // ..
  102. if (const CompoundStmt *CS = dyn_cast<CompoundStmt>(Stmt1)) {
  103. if (!CS->body_empty()) {
  104. const IfStmt *InnerIf = dyn_cast<IfStmt>(*CS->body_begin());
  105. if (InnerIf && isIdenticalStmt(AC->getASTContext(), I->getCond(), InnerIf->getCond(), /*IgnoreSideEffects=*/ false)) {
  106. PathDiagnosticLocation ELoc(InnerIf->getCond(), BR.getSourceManager(), AC);
  107. BR.EmitBasicReport(AC->getDecl(), Checker, "Identical conditions",
  108. categories::LogicError,
  109. "conditions of the inner and outer statements are identical",
  110. ELoc);
  111. }
  112. }
  113. }
  114. // Check for identical conditions:
  115. //
  116. // if (b) {
  117. // foo1();
  118. // } else if (b) {
  119. // foo2();
  120. // }
  121. if (Stmt1 && Stmt2) {
  122. const Expr *Cond1 = I->getCond();
  123. const Stmt *Else = Stmt2;
  124. while (const IfStmt *I2 = dyn_cast_or_null<IfStmt>(Else)) {
  125. const Expr *Cond2 = I2->getCond();
  126. if (isIdenticalStmt(AC->getASTContext(), Cond1, Cond2, false)) {
  127. SourceRange Sr = Cond1->getSourceRange();
  128. PathDiagnosticLocation ELoc(Cond2, BR.getSourceManager(), AC);
  129. BR.EmitBasicReport(AC->getDecl(), Checker, "Identical conditions",
  130. categories::LogicError,
  131. "expression is identical to previous condition",
  132. ELoc, Sr);
  133. }
  134. Else = I2->getElse();
  135. }
  136. }
  137. if (!Stmt1 || !Stmt2)
  138. return true;
  139. // Special handling for code like:
  140. //
  141. // if (b) {
  142. // i = 1;
  143. // } else
  144. // i = 1;
  145. if (const CompoundStmt *CompStmt = dyn_cast<CompoundStmt>(Stmt1)) {
  146. if (CompStmt->size() == 1)
  147. Stmt1 = CompStmt->body_back();
  148. }
  149. if (const CompoundStmt *CompStmt = dyn_cast<CompoundStmt>(Stmt2)) {
  150. if (CompStmt->size() == 1)
  151. Stmt2 = CompStmt->body_back();
  152. }
  153. if (isIdenticalStmt(AC->getASTContext(), Stmt1, Stmt2, true)) {
  154. PathDiagnosticLocation ELoc =
  155. PathDiagnosticLocation::createBegin(I, BR.getSourceManager(), AC);
  156. BR.EmitBasicReport(AC->getDecl(), Checker,
  157. "Identical branches",
  158. categories::LogicError,
  159. "true and false branches are identical", ELoc);
  160. }
  161. return true;
  162. }
  163. bool FindIdenticalExprVisitor::VisitBinaryOperator(const BinaryOperator *B) {
  164. BinaryOperator::Opcode Op = B->getOpcode();
  165. if (BinaryOperator::isBitwiseOp(Op))
  166. checkBitwiseOrLogicalOp(B, true);
  167. if (BinaryOperator::isLogicalOp(Op))
  168. checkBitwiseOrLogicalOp(B, false);
  169. if (BinaryOperator::isComparisonOp(Op))
  170. checkComparisonOp(B);
  171. // We want to visit ALL nodes (subexpressions of binary comparison
  172. // expressions too) that contains comparison operators.
  173. // True is always returned to traverse ALL nodes.
  174. return true;
  175. }
  176. void FindIdenticalExprVisitor::checkComparisonOp(const BinaryOperator *B) {
  177. BinaryOperator::Opcode Op = B->getOpcode();
  178. //
  179. // Special case for floating-point representation.
  180. //
  181. // If expressions on both sides of comparison operator are of type float,
  182. // then for some comparison operators no warning shall be
  183. // reported even if the expressions are identical from a symbolic point of
  184. // view. Comparison between expressions, declared variables and literals
  185. // are treated differently.
  186. //
  187. // != and == between float literals that have the same value should NOT warn.
  188. // < > between float literals that have the same value SHOULD warn.
  189. //
  190. // != and == between the same float declaration should NOT warn.
  191. // < > between the same float declaration SHOULD warn.
  192. //
  193. // != and == between eq. expressions that evaluates into float
  194. // should NOT warn.
  195. // < > between eq. expressions that evaluates into float
  196. // should NOT warn.
  197. //
  198. const Expr *LHS = B->getLHS()->IgnoreParenImpCasts();
  199. const Expr *RHS = B->getRHS()->IgnoreParenImpCasts();
  200. const DeclRefExpr *DeclRef1 = dyn_cast<DeclRefExpr>(LHS);
  201. const DeclRefExpr *DeclRef2 = dyn_cast<DeclRefExpr>(RHS);
  202. const FloatingLiteral *FloatLit1 = dyn_cast<FloatingLiteral>(LHS);
  203. const FloatingLiteral *FloatLit2 = dyn_cast<FloatingLiteral>(RHS);
  204. if ((DeclRef1) && (DeclRef2)) {
  205. if ((DeclRef1->getType()->hasFloatingRepresentation()) &&
  206. (DeclRef2->getType()->hasFloatingRepresentation())) {
  207. if (DeclRef1->getDecl() == DeclRef2->getDecl()) {
  208. if ((Op == BO_EQ) || (Op == BO_NE)) {
  209. return;
  210. }
  211. }
  212. }
  213. } else if ((FloatLit1) && (FloatLit2)) {
  214. if (FloatLit1->getValue().bitwiseIsEqual(FloatLit2->getValue())) {
  215. if ((Op == BO_EQ) || (Op == BO_NE)) {
  216. return;
  217. }
  218. }
  219. } else if (LHS->getType()->hasFloatingRepresentation()) {
  220. // If any side of comparison operator still has floating-point
  221. // representation, then it's an expression. Don't warn.
  222. // Here only LHS is checked since RHS will be implicit casted to float.
  223. return;
  224. } else {
  225. // No special case with floating-point representation, report as usual.
  226. }
  227. if (isIdenticalStmt(AC->getASTContext(), B->getLHS(), B->getRHS())) {
  228. PathDiagnosticLocation ELoc =
  229. PathDiagnosticLocation::createOperatorLoc(B, BR.getSourceManager());
  230. StringRef Message;
  231. if (Op == BO_Cmp)
  232. Message = "comparison of identical expressions always evaluates to "
  233. "'equal'";
  234. else if (((Op == BO_EQ) || (Op == BO_LE) || (Op == BO_GE)))
  235. Message = "comparison of identical expressions always evaluates to true";
  236. else
  237. Message = "comparison of identical expressions always evaluates to false";
  238. BR.EmitBasicReport(AC->getDecl(), Checker,
  239. "Compare of identical expressions",
  240. categories::LogicError, Message, ELoc);
  241. }
  242. }
  243. bool FindIdenticalExprVisitor::VisitConditionalOperator(
  244. const ConditionalOperator *C) {
  245. // Check if expressions in conditional expression are identical
  246. // from a symbolic point of view.
  247. if (isIdenticalStmt(AC->getASTContext(), C->getTrueExpr(),
  248. C->getFalseExpr(), true)) {
  249. PathDiagnosticLocation ELoc =
  250. PathDiagnosticLocation::createConditionalColonLoc(
  251. C, BR.getSourceManager());
  252. SourceRange Sr[2];
  253. Sr[0] = C->getTrueExpr()->getSourceRange();
  254. Sr[1] = C->getFalseExpr()->getSourceRange();
  255. BR.EmitBasicReport(
  256. AC->getDecl(), Checker,
  257. "Identical expressions in conditional expression",
  258. categories::LogicError,
  259. "identical expressions on both sides of ':' in conditional expression",
  260. ELoc, Sr);
  261. }
  262. // We want to visit ALL nodes (expressions in conditional
  263. // expressions too) that contains conditional operators,
  264. // thus always return true to traverse ALL nodes.
  265. return true;
  266. }
  267. /// Determines whether two statement trees are identical regarding
  268. /// operators and symbols.
  269. ///
  270. /// Exceptions: expressions containing macros or functions with possible side
  271. /// effects are never considered identical.
  272. /// Limitations: (t + u) and (u + t) are not considered identical.
  273. /// t*(u + t) and t*u + t*t are not considered identical.
  274. ///
  275. static bool isIdenticalStmt(const ASTContext &Ctx, const Stmt *Stmt1,
  276. const Stmt *Stmt2, bool IgnoreSideEffects) {
  277. if (!Stmt1 || !Stmt2) {
  278. return !Stmt1 && !Stmt2;
  279. }
  280. // If Stmt1 & Stmt2 are of different class then they are not
  281. // identical statements.
  282. if (Stmt1->getStmtClass() != Stmt2->getStmtClass())
  283. return false;
  284. const Expr *Expr1 = dyn_cast<Expr>(Stmt1);
  285. const Expr *Expr2 = dyn_cast<Expr>(Stmt2);
  286. if (Expr1 && Expr2) {
  287. // If Stmt1 has side effects then don't warn even if expressions
  288. // are identical.
  289. if (!IgnoreSideEffects && Expr1->HasSideEffects(Ctx))
  290. return false;
  291. // If either expression comes from a macro then don't warn even if
  292. // the expressions are identical.
  293. if ((Expr1->getExprLoc().isMacroID()) || (Expr2->getExprLoc().isMacroID()))
  294. return false;
  295. // If all children of two expressions are identical, return true.
  296. Expr::const_child_iterator I1 = Expr1->child_begin();
  297. Expr::const_child_iterator I2 = Expr2->child_begin();
  298. while (I1 != Expr1->child_end() && I2 != Expr2->child_end()) {
  299. if (!*I1 || !*I2 || !isIdenticalStmt(Ctx, *I1, *I2, IgnoreSideEffects))
  300. return false;
  301. ++I1;
  302. ++I2;
  303. }
  304. // If there are different number of children in the statements, return
  305. // false.
  306. if (I1 != Expr1->child_end())
  307. return false;
  308. if (I2 != Expr2->child_end())
  309. return false;
  310. }
  311. switch (Stmt1->getStmtClass()) {
  312. default:
  313. return false;
  314. case Stmt::CallExprClass:
  315. case Stmt::ArraySubscriptExprClass:
  316. case Stmt::OMPArraySectionExprClass:
  317. case Stmt::OMPArrayShapingExprClass:
  318. case Stmt::OMPIteratorExprClass:
  319. case Stmt::ImplicitCastExprClass:
  320. case Stmt::ParenExprClass:
  321. case Stmt::BreakStmtClass:
  322. case Stmt::ContinueStmtClass:
  323. case Stmt::NullStmtClass:
  324. return true;
  325. case Stmt::CStyleCastExprClass: {
  326. const CStyleCastExpr* CastExpr1 = cast<CStyleCastExpr>(Stmt1);
  327. const CStyleCastExpr* CastExpr2 = cast<CStyleCastExpr>(Stmt2);
  328. return CastExpr1->getTypeAsWritten() == CastExpr2->getTypeAsWritten();
  329. }
  330. case Stmt::ReturnStmtClass: {
  331. const ReturnStmt *ReturnStmt1 = cast<ReturnStmt>(Stmt1);
  332. const ReturnStmt *ReturnStmt2 = cast<ReturnStmt>(Stmt2);
  333. return isIdenticalStmt(Ctx, ReturnStmt1->getRetValue(),
  334. ReturnStmt2->getRetValue(), IgnoreSideEffects);
  335. }
  336. case Stmt::ForStmtClass: {
  337. const ForStmt *ForStmt1 = cast<ForStmt>(Stmt1);
  338. const ForStmt *ForStmt2 = cast<ForStmt>(Stmt2);
  339. if (!isIdenticalStmt(Ctx, ForStmt1->getInit(), ForStmt2->getInit(),
  340. IgnoreSideEffects))
  341. return false;
  342. if (!isIdenticalStmt(Ctx, ForStmt1->getCond(), ForStmt2->getCond(),
  343. IgnoreSideEffects))
  344. return false;
  345. if (!isIdenticalStmt(Ctx, ForStmt1->getInc(), ForStmt2->getInc(),
  346. IgnoreSideEffects))
  347. return false;
  348. if (!isIdenticalStmt(Ctx, ForStmt1->getBody(), ForStmt2->getBody(),
  349. IgnoreSideEffects))
  350. return false;
  351. return true;
  352. }
  353. case Stmt::DoStmtClass: {
  354. const DoStmt *DStmt1 = cast<DoStmt>(Stmt1);
  355. const DoStmt *DStmt2 = cast<DoStmt>(Stmt2);
  356. if (!isIdenticalStmt(Ctx, DStmt1->getCond(), DStmt2->getCond(),
  357. IgnoreSideEffects))
  358. return false;
  359. if (!isIdenticalStmt(Ctx, DStmt1->getBody(), DStmt2->getBody(),
  360. IgnoreSideEffects))
  361. return false;
  362. return true;
  363. }
  364. case Stmt::WhileStmtClass: {
  365. const WhileStmt *WStmt1 = cast<WhileStmt>(Stmt1);
  366. const WhileStmt *WStmt2 = cast<WhileStmt>(Stmt2);
  367. if (!isIdenticalStmt(Ctx, WStmt1->getCond(), WStmt2->getCond(),
  368. IgnoreSideEffects))
  369. return false;
  370. if (!isIdenticalStmt(Ctx, WStmt1->getBody(), WStmt2->getBody(),
  371. IgnoreSideEffects))
  372. return false;
  373. return true;
  374. }
  375. case Stmt::IfStmtClass: {
  376. const IfStmt *IStmt1 = cast<IfStmt>(Stmt1);
  377. const IfStmt *IStmt2 = cast<IfStmt>(Stmt2);
  378. if (!isIdenticalStmt(Ctx, IStmt1->getCond(), IStmt2->getCond(),
  379. IgnoreSideEffects))
  380. return false;
  381. if (!isIdenticalStmt(Ctx, IStmt1->getThen(), IStmt2->getThen(),
  382. IgnoreSideEffects))
  383. return false;
  384. if (!isIdenticalStmt(Ctx, IStmt1->getElse(), IStmt2->getElse(),
  385. IgnoreSideEffects))
  386. return false;
  387. return true;
  388. }
  389. case Stmt::CompoundStmtClass: {
  390. const CompoundStmt *CompStmt1 = cast<CompoundStmt>(Stmt1);
  391. const CompoundStmt *CompStmt2 = cast<CompoundStmt>(Stmt2);
  392. if (CompStmt1->size() != CompStmt2->size())
  393. return false;
  394. CompoundStmt::const_body_iterator I1 = CompStmt1->body_begin();
  395. CompoundStmt::const_body_iterator I2 = CompStmt2->body_begin();
  396. while (I1 != CompStmt1->body_end() && I2 != CompStmt2->body_end()) {
  397. if (!isIdenticalStmt(Ctx, *I1, *I2, IgnoreSideEffects))
  398. return false;
  399. ++I1;
  400. ++I2;
  401. }
  402. return true;
  403. }
  404. case Stmt::CompoundAssignOperatorClass:
  405. case Stmt::BinaryOperatorClass: {
  406. const BinaryOperator *BinOp1 = cast<BinaryOperator>(Stmt1);
  407. const BinaryOperator *BinOp2 = cast<BinaryOperator>(Stmt2);
  408. return BinOp1->getOpcode() == BinOp2->getOpcode();
  409. }
  410. case Stmt::CharacterLiteralClass: {
  411. const CharacterLiteral *CharLit1 = cast<CharacterLiteral>(Stmt1);
  412. const CharacterLiteral *CharLit2 = cast<CharacterLiteral>(Stmt2);
  413. return CharLit1->getValue() == CharLit2->getValue();
  414. }
  415. case Stmt::DeclRefExprClass: {
  416. const DeclRefExpr *DeclRef1 = cast<DeclRefExpr>(Stmt1);
  417. const DeclRefExpr *DeclRef2 = cast<DeclRefExpr>(Stmt2);
  418. return DeclRef1->getDecl() == DeclRef2->getDecl();
  419. }
  420. case Stmt::IntegerLiteralClass: {
  421. const IntegerLiteral *IntLit1 = cast<IntegerLiteral>(Stmt1);
  422. const IntegerLiteral *IntLit2 = cast<IntegerLiteral>(Stmt2);
  423. llvm::APInt I1 = IntLit1->getValue();
  424. llvm::APInt I2 = IntLit2->getValue();
  425. if (I1.getBitWidth() != I2.getBitWidth())
  426. return false;
  427. return I1 == I2;
  428. }
  429. case Stmt::FloatingLiteralClass: {
  430. const FloatingLiteral *FloatLit1 = cast<FloatingLiteral>(Stmt1);
  431. const FloatingLiteral *FloatLit2 = cast<FloatingLiteral>(Stmt2);
  432. return FloatLit1->getValue().bitwiseIsEqual(FloatLit2->getValue());
  433. }
  434. case Stmt::StringLiteralClass: {
  435. const StringLiteral *StringLit1 = cast<StringLiteral>(Stmt1);
  436. const StringLiteral *StringLit2 = cast<StringLiteral>(Stmt2);
  437. return StringLit1->getBytes() == StringLit2->getBytes();
  438. }
  439. case Stmt::MemberExprClass: {
  440. const MemberExpr *MemberStmt1 = cast<MemberExpr>(Stmt1);
  441. const MemberExpr *MemberStmt2 = cast<MemberExpr>(Stmt2);
  442. return MemberStmt1->getMemberDecl() == MemberStmt2->getMemberDecl();
  443. }
  444. case Stmt::UnaryOperatorClass: {
  445. const UnaryOperator *UnaryOp1 = cast<UnaryOperator>(Stmt1);
  446. const UnaryOperator *UnaryOp2 = cast<UnaryOperator>(Stmt2);
  447. return UnaryOp1->getOpcode() == UnaryOp2->getOpcode();
  448. }
  449. }
  450. }
  451. //===----------------------------------------------------------------------===//
  452. // FindIdenticalExprChecker
  453. //===----------------------------------------------------------------------===//
  454. namespace {
  455. class FindIdenticalExprChecker : public Checker<check::ASTCodeBody> {
  456. public:
  457. void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr,
  458. BugReporter &BR) const {
  459. FindIdenticalExprVisitor Visitor(BR, this, Mgr.getAnalysisDeclContext(D));
  460. Visitor.TraverseDecl(const_cast<Decl *>(D));
  461. }
  462. };
  463. } // end anonymous namespace
  464. void ento::registerIdenticalExprChecker(CheckerManager &Mgr) {
  465. Mgr.registerChecker<FindIdenticalExprChecker>();
  466. }
  467. bool ento::shouldRegisterIdenticalExprChecker(const CheckerManager &mgr) {
  468. return true;
  469. }