Clang Static Analysis (CSA) does static analysis on the source code level IR, the Clang AST, rather than LLVM low level IR.
clang inside the prebuilt binaries.The full example code is here but you may want to know the details of the code tree.
For the Clion project setup, please refer to the pass document:
Hello_World Pass (Take CLion on Mac as an Example)
CMakeLists.txt and add a checkerChange the CMakeLists.txt as follows:
cmake_minimum_required(VERSION 2.8.8)
#Project declaration
project(ClangTypeAnalysis DESCRIPTION "libClangTypeAnalysis")
find_program(CCACHE_FOUND ccache)
if(CCACHE_FOUND)
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache)
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache)
endif(CCACHE_FOUND)
#We might only have llvm-config-X.Y
find_program (LLVM_CONFIG_WITH_NUMBER "llvm-config-${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}")
if(LLVM_CONFIG_WITH_NUMBER MATCHES "NOTFOUND")
find_program (REAL_LLVM_CONFIG "llvm-config")
else()
set(REAL_LLVM_CONFIG ${LLVM_CONFIG_WITH_NUMBER})
endif()
message(STATUS "Found: ${REAL_LLVM_CONFIG}")
find_package(LLVM REQUIRED CONFIG)
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
llvm_map_components_to_libnames(llvm_libs support core irreader bitwriter TransformUtils option)
#get compile flags
execute_process(COMMAND "${REAL_LLVM_CONFIG}" --cxxflags OUTPUT_VARIABLE CPP_LLVM_CONFIG OUTPUT_STRIP_TRAILING_WHITESPACE)
#get system lib link flags
execute_process(COMMAND "${REAL_LLVM_CONFIG}" --system-libs OUTPUT_VARIABLE SYSTEM_LIBS_LLVM_CONFIG OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(COMMAND "${REAL_LLVM_CONFIG}" --includedir OUTPUT_VARIABLE LLVM_CONFIG_INCLUDE_DIR OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(COMMAND "${REAL_LLVM_CONFIG}" --src-root OUTPUT_VARIABLE LLVM_SRC_DIR OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(COMMAND "${REAL_LLVM_CONFIG}" --obj-root OUTPUT_VARIABLE LLVM_OBJ_DIR OUTPUT_STRIP_TRAILING_WHITESPACE)
include_directories(SYSTEM ${LLVM_SRC_DIR}/tools/clang/include SYSTEM ${LLVM_OBJ_DIR}/tools/clang/include SYSTEM ${LLVM_CONFIG_INCLUDE_DIR})
message(STATUS "include dir is ${LLVM_CONFIG_INCLUDE_DIR} ${LLVM_OBJ_DIR}/tools/clang/include ${LLVM_CONFIG_INCLUDE_DIR}")
execute_process(COMMAND "${REAL_LLVM_CONFIG}" --libdir OUTPUT_VARIABLE LLVM_CONFIG_LINk_DIR OUTPUT_STRIP_TRAILING_WHITESPACE)
link_directories( ${LLVM_CONFIG_LINk_DIR} )
message(STATUS "link dir is ${LLVM_CONFIG_LINk_DIR}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 ${CPP_LLVM_CONFIG}")
add_library(ClangTypeAnalysis SHARED ClangTypeAnalysis.cpp)
set (LLVM_LIBS
${llvm_libs}
)
set(CLANG_LIBS
clangBasic
clangEdit
clangFrontend
clangStaticAnalyzerFrontend
clangStaticAnalyzerCore
clangStaticAnalyzerCheckers
clangSerialization
clangSema
clangAnalysis
clangCrossTU
clangRewriteFrontend
clangRewrite
clangLex
clangAST
clangASTMatchers
clangDynamicASTMatchers
clangIndex
clangDriver
clangParse
)
if(${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU")
set(START_GROUP "-Wl,--start-group")
set(END_GROUP "-Wl,--end-group")
else()
set(START_GROUP "")
set(END_GROUP "")
endif()
target_link_libraries(ClangTypeAnalysis
${START_GROUP}
${CLANG_LIBS}
${LLVM_LIBS}
${END_GROUP}
${SYSTEM_LIBS_LLVM_CONFIG})
Note that the CMakeLists.txt defines the Clang libraries to use in Clang Static Analysis checker development. Now we need to create a checker cpp file since CMakeLists.txt specifies it as ClangTypeAnalysis.cpp .
#include <iostream>
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
using namespace clang;
using namespace ento;
namespace {
class ClangTypeAnalysis : public Checker < check::PreStmt<Stmt>,
check::ASTDecl<Decl>,
check::EndAnalysis > {
mutable std::unique_ptr<BugType> BT;
public:
void checkPreStmt(const Stmt *ST, CheckerContext &C) const;
void checkASTDecl(const Decl *D,
AnalysisManager &Mgr,
BugReporter &BR) const;
void checkEndAnalysis(ExplodedGraph &G,
BugReporter &BR, ExprEngine &Eng) const;
};
} // end anonymous namespace
void ClangTypeAnalysis::checkPreStmt(const Stmt *ST, CheckerContext &C) const {
SourceLocation Loc = ST->getBeginLoc();
auto& SM = C.getSourceManager();
if ((Loc.isInvalid() || SM.isInSystemHeader(Loc) || SM.isInExternCSystemHeader(Loc) || SM.isInSystemMacro(Loc)) && !SM.isWrittenInMainFile(Loc)) {
return;
}
// do something with stmt
}
void ClangTypeAnalysis::checkASTDecl(const Decl *D,
AnalysisManager &Mgr,
BugReporter &BR) const {
SourceLocation Loc = D->getBeginLoc();
auto& SM = Mgr.getSourceManager();
if ((Loc.isInvalid() || SM.isInSystemHeader(Loc) || SM.isInExternCSystemHeader(Loc) || SM.isInSystemMacro(Loc)) && !SM.isWrittenInMainFile(Loc)) {
return;
}
// do something with decl
}
void ClangTypeAnalysis::checkEndAnalysis(ExplodedGraph &G,
BugReporter &BR, ExprEngine &Eng) const {
// evaluate at the end of analysis
}
// Register plugin!
extern "C"
void clang_registerCheckers (CheckerRegistry ®istry) {
registry.addChecker<ClangTypeAnalysis>("homework.ClangTypeAnalysis", "Type Analysis", "nodoc");
}
extern "C"
const char clang_analyzerAPIVersionString[] = CLANG_ANALYZER_API_VERSION_STRING;
The class PreStmt and ASTDecl are checker callback classes that perform operations on each Decl and Stmt node of the Clang AST. The endAnalysis is a class that does evaluation when all the checks have finished, which is useful to print out statistics of the analysis result. The registry method specifies the name of the checker in the command line, which is "homework.ClangTypeAnalysis" in this example.
Build the project. The output shall be similar to this:

Run the checker manually.
macOS (dynamic libraries end with dylib)
clang-10 -Xclang -load -Xclang cmake-build-release/libClangTypeAnalysis.dylib -Xclang -analyze -Xclang -analyzer-checker=homework.ClangTypeAnalysis -g -c test/lambda.cpp -v
Linux (dynamic libraries end with so)
clang-10 -Xclang -load -Xclang cmake-build-release/libClangTypeAnalysis.so -Xclang -analyze -Xclang -analyzer-checker=homework.ClangTypeAnalysis -g -c test/lambda.cpp -v
The result should be similar as this (because we almost do nothing but print out the verbose message):
