Skip to content

Commit 8f40b08

Browse files
committed
Add a framework for a subspace clang-tidy module
1 parent 220dd42 commit 8f40b08

9 files changed

+376
-0
lines changed

CMakeLists.txt

+13
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,11 @@ include(OptionIf)
3535

3636
option_if_not_defined(SUBSPACE_BUILD_CIR "Build CIR (requires LLVM)" ON)
3737
option_if_not_defined(SUBSPACE_BUILD_SUBDOC "Build subdoc (requires LLVM)" ON)
38+
option_if_not_defined(SUBSPACE_BUILD_TIDY "Build clang-tidy plugin (requires LLVM)" NOT WIN32)
3839

3940
message(STATUS "Build CIR: ${SUBSPACE_BUILD_CIR}")
4041
message(STATUS "Build subdoc: ${SUBSPACE_BUILD_SUBDOC}")
42+
message(STATUS "Build clang-tidy plugin: ${SUBSPACE_BUILD_TIDY}")
4143

4244
function(subspace_default_compile_options TARGET)
4345
if(MSVC)
@@ -81,3 +83,14 @@ endif()
8183
if (${SUBSPACE_BUILD_SUBDOC})
8284
add_subdirectory(subdoc)
8385
endif()
86+
87+
if(${SUBSPACE_BUILD_TIDY})
88+
add_subdirectory(tidy)
89+
90+
#set(CMAKE_CXX_CLANG_TIDY
91+
# clang-tidy;
92+
# -header-filter=.;
93+
# -checks=-*,subspace-*;
94+
# -warnings-as-errors=*;
95+
#)
96+
endif()

tidy/CMakeLists.txt

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# Copyright 2023 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
add_library(subspace_clang_tidy_module MODULE "")
16+
add_library(subspace::clang_tidy ALIAS subspace_clang_tidy_module)
17+
target_sources(subspace_clang_tidy_module PUBLIC
18+
"llvm.h"
19+
"module.cc"
20+
"smoke_check.cc"
21+
"smoke_check.h"
22+
)
23+
24+
target_link_libraries(subspace_clang_tidy_module
25+
subspace::lib
26+
27+
clangTidy
28+
29+
clangAnalysis
30+
clangAnalysisFlowSensitive
31+
clangAnalysisFlowSensitiveModels
32+
clangAPINotes
33+
clangARCMigrate
34+
clangAST
35+
clangASTMatchers
36+
clangBasic
37+
clangCodeGen
38+
clangCrossTU
39+
clangDependencyScanning
40+
clangDirectoryWatcher
41+
clangDriver
42+
clangDynamicASTMatchers
43+
clangEdit
44+
clangExtractAPI
45+
clangFormat
46+
clangFrontend
47+
clangFrontendTool
48+
clangHandleCXX
49+
clangHandleLLVM
50+
clangIndex
51+
clangIndexSerialization
52+
clangInterpreter
53+
clangLex
54+
clangParse
55+
clangRewrite
56+
clangRewriteFrontend
57+
clangSema
58+
clangSerialization
59+
clangStaticAnalyzerCheckers
60+
clangStaticAnalyzerCore
61+
clangStaticAnalyzerFrontend
62+
clangSupport
63+
clangTooling
64+
clangToolingASTDiff
65+
clangToolingCore
66+
clangToolingInclusions
67+
clangToolingInclusionsStdlib
68+
clangToolingRefactoring
69+
clangToolingSyntax
70+
clangTransformer
71+
)
72+
73+
find_package(Clang REQUIRED)
74+
llvm_config(subdoc_lib)
75+
target_include_directories(subspace_clang_tidy_module PUBLIC ${LLVM_INCLUDE_DIRS})
76+
target_link_directories(subspace_clang_tidy_module PUBLIC ${LLVM_LIBRARY_DIRS})
77+
78+
# Subspace clang-tidy module.
79+
subspace_default_compile_options(subspace_clang_tidy_module)
80+
81+
add_subdirectory(tests)

tidy/llvm.h

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright 2023 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#pragma once
16+
17+
// All LLVM and Clang includes go here, because they are full of compiler
18+
// warnings that we have to disable.
19+
20+
#pragma warning(push)
21+
#pragma warning(disable : 4100)
22+
#pragma warning(disable : 4127)
23+
#pragma warning(disable : 4146)
24+
#pragma warning(disable : 4244)
25+
#pragma warning(disable : 4245)
26+
#pragma warning(disable : 4267)
27+
#pragma warning(disable : 4291)
28+
#pragma warning(disable : 4324)
29+
#pragma warning(disable : 4389)
30+
#pragma warning(disable : 4456)
31+
#pragma warning(disable : 4458)
32+
#pragma warning(disable : 4459)
33+
#pragma warning(disable : 4616)
34+
#pragma warning(disable : 4624)
35+
#pragma warning(disable : 4702)
36+
37+
#include "clang-tidy/ClangTidyCheck.h"
38+
#include "clang-tidy/ClangTidyModule.h"
39+
#include "clang-tidy/ClangTidyModuleRegistry.h"
40+
#include "clang/AST/Decl.h"
41+
#include "clang/AST/DeclGroup.h"
42+
#include "clang/AST/DeclTemplate.h"
43+
#include "clang/AST/RecursiveASTVisitor.h"
44+
#include "clang/ASTMatchers/ASTMatchFinder.h"
45+
#include "clang/ASTMatchers/ASTMatchers.h"
46+
47+
#pragma warning(pop)

tidy/module.cc

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright 2023 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "tidy/llvm.h"
16+
#include "tidy/smoke_check.h"
17+
18+
namespace clang::tidy::subspace {
19+
20+
class SubspaceClangTidyModule : public ClangTidyModule {
21+
void addCheckFactories(ClangTidyCheckFactories& factories) override {
22+
factories.registerCheck<SmokeCheck>("subspace-smoke");
23+
}
24+
};
25+
26+
static ClangTidyModuleRegistry::Add<SubspaceClangTidyModule> X(
27+
"subspace-clang-tidy-module",
28+
"Adds lint checks to be used with the Subspace library.");
29+
30+
} // namespace clang::tidy::subspace

tidy/smoke_check.cc

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright 2023 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "tidy/smoke_check.h"
16+
17+
#include "subspace/prelude.h"
18+
#include "tidy/llvm.h"
19+
20+
namespace clang::tidy::subspace {
21+
22+
SmokeCheck::SmokeCheck(llvm::StringRef name, ClangTidyContext* context)
23+
: ClangTidyCheck(sus::move(name), context) {}
24+
25+
void SmokeCheck::registerMatchers(ast_matchers::MatchFinder* finder) {
26+
using namespace ast_matchers;
27+
28+
finder->addMatcher(functionDecl().bind("x"), this);
29+
}
30+
31+
void SmokeCheck::check(const ast_matchers::MatchFinder::MatchResult& match) {
32+
const auto* MatchedDecl = match.Nodes.getNodeAs<FunctionDecl>("x");
33+
if (!MatchedDecl->getIdentifier() ||
34+
MatchedDecl->getName().startswith("awesome_"))
35+
return;
36+
diag(MatchedDecl->getLocation(), "function %0 is insufficiently awesome")
37+
<< MatchedDecl
38+
<< FixItHint::CreateInsertion(MatchedDecl->getLocation(), "awesome_");
39+
}
40+
41+
} // namespace clang::tidy::subspace

tidy/smoke_check.h

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright 2023 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#pragma once
16+
17+
#include "subspace/macros/compiler.h"
18+
#include "tidy/llvm.h"
19+
20+
namespace clang::tidy::subspace {
21+
22+
class SmokeCheck : public ClangTidyCheck {
23+
public:
24+
SmokeCheck(llvm::StringRef name, ClangTidyContext* context);
25+
void registerMatchers(ast_matchers::MatchFinder* finder) override;
26+
void check(const ast_matchers::MatchFinder::MatchResult& match) override;
27+
};
28+
29+
} // namespace clang::tidy::subspace

tidy/tests/CMakeLists.txt

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Copyright 2023 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
enable_testing()
16+
17+
function(tidy_test check_name)
18+
add_test(NAME "RunClangTidy.${check_name}" COMMAND ${CMAKE_COMMAND}
19+
"-DCLANG_TIDY_COMMAND=$<TARGET_FILE:clang-tidy>"
20+
"-DCLANG_TIDY_MODULE=$<TARGET_FILE:subspace_clang_tidy_module>"
21+
"-DCHECK_NAME=${check_name}"
22+
"-DRunClangTidy_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR}"
23+
-P "${CMAKE_CURRENT_SOURCE_DIR}/run_clang_tidy.cmake"
24+
)
25+
endfunction()
26+
27+
tidy_test(subspace-smoke)

tidy/tests/run_clang_tidy.cmake

+107
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# Copyright 2023 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
set(config_arg)
16+
if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/${CHECK_NAME}.clang-tidy")
17+
set(config_arg "--config-file=${CMAKE_CURRENT_LIST_DIR}/${CHECK_NAME}.clang-tidy")
18+
endif()
19+
20+
if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/${CHECK_NAME}-stdout.txt")
21+
file(READ "${CMAKE_CURRENT_LIST_DIR}/${CHECK_NAME}-stdout.txt" expect_stdout)
22+
string(REGEX REPLACE "\n+$" "" expect_stdout "${expect_stdout}")
23+
else()
24+
set(expect_stdout "")
25+
endif()
26+
27+
set(source_file "${RunClangTidy_BINARY_DIR}/${CHECK_NAME}.cc")
28+
configure_file("${CMAKE_CURRENT_LIST_DIR}/${CHECK_NAME}.cc" "${source_file}" COPYONLY)
29+
30+
file(GLOB header_files RELATIVE "${CMAKE_CURRENT_LIST_DIR}/${CHECK_NAME}" "${CMAKE_CURRENT_LIST_DIR}/${CHECK_NAME}/*")
31+
file(REMOVE_RECURSE "${RunClangTiy_BINARY_DIR}/${CHECK_NAME}")
32+
foreach(header_file IN LISTS header_files)
33+
if(NOT header_file MATCHES "-fixit\\.h\$")
34+
file(MAKE_DIRECTORY "${RunClangTidy_BINARY_DIR}/${CHECK_NAME}")
35+
configure_file("${CMAKE_CURRENT_LIST_DIR}/${CHECK_NAME}/${header_file}" "${RunClangTidy_BINARY_DIR}/${CHECK_NAME}/${header_file}" COPYONLY)
36+
endif()
37+
endforeach()
38+
39+
set(command
40+
"${CLANG_TIDY_COMMAND}"
41+
"--load=${CLANG_TIDY_MODULE}"
42+
"--checks=-*,${CHECK_NAME}"
43+
"--fix"
44+
"--format-style=file"
45+
"--header-filter=/${CHECK_NAME}/"
46+
${config_arg}
47+
"${source_file}"
48+
--
49+
)
50+
execute_process(
51+
COMMAND ${command}
52+
RESULT_VARIABLE result
53+
OUTPUT_VARIABLE actual_stdout
54+
ERROR_VARIABLE actual_stderr
55+
)
56+
string(REPLACE "${RunClangTidy_BINARY_DIR}/" "" actual_stdout "${actual_stdout}")
57+
58+
set(RunClangTidy_TEST_FAILED)
59+
60+
if(NOT result EQUAL 0)
61+
string(APPEND RunClangTidy_TEST_FAILED "Expected result: 0, actual result: ${result}\n")
62+
endif()
63+
64+
string(REGEX REPLACE "\n+$" "" actual_stdout "${actual_stdout}")
65+
if(NOT actual_stdout STREQUAL expect_stdout)
66+
string(REPLACE "\n" "\n " expect_stdout_formatted " ${expect_stdout}")
67+
string(REPLACE "\n" "\n " actual_stdout_formatted " ${actual_stdout}")
68+
string(APPEND RunClangTidy_TEST_FAILED "Expected stdout:\n${expect_stdout_formatted}\nActual stdout:\n${actual_stdout_formatted}\n")
69+
endif()
70+
71+
function(check_fixit expected fallback_expected actual)
72+
if(EXISTS "${expected}")
73+
set(expect_fixit_file "${expected}")
74+
else()
75+
set(expect_fixit_file "${fallback_expected}")
76+
endif()
77+
file(READ "${expect_fixit_file}" expect_fixit)
78+
file(READ "${actual}" actual_fixit)
79+
if(NOT expect_fixit STREQUAL actual_fixit)
80+
string(REPLACE "\n" "\n " expect_fixit_formatted " ${expect_fixit}")
81+
string(REPLACE "\n" "\n " actual_fixit_formatted " ${actual_fixit}")
82+
string(APPEND RunClangTidy_TEST_FAILED "Expected fixit for ${actual}:\n${expect_fixit_formatted}\nActual fixit:\n${actual_fixit_formatted}\n")
83+
set(RunClangTidy_TEST_FAILED "${RunClangTidy_TEST_FAILED}" PARENT_SCOPE)
84+
endif()
85+
endfunction()
86+
87+
check_fixit(
88+
"${CMAKE_CURRENT_LIST_DIR}/${CHECK_NAME}-fixit.cc"
89+
"${CMAKE_CURRENT_LIST_DIR}/${CHECK_NAME}.cc"
90+
"${source_file}"
91+
)
92+
93+
foreach(header_file IN LISTS header_files)
94+
if(NOT header_file MATCHES "-fixit\\.h\$")
95+
string(REGEX REPLACE "\\.h\$" "-fixit.h" header_fixit "${header_file}")
96+
check_fixit(
97+
"${CMAKE_CURRENT_LIST_DIR}/${CHECK_NAME}/${header_fixit}"
98+
"${CMAKE_CURRENT_LIST_DIR}/${CHECK_NAME}/${header_file}"
99+
"${RunClangTidy_BINARY_DIR}/${CHECK_NAME}/${header_file}"
100+
)
101+
endif()
102+
endforeach()
103+
104+
if(RunClangTidy_TEST_FAILED)
105+
string(REPLACE ";" " " command_formatted "${command}")
106+
message(FATAL_ERROR "Command:\n ${command_formatted}\n${RunClangTidy_TEST_FAILED}")
107+
endif()

tidy/tests/subspace-smoke.cc

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
void f() {}

0 commit comments

Comments
 (0)