Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use clang plugin to check Maybe variables are used #5358

Merged
merged 48 commits into from
Jul 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
95cb353
check in files
jackalcooper Jun 30, 2021
c510e11
refine
jackalcooper Jun 30, 2021
b842d87
fix yml
jackalcooper Jun 30, 2021
470c5d7
fix path
jackalcooper Jun 30, 2021
5b36785
refine
jackalcooper Jun 30, 2021
052145d
refine
jackalcooper Jun 30, 2021
4cf869f
refine
jackalcooper Jun 30, 2021
3e1323a
refine
jackalcooper Jun 30, 2021
1110ec5
refine cache
jackalcooper Jun 30, 2021
dc3d878
refine
jackalcooper Jun 30, 2021
f628f72
refine
jackalcooper Jun 30, 2021
aadcac5
check out oneflow first
jackalcooper Jun 30, 2021
003d23e
refine
jackalcooper Jun 30, 2021
6168070
setup clang as compiler
jackalcooper Jun 30, 2021
1c79447
add ONEFLOW_MAYBE_CHECK_ONLY_FN
jackalcooper Jun 30, 2021
1089634
add llvm libs to LD_LIBRARY_PATH
jackalcooper Jun 30, 2021
8cce0ad
refine
jackalcooper Jun 30, 2021
c2b15ff
refine
jackalcooper Jun 30, 2021
11900ec
refine
jackalcooper Jun 30, 2021
62eb849
refine
jackalcooper Jun 30, 2021
ea84c7a
update yml to use apt to install clang
daquexian Jun 30, 2021
edc1867
fix wrong --target
daquexian Jun 30, 2021
037a33e
add oneflow deps
jackalcooper Jun 30, 2021
927ad38
refine
jackalcooper Jun 30, 2021
7074e20
Merge branch 'master' into ci_check_maybe_add_just_with_clang_ext
jackalcooper Jun 30, 2021
3ca7bc7
use debug to run faster
jackalcooper Jun 30, 2021
2eb2397
add note
jackalcooper Jun 30, 2021
e3b33a3
Merge branch 'ci_check_maybe_add_just_with_clang_ext' of https://gith…
jackalcooper Jun 30, 2021
683bbfb
easy to see
jackalcooper Jun 30, 2021
7c0eedc
refine
jackalcooper Jun 30, 2021
4c3cfd5
fix
jackalcooper Jun 30, 2021
11c285e
refine
jackalcooper Jun 30, 2021
8b7fb54
refine
jackalcooper Jun 30, 2021
a80ed06
Merge branch 'master' of https://github.com/Oneflow-Inc/oneflow into …
jackalcooper Jun 30, 2021
6213115
refine
jackalcooper Jun 30, 2021
6d717d1
Merge branch 'master' into ci_check_maybe_add_just_with_clang_ext
daquexian Jul 1, 2021
21f9e64
add missing JUST
daquexian Jul 1, 2021
f729658
skip .cfg.cpp
daquexian Jul 1, 2021
45e04f6
Merge remote-tracking branch 'origin/master' into ci_check_maybe_add_…
daquexian Jul 1, 2021
00e0eb2
improve ONEFLOW_MAYBE_CHECK_ONLY_FN
daquexian Jul 1, 2021
6101bdc
fix cmake wrong message and fix env var
daquexian Jul 1, 2021
d189674
remove temp log
daquexian Jul 1, 2021
a936bae
Merge branch 'master' into ci_check_maybe_add_just_with_clang_ext
oneflow-ci-bot Jul 1, 2021
408bded
add missing JUST in master
daquexian Jul 1, 2021
d20c497
Merge branch 'master' into ci_check_maybe_add_just_with_clang_ext
oneflow-ci-bot Jul 1, 2021
3b871f1
Merge branch 'master' into ci_check_maybe_add_just_with_clang_ext
oneflow-ci-bot Jul 1, 2021
113302c
Merge branch 'master' into ci_check_maybe_add_just_with_clang_ext
oneflow-ci-bot Jul 1, 2021
d53adbb
Merge branch 'master' into ci_check_maybe_add_just_with_clang_ext
oneflow-ci-bot Jul 1, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,52 @@ jobs:
run: |
exit 1

static_analysis_with_clang_plug_in:
name: Static analysis with Clang plug-in
runs-on: ubuntu-20.04
if: github.event.pull_request.draft == false && contains(github.event.pull_request.requested_reviewers.*.login, 'oneflow-ci-bot')
steps:
- name: Check out OneFlow
uses: actions/checkout@v2
with:
ref: ${{ github.event.pull_request.head.ref }}
repository: ${{github.event.pull_request.head.repo.full_name}}
- name: Install dependencies
run: |
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
sudo apt-add-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-12 main"
sudo apt-get update
sudo apt-get install -y llvm-12 llvm-12-dev llvm-12-tools clang-12 libclang-common-12-dev libclang-12-dev \
libopenblas-dev nasm python3-pip ninja-build

- name: Build Clang plug-in
run: |
export CC=clang-12
export CXX=clang++-12
cd tools/clang-plugin
mkdir build
cd build
cmake .. -DCP_LLVM_INSTALL_DIR=/usr/lib/llvm-12
make -j$(nproc)
clang_plugin_path="${PWD}/lib/libCheckUnusedMaybe.so"
ldd ${clang_plugin_path}
echo "clang_plugin_path=${clang_plugin_path}" >> $GITHUB_ENV
- name: Check unused Maybe (by building OneFlow with Clang plug-in)
run: |
export PROJECT_DIR=${PWD}
mkdir build
cd build
export CC=clang-12
export CXX=clang++-12
export ONEFLOW_MAYBE_CHECK_ONLY_FN=${PROJECT_DIR}/oneflow/
export ONEFLOW_MAYBE_CHECK_SKIP_FN=.cfg.cpp
cmake .. -C ../cmake/caches/international/cpu.cmake \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_TESTING=ON \
-DCMAKE_CXX_FLAGS="-fplugin=${{ env.clang_plugin_path }}"
cmake --build . -j$(nproc) --target oneflow_deps
cmake --build . -j$(nproc)

wait_for_gpu_slot:
name: Wait for GPU slots
runs-on: [self-hosted, scheduler]
Expand Down
2 changes: 1 addition & 1 deletion oneflow/core/framework/consistent_tensor_infer_cache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ Maybe<Operator> MakeOp(const UserOpExpr& user_op_expr, const AttrMap& attrs,
[&](int32_t i) { return output_mut_metas.at(i).mut_tensor_meta(); }));
}
const auto& op = JUST(MakeOp(user_op_expr, infer_args.attrs(), parallel_desc->device_tag()));
op->FillOpParallelDesc(parallel_desc.shared_from_symbol());
JUST(op->FillOpParallelDesc(parallel_desc.shared_from_symbol()));
{
// Infer parallel distribution.
cfg::ParallelDistributionSignature parallel_distribution_constraints;
Expand Down
10 changes: 8 additions & 2 deletions oneflow/core/framework/instructions_builder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,12 +145,18 @@ Maybe<compatible_py::BlobObject> MakeNewBlobObjectLike(
}

Maybe<void> _ReleaseLogicalObject(compatible_py::Object* obj) {
JUST(LogicalRun([&obj](InstructionsBuilder* build) { build->DeleteObject(obj); }));
JUST(LogicalRun([&obj](InstructionsBuilder* build) -> Maybe<void> {
JUST(build->DeleteObject(obj));
return Maybe<void>::Ok();
}));
return Maybe<void>::Ok();
}

Maybe<void> _ReleasePhysicalObject(compatible_py::Object* obj) {
JUST(PhysicalRun([&obj](InstructionsBuilder* build) { build->DeleteObject(obj); }));
JUST(PhysicalRun([&obj](InstructionsBuilder* build) -> Maybe<void> {
JUST(build->DeleteObject(obj));
return Maybe<void>::Ok();
}));
return Maybe<void>::Ok();
}

Expand Down
4 changes: 4 additions & 0 deletions tools/clang-plugin/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
build/
build-*/
.cache/
compile_commands.json
66 changes: 66 additions & 0 deletions tools/clang-plugin/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
cmake_minimum_required(VERSION 3.10)
project(clang-plugins CXX)

set(CP_LLVM_INSTALL_DIR "" CACHE PATH "LLVM installation directory")

# A bit of a sanity checking
set(CP_LLVM_INCLUDE_DIR "${CP_LLVM_INSTALL_DIR}/include/llvm")
if(NOT EXISTS "${CP_LLVM_INCLUDE_DIR}")
message(FATAL_ERROR
"${CP_LLVM_INCLUDE_DIR} does not exist, CP_LLVM_INSTALL_DIR (${CP_LLVM_INSTALL_DIR}) is invalid")
endif()

set(CP_LLVM_CMAKE_FILE "${CP_LLVM_INSTALL_DIR}/lib/cmake/clang/ClangConfig.cmake")
if(NOT EXISTS "${CP_LLVM_CMAKE_FILE}")
message(FATAL_ERROR
" CP_LLVM_CMAKE_FILE (${CP_LLVM_CMAKE_FILE}) is invalid.")
endif()

#===============================================================================
# 2. LOAD CLANG CONFIGURATION
# For more: http://llvm.org/docs/CMake.html#embedding-llvm-in-your-project
#===============================================================================
list(APPEND CMAKE_PREFIX_PATH "${CP_LLVM_INSTALL_DIR}/lib/cmake/llvm/")
list(APPEND CMAKE_PREFIX_PATH "${CP_LLVM_INSTALL_DIR}/lib/cmake/clang/")

find_package(Clang REQUIRED CONFIG)

# Sanity check. As Clang does not expose e.g. `CLANG_VERSION_MAJOR` through
# AddClang.cmake, we have to use LLVM_VERSION_MAJOR instead.
# TODO: Revisit when next version is released.
if(NOT "12" VERSION_EQUAL "${LLVM_VERSION_MAJOR}")
message(FATAL_ERROR "Found LLVM ${LLVM_VERSION_MAJOR}, but need LLVM 12")
endif()

message(STATUS "Found Clang ${LLVM_PACKAGE_VERSION}")
message(STATUS "Using ClangConfig.cmake in: ${CP_LLVM_INSTALL_DIR}")

message("CLANG STATUS:
Includes (clang) ${CLANG_INCLUDE_DIRS}
Includes (llvm) ${LLVM_INCLUDE_DIRS}"
)

# Set the LLVM and Clang header and library paths
include_directories(SYSTEM "${LLVM_INCLUDE_DIRS};${CLANG_INCLUDE_DIRS}")

#===============================================================================
# 3. BUILD CONFIGURATION
#===============================================================================
# Use the same C++ standard as LLVM does
set(CMAKE_CXX_STANDARD 14 CACHE STRING "")

# -fvisibility-inlines-hidden is set when building LLVM and on Darwin warnings
# are triggered if llvm-tutor is built without this flag (though otherwise it
# builds fine). For consistency, add it here too.
include(CheckCXXCompilerFlag)
check_cxx_compiler_flag("-fvisibility-inlines-hidden" SUPPORTS_FVISIBILITY_INLINES_HIDDEN_FLAG)
if (${SUPPORTS_FVISIBILITY_INLINES_HIDDEN_FLAG} EQUAL "1")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility-inlines-hidden")
endif()

# Set the build directories
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib")

add_subdirectory(CheckUnusedMaybe)

18 changes: 18 additions & 0 deletions tools/clang-plugin/CheckUnusedMaybe/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
add_library(CheckUnusedMaybe SHARED CheckUnusedMaybe.cpp)

# On Darwin (unlike on Linux), undefined symbols in shared objects are not
# allowed at the end of the link-edit. The plugins defined here:
# - _are_ shared objects
# - reference symbols from LLVM shared libraries, i.e. symbols which are
# undefined until those shared objects are loaded in memory (and hence
# _undefined_ during static linking)
# The build will fail with errors like this:
# "Undefined symbols for architecture x86_64"
# with various LLVM symbols being undefined. Since those symbols are later
# loaded and resolved at runtime, these errors are false positives.
# This behaviour can be modified via the '-undefined' OS X linker flag as
# follows.
target_link_libraries(
CheckUnusedMaybe
"$<$<PLATFORM_ID:Darwin>:-undefined dynamic_lookup>"
)
142 changes: 142 additions & 0 deletions tools/clang-plugin/CheckUnusedMaybe/CheckUnusedMaybe.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/Stmt.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendAction.h"
#include "clang/Frontend/FrontendPluginRegistry.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/CrashRecoveryContext.h"

using namespace clang;

class CheckUnusedMaybeVisitor
: public RecursiveASTVisitor<CheckUnusedMaybeVisitor> {
public:
explicit CheckUnusedMaybeVisitor(ASTContext *Context) : Context(Context) {
{
auto skip_filenames_env =
llvm::StringRef(std::getenv("ONEFLOW_MAYBE_CHECK_SKIP_FN"));
if (!skip_filenames_env.empty()) {
skip_filenames_env.split(skip_filenames, ";");
for (const auto &x : skip_filenames) {
llvm::outs() << "skip: " << x << "\n";
}
}
}
{
auto only_filenames_env =
llvm::StringRef(std::getenv("ONEFLOW_MAYBE_CHECK_ONLY_FN"));
if (!only_filenames_env.empty()) {
only_filenames_env.split(only_filenames, ";");
for (const auto &x : only_filenames) {
llvm::outs() << "only: " << x << "\n";
}
}
}
}

virtual bool VisitCompoundStmt(CompoundStmt *stmt) {
if (!only_filenames.empty()) {
bool skip = true;
for (const auto &x : only_filenames) {
if (Context->getSourceManager()
.getFilename(stmt->getBeginLoc())
.contains(x)) {
skip = false;
}
}
if (skip) {
return true;
}
}

for (const auto &x : skip_filenames) {
if (Context->getSourceManager()
.getFilename(stmt->getBeginLoc())
.contains(x)) {
return true;
}
}

for (const auto &x : stmt->children()) {
if (ExprWithCleanups *expr = dyn_cast<ExprWithCleanups>(x)) {
std::string type = expr->getType().getAsString();
if (type.substr(0, 12) == "class Maybe<" ||
type.substr(0, 6) == "Maybe<") {
DiagnosticsEngine &DE = Context->getDiagnostics();
unsigned DiagID =
DE.getCustomDiagID(DiagnosticsEngine::Error,
"This function returns Maybe but the return "
"value is ignored. Wrap it with JUST(..)?");
auto DB = DE.Report(expr->getBeginLoc(), DiagID);
DB.AddSourceRange(
clang::CharSourceRange::getCharRange(expr->getSourceRange()));
}
}
if (CallExpr *call = dyn_cast<CallExpr>(x)) {
std::string returnType;
llvm::CrashRecoveryContext CRC;
CRC.RunSafely([&call, &returnType, this]() {
if (auto *callee = call->getDirectCallee()) {
returnType = callee->getReturnType().getAsString();
} else {
returnType = call->getCallReturnType(*this->Context).getAsString();
}
});
if (returnType.substr(0, 12) == "class Maybe<" ||
returnType.substr(0, 6) == "Maybe<") {
DiagnosticsEngine &DE = Context->getDiagnostics();
unsigned DiagID =
DE.getCustomDiagID(DiagnosticsEngine::Error,
"This function returns Maybe but the return "
"value is ignored. Wrap it with JUST(..)?");
auto DB = DE.Report(call->getBeginLoc(), DiagID);
DB.AddSourceRange(
clang::CharSourceRange::getCharRange(call->getSourceRange()));
}
}
}
return true;
}

private:
ASTContext *Context;
llvm::SmallVector<llvm::StringRef, 10> skip_filenames;
llvm::SmallVector<llvm::StringRef, 10> only_filenames;
};

class CheckUnusedMaybeConsumer : public clang::ASTConsumer {
public:
explicit CheckUnusedMaybeConsumer(ASTContext *Context) : Visitor(Context) {}

void HandleTranslationUnit(clang::ASTContext &Context) override {
Visitor.TraverseDecl(Context.getTranslationUnitDecl());
}

private:
CheckUnusedMaybeVisitor Visitor;
};

class CheckUnusedMaybeAction : public clang::PluginASTAction {
public:
virtual std::unique_ptr<clang::ASTConsumer>
CreateASTConsumer(clang::CompilerInstance &Compiler,
llvm::StringRef InFile) override {
return std::make_unique<CheckUnusedMaybeConsumer>(
&Compiler.getASTContext());
}

bool ParseArgs(const CompilerInstance &CI,
const std::vector<std::string> &args) override {
return true;
}

// Automatically run the plugin after the main AST action
ActionType getActionType() override { return AddAfterMainAction; }
};

static FrontendPluginRegistry::Add<CheckUnusedMaybeAction>
X("check-unused-maybe", "Check unused maybe");

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
template <class T> class Maybe {};

Maybe<void> a() { return Maybe<void>(); }

int main() {
a();
return 0;
}