blob: 74f4a62ccce63f3c9a4e4e66ec35c3ec0953198b [file] [log] [blame]
Tom Caredb2fa8a2010-07-06 21:43:29 +00001//==- IdempotentOperationChecker.cpp - Idempotent Operations ----*- C++ -*-==//
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// This file defines a set of path-sensitive checks for idempotent and/or
11// tautological operations. Each potential operation is checked along all paths
12// to see if every path results in a pointless operation.
13// +-------------------------------------------+
14// |Table of idempotent/tautological operations|
15// +-------------------------------------------+
16//+--------------------------------------------------------------------------+
17//|Operator | x op x | x op 1 | 1 op x | x op 0 | 0 op x | x op ~0 | ~0 op x |
18//+--------------------------------------------------------------------------+
19// +, += | | | | x | x | |
20// -, -= | | | | x | -x | |
21// *, *= | | x | x | 0 | 0 | |
22// /, /= | 1 | x | | N/A | 0 | |
23// &, &= | x | | | 0 | 0 | x | x
24// |, |= | x | | | x | x | ~0 | ~0
25// ^, ^= | 0 | | | x | x | |
26// <<, <<= | | | | x | 0 | |
27// >>, >>= | | | | x | 0 | |
28// || | 1 | 1 | 1 | x | x | 1 | 1
29// && | 1 | x | x | 0 | 0 | x | x
30// = | x | | | | | |
31// == | 1 | | | | | |
32// >= | 1 | | | | | |
33// <= | 1 | | | | | |
34// > | 0 | | | | | |
35// < | 0 | | | | | |
36// != | 0 | | | | | |
37//===----------------------------------------------------------------------===//
38//
Tom Carea7a8a452010-08-12 22:45:47 +000039// Things TODO:
Tom Caredb2fa8a2010-07-06 21:43:29 +000040// - Improved error messages
41// - Handle mixed assumptions (which assumptions can belong together?)
42// - Finer grained false positive control (levels)
Tom Carea7a8a452010-08-12 22:45:47 +000043// - Handling ~0 values
Tom Caredb2fa8a2010-07-06 21:43:29 +000044
Tom Care1fafd1d2010-08-06 22:23:07 +000045#include "GRExprEngineExperimentalChecks.h"
Tom Carea7a8a452010-08-12 22:45:47 +000046#include "clang/Analysis/CFGStmtMap.h"
Tom Caredb2fa8a2010-07-06 21:43:29 +000047#include "clang/Checker/BugReporter/BugType.h"
Tom Carea9fbf5b2010-07-27 23:26:07 +000048#include "clang/Checker/PathSensitive/CheckerHelpers.h"
Tom Caredb2fa8a2010-07-06 21:43:29 +000049#include "clang/Checker/PathSensitive/CheckerVisitor.h"
Tom Carea7a8a452010-08-12 22:45:47 +000050#include "clang/Checker/PathSensitive/GRCoreEngine.h"
Tom Caredb2fa8a2010-07-06 21:43:29 +000051#include "clang/Checker/PathSensitive/SVals.h"
52#include "clang/AST/Stmt.h"
53#include "llvm/ADT/DenseMap.h"
Tom Carea7a8a452010-08-12 22:45:47 +000054#include "llvm/ADT/SmallSet.h"
Chandler Carruth256565b2010-07-07 00:07:37 +000055#include "llvm/Support/ErrorHandling.h"
Tom Carea7a8a452010-08-12 22:45:47 +000056#include <deque>
Tom Caredb2fa8a2010-07-06 21:43:29 +000057
58using namespace clang;
59
60namespace {
61class IdempotentOperationChecker
62 : public CheckerVisitor<IdempotentOperationChecker> {
63 public:
64 static void *getTag();
65 void PreVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B);
Tom Carebc42c532010-08-03 01:55:07 +000066 void VisitEndAnalysis(ExplodedGraph &G, BugReporter &B, GRExprEngine &Eng);
Tom Caredb2fa8a2010-07-06 21:43:29 +000067
68 private:
69 // Our assumption about a particular operation.
Ted Kremenekfe97fa12010-08-02 20:33:02 +000070 enum Assumption { Possible = 0, Impossible, Equal, LHSis1, RHSis1, LHSis0,
Tom Caredb2fa8a2010-07-06 21:43:29 +000071 RHSis0 };
72
73 void UpdateAssumption(Assumption &A, const Assumption &New);
74
75 /// contains* - Useful recursive methods to see if a statement contains an
76 /// element somewhere. Used in static analysis to reduce false positives.
Tom Caredf4ca422010-07-16 20:41:41 +000077 static bool isParameterSelfAssign(const Expr *LHS, const Expr *RHS);
78 static bool isTruncationExtensionAssignment(const Expr *LHS,
79 const Expr *RHS);
Tom Carea7a8a452010-08-12 22:45:47 +000080 static bool PathWasCompletelyAnalyzed(const CFG *C,
81 const CFGBlock *CB,
82 const GRCoreEngine &CE);
83 static bool CanVary(const Expr *Ex, ASTContext &Ctx);
Tom Caredb2fa8a2010-07-06 21:43:29 +000084
85 // Hash table
Tom Carea7a8a452010-08-12 22:45:47 +000086 typedef llvm::DenseMap<const BinaryOperator *,
87 std::pair<Assumption, AnalysisContext*> >
88 AssumptionMap;
Tom Caredb2fa8a2010-07-06 21:43:29 +000089 AssumptionMap hash;
90};
91}
92
93void *IdempotentOperationChecker::getTag() {
94 static int x = 0;
95 return &x;
96}
97
98void clang::RegisterIdempotentOperationChecker(GRExprEngine &Eng) {
99 Eng.registerCheck(new IdempotentOperationChecker());
100}
101
102void IdempotentOperationChecker::PreVisitBinaryOperator(
103 CheckerContext &C,
104 const BinaryOperator *B) {
Ted Kremenekfe97fa12010-08-02 20:33:02 +0000105 // Find or create an entry in the hash for this BinaryOperator instance.
106 // If we haven't done a lookup before, it will get default initialized to
107 // 'Possible'.
Tom Carea7a8a452010-08-12 22:45:47 +0000108 std::pair<Assumption, AnalysisContext *> &Data = hash[B];
109 Assumption &A = Data.first;
110 Data.second = C.getCurrentAnalysisContext();
Tom Caredb2fa8a2010-07-06 21:43:29 +0000111
112 // If we already have visited this node on a path that does not contain an
113 // idempotent operation, return immediately.
114 if (A == Impossible)
115 return;
116
Tom Carea7a8a452010-08-12 22:45:47 +0000117 // Retrieve both sides of the operator and determine if they can vary (which
118 // may mean this is a false positive.
Tom Caredb2fa8a2010-07-06 21:43:29 +0000119 const Expr *LHS = B->getLHS();
120 const Expr *RHS = B->getRHS();
Tom Carea7a8a452010-08-12 22:45:47 +0000121 bool LHSCanVary = CanVary(LHS, C.getASTContext());
122 bool RHSCanVary = CanVary(RHS, C.getASTContext());
Tom Caredb2fa8a2010-07-06 21:43:29 +0000123
124 const GRState *state = C.getState();
125
126 SVal LHSVal = state->getSVal(LHS);
127 SVal RHSVal = state->getSVal(RHS);
128
129 // If either value is unknown, we can't be 100% sure of all paths.
130 if (LHSVal.isUnknownOrUndef() || RHSVal.isUnknownOrUndef()) {
131 A = Impossible;
132 return;
133 }
134 BinaryOperator::Opcode Op = B->getOpcode();
135
136 // Dereference the LHS SVal if this is an assign operation
137 switch (Op) {
138 default:
139 break;
140
141 // Fall through intentional
142 case BinaryOperator::AddAssign:
143 case BinaryOperator::SubAssign:
144 case BinaryOperator::MulAssign:
145 case BinaryOperator::DivAssign:
146 case BinaryOperator::AndAssign:
147 case BinaryOperator::OrAssign:
148 case BinaryOperator::XorAssign:
149 case BinaryOperator::ShlAssign:
150 case BinaryOperator::ShrAssign:
151 case BinaryOperator::Assign:
152 // Assign statements have one extra level of indirection
153 if (!isa<Loc>(LHSVal)) {
154 A = Impossible;
155 return;
156 }
157 LHSVal = state->getSVal(cast<Loc>(LHSVal));
158 }
159
160
161 // We now check for various cases which result in an idempotent operation.
162
163 // x op x
164 switch (Op) {
165 default:
166 break; // We don't care about any other operators.
167
168 // Fall through intentional
Tom Caredf4ca422010-07-16 20:41:41 +0000169 case BinaryOperator::Assign:
170 // x Assign x has a few more false positives we can check for
171 if (isParameterSelfAssign(RHS, LHS)
172 || isTruncationExtensionAssignment(RHS, LHS)) {
173 A = Impossible;
174 return;
175 }
176
Tom Caredb2fa8a2010-07-06 21:43:29 +0000177 case BinaryOperator::SubAssign:
178 case BinaryOperator::DivAssign:
179 case BinaryOperator::AndAssign:
180 case BinaryOperator::OrAssign:
181 case BinaryOperator::XorAssign:
Tom Caredb2fa8a2010-07-06 21:43:29 +0000182 case BinaryOperator::Sub:
183 case BinaryOperator::Div:
184 case BinaryOperator::And:
185 case BinaryOperator::Or:
186 case BinaryOperator::Xor:
187 case BinaryOperator::LOr:
188 case BinaryOperator::LAnd:
Tom Carea7a8a452010-08-12 22:45:47 +0000189 if (LHSVal != RHSVal || !LHSCanVary || !RHSCanVary)
Tom Caredb2fa8a2010-07-06 21:43:29 +0000190 break;
191 UpdateAssumption(A, Equal);
192 return;
193 }
194
195 // x op 1
196 switch (Op) {
197 default:
198 break; // We don't care about any other operators.
199
200 // Fall through intentional
201 case BinaryOperator::MulAssign:
202 case BinaryOperator::DivAssign:
203 case BinaryOperator::Mul:
204 case BinaryOperator::Div:
205 case BinaryOperator::LOr:
206 case BinaryOperator::LAnd:
Tom Carea7a8a452010-08-12 22:45:47 +0000207 if (!RHSVal.isConstant(1) || !RHSCanVary)
Tom Caredb2fa8a2010-07-06 21:43:29 +0000208 break;
209 UpdateAssumption(A, RHSis1);
210 return;
211 }
212
213 // 1 op x
214 switch (Op) {
215 default:
216 break; // We don't care about any other operators.
217
218 // Fall through intentional
219 case BinaryOperator::MulAssign:
220 case BinaryOperator::Mul:
221 case BinaryOperator::LOr:
222 case BinaryOperator::LAnd:
Tom Carea7a8a452010-08-12 22:45:47 +0000223 if (!LHSVal.isConstant(1) || !LHSCanVary)
Tom Caredb2fa8a2010-07-06 21:43:29 +0000224 break;
225 UpdateAssumption(A, LHSis1);
226 return;
227 }
228
229 // x op 0
230 switch (Op) {
231 default:
232 break; // We don't care about any other operators.
233
234 // Fall through intentional
235 case BinaryOperator::AddAssign:
236 case BinaryOperator::SubAssign:
237 case BinaryOperator::MulAssign:
238 case BinaryOperator::AndAssign:
239 case BinaryOperator::OrAssign:
240 case BinaryOperator::XorAssign:
241 case BinaryOperator::Add:
242 case BinaryOperator::Sub:
243 case BinaryOperator::Mul:
244 case BinaryOperator::And:
245 case BinaryOperator::Or:
246 case BinaryOperator::Xor:
247 case BinaryOperator::Shl:
248 case BinaryOperator::Shr:
249 case BinaryOperator::LOr:
250 case BinaryOperator::LAnd:
Tom Carea7a8a452010-08-12 22:45:47 +0000251 if (!RHSVal.isConstant(0) || !RHSCanVary)
Tom Caredb2fa8a2010-07-06 21:43:29 +0000252 break;
253 UpdateAssumption(A, RHSis0);
254 return;
255 }
256
257 // 0 op x
258 switch (Op) {
259 default:
260 break; // We don't care about any other operators.
261
262 // Fall through intentional
263 //case BinaryOperator::AddAssign: // Common false positive
264 case BinaryOperator::SubAssign: // Check only if unsigned
265 case BinaryOperator::MulAssign:
266 case BinaryOperator::DivAssign:
267 case BinaryOperator::AndAssign:
268 //case BinaryOperator::OrAssign: // Common false positive
269 //case BinaryOperator::XorAssign: // Common false positive
270 case BinaryOperator::ShlAssign:
271 case BinaryOperator::ShrAssign:
272 case BinaryOperator::Add:
273 case BinaryOperator::Sub:
274 case BinaryOperator::Mul:
275 case BinaryOperator::Div:
276 case BinaryOperator::And:
277 case BinaryOperator::Or:
278 case BinaryOperator::Xor:
279 case BinaryOperator::Shl:
280 case BinaryOperator::Shr:
281 case BinaryOperator::LOr:
282 case BinaryOperator::LAnd:
Tom Carea7a8a452010-08-12 22:45:47 +0000283 if (!LHSVal.isConstant(0) || !LHSCanVary)
Tom Caredb2fa8a2010-07-06 21:43:29 +0000284 break;
285 UpdateAssumption(A, LHSis0);
286 return;
287 }
288
289 // If we get to this point, there has been a valid use of this operation.
290 A = Impossible;
291}
292
293void IdempotentOperationChecker::VisitEndAnalysis(ExplodedGraph &G,
Ted Kremenek3e5637f2010-07-27 18:49:08 +0000294 BugReporter &BR,
Tom Carebc42c532010-08-03 01:55:07 +0000295 GRExprEngine &Eng) {
Tom Caredb2fa8a2010-07-06 21:43:29 +0000296 // Iterate over the hash to see if we have any paths with definite
297 // idempotent operations.
Tom Carea7a8a452010-08-12 22:45:47 +0000298 for (AssumptionMap::const_iterator i = hash.begin(); i != hash.end(); ++i) {
299 // Unpack the hash contents
300 const std::pair<Assumption, AnalysisContext *> &Data = i->second;
301 const Assumption &A = Data.first;
302 AnalysisContext *AC = Data.second;
Ted Kremenek3e5637f2010-07-27 18:49:08 +0000303
Tom Carea7a8a452010-08-12 22:45:47 +0000304 const BinaryOperator *B = i->first;
Tom Caredb2fa8a2010-07-06 21:43:29 +0000305
Tom Carea7a8a452010-08-12 22:45:47 +0000306 if (A == Impossible)
307 continue;
308
309 // If the analyzer did not finish, check to see if we can still emit this
310 // warning
311 if (Eng.hasWorkRemaining()) {
312 const CFGStmtMap *CBM = CFGStmtMap::Build(AC->getCFG(),
313 &AC->getParentMap());
314
315 // If we can trace back
316 if (!PathWasCompletelyAnalyzed(AC->getCFG(),
317 CBM->getBlock(B),
318 Eng.getCoreEngine()))
319 continue;
320
321 delete CBM;
Tom Caredb2fa8a2010-07-06 21:43:29 +0000322 }
Tom Carea7a8a452010-08-12 22:45:47 +0000323
324 // Select the error message.
325 llvm::SmallString<128> buf;
326 llvm::raw_svector_ostream os(buf);
327 switch (A) {
328 case Equal:
329 if (B->getOpcode() == BinaryOperator::Assign)
330 os << "Assigned value is always the same as the existing value";
331 else
332 os << "Both operands to '" << B->getOpcodeStr()
333 << "' always have the same value";
334 break;
335 case LHSis1:
336 os << "The left operand to '" << B->getOpcodeStr() << "' is always 1";
337 break;
338 case RHSis1:
339 os << "The right operand to '" << B->getOpcodeStr() << "' is always 1";
340 break;
341 case LHSis0:
342 os << "The left operand to '" << B->getOpcodeStr() << "' is always 0";
343 break;
344 case RHSis0:
345 os << "The right operand to '" << B->getOpcodeStr() << "' is always 0";
346 break;
347 case Possible:
348 llvm_unreachable("Operation was never marked with an assumption");
349 case Impossible:
350 llvm_unreachable(0);
351 }
352
353 // Create the SourceRange Arrays
354 SourceRange S[2] = { i->first->getLHS()->getSourceRange(),
355 i->first->getRHS()->getSourceRange() };
356 BR.EmitBasicReport("Idempotent operation", "Dead code",
357 os.str(), i->first->getOperatorLoc(), S, 2);
Tom Caredb2fa8a2010-07-06 21:43:29 +0000358 }
359}
360
361// Updates the current assumption given the new assumption
362inline void IdempotentOperationChecker::UpdateAssumption(Assumption &A,
363 const Assumption &New) {
364 switch (A) {
365 // If we don't currently have an assumption, set it
366 case Possible:
367 A = New;
368 return;
369
370 // If we have determined that a valid state happened, ignore the new
371 // assumption.
372 case Impossible:
373 return;
374
375 // Any other case means that we had a different assumption last time. We don't
376 // currently support mixing assumptions for diagnostic reasons, so we set
377 // our assumption to be impossible.
378 default:
379 A = Impossible;
380 return;
381 }
382}
383
Tom Caredf4ca422010-07-16 20:41:41 +0000384// Check for a statement were a parameter is self assigned (to avoid an unused
385// variable warning)
386bool IdempotentOperationChecker::isParameterSelfAssign(const Expr *LHS,
387 const Expr *RHS) {
388 LHS = LHS->IgnoreParenCasts();
389 RHS = RHS->IgnoreParenCasts();
390
391 const DeclRefExpr *LHS_DR = dyn_cast<DeclRefExpr>(LHS);
392 if (!LHS_DR)
393 return false;
394
395 const ParmVarDecl *PD = dyn_cast<ParmVarDecl>(LHS_DR->getDecl());
396 if (!PD)
397 return false;
398
399 const DeclRefExpr *RHS_DR = dyn_cast<DeclRefExpr>(RHS);
400 if (!RHS_DR)
401 return false;
402
403 return PD == RHS_DR->getDecl();
404}
405
406// Check for self casts truncating/extending a variable
407bool IdempotentOperationChecker::isTruncationExtensionAssignment(
408 const Expr *LHS,
409 const Expr *RHS) {
410
411 const DeclRefExpr *LHS_DR = dyn_cast<DeclRefExpr>(LHS->IgnoreParenCasts());
412 if (!LHS_DR)
413 return false;
414
415 const VarDecl *VD = dyn_cast<VarDecl>(LHS_DR->getDecl());
416 if (!VD)
417 return false;
418
419 const DeclRefExpr *RHS_DR = dyn_cast<DeclRefExpr>(RHS->IgnoreParenCasts());
420 if (!RHS_DR)
421 return false;
422
423 if (VD != RHS_DR->getDecl())
424 return false;
425
426 return dyn_cast<DeclRefExpr>(RHS->IgnoreParens()) == NULL;
427}
428
Tom Carea7a8a452010-08-12 22:45:47 +0000429// Returns false if a path to this block was not completely analyzed, or true
430// otherwise.
431bool IdempotentOperationChecker::PathWasCompletelyAnalyzed(
432 const CFG *C,
433 const CFGBlock *CB,
434 const GRCoreEngine &CE) {
435 std::deque<const CFGBlock *> WorkList;
436 llvm::SmallSet<unsigned, 8> Aborted;
437 llvm::SmallSet<unsigned, 128> Visited;
Tom Caredb2fa8a2010-07-06 21:43:29 +0000438
Tom Carea7a8a452010-08-12 22:45:47 +0000439 // Create a set of all aborted blocks
440 typedef GRCoreEngine::BlocksAborted::const_iterator AbortedIterator;
441 for (AbortedIterator I = CE.blocks_aborted_begin(),
442 E = CE.blocks_aborted_end(); I != E; ++I) {
443 const BlockEdge &BE = I->first;
Tom Caredb2fa8a2010-07-06 21:43:29 +0000444
Tom Carea7a8a452010-08-12 22:45:47 +0000445 // The destination block on the BlockEdge is the first block that was not
446 // analyzed.
447 Aborted.insert(BE.getDst()->getBlockID());
Ted Kremenek45329312010-07-17 00:40:32 +0000448 }
Tom Caredb2fa8a2010-07-06 21:43:29 +0000449
Tom Carea7a8a452010-08-12 22:45:47 +0000450 // Save the entry block ID for early exiting
451 unsigned EntryBlockID = C->getEntry().getBlockID();
Tom Caredb2fa8a2010-07-06 21:43:29 +0000452
Tom Carea7a8a452010-08-12 22:45:47 +0000453 // Create initial node
454 WorkList.push_back(CB);
455
456 while (!WorkList.empty()) {
457 const CFGBlock *Head = WorkList.front();
458 WorkList.pop_front();
459 Visited.insert(Head->getBlockID());
460
461 // If we found the entry block, then there exists a path from the target
462 // node to the entry point of this function -> the path was completely
463 // analyzed.
464 if (Head->getBlockID() == EntryBlockID)
465 return true;
466
467 // If any of the aborted blocks are on the path to the beginning, then all
468 // paths to this block were not analyzed.
469 if (Aborted.count(Head->getBlockID()))
470 return false;
471
472 // Add the predecessors to the worklist unless we have already visited them
473 for (CFGBlock::const_pred_iterator I = Head->pred_begin();
474 I != Head->pred_end(); ++I)
475 if (!Visited.count((*I)->getBlockID()))
476 WorkList.push_back(*I);
477 }
478
479 // If we get to this point, there is no connection to the entry block or an
480 // aborted block. This path is unreachable and we can report the error.
481 return true;
482}
483
484// Recursive function that determines whether an expression contains any element
485// that varies. This could be due to a compile-time constant like sizeof. An
486// expression may also involve a variable that behaves like a constant. The
487// function returns true if the expression varies, and false otherwise.
488bool IdempotentOperationChecker::CanVary(const Expr *Ex, ASTContext &Ctx) {
489 // Parentheses and casts are irrelevant here
490 Ex = Ex->IgnoreParenCasts();
491
492 if (Ex->getLocStart().isMacroID())
493 return false;
494
495 switch (Ex->getStmtClass()) {
496 // Trivially true cases
497 case Stmt::ArraySubscriptExprClass:
498 case Stmt::MemberExprClass:
499 case Stmt::StmtExprClass:
500 case Stmt::CallExprClass:
501 case Stmt::VAArgExprClass:
502 case Stmt::ShuffleVectorExprClass:
503 return true;
504 default:
505 return true;
506
507 // Trivially false cases
508 case Stmt::IntegerLiteralClass:
509 case Stmt::CharacterLiteralClass:
510 case Stmt::FloatingLiteralClass:
511 case Stmt::PredefinedExprClass:
512 case Stmt::ImaginaryLiteralClass:
513 case Stmt::StringLiteralClass:
514 case Stmt::OffsetOfExprClass:
515 case Stmt::CompoundLiteralExprClass:
516 case Stmt::AddrLabelExprClass:
517 case Stmt::TypesCompatibleExprClass:
518 case Stmt::GNUNullExprClass:
519 case Stmt::InitListExprClass:
520 case Stmt::DesignatedInitExprClass:
521 case Stmt::BlockExprClass:
522 case Stmt::BlockDeclRefExprClass:
523 return false;
524
525 // Cases requiring custom logic
526 case Stmt::SizeOfAlignOfExprClass: {
527 const SizeOfAlignOfExpr *SE = cast<const SizeOfAlignOfExpr>(Ex);
528 if (!SE->isSizeOf())
529 return false;
530 return SE->getTypeOfArgument()->isVariableArrayType();
531 }
532 case Stmt::DeclRefExprClass:
533 // return !IsPseudoConstant(cast<DeclRefExpr>(Ex));
534 return true;
535
536 // The next cases require recursion for subexpressions
537 case Stmt::BinaryOperatorClass: {
538 const BinaryOperator *B = cast<const BinaryOperator>(Ex);
539 return CanVary(B->getRHS(), Ctx) || CanVary(B->getLHS(), Ctx);
540 }
541 case Stmt::UnaryOperatorClass: {
542 const UnaryOperator *U = cast<const UnaryOperator>(Ex);
Eli Friedmande7e6622010-08-13 01:36:11 +0000543 // Handle trivial case first
Tom Carea7a8a452010-08-12 22:45:47 +0000544 switch (U->getOpcode()) {
545 case UnaryOperator::Extension:
Tom Carea7a8a452010-08-12 22:45:47 +0000546 return false;
547 default:
548 return CanVary(U->getSubExpr(), Ctx);
549 }
550 }
551 case Stmt::ChooseExprClass:
552 return CanVary(cast<const ChooseExpr>(Ex)->getChosenSubExpr(Ctx), Ctx);
553 case Stmt::ConditionalOperatorClass:
554 return CanVary(cast<const ConditionalOperator>(Ex)->getCond(), Ctx);
555 }
Tom Caredb2fa8a2010-07-06 21:43:29 +0000556}
557