brintos

brintos / llvm-project-archived public Read only

0
0
Text · 5.3 KiB · 2d1323b Raw
151 lines · cpp
1//=== UnsignedStatDemo.cpp --------------------------------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This checker demonstrates the use of UnsignedEPStat for per-entry-point10// statistics. It conditionally sets a statistic based on the entry point name.11//12//===----------------------------------------------------------------------===//13 14#include "CheckerRegistration.h"15#include "clang/StaticAnalyzer/Core/Checker.h"16#include "clang/StaticAnalyzer/Core/CheckerManager.h"17#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"18#include "clang/StaticAnalyzer/Core/PathSensitive/EntryPointStats.h"19#include "llvm/ADT/STLExtras.h"20#include "llvm/ADT/ScopeExit.h"21#include "llvm/ADT/StringMap.h"22#include "llvm/Support/MemoryBuffer.h"23#include "gtest/gtest.h"24#include <optional>25 26using namespace clang;27using namespace ento;28 29static UnsignedEPStat DemoStat("DemoStat");30 31namespace {32class UnsignedStatTesterChecker : public Checker<check::BeginFunction> {33public:34  void checkBeginFunction(CheckerContext &C) const {35    StringRef Name;36    if (const Decl *D = C.getLocationContext()->getDecl())37      if (const FunctionDecl *F = D->getAsFunction())38        Name = F->getName();39 40    // Conditionally set the statistic based on the function name (leaving it41    // undefined for all other functions)42    if (Name == "func_one")43      DemoStat.set(1);44    else if (Name == "func_two")45      DemoStat.set(2);46    else47      ; // For any other function (e.g., "func_none") don't set the statistic48  }49};50 51void addUnsignedStatTesterChecker(AnalysisASTConsumer &AnalysisConsumer,52                                  AnalyzerOptions &AnOpts) {53  AnOpts.CheckersAndPackages = {{"test.DemoStatChecker", true}};54  AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) {55    Registry.addChecker<UnsignedStatTesterChecker>(56        "test.DemoStatChecker", "DescriptionOfDemoStatChecker");57  });58}59 60// Find the index of a column in the CSV header.61// Returns std::nullopt if the column is not found.62static std::optional<unsigned>63findColumnIndex(llvm::ArrayRef<llvm::StringRef> Header,64                llvm::StringRef ColumnName) {65  auto Iter = llvm::find(Header, ColumnName);66  if (Iter != Header.end())67    return std::distance(Header.begin(), Iter);68  return std::nullopt;69}70 71// Parse CSV content and extract a mapping from one column to another.72// KeyColumn is used as the map key (e.g., "DebugName").73// ValueColumn is used as the map value (e.g., "DemoStat").74// Returns a map from key column values to value column values.75static llvm::StringMap<std::string>76parseCSVColumnMapping(llvm::StringRef CSVContent, llvm::StringRef KeyColumn,77                      llvm::StringRef ValueColumn) {78  llvm::StringMap<std::string> Result;79 80  // Parse CSV: first line is header, subsequent lines are data81  llvm::SmallVector<llvm::StringRef, 8> Lines;82  CSVContent.split(Lines, '\n', -1, false);83  if (Lines.size() < 2) // Need at least header + one data row84    return Result;85 86  // Parse header to find column indices87  llvm::SmallVector<llvm::StringRef, 32> Header;88  Lines[0].split(Header, ',');89  std::optional<unsigned> KeyIdx = findColumnIndex(Header, KeyColumn);90  std::optional<unsigned> ValueIdx = findColumnIndex(Header, ValueColumn);91 92  if (!KeyIdx || !ValueIdx)93    return Result;94 95  // Parse data rows and extract mappings96  for (auto Line : llvm::drop_begin(Lines)) {97    llvm::SmallVector<llvm::StringRef, 32> Row;98    Line.split(Row, ',');99    if (Row.size() <= std::max(*KeyIdx, *ValueIdx))100      continue;101 102    llvm::StringRef KeyVal = Row[*KeyIdx].trim().trim('"');103    llvm::StringRef ValueVal = Row[*ValueIdx].trim().trim('"');104 105    if (!KeyVal.empty())106      Result[KeyVal] = ValueVal.str();107  }108 109  return Result;110}111 112TEST(UnsignedStat, ExplicitlySetUnsignedStatistic) {113  llvm::SmallString<128> TempMetricsCsvPath;114  std::error_code EC =115      llvm::sys::fs::createTemporaryFile("ep_stats", "csv", TempMetricsCsvPath);116  ASSERT_FALSE(EC);117  std::vector<std::string> Args = {118      "-Xclang", "-analyzer-config", "-Xclang",119      std::string("dump-entry-point-stats-to-csv=") +120          TempMetricsCsvPath.str().str()};121  // Clean up on exit122  auto Cleanup = llvm::make_scope_exit(123      [&]() { llvm::sys::fs::remove(TempMetricsCsvPath); });124  EXPECT_TRUE(runCheckerOnCodeWithArgs<addUnsignedStatTesterChecker>(125      R"cpp(126            void func_one() {}127            void func_two() {}128            void func_none() {}129      )cpp",130      Args));131 132  auto BufferOrError = llvm::MemoryBuffer::getFile(TempMetricsCsvPath);133  ASSERT_TRUE(BufferOrError);134  llvm::StringRef CSVContent = BufferOrError.get()->getBuffer();135 136  // Parse the CSV and extract function statistics137  llvm::StringMap<std::string> FunctionStats =138      parseCSVColumnMapping(CSVContent, "DebugName", "DemoStat");139 140  // Verify the expected values141  ASSERT_TRUE(FunctionStats.count("func_one()"));142  EXPECT_EQ(FunctionStats["func_one()"], "1");143 144  ASSERT_TRUE(FunctionStats.count("func_two()"));145  EXPECT_EQ(FunctionStats["func_two()"], "2");146 147  ASSERT_TRUE(FunctionStats.count("func_none()"));148  EXPECT_EQ(FunctionStats["func_none()"], ""); // Not set, should be empty149}150} // namespace151