static Attr *handleOpenCLUnrollHint(Sema &S, Stmt *St, const ParsedAttr &A, SourceRange Range) { // Although the feature was introduced only in OpenCL C v2.0 s6.11.5, it's // useful for OpenCL 1.x too and doesn't require HW support. // opencl_unroll_hint can have 0 arguments (compiler // determines unrolling factor) or 1 argument (the unroll factor provided // by the user). unsigned NumArgs = A.getNumArgs(); if (NumArgs > 1) { S.Diag(A.getLoc(), diag::err_attribute_too_many_arguments) << A << 1; return nullptr; } unsigned UnrollFactor = 0; if (NumArgs == 1) { Expr *E = A.getArgAsExpr(0); llvm::APSInt ArgVal(32); if (!E->isIntegerConstantExpr(ArgVal, S.Context)) { S.Diag(A.getLoc(), diag::err_attribute_argument_type) << A << AANT_ArgumentIntegerConstant << E->getSourceRange(); return nullptr; } int Val = ArgVal.getSExtValue(); if (Val <= 0) { S.Diag(A.getRange().getBegin(), diag::err_attribute_requires_positive_integer) << A; return nullptr; } UnrollFactor = Val; } return OpenCLUnrollHintAttr::CreateImplicit(S.Context, UnrollFactor); }
/// ActOnCXXNew - Parsed a C++ 'new' expression (C++ 5.3.4), as in e.g.: /// @code new (memory) int[size][4] @endcode /// or /// @code ::new Foo(23, "hello") @endcode /// For the interpretation of this heap of arguments, consult the base version. Action::OwningExprResult Sema::ActOnCXXNew(SourceLocation StartLoc, bool UseGlobal, SourceLocation PlacementLParen, MultiExprArg PlacementArgs, SourceLocation PlacementRParen, bool ParenTypeId, Declarator &D, SourceLocation ConstructorLParen, MultiExprArg ConstructorArgs, SourceLocation ConstructorRParen) { Expr *ArraySize = 0; unsigned Skip = 0; // If the specified type is an array, unwrap it and save the expression. if (D.getNumTypeObjects() > 0 && D.getTypeObject(0).Kind == DeclaratorChunk::Array) { DeclaratorChunk &Chunk = D.getTypeObject(0); if (Chunk.Arr.hasStatic) return ExprError(Diag(Chunk.Loc, diag::err_static_illegal_in_new) << D.getSourceRange()); if (!Chunk.Arr.NumElts) return ExprError(Diag(Chunk.Loc, diag::err_array_new_needs_size) << D.getSourceRange()); ArraySize = static_cast<Expr*>(Chunk.Arr.NumElts); Skip = 1; } QualType AllocType = GetTypeForDeclarator(D, /*Scope=*/0, Skip); if (D.getInvalidType()) return ExprError(); if (CheckAllocatedType(AllocType, D)) return ExprError(); QualType ResultType = AllocType->isDependentType() ? Context.DependentTy : Context.getPointerType(AllocType); // That every array dimension except the first is constant was already // checked by the type check above. // C++ 5.3.4p6: "The expression in a direct-new-declarator shall have integral // or enumeration type with a non-negative value." if (ArraySize && !ArraySize->isTypeDependent()) { QualType SizeType = ArraySize->getType(); if (!SizeType->isIntegralType() && !SizeType->isEnumeralType()) return ExprError(Diag(ArraySize->getSourceRange().getBegin(), diag::err_array_size_not_integral) << SizeType << ArraySize->getSourceRange()); // Let's see if this is a constant < 0. If so, we reject it out of hand. // We don't care about special rules, so we tell the machinery it's not // evaluated - it gives us a result in more cases. if (!ArraySize->isValueDependent()) { llvm::APSInt Value; if (ArraySize->isIntegerConstantExpr(Value, Context, 0, false)) { if (Value < llvm::APSInt( llvm::APInt::getNullValue(Value.getBitWidth()), false)) return ExprError(Diag(ArraySize->getSourceRange().getBegin(), diag::err_typecheck_negative_array_size) << ArraySize->getSourceRange()); } } } FunctionDecl *OperatorNew = 0; FunctionDecl *OperatorDelete = 0; Expr **PlaceArgs = (Expr**)PlacementArgs.get(); unsigned NumPlaceArgs = PlacementArgs.size(); if (!AllocType->isDependentType() && !Expr::hasAnyTypeDependentArguments(PlaceArgs, NumPlaceArgs) && FindAllocationFunctions(StartLoc, SourceRange(PlacementLParen, PlacementRParen), UseGlobal, AllocType, ArraySize, PlaceArgs, NumPlaceArgs, OperatorNew, OperatorDelete)) return ExprError(); bool Init = ConstructorLParen.isValid(); // --- Choosing a constructor --- // C++ 5.3.4p15 // 1) If T is a POD and there's no initializer (ConstructorLParen is invalid) // the object is not initialized. If the object, or any part of it, is // const-qualified, it's an error. // 2) If T is a POD and there's an empty initializer, the object is value- // initialized. // 3) If T is a POD and there's one initializer argument, the object is copy- // constructed. // 4) If T is a POD and there's more initializer arguments, it's an error. // 5) If T is not a POD, the initializer arguments are used as constructor // arguments. // // Or by the C++0x formulation: // 1) If there's no initializer, the object is default-initialized according // to C++0x rules. // 2) Otherwise, the object is direct-initialized. CXXConstructorDecl *Constructor = 0; Expr **ConsArgs = (Expr**)ConstructorArgs.get(); unsigned NumConsArgs = ConstructorArgs.size(); if (AllocType->isDependentType()) { // Skip all the checks. } // FIXME: Should check for primitive/aggregate here, not record. else if (const RecordType *RT = AllocType->getAsRecordType()) { // FIXME: This is incorrect for when there is an empty initializer and // no user-defined constructor. Must zero-initialize, not default-construct. Constructor = PerformInitializationByConstructor( AllocType, ConsArgs, NumConsArgs, D.getSourceRange().getBegin(), SourceRange(D.getSourceRange().getBegin(), ConstructorRParen), RT->getDecl()->getDeclName(), NumConsArgs != 0 ? IK_Direct : IK_Default); if (!Constructor) return ExprError(); } else { if (!Init) { // FIXME: Check that no subpart is const. if (AllocType.isConstQualified()) return ExprError(Diag(StartLoc, diag::err_new_uninitialized_const) << D.getSourceRange()); } else if (NumConsArgs == 0) { // Object is value-initialized. Do nothing. } else if (NumConsArgs == 1) { // Object is direct-initialized. // FIXME: WHAT DeclarationName do we pass in here? if (CheckInitializerTypes(ConsArgs[0], AllocType, StartLoc, DeclarationName() /*AllocType.getAsString()*/, /*DirectInit=*/true)) return ExprError(); } else { return ExprError(Diag(StartLoc, diag::err_builtin_direct_init_more_than_one_arg) << SourceRange(ConstructorLParen, ConstructorRParen)); } } // FIXME: Also check that the destructor is accessible. (C++ 5.3.4p16) PlacementArgs.release(); ConstructorArgs.release(); return Owned(new (Context) CXXNewExpr(UseGlobal, OperatorNew, PlaceArgs, NumPlaceArgs, ParenTypeId, ArraySize, Constructor, Init, ConsArgs, NumConsArgs, OperatorDelete, ResultType, StartLoc, Init ? ConstructorRParen : SourceLocation())); }
void Sema::ActOnPragmaPack(PragmaPackKind Kind, IdentifierInfo *Name, ExprTy *alignment, SourceLocation PragmaLoc, SourceLocation LParenLoc, SourceLocation RParenLoc) { Expr *Alignment = static_cast<Expr *>(alignment); // If specified then alignment must be a "small" power of two. unsigned AlignmentVal = 0; if (Alignment) { llvm::APSInt Val; // pack(0) is like pack(), which just works out since that is what // we use 0 for in PackAttr. if (Alignment->isTypeDependent() || Alignment->isValueDependent() || !Alignment->isIntegerConstantExpr(Val, Context) || !(Val == 0 || Val.isPowerOf2()) || Val.getZExtValue() > 16) { Diag(PragmaLoc, diag::warn_pragma_pack_invalid_alignment); return; // Ignore } AlignmentVal = (unsigned) Val.getZExtValue(); } if (PackContext == 0) PackContext = new PragmaPackStack(); PragmaPackStack *Context = static_cast<PragmaPackStack*>(PackContext); switch (Kind) { case Sema::PPK_Default: // pack([n]) Context->setAlignment(AlignmentVal); break; case Sema::PPK_Show: // pack(show) // Show the current alignment, making sure to show the right value // for the default. AlignmentVal = Context->getAlignment(); // FIXME: This should come from the target. if (AlignmentVal == 0) AlignmentVal = 8; if (AlignmentVal == PackStackEntry::kMac68kAlignmentSentinel) Diag(PragmaLoc, diag::warn_pragma_pack_show) << "mac68k"; else Diag(PragmaLoc, diag::warn_pragma_pack_show) << AlignmentVal; break; case Sema::PPK_Push: // pack(push [, id] [, [n]) Context->push(Name); // Set the new alignment if specified. if (Alignment) Context->setAlignment(AlignmentVal); break; case Sema::PPK_Pop: // pack(pop [, id] [, n]) // MSDN, C/C++ Preprocessor Reference > Pragma Directives > pack: // "#pragma pack(pop, identifier, n) is undefined" if (Alignment && Name) Diag(PragmaLoc, diag::warn_pragma_pack_pop_identifer_and_alignment); // Do the pop. if (!Context->pop(Name, /*IsReset=*/false)) { // If a name was specified then failure indicates the name // wasn't found. Otherwise failure indicates the stack was // empty. Diag(PragmaLoc, diag::warn_pragma_pack_pop_failed) << (Name ? "no record matching name" : "stack empty"); // FIXME: Warn about popping named records as MSVC does. } else { // Pop succeeded, set the new alignment if specified. if (Alignment) Context->setAlignment(AlignmentVal); } break; default: assert(0 && "Invalid #pragma pack kind."); } }
/* Simplify a logical expression. Currently this eliminates extra parens and * casts, and performs basic boolean simplification according to common * identities. * * FIXME: Ideally, this should should be a full boolean expression minimiser, * returning in disjunctive normal form. */ static Expr* _simplify_boolean_expr (Expr* expr, const ASTContext& context) { expr = expr->IgnoreParens (); DEBUG ("Simplifying boolean expression of type " << expr->getStmtClassName ()); if (expr->getStmtClass () == Expr::UnaryOperatorClass) { UnaryOperator& op_expr = cast<UnaryOperator> (*expr); Expr* sub_expr = _simplify_boolean_expr (op_expr.getSubExpr (), context); if (op_expr.getOpcode () != UnaryOperatorKind::UO_LNot) { /* op S ↦ op simplify(S) */ op_expr.setSubExpr (sub_expr); return expr; } if (sub_expr->getStmtClass () == Expr::UnaryOperatorClass) { UnaryOperator& op_sub_expr = cast<UnaryOperator> (*sub_expr); Expr* sub_sub_expr = _simplify_boolean_expr ( op_sub_expr.getSubExpr (), context); if (op_sub_expr.getOpcode () == UnaryOperatorKind::UO_LNot) { /* ! ! S ↦ simplify(S) */ return sub_sub_expr; } /* ! op S ↦ ! op simplify(S) */ op_sub_expr.setSubExpr (sub_sub_expr); return expr; } else if (sub_expr->getStmtClass () == Expr::BinaryOperatorClass) { BinaryOperator& op_sub_expr = cast<BinaryOperator> (*sub_expr); Expr* lhs = _simplify_boolean_expr ( op_sub_expr.getLHS (), context); Expr* rhs = _simplify_boolean_expr ( op_sub_expr.getRHS (), context); if (op_sub_expr.getOpcode () == BinaryOperatorKind::BO_EQ || op_sub_expr.getOpcode () == BinaryOperatorKind::BO_NE) { /* ! (S1 == S2) ↦ * simplify(S1) != simplify(S2) * or * ! (S1 != S2) ↦ * simplify(S1) == simplify(S2) */ BinaryOperatorKind opcode = (op_sub_expr.getOpcode () == BinaryOperatorKind::BO_EQ) ? BinaryOperatorKind::BO_NE : BinaryOperatorKind::BO_EQ; return new (context) BinaryOperator (lhs, rhs, opcode, context.getLogicalOperationType (), VK_RValue, OK_Ordinary, SourceLocation (), false); } /* ! (S1 op S2) ↦ ! (simplify(S1) op simplify(S2)) */ op_sub_expr.setLHS (lhs); op_sub_expr.setRHS (rhs); return expr; } } else if (expr->getStmtClass () == Expr::BinaryOperatorClass) { BinaryOperator& op_expr = cast<BinaryOperator> (*expr); Expr* lhs = _simplify_boolean_expr (op_expr.getLHS (), context); Expr* rhs = _simplify_boolean_expr (op_expr.getRHS (), context); /* Guaranteed one-hot. */ bool is_and = op_expr.getOpcode () == BinaryOperatorKind::BO_LAnd; bool is_or = op_expr.getOpcode () == BinaryOperatorKind::BO_LOr; if (!is_and && !is_or) { /* S1 op S2 ↦ simplify(S1) op simplify(S2) */ op_expr.setLHS (lhs); op_expr.setRHS (rhs); return expr; } llvm::APSInt bool_expr; if (lhs->isIntegerConstantExpr (bool_expr, context)) { if (is_or && bool_expr.getBoolValue ()) { /* 1 || S2 ↦ 1 */ return new (context) IntegerLiteral (context, context.MakeIntValue (1, context.getLogicalOperationType ()), context.getLogicalOperationType (), SourceLocation ()); } else if (is_and && !bool_expr.getBoolValue ()) { /* 0 && S2 ↦ 0 */ return new (context) IntegerLiteral (context, context.MakeIntValue (0, context.getLogicalOperationType ()), context.getLogicalOperationType (), SourceLocation ()); } else { /* 1 && S2 ↦ simplify(S2) * or * 0 || S2 ↦ simplify(S2) */ return rhs; } } else if (rhs->isIntegerConstantExpr (bool_expr, context)) { if (is_or && bool_expr.getBoolValue ()) { /* S1 || 1 ↦ 1 */ return new (context) IntegerLiteral (context, context.MakeIntValue (1, context.getLogicalOperationType ()), context.getLogicalOperationType (), SourceLocation ()); } else if (is_and && !bool_expr.getBoolValue ()) { /* S2 && 0 ↦ 0 */ return new (context) IntegerLiteral (context, context.MakeIntValue (0, context.getLogicalOperationType ()), context.getLogicalOperationType (), SourceLocation ()); } else { /* S1 && 1 ↦ simplify(S1) * or * S1 || 0 ↦ simplify(S1) */ return lhs; } } /* S1 op S2 ↦ simplify(S1) op simplify(S2) */ op_expr.setLHS (lhs); op_expr.setRHS (rhs); return expr; } return expr; }
/* Does the given statement look like: * • g_return_if_fail(…) * • g_return_val_if_fail(…) * • g_assert(…) * • g_assert_*(…) * • assert(…) * This is complicated by the fact that if the gmessages.h header isn’t * available, they’ll present as CallExpr function calls with those names; if it * is available, they’ll be expanded as macros and turn into DoStmts with misc. * rubbish beneath. * * If the statement changes program state at all, return NULL. Otherwise, return * the condition which holds for the assertion to be bypassed (i.e. for the * assertion to succeed). This function is built recursively, building a boolean * expression for the condition based on avoiding branches which call * abort()-like functions. * * This function is based on a transformation of the AST to an augmented boolean * expression, using rules documented in each switch case. In this * documentation, calc(S) refers to the transformation function. The augmented * boolean expressions can be either NULL, or a normal boolean expression * (TRUE, FALSE, ∧, ∨, ¬). NULL is used iff the statement potentially changes * program state, and poisons any boolean expression: * B ∧ NULL ≡ NULL * B ∨ NULL ≡ NULL * ¬NULL ≡ NULL */ Expr* AssertionExtracter::is_assertion_stmt (Stmt& stmt, const ASTContext& context) { DEBUG ("Checking " << stmt.getStmtClassName () << " for assertions."); /* Slow path: walk through the AST, aborting on statements which * potentially mutate program state, and otherwise trying to find a base * function call such as: * • g_return_if_fail_warning() * • g_assertion_message() * • g_assertion_message_*() */ switch ((int) stmt.getStmtClass ()) { case Stmt::StmtClass::CallExprClass: { /* Handle a direct function call. * Transformations: * [g_return_if_fail|assert|…](C) ↦ C * [g_return_if_fail_warning|__assert_fail|…](C) ↦ FALSE * other_funcs(…) ↦ NULL */ CallExpr& call_expr = cast<CallExpr> (stmt); FunctionDecl* func = call_expr.getDirectCallee (); if (func == NULL) return NULL; std::string func_name = func->getNameAsString (); DEBUG ("CallExpr to function " << func_name); if (_is_assertion_name (func_name)) { /* Assertion path where the compiler hasn't seen the * definition of the assertion macro, so still thinks * it's a function. * * Extract the assertion condition as the first function * parameter. * * TODO: May need to fix up the condition for macros * like g_assert_null(). */ return call_expr.getArg (0); } else if (_is_assertion_fail_func_name (func_name)) { /* Assertion path where the assertion macro has been * expanded and we're on the assertion failure branch. * * In this case, the assertion condition has been * grabbed from an if statement already, so negate it * (to avoid the failure condition) and return. */ return new (context) IntegerLiteral (context, context.MakeIntValue (0, context.getLogicalOperationType ()), context.getLogicalOperationType (), SourceLocation ()); } /* Not an assertion path. */ return NULL; } case Stmt::StmtClass::DoStmtClass: { /* Handle a do { … } while (0) block (commonly used to allow * macros to optionally be suffixed by a semicolon). * Transformations: * do { S } while (0) ↦ calc(S) * do { S } while (C) ↦ NULL * Note the second condition is overly-conservative. No * solutions for the halting problem here. */ DoStmt& do_stmt = cast<DoStmt> (stmt); Stmt* body = do_stmt.getBody (); Stmt* cond = do_stmt.getCond (); Expr* expr = dyn_cast<Expr> (cond); llvm::APSInt bool_expr; if (body != NULL && expr != NULL && expr->isIntegerConstantExpr (bool_expr, context) && !bool_expr.getBoolValue ()) { return is_assertion_stmt (*body, context); } return NULL; } case Stmt::StmtClass::IfStmtClass: { /* Handle an if(…) { … } else { … } block. * Transformations: * if (C) { S1 } else { S2 } ↦ * (C ∧ calc(S1)) ∨ (¬C ∧ calc(S2)) * if (C) { S } ↦ (C ∧ calc(S)) ∨ ¬C * i.e. * if (C) { S } ≡ if (C) { S } else {} * where {} is an empty compound statement, below. */ IfStmt& if_stmt = cast<IfStmt> (stmt); assert (if_stmt.getThen () != NULL); Expr* neg_cond = _negation_expr (if_stmt.getCond (), context); Expr* then_assertion = is_assertion_stmt (*(if_stmt.getThen ()), context); if (then_assertion == NULL) return NULL; then_assertion = _conjunction_expr (if_stmt.getCond (), then_assertion, context); if (if_stmt.getElse () == NULL) return _disjunction_expr (then_assertion, neg_cond, context); Expr* else_assertion = is_assertion_stmt (*(if_stmt.getElse ()), context); if (else_assertion == NULL) return NULL; else_assertion = _conjunction_expr (neg_cond, else_assertion, context); return _disjunction_expr (then_assertion, else_assertion, context); } case Stmt::StmtClass::ConditionalOperatorClass: { /* Handle a ternary operator. * Transformations: * C ? S1 : S2 ↦ * (C ∧ calc(S1)) ∨ (¬C ∧ calc(S2)) */ ConditionalOperator& op_expr = cast<ConditionalOperator> (stmt); assert (op_expr.getTrueExpr () != NULL); assert (op_expr.getFalseExpr () != NULL); Expr* neg_cond = _negation_expr (op_expr.getCond (), context); Expr* then_assertion = is_assertion_stmt (*(op_expr.getTrueExpr ()), context); if (then_assertion == NULL) return NULL; then_assertion = _conjunction_expr (op_expr.getCond (), then_assertion, context); Expr* else_assertion = is_assertion_stmt (*(op_expr.getFalseExpr ()), context); if (else_assertion == NULL) return NULL; else_assertion = _conjunction_expr (neg_cond, else_assertion, context); return _disjunction_expr (then_assertion, else_assertion, context); } case Stmt::StmtClass::SwitchStmtClass: { /* Handle a switch statement. * Transformations: * switch (C) { L1: S1; L2: S2; …; Lz: Sz } ↦ NULL * FIXME: This should get a proper transformation sometime. */ return NULL; } case Stmt::StmtClass::AttributedStmtClass: { /* Handle an attributed statement, e.g. G_LIKELY(…). * Transformations: * att S ↦ calc(S) */ AttributedStmt& attr_stmt = cast<AttributedStmt> (stmt); Stmt* sub_stmt = attr_stmt.getSubStmt (); if (sub_stmt == NULL) return NULL; return is_assertion_stmt (*sub_stmt, context); } case Stmt::StmtClass::CompoundStmtClass: { /* Handle a compound statement, e.g. { stmt1; stmt2; }. * Transformations: * S1; S2; …; Sz ↦ calc(S1) ∧ calc(S2) ∧ … ∧ calc(Sz) * {} ↦ TRUE * * This is implemented by starting with a base TRUE case in the * compound_condition, then taking the conjunction with the next * statement’s assertion condition for each statement in the * compound. * * If the compound is empty, the compound_condition will be * TRUE. Otherwise, it will be (TRUE ∧ …), which will be * simplified later. */ CompoundStmt& compound_stmt = cast<CompoundStmt> (stmt); Expr* compound_condition = new (context) IntegerLiteral (context, context.MakeIntValue (1, context.getLogicalOperationType ()), context.getLogicalOperationType (), SourceLocation ()); for (CompoundStmt::const_body_iterator it = compound_stmt.body_begin (), ie = compound_stmt.body_end (); it != ie; ++it) { Stmt* body_stmt = *it; Expr* body_assertion = is_assertion_stmt (*body_stmt, context); if (body_assertion == NULL) { /* Reached a program state mutation. */ return NULL; } /* Update the compound condition. */ compound_condition = _conjunction_expr (compound_condition, body_assertion, context); DEBUG_EXPR ("Compound condition: ", *compound_condition); } return compound_condition; } case Stmt::StmtClass::GotoStmtClass: /* Handle a goto statement. * Transformations: * goto L ↦ FALSE */ case Stmt::StmtClass::ReturnStmtClass: { /* Handle a return statement. * Transformations: * return ↦ FALSE */ return new (context) IntegerLiteral (context, context.MakeIntValue (0, context.getLogicalOperationType ()), context.getLogicalOperationType (), SourceLocation ()); } case Stmt::StmtClass::NullStmtClass: /* Handle a null statement. * Transformations: * ; ↦ TRUE */ case Stmt::StmtClass::DeclRefExprClass: /* Handle a variable reference expression. These don’t modify * program state. * Transformations: * E ↦ TRUE */ case Stmt::StmtClass::DeclStmtClass: { /* Handle a variable declaration statement. These don’t modify * program state; they only introduce new state, so can’t affect * subsequent assertions. (FIXME: For the moment, we ignore the * possibility of the rvalue modifying program state.) * Transformations: * T S1 ↦ TRUE * T S1 = S2 ↦ TRUE */ return new (context) IntegerLiteral (context, context.MakeIntValue (1, context.getLogicalOperationType ()), context.getLogicalOperationType (), SourceLocation ()); } case Stmt::StmtClass::IntegerLiteralClass: { /* Handle an integer literal. This doesn’t modify program state, * and evaluates directly to a boolean. * Transformations: * 0 ↦ FALSE * I ↦ TRUE */ return dyn_cast<Expr> (&stmt); } case Stmt::StmtClass::ParenExprClass: { /* Handle a parenthesised expression. * Transformations: * ( S ) ↦ calc(S) */ ParenExpr& paren_expr = cast<ParenExpr> (stmt); Stmt* sub_expr = paren_expr.getSubExpr (); if (sub_expr == NULL) return NULL; return is_assertion_stmt (*sub_expr, context); } case Stmt::StmtClass::LabelStmtClass: { /* Handle a label statement. * Transformations: * label: S ↦ calc(S) */ LabelStmt& label_stmt = cast<LabelStmt> (stmt); Stmt* sub_stmt = label_stmt.getSubStmt (); if (sub_stmt == NULL) return NULL; return is_assertion_stmt (*sub_stmt, context); } case Stmt::StmtClass::ImplicitCastExprClass: case Stmt::StmtClass::CStyleCastExprClass: { /* Handle an explicit or implicit cast. * Transformations: * (T) S ↦ calc(S) */ CastExpr& cast_expr = cast<CastExpr> (stmt); Stmt* sub_expr = cast_expr.getSubExpr (); if (sub_expr == NULL) return NULL; return is_assertion_stmt (*sub_expr, context); } case Stmt::StmtClass::GCCAsmStmtClass: case Stmt::StmtClass::MSAsmStmtClass: /* Inline assembly. There is no way we are parsing this, so * conservatively assume it modifies program state. * Transformations: * A ↦ NULL */ case Stmt::StmtClass::BinaryOperatorClass: /* Handle a binary operator statement. Since this is being * processed at the top level, it’s most likely an assignment, * so conservatively assume it modifies program state. * Transformations: * S1 op S2 ↦ NULL */ case Stmt::StmtClass::UnaryOperatorClass: /* Handle a unary operator statement. Since this is being * processed at the top level, it’s not very interesting re. * assertions, even though it probably won’t modify program * state (unless it’s a pre- or post-increment or -decrement * operator). Be conservative and assume it does, though. * Transformations: * op S ↦ NULL */ case Stmt::StmtClass::CompoundAssignOperatorClass: /* Handle a compound assignment operator, e.g. x += 5. This * definitely modifies program state, so ignore it. * Transformations: * S1 op S2 ↦ NULL */ case Stmt::StmtClass::ForStmtClass: /* Handle a for statement. We assume these *always* change * program state. * Transformations: * for (…) { … } ↦ NULL */ case Stmt::StmtClass::WhileStmtClass: { /* Handle a while(…) { … } block. Because we don't want to solve * the halting problem, just assume all while statements cannot * be assertion statements. * Transformations: * while (C) { S } ↦ NULL */ return NULL; } case Stmt::StmtClass::NoStmtClass: default: WARN_EXPR (__func__ << "() can’t handle statements of type " << stmt.getStmtClassName (), stmt); return NULL; } }