[libc++] Refactor clang-query checks to clang-tidy checks to get less obscure error messages

Also remove clang-query related code, since it's unused now.

Reviewed By: ldionne, Mordante, #libc

Spies: libcxx-commits, arichardson

Differential Revision: https://reviews.llvm.org/D141805
This commit is contained in:
Nikolas Klauser 2023-01-09 03:01:26 +01:00
parent 7458908f12
commit 06385491a6
14 changed files with 148 additions and 358 deletions

View File

@ -98,8 +98,7 @@ Additional tools
The libc++ test suite uses a few optional tools to improve the code quality.
These tools are:
- clang-query
- clang-tidy
- clang-tidy (you might need additional dev packages to compile libc++-specific clang-tidy checks)
Writing Tests
-------------

View File

@ -85,7 +85,7 @@ protected:
void do_deallocate(void* __p, size_t __bytes, size_t __align) override;
_LIBCPP_HIDE_FROM_ABI bool do_is_equal(const memory_resource& __other) const _NOEXCEPT override {
_LIBCPP_HIDE_FROM_ABI_VIRTUAL bool do_is_equal(const memory_resource& __other) const _NOEXCEPT override {
return &__other == this;
}

View File

@ -1,277 +0,0 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Run clang-query on the headers - see clang_query/README.md for more information
// REQUIRES: has-clang-query
// The attributes hide_from_abi_of_visible.query relies on aren't applied on windows.
// XFAIL: windows
// RUN: %{clang-query} -f %S/clang_query/hide_from_abi_or_visible.query %s --use-color -- -Wno-unknown-warning-option %{compile_flags} -fno-modules > %t.output
// RUN: cat %t.output
// RUN: cat %t.output | wc -l | grep -Fxq 1
// RUN: %{clang-query} -f %S/clang_query/abi_tag_on_virtual.query %s --use-color -- -Wno-unknown-warning-option %{compile_flags} -fno-modules > %t.output
// RUN: cat %t.output
// RUN: cat %t.output | wc -l | grep -Fxq 1
// Prevent <ext/hash_map> from generating deprecated warnings for this test.
#if defined(__DEPRECATED)
# undef __DEPRECATED
#endif
/*
BEGIN-SCRIPT
for header in public_headers:
print("{}#{}include <{}>{}".format(
'#if ' + header_restrictions[header] + '\n' if header in header_restrictions else '',
3 * ' ' if header in header_restrictions else '',
header,
'\n#endif' if header in header_restrictions else ''
))
END-SCRIPT
*/
// DO NOT MANUALLY EDIT ANYTHING BETWEEN THE MARKERS BELOW
// GENERATED-MARKER
#include <algorithm>
#include <any>
#include <array>
#include <atomic>
#if !defined(_LIBCPP_HAS_NO_THREADS)
# include <barrier>
#endif
#include <bit>
#include <bitset>
#include <cassert>
#include <ccomplex>
#include <cctype>
#include <cerrno>
#include <cfenv>
#include <cfloat>
#include <charconv>
#include <chrono>
#include <cinttypes>
#include <ciso646>
#include <climits>
#if !defined(_LIBCPP_HAS_NO_LOCALIZATION)
# include <clocale>
#endif
#include <cmath>
#if !defined(_LIBCPP_HAS_NO_LOCALIZATION)
# include <codecvt>
#endif
#include <compare>
#include <complex>
#include <complex.h>
#include <concepts>
#include <condition_variable>
#include <coroutine>
#include <csetjmp>
#include <csignal>
#include <cstdarg>
#include <cstdbool>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctgmath>
#include <ctime>
#include <ctype.h>
#include <cuchar>
#if !defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS)
# include <cwchar>
#endif
#if !defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS)
# include <cwctype>
#endif
#include <deque>
#include <errno.h>
#include <exception>
#include <execution>
#include <fenv.h>
#if !defined(_LIBCPP_HAS_NO_FILESYSTEM_LIBRARY)
# include <filesystem>
#endif
#include <float.h>
#include <format>
#include <forward_list>
#if !defined(_LIBCPP_HAS_NO_LOCALIZATION)
# include <fstream>
#endif
#include <functional>
#if !defined(_LIBCPP_HAS_NO_THREADS)
# include <future>
#endif
#include <initializer_list>
#include <inttypes.h>
#if !defined(_LIBCPP_HAS_NO_LOCALIZATION)
# include <iomanip>
#endif
#if !defined(_LIBCPP_HAS_NO_LOCALIZATION)
# include <ios>
#endif
#include <iosfwd>
#if !defined(_LIBCPP_HAS_NO_LOCALIZATION)
# include <iostream>
#endif
#if !defined(_LIBCPP_HAS_NO_LOCALIZATION)
# include <istream>
#endif
#include <iterator>
#if !defined(_LIBCPP_HAS_NO_THREADS)
# include <latch>
#endif
#include <limits>
#include <limits.h>
#include <list>
#if !defined(_LIBCPP_HAS_NO_LOCALIZATION)
# include <locale>
#endif
#if !defined(_LIBCPP_HAS_NO_LOCALIZATION)
# include <locale.h>
#endif
#include <map>
#include <math.h>
#include <memory>
#if !defined(_LIBCPP_HAS_NO_THREADS)
# include <mutex>
#endif
#include <new>
#include <numbers>
#include <numeric>
#include <optional>
#if !defined(_LIBCPP_HAS_NO_LOCALIZATION)
# include <ostream>
#endif
#include <queue>
#include <random>
#include <ranges>
#include <ratio>
#if !defined(_LIBCPP_HAS_NO_LOCALIZATION)
# include <regex>
#endif
#include <scoped_allocator>
#if !defined(_LIBCPP_HAS_NO_THREADS)
# include <semaphore>
#endif
#include <set>
#include <setjmp.h>
#if !defined(_LIBCPP_HAS_NO_THREADS)
# include <shared_mutex>
#endif
#include <span>
#if !defined(_LIBCPP_HAS_NO_LOCALIZATION)
# include <sstream>
#endif
#include <stack>
#if __cplusplus > 202002L && !defined(_LIBCPP_HAS_NO_THREADS)
# include <stdatomic.h>
#endif
#include <stdbool.h>
#include <stddef.h>
#include <stdexcept>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#if !defined(_LIBCPP_HAS_NO_LOCALIZATION)
# include <streambuf>
#endif
#include <string>
#include <string.h>
#include <string_view>
#if !defined(_LIBCPP_HAS_NO_LOCALIZATION)
# include <strstream>
#endif
#include <system_error>
#include <tgmath.h>
#if !defined(_LIBCPP_HAS_NO_THREADS)
# include <thread>
#endif
#include <tuple>
#include <type_traits>
#include <typeindex>
#include <typeinfo>
#include <uchar.h>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <valarray>
#include <variant>
#include <vector>
#include <version>
#if !defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS)
# include <wchar.h>
#endif
#if !defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS)
# include <wctype.h>
#endif
#if __cplusplus >= 201103L
# include <experimental/algorithm>
#endif
#if __cplusplus >= 201103L && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_COROUTINES)
# include <experimental/coroutine>
#endif
#if __cplusplus >= 201103L
# include <experimental/deque>
#endif
#if __cplusplus >= 201103L
# include <experimental/forward_list>
#endif
#if __cplusplus >= 201103L
# include <experimental/functional>
#endif
#if __cplusplus >= 201103L
# include <experimental/iterator>
#endif
#if __cplusplus >= 201103L
# include <experimental/list>
#endif
#if __cplusplus >= 201103L
# include <experimental/map>
#endif
#if __cplusplus >= 201103L
# include <experimental/memory_resource>
#endif
#if __cplusplus >= 201103L
# include <experimental/propagate_const>
#endif
#if !defined(_LIBCPP_HAS_NO_LOCALIZATION) && __cplusplus >= 201103L
# include <experimental/regex>
#endif
#if __cplusplus >= 201103L
# include <experimental/set>
#endif
#if __cplusplus >= 201103L
# include <experimental/simd>
#endif
#if __cplusplus >= 201103L
# include <experimental/string>
#endif
#if __cplusplus >= 201103L
# include <experimental/type_traits>
#endif
#if __cplusplus >= 201103L
# include <experimental/unordered_map>
#endif
#if __cplusplus >= 201103L
# include <experimental/unordered_set>
#endif
#if __cplusplus >= 201103L
# include <experimental/utility>
#endif
#if __cplusplus >= 201103L
# include <experimental/vector>
#endif
#include <ext/hash_map>
#include <ext/hash_set>
// GENERATED-MARKER

View File

@ -1,4 +0,0 @@
This directory contains [AST matchers](https://clang.llvm.org/docs/LibASTMatchers.html) for clang-query.
These allow us to enforce some rules in the libc++ source code which are hard to enforce through other means,
like clang-tidy or regex matchers.

View File

@ -1,29 +0,0 @@
#===------------------------------------------------------------------------===#
#
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
# See https://llvm.org/LICENSE.txt for license information.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#
#===------------------------------------------------------------------------===#
# This clang-query test ensures that we don't place an abi_tag attribute on
# virtual functions. This can happen by mistakenly applying a macro like
# _LIBCPP_HIDE_FROM_ABI on a virtual function.
#
# The problem is that arm64e pointer authentication extensions use the mangled
# name of the function to sign the function pointer in the vtable, which means
# that the ABI tag effectively influences how the pointers are signed.
#
# This can lead to PAC failures when passing an object that holds one of these
# pointers in its vtable across an ABI boundary if the two sides have been compiled
# with different versions of libc++: one side will sign the pointer using one function
# mangling (with one ABI tag), and the other side will authenticate the pointer expecting
# it to have a different mangled name due to the ABI tag being different, which will crash.
#
# This test ensures that we don't re-introduce this issue in the code base.
match
cxxMethodDecl(isVirtual(),
hasAttr("attr::AbiTag"),
unless(isExpansionInSystemHeader())
)

View File

@ -1,37 +0,0 @@
#===------------------------------------------------------------------------===#
#
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
# See https://llvm.org/LICENSE.txt for license information.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#
#===------------------------------------------------------------------------===#
# Check that all functions in the libc++ headers are either marked with hidden
# visibility or default visibility
# TODO: enable the check for all functions once we don't force-inline everything with GCC
match
functionDecl(
unless(
anyOf(
hasName("__introsort"),
hasName("__inplace_merge"),
hasName("__libcpp_snprintf_l"),
hasName("__libcpp_asprintf_l"),
hasName("__libcpp_sscanf_l"),
hasName("__tree_sub_invariant"),
hasName("__stable_sort_move"),
hasName("__stable_sort"),
hasName("__stable_partition"),
hasName("__lock_first"),
hasName("__stable_partition_impl"),
hasAttr("attr::Visibility"),
hasAttr("attr::AbiTag"),
cxxMethodDecl(), # We have explicitly instantiated classes and some of their methods don't have these attributes
isDeleted(),
isConsteval(),
isExpansionInSystemHeader()
)
),
isDefinition()
)

View File

@ -6,11 +6,13 @@ set(CMAKE_FIND_PACKAGE_SORT_DIRECTION DEC)
find_package(Clang 16)
set(SOURCES
abi_tag_on_virtual.cpp
hide_from_abi.cpp
robust_against_adl.cpp
libcpp_module.cpp
qualify_declval.cpp
)
libcpp_module.cpp
)
if(NOT Clang_FOUND)
message(STATUS "Could not find a suitable version of the Clang development package;

View File

@ -0,0 +1,46 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "clang-tidy/ClangTidyCheck.h"
#include "clang-tidy/ClangTidyModuleRegistry.h"
#include "abi_tag_on_virtual.hpp"
// This clang-tidy check ensures that we don't place an abi_tag attribute on
// virtual functions. This can happen by mistakenly applying a macro like
// _LIBCPP_HIDE_FROM_ABI on a virtual function.
//
// The problem is that arm64e pointer authentication extensions use the mangled
// name of the function to sign the function pointer in the vtable, which means
// that the ABI tag effectively influences how the pointers are signed.
//
// This can lead to PAC failures when passing an object that holds one of these
// pointers in its vtable across an ABI boundary if the two sides have been compiled
// with different versions of libc++: one side will sign the pointer using one function
// mangling (with one ABI tag), and the other side will authenticate the pointer expecting
// it to have a different mangled name due to the ABI tag being different, which will crash.
//
// This test ensures that we don't re-introduce this issue in the code base.
namespace libcpp {
abi_tag_on_virtual::abi_tag_on_virtual(llvm::StringRef name, clang::tidy::ClangTidyContext* context)
: clang::tidy::ClangTidyCheck(name, context) {}
void abi_tag_on_virtual::registerMatchers(clang::ast_matchers::MatchFinder* finder) {
using namespace clang::ast_matchers;
finder->addMatcher(cxxMethodDecl(isVirtual(), hasAttr(clang::attr::AbiTag)).bind("abi_tag_on_virtual"), this);
}
void abi_tag_on_virtual::check(const clang::ast_matchers::MatchFinder::MatchResult& result) {
if (const auto* call = result.Nodes.getNodeAs<clang::CXXMethodDecl>("abi_tag_on_virtual"); call != nullptr) {
diag(call->getBeginLoc(),
"_LIBCPP_HIDE_FROM_ABI should not be used on virtual functions to avoid problems with pointer authentication. "
"Use _LIBCPP_HIDE_FROM_ABI_VIRTUAL instead.");
}
}
} // namespace libcpp

View File

@ -0,0 +1,18 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "clang-tidy/ClangTidyCheck.h"
namespace libcpp {
class abi_tag_on_virtual : public clang::tidy::ClangTidyCheck {
public:
abi_tag_on_virtual(llvm::StringRef, clang::tidy::ClangTidyContext*);
void registerMatchers(clang::ast_matchers::MatchFinder*) override;
void check(const clang::ast_matchers::MatchFinder::MatchResult&) override;
};
} // namespace libcpp

View File

@ -0,0 +1,55 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "clang-tidy/ClangTidyCheck.h"
#include "clang-tidy/ClangTidyModuleRegistry.h"
#include "hide_from_abi.hpp"
namespace libcpp {
hide_from_abi::hide_from_abi(llvm::StringRef name, clang::tidy::ClangTidyContext* context)
: clang::tidy::ClangTidyCheck(name, context) {}
void hide_from_abi::registerMatchers(clang::ast_matchers::MatchFinder* finder) {
using namespace clang::ast_matchers;
finder->addMatcher(
functionDecl(
unless(anyOf(
// These functions can't be marked `[[gnu::always_inline]]` for various reasons,
// so we can't mark them `_LIBCPP_HIDE_FROM_ABI`. These functions are ignored in
// all namespaces. Checking the qualified name is a lot harder and these names
// should result in very few (if any) false-negatives. This is also just a
// temporary work-around until we can mark functions as HIDE_FROM_ABI without
// having to add `[[gnu::always_inline]]` with GCC.
hasAnyName("__introsort",
"__inplace_merge",
"__libcpp_snprintf_l",
"__libcpp_asprintf_l",
"__libcpp_sscanf_l",
"__tree_sub_invariant",
"__stable_sort_move",
"__stable_sort",
"__stable_partition",
"__lock_first",
"__stable_partition_impl"),
hasAttr(clang::attr::Visibility),
hasAttr(clang::attr::AbiTag),
cxxMethodDecl(), // We have explicitly instantiated classes and some of their methods don't have these attributes
isDeleted(),
isConsteval())),
isDefinition())
.bind("missing_hide_from_abi"),
this);
}
void hide_from_abi::check(const clang::ast_matchers::MatchFinder::MatchResult& result) {
if (const auto* call = result.Nodes.getNodeAs<clang::FunctionDecl>("missing_hide_from_abi"); call != nullptr) {
diag(call->getBeginLoc(), "_LIBCPP_HIDE_FROM_ABI is missing");
}
}
} // namespace libcpp

View File

@ -0,0 +1,18 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "clang-tidy/ClangTidyCheck.h"
namespace libcpp {
class hide_from_abi : public clang::tidy::ClangTidyCheck {
public:
hide_from_abi(llvm::StringRef, clang::tidy::ClangTidyContext*);
void registerMatchers(clang::ast_matchers::MatchFinder*) override;
void check(const clang::ast_matchers::MatchFinder::MatchResult&) override;
};
} // namespace libcpp

View File

@ -8,6 +8,9 @@
#include "clang-tidy/ClangTidyModule.h"
#include "clang-tidy/ClangTidyModuleRegistry.h"
#include "abi_tag_on_virtual.hpp"
#include "hide_from_abi.hpp"
#include "robust_against_adl.hpp"
#include "qualify_declval.hpp"
@ -15,6 +18,8 @@ namespace {
class LibcxxTestModule : public clang::tidy::ClangTidyModule {
public:
void addCheckFactories(clang::tidy::ClangTidyCheckFactories& check_factories) override {
check_factories.registerCheck<libcpp::abi_tag_on_virtual>("libcpp-avoid-abi-tag-on-virtual");
check_factories.registerCheck<libcpp::hide_from_abi>("libcpp-hide-from-abi");
check_factories.registerCheck<libcpp::robust_against_adl_check>("libcpp-robust-against-adl");
check_factories.registerCheck<libcpp::qualify_declval>("libcpp-qualify-declval");
}

View File

@ -91,9 +91,6 @@ RUN apt-get update && apt-get install -y llvm-$(($LLVM_HEAD_VERSION - 1))-dev
# TODO LLVM16 Don't install llvm-16-dev explicitly
RUN apt-get update && apt-get install -y llvm-16-dev libclang-16-dev
# Install clang-tools
RUN apt-get update && apt-get install -y clang-tools-$(($LLVM_HEAD_VERSION - 1)) clang-tools-$LLVM_HEAD_VERSION
# Install the most recent GCC, like clang install the previous version as a transition.
ENV GCC_LATEST_VERSION=12
RUN apt-get update && apt install -y gcc-$((GCC_LATEST_VERSION - 1)) g++-$((GCC_LATEST_VERSION - 1))

View File

@ -159,9 +159,6 @@ DEFAULT_FEATURES = [
Feature(name='has-clang-tidy',
when=lambda cfg: _getSuitableClangTidy(cfg) is not None,
actions=[AddSubstitution('%{clang-tidy}', lambda cfg: _getSuitableClangTidy(cfg))]),
Feature(name='has-clang-query',
when=lambda cfg: runScriptExitCode(cfg, ['clang-query-15 --version']) == 0,
actions=[AddSubstitution('%{clang-query}', 'clang-query-15')]),
Feature(name='apple-clang', when=_isAppleClang),
Feature(name=lambda cfg: 'apple-clang-{__clang_major__}'.format(**compilerMacros(cfg)), when=_isAppleClang),