fold/tests/unit/test_deprecated_api.cpp

105 lines
3.6 KiB
C++

// Matrix row C7 — deprecated-API detector. Flags call sites whose
// target decl is marked `[[deprecated]]`, `__attribute__((deprecated))`,
// or `__attribute__((availability(...,deprecated=...)))`. Uses
// clang::Decl::isDeprecated under the hood.
#include <clang/Frontend/ASTUnit.h>
#include <clang/Tooling/Tooling.h>
#include <catch2/catch_test_macros.hpp>
#include <memory>
#include <string>
#include "fold/detect/deprecated_api.hpp"
#include "fold/report/finding.hpp"
namespace {
std::unique_ptr<clang::ASTUnit> parse(const std::string& code) {
return clang::tooling::buildASTFromCodeWithArgs(code, {"-std=c++20"}, "input.cpp");
}
} // namespace
TEST_CASE("deprecated-api: [[deprecated]] call site is flagged",
"[deprecated-api][cxx-attribute]") {
auto unit = parse(R"(
[[deprecated]] int legacy();
int caller() { return legacy(); }
)");
REQUIRE(unit);
auto findings = fold::detectDeprecatedApiInContext(unit->getASTContext());
REQUIRE(findings.size() == 1);
REQUIRE(findings[0].kind == fold::FindingKind::DeprecatedApi);
}
TEST_CASE("deprecated-api: __attribute__((deprecated)) call site is flagged",
"[deprecated-api][gnu-attribute]") {
auto unit = parse(R"(
__attribute__((deprecated)) int legacy();
int caller() { return legacy(); }
)");
REQUIRE(unit);
auto findings = fold::detectDeprecatedApiInContext(unit->getASTContext());
REQUIRE(findings.size() == 1);
REQUIRE(findings[0].kind == fold::FindingKind::DeprecatedApi);
}
TEST_CASE("deprecated-api: deprecation message is carried into the finding",
"[deprecated-api][message]") {
auto unit = parse(R"(
[[deprecated("use modern_api() instead")]] int legacy();
int caller() { return legacy(); }
)");
REQUIRE(unit);
auto findings = fold::detectDeprecatedApiInContext(unit->getASTContext());
REQUIRE(findings.size() == 1);
REQUIRE(findings[0].message.find("use modern_api() instead") != std::string::npos);
}
TEST_CASE("deprecated-api: non-deprecated calls do not fire", "[deprecated-api][negative]") {
auto unit = parse(R"(
int modern();
int caller() { return modern(); }
)");
REQUIRE(unit);
auto findings = fold::detectDeprecatedApiInContext(unit->getASTContext());
REQUIRE(findings.empty());
}
TEST_CASE("deprecated-api: declaration alone is not flagged (only call sites)",
"[deprecated-api][decl-only]") {
// A deprecated declaration without any call sites should produce
// zero findings — the detector flags USE, not definition.
auto unit = parse(R"(
[[deprecated]] int legacy();
// no caller
)");
REQUIRE(unit);
auto findings = fold::detectDeprecatedApiInContext(unit->getASTContext());
REQUIRE(findings.empty());
}
TEST_CASE("deprecated-api: multiple call sites produce multiple findings",
"[deprecated-api][repeat]") {
auto unit = parse(R"(
[[deprecated]] int legacy();
int caller() { return legacy() + legacy(); }
)");
REQUIRE(unit);
auto findings = fold::detectDeprecatedApiInContext(unit->getASTContext());
REQUIRE(findings.size() == 2);
}
TEST_CASE("deprecated-api: deprecated method on a class is flagged", "[deprecated-api][method]") {
auto unit = parse(R"(
struct S {
[[deprecated]] int legacy();
int modern();
};
int caller(S* s) { return s->legacy() + s->modern(); }
)");
REQUIRE(unit);
auto findings = fold::detectDeprecatedApiInContext(unit->getASTContext());
REQUIRE(findings.size() == 1);
}