r314571 - [Analyzer] Synthesize function body for std::call_once

Alexander Kornienko via cfe-commits cfe-commits at lists.llvm.org
Mon Oct 9 05:54:36 PDT 2017


Bugzilla is not accessible, so here's a reduced test case:
$ cat test-clang__BodyFarm__getBody.cc
namespace std {
template <typename d, typename e>
void call_once(d, e);
}
void g();
void f() {
  std::call_once(g, false);
}
$ clang-tidy -checks=-*,clang-analyzer* test-clang__BodyFarm__getBody.cc --
-std=c++11 -w
*** SIGSEGV; stack trace: ***
PC: @          0x2f96e3a  (unknown)  clang::DeclContext::lookup()
    @          0x532bb51       1152  FailureSignalHandler()
    @     0x7f42b341f9a0  (unknown)  (unknown)
    @          0x2ae74c5        576  (anonymous
namespace)::ASTMaker::findMemberField()
    @          0x2ae59fc        704  create_call_once()
    @          0x2ae5040        784  clang::BodyFarm::getBody()
    @          0x2ac67af        144  clang::AnalysisDeclContext::getBody()
    @          0x1c499fa        128
 clang::ento::AnyFunctionCall::getRuntimeDefinition()
    @          0x1cc6146        320
 clang::ento::ExprEngine::defaultEvalCall()
    @          0x1c61033        464
 clang::ento::CheckerManager::runCheckersForEvalCall()
    @          0x1cc4fc9        352  clang::ento::ExprEngine::evalCall()
    @          0x1cc4e8c        432
 clang::ento::ExprEngine::VisitCallExpr()
    @          0x1c7da94       4000  clang::ento::ExprEngine::Visit()
    @          0x1c7a821        496  clang::ento::ExprEngine::ProcessStmt()
    @          0x1c7a4da        240
 clang::ento::ExprEngine::processCFGElement()
    @          0x1ca8ed6        128
 clang::ento::CoreEngine::HandlePostStmt()
    @          0x1ca87d6        496
 clang::ento::CoreEngine::dispatchWorkItem()
    @          0x1ca8338        544
 clang::ento::CoreEngine::ExecuteWorkList()
    @           0xf954d5         80
 clang::ento::ExprEngine::ExecuteWorkList()
    @           0xf3c612       1056  (anonymous
namespace)::AnalysisConsumer::ActionExprEngine()
    @           0xf3c3d1         80  (anonymous
namespace)::AnalysisConsumer::RunPathSensitiveChecks()
    @           0xf3c095        288  (anonymous
namespace)::AnalysisConsumer::HandleCode()
    @           0xf2f6e3        416  (anonymous
namespace)::AnalysisConsumer::HandleDeclsCallGraph()
    @           0xf2d967        336  (anonymous
namespace)::AnalysisConsumer::HandleTranslationUnit()
    @          0x1366eae         80
 clang::MultiplexConsumer::HandleTranslationUnit()
    @          0x1dc6ae6        288  clang::ParseAST()
    @          0x135475a         80
 clang::ASTFrontendAction::ExecuteAction()
    @          0x13541f0        112  clang::FrontendAction::Execute()
    @          0x1169822        496
 clang::CompilerInstance::ExecuteAction()
    @          0x1032ba2        464
 clang::tooling::FrontendActionFactory::runInvocation()
    @          0x1032a43        160
 clang::tooling::ToolInvocation::runInvocation()
    @          0x1031306       1840  clang::tooling::ToolInvocation::run()
    @          0x1033c30       1664  clang::tooling::ClangTool::run()

On Sat, Oct 7, 2017 at 12:56 PM, Alexander Kornienko <alexfh at google.com>
wrote:

> This revision might be the cause of https://bugs.llvm.org/show_
> bug.cgi?id=34869. I'm still working on a reduced test case.
>
> On Sat, Sep 30, 2017 at 2:03 AM, George Karpenkov via cfe-commits <
> cfe-commits at lists.llvm.org> wrote:
>
>> Author: george.karpenkov
>> Date: Fri Sep 29 17:03:22 2017
>> New Revision: 314571
>>
>> URL: http://llvm.org/viewvc/llvm-project?rev=314571&view=rev
>> Log:
>> [Analyzer] Synthesize function body for std::call_once
>>
>> Differential Revision: https://reviews.llvm.org/D37840
>>
>> Added:
>>     cfe/trunk/test/Analysis/call_once.cpp
>> Modified:
>>     cfe/trunk/lib/Analysis/BodyFarm.cpp
>>
>> Modified: cfe/trunk/lib/Analysis/BodyFarm.cpp
>> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/
>> BodyFarm.cpp?rev=314571&r1=314570&r2=314571&view=diff
>> ============================================================
>> ==================
>> --- cfe/trunk/lib/Analysis/BodyFarm.cpp (original)
>> +++ cfe/trunk/lib/Analysis/BodyFarm.cpp Fri Sep 29 17:03:22 2017
>> @@ -14,11 +14,18 @@
>>
>>  #include "BodyFarm.h"
>>  #include "clang/AST/ASTContext.h"
>> +#include "clang/AST/CXXInheritance.h"
>>  #include "clang/AST/Decl.h"
>>  #include "clang/AST/Expr.h"
>> +#include "clang/AST/ExprCXX.h"
>>  #include "clang/AST/ExprObjC.h"
>> +#include "clang/AST/NestedNameSpecifier.h"
>>  #include "clang/Analysis/CodeInjector.h"
>> +#include "clang/Basic/OperatorKinds.h"
>>  #include "llvm/ADT/StringSwitch.h"
>> +#include "llvm/Support/Debug.h"
>> +
>> +#define DEBUG_TYPE "body-farm"
>>
>>  using namespace clang;
>>
>> @@ -55,7 +62,9 @@ public:
>>    CompoundStmt *makeCompound(ArrayRef<Stmt*>);
>>
>>    /// Create a new DeclRefExpr for the referenced variable.
>> -  DeclRefExpr *makeDeclRefExpr(const VarDecl *D);
>> +  DeclRefExpr *makeDeclRefExpr(const VarDecl *D,
>> +                               bool RefersToEnclosingVariableOrCapture
>> = false,
>> +                               bool GetNonReferenceType = false);
>>
>>    /// Create a new UnaryOperator representing a dereference.
>>    UnaryOperator *makeDereference(const Expr *Arg, QualType Ty);
>> @@ -66,9 +75,24 @@ public:
>>    /// Create an implicit cast to a builtin boolean type.
>>    ImplicitCastExpr *makeIntegralCastToBoolean(const Expr *Arg);
>>
>> -  // Create an implicit cast for lvalue-to-rvaluate conversions.
>> +  /// Create an implicit cast for lvalue-to-rvaluate conversions.
>>    ImplicitCastExpr *makeLvalueToRvalue(const Expr *Arg, QualType Ty);
>>
>> +  /// Create an implicit cast for lvalue-to-rvaluate conversions.
>> +  ImplicitCastExpr *makeLvalueToRvalue(const Expr *Arg,
>> +                                       bool GetNonReferenceType = false);
>> +
>> +  /// Make RValue out of variable declaration, creating a temporary
>> +  /// DeclRefExpr in the process.
>> +  ImplicitCastExpr *
>> +  makeLvalueToRvalue(const VarDecl *Decl,
>> +                     bool RefersToEnclosingVariableOrCapture = false,
>> +                     bool GetNonReferenceType = false);
>> +
>> +  /// Create an implicit cast of the given type.
>> +  ImplicitCastExpr *makeImplicitCast(const Expr *Arg, QualType Ty,
>> +                                     CastKind CK = CK_LValueToRValue);
>> +
>>    /// Create an Objective-C bool literal.
>>    ObjCBoolLiteralExpr *makeObjCBool(bool Val);
>>
>> @@ -78,6 +102,18 @@ public:
>>    /// Create a Return statement.
>>    ReturnStmt *makeReturn(const Expr *RetVal);
>>
>> +  /// Create an integer literal.
>> +  IntegerLiteral *makeIntegerLiteral(uint64_t value);
>> +
>> +  /// Create a member expression.
>> +  MemberExpr *makeMemberExpression(Expr *base, ValueDecl *MemberDecl,
>> +                                   bool IsArrow = false,
>> +                                   ExprValueKind ValueKind = VK_LValue);
>> +
>> +  /// Returns a *first* member field of a record declaration with a
>> given name.
>> +  /// \return an nullptr if no member with such a name exists.
>> +  NamedDecl *findMemberField(const CXXRecordDecl *RD, StringRef Name);
>> +
>>  private:
>>    ASTContext &C;
>>  };
>> @@ -106,16 +142,16 @@ CompoundStmt *ASTMaker::makeCompound(Arr
>>    return new (C) CompoundStmt(C, Stmts, SourceLocation(),
>> SourceLocation());
>>  }
>>
>> -DeclRefExpr *ASTMaker::makeDeclRefExpr(const VarDecl *D) {
>> -  DeclRefExpr *DR =
>> -    DeclRefExpr::Create(/* Ctx = */ C,
>> -                        /* QualifierLoc = */ NestedNameSpecifierLoc(),
>> -                        /* TemplateKWLoc = */ SourceLocation(),
>> -                        /* D = */ const_cast<VarDecl*>(D),
>> -                        /* RefersToEnclosingVariableOrCapture = */
>> false,
>> -                        /* NameLoc = */ SourceLocation(),
>> -                        /* T = */ D->getType(),
>> -                        /* VK = */ VK_LValue);
>> +DeclRefExpr *ASTMaker::makeDeclRefExpr(const VarDecl *D,
>> +                                       bool
>> RefersToEnclosingVariableOrCapture,
>> +                                       bool GetNonReferenceType) {
>> +  auto Type = D->getType();
>> +  if (GetNonReferenceType)
>> +    Type = Type.getNonReferenceType();
>> +
>> +  DeclRefExpr *DR = DeclRefExpr::Create(
>> +      C, NestedNameSpecifierLoc(), SourceLocation(), const_cast<VarDecl
>> *>(D),
>> +      RefersToEnclosingVariableOrCapture, SourceLocation(), Type,
>> VK_LValue);
>>    return DR;
>>  }
>>
>> @@ -125,8 +161,38 @@ UnaryOperator *ASTMaker::makeDereference
>>  }
>>
>>  ImplicitCastExpr *ASTMaker::makeLvalueToRvalue(const Expr *Arg,
>> QualType Ty) {
>> -  return ImplicitCastExpr::Create(C, Ty, CK_LValueToRValue,
>> -                                  const_cast<Expr*>(Arg), nullptr,
>> VK_RValue);
>> +  return makeImplicitCast(Arg, Ty, CK_LValueToRValue);
>> +}
>> +
>> +ImplicitCastExpr *ASTMaker::makeLvalueToRvalue(const Expr *Arg,
>> +                                               bool GetNonReferenceType)
>> {
>> +
>> +  QualType Type = Arg->getType();
>> +  if (GetNonReferenceType)
>> +    Type = Type.getNonReferenceType();
>> +  return makeImplicitCast(Arg, Type, CK_LValueToRValue);
>> +}
>> +
>> +ImplicitCastExpr *
>> +ASTMaker::makeLvalueToRvalue(const VarDecl *Arg,
>> +                             bool RefersToEnclosingVariableOrCapture,
>> +                             bool GetNonReferenceType) {
>> +  auto Type = Arg->getType();
>> +  if (GetNonReferenceType)
>> +    Type = Type.getNonReferenceType();
>> +  return makeLvalueToRvalue(makeDeclRefExpr(Arg,
>> +
>> RefersToEnclosingVariableOrCapture,
>> +                                            GetNonReferenceType),
>> +                            Type);
>> +}
>> +
>> +ImplicitCastExpr *ASTMaker::makeImplicitCast(const Expr *Arg, QualType
>> Ty,
>> +                                             CastKind CK) {
>> +  return ImplicitCastExpr::Create(C, Ty,
>> +                                  /* CastKind= */ CK,
>> +                                  /* Expr= */ const_cast<Expr *>(Arg),
>> +                                  /* CXXCastPath= */ nullptr,
>> +                                  /* ExprValueKind= */ VK_RValue);
>>  }
>>
>>  Expr *ASTMaker::makeIntegralCast(const Expr *Arg, QualType Ty) {
>> @@ -161,12 +227,196 @@ ReturnStmt *ASTMaker::makeReturn(const E
>>                              nullptr);
>>  }
>>
>> +IntegerLiteral *ASTMaker::makeIntegerLiteral(uint64_t value) {
>> +  return IntegerLiteral::Create(C,
>> +                                llvm::APInt(
>> +                                    /*numBits=*/C.getTypeSize(C.IntTy),
>> value),
>> +                                /*QualType=*/C.IntTy, SourceLocation());
>> +}
>> +
>> +MemberExpr *ASTMaker::makeMemberExpression(Expr *base, ValueDecl
>> *MemberDecl,
>> +                                           bool IsArrow,
>> +                                           ExprValueKind ValueKind) {
>> +
>> +  DeclAccessPair FoundDecl = DeclAccessPair::make(MemberDecl,
>> AS_public);
>> +  return MemberExpr::Create(
>> +      C, base, IsArrow, SourceLocation(), NestedNameSpecifierLoc(),
>> +      SourceLocation(), MemberDecl, FoundDecl,
>> +      DeclarationNameInfo(MemberDecl->getDeclName(), SourceLocation()),
>> +      /* TemplateArgumentListInfo= */ nullptr, MemberDecl->getType(),
>> ValueKind,
>> +      OK_Ordinary);
>> +}
>> +
>> +NamedDecl *ASTMaker::findMemberField(const CXXRecordDecl *RD, StringRef
>> Name) {
>> +
>> +  CXXBasePaths Paths(
>> +      /* FindAmbiguities=*/false,
>> +      /* RecordPaths=*/false,
>> +      /* DetectVirtual= */ false);
>> +  const IdentifierInfo &II = C.Idents.get(Name);
>> +  DeclarationName DeclName = C.DeclarationNames.getIdentifier(&II);
>> +
>> +  DeclContextLookupResult Decls = RD->lookup(DeclName);
>> +  for (NamedDecl *FoundDecl : Decls)
>> +    if (!FoundDecl->getDeclContext()->isFunctionOrMethod())
>> +      return FoundDecl;
>> +
>> +  return nullptr;
>> +}
>> +
>>  //===------------------------------------------------------
>> ----------------===//
>>  // Creation functions for faux ASTs.
>>  //===------------------------------------------------------
>> ----------------===//
>>
>>  typedef Stmt *(*FunctionFarmer)(ASTContext &C, const FunctionDecl *D);
>>
>> +static CallExpr *
>> +create_call_once_funcptr_call(ASTContext &C, ASTMaker M,
>> +                              const ParmVarDecl *Callback,
>> +                              SmallVectorImpl<Expr *> &CallArgs) {
>> +
>> +  return new (C) CallExpr(
>> +      /*ASTContext=*/C,
>> +      /*StmtClass=*/M.makeLvalueToRvalue(/*Expr=*/Callback),
>> +      /*args=*/CallArgs,
>> +      /*QualType=*/C.VoidTy,
>> +      /*ExprValueType=*/VK_RValue,
>> +      /*SourceLocation=*/SourceLocation());
>> +}
>> +
>> +static CallExpr *
>> +create_call_once_lambda_call(ASTContext &C, ASTMaker M,
>> +                             const ParmVarDecl *Callback, QualType
>> CallbackType,
>> +                             SmallVectorImpl<Expr *> &CallArgs) {
>> +
>> +  CXXRecordDecl *CallbackDecl = CallbackType->getAsCXXRecordDecl();
>> +
>> +  assert(CallbackDecl != nullptr);
>> +  assert(CallbackDecl->isLambda());
>> +  FunctionDecl *callOperatorDecl = CallbackDecl->getLambdaCallOpe
>> rator();
>> +  assert(callOperatorDecl != nullptr);
>> +
>> +  DeclRefExpr *callOperatorDeclRef =
>> +      DeclRefExpr::Create(/* Ctx = */ C,
>> +                          /* QualifierLoc = */ NestedNameSpecifierLoc(),
>> +                          /* TemplateKWLoc = */ SourceLocation(),
>> +                          const_cast<FunctionDecl *>(callOperatorDecl),
>> +                          /* RefersToEnclosingVariableOrCapture= */
>> false,
>> +                          /* NameLoc = */ SourceLocation(),
>> +                          /* T = */ callOperatorDecl->getType(),
>> +                          /* VK = */ VK_LValue);
>> +
>> +  CallArgs.insert(
>> +      CallArgs.begin(),
>> +      M.makeDeclRefExpr(Callback,
>> +                        /* RefersToEnclosingVariableOrCapture= */ true,
>> +                        /* GetNonReferenceType= */ true));
>> +
>> +  return new (C)
>> +      CXXOperatorCallExpr(/*AstContext=*/C, OO_Call,
>> callOperatorDeclRef,
>> +                          /*args=*/CallArgs,
>> +                          /*QualType=*/C.VoidTy,
>> +                          /*ExprValueType=*/VK_RValue,
>> +                          /*SourceLocation=*/SourceLocation(),
>> FPOptions());
>> +}
>> +
>> +/// Create a fake body for std::call_once.
>> +/// Emulates the following function body:
>> +///
>> +/// \code
>> +/// typedef struct once_flag_s {
>> +///   unsigned long __state = 0;
>> +/// } once_flag;
>> +/// template<class Callable>
>> +/// void call_once(once_flag& o, Callable func) {
>> +///   if (!o.__state) {
>> +///     func();
>> +///   }
>> +///   o.__state = 1;
>> +/// }
>> +/// \endcode
>> +static Stmt *create_call_once(ASTContext &C, const FunctionDecl *D) {
>> +  DEBUG(llvm::dbgs() << "Generating body for call_once\n");
>> +
>> +  // We need at least two parameters.
>> +  if (D->param_size() < 2)
>> +    return nullptr;
>> +
>> +  ASTMaker M(C);
>> +
>> +  const ParmVarDecl *Flag = D->getParamDecl(0);
>> +  const ParmVarDecl *Callback = D->getParamDecl(1);
>> +  QualType CallbackType = Callback->getType().getNonReferenceType();
>> +
>> +  SmallVector<Expr *, 5> CallArgs;
>> +
>> +  // All arguments past first two ones are passed to the callback.
>> +  for (unsigned int i = 2; i < D->getNumParams(); i++)
>> +    CallArgs.push_back(M.makeLvalueToRvalue(D->getParamDecl(i)));
>> +
>> +  CallExpr *CallbackCall;
>> +  if (CallbackType->getAsCXXRecordDecl() &&
>> +      CallbackType->getAsCXXRecordDecl()->isLambda()) {
>> +
>> +    CallbackCall =
>> +        create_call_once_lambda_call(C, M, Callback, CallbackType,
>> CallArgs);
>> +  } else {
>> +
>> +    // Function pointer case.
>> +    CallbackCall = create_call_once_funcptr_call(C, M, Callback,
>> CallArgs);
>> +  }
>> +
>> +  QualType FlagType = Flag->getType().getNonReferenceType();
>> +  DeclRefExpr *FlagDecl =
>> +      M.makeDeclRefExpr(Flag,
>> +                        /* RefersToEnclosingVariableOrCapture=*/true,
>> +                        /* GetNonReferenceType=*/true);
>> +
>> +  CXXRecordDecl *FlagCXXDecl = FlagType->getAsCXXRecordDecl();
>> +
>> +  // Note: here we are assuming libc++ implementation of call_once,
>> +  // which has a struct with a field `__state_`.
>> +  // Body farming might not work for other `call_once` implementations.
>> +  NamedDecl *FoundDecl = M.findMemberField(FlagCXXDecl, "__state_");
>> +  ValueDecl *FieldDecl;
>> +  if (FoundDecl) {
>> +    FieldDecl = dyn_cast<ValueDecl>(FoundDecl);
>> +  } else {
>> +    DEBUG(llvm::dbgs() << "No field __state_ found on std::once_flag
>> struct, "
>> +                       << "unable to synthesize call_once body, ignoring
>> "
>> +                       << "the call.\n");
>> +    return nullptr;
>> +  }
>> +
>> +  MemberExpr *Deref = M.makeMemberExpression(FlagDecl, FieldDecl);
>> +  assert(Deref->isLValue());
>> +  QualType DerefType = Deref->getType();
>> +
>> +  // Negation predicate.
>> +  UnaryOperator *FlagCheck = new (C) UnaryOperator(
>> +      /* input= */
>> +      M.makeImplicitCast(M.makeLvalueToRvalue(Deref, DerefType),
>> DerefType,
>> +                         CK_IntegralToBoolean),
>> +      /* opc= */ UO_LNot,
>> +      /* QualType= */ C.IntTy,
>> +      /* ExprValueKind= */ VK_RValue,
>> +      /* ExprObjectKind= */ OK_Ordinary, SourceLocation());
>> +
>> +  // Create assignment.
>> +  BinaryOperator *FlagAssignment = M.makeAssignment(
>> +      Deref, M.makeIntegralCast(M.makeIntegerLiteral(1), DerefType),
>> DerefType);
>> +
>> +  IfStmt *Out = new (C)
>> +      IfStmt(C, SourceLocation(),
>> +             /* IsConstexpr= */ false,
>> +             /* init= */ nullptr,
>> +             /* var= */ nullptr,
>> +             /* cond= */ FlagCheck,
>> +             /* then= */ M.makeCompound({CallbackCall, FlagAssignment}));
>> +
>> +  return Out;
>> +}
>> +
>>  /// Create a fake body for dispatch_once.
>>  static Stmt *create_dispatch_once(ASTContext &C, const FunctionDecl *D)
>> {
>>    // Check if we have at least two parameters.
>> @@ -202,15 +452,17 @@ static Stmt *create_dispatch_once(ASTCon
>>    ASTMaker M(C);
>>
>>    // (1) Create the call.
>> -  DeclRefExpr *DR = M.makeDeclRefExpr(Block);
>> -  ImplicitCastExpr *ICE = M.makeLvalueToRvalue(DR, Ty);
>> -  CallExpr *CE = new (C) CallExpr(C, ICE, None, C.VoidTy, VK_RValue,
>> -                                  SourceLocation());
>> +  CallExpr *CE = new (C) CallExpr(
>> +      /*ASTContext=*/C,
>> +      /*StmtClass=*/M.makeLvalueToRvalue(/*Expr=*/Block),
>> +      /*args=*/None,
>> +      /*QualType=*/C.VoidTy,
>> +      /*ExprValueType=*/VK_RValue,
>> +      /*SourceLocation=*/SourceLocation());
>>
>>    // (2) Create the assignment to the predicate.
>> -  IntegerLiteral *IL =
>> -    IntegerLiteral::Create(C, llvm::APInt(C.getTypeSize(C.IntTy),
>> (uint64_t) 1),
>> -                           C.IntTy, SourceLocation());
>> +  IntegerLiteral *IL = M.makeIntegerLiteral(1);
>> +
>>    BinaryOperator *B =
>>      M.makeAssignment(
>>         M.makeDereference(
>> @@ -234,13 +486,20 @@ static Stmt *create_dispatch_once(ASTCon
>>          PredicateTy),
>>      PredicateTy);
>>
>> -  UnaryOperator *UO = new (C) UnaryOperator(LValToRval, UO_LNot, C.IntTy,
>> -                                           VK_RValue, OK_Ordinary,
>> -                                           SourceLocation());
>> +  UnaryOperator *UO = new (C) UnaryOperator(
>> +      /* input= */ LValToRval,
>> +      /* opc= */ UO_LNot,
>> +      /* QualType= */ C.IntTy,
>> +      /* ExprValueKind= */ VK_RValue,
>> +      /* ExprObjectKind= */ OK_Ordinary, SourceLocation());
>>
>>    // (5) Create the 'if' statement.
>> -  IfStmt *If = new (C) IfStmt(C, SourceLocation(), false, nullptr,
>> nullptr,
>> -                              UO, CS);
>> +  IfStmt *If = new (C) IfStmt(C, SourceLocation(),
>> +                              /* IsConstexpr= */ false,
>> +                              /* init= */ nullptr,
>> +                              /* var= */ nullptr,
>> +                              /* cond= */ UO,
>> +                              /* then= */ CS);
>>    return If;
>>  }
>>
>> @@ -370,8 +629,9 @@ Stmt *BodyFarm::getBody(const FunctionDe
>>    if (Name.startswith("OSAtomicCompareAndSwap") ||
>>        Name.startswith("objc_atomicCompareAndSwap")) {
>>      FF = create_OSAtomicCompareAndSwap;
>> -  }
>> -  else {
>> +  } else if (Name == "call_once" && D->getDeclContext()->isStdNamespace())
>> {
>> +    FF = create_call_once;
>> +  } else {
>>      FF = llvm::StringSwitch<FunctionFarmer>(Name)
>>            .Case("dispatch_sync", create_dispatch_sync)
>>            .Case("dispatch_once", create_dispatch_once)
>>
>> Added: cfe/trunk/test/Analysis/call_once.cpp
>> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/
>> call_once.cpp?rev=314571&view=auto
>> ============================================================
>> ==================
>> --- cfe/trunk/test/Analysis/call_once.cpp (added)
>> +++ cfe/trunk/test/Analysis/call_once.cpp Fri Sep 29 17:03:22 2017
>> @@ -0,0 +1,233 @@
>> +// RUN: %clang_analyze_cc1 -std=c++11 -fblocks
>> -analyzer-checker=core,debug.ExprInspection -w -verify %s
>> +
>> +void clang_analyzer_eval(bool);
>> +
>> +// Faking std::std::call_once implementation.
>> +namespace std {
>> +typedef struct once_flag_s {
>> +  unsigned long __state_ = 0;
>> +} once_flag;
>> +
>> +template <class Callable, class... Args>
>> +void call_once(once_flag &o, Callable func, Args... args);
>> +} // namespace std
>> +
>> +// Check with Lambdas.
>> +void test_called_warning() {
>> +  std::once_flag g_initialize;
>> +  int z;
>> +
>> +  std::call_once(g_initialize, [&] {
>> +    int *x = nullptr;
>> +    int y = *x; // expected-warning{{Dereference of null pointer (loaded
>> from variable 'x')}}
>> +    z = 200;
>> +  });
>> +}
>> +
>> +void test_called_on_path_inside_no_warning() {
>> +  std::once_flag g_initialize;
>> +
>> +  int *x = nullptr;
>> +  int y = 100;
>> +  int z;
>> +
>> +  std::call_once(g_initialize, [&] {
>> +    z = 200;
>> +    x = &z;
>> +  });
>> +
>> +  *x = 100; // no-warning
>> +  clang_analyzer_eval(z == 100); // expected-warning{{TRUE}}
>> +}
>> +
>> +void test_called_on_path_no_warning() {
>> +  std::once_flag g_initialize;
>> +
>> +  int *x = nullptr;
>> +  int y = 100;
>> +
>> +  std::call_once(g_initialize, [&] {
>> +    x = &y;
>> +  });
>> +
>> +  *x = 100; // no-warning
>> +}
>> +
>> +void test_called_on_path_warning() {
>> +  std::once_flag g_initialize;
>> +
>> +  int y = 100;
>> +  int *x = &y;
>> +
>> +  std::call_once(g_initialize, [&] {
>> +    x = nullptr;
>> +  });
>> +
>> +  *x = 100; // expected-warning{{Dereference of null pointer (loaded
>> from variable 'x')}}
>> +}
>> +
>> +void test_called_once_warning() {
>> +  std::once_flag g_initialize;
>> +
>> +  int *x = nullptr;
>> +  int y = 100;
>> +
>> +  std::call_once(g_initialize, [&] {
>> +    x = nullptr;
>> +  });
>> +
>> +  std::call_once(g_initialize, [&] {
>> +    x = &y;
>> +  });
>> +
>> +  *x = 100; // expected-warning{{Dereference of null pointer (loaded
>> from variable 'x')}}
>> +}
>> +
>> +void test_called_once_no_warning() {
>> +  std::once_flag g_initialize;
>> +
>> +  int *x = nullptr;
>> +  int y = 100;
>> +
>> +  std::call_once(g_initialize, [&] {
>> +    x = &y;
>> +  });
>> +
>> +  std::call_once(g_initialize, [&] {
>> +    x = nullptr;
>> +  });
>> +
>> +  *x = 100; // no-warning
>> +}
>> +
>> +static int global = 0;
>> +void funcPointer() {
>> +  global = 1;
>> +}
>> +
>> +void test_func_pointers() {
>> +  static std::once_flag flag;
>> +  std::call_once(flag, &funcPointer);
>> +  clang_analyzer_eval(global == 1); // expected-warning{{TRUE}}
>> +}
>> +
>> +template <class _Fp>
>> +class function; // undefined
>> +template <class _Rp, class... _ArgTypes>
>> +struct function<_Rp(_ArgTypes...)> {
>> +  _Rp operator()(_ArgTypes...) const;
>> +  template <class _Fp>
>> +  function(_Fp);
>> +};
>> +
>> +// Note: currently we do not support calls to std::function,
>> +// but the analyzer should not crash either.
>> +void test_function_objects_warning() {
>> +  int x = 0;
>> +  int *y = &x;
>> +
>> +  std::once_flag flag;
>> +
>> +  function<void()> func = [&]() {
>> +    y = nullptr;
>> +  };
>> +
>> +  std::call_once(flag, func);
>> +
>> +  func();
>> +  int z = *y;
>> +}
>> +
>> +void test_param_passing_lambda() {
>> +  std::once_flag flag;
>> +  int x = 120;
>> +  int y = 0;
>> +
>> +  std::call_once(flag, [&](int p) {
>> +    y = p;
>> +  },
>> +                 x);
>> +
>> +  clang_analyzer_eval(y == 120); // expected-warning{{TRUE}}
>> +}
>> +
>> +void test_param_passing_lambda_false() {
>> +  std::once_flag flag;
>> +  int x = 120;
>> +
>> +  std::call_once(flag, [&](int p) {
>> +    x = 0;
>> +  },
>> +                 x);
>> +
>> +  clang_analyzer_eval(x == 120); // expected-warning{{FALSE}}
>> +}
>> +
>> +void test_param_passing_stored_lambda() {
>> +  std::once_flag flag;
>> +  int x = 120;
>> +  int y = 0;
>> +
>> +  auto lambda = [&](int p) {
>> +    y = p;
>> +  };
>> +
>> +  std::call_once(flag, lambda, x);
>> +  clang_analyzer_eval(y == 120); // expected-warning{{TRUE}}
>> +}
>> +
>> +void test_multiparam_passing_lambda() {
>> +  std::once_flag flag;
>> +  int x = 120;
>> +
>> +  std::call_once(flag, [&](int a, int b, int c) {
>> +    x = a + b + c;
>> +  },
>> +                 1, 2, 3);
>> +
>> +  clang_analyzer_eval(x == 120); // expected-warning{{FALSE}}
>> +  clang_analyzer_eval(x == 6); // expected-warning{{TRUE}}
>> +}
>> +
>> +static int global2 = 0;
>> +void test_param_passing_lambda_global() {
>> +  std::once_flag flag;
>> +  global2 = 0;
>> +  std::call_once(flag, [&](int a, int b, int c) {
>> +    global2 = a + b + c;
>> +  },
>> +                 1, 2, 3);
>> +  clang_analyzer_eval(global2 == 6); // expected-warning{{TRUE}}
>> +}
>> +
>> +static int global3 = 0;
>> +void funcptr(int a, int b, int c) {
>> +  global3 = a + b + c;
>> +}
>> +
>> +void test_param_passing_funcptr() {
>> +  std::once_flag flag;
>> +  global3 = 0;
>> +
>> +  std::call_once(flag, &funcptr, 1, 2, 3);
>> +
>> +  clang_analyzer_eval(global3 == 6); // expected-warning{{TRUE}}
>> +}
>> +
>> +void test_blocks() {
>> +  global3 = 0;
>> +  std::once_flag flag;
>> +  std::call_once(flag, ^{
>> +    global3 = 120;
>> +  });
>> +  clang_analyzer_eval(global3 == 120); // expected-warning{{TRUE}}
>> +}
>> +
>> +int call_once() {
>> +  return 5;
>> +}
>> +
>> +void test_non_std_call_once() {
>> +  int x = call_once();
>> +  clang_analyzer_eval(x == 5); // expected-warning{{TRUE}}
>> +}
>>
>>
>> _______________________________________________
>> cfe-commits mailing list
>> cfe-commits at lists.llvm.org
>> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
>>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20171009/785600ce/attachment-0001.html>


More information about the cfe-commits mailing list