Compare commits
8 Commits
feat/corpu
...
lint
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
04af0e51f8 | ||
|
|
7a37222fd3 | ||
|
|
8e49d7b9e9 | ||
|
|
98e258d50c | ||
|
|
cb190d3d99 | ||
|
|
2baf947bff | ||
|
|
a17b3f6b7b | ||
|
|
3b45888622 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -68,8 +68,7 @@ tests/unit/Local/
|
||||
.pixi/*
|
||||
!.pixi/config.toml
|
||||
|
||||
.codex/
|
||||
.codex
|
||||
.claude/*
|
||||
!.claude/CLAUDE.md
|
||||
!.claude/commands/
|
||||
openspec/
|
||||
|
||||
@@ -124,8 +124,31 @@ if(CLICE_CI_ENVIRONMENT)
|
||||
target_compile_definitions(clice_options INTERFACE CLICE_CI_ENVIRONMENT=1)
|
||||
endif()
|
||||
|
||||
set(CLICE_CLANG_TIDY_MODULE_LIBRARIES)
|
||||
set(CLICE_MISSING_CLANG_TIDY_MODULES)
|
||||
foreach(module IN LISTS CLICE_CLANG_TIDY_MODULE_COMPONENTS)
|
||||
find_library(CLICE_${module}_LIBRARY
|
||||
NAMES "${module}"
|
||||
PATHS "${LLVM_INSTALL_PATH}/lib"
|
||||
NO_DEFAULT_PATH
|
||||
)
|
||||
if(CLICE_${module}_LIBRARY)
|
||||
list(APPEND CLICE_CLANG_TIDY_MODULE_LIBRARIES "${CLICE_${module}_LIBRARY}")
|
||||
else()
|
||||
list(APPEND CLICE_MISSING_CLANG_TIDY_MODULES "${module}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(CLICE_MISSING_CLANG_TIDY_MODULES)
|
||||
message(STATUS "Clang-tidy module libraries not available: ${CLICE_MISSING_CLANG_TIDY_MODULES}")
|
||||
else()
|
||||
target_compile_definitions(clice_options INTERFACE CLICE_HAS_CLANG_TIDY_MODULES=1)
|
||||
endif()
|
||||
|
||||
set(FBS_SCHEMA_FILE "${PROJECT_SOURCE_DIR}/src/index/schema.fbs")
|
||||
set(GENERATED_HEADER "${PROJECT_BINARY_DIR}/generated/schema_generated.h")
|
||||
set(CLANG_TIDY_CONFIG_SOURCE_FILE "${PROJECT_SOURCE_DIR}/config/clang-tidy-config.h")
|
||||
set(CLANG_TIDY_CONFIG_GENERATED_FILE "${PROJECT_BINARY_DIR}/generated/clang-tidy-config.h")
|
||||
|
||||
if(CMAKE_CROSSCOMPILING)
|
||||
find_program(FLATC_EXECUTABLE flatc REQUIRED)
|
||||
@@ -143,10 +166,21 @@ add_custom_command(
|
||||
|
||||
add_custom_target(generate_flatbuffers_schema DEPENDS "${GENERATED_HEADER}")
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT "${CLANG_TIDY_CONFIG_GENERATED_FILE}"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"${CLANG_TIDY_CONFIG_SOURCE_FILE}"
|
||||
"${CLANG_TIDY_CONFIG_GENERATED_FILE}"
|
||||
DEPENDS "${CLANG_TIDY_CONFIG_SOURCE_FILE}"
|
||||
COMMENT "Generating C++ header from ${CLANG_TIDY_CONFIG_SOURCE_FILE}"
|
||||
)
|
||||
|
||||
add_custom_target(generate_clang_tidy_config DEPENDS "${CLANG_TIDY_CONFIG_GENERATED_FILE}")
|
||||
|
||||
file(GLOB_RECURSE CLICE_CORE_SOURCES CONFIGURE_DEPENDS "${PROJECT_SOURCE_DIR}/src/*.cpp")
|
||||
add_library(clice-core STATIC ${CLICE_CORE_SOURCES})
|
||||
add_library(clice::core ALIAS clice-core)
|
||||
add_dependencies(clice-core generate_flatbuffers_schema)
|
||||
add_dependencies(clice-core generate_flatbuffers_schema generate_clang_tidy_config)
|
||||
|
||||
target_include_directories(clice-core PUBLIC
|
||||
"${PROJECT_SOURCE_DIR}/src"
|
||||
@@ -162,6 +196,9 @@ target_link_libraries(clice-core PUBLIC
|
||||
kota::codec::toml
|
||||
simdjson::simdjson
|
||||
)
|
||||
if(CLICE_CLANG_TIDY_MODULE_LIBRARIES)
|
||||
target_link_libraries(clice-core PUBLIC ${CLICE_CLANG_TIDY_MODULE_LIBRARIES})
|
||||
endif()
|
||||
|
||||
add_executable(clice "${PROJECT_SOURCE_DIR}/src/clice.cc")
|
||||
target_link_libraries(clice PRIVATE clice::core kota::deco)
|
||||
|
||||
@@ -1,5 +1,34 @@
|
||||
include_guard()
|
||||
|
||||
set(CLICE_CLANG_TIDY_MODULE_COMPONENTS
|
||||
# Keep this in sync with scripts/llvm-components.json and the old
|
||||
# ALL_CLANG_TIDY_CHECKS list. MPIModule is intentionally excluded because
|
||||
# clice disables static analyzer checks in ClangTidyForceLinker.h.
|
||||
clangTidyAndroidModule
|
||||
clangTidyAbseilModule
|
||||
clangTidyAlteraModule
|
||||
clangTidyBoostModule
|
||||
clangTidyBugproneModule
|
||||
clangTidyCERTModule
|
||||
clangTidyConcurrencyModule
|
||||
clangTidyCppCoreGuidelinesModule
|
||||
clangTidyDarwinModule
|
||||
clangTidyFuchsiaModule
|
||||
clangTidyGoogleModule
|
||||
clangTidyHICPPModule
|
||||
clangTidyLinuxKernelModule
|
||||
clangTidyLLVMModule
|
||||
clangTidyLLVMLibcModule
|
||||
clangTidyMiscModule
|
||||
clangTidyModernizeModule
|
||||
clangTidyObjCModule
|
||||
clangTidyOpenMPModule
|
||||
clangTidyPerformanceModule
|
||||
clangTidyPortabilityModule
|
||||
clangTidyReadabilityModule
|
||||
clangTidyZirconModule
|
||||
)
|
||||
|
||||
function(setup_llvm LLVM_VERSION)
|
||||
find_package(Python3 COMPONENTS Interpreter REQUIRED)
|
||||
|
||||
@@ -87,29 +116,6 @@ function(setup_llvm LLVM_VERSION)
|
||||
clangSerialization
|
||||
clangTidy
|
||||
clangTidyUtils
|
||||
clangTidyAndroidModule
|
||||
clangTidyAbseilModule
|
||||
clangTidyAlteraModule
|
||||
clangTidyBoostModule
|
||||
clangTidyBugproneModule
|
||||
clangTidyCERTModule
|
||||
clangTidyConcurrencyModule
|
||||
clangTidyCppCoreGuidelinesModule
|
||||
clangTidyDarwinModule
|
||||
clangTidyFuchsiaModule
|
||||
clangTidyGoogleModule
|
||||
clangTidyHICPPModule
|
||||
clangTidyLinuxKernelModule
|
||||
clangTidyLLVMModule
|
||||
clangTidyLLVMLibcModule
|
||||
clangTidyMiscModule
|
||||
clangTidyModernizeModule
|
||||
clangTidyObjCModule
|
||||
clangTidyOpenMPModule
|
||||
clangTidyPerformanceModule
|
||||
clangTidyPortabilityModule
|
||||
clangTidyReadabilityModule
|
||||
clangTidyZirconModule
|
||||
clangTooling
|
||||
clangToolingCore
|
||||
clangToolingInclusions
|
||||
|
||||
@@ -16,7 +16,10 @@ import subprocess
|
||||
import time
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
from typing import Iterable, List, Optional
|
||||
from typing import Iterable, List, Optional, Set
|
||||
|
||||
|
||||
LLVM_COMPONENTS_FILE = Path(__file__).with_name("llvm-components.json")
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
@@ -102,12 +105,33 @@ def run_build(build_dir: Path) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
def protected_library_names() -> Set[str]:
|
||||
data = json.loads(LLVM_COMPONENTS_FILE.read_text())
|
||||
components = data.get("components", [])
|
||||
if not isinstance(components, list):
|
||||
raise ValueError(f"{LLVM_COMPONENTS_FILE} missing 'components' list")
|
||||
|
||||
names: Set[str] = set()
|
||||
for component in components:
|
||||
if not isinstance(component, str):
|
||||
continue
|
||||
if not (component.startswith("clangTidy") and component.endswith("Module")):
|
||||
continue
|
||||
names.add(f"lib{component}.a")
|
||||
names.add(f"{component}.lib")
|
||||
return names
|
||||
|
||||
|
||||
def candidate_files(install_dir: Path) -> Iterable[Path]:
|
||||
if not install_dir.is_dir():
|
||||
raise FileNotFoundError(f"lib dir not found: {install_dir}")
|
||||
protected = protected_library_names()
|
||||
for path in sorted(install_dir.iterdir()):
|
||||
if not path.is_file():
|
||||
continue
|
||||
if path.name in protected:
|
||||
print(f"Keeping protected clang-tidy module library: {path.name}")
|
||||
continue
|
||||
if path.suffix.lower() in {".a", ".lib"}:
|
||||
yield path
|
||||
else:
|
||||
@@ -156,7 +180,11 @@ def apply_manifest(manifest: Path, install_dir: Path) -> None:
|
||||
removed = data.get("removed", [])
|
||||
if not isinstance(removed, list):
|
||||
raise ValueError("Manifest missing 'removed' list")
|
||||
protected = protected_library_names()
|
||||
for name in removed:
|
||||
if name in protected:
|
||||
print(f"Keeping protected clang-tidy module library from manifest: {name}")
|
||||
continue
|
||||
target = install_dir / name
|
||||
if target.exists():
|
||||
print(f"Deleting {target}")
|
||||
|
||||
@@ -53,7 +53,7 @@ struct Options {
|
||||
help =
|
||||
"Agentic method (compileCommand, symbolSearch, definition, references, "
|
||||
"documentSymbols, readSymbol, callGraph, typeHierarchy, projectFiles, "
|
||||
"fileDeps, impactAnalysis, status, shutdown)",
|
||||
"lint, fileDeps, impactAnalysis, status, shutdown)",
|
||||
required = false)
|
||||
<std::string> method;
|
||||
|
||||
|
||||
@@ -418,6 +418,8 @@ CompilationUnit compile(CompilationParams& params, PCMInfo& out) {
|
||||
}
|
||||
|
||||
CompilationUnit complete(CompilationParams& params, clang::CodeCompleteConsumer* consumer) {
|
||||
params.kind = CompilationKind::Completion;
|
||||
|
||||
auto& [file, offset] = params.completion;
|
||||
|
||||
/// The location of clang is 1-1 based.
|
||||
|
||||
@@ -65,7 +65,7 @@ struct PCMInfo : ModuleInfo {
|
||||
|
||||
struct CompilationParams {
|
||||
/// The kind of this compilation.
|
||||
CompilationKind kind;
|
||||
CompilationKind kind = CompilationKind::Content;
|
||||
|
||||
/// Whether to run clang-tidy.
|
||||
bool clang_tidy = false;
|
||||
|
||||
@@ -12,6 +12,10 @@
|
||||
#include "clang-tidy/ClangTidyDiagnosticConsumer.h"
|
||||
#include "clang-tidy/ClangTidyModuleRegistry.h"
|
||||
#include "clang-tidy/ClangTidyOptions.h"
|
||||
#ifdef CLICE_HAS_CLANG_TIDY_MODULES
|
||||
#define CLANG_TIDY_DISABLE_STATIC_ANALYZER_CHECKS
|
||||
#include "clang-tidy/ClangTidyForceLinker.h"
|
||||
#endif
|
||||
|
||||
namespace clice::tidy {
|
||||
|
||||
|
||||
@@ -34,6 +34,34 @@ bool is_dependent(const clang::Decl* D) {
|
||||
return isa<clang::UnresolvedUsingValueDecl>(D);
|
||||
}
|
||||
|
||||
/// Whether a declaration name is backed by source text that should be highlighted.
|
||||
bool can_highlight_name(clang::DeclarationName name) {
|
||||
switch(name.getNameKind()) {
|
||||
case clang::DeclarationName::Identifier: {
|
||||
auto* info = name.getAsIdentifierInfo();
|
||||
return info && !info->getName().empty();
|
||||
}
|
||||
|
||||
case clang::DeclarationName::CXXConstructorName:
|
||||
case clang::DeclarationName::CXXDestructorName: {
|
||||
return true;
|
||||
}
|
||||
|
||||
case clang::DeclarationName::CXXConversionFunctionName:
|
||||
case clang::DeclarationName::CXXOperatorName:
|
||||
case clang::DeclarationName::CXXDeductionGuideName:
|
||||
case clang::DeclarationName::CXXLiteralOperatorName:
|
||||
case clang::DeclarationName::CXXUsingDirective:
|
||||
case clang::DeclarationName::ObjCZeroArgSelector:
|
||||
case clang::DeclarationName::ObjCOneArgSelector:
|
||||
case clang::DeclarationName::ObjCMultiArgSelector: {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::unreachable();
|
||||
}
|
||||
|
||||
/// Returns true if `decl` is considered to be from a default/system library.
|
||||
/// This currently checks the systemness of the file by include type, although
|
||||
/// different heuristics may be used in the future (e.g. sysroot paths).
|
||||
@@ -171,6 +199,10 @@ public:
|
||||
void handleDeclOccurrence(const clang::NamedDecl* decl,
|
||||
RelationKind relation,
|
||||
clang::SourceLocation location) {
|
||||
if(relation.isReference() && !can_highlight_name(decl->getDeclName())) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::uint32_t modifiers = 0;
|
||||
if(relation.is_one_of(RelationKind::Definition)) {
|
||||
// todo: clangd add both Declaration and Definition modifiers for definitions.
|
||||
|
||||
@@ -669,6 +669,7 @@ kota::task<> Compiler::run_compile(std::uint32_t pid, std::shared_ptr<Session::P
|
||||
params.path = file_path;
|
||||
params.version = sess->version;
|
||||
params.text = sess->text;
|
||||
params.clang_tidy = workspace.config.project.clang_tidy.value;
|
||||
if(!fill_compile_args(file_path, params.directory, params.arguments, sess)) {
|
||||
finish_compile();
|
||||
co_return;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "kota/ipc/lsp/protocol.h"
|
||||
#include "kota/ipc/protocol.h"
|
||||
|
||||
namespace clice::agentic {
|
||||
@@ -202,6 +203,13 @@ struct TypeHierarchyResult {
|
||||
std::vector<TypeHierarchyEntry> subtypes;
|
||||
};
|
||||
|
||||
struct LintParams {
|
||||
std::string path;
|
||||
std::optional<int> line;
|
||||
};
|
||||
|
||||
using LintResult = std::vector<kota::ipc::protocol::Diagnostic>;
|
||||
|
||||
struct StatusParams {};
|
||||
|
||||
struct StatusResult {
|
||||
@@ -283,6 +291,12 @@ struct RequestTraits<clice::agentic::TypeHierarchyParams> {
|
||||
constexpr inline static std::string_view method = "agentic/typeHierarchy";
|
||||
};
|
||||
|
||||
template <>
|
||||
struct RequestTraits<clice::agentic::LintParams> {
|
||||
using Result = clice::agentic::LintResult;
|
||||
constexpr inline static std::string_view method = "agentic/lint";
|
||||
};
|
||||
|
||||
template <>
|
||||
struct RequestTraits<clice::agentic::StatusParams> {
|
||||
using Result = clice::agentic::StatusResult;
|
||||
|
||||
@@ -43,6 +43,7 @@ struct CompileParams {
|
||||
std::string text;
|
||||
std::string directory;
|
||||
std::vector<std::string> arguments;
|
||||
bool clang_tidy = false;
|
||||
std::pair<std::string, uint32_t> pch;
|
||||
std::unordered_map<std::string, std::string> pcms;
|
||||
};
|
||||
|
||||
@@ -6,11 +6,14 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "compile/compilation.h"
|
||||
#include "feature/feature.h"
|
||||
#include "server/protocol/agentic.h"
|
||||
#include "server/service/master_server.h"
|
||||
#include "support/filesystem.h"
|
||||
#include "support/logging.h"
|
||||
|
||||
#include "kota/async/async.h"
|
||||
#include "kota/ipc/lsp/uri.h"
|
||||
#include "kota/meta/enum.h"
|
||||
#include "llvm/ADT/DenseSet.h"
|
||||
@@ -769,6 +772,36 @@ AgentClient::AgentClient(MasterServer& server, kota::ipc::JsonPeer& peer) :
|
||||
co_return result;
|
||||
});
|
||||
|
||||
peer.on_request([&srv](RequestContext&, const LintParams& params) -> RequestResult<LintParams> {
|
||||
std::string directory;
|
||||
std::vector<std::string> arguments;
|
||||
if(!srv.compiler.fill_compile_args(params.path, directory, arguments)) {
|
||||
co_return kota::outcome_error(
|
||||
kota::ipc::Error{std::format("no compile command found for {}", params.path)});
|
||||
}
|
||||
|
||||
auto result = co_await kota::queue([path = params.path,
|
||||
directory = std::move(directory),
|
||||
arguments = std::move(arguments)]() mutable {
|
||||
CompilationParams cp;
|
||||
cp.kind = CompilationKind::Content;
|
||||
cp.clang_tidy = true;
|
||||
cp.directory = std::move(directory);
|
||||
for(auto& arg: arguments) {
|
||||
cp.arguments.push_back(arg.c_str());
|
||||
}
|
||||
|
||||
auto unit = compile(cp);
|
||||
if(!unit.completed() && !unit.fatal_error()) {
|
||||
LOG_WARN("Lint compilation failed: {}", path);
|
||||
return LintResult{};
|
||||
}
|
||||
|
||||
return feature::diagnostics(unit);
|
||||
});
|
||||
co_return result.value();
|
||||
});
|
||||
|
||||
peer.on_request([&srv](RequestContext&, const StatusParams&) -> RequestResult<StatusParams> {
|
||||
StatusResult result;
|
||||
result.idle = srv.indexer.is_idle();
|
||||
|
||||
@@ -85,6 +85,9 @@ static kota::task<> agentic_request(kota::ipc::JsonPeer& peer,
|
||||
.line = line,
|
||||
.direction = dir,
|
||||
});
|
||||
} else if(opts.method == "lint") {
|
||||
auto line = opts.line > 0 ? std::optional(opts.line) : std::nullopt;
|
||||
ok = co_await send_and_print(peer, agentic::LintParams{.path = opts.path, .line = line});
|
||||
} else if(opts.method == "fileDeps") {
|
||||
auto dir = opts.direction.empty() ? std::nullopt : std::optional(opts.direction);
|
||||
ok = co_await send_and_print(peer,
|
||||
|
||||
@@ -152,6 +152,7 @@ void StatefulWorker::register_handlers() {
|
||||
|
||||
CompilationParams cp;
|
||||
cp.kind = CompilationKind::Content;
|
||||
cp.clang_tidy = params.clang_tidy;
|
||||
fill_args(cp, doc->directory, doc->arguments);
|
||||
if(!doc->pch.first.empty()) {
|
||||
cp.pch = doc->pch;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "test/test.h"
|
||||
#include "compile/compilation.h"
|
||||
#include "compile/implement.h"
|
||||
|
||||
namespace clice::testing {
|
||||
namespace {
|
||||
@@ -7,6 +8,10 @@ namespace {
|
||||
TEST_SUITE(ClangTidy) {
|
||||
|
||||
TEST_CASE(FastCheck) {
|
||||
#ifdef CLICE_HAS_CLANG_TIDY_MODULES
|
||||
ASSERT_TRUE(tidy::is_registered_tidy_check("bugprone-integer-division"));
|
||||
#endif
|
||||
|
||||
// ASSERT_TRUE(tidy::is_fast_tidy_check("readability-misleading-indentation"));
|
||||
// ASSERT_TRUE(tidy::is_fast_tidy_check("bugprone-unused-return-value"));
|
||||
//
|
||||
@@ -22,6 +27,7 @@ TEST_CASE(Tidy) {
|
||||
|
||||
std::string main_path = TestVFS::path("main.cpp");
|
||||
CompilationParams params;
|
||||
params.kind = CompilationKind::Content;
|
||||
params.clang_tidy = true;
|
||||
params.vfs = vfs;
|
||||
params.arguments = {"clang++", "-ffreestanding", "-Xclang", "-undef", main_path.c_str()};
|
||||
@@ -30,6 +36,37 @@ TEST_CASE(Tidy) {
|
||||
ASSERT_FALSE(unit.diagnostics().empty());
|
||||
}
|
||||
|
||||
#ifdef CLICE_HAS_CLANG_TIDY_MODULES
|
||||
TEST_CASE(BugproneIntegerDivision) {
|
||||
auto vfs = llvm::makeIntrusiveRefCnt<TestVFS>();
|
||||
vfs->add("main.cpp",
|
||||
"int main() {"
|
||||
" double d;"
|
||||
" int i = 42;"
|
||||
" d = 32 * 8 / (2 + i);"
|
||||
" return static_cast<int>(d);"
|
||||
"}");
|
||||
|
||||
std::string main_path = TestVFS::path("main.cpp");
|
||||
CompilationParams params;
|
||||
params.kind = CompilationKind::Content;
|
||||
params.clang_tidy = true;
|
||||
params.vfs = vfs;
|
||||
params.arguments = {"clang++", "-ffreestanding", "-Xclang", "-undef", main_path.c_str()};
|
||||
auto unit = compile(params);
|
||||
ASSERT_TRUE(unit.completed());
|
||||
|
||||
bool found = false;
|
||||
for(auto& diagnostic: unit.diagnostics()) {
|
||||
if(llvm::StringRef(diagnostic.message).contains("integer division")) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ASSERT_TRUE(found);
|
||||
}
|
||||
#endif
|
||||
|
||||
}; // TEST_SUITE(ClangTidy)
|
||||
} // namespace
|
||||
} // namespace clice::testing
|
||||
|
||||
@@ -140,6 +140,10 @@ void EXPECT_TOKEN(llvm::StringRef name,
|
||||
ASSERT_EQ(token->modifiers, expected_modifiers);
|
||||
}
|
||||
|
||||
void EXPECT_NO_TOKEN(llvm::StringRef name) {
|
||||
ASSERT_TRUE(find_by_range(name) == nullptr);
|
||||
}
|
||||
|
||||
TEST_CASE(BasicLexicalKinds) {
|
||||
run_utf8(R"cpp(
|
||||
@d1[#define] @m0[FOO]
|
||||
@@ -266,6 +270,44 @@ int main() {
|
||||
EXPECT_TOKEN("x3", SymbolKind::Variable, 0);
|
||||
}
|
||||
|
||||
TEST_CASE(IneligibleOperatorReferenceIsSuppressed) {
|
||||
run_utf8(R"cpp(
|
||||
struct S {};
|
||||
|
||||
S operator+(S lhs, S rhs);
|
||||
|
||||
void use(S lhs, S rhs) {
|
||||
(void)(lhs @plus[+] rhs);
|
||||
}
|
||||
)cpp");
|
||||
|
||||
EXPECT_NO_TOKEN("plus");
|
||||
}
|
||||
|
||||
TEST_CASE(ConstructorAndDestructorNamesRemainHighlighted) {
|
||||
run_utf8(R"cpp(
|
||||
struct S {
|
||||
@ctor_decl[S]();
|
||||
@dtor_decl[~]S();
|
||||
};
|
||||
|
||||
S::@ctor_def[S]() {}
|
||||
|
||||
void use(S* value) {
|
||||
value->@dtor_ref[~]S();
|
||||
}
|
||||
)cpp");
|
||||
|
||||
auto declaration = modifier_mask({SymbolModifiers::Declaration});
|
||||
auto definition = modifier_mask({SymbolModifiers::Definition});
|
||||
auto special_member = modifier_mask({SymbolModifiers::ConstructorOrDestructor});
|
||||
|
||||
EXPECT_TOKEN("ctor_decl", SymbolKind::Method, declaration | special_member);
|
||||
EXPECT_TOKEN("dtor_decl", SymbolKind::Method, declaration | special_member);
|
||||
EXPECT_TOKEN("ctor_def", SymbolKind::Method, definition | special_member);
|
||||
EXPECT_TOKEN("dtor_ref", SymbolKind::Method, special_member);
|
||||
}
|
||||
|
||||
TEST_CASE(LegacyVarDeclTemplates) {
|
||||
run_utf8(R"cpp(
|
||||
extern int @x1[x];
|
||||
|
||||
Reference in New Issue
Block a user