forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			163 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			163 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			C++
		
	
	
	
//===- unittests/StaticAnalyzer/CallDescriptionTest.cpp -------------------===//
 | 
						|
//
 | 
						|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 | 
						|
// See https://llvm.org/LICENSE.txt for license information.
 | 
						|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 | 
						|
//
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
 | 
						|
#include "Reusables.h"
 | 
						|
 | 
						|
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
 | 
						|
#include "clang/Tooling/Tooling.h"
 | 
						|
#include "gtest/gtest.h"
 | 
						|
 | 
						|
namespace clang {
 | 
						|
namespace ento {
 | 
						|
namespace {
 | 
						|
 | 
						|
// A wrapper around CallDescriptionMap<bool> that allows verifying that
 | 
						|
// all functions have been found. This is needed because CallDescriptionMap
 | 
						|
// isn't supposed to support iteration.
 | 
						|
class ResultMap {
 | 
						|
  size_t Found, Total;
 | 
						|
  CallDescriptionMap<bool> Impl;
 | 
						|
 | 
						|
public:
 | 
						|
  ResultMap(std::initializer_list<std::pair<CallDescription, bool>> Data)
 | 
						|
      : Found(0),
 | 
						|
        Total(std::count_if(Data.begin(), Data.end(),
 | 
						|
                            [](const std::pair<CallDescription, bool> &Pair) {
 | 
						|
                              return Pair.second == true;
 | 
						|
                            })),
 | 
						|
        Impl(std::move(Data)) {}
 | 
						|
 | 
						|
  const bool *lookup(const CallEvent &Call) {
 | 
						|
    const bool *Result = Impl.lookup(Call);
 | 
						|
    // If it's a function we expected to find, remember that we've found it.
 | 
						|
    if (Result && *Result)
 | 
						|
      ++Found;
 | 
						|
    return Result;
 | 
						|
  }
 | 
						|
 | 
						|
  // Fail the test if we haven't found all the true-calls we were looking for.
 | 
						|
  ~ResultMap() { EXPECT_EQ(Found, Total); }
 | 
						|
};
 | 
						|
 | 
						|
// Scan the code body for call expressions and see if we find all calls that
 | 
						|
// we were supposed to find ("true" in the provided ResultMap) and that we
 | 
						|
// don't find the ones that we weren't supposed to find
 | 
						|
// ("false" in the ResultMap).
 | 
						|
class CallDescriptionConsumer : public ExprEngineConsumer {
 | 
						|
  ResultMap &RM;
 | 
						|
  void performTest(const Decl *D) {
 | 
						|
    using namespace ast_matchers;
 | 
						|
 | 
						|
    if (!D->hasBody())
 | 
						|
      return;
 | 
						|
 | 
						|
    const CallExpr *CE = findNode<CallExpr>(D, callExpr());
 | 
						|
    const StackFrameContext *SFC =
 | 
						|
        Eng.getAnalysisDeclContextManager().getStackFrame(D);
 | 
						|
    ProgramStateRef State = Eng.getInitialState(SFC);
 | 
						|
    CallEventRef<> Call =
 | 
						|
        Eng.getStateManager().getCallEventManager().getCall(CE, State, SFC);
 | 
						|
 | 
						|
    const bool *LookupResult = RM.lookup(*Call);
 | 
						|
    // Check that we've found the function in the map
 | 
						|
    // with the correct description.
 | 
						|
    EXPECT_TRUE(LookupResult && *LookupResult);
 | 
						|
 | 
						|
    // ResultMap is responsible for making sure that we've found *all* calls.
 | 
						|
  }
 | 
						|
 | 
						|
public:
 | 
						|
  CallDescriptionConsumer(CompilerInstance &C,
 | 
						|
                          ResultMap &RM)
 | 
						|
      : ExprEngineConsumer(C), RM(RM) {}
 | 
						|
 | 
						|
  bool HandleTopLevelDecl(DeclGroupRef DG) override {
 | 
						|
    for (const auto *D : DG)
 | 
						|
      performTest(D);
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
class CallDescriptionAction : public ASTFrontendAction {
 | 
						|
  ResultMap RM;
 | 
						|
 | 
						|
public:
 | 
						|
  CallDescriptionAction(
 | 
						|
      std::initializer_list<std::pair<CallDescription, bool>> Data)
 | 
						|
      : RM(Data) {}
 | 
						|
 | 
						|
  std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
 | 
						|
                                                 StringRef File) override {
 | 
						|
    return std::make_unique<CallDescriptionConsumer>(Compiler, RM);
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
TEST(CallEvent, CallDescription) {
 | 
						|
  // Test simple name matching.
 | 
						|
  EXPECT_TRUE(tooling::runToolOnCode(
 | 
						|
      std::unique_ptr<CallDescriptionAction>(new CallDescriptionAction({
 | 
						|
          {{"bar"}, false}, // false: there's no call to 'bar' in this code.
 | 
						|
          {{"foo"}, true},  // true: there's a call to 'foo' in this code.
 | 
						|
      })), "void foo(); void bar() { foo(); }"));
 | 
						|
 | 
						|
  // Test arguments check.
 | 
						|
  EXPECT_TRUE(tooling::runToolOnCode(
 | 
						|
      std::unique_ptr<CallDescriptionAction>(new CallDescriptionAction({
 | 
						|
          {{"foo", 1}, true},
 | 
						|
          {{"foo", 2}, false},
 | 
						|
      })), "void foo(int); void foo(int, int); void bar() { foo(1); }"));
 | 
						|
 | 
						|
  // Test lack of arguments check.
 | 
						|
  EXPECT_TRUE(tooling::runToolOnCode(
 | 
						|
      std::unique_ptr<CallDescriptionAction>(new CallDescriptionAction({
 | 
						|
          {{"foo", None}, true},
 | 
						|
          {{"foo", 2}, false},
 | 
						|
      })), "void foo(int); void foo(int, int); void bar() { foo(1); }"));
 | 
						|
 | 
						|
  // Test qualified names.
 | 
						|
  EXPECT_TRUE(tooling::runToolOnCode(
 | 
						|
      std::unique_ptr<CallDescriptionAction>(new CallDescriptionAction({
 | 
						|
          {{{"std", "basic_string", "c_str"}}, true},
 | 
						|
      })),
 | 
						|
      "namespace std { inline namespace __1 {"
 | 
						|
      "  template<typename T> class basic_string {"
 | 
						|
      "  public:"
 | 
						|
      "    T *c_str();"
 | 
						|
      "  };"
 | 
						|
      "}}"
 | 
						|
      "void foo() {"
 | 
						|
      "  using namespace std;"
 | 
						|
      "  basic_string<char> s;"
 | 
						|
      "  s.c_str();"
 | 
						|
      "}"));
 | 
						|
 | 
						|
  // A negative test for qualified names.
 | 
						|
  EXPECT_TRUE(tooling::runToolOnCode(
 | 
						|
      std::unique_ptr<CallDescriptionAction>(new CallDescriptionAction({
 | 
						|
          {{{"foo", "bar"}}, false},
 | 
						|
          {{{"bar", "foo"}}, false},
 | 
						|
          {{"foo"}, true},
 | 
						|
      })), "void foo(); struct bar { void foo(); }; void test() { foo(); }"));
 | 
						|
 | 
						|
  // Test CDF_MaybeBuiltin - a flag that allows matching weird builtins.
 | 
						|
  EXPECT_TRUE(tooling::runToolOnCode(
 | 
						|
      std::unique_ptr<CallDescriptionAction>(new CallDescriptionAction({
 | 
						|
          {{"memset", 3}, false},
 | 
						|
          {{CDF_MaybeBuiltin, "memset", 3}, true}
 | 
						|
      })),
 | 
						|
      "void foo() {"
 | 
						|
      "  int x;"
 | 
						|
      "  __builtin___memset_chk(&x, 0, sizeof(x),"
 | 
						|
      "                         __builtin_object_size(&x, 0));"
 | 
						|
      "}"));
 | 
						|
}
 | 
						|
 | 
						|
} // namespace
 | 
						|
} // namespace ento
 | 
						|
} // namespace clang
 |