105 lines
3.6 KiB
C++
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);
|
|
}
|