From a5f0954c290157875b4dfb79edcf651f55742dc2 Mon Sep 17 00:00:00 2001 From: Jade Lovelace Date: Sun, 4 Aug 2024 22:02:53 -0700 Subject: [PATCH] clang-tidy: write a lint for charptr_cast This lets us ensure that nobody is putting in new reinterpret_cast instances where they could safely use charptr_cast instead. Change-Id: I6358a3934c8133c7150042635843bdbb6b9218d4 --- .clang-tidy | 7 +++ subprojects/lix-clang-tidy/CharPtrCast.cc | 45 +++++++++++++++++++ subprojects/lix-clang-tidy/CharPtrCast.hh | 32 +++++++++++++ .../lix-clang-tidy/LixClangTidyChecks.cc | 2 + subprojects/lix-clang-tidy/meson.build | 3 +- 5 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 subprojects/lix-clang-tidy/CharPtrCast.cc create mode 100644 subprojects/lix-clang-tidy/CharPtrCast.hh diff --git a/.clang-tidy b/.clang-tidy index ccfdf9e7d..87f6d0404 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -22,6 +22,13 @@ Checks: - cppcoreguidelines-avoid-capturing-lambda-coroutines # crimes must be appropriately declared as crimes - cppcoreguidelines-pro-type-cstyle-cast + - lix-* + # This can not yet be applied to Lix itself since we need to do source + # reorganization so that lix/ include paths work. + - -lix-fixincludes + # This lint is included as an example, but the lib function it replaces is + # already gone. + - -lix-hasprefixsuffix CheckOptions: diff --git a/subprojects/lix-clang-tidy/CharPtrCast.cc b/subprojects/lix-clang-tidy/CharPtrCast.cc new file mode 100644 index 000000000..e76797f5d --- /dev/null +++ b/subprojects/lix-clang-tidy/CharPtrCast.cc @@ -0,0 +1,45 @@ +#include "CharPtrCast.hh" +#include +#include +#include + +namespace nix::clang_tidy { +using namespace clang::ast_matchers; +using namespace clang; + +void CharPtrCastCheck::registerMatchers(ast_matchers::MatchFinder *Finder) { + Finder->addMatcher( + traverse(clang::TK_IgnoreUnlessSpelledInSource, + cxxReinterpretCastExpr(allOf( + hasDestinationType(qualType(pointsTo(isAnyCharacter()))), + has(expr(hasType(qualType(pointsTo(isAnyCharacter())))))))) + .bind("reinterpret-cast-expr"), + this); +} + +void CharPtrCastCheck::check( + const ast_matchers::MatchFinder::MatchResult &Result) { + const auto ReinterpretCastExpr = + Result.Nodes.getNodeAs("reinterpret-cast-expr"); + const auto ToTypeSpan = ReinterpretCastExpr->getAngleBrackets(); + const auto & SM = Result.Context->getSourceManager(); + + auto Diag = + diag(ReinterpretCastExpr->getExprLoc(), + "reinterpret_cast used for trivially safe character pointer cast"); + Diag << ReinterpretCastExpr->getSourceRange(); + + auto Inside = tooling::getText(*ReinterpretCastExpr->getSubExprAsWritten(), + *Result.Context); + + Diag << Inserter.createIncludeInsertion(SM.getFileID(ReinterpretCastExpr->getExprLoc()), "charptr-cast.hh"); + + llvm::Twine Replacement = + "charptr_cast" + + tooling::getText(CharSourceRange(ToTypeSpan, true), *Result.Context) + + "(" + Inside + ")"; + Diag << FixItHint::CreateReplacement(ReinterpretCastExpr->getSourceRange(), + Replacement.str()); +} + +} // namespace nix::clang_tidy diff --git a/subprojects/lix-clang-tidy/CharPtrCast.hh b/subprojects/lix-clang-tidy/CharPtrCast.hh new file mode 100644 index 000000000..66883d055 --- /dev/null +++ b/subprojects/lix-clang-tidy/CharPtrCast.hh @@ -0,0 +1,32 @@ +#pragma once +///@file + +#include +#include +#include +#include + +namespace nix::clang_tidy { + +using namespace clang; +using namespace clang::tidy; + +class CharPtrCastCheck : public ClangTidyCheck { + tidy::utils::IncludeInserter Inserter{ + Options.getLocalOrGlobal("IncludeStyle", + tidy::utils::IncludeSorter::IS_Google), + false}; + +public: + CharPtrCastCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + + void registerPPCallbacks(const SourceManager &, Preprocessor *PP, + Preprocessor *) override { + Inserter.registerPreprocessor(PP); + } + + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; +} // namespace nix::clang_tidy diff --git a/subprojects/lix-clang-tidy/LixClangTidyChecks.cc b/subprojects/lix-clang-tidy/LixClangTidyChecks.cc index b3503dd3a..283e0e7c8 100644 --- a/subprojects/lix-clang-tidy/LixClangTidyChecks.cc +++ b/subprojects/lix-clang-tidy/LixClangTidyChecks.cc @@ -2,6 +2,7 @@ #include #include "FixIncludes.hh" #include "HasPrefixSuffix.hh" +#include "CharPtrCast.hh" namespace nix::clang_tidy { using namespace clang; @@ -12,6 +13,7 @@ class NixClangTidyChecks : public ClangTidyModule { void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { CheckFactories.registerCheck("lix-hasprefixsuffix"); CheckFactories.registerCheck("lix-fixincludes"); + CheckFactories.registerCheck("lix-charptrcast"); } }; diff --git a/subprojects/lix-clang-tidy/meson.build b/subprojects/lix-clang-tidy/meson.build index ef0226420..43648a1c8 100644 --- a/subprojects/lix-clang-tidy/meson.build +++ b/subprojects/lix-clang-tidy/meson.build @@ -5,9 +5,10 @@ project('lix-clang-tidy', ['cpp', 'c'], llvm = dependency('Clang', version: '>= 17', modules: ['libclang']) sources = files( + 'CharPtrCast.cc', + 'FixIncludes.cc', 'HasPrefixSuffix.cc', 'LixClangTidyChecks.cc', - 'FixIncludes.cc', ) lix_clang_tidy = shared_module('lix-clang-tidy', sources,