blob: a76dff120bebc7697ef928e293f4ffdd2f228e39 [file] [log] [blame]
Jordan Rose51327f92013-11-08 01:15:39 +00001//== IdenticalExprChecker.cpp - Identical expression checker----------------==//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9///
10/// \file
11/// \brief This defines IdenticalExprChecker, a check that warns about
12/// unintended use of identical expressions.
13///
Jordan Rose60bd88d2013-12-10 18:18:06 +000014/// It checks for use of identical expressions with comparison operators and
15/// inside conditional expressions.
Jordan Rose51327f92013-11-08 01:15:39 +000016///
17//===----------------------------------------------------------------------===//
18
19#include "ClangSACheckers.h"
Chandler Carruth5553d0d2014-01-07 11:51:46 +000020#include "clang/AST/RecursiveASTVisitor.h"
Jordan Rose51327f92013-11-08 01:15:39 +000021#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
22#include "clang/StaticAnalyzer/Core/Checker.h"
23#include "clang/StaticAnalyzer/Core/CheckerManager.h"
24#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
Jordan Rose51327f92013-11-08 01:15:39 +000025
26using namespace clang;
27using namespace ento;
28
Jordan Rose70e7e872014-02-19 17:44:11 +000029static bool isIdenticalStmt(const ASTContext &Ctx, const Stmt *Stmt1,
30 const Stmt *Stmt2, bool IgnoreSideEffects = false);
Jordan Rose51327f92013-11-08 01:15:39 +000031//===----------------------------------------------------------------------===//
32// FindIdenticalExprVisitor - Identify nodes using identical expressions.
33//===----------------------------------------------------------------------===//
34
Benjamin Kramere8a2c182013-11-14 15:46:10 +000035namespace {
Jordan Rose51327f92013-11-08 01:15:39 +000036class FindIdenticalExprVisitor
37 : public RecursiveASTVisitor<FindIdenticalExprVisitor> {
Jordan Rose94008122014-02-19 17:44:16 +000038 BugReporter &BR;
39 const CheckerBase *Checker;
40 AnalysisDeclContext *AC;
Jordan Rose51327f92013-11-08 01:15:39 +000041public:
Alexander Kornienko4aca9b12014-02-11 21:49:21 +000042 explicit FindIdenticalExprVisitor(BugReporter &B,
43 const CheckerBase *Checker,
44 AnalysisDeclContext *A)
45 : BR(B), Checker(Checker), AC(A) {}
Jordan Rose51327f92013-11-08 01:15:39 +000046 // FindIdenticalExprVisitor only visits nodes
Jordan Rose70e7e872014-02-19 17:44:11 +000047 // that are binary operators, if statements or
48 // conditional operators.
Jordan Rose51327f92013-11-08 01:15:39 +000049 bool VisitBinaryOperator(const BinaryOperator *B);
Jordan Rose70e7e872014-02-19 17:44:11 +000050 bool VisitIfStmt(const IfStmt *I);
Jordan Rose60bd88d2013-12-10 18:18:06 +000051 bool VisitConditionalOperator(const ConditionalOperator *C);
Jordan Rose51327f92013-11-08 01:15:39 +000052
53private:
Jordan Rose94008122014-02-19 17:44:16 +000054 void reportIdenticalExpr(const BinaryOperator *B, bool CheckBitwise,
55 ArrayRef<SourceRange> Sr);
56 void checkBitwiseOrLogicalOp(const BinaryOperator *B, bool CheckBitwise);
57 void checkComparisonOp(const BinaryOperator *B);
Jordan Rose51327f92013-11-08 01:15:39 +000058};
Benjamin Kramere8a2c182013-11-14 15:46:10 +000059} // end anonymous namespace
Jordan Rose51327f92013-11-08 01:15:39 +000060
Jordan Rose94008122014-02-19 17:44:16 +000061void FindIdenticalExprVisitor::reportIdenticalExpr(const BinaryOperator *B,
62 bool CheckBitwise,
63 ArrayRef<SourceRange> Sr) {
64 StringRef Message;
65 if (CheckBitwise)
66 Message = "identical expressions on both sides of bitwise operator";
67 else
68 Message = "identical expressions on both sides of logical operator";
69
70 PathDiagnosticLocation ELoc =
71 PathDiagnosticLocation::createOperatorLoc(B, BR.getSourceManager());
72 BR.EmitBasicReport(AC->getDecl(), Checker,
73 "Use of identical expressions",
74 categories::LogicError,
75 Message, ELoc, Sr);
76}
77
78void FindIdenticalExprVisitor::checkBitwiseOrLogicalOp(const BinaryOperator *B,
79 bool CheckBitwise) {
80 SourceRange Sr[2];
81
82 const Expr *LHS = B->getLHS();
83 const Expr *RHS = B->getRHS();
84
85 // Split operators as long as we still have operators to split on. We will
86 // get called for every binary operator in an expression so there is no need
87 // to check every one against each other here, just the right most one with
88 // the others.
89 while (const BinaryOperator *B2 = dyn_cast<BinaryOperator>(LHS)) {
90 if (B->getOpcode() != B2->getOpcode())
91 break;
92 if (isIdenticalStmt(AC->getASTContext(), RHS, B2->getRHS())) {
93 Sr[0] = RHS->getSourceRange();
94 Sr[1] = B2->getRHS()->getSourceRange();
95 reportIdenticalExpr(B, CheckBitwise, Sr);
96 }
97 LHS = B2->getLHS();
98 }
99
100 if (isIdenticalStmt(AC->getASTContext(), RHS, LHS)) {
101 Sr[0] = RHS->getSourceRange();
102 Sr[1] = LHS->getSourceRange();
103 reportIdenticalExpr(B, CheckBitwise, Sr);
104 }
105}
106
Jordan Rose70e7e872014-02-19 17:44:11 +0000107bool FindIdenticalExprVisitor::VisitIfStmt(const IfStmt *I) {
108 const Stmt *Stmt1 = I->getThen();
109 const Stmt *Stmt2 = I->getElse();
110
111 if (!Stmt1 || !Stmt2)
112 return true;
113
114 // Special handling for code like:
115 //
116 // if (b) {
117 // i = 1;
118 // } else
119 // i = 1;
120 if (const CompoundStmt *CompStmt = dyn_cast<CompoundStmt>(Stmt1)) {
121 if (CompStmt->size() == 1)
122 Stmt1 = CompStmt->body_back();
123 }
124 if (const CompoundStmt *CompStmt = dyn_cast<CompoundStmt>(Stmt2)) {
125 if (CompStmt->size() == 1)
126 Stmt2 = CompStmt->body_back();
127 }
128
129 if (isIdenticalStmt(AC->getASTContext(), Stmt1, Stmt2, true)) {
130 PathDiagnosticLocation ELoc =
131 PathDiagnosticLocation::createBegin(I, BR.getSourceManager(), AC);
132 BR.EmitBasicReport(AC->getDecl(), Checker,
133 "Identical branches",
134 categories::LogicError,
135 "true and false branches are identical", ELoc);
136 }
137 return true;
138}
139
Jordan Rose51327f92013-11-08 01:15:39 +0000140bool FindIdenticalExprVisitor::VisitBinaryOperator(const BinaryOperator *B) {
141 BinaryOperator::Opcode Op = B->getOpcode();
Jordan Rose94008122014-02-19 17:44:16 +0000142
143 if (BinaryOperator::isBitwiseOp(Op))
144 checkBitwiseOrLogicalOp(B, true);
145
146 if (BinaryOperator::isLogicalOp(Op))
147 checkBitwiseOrLogicalOp(B, false);
148
149 if (BinaryOperator::isComparisonOp(Op))
150 checkComparisonOp(B);
151
152 // We want to visit ALL nodes (subexpressions of binary comparison
153 // expressions too) that contains comparison operators.
154 // True is always returned to traverse ALL nodes.
155 return true;
156}
157
158void FindIdenticalExprVisitor::checkComparisonOp(const BinaryOperator *B) {
159 BinaryOperator::Opcode Op = B->getOpcode();
160
Jordan Rose51327f92013-11-08 01:15:39 +0000161 //
162 // Special case for floating-point representation.
163 //
164 // If expressions on both sides of comparison operator are of type float,
165 // then for some comparison operators no warning shall be
166 // reported even if the expressions are identical from a symbolic point of
167 // view. Comparison between expressions, declared variables and literals
168 // are treated differently.
169 //
170 // != and == between float literals that have the same value should NOT warn.
171 // < > between float literals that have the same value SHOULD warn.
172 //
173 // != and == between the same float declaration should NOT warn.
174 // < > between the same float declaration SHOULD warn.
175 //
176 // != and == between eq. expressions that evaluates into float
177 // should NOT warn.
178 // < > between eq. expressions that evaluates into float
179 // should NOT warn.
180 //
181 const Expr *LHS = B->getLHS()->IgnoreParenImpCasts();
182 const Expr *RHS = B->getRHS()->IgnoreParenImpCasts();
183
184 const DeclRefExpr *DeclRef1 = dyn_cast<DeclRefExpr>(LHS);
185 const DeclRefExpr *DeclRef2 = dyn_cast<DeclRefExpr>(RHS);
186 const FloatingLiteral *FloatLit1 = dyn_cast<FloatingLiteral>(LHS);
187 const FloatingLiteral *FloatLit2 = dyn_cast<FloatingLiteral>(RHS);
188 if ((DeclRef1) && (DeclRef2)) {
189 if ((DeclRef1->getType()->hasFloatingRepresentation()) &&
190 (DeclRef2->getType()->hasFloatingRepresentation())) {
191 if (DeclRef1->getDecl() == DeclRef2->getDecl()) {
192 if ((Op == BO_EQ) || (Op == BO_NE)) {
Jordan Rose94008122014-02-19 17:44:16 +0000193 return;
Jordan Rose51327f92013-11-08 01:15:39 +0000194 }
195 }
196 }
197 } else if ((FloatLit1) && (FloatLit2)) {
198 if (FloatLit1->getValue().bitwiseIsEqual(FloatLit2->getValue())) {
199 if ((Op == BO_EQ) || (Op == BO_NE)) {
Jordan Rose94008122014-02-19 17:44:16 +0000200 return;
Jordan Rose51327f92013-11-08 01:15:39 +0000201 }
202 }
203 } else if (LHS->getType()->hasFloatingRepresentation()) {
204 // If any side of comparison operator still has floating-point
205 // representation, then it's an expression. Don't warn.
206 // Here only LHS is checked since RHS will be implicit casted to float.
Jordan Rose94008122014-02-19 17:44:16 +0000207 return;
Jordan Rose51327f92013-11-08 01:15:39 +0000208 } else {
209 // No special case with floating-point representation, report as usual.
210 }
211
Jordan Rose70e7e872014-02-19 17:44:11 +0000212 if (isIdenticalStmt(AC->getASTContext(), B->getLHS(), B->getRHS())) {
Jordan Rose51327f92013-11-08 01:15:39 +0000213 PathDiagnosticLocation ELoc =
214 PathDiagnosticLocation::createOperatorLoc(B, BR.getSourceManager());
215 StringRef Message;
216 if (((Op == BO_EQ) || (Op == BO_LE) || (Op == BO_GE)))
217 Message = "comparison of identical expressions always evaluates to true";
218 else
219 Message = "comparison of identical expressions always evaluates to false";
Alexander Kornienko4aca9b12014-02-11 21:49:21 +0000220 BR.EmitBasicReport(AC->getDecl(), Checker,
221 "Compare of identical expressions",
Jordan Rose51327f92013-11-08 01:15:39 +0000222 categories::LogicError, Message, ELoc);
223 }
Jordan Rose51327f92013-11-08 01:15:39 +0000224}
Jordan Rose60bd88d2013-12-10 18:18:06 +0000225
226bool FindIdenticalExprVisitor::VisitConditionalOperator(
227 const ConditionalOperator *C) {
228
229 // Check if expressions in conditional expression are identical
230 // from a symbolic point of view.
231
Jordan Rose70e7e872014-02-19 17:44:11 +0000232 if (isIdenticalStmt(AC->getASTContext(), C->getTrueExpr(),
Jordan Rose60bd88d2013-12-10 18:18:06 +0000233 C->getFalseExpr(), true)) {
234 PathDiagnosticLocation ELoc =
235 PathDiagnosticLocation::createConditionalColonLoc(
236 C, BR.getSourceManager());
237
238 SourceRange Sr[2];
239 Sr[0] = C->getTrueExpr()->getSourceRange();
240 Sr[1] = C->getFalseExpr()->getSourceRange();
241 BR.EmitBasicReport(
Alexander Kornienko4aca9b12014-02-11 21:49:21 +0000242 AC->getDecl(), Checker,
243 "Identical expressions in conditional expression",
Jordan Rose60bd88d2013-12-10 18:18:06 +0000244 categories::LogicError,
245 "identical expressions on both sides of ':' in conditional expression",
246 ELoc, Sr);
247 }
248 // We want to visit ALL nodes (expressions in conditional
249 // expressions too) that contains conditional operators,
250 // thus always return true to traverse ALL nodes.
251 return true;
252}
253
Jordan Rose70e7e872014-02-19 17:44:11 +0000254/// \brief Determines whether two statement trees are identical regarding
Jordan Rose51327f92013-11-08 01:15:39 +0000255/// operators and symbols.
256///
257/// Exceptions: expressions containing macros or functions with possible side
258/// effects are never considered identical.
259/// Limitations: (t + u) and (u + t) are not considered identical.
260/// t*(u + t) and t*u + t*t are not considered identical.
261///
Jordan Rose70e7e872014-02-19 17:44:11 +0000262static bool isIdenticalStmt(const ASTContext &Ctx, const Stmt *Stmt1,
263 const Stmt *Stmt2, bool IgnoreSideEffects) {
264
265 if (!Stmt1 || !Stmt2) {
266 if (!Stmt1 && !Stmt2)
267 return true;
Jordan Rose51327f92013-11-08 01:15:39 +0000268 return false;
Jordan Rose51327f92013-11-08 01:15:39 +0000269 }
Jordan Rose70e7e872014-02-19 17:44:11 +0000270
271 // If Stmt1 & Stmt2 are of different class then they are not
272 // identical statements.
273 if (Stmt1->getStmtClass() != Stmt2->getStmtClass())
Jordan Rose51327f92013-11-08 01:15:39 +0000274 return false;
275
Jordan Rose70e7e872014-02-19 17:44:11 +0000276 const Expr *Expr1 = dyn_cast<Expr>(Stmt1);
277 const Expr *Expr2 = dyn_cast<Expr>(Stmt2);
278
279 if (Expr1 && Expr2) {
280 // If Stmt1 has side effects then don't warn even if expressions
281 // are identical.
282 if (!IgnoreSideEffects && Expr1->HasSideEffects(Ctx))
283 return false;
284 // If either expression comes from a macro then don't warn even if
285 // the expressions are identical.
286 if ((Expr1->getExprLoc().isMacroID()) || (Expr2->getExprLoc().isMacroID()))
287 return false;
288
289 // If all children of two expressions are identical, return true.
290 Expr::const_child_iterator I1 = Expr1->child_begin();
291 Expr::const_child_iterator I2 = Expr2->child_begin();
292 while (I1 != Expr1->child_end() && I2 != Expr2->child_end()) {
293 if (!*I1 || !*I2 || !isIdenticalStmt(Ctx, *I1, *I2, IgnoreSideEffects))
294 return false;
295 ++I1;
296 ++I2;
297 }
298 // If there are different number of children in the statements, return
299 // false.
300 if (I1 != Expr1->child_end())
301 return false;
302 if (I2 != Expr2->child_end())
303 return false;
304 }
305
306 switch (Stmt1->getStmtClass()) {
Jordan Rose51327f92013-11-08 01:15:39 +0000307 default:
308 return false;
Jordan Rose60bd88d2013-12-10 18:18:06 +0000309 case Stmt::CallExprClass:
Jordan Rose51327f92013-11-08 01:15:39 +0000310 case Stmt::ArraySubscriptExprClass:
Jordan Rose51327f92013-11-08 01:15:39 +0000311 case Stmt::ImplicitCastExprClass:
312 case Stmt::ParenExprClass:
Jordan Rose70e7e872014-02-19 17:44:11 +0000313 case Stmt::BreakStmtClass:
314 case Stmt::ContinueStmtClass:
315 case Stmt::NullStmtClass:
Jordan Rose51327f92013-11-08 01:15:39 +0000316 return true;
Jordan Rose70e7e872014-02-19 17:44:11 +0000317 case Stmt::CStyleCastExprClass: {
318 const CStyleCastExpr* CastExpr1 = cast<CStyleCastExpr>(Stmt1);
319 const CStyleCastExpr* CastExpr2 = cast<CStyleCastExpr>(Stmt2);
320
321 return CastExpr1->getTypeAsWritten() == CastExpr2->getTypeAsWritten();
322 }
323 case Stmt::ReturnStmtClass: {
324 const ReturnStmt *ReturnStmt1 = cast<ReturnStmt>(Stmt1);
325 const ReturnStmt *ReturnStmt2 = cast<ReturnStmt>(Stmt2);
326
327 return isIdenticalStmt(Ctx, ReturnStmt1->getRetValue(),
328 ReturnStmt2->getRetValue(), IgnoreSideEffects);
329 }
330 case Stmt::ForStmtClass: {
331 const ForStmt *ForStmt1 = cast<ForStmt>(Stmt1);
332 const ForStmt *ForStmt2 = cast<ForStmt>(Stmt2);
333
334 if (!isIdenticalStmt(Ctx, ForStmt1->getInit(), ForStmt2->getInit(),
335 IgnoreSideEffects))
336 return false;
337 if (!isIdenticalStmt(Ctx, ForStmt1->getCond(), ForStmt2->getCond(),
338 IgnoreSideEffects))
339 return false;
340 if (!isIdenticalStmt(Ctx, ForStmt1->getInc(), ForStmt2->getInc(),
341 IgnoreSideEffects))
342 return false;
343 if (!isIdenticalStmt(Ctx, ForStmt1->getBody(), ForStmt2->getBody(),
344 IgnoreSideEffects))
345 return false;
346 return true;
347 }
348 case Stmt::DoStmtClass: {
349 const DoStmt *DStmt1 = cast<DoStmt>(Stmt1);
350 const DoStmt *DStmt2 = cast<DoStmt>(Stmt2);
351
352 if (!isIdenticalStmt(Ctx, DStmt1->getCond(), DStmt2->getCond(),
353 IgnoreSideEffects))
354 return false;
355 if (!isIdenticalStmt(Ctx, DStmt1->getBody(), DStmt2->getBody(),
356 IgnoreSideEffects))
357 return false;
358 return true;
359 }
360 case Stmt::WhileStmtClass: {
361 const WhileStmt *WStmt1 = cast<WhileStmt>(Stmt1);
362 const WhileStmt *WStmt2 = cast<WhileStmt>(Stmt2);
363
364 return isIdenticalStmt(Ctx, WStmt1->getCond(), WStmt2->getCond(),
365 IgnoreSideEffects);
366 }
367 case Stmt::IfStmtClass: {
368 const IfStmt *IStmt1 = cast<IfStmt>(Stmt1);
369 const IfStmt *IStmt2 = cast<IfStmt>(Stmt2);
370
371 if (!isIdenticalStmt(Ctx, IStmt1->getCond(), IStmt2->getCond(),
372 IgnoreSideEffects))
373 return false;
374 if (!isIdenticalStmt(Ctx, IStmt1->getThen(), IStmt2->getThen(),
375 IgnoreSideEffects))
376 return false;
377 if (!isIdenticalStmt(Ctx, IStmt1->getElse(), IStmt2->getElse(),
378 IgnoreSideEffects))
379 return false;
380 return true;
381 }
382 case Stmt::CompoundStmtClass: {
383 const CompoundStmt *CompStmt1 = cast<CompoundStmt>(Stmt1);
384 const CompoundStmt *CompStmt2 = cast<CompoundStmt>(Stmt2);
385
386 if (CompStmt1->size() != CompStmt2->size())
387 return false;
388
389 CompoundStmt::const_body_iterator I1 = CompStmt1->body_begin();
390 CompoundStmt::const_body_iterator I2 = CompStmt2->body_begin();
391 while (I1 != CompStmt1->body_end() && I2 != CompStmt2->body_end()) {
392 if (!isIdenticalStmt(Ctx, *I1, *I2, IgnoreSideEffects))
393 return false;
394 ++I1;
395 ++I2;
396 }
397
398 return true;
399 }
400 case Stmt::CompoundAssignOperatorClass:
Jordan Rose51327f92013-11-08 01:15:39 +0000401 case Stmt::BinaryOperatorClass: {
Jordan Rose70e7e872014-02-19 17:44:11 +0000402 const BinaryOperator *BinOp1 = cast<BinaryOperator>(Stmt1);
403 const BinaryOperator *BinOp2 = cast<BinaryOperator>(Stmt2);
Jordan Rose51327f92013-11-08 01:15:39 +0000404 return BinOp1->getOpcode() == BinOp2->getOpcode();
405 }
406 case Stmt::CharacterLiteralClass: {
Jordan Rose70e7e872014-02-19 17:44:11 +0000407 const CharacterLiteral *CharLit1 = cast<CharacterLiteral>(Stmt1);
408 const CharacterLiteral *CharLit2 = cast<CharacterLiteral>(Stmt2);
Jordan Rose51327f92013-11-08 01:15:39 +0000409 return CharLit1->getValue() == CharLit2->getValue();
410 }
411 case Stmt::DeclRefExprClass: {
Jordan Rose70e7e872014-02-19 17:44:11 +0000412 const DeclRefExpr *DeclRef1 = cast<DeclRefExpr>(Stmt1);
413 const DeclRefExpr *DeclRef2 = cast<DeclRefExpr>(Stmt2);
Jordan Rose51327f92013-11-08 01:15:39 +0000414 return DeclRef1->getDecl() == DeclRef2->getDecl();
415 }
416 case Stmt::IntegerLiteralClass: {
Jordan Rose70e7e872014-02-19 17:44:11 +0000417 const IntegerLiteral *IntLit1 = cast<IntegerLiteral>(Stmt1);
418 const IntegerLiteral *IntLit2 = cast<IntegerLiteral>(Stmt2);
Jordan Rose51327f92013-11-08 01:15:39 +0000419 return IntLit1->getValue() == IntLit2->getValue();
420 }
421 case Stmt::FloatingLiteralClass: {
Jordan Rose70e7e872014-02-19 17:44:11 +0000422 const FloatingLiteral *FloatLit1 = cast<FloatingLiteral>(Stmt1);
423 const FloatingLiteral *FloatLit2 = cast<FloatingLiteral>(Stmt2);
Jordan Rose51327f92013-11-08 01:15:39 +0000424 return FloatLit1->getValue().bitwiseIsEqual(FloatLit2->getValue());
425 }
Jordan Rose70e7e872014-02-19 17:44:11 +0000426 case Stmt::StringLiteralClass: {
427 const StringLiteral *StringLit1 = cast<StringLiteral>(Stmt1);
428 const StringLiteral *StringLit2 = cast<StringLiteral>(Stmt2);
429 return StringLit1->getString() == StringLit2->getString();
430 }
Jordan Rose51327f92013-11-08 01:15:39 +0000431 case Stmt::MemberExprClass: {
Jordan Rose70e7e872014-02-19 17:44:11 +0000432 const MemberExpr *MemberStmt1 = cast<MemberExpr>(Stmt1);
433 const MemberExpr *MemberStmt2 = cast<MemberExpr>(Stmt2);
434 return MemberStmt1->getMemberDecl() == MemberStmt2->getMemberDecl();
Jordan Rose51327f92013-11-08 01:15:39 +0000435 }
436 case Stmt::UnaryOperatorClass: {
Jordan Rose70e7e872014-02-19 17:44:11 +0000437 const UnaryOperator *UnaryOp1 = cast<UnaryOperator>(Stmt1);
438 const UnaryOperator *UnaryOp2 = cast<UnaryOperator>(Stmt2);
Jordan Rose6f2f3902013-12-10 18:18:10 +0000439 return UnaryOp1->getOpcode() == UnaryOp2->getOpcode();
Jordan Rose51327f92013-11-08 01:15:39 +0000440 }
441 }
442}
443
444//===----------------------------------------------------------------------===//
445// FindIdenticalExprChecker
446//===----------------------------------------------------------------------===//
447
Benjamin Kramere8a2c182013-11-14 15:46:10 +0000448namespace {
Jordan Rose51327f92013-11-08 01:15:39 +0000449class FindIdenticalExprChecker : public Checker<check::ASTCodeBody> {
450public:
451 void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr,
452 BugReporter &BR) const {
Alexander Kornienko4aca9b12014-02-11 21:49:21 +0000453 FindIdenticalExprVisitor Visitor(BR, this, Mgr.getAnalysisDeclContext(D));
Jordan Rose51327f92013-11-08 01:15:39 +0000454 Visitor.TraverseDecl(const_cast<Decl *>(D));
455 }
456};
Benjamin Kramere8a2c182013-11-14 15:46:10 +0000457} // end anonymous namespace
Jordan Rose51327f92013-11-08 01:15:39 +0000458
459void ento::registerIdenticalExprChecker(CheckerManager &Mgr) {
460 Mgr.registerChecker<FindIdenticalExprChecker>();
461}