Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,11 @@ static FindArgsResult findArgs(const CallExpr *Call) {
return Result;
}

static SmallVector<FixItHint>
// Returns `true` as `first` only if a nested call to `std::min` or
// `std::max` was found. Checking if `FixItHint`s were generated is not enough,
// as the explicit casts that the check introduces may be generated without a
// nested `std::min` or `std::max` call.
static std::pair<bool, SmallVector<FixItHint>>
generateReplacements(const MatchFinder::MatchResult &Match,
const CallExpr *TopCall, const FindArgsResult &Result,
const bool IgnoreNonTrivialTypes,
Expand All @@ -91,13 +95,15 @@ generateReplacements(const MatchFinder::MatchResult &Match,
const bool IsResultTypeTrivial = ResultType.isTrivialType(*Match.Context);

if ((!IsResultTypeTrivial && IgnoreNonTrivialTypes))
return FixItHints;
return {false, FixItHints};

if (IsResultTypeTrivial &&
static_cast<std::uint64_t>(
Match.Context->getTypeSizeInChars(ResultType).getQuantity()) >
IgnoreTrivialTypesOfSizeAbove)
return FixItHints;
return {false, FixItHints};

bool FoundNestedCall = false;

for (const Expr *Arg : Result.Args) {
const auto *InnerCall = dyn_cast<CallExpr>(Arg->IgnoreParenImpCasts());
Expand Down Expand Up @@ -146,6 +152,9 @@ generateReplacements(const MatchFinder::MatchResult &Match,
*Match.Context))
continue;

// We have found a nested call
FoundNestedCall = true;

// remove the function call
FixItHints.push_back(
FixItHint::CreateRemoval(InnerCall->getCallee()->getSourceRange()));
Expand All @@ -168,7 +177,7 @@ generateReplacements(const MatchFinder::MatchResult &Match,
CharSourceRange::getTokenRange(InnerResult.First->getEndLoc())));
}

const SmallVector<FixItHint> InnerReplacements = generateReplacements(
const auto [_, InnerReplacements] = generateReplacements(
Match, InnerCall, InnerResult, IgnoreNonTrivialTypes,
IgnoreTrivialTypesOfSizeAbove);

Expand All @@ -189,7 +198,7 @@ generateReplacements(const MatchFinder::MatchResult &Match,
}
}

return FixItHints;
return {FoundNestedCall, FixItHints};
}

MinMaxUseInitializerListCheck::MinMaxUseInitializerListCheck(
Expand Down Expand Up @@ -238,11 +247,11 @@ void MinMaxUseInitializerListCheck::check(
const auto *TopCall = Match.Nodes.getNodeAs<CallExpr>("topCall");

const FindArgsResult Result = findArgs(TopCall);
const SmallVector<FixItHint> Replacements =
const auto [FoundNestedCall, Replacements] =
generateReplacements(Match, TopCall, Result, IgnoreNonTrivialTypes,
IgnoreTrivialTypesOfSizeAbove);

if (Replacements.empty())
if (!FoundNestedCall)
return;

const DiagnosticBuilder Diagnostic =
Expand Down
42 changes: 26 additions & 16 deletions clang-tools-extra/clang-tidy/readability/ContainerContainsCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,30 +13,40 @@
using namespace clang::ast_matchers;

namespace clang::tidy::readability {

void ContainerContainsCheck::registerMatchers(MatchFinder *Finder) {
const auto SupportedContainers = hasType(
hasUnqualifiedDesugaredType(recordType(hasDeclaration(cxxRecordDecl(
hasAnyName("::std::set", "::std::unordered_set", "::std::map",
"::std::unordered_map", "::std::multiset",
"::std::unordered_multiset", "::std::multimap",
"::std::unordered_multimap"))))));
const auto HasContainsMatchingParamType = hasMethod(
cxxMethodDecl(isConst(), parameterCountIs(1), returns(booleanType()),
hasName("contains"), unless(isDeleted()), isPublic(),
hasParameter(0, hasType(hasUnqualifiedDesugaredType(
equalsBoundNode("parameterType"))))));

const auto CountCall =
cxxMemberCallExpr(on(SupportedContainers),
callee(cxxMethodDecl(hasName("count"))),
argumentCountIs(1))
cxxMemberCallExpr(
argumentCountIs(1),
callee(cxxMethodDecl(
hasName("count"),
hasParameter(0, hasType(hasUnqualifiedDesugaredType(
type().bind("parameterType")))),
ofClass(cxxRecordDecl(HasContainsMatchingParamType)))))
.bind("call");

const auto FindCall =
cxxMemberCallExpr(on(SupportedContainers),
callee(cxxMethodDecl(hasName("find"))),
argumentCountIs(1))
cxxMemberCallExpr(
argumentCountIs(1),
callee(cxxMethodDecl(
hasName("find"),
hasParameter(0, hasType(hasUnqualifiedDesugaredType(
type().bind("parameterType")))),
ofClass(cxxRecordDecl(HasContainsMatchingParamType)))))
.bind("call");

const auto EndCall = cxxMemberCallExpr(on(SupportedContainers),
callee(cxxMethodDecl(hasName("end"))),
argumentCountIs(0));
const auto EndCall = cxxMemberCallExpr(
argumentCountIs(0),
callee(
cxxMethodDecl(hasName("end"),
// In the matchers below, FindCall should always appear
// before EndCall so 'parameterType' is properly bound.
ofClass(cxxRecordDecl(HasContainsMatchingParamType)))));

const auto Literal0 = integerLiteral(equals(0));
const auto Literal1 = integerLiteral(equals(1));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@

namespace clang::tidy::readability {

/// Finds usages of `container.count()` and `find() == end()` which should be
/// replaced by a call to the `container.contains()` method introduced in C++20.
/// Finds usages of `container.count()` and
/// `container.find() == container.end()` which should be replaced by a call
/// to the `container.contains()` method.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/readability/container-contains.html
Expand All @@ -24,10 +25,11 @@ class ContainerContainsCheck : public ClangTidyCheck {
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) final;
void check(const ast_matchers::MatchFinder::MatchResult &Result) final;

protected:
bool isLanguageVersionSupported(const LangOptions &LO) const final {
return LO.CPlusPlus20;
return LO.CPlusPlus;
}
std::optional<TraversalKind> getCheckTraversalKind() const override {
return TK_AsIs;
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,16 +141,18 @@ void EnumInitialValueCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
}

void EnumInitialValueCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
enumDecl(unless(isMacro()), unless(hasConsistentInitialValues()))
.bind("inconsistent"),
this);
Finder->addMatcher(enumDecl(isDefinition(), unless(isMacro()),
unless(hasConsistentInitialValues()))
.bind("inconsistent"),
this);
if (!AllowExplicitZeroFirstInitialValue)
Finder->addMatcher(
enumDecl(hasZeroInitialValueForFirstEnumerator()).bind("zero_first"),
enumDecl(isDefinition(), hasZeroInitialValueForFirstEnumerator())
.bind("zero_first"),
this);
if (!AllowExplicitSequentialInitialValues)
Finder->addMatcher(enumDecl(unless(isMacro()), hasSequentialInitialValues())
Finder->addMatcher(enumDecl(isDefinition(), unless(isMacro()),
hasSequentialInitialValues())
.bind("sequential"),
this);
}
Expand All @@ -159,7 +161,7 @@ void EnumInitialValueCheck::check(const MatchFinder::MatchResult &Result) {
if (const auto *Enum = Result.Nodes.getNodeAs<EnumDecl>("inconsistent")) {
DiagnosticBuilder Diag =
diag(Enum->getBeginLoc(),
"inital values in enum %0 are not consistent, consider explicit "
"initial values in enum %0 are not consistent, consider explicit "
"initialization of all, none or only the first enumerator")
<< Enum;
for (const EnumConstantDecl *ECD : Enum->enumerators())
Expand Down
12 changes: 6 additions & 6 deletions clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -305,33 +305,33 @@ TEST_F(ConfigCompileTests, DiagnosticSuppression) {
{
auto D = DiagEngine.Report(diag::warn_unreachable);
EXPECT_TRUE(isDiagnosticSuppressed(
Diag{&DiagEngine}, Conf.Diagnostics.Suppress, LangOptions()));
Diag{&DiagEngine, D}, Conf.Diagnostics.Suppress, LangOptions()));
}
// Subcategory not respected/suppressed.
{
auto D = DiagEngine.Report(diag::warn_unreachable_break);
EXPECT_FALSE(isDiagnosticSuppressed(
Diag{&DiagEngine}, Conf.Diagnostics.Suppress, LangOptions()));
Diag{&DiagEngine, D}, Conf.Diagnostics.Suppress, LangOptions()));
}
{
auto D = DiagEngine.Report(diag::warn_unused_variable);
EXPECT_TRUE(isDiagnosticSuppressed(
Diag{&DiagEngine}, Conf.Diagnostics.Suppress, LangOptions()));
Diag{&DiagEngine, D}, Conf.Diagnostics.Suppress, LangOptions()));
}
{
auto D = DiagEngine.Report(diag::err_typecheck_bool_condition);
EXPECT_TRUE(isDiagnosticSuppressed(
Diag{&DiagEngine}, Conf.Diagnostics.Suppress, LangOptions()));
Diag{&DiagEngine, D}, Conf.Diagnostics.Suppress, LangOptions()));
}
{
auto D = DiagEngine.Report(diag::err_unexpected_friend);
EXPECT_TRUE(isDiagnosticSuppressed(
Diag{&DiagEngine}, Conf.Diagnostics.Suppress, LangOptions()));
Diag{&DiagEngine, D}, Conf.Diagnostics.Suppress, LangOptions()));
}
{
auto D = DiagEngine.Report(diag::warn_alloca);
EXPECT_TRUE(isDiagnosticSuppressed(
Diag{&DiagEngine}, Conf.Diagnostics.Suppress, LangOptions()));
Diag{&DiagEngine, D}, Conf.Diagnostics.Suppress, LangOptions()));
}

Frag.Diagnostics.Suppress.emplace_back("*");
Expand Down
47 changes: 40 additions & 7 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,6 @@ Improvements to clang-doc
Improvements to clang-query
---------------------------

Improvements to clang-rename
----------------------------

The improvements are...

Improvements to clang-tidy
Expand All @@ -104,21 +101,43 @@ New checks
New check aliases
^^^^^^^^^^^^^^^^^

- New alias :doc:`cert-arr39-c <clang-tidy/checks/cert/arr39-c>` to
:doc:`bugprone-sizeof-expression
<clang-tidy/checks/bugprone/sizeof-expression>` was added.

Changes in existing checks
^^^^^^^^^^^^^^^^^^^^^^^^^^

- Improved :doc:`bugprone-casting-through-void
<clang-tidy/checks/bugprone/casting-through-void>` check to suggest replacing
the offending code with ``reinterpret_cast``, to more clearly express intent.

- Improved :doc:`cert-flp30-c<clang-tidy/checks/cert/flp30-c>` check to
- Improved :doc:`bugprone-forwarding-reference-overload
<clang-tidy/checks/bugprone/forwarding-reference-overload>` check by fixing
a crash when determining if an ``enable_if[_t]`` was found.

- Improved :doc:`bugprone-sizeof-expression
<clang-tidy/checks/bugprone/sizeof-expression>` check to find suspicious
usages of ``sizeof()``, ``alignof()``, and ``offsetof()`` when adding or
subtracting from a pointer.

- Improved :doc:`cert-flp30-c <clang-tidy/checks/cert/flp30-c>` check to
fix false positive that floating point variable is only used in increment
expression.

- Improved :doc:`cppcoreguidelines-prefer-member-initializer
<clang-tidy/checks/cppcoreguidelines/prefer-member-initializer>` check to avoid
false positive when member initialization depends on a structured binging
variable.
<clang-tidy/checks/cppcoreguidelines/prefer-member-initializer>` check to
avoid false positive when member initialization depends on a structured
binding variable.

- Improved :doc:`misc-definitions-in-headers
<clang-tidy/checks/misc/definitions-in-headers>` check by rewording the
diagnostic note that suggests adding ``inline``.

- Improved :doc:`modernize-avoid-c-arrays
<clang-tidy/checks/modernize/avoid-c-arrays>` check to suggest using ``std::span``
as a replacement for parameters of incomplete C array type in C++20 and
``std::array`` or ``std::vector`` before C++20.

- Improved :doc:`modernize-use-std-format
<clang-tidy/checks/modernize/use-std-format>` check to support replacing
Expand All @@ -128,14 +147,28 @@ Changes in existing checks
<clang-tidy/checks/misc/unconventional-assign-operator>` check to avoid
false positive for C++23 deducing this.

- Improved :doc:`modernize-min-max-use-initializer-list
<clang-tidy/checks/modernize/min-max-use-initializer-list>` check by fixing
a false positive when only an implicit conversion happened inside an
initializer list.

- Improved :doc:`modernize-use-std-print
<clang-tidy/checks/modernize/use-std-print>` check to support replacing
member function calls too.

- Improved :doc:`readability-enum-initial-value
<clang-tidy/checks/readability/enum-initial-value>` check by only issuing
diagnostics for the definition of an ``enum``, and by fixing a typo in the
diagnostic.

- Improved :doc:`performance-avoid-endl
<clang-tidy/checks/performance/avoid-endl>` check to use ``std::endl`` as
placeholder when lexer cannot get source text.

- Improved :doc:`readability-container-contains
<clang-tidy/checks/readability/container-contains>` check to let it work on
any class that has a ``contains`` method.

- Improved :doc:`readability-implicit-bool-conversion
<clang-tidy/checks/readability/implicit-bool-conversion>` check
by adding the option `UseUpperCaseLiteralSuffix` to select the
Expand Down
168 changes: 0 additions & 168 deletions clang-tools-extra/docs/clang-rename.rst

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,103 @@ hidden through macros.
memcpy(dst, buf, sizeof(INT_SZ)); // sizeof(sizeof(int)) is suspicious.
}

Suspicious usages of 'sizeof(...)' in pointer arithmetic
--------------------------------------------------------

Arithmetic operators on pointers automatically scale the result with the size
of the pointed typed.
Further use of ``sizeof`` around pointer arithmetic will typically result in an
unintended result.

Scaling the result of pointer difference
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Subtracting two pointers results in an integer expression (of type
``ptrdiff_t``) which expresses the distance between the two pointed objects in
"number of objects between".
A common mistake is to think that the result is "number of bytes between", and
scale the difference with ``sizeof``, such as ``P1 - P2 == N * sizeof(T)``
(instead of ``P1 - P2 == N``) or ``(P1 - P2) / sizeof(T)`` instead of
``P1 - P2``.

.. code-block:: c++

void splitFour(const Obj* Objs, size_t N, Obj Delimiter) {
const Obj *P = Objs;
while (P < Objs + N) {
if (*P == Delimiter) {
break;
}
}
if (P - Objs != 4 * sizeof(Obj)) { // Expecting a distance multiplied by sizeof is suspicious.
error();
}
}

.. code-block:: c++

void iterateIfEvenLength(int *Begin, int *End) {
auto N = (Begin - End) / sizeof(int); // Dividing by sizeof() is suspicious.
if (N % 2)
return;
// ...
}

Stepping a pointer with a scaled integer
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Conversely, when performing pointer arithmetics to add or subtract from a
pointer, the arithmetic operator implicitly scales the value actually added to
the pointer with the size of the pointee, as ``Ptr + N`` expects ``N`` to be
"number of objects to step", and not "number of bytes to step".

Seeing the calculation of a pointer where ``sizeof`` appears is suspicious,
and the result is typically unintended, often out of bounds.
``Ptr + sizeof(T)`` will offset the pointer by ``sizeof(T)`` elements,
effectively exponentiating the scaling factor to the power of 2.

This case also checks suspicious ``alignof`` and ``offsetof`` usages in
pointer arithmetic, as both return the "size" in bytes and not elements,
potentially resulting in doubly-scaled offsets.

.. code-block:: c++

void printEveryEvenIndexElement(int *Array, size_t N) {
int *P = Array;
while (P <= Array + N * sizeof(int)) { // Suspicious pointer arithmetic using sizeof()!
printf("%d ", *P);
P += 2 * sizeof(int); // Suspicious pointer arithmetic using sizeof()!
}
}

.. code-block:: c++

struct Message { /* ... */; char Flags[8]; };
void clearFlags(Message *Array, size_t N) {
const Message *End = Array + N;
while (Array < End) {
memset(Array + offsetof(Message, Flags), // Suspicious pointer arithmetic using offsetof()!
0, sizeof(Message::Flags));
++Array;
}
}
For this checked bogus pattern, `cert-arr39-c` redirects here as an alias of
this check.

This check corresponds to the CERT C Coding Standard rule
`ARR39-C. Do not add or subtract a scaled integer to a pointer
<http://wiki.sei.cmu.edu/confluence/display/c/ARR39-C.+Do+not+add+or+subtract+a+scaled+integer+to+a+pointer>`_.

Limitations
"""""""""""

Cases where the pointee type has a size of `1` byte (such as, and most
importantly, ``char``) are excluded.

Options
-------

Expand Down
10 changes: 10 additions & 0 deletions clang-tools-extra/docs/clang-tidy/checks/cert/arr39-c.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.. title:: clang-tidy - cert-arr39-c
.. meta::
:http-equiv=refresh: 5;URL=../bugprone/sizeof-expression.html

cert-arr39-c
============

The `cert-arr39-c` check is an alias, please see
:doc:`bugprone-sizeof-expression <../bugprone/sizeof-expression>`
for more information.
1 change: 1 addition & 0 deletions clang-tools-extra/docs/clang-tidy/checks/list.rst
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,7 @@ Check aliases
:header: "Name", "Redirect", "Offers fixes"

:doc:`bugprone-narrowing-conversions <bugprone/narrowing-conversions>`, :doc:`cppcoreguidelines-narrowing-conversions <cppcoreguidelines/narrowing-conversions>`,
:doc:`cert-arr39-c <cert/arr39-c>`, :doc:`bugprone-sizeof-expression <bugprone/sizeof-expression>`,
:doc:`cert-con36-c <cert/con36-c>`, :doc:`bugprone-spuriously-wake-up-functions <bugprone/spuriously-wake-up-functions>`,
:doc:`cert-con54-cpp <cert/con54-cpp>`, :doc:`bugprone-spuriously-wake-up-functions <bugprone/spuriously-wake-up-functions>`,
:doc:`cert-ctr56-cpp <cert/ctr56-cpp>`, :doc:`bugprone-pointer-arithmetic-on-polymorphic-object <bugprone/pointer-arithmetic-on-polymorphic-object>`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ modernize-avoid-c-arrays
Finds C-style array types and recommend to use ``std::array<>`` /
``std::vector<>``. All types of C arrays are diagnosed.

For incomplete C-style array types appeared in parameters, It would be better to
use ``std::span`` / ``gsl::span`` as replacement.

However, fix-it are potentially dangerous in header files and are therefore not
emitted right now.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ Options
extern std::string strprintf(const char *format, ...);
int i = -42;
unsigned int u = 0xffffffff;
return strprintf("%d %u\n", i, u);
return strprintf("%u %d\n", i, u);
would be converted to

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ Options

int i = -42;
unsigned int u = 0xffffffff;
printf("%d %u\n", i, u);
printf("%u %d\n", i, u);

would be converted to:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,31 @@
readability-container-contains
==============================

Finds usages of ``container.count()`` and ``container.find() == container.end()`` which should be replaced by a call to the ``container.contains()`` method introduced in C++20.
Finds usages of ``container.count()`` and
``container.find() == container.end()`` which should be replaced by a call to
the ``container.contains()`` method.

Whether an element is contained inside a container should be checked with ``contains`` instead of ``count``/``find`` because ``contains`` conveys the intent more clearly. Furthermore, for containers which permit multiple entries per key (``multimap``, ``multiset``, ...), ``contains`` is more efficient than ``count`` because ``count`` has to do unnecessary additional work.
Whether an element is contained inside a container should be checked with
``contains`` instead of ``count``/``find`` because ``contains`` conveys the
intent more clearly. Furthermore, for containers which permit multiple entries
per key (``multimap``, ``multiset``, ...), ``contains`` is more efficient than
``count`` because ``count`` has to do unnecessary additional work.

Examples:

=========================================== ==============================
Initial expression Result
------------------------------------------- ------------------------------
``myMap.find(x) == myMap.end()`` ``!myMap.contains(x)``
``myMap.find(x) != myMap.end()`` ``myMap.contains(x)``
``if (myMap.count(x))`` ``if (myMap.contains(x))``
``bool exists = myMap.count(x)`` ``bool exists = myMap.contains(x)``
``bool exists = myMap.count(x) > 0`` ``bool exists = myMap.contains(x)``
``bool exists = myMap.count(x) >= 1`` ``bool exists = myMap.contains(x)``
``bool missing = myMap.count(x) == 0`` ``bool missing = !myMap.contains(x)``
=========================================== ==============================
====================================== =====================================
Initial expression Result
-------------------------------------- -------------------------------------
``myMap.find(x) == myMap.end()`` ``!myMap.contains(x)``
``myMap.find(x) != myMap.end()`` ``myMap.contains(x)``
``if (myMap.count(x))`` ``if (myMap.contains(x))``
``bool exists = myMap.count(x)`` ``bool exists = myMap.contains(x)``
``bool exists = myMap.count(x) > 0`` ``bool exists = myMap.contains(x)``
``bool exists = myMap.count(x) >= 1`` ``bool exists = myMap.contains(x)``
``bool missing = myMap.count(x) == 0`` ``bool missing = !myMap.contains(x)``
====================================== =====================================

This check applies to ``std::set``, ``std::unordered_set``, ``std::map``, ``std::unordered_map`` and the corresponding multi-key variants.
It is only active for C++20 and later, as the ``contains`` method was only added in C++20.
This check will apply to any class that has a ``contains`` method, notably
including ``std::set``, ``std::unordered_set``, ``std::map``, and
``std::unordered_map`` as of C++20, and ``std::string`` and ``std::string_view``
as of C++23.
1 change: 0 additions & 1 deletion clang-tools-extra/docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ Contents
clang-include-fixer
modularize
pp-trace
clang-rename
clangd <https://clangd.llvm.org/>
clang-doc

Expand Down
3 changes: 0 additions & 3 deletions clang-tools-extra/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,6 @@ configure_lit_site_cfg(
)

set(CLANG_TOOLS_TEST_DEPS
# For the clang-apply-replacements test that uses clang-rename.
clang-rename

# For the clang-doc tests that emit bitcode files.
llvm-bcanalyzer

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -261,3 +261,9 @@ class Test11 {
Test11(const Test11 &) = default;
Test11(Test11 &&) = default;
};

template <template <class> typename T, typename U>
struct gh106333
{
gh106333(U && arg1, T<int> arg2) {}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// RUN: %check_clang_tidy -std=c11-or-later %s bugprone-sizeof-expression %t

#define alignof(type_name) _Alignof(type_name)
extern void sink(const void *P);

enum { BufferSize = 1024 };

struct S {
long A, B, C;
};

void bad4d(void) {
struct S Buffer[BufferSize];

struct S *P = &Buffer[0];
struct S *Q = P;
while (Q < P + alignof(Buffer)) {
// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: suspicious usage of 'alignof(...)' in pointer arithmetic; this scaled value will be scaled again by the '+' operator [bugprone-sizeof-expression]
// CHECK-MESSAGES: :[[@LINE-2]]:16: note: '+' in pointer arithmetic internally scales with 'sizeof(struct S)' == {{[0-9]+}}
sink(Q++);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,372 @@
// RUN: %check_clang_tidy %s bugprone-sizeof-expression %t

#define offsetof(type, member) __builtin_offsetof(type, member)

typedef __SIZE_TYPE__ size_t;
typedef __WCHAR_TYPE__ wchar_t;

extern void *memset(void *Dest, int Ch, size_t Count);
extern size_t strlen(const char *Str);
extern size_t wcslen(const wchar_t *Str);
extern char *strcpy(char *Dest, const char *Src);
extern wchar_t *wcscpy(wchar_t *Dest, const wchar_t *Src);
extern int scanf(const char *Format, ...);
extern int wscanf(const wchar_t *Format, ...);

extern void sink(const void *P);

enum { BufferSize = 1024 };

void bad1a(void) {
int Buffer[BufferSize];

int *P = &Buffer[0];
int *Q = P;
while (Q < P + sizeof(Buffer)) {
// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: suspicious usage of 'sizeof(...)' in pointer arithmetic; this scaled value will be scaled again by the '+' operator [bugprone-sizeof-expression]
// CHECK-MESSAGES: :[[@LINE-2]]:16: note: '+' in pointer arithmetic internally scales with 'sizeof(int)' == {{[0-9]+}}
*Q++ = 0;
}
}

void bad1b(void) {
typedef int Integer;
Integer Buffer[BufferSize];

Integer *P = &Buffer[0];
Integer *Q = P;
while (Q < P + sizeof(Buffer)) {
// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: suspicious usage of 'sizeof(...)' in pointer arithmetic; this scaled value will be scaled again by the '+' operator
// CHECK-MESSAGES: :[[@LINE-2]]:16: note: '+' in pointer arithmetic internally scales with 'sizeof(Integer)' == {{[0-9]+}}
*Q++ = 0;
}
}

void good1(void) {
int Buffer[BufferSize];

int *P = &Buffer[0];
int *Q = P;
while (Q < P + BufferSize) {
*Q++ = 0;
}
}

void bad2(void) {
int Buffer[BufferSize];
int *P = Buffer;

while (P < Buffer + sizeof(Buffer)) {
// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: suspicious usage of 'sizeof(...)' in pointer arithmetic; this scaled value will be scaled again by the '+' operator
// CHECK-MESSAGES: :[[@LINE-2]]:21: note: '+' in pointer arithmetic internally scales with 'sizeof(int)' == {{[0-9]+}}
*P++ = 0;
}
}

void good2(void) {
int Buffer[BufferSize];
int *P = Buffer;

while (P < Buffer + BufferSize) {
*P++ = 0;
}
}

struct S {
long A, B, C;
};

void bad3a(struct S *S) {
const size_t Offset = offsetof(struct S, B);
struct S *P = S;

// This is not captureable by Tidy because the size/offset expression is
// not a direct child of the pointer arithmetics.
memset(P + Offset, 0, sizeof(struct S) - Offset);
}

void good3a(struct S *S) {
const size_t Offset = offsetof(struct S, B);
char *P = (char*)S;

// This is not captureable by Tidy because the size/offset expression is
// not a direct child of the pointer arithmetics.
memset(P + Offset, 0, sizeof(struct S) - Offset);
}

void bad3b(struct S *S) {
memset(S + offsetof(struct S, B), 0,
sizeof(struct S) - offsetof(struct S, B));
// CHECK-MESSAGES: :[[@LINE-2]]:12: warning: suspicious usage of 'offsetof(...)' in pointer arithmetic; this scaled value will be scaled again by the '+' operator
// CHECK-MESSAGES: :[[@LINE-3]]:12: note: '+' in pointer arithmetic internally scales with 'sizeof(struct S)' == {{[0-9]+}}
}

void good3b(struct S *S) {
char *P = (char*)S;
memset(P + offsetof(struct S, B), 0,
sizeof(struct S) - offsetof(struct S, B));
}

void bad3c(void) {
struct S Buffer[BufferSize];

struct S *P = &Buffer[0];
struct S *Q = P;
while (Q < P + sizeof(Buffer)) {
// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: suspicious usage of 'sizeof(...)' in pointer arithmetic; this scaled value will be scaled again by the '+' operator
// CHECK-MESSAGES: :[[@LINE-2]]:16: note: '+' in pointer arithmetic internally scales with 'sizeof(struct S)' == {{[0-9]+}}
sink(Q++);
}
}

void bad4(void) {
int Buffer[BufferSize];

int *P = &Buffer[0];
int *Q = P;
while (Q < P + BufferSize) {
*Q = 0;
Q += sizeof(*Q);
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: suspicious usage of 'sizeof(...)' in pointer arithmetic; this scaled value will be scaled again by the '+=' operator
// CHECK-MESSAGES: :[[@LINE-2]]:7: note: '+=' in pointer arithmetic internally scales with 'sizeof(int)' == {{[0-9]+}}
}
}

void silenced4(void) {
char Buffer[BufferSize];

char *P = &Buffer[0];
char *Q = P;
while (Q < P + BufferSize) {
*Q = 0;
Q += sizeof(*Q);
}
}

void good4(void) {
char Buffer[BufferSize];

char *P = &Buffer[0];
char *Q = P;
while (Q < P + BufferSize) {
*Q = 0;
Q += 1;
}
}

void good5aa(void) {
int Buffer[BufferSize];

int *P = &Buffer[0];
int *Q = P;
while (Q < P + BufferSize) {
*Q = 0;
Q += ( sizeof(Buffer) / sizeof(Buffer[0]) );
}
}

void good5ab(void) {
int Buffer[BufferSize];

int *P = &Buffer[0];
int *Q = P;
while (Q < P + BufferSize) {
*Q = 0;
Q = Q + ( sizeof(Buffer) / sizeof(Buffer[0]) );
}
}

void good5ba(void) {
int Buffer[BufferSize];

int *P = &Buffer[0];
int *Q = P;
while (Q < P + BufferSize) {
*Q = 0;
Q -= ( sizeof(Buffer) / sizeof(Buffer[0]) );
}
}

void good5bb(void) {
int Buffer[BufferSize];

int *P = &Buffer[0];
int *Q = P;
while (Q < P + BufferSize) {
*Q = 0;
Q = Q - ( sizeof(Buffer) / sizeof(Buffer[0]) );
}
}

void bad6(void) {
int Buffer[BufferSize];

int *P = &Buffer[0];
int *Q = P;
while (Q < P + BufferSize) {
*Q = 0;
Q = Q + sizeof(*Q);
// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: suspicious usage of 'sizeof(...)' in pointer arithmetic; this scaled value will be scaled again by the '+' operator
// CHECK-MESSAGES: :[[@LINE-2]]:11: note: '+' in pointer arithmetic internally scales with 'sizeof(int)' == {{[0-9]+}}
}
}

void silenced6(void) {
char Buffer[BufferSize];

char *P = &Buffer[0];
char *Q = P;
while (Q < P + BufferSize) {
*Q = 0;
Q = Q + sizeof(*Q);
}
}

void good6(void) {
char Buffer[BufferSize];

char *P = &Buffer[0];
char *Q = P;
while (Q < P + BufferSize) {
*Q = 0;
Q = Q + 1;
}
}

void silenced7(void) {
char Buffer[BufferSize];

char *P = &Buffer[0];
const char *Q = P;
while (Q < P + BufferSize) {
sink(Q);
Q = Q + sizeof(*Q);
}
}

void good7(void) {
char Buffer[BufferSize];

char *P = &Buffer[0];
const char *Q = P;
while (Q < P + BufferSize) {
sink(Q);
Q = Q + 1;
}
}

void bad8(void) {
int Buffer[BufferSize];

int *P = &Buffer[0];
int *Q = P;
while (Q >= P) {
*Q = 0;
Q = Q - sizeof(*Q);
// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: suspicious usage of 'sizeof(...)' in pointer arithmetic; this scaled value will be scaled again by the '-' operator
// CHECK-MESSAGES: :[[@LINE-2]]:11: note: '-' in pointer arithmetic internally scales with 'sizeof(int)' == {{[0-9]+}}
}
}

void silenced8(void) {
char Buffer[BufferSize];

char *P = &Buffer[0];
char *Q = P;
while (Q >= P) {
*Q = 0;
Q = Q - sizeof(*Q);
}
}

void good8(void) {
char Buffer[BufferSize];

char *P = &Buffer[0];
char *Q = P;
while (Q >= P) {
*Q = 0;
Q = Q - 1;
}
}

void good9(void) {
int Buffer[BufferSize];

int *P = &Buffer[0];
int *Q = P + BufferSize;
int N = Q - P;
while (N >= 0) {
Q[N] = 0;
N = N - 1;
}
}

void good10(void) {
int Buffer[BufferSize];

int *P = &Buffer[0];
int *Q = Buffer + BufferSize;
int I = sizeof(*P) - sizeof(*Q);

sink(&I);
}

void good11(void) {
int Buffer[BufferSize];

int *P = &Buffer[0];
int *Q = Buffer + BufferSize;
int I = sizeof(Q) - sizeof(*P);

sink(&I);
}

void bad12(void) {
wchar_t Message[BufferSize];
wcscpy(Message, L"Message: ");
wscanf(L"%s", Message + wcslen(Message) * sizeof(wchar_t));
// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: suspicious usage of 'sizeof(...)' in pointer arithmetic; this scaled value will be scaled again by the '+' operator
// CHECK-MESSAGES: :[[@LINE-2]]:25: note: '+' in pointer arithmetic internally scales with 'sizeof(wchar_t)' == {{[0-9]+}}
}

void silenced12(void) {
char Message[BufferSize];
strcpy(Message, "Message: ");
scanf("%s", Message + strlen(Message) * sizeof(char));
}

void nomatch12(void) {
char Message[BufferSize];
strcpy(Message, "Message: ");
scanf("%s", Message + strlen(Message));
}

void good12(void) {
wchar_t Message[BufferSize];
wcscpy(Message, L"Message: ");
wscanf(L"%s", Message + wcslen(Message));
}

void good13(void) {
int Buffer[BufferSize];

int *P = &Buffer[0];
while (P < (Buffer + sizeof(Buffer) / sizeof(int))) {
// NO-WARNING: Calculating the element count of the buffer here, which is
// safe with this idiom (as long as the types don't change).
++P;
}

while (P < (Buffer + sizeof(Buffer) / sizeof(Buffer[0]))) {
// NO-WARNING: Calculating the element count of the buffer here, which is
// safe with this idiom.
++P;
}

while (P < (Buffer + sizeof(Buffer) / sizeof(*P))) {
// NO-WARNING: Calculating the element count of the buffer here, which is
// safe with this idiom.
++P;
}
}
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -289,14 +289,43 @@ int Test6() {
return sum;
}

static constexpr inline int BufferSize = 1024;

template <typename T>
T next(const T *&Read) {
T value = *Read;
Read += sizeof(T);
return value;
}

void Test7() {
int Buffer[BufferSize];
int *P = &Buffer[0];

const int *P2 = P;
int V1 = next(P2);
// CHECK-MESSAGES: :[[@LINE-10]]:8: warning: suspicious usage of 'sizeof(...)' in pointer arithmetic; this scaled value will be scaled again by the '+=' operator
// CHECK-MESSAGES: :[[@LINE-11]]:8: note: '+=' in pointer arithmetic internally scales with 'sizeof(const int)' == {{[0-9]+}}
int V2 = next(P2);
(void)V1;
(void)V2;

int *Q = P;
while (Q < P + sizeof(Buffer)) {
// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: suspicious usage of 'sizeof(...)' in pointer arithmetic; this scaled value will be scaled again by the '+' operator
// CHECK-MESSAGES: :[[@LINE-2]]:16: note: '+' in pointer arithmetic internally scales with 'sizeof(int)' == {{[0-9]+}}
*Q++ = 0;
}
}

#ifdef __SIZEOF_INT128__
template <__int128_t N>
#else
template <long N> // Fallback for platforms which do not define `__int128_t`
#endif
bool Baz() { return sizeof(A) < N; }
// CHECK-MESSAGES: :[[@LINE-1]]:31: warning: suspicious comparison of 'sizeof(expr)' to a constant
bool Test7() { return Baz<-1>(); }
bool Test8() { return Baz<-1>(); }

void some_generic_function(const void *arg, int argsize);
int *IntP, **IntPP;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -641,9 +641,9 @@ struct S3 {
}

namespace GH82970 {
struct InitFromBingingDecl {
struct InitFromBindingDecl {
int m;
InitFromBingingDecl() {
InitFromBindingDecl() {
struct { int i; } a;
auto [n] = a;
m = n;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

int f() {
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'f' defined in a header file; function definitions in header files can lead to ODR violations [misc-definitions-in-headers]
// CHECK-MESSAGES: :[[@LINE-2]]:5: note: make as 'inline'
// CHECK-MESSAGES: :[[@LINE-2]]:5: note: mark the definition as 'inline'
// CHECK-FIXES: inline int f() {
return 1;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// RUN: %check_clang_tidy -std=c++20 %s modernize-avoid-c-arrays %t

int f1(int data[], int size) {
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: do not declare C-style arrays, use std::span<> instead
int f4[] = {1, 2};
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not declare C-style arrays, use std::array<> instead
}

int f2(int data[100]) {
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: do not declare C-style arrays, use std::array<> instead
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// RUN: %check_clang_tidy %s modernize-avoid-c-arrays %t
// RUN: %check_clang_tidy -std=c++17 %s modernize-avoid-c-arrays %t

int not_main(int argc, char *argv[]) {
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: do not declare C-style arrays, use std::array<> instead
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: do not declare C-style arrays, use std::array<> or std::vector<> instead
int f4[] = {1, 2};
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not declare C-style arrays, use std::array<> instead
}
Expand All @@ -11,7 +11,7 @@ int main(int argc, char *argv[]) {
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not declare C-style arrays, use std::array<> instead

auto not_main = [](int argc, char *argv[]) {
// CHECK-MESSAGES: :[[@LINE-1]]:32: warning: do not declare C-style arrays, use std::array<> instead
// CHECK-MESSAGES: :[[@LINE-1]]:32: warning: do not declare C-style arrays, use std::array<> or std::vector<> instead
int f6[] = {1, 2};
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: do not declare C-style arrays, use std::array<> instead
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// RUN: %check_clang_tidy %s modernize-avoid-c-arrays %t -- \
// RUN: %check_clang_tidy -std=c++17 %s modernize-avoid-c-arrays %t -- \
// RUN: -config='{CheckOptions: { modernize-avoid-c-arrays.AllowStringArrays: true }}'

const char name[] = "name";
const char array[] = {'n', 'a', 'm', 'e', '\0'};
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: do not declare C-style arrays, use std::array<> instead [modernize-avoid-c-arrays]

void takeCharArray(const char name[]);
// CHECK-MESSAGES: :[[@LINE-1]]:26: warning: do not declare C-style arrays, use std::array<> instead [modernize-avoid-c-arrays]
// CHECK-MESSAGES: :[[@LINE-1]]:26: warning: do not declare C-style arrays, use std::array<> or std::vector<> instead [modernize-avoid-c-arrays]
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// RUN: %check_clang_tidy %s modernize-avoid-c-arrays %t
// RUN: %check_clang_tidy -std=c++17 %s modernize-avoid-c-arrays %t

int not_main(int argc, char *argv[], char *argw[]) {
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: do not declare C-style arrays, use std::array<> instead
// CHECK-MESSAGES: :[[@LINE-2]]:38: warning: do not declare C-style arrays, use std::array<> instead
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: do not declare C-style arrays, use std::array<> or std::vector<> instead
// CHECK-MESSAGES: :[[@LINE-2]]:38: warning: do not declare C-style arrays, use std::array<> or std::vector<> instead
int f4[] = {1, 2};
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not declare C-style arrays, use std::array<> instead
}
Expand All @@ -12,8 +12,8 @@ int main(int argc, char *argv[], char *argw[]) {
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not declare C-style arrays, use std::array<> instead

auto not_main = [](int argc, char *argv[], char *argw[]) {
// CHECK-MESSAGES: :[[@LINE-1]]:32: warning: do not declare C-style arrays, use std::array<> instead
// CHECK-MESSAGES: :[[@LINE-2]]:46: warning: do not declare C-style arrays, use std::array<> instead
// CHECK-MESSAGES: :[[@LINE-1]]:32: warning: do not declare C-style arrays, use std::array<> or std::vector<> instead
// CHECK-MESSAGES: :[[@LINE-2]]:46: warning: do not declare C-style arrays, use std::array<> or std::vector<> instead
int f6[] = {1, 2};
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: do not declare C-style arrays, use std::array<> instead
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// RUN: %check_clang_tidy %s modernize-avoid-c-arrays %t
// RUN: %check_clang_tidy -std=c++17 %s modernize-avoid-c-arrays %t

int a[] = {1, 2};
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: do not declare C-style arrays, use std::array<> instead
Expand Down Expand Up @@ -91,4 +91,4 @@ const char name[] = "Some string";
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: do not declare C-style arrays, use std::array<> instead [modernize-avoid-c-arrays]

void takeCharArray(const char name[]);
// CHECK-MESSAGES: :[[@LINE-1]]:26: warning: do not declare C-style arrays, use std::array<> instead [modernize-avoid-c-arrays]
// CHECK-MESSAGES: :[[@LINE-1]]:26: warning: do not declare C-style arrays, use std::array<> or std::vector<> instead [modernize-avoid-c-arrays]
Original file line number Diff line number Diff line change
Expand Up @@ -323,5 +323,11 @@ struct GH91982 {
}
};

struct GH107594 {
int foo(int a, int b, char c) {
return std::max<int>({a, b, c});
}
};

} // namespace

Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// RUN: %check_clang_tidy -std=c++14-or-later %s performance-unnecessary-value-param %t

// The test case used to crash clang-tidy.
// https://github.com/llvm/llvm-project/issues/108963

struct A
{
template<typename T> A(T&&) {}
};

struct B
{
~B();
};

struct C
{
A a;
C(B, int i) : a(i) {}
// CHECK-MESSAGES: [[@LINE-1]]:6: warning: the parameter #1 is copied for each invocation but only used as a const reference; consider making it a const reference
};

C c(B(), 0);
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ int testMacroExpansion(std::unordered_set<int> &MySet) {
return 0;
}

// The following map has the same interface like `std::map`.
// The following map has the same interface as `std::map`.
template <class Key, class T>
struct CustomMap {
unsigned count(const Key &K) const;
Expand All @@ -249,13 +249,180 @@ struct CustomMap {
void *end();
};

// The clang-tidy check is currently hard-coded against the `std::` containers
// and hence won't annotate the following instance. We might change this in the
// future and also detect the following case.
void *testDifferentCheckTypes(CustomMap<int, int> &MyMap) {
if (MyMap.count(0))
// NO-WARNING.
// CHECK-FIXES: if (MyMap.count(0))
return nullptr;
return MyMap.find(2);
void testDifferentCheckTypes(CustomMap<int, int> &MyMap) {
if (MyMap.count(0)) {};
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: if (MyMap.contains(0)) {};

MyMap.find(0) != MyMap.end();
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: MyMap.contains(0);
}

struct MySubmap : public CustomMap<int, int> {};

void testSubclass(MySubmap& MyMap) {
if (MyMap.count(0)) {};
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: if (MyMap.contains(0)) {};
}

using UsingMap = CustomMap<int, int>;
struct MySubmap2 : public UsingMap {};
using UsingMap2 = MySubmap2;

void testUsing(UsingMap2& MyMap) {
if (MyMap.count(0)) {};
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: if (MyMap.contains(0)) {};
}

template <class Key, class T>
struct CustomMapContainsDeleted {
unsigned count(const Key &K) const;
bool contains(const Key &K) const = delete;
void *find(const Key &K);
void *end();
};

struct SubmapContainsDeleted : public CustomMapContainsDeleted<int, int> {};

void testContainsDeleted(CustomMapContainsDeleted<int, int> &MyMap,
SubmapContainsDeleted &MyMap2) {
// No warning if the `contains` method is deleted.
if (MyMap.count(0)) {};
if (MyMap2.count(0)) {};
}

template <class Key, class T>
struct CustomMapPrivateContains {
unsigned count(const Key &K) const;
void *find(const Key &K);
void *end();

private:
bool contains(const Key &K) const;
};

struct SubmapPrivateContains : public CustomMapPrivateContains<int, int> {};

void testPrivateContains(CustomMapPrivateContains<int, int> &MyMap,
SubmapPrivateContains &MyMap2) {
// No warning if the `contains` method is not public.
if (MyMap.count(0)) {};
if (MyMap2.count(0)) {};
}

struct MyString {};

struct WeirdNonMatchingContains {
unsigned count(char) const;
bool contains(const MyString&) const;
};

void testWeirdNonMatchingContains(WeirdNonMatchingContains &MyMap) {
// No warning if there is no `contains` method with the right type.
if (MyMap.count('a')) {};
}

template <class T>
struct SmallPtrSet {
using ConstPtrType = const T*;
unsigned count(ConstPtrType Ptr) const;
bool contains(ConstPtrType Ptr) const;
};

template <class T>
struct SmallPtrPtrSet {
using ConstPtrType = const T**;
unsigned count(ConstPtrType Ptr) const;
bool contains(ConstPtrType Ptr) const;
};

template <class T>
struct SmallPtrPtrPtrSet {
using ConstPtrType = const T***;
unsigned count(ConstPtrType Ptr) const;
bool contains(ConstPtrType Ptr) const;
};

void testSmallPtrSet(const int ***Ptr,
SmallPtrSet<int> &MySet,
SmallPtrPtrSet<int> &MySet2,
SmallPtrPtrPtrSet<int> &MySet3) {
if (MySet.count(**Ptr)) {};
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: if (MySet.contains(**Ptr)) {};
if (MySet2.count(*Ptr)) {};
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: if (MySet2.contains(*Ptr)) {};
if (MySet3.count(Ptr)) {};
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: if (MySet3.contains(Ptr)) {};
}

struct X {};
struct Y : public X {};

void testSubclassEntry(SmallPtrSet<X>& Set, Y* Entry) {
if (Set.count(Entry)) {}
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: if (Set.contains(Entry)) {}
}

struct WeirdPointerApi {
unsigned count(int** Ptr) const;
bool contains(int* Ptr) const;
};

void testWeirdApi(WeirdPointerApi& Set, int* E) {
if (Set.count(&E)) {}
}

void testIntUnsigned(std::set<int>& S, unsigned U) {
if (S.count(U)) {}
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: if (S.contains(U)) {}
}

template <class T>
struct CustomSetConvertible {
unsigned count(const T &K) const;
bool contains(const T &K) const;
};

struct A {};
struct B { B() = default; B(const A&) {} };
struct C { operator A() const; };

void testConvertibleTypes() {
CustomSetConvertible<B> MyMap;
if (MyMap.count(A())) {};
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: if (MyMap.contains(A())) {};

CustomSetConvertible<A> MyMap2;
if (MyMap2.count(C())) {};
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: if (MyMap2.contains(C())) {};

if (MyMap2.count(C()) != 0) {};
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: if (MyMap2.contains(C())) {};
}

template<class U>
using Box = const U& ;

template <class T>
struct CustomBoxedSet {
unsigned count(Box<T> K) const;
bool contains(Box<T> K) const;
};

void testBox() {
CustomBoxedSet<int> Set;
if (Set.count(0)) {};
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: if (Set.contains(0)) {};
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
// RUN: }}'

enum EError {
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: inital values in enum 'EError' are not consistent
// CHECK-MESSAGES-ENABLE: :[[@LINE-2]]:1: warning: inital values in enum 'EError' are not consistent
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: initial values in enum 'EError' are not consistent
// CHECK-MESSAGES-ENABLE: :[[@LINE-2]]:1: warning: initial values in enum 'EError' are not consistent
EError_a = 1,
EError_b,
// CHECK-FIXES: EError_b = 2,
Expand All @@ -34,8 +34,8 @@ enum EAll {

#define ENUMERATOR_1 EMacro1_b
enum EMacro1 {
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: inital values in enum 'EMacro1' are not consistent
// CHECK-MESSAGES-ENABLE: :[[@LINE-2]]:1: warning: inital values in enum 'EMacro1' are not consistent
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: initial values in enum 'EMacro1' are not consistent
// CHECK-MESSAGES-ENABLE: :[[@LINE-2]]:1: warning: initial values in enum 'EMacro1' are not consistent
EMacro1_a = 1,
ENUMERATOR_1,
// CHECK-FIXES: ENUMERATOR_1 = 2,
Expand All @@ -45,8 +45,8 @@ enum EMacro1 {

#define ENUMERATOR_2 EMacro2_b = 2
enum EMacro2 {
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: inital values in enum 'EMacro2' are not consistent
// CHECK-MESSAGES-ENABLE: :[[@LINE-2]]:1: warning: inital values in enum 'EMacro2' are not consistent
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: initial values in enum 'EMacro2' are not consistent
// CHECK-MESSAGES-ENABLE: :[[@LINE-2]]:1: warning: initial values in enum 'EMacro2' are not consistent
EMacro2_a = 1,
ENUMERATOR_2,
EMacro2_c,
Expand Down Expand Up @@ -78,3 +78,40 @@ enum EnumSequentialInitialValue {
EnumSequentialInitialValue_2 = 4,
// CHECK-FIXES-ENABLE: EnumSequentialInitialValue_2 ,
};

// gh107590
enum WithFwdDeclInconsistent : int;

enum WithFwdDeclInconsistent : int {
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: initial values in enum 'WithFwdDeclInconsistent' are not consistent
// CHECK-MESSAGES-ENABLE: :[[@LINE-2]]:1: warning: initial values in enum 'WithFwdDeclInconsistent' are not consistent
EFI0,
// CHECK-FIXES: EFI0 = 0,
EFI1 = 1,
EFI2,
// CHECK-FIXES: EFI2 = 2,
};

enum WithFwdDeclZeroFirst : int;

enum WithFwdDeclZeroFirst : int {
// CHECK-MESSAGES-ENABLE: :[[@LINE+1]]:3: warning: zero initial value for the first enumerator in 'WithFwdDeclZeroFirst' can be disregarded
EFZ0 = 0,
// CHECK-FIXES-ENABLE: EFZ0 ,
EFZ1,
EFZ2,
};


enum WithFwdDeclSequential : int;

enum WithFwdDeclSequential : int {
// CHECK-MESSAGES-ENABLE: :[[@LINE-1]]:1: warning: sequential initial value in 'WithFwdDeclSequential' can be ignored
EFS0 = 2,
// CHECK-FIXES-ENABLE: EFS0 = 2,
EFS1 = 3,
// CHECK-FIXES-ENABLE: EFS1 ,
EFS2 = 4,
// CHECK-FIXES-ENABLE: EFS2 ,
};

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// RUN: %check_clang_tidy %s readability-enum-initial-value %t

enum class EError {
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: inital values in enum 'EError' are not consistent
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: initial values in enum 'EError' are not consistent
EError_a = 1,
EError_b,
// CHECK-FIXES: EError_b = 2,
Expand Down
5 changes: 0 additions & 5 deletions clang/docs/ClangFormattedStatus.rst
Original file line number Diff line number Diff line change
Expand Up @@ -809,11 +809,6 @@ tree in terms of conformance to :doc:`ClangFormat` as of: March 06, 2022 17:32:2
- `4`
- `0`
- :good:`100%`
* - clang/tools/clang-rename
- `1`
- `1`
- `0`
- :good:`100%`
* - clang/tools/clang-repl
- `1`
- `1`
Expand Down
1 change: 1 addition & 0 deletions clang/docs/LanguageExtensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -667,6 +667,7 @@ Unless specified otherwise operation(±0) = ±0 and operation(±infinity) = ±in
T __builtin_elementwise_log(T x) return the natural logarithm of x floating point types
T __builtin_elementwise_log2(T x) return the base 2 logarithm of x floating point types
T __builtin_elementwise_log10(T x) return the base 10 logarithm of x floating point types
T __builtin_elementwise_popcount(T x) return the number of 1 bits in x integer types
T __builtin_elementwise_pow(T x, T y) return x raised to the power of y floating point types
T __builtin_elementwise_bitreverse(T x) return the integer represented after reversing the bits of x integer types
T __builtin_elementwise_exp(T x) returns the base-e exponential, e^x, of the specified value floating point types
Expand Down
18 changes: 16 additions & 2 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ code bases.

- The ``le32`` and ``le64`` targets have been removed.

- The ``clang-rename`` tool has been removed.

C/C++ Language Potentially Breaking Changes
-------------------------------------------

Expand Down Expand Up @@ -114,6 +116,7 @@ C++ Language Changes

- Accept C++26 user-defined ``static_assert`` messages in C++11 as an extension.

- Add ``__builtin_elementwise_popcount`` builtin for integer types only.

C++2c Feature Support
^^^^^^^^^^^^^^^^^^^^^
Expand Down Expand Up @@ -170,6 +173,9 @@ Resolutions to C++ Defect Reports
in constant expressions. These comparisons always worked in non-constant expressions.
(`CWG2749: Treatment of "pointer to void" for relational comparisons <https://cplusplus.github.io/CWG/issues/2749.html>`_).

- Reject explicit object parameters with type ``void`` (``this void``).
(`CWG2915: Explicit object parameters of type void <https://cplusplus.github.io/CWG/issues/2915.html>`_).

C Language Changes
------------------

Expand Down Expand Up @@ -249,7 +255,10 @@ Attribute Changes in Clang
(#GH106864)

- Introduced a new attribute ``[[clang::coro_await_elidable]]`` on coroutine return types
to express elideability at call sites where the coroutine is co_awaited as a prvalue.
to express elideability at call sites where the coroutine is invoked under a safe elide context.

- Introduced a new attribute ``[[clang::coro_await_elidable_argument]]`` on function parameters
to propagate safe elide context to arguments if such function is also under a safe elide context.

Improvements to Clang's diagnostics
-----------------------------------
Expand Down Expand Up @@ -389,8 +398,11 @@ Bug Fixes to C++ Support
- A follow-up fix was added for (#GH61460), as the previous fix was not entirely correct. (#GH86361)
- Fixed a crash in the typo correction of an invalid CTAD guide. (#GH107887)
- Fixed a crash when clang tries to subtitute parameter pack while retaining the parameter
pack. #GH63819, #GH107560
pack. (#GH63819), (#GH107560)
- Fix a crash when a static assert declaration has an invalid close location. (#GH108687)
- Avoided a redundant friend declaration instantiation under a certain ``consteval`` context. (#GH107175)
- Fixed an assertion failure in debug mode, and potential crashes in release mode, when
diagnosing a failed cast caused indirectly by a failed implicit conversion to the type of the constructor parameter.

Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down Expand Up @@ -506,6 +518,8 @@ AST Matchers
- Fixed an ordering issue with the `hasOperands` matcher occurring when setting a
binding in the first matcher and using it in the second matcher.

- Fixed a crash when traverse lambda expr with invalid captures. (#GH106444)

clang-format
------------

Expand Down
1 change: 0 additions & 1 deletion clang/docs/tools/clang-formatted-files.txt
Original file line number Diff line number Diff line change
Expand Up @@ -608,7 +608,6 @@ clang/tools/clang-refactor/ClangRefactor.cpp
clang/tools/clang-refactor/TestSupport.cpp
clang/tools/clang-refactor/TestSupport.h
clang/tools/clang-refactor/ToolRefactoringResultConsumer.h
clang/tools/clang-rename/ClangRename.cpp
clang/tools/clang-repl/ClangRepl.cpp
clang/tools/clang-scan-deps/ClangScanDeps.cpp
clang/tools/clang-shlib/clang-shlib.cpp
Expand Down
17 changes: 17 additions & 0 deletions clang/include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -782,6 +782,23 @@ class ASTContext : public RefCountedBase<ASTContext> {
const TargetInfo &getTargetInfo() const { return *Target; }
const TargetInfo *getAuxTargetInfo() const { return AuxTarget; }

const QualType GetHigherPrecisionFPType(QualType ElementType) const {
const auto *CurrentBT = cast<BuiltinType>(ElementType);
switch (CurrentBT->getKind()) {
case BuiltinType::Kind::Half:
case BuiltinType::Kind::Float16:
return FloatTy;
case BuiltinType::Kind::Float:
case BuiltinType::Kind::BFloat16:
return DoubleTy;
case BuiltinType::Kind::Double:
return LongDoubleTy;
default:
return ElementType;
}
return ElementType;
}

/// getIntTypeForBitwidth -
/// sets integer QualTy according to specified details:
/// bitwidth, signed/unsigned.
Expand Down
17 changes: 13 additions & 4 deletions clang/include/clang/Analysis/Analyses/ExprMutationAnalyzer.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,19 @@ class FunctionParmMutationAnalyzer {
static FunctionParmMutationAnalyzer *
getFunctionParmMutationAnalyzer(const FunctionDecl &Func, ASTContext &Context,
ExprMutationAnalyzer::Memoized &Memorized) {
auto [it, Inserted] = Memorized.FuncParmAnalyzer.try_emplace(&Func);
if (Inserted)
it->second = std::unique_ptr<FunctionParmMutationAnalyzer>(
new FunctionParmMutationAnalyzer(Func, Context, Memorized));
auto it = Memorized.FuncParmAnalyzer.find(&Func);
if (it == Memorized.FuncParmAnalyzer.end()) {
// Creating a new instance of FunctionParmMutationAnalyzer below may add
// additional elements to FuncParmAnalyzer. If we did try_emplace before
// creating a new instance, the returned iterator of try_emplace could be
// invalidated.
it =
Memorized.FuncParmAnalyzer
.try_emplace(&Func, std::unique_ptr<FunctionParmMutationAnalyzer>(
new FunctionParmMutationAnalyzer(
Func, Context, Memorized)))
.first;
}
return it->getSecond().get();
}

Expand Down
4 changes: 2 additions & 2 deletions clang/include/clang/Basic/AMDGPUTypes.def
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@
//===----------------------------------------------------------------------===//

#ifndef AMDGPU_OPAQUE_PTR_TYPE
#define AMDGPU_OPAQUE_PTR_TYPE(Name, MangledName, AS, Width, Align, Id, SingletonId) \
#define AMDGPU_OPAQUE_PTR_TYPE(Name, AS, Width, Align, Id, SingletonId) \
AMDGPU_TYPE(Name, Id, SingletonId)
#endif

AMDGPU_OPAQUE_PTR_TYPE("__amdgpu_buffer_rsrc_t", "__amdgpu_buffer_rsrc_t", 8, 128, 128, AMDGPUBufferRsrc, AMDGPUBufferRsrcTy)
AMDGPU_OPAQUE_PTR_TYPE("__amdgpu_buffer_rsrc_t", 8, 128, 128, AMDGPUBufferRsrc, AMDGPUBufferRsrcTy)

#undef AMDGPU_TYPE
#undef AMDGPU_OPAQUE_PTR_TYPE
8 changes: 8 additions & 0 deletions clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -1258,6 +1258,14 @@ def CoroAwaitElidable : InheritableAttr {
let SimpleHandler = 1;
}

def CoroAwaitElidableArgument : InheritableAttr {
let Spellings = [Clang<"coro_await_elidable_argument">];
let Subjects = SubjectList<[ParmVar]>;
let LangOpts = [CPlusPlus];
let Documentation = [CoroAwaitElidableArgumentDoc];
let SimpleHandler = 1;
}

// OSObject-based attributes.
def OSConsumed : InheritableParamAttr {
let Spellings = [Clang<"os_consumed">];
Expand Down
83 changes: 73 additions & 10 deletions clang/include/clang/Basic/AttrDocs.td
Original file line number Diff line number Diff line change
Expand Up @@ -8258,15 +8258,23 @@ but do not pass them to the underlying coroutine or pass them by value.
def CoroAwaitElidableDoc : Documentation {
let Category = DocCatDecl;
let Content = [{
The ``[[clang::coro_await_elidable]]`` is a class attribute which can be applied
to a coroutine return type.
The ``[[clang::coro_await_elidable]]`` is a class attribute which can be
applied to a coroutine return type. It provides a hint to the compiler to apply
Heap Allocation Elision more aggressively.

When a coroutine function that returns such a type calls another coroutine function,
the compiler performs heap allocation elision when the call to the coroutine function
is immediately co_awaited as a prvalue. In this case, the coroutine frame for the
callee will be a local variable within the enclosing braces in the caller's stack
frame. And the local variable, like other variables in coroutines, may be collected
into the coroutine frame, which may be allocated on the heap.
When a coroutine function returns such a type, a direct call expression therein
that returns a prvalue of a type attributed ``[[clang::coro_await_elidable]]``
is said to be under a safe elide context if one of the following is true:
- it is the immediate right-hand side operand to a co_await expression.
- it is an argument to a ``[[clang::coro_await_elidable_argument]]`` parameter
or parameter pack of another direct call expression under a safe elide context.

Do note that the safe elide context applies only to the call expression itself,
and the context does not transitively include any of its subexpressions unless
exceptional rules of ``[[clang::coro_await_elidable_argument]]`` apply.

The compiler performs heap allocation elision on call expressions under a safe
elide context, if the callee is a coroutine.

Example:

Expand All @@ -8281,8 +8289,63 @@ Example:
co_await t;
}

The behavior is undefined if the caller coroutine is destroyed earlier than the
callee coroutine.
Such elision replaces the heap allocated activation frame of the callee coroutine
with a local variable within the enclosing braces in the caller's stack frame.
The local variable, like other variables in coroutines, may be collected into the
coroutine frame, which may be allocated on the heap. The behavior is undefined
if the caller coroutine is destroyed earlier than the callee coroutine.

}];
}

def CoroAwaitElidableArgumentDoc : Documentation {
let Category = DocCatDecl;
let Content = [{

The ``[[clang::coro_await_elidable_argument]]`` is a function parameter attribute.
It works in conjunction with ``[[clang::coro_await_elidable]]`` to propagate a
safe elide context to a parameter or parameter pack if the function is called
under a safe elide context.

This is sometimes necessary on utility functions used to compose or modify the
behavior of a callee coroutine.

Example:

.. code-block:: c++

template <typename T>
class [[clang::coro_await_elidable]] Task { ... };

template <typename... T>
class [[clang::coro_await_elidable]] WhenAll { ... };

// `when_all` is a utility function that composes coroutines. It does not
// need to be a coroutine to propagate.
template <typename... T>
WhenAll<T...> when_all([[clang::coro_await_elidable_argument]] Task<T> tasks...);

Task<int> foo();
Task<int> bar();
Task<void> example1() {
// `when_all``, `foo``, and `bar` are all elide safe because `when_all` is
// under a safe elide context and, thanks to the [[clang::coro_await_elidable_argument]]
// attribute, such context is propagated to foo and bar.
co_await when_all(foo(), bar());
}

Task<void> example2() {
// `when_all` and `bar` are elide safe. `foo` is not elide safe.
auto f = foo();
co_await when_all(f, bar());
}


Task<void> example3() {
// None of the calls are elide safe.
auto t = when_all(foo(), bar());
co_await t;
}

}];
}
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/Builtins.td
Original file line number Diff line number Diff line change
Expand Up @@ -1322,6 +1322,12 @@ def ElementwiseLog10 : Builtin {
let Prototype = "void(...)";
}

def ElementwisePopcount : Builtin {
let Spellings = ["__builtin_elementwise_popcount"];
let Attributes = [NoThrow, Const, CustomTypeChecking];
let Prototype = "void(...)";
}

def ElementwisePow : Builtin {
let Spellings = ["__builtin_elementwise_pow"];
let Attributes = [NoThrow, Const, CustomTypeChecking];
Expand Down
270 changes: 90 additions & 180 deletions clang/include/clang/Basic/Diagnostic.h

Large diffs are not rendered by default.

5 changes: 0 additions & 5 deletions clang/include/clang/Basic/DiagnosticCommonKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,6 @@ def note_using : Note<"using">;
def note_possibility : Note<"one possibility">;
def note_also_found : Note<"also found">;

def warn_next_larger_fp_type_same_size_than_fp : Warning<
"higher precision floating-point type size has the same size than "
"floating-point type size">,
InGroup<DiagGroup<"higher-precision-fp">>;

// Parse && Lex

let CategoryName = "Lexical or Preprocessor Issue" in {
Expand Down
7 changes: 5 additions & 2 deletions clang/include/clang/Basic/DiagnosticIDs.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

namespace clang {
class DiagnosticsEngine;
class DiagnosticBuilder;
class SourceLocation;

// Import the diagnostic enums themselves.
Expand Down Expand Up @@ -486,11 +487,13 @@ class DiagnosticIDs : public RefCountedBase<DiagnosticIDs> {
///
/// \returns \c true if the diagnostic was emitted, \c false if it was
/// suppressed.
bool ProcessDiag(DiagnosticsEngine &Diag) const;
bool ProcessDiag(DiagnosticsEngine &Diag,
const DiagnosticBuilder &DiagBuilder) const;

/// Used to emit a diagnostic that is finally fully formed,
/// ignoring suppression.
void EmitDiag(DiagnosticsEngine &Diag, Level DiagLevel) const;
void EmitDiag(DiagnosticsEngine &Diag, const DiagnosticBuilder &DiagBuilder,
Level DiagLevel) const;

/// Whether the diagnostic may leave the AST in a state where some
/// invariants can break.
Expand Down
9 changes: 6 additions & 3 deletions clang/include/clang/Basic/DiagnosticParseKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,12 @@ def ext_cxx11_enum_fixed_underlying_type : Extension<
def ext_ms_c_enum_fixed_underlying_type : Extension<
"enumeration types with a fixed underlying type are a Microsoft extension">,
InGroup<MicrosoftFixedEnum>;
def ext_clang_c_enum_fixed_underlying_type : Extension<
"enumeration types with a fixed underlying type are a Clang extension">,
InGroup<DiagGroup<"fixed-enum-extension">>;
def ext_c23_enum_fixed_underlying_type : Extension<
"enumeration types with a fixed underlying type are a C23 extension">,
InGroup<C23>;
def warn_c17_compat_enum_fixed_underlying_type : Warning<
"enumeration types with a fixed underlying type are incompatible with C standards before C23">,
DefaultIgnore, InGroup<CPre23Compat>;
def warn_cxx98_compat_enum_fixed_underlying_type : Warning<
"enumeration types with a fixed underlying type are incompatible with C++98">,
InGroup<CXX98Compat>, DefaultIgnore;
Expand Down
15 changes: 12 additions & 3 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -3290,7 +3290,7 @@ def err_attribute_unsupported_m_profile
def err_duplicate_target_attribute
: Error<"%select{unsupported|duplicate|unknown}0%select{| CPU|"
" tune CPU}1 '%2' in the '%select{target|target_clones|target_version}3' "
"attribute string; ">;
"attribute string;">;
// The err_*_attribute_argument_not_int are separate because they're used by
// VerifyIntegerConstantExpression.
def err_aligned_attribute_argument_not_int : Error<
Expand Down Expand Up @@ -4687,6 +4687,8 @@ def err_void_only_param : Error<
"'void' must be the first and only parameter if specified">;
def err_void_param_qualified : Error<
"'void' as parameter must not have type qualifiers">;
def err_void_explicit_object_param : Error<
"explicit object parameter cannot have 'void' type">;
def err_ident_list_in_fn_declaration : Error<
"a parameter list without types is only allowed in a function definition">;
def ext_param_not_declared : ExtWarn<
Expand Down Expand Up @@ -6800,6 +6802,12 @@ def warn_arc_lifetime_result_type : Warning<
"ARC %select{unused|__unsafe_unretained|__strong|__weak|__autoreleasing}0 "
"lifetime qualifier on return type is ignored">,
InGroup<IgnoredQualifiers>;
def warn_excess_precision_not_supported : Warning<
"excess precision is requested but the target does not support excess "
"precision which may result in observable differences in complex division "
"behavior%select{|, additional uses where the requested higher precision "
"cannot be honored were found but not diagnosed}0">,
InGroup<DiagGroup<"higher-precision-for-complex-division">>;

let CategoryName = "ARC Retain Cycle" in {

Expand Down Expand Up @@ -9907,7 +9915,7 @@ def err_defaulted_comparison_constexpr_mismatch : Error<
"three-way comparison operator}0 cannot be "
"declared %select{constexpr|consteval}2 because "
"%select{it|for which the corresponding implicit 'operator==' }0 "
"invokes a non-constexpr comparison function ">;
"invokes a non-constexpr comparison function">;
def note_defaulted_comparison_not_constexpr : Note<
"non-constexpr comparison function would be used to compare "
"%select{|member %1|base class %1}0">;
Expand Down Expand Up @@ -11551,7 +11559,7 @@ def err_omp_wrong_device_function_call : Error<
"function with 'device_type(%0)' is not available on %select{device|host}1">;
def note_omp_marked_device_type_here : Note<"marked as 'device_type(%0)' here">;
def err_omp_declare_target_has_local_vars : Error<
"local variable '%0' should not be used in 'declare target' directive; ">;
"local variable '%0' should not be used in 'declare target' directive;">;
def warn_omp_declare_target_after_first_use : Warning<
"declaration marked as declare target after first use, it may lead to incorrect results">,
InGroup<OpenMPTarget>;
Expand Down Expand Up @@ -12373,6 +12381,7 @@ def warn_hlsl_deprecated_register_type_b: Warning<"binding type 'b' only applies
def warn_hlsl_deprecated_register_type_i: Warning<"binding type 'i' ignored. The 'integer constant' binding type is no longer supported">, InGroup<LegacyConstantRegisterBinding>, DefaultError;
def err_hlsl_unsupported_register_number : Error<"register number should be an integer">;
def err_hlsl_expected_space : Error<"invalid space specifier '%0' used; expected 'space' followed by an integer, like space1">;
def err_hlsl_space_on_global_constant : Error<"register space cannot be specified on global constants">;
def warn_hlsl_packoffset_mix : Warning<"cannot mix packoffset elements with nonpackoffset elements in a cbuffer">,
InGroup<HLSLMixPackOffset>;
def err_hlsl_packoffset_overlap : Error<"packoffset overlap between %0, %1">;
Expand Down
5 changes: 1 addition & 4 deletions clang/include/clang/Basic/PartialDiagnostic.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,13 +166,10 @@ class PartialDiagnostic : public StreamingDiagnostic {

void EmitToString(DiagnosticsEngine &Diags,
SmallVectorImpl<char> &Buf) const {
// FIXME: It should be possible to render a diagnostic to a string without
// messing with the state of the diagnostics engine.
DiagnosticBuilder DB(Diags.Report(getDiagID()));
Emit(DB);
Diagnostic(&Diags).FormatDiagnostic(Buf);
Diagnostic(&Diags, DB).FormatDiagnostic(Buf);
DB.Clear();
Diags.Clear();
}

/// Clear out this partial diagnostic, giving it a new diagnostic ID
Expand Down
7 changes: 5 additions & 2 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -5572,7 +5572,8 @@ def : Flag<["-"], "nocudalib">, Alias<nogpulib>;
def gpulibc : Flag<["-"], "gpulibc">, Visibility<[ClangOption, CC1Option, FlangOption, FC1Option]>,
HelpText<"Link the LLVM C Library for GPUs">;
def nogpulibc : Flag<["-"], "nogpulibc">, Visibility<[ClangOption, CC1Option, FlangOption, FC1Option]>;
def nodefaultlibs : Flag<["-"], "nodefaultlibs">;
def nodefaultlibs : Flag<["-"], "nodefaultlibs">,
Visibility<[ClangOption, FlangOption, CLOption, DXCOption]>;
def nodriverkitlib : Flag<["-"], "nodriverkitlib">;
def nofixprebinding : Flag<["-"], "nofixprebinding">;
def nolibc : Flag<["-"], "nolibc">;
Expand All @@ -5592,7 +5593,9 @@ def nostdincxx : Flag<["-"], "nostdinc++">, Visibility<[ClangOption, CC1Option]>
Group<IncludePath_Group>,
HelpText<"Disable standard #include directories for the C++ standard library">,
MarshallingInfoNegativeFlag<HeaderSearchOpts<"UseStandardCXXIncludes">>;
def nostdlib : Flag<["-"], "nostdlib">, Group<Link_Group>;
def nostdlib : Flag<["-"], "nostdlib">,
Visibility<[ClangOption, CLOption, FlangOption, DXCOption]>,
Group<Link_Group>;
def nostdlibxx : Flag<["-"], "nostdlib++">;
def object : Flag<["-"], "object">;
def o : JoinedOrSeparate<["-"], "o">,
Expand Down
10 changes: 7 additions & 3 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -626,10 +626,10 @@ class Sema final : public SemaBase {
const llvm::MapVector<FieldDecl *, DeleteLocs> &
getMismatchingDeleteExpressions() const;

/// Cause the active diagnostic on the DiagosticsEngine to be
/// emitted. This is closely coupled to the SemaDiagnosticBuilder class and
/// Cause the built diagnostic to be emitted on the DiagosticsEngine.
/// This is closely coupled to the SemaDiagnosticBuilder class and
/// should not be used elsewhere.
void EmitCurrentDiagnostic(unsigned DiagID);
void EmitDiagnostic(unsigned DiagID, const DiagnosticBuilder &DB);

void addImplicitTypedef(StringRef Name, QualType T);

Expand Down Expand Up @@ -7957,6 +7957,10 @@ class Sema final : public SemaBase {
/// Do an explicit extend of the given block pointer if we're in ARC.
void maybeExtendBlockObject(ExprResult &E);

std::vector<std::pair<QualType, unsigned>> ExcessPrecisionNotSatisfied;
SourceLocation LocationOfExcessPrecisionNotSatisfied;
void DiagnosePrecisionLossInComplexDivision();

private:
static BinaryOperatorKind ConvertTokenKindToBinaryOpcode(tok::TokenKind Kind);

Expand Down
6 changes: 3 additions & 3 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2243,8 +2243,7 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const {
Align = 8; \
break;
#include "clang/Basic/WebAssemblyReferenceTypes.def"
#define AMDGPU_OPAQUE_PTR_TYPE(NAME, MANGLEDNAME, AS, WIDTH, ALIGN, ID, \
SINGLETONID) \
#define AMDGPU_OPAQUE_PTR_TYPE(NAME, AS, WIDTH, ALIGN, ID, SINGLETONID) \
case BuiltinType::ID: \
Width = WIDTH; \
Align = ALIGN; \
Expand Down Expand Up @@ -3378,7 +3377,8 @@ static void encodeTypeForFunctionPointerAuth(const ASTContext &Ctx,
#include "clang/Basic/HLSLIntangibleTypes.def"
case BuiltinType::Dependent:
llvm_unreachable("should never get here");
case BuiltinType::AMDGPUBufferRsrc:
#define AMDGPU_TYPE(Name, Id, SingletonId) case BuiltinType::Id:
#include "clang/Basic/AMDGPUTypes.def"
case BuiltinType::WasmExternRef:
#define RVV_TYPE(Name, Id, SingletonId) case BuiltinType::Id:
#include "clang/Basic/RISCVVTypes.def"
Expand Down
35 changes: 19 additions & 16 deletions clang/lib/AST/ByteCode/Interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,10 @@ bool CallBI(InterpState &S, CodePtr OpPC, const Function *Func,
bool CallPtr(InterpState &S, CodePtr OpPC, uint32_t ArgSize,
const CallExpr *CE);

enum class ShiftDir { Left, Right };

/// Checks if the shift operation is legal.
template <typename LT, typename RT>
template <ShiftDir Dir, typename LT, typename RT>
bool CheckShift(InterpState &S, CodePtr OpPC, const LT &LHS, const RT &RHS,
unsigned Bits) {
if (RHS.isNegative()) {
Expand All @@ -181,19 +183,21 @@ bool CheckShift(InterpState &S, CodePtr OpPC, const LT &LHS, const RT &RHS,
return false;
}

if (LHS.isSigned() && !S.getLangOpts().CPlusPlus20) {
const Expr *E = S.Current->getExpr(OpPC);
// C++11 [expr.shift]p2: A signed left shift must have a non-negative
// operand, and must not overflow the corresponding unsigned type.
if (LHS.isNegative()) {
S.CCEDiag(E, diag::note_constexpr_lshift_of_negative) << LHS.toAPSInt();
if (!S.noteUndefinedBehavior())
return false;
} else if (LHS.toUnsigned().countLeadingZeros() <
static_cast<unsigned>(RHS)) {
S.CCEDiag(E, diag::note_constexpr_lshift_discards);
if (!S.noteUndefinedBehavior())
return false;
if constexpr (Dir == ShiftDir::Left) {
if (LHS.isSigned() && !S.getLangOpts().CPlusPlus20) {
const Expr *E = S.Current->getExpr(OpPC);
// C++11 [expr.shift]p2: A signed left shift must have a non-negative
// operand, and must not overflow the corresponding unsigned type.
if (LHS.isNegative()) {
S.CCEDiag(E, diag::note_constexpr_lshift_of_negative) << LHS.toAPSInt();
if (!S.noteUndefinedBehavior())
return false;
} else if (LHS.toUnsigned().countLeadingZeros() <
static_cast<unsigned>(RHS)) {
S.CCEDiag(E, diag::note_constexpr_lshift_discards);
if (!S.noteUndefinedBehavior())
return false;
}
}
}

Expand Down Expand Up @@ -2394,7 +2398,6 @@ inline bool RVOPtr(InterpState &S, CodePtr OpPC) {
//===----------------------------------------------------------------------===//
// Shr, Shl
//===----------------------------------------------------------------------===//
enum class ShiftDir { Left, Right };

template <class LT, class RT, ShiftDir Dir>
inline bool DoShift(InterpState &S, CodePtr OpPC, LT &LHS, RT &RHS) {
Expand Down Expand Up @@ -2431,7 +2434,7 @@ inline bool DoShift(InterpState &S, CodePtr OpPC, LT &LHS, RT &RHS) {
}
}

if (!CheckShift(S, OpPC, LHS, RHS, Bits))
if (!CheckShift<Dir>(S, OpPC, LHS, RHS, Bits))
return false;

// Limit the shift amount to Bits - 1. If this happened,
Expand Down
11 changes: 10 additions & 1 deletion clang/lib/AST/ByteCode/Program.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -204,9 +204,18 @@ std::optional<unsigned> Program::createGlobal(const ValueDecl *VD,
IsStatic = false;
IsExtern = true;
}

// Register all previous declarations as well. For extern blocks, just replace
// the index with the new variable.
if (auto Idx = createGlobal(VD, VD->getType(), IsStatic, IsExtern, Init)) {
for (const Decl *P = VD; P; P = P->getPreviousDecl())
for (const Decl *P = VD; P; P = P->getPreviousDecl()) {
if (P != VD) {
unsigned PIdx = GlobalIndices[P];
if (Globals[PIdx]->block()->isExtern())
Globals[PIdx] = Globals[*Idx];
}
GlobalIndices[P] = *Idx;
}
return *Idx;
}
return std::nullopt;
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/AST/ExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2146,7 +2146,7 @@ static bool IsLiteralLValue(const LValue &Value) {
if (Value.getLValueCallIndex())
return false;
const Expr *E = Value.Base.dyn_cast<const Expr*>();
return E && !isa<MaterializeTemporaryExpr, StringLiteral>(E);
return E && !isa<MaterializeTemporaryExpr>(E);
}

static bool IsWeakLValue(const LValue &Value) {
Expand Down
5 changes: 3 additions & 2 deletions clang/lib/ASTMatchers/ASTMatchFinder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -285,12 +285,13 @@ class MatchChildASTVisitor
ScopedIncrement ScopedDepth(&CurrentDepth);

for (unsigned I = 0, N = Node->capture_size(); I != N; ++I) {
const auto *C = Node->capture_begin() + I;
const LambdaCapture *C = Node->capture_begin() + I;
if (!C->isExplicit())
continue;
if (Node->isInitCapture(C) && !match(*C->getCapturedVar()))
return false;
if (!match(*Node->capture_init_begin()[I]))
const Expr *CIE = Node->capture_init_begin()[I];
if (CIE != nullptr && !match(*CIE))
return false;
}

Expand Down
85 changes: 42 additions & 43 deletions clang/lib/Basic/Diagnostic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,7 @@ void DiagnosticsEngine::Reset(bool soft /*=false*/) {
TrapNumErrorsOccurred = 0;
TrapNumUnrecoverableErrorsOccurred = 0;

CurDiagID = std::numeric_limits<unsigned>::max();
LastDiagLevel = DiagnosticIDs::Ignored;
DelayedDiagID = 0;

if (!soft) {
// Clear state related to #pragma diagnostic.
Expand All @@ -143,23 +141,6 @@ void DiagnosticsEngine::Reset(bool soft /*=false*/) {
}
}

void DiagnosticsEngine::SetDelayedDiagnostic(unsigned DiagID, StringRef Arg1,
StringRef Arg2, StringRef Arg3) {
if (DelayedDiagID)
return;

DelayedDiagID = DiagID;
DelayedDiagArg1 = Arg1.str();
DelayedDiagArg2 = Arg2.str();
DelayedDiagArg3 = Arg3.str();
}

void DiagnosticsEngine::ReportDelayed() {
unsigned ID = DelayedDiagID;
DelayedDiagID = 0;
Report(ID) << DelayedDiagArg1 << DelayedDiagArg2 << DelayedDiagArg3;
}

DiagnosticMapping &
DiagnosticsEngine::DiagState::getOrAddMapping(diag::kind Diag) {
std::pair<iterator, bool> Result =
Expand Down Expand Up @@ -503,39 +484,31 @@ void DiagnosticsEngine::setSeverityForAll(diag::Flavor Flavor,
}

void DiagnosticsEngine::Report(const StoredDiagnostic &storedDiag) {
assert(CurDiagID == std::numeric_limits<unsigned>::max() &&
"Multiple diagnostics in flight at once!");

CurDiagLoc = storedDiag.getLocation();
CurDiagID = storedDiag.getID();
DiagStorage.NumDiagArgs = 0;

DiagStorage.DiagRanges.clear();
DiagnosticStorage DiagStorage;
DiagStorage.DiagRanges.append(storedDiag.range_begin(),
storedDiag.range_end());

DiagStorage.FixItHints.clear();
DiagStorage.FixItHints.append(storedDiag.fixit_begin(),
storedDiag.fixit_end());

assert(Client && "DiagnosticConsumer not set!");
Level DiagLevel = storedDiag.getLevel();
Diagnostic Info(this, storedDiag.getMessage());
Diagnostic Info(this, storedDiag.getLocation(), storedDiag.getID(),
DiagStorage, storedDiag.getMessage());
Client->HandleDiagnostic(DiagLevel, Info);
if (Client->IncludeInDiagnosticCounts()) {
if (DiagLevel == DiagnosticsEngine::Warning)
++NumWarnings;
}

CurDiagID = std::numeric_limits<unsigned>::max();
}

bool DiagnosticsEngine::EmitCurrentDiagnostic(bool Force) {
bool DiagnosticsEngine::EmitDiagnostic(const DiagnosticBuilder &DB,
bool Force) {
assert(getClient() && "DiagnosticClient not set!");

bool Emitted;
if (Force) {
Diagnostic Info(this);
Diagnostic Info(this, DB);

// Figure out the diagnostic level of this message.
DiagnosticIDs::Level DiagLevel
Expand All @@ -544,24 +517,50 @@ bool DiagnosticsEngine::EmitCurrentDiagnostic(bool Force) {
Emitted = (DiagLevel != DiagnosticIDs::Ignored);
if (Emitted) {
// Emit the diagnostic regardless of suppression level.
Diags->EmitDiag(*this, DiagLevel);
Diags->EmitDiag(*this, DB, DiagLevel);
}
} else {
// Process the diagnostic, sending the accumulated information to the
// DiagnosticConsumer.
Emitted = ProcessDiag();
Emitted = ProcessDiag(DB);
}

// Clear out the current diagnostic object.
Clear();
return Emitted;
}

DiagnosticBuilder::DiagnosticBuilder(DiagnosticsEngine *DiagObj,
SourceLocation DiagLoc, unsigned DiagID)
: StreamingDiagnostic(DiagObj->DiagAllocator), DiagObj(DiagObj),
DiagLoc(DiagLoc), DiagID(DiagID), IsActive(true) {
assert(DiagObj && "DiagnosticBuilder requires a valid DiagnosticsEngine!");
}

// If there was a delayed diagnostic, emit it now.
if (!Force && DelayedDiagID)
ReportDelayed();
DiagnosticBuilder::DiagnosticBuilder(const DiagnosticBuilder &D)
: StreamingDiagnostic() {
DiagLoc = D.DiagLoc;
DiagID = D.DiagID;
FlagValue = D.FlagValue;
DiagObj = D.DiagObj;
DiagStorage = D.DiagStorage;
D.DiagStorage = nullptr;
Allocator = D.Allocator;
IsActive = D.IsActive;
IsForceEmit = D.IsForceEmit;
D.Clear();
}

return Emitted;
Diagnostic::Diagnostic(const DiagnosticsEngine *DO,
const DiagnosticBuilder &DiagBuilder)
: DiagObj(DO), DiagLoc(DiagBuilder.DiagLoc), DiagID(DiagBuilder.DiagID),
FlagValue(DiagBuilder.FlagValue), DiagStorage(*DiagBuilder.getStorage()) {
}

Diagnostic::Diagnostic(const DiagnosticsEngine *DO, SourceLocation DiagLoc,
unsigned DiagID, const DiagnosticStorage &DiagStorage,
StringRef StoredDiagMessage)
: DiagObj(DO), DiagLoc(DiagLoc), DiagID(DiagID), DiagStorage(DiagStorage),
StoredDiagMessage(StoredDiagMessage) {}

DiagnosticConsumer::~DiagnosticConsumer() = default;

void DiagnosticConsumer::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
Expand Down Expand Up @@ -1216,13 +1215,13 @@ bool ForwardingDiagnosticConsumer::IncludeInDiagnosticCounts() const {
return Target.IncludeInDiagnosticCounts();
}

PartialDiagnostic::DiagStorageAllocator::DiagStorageAllocator() {
DiagStorageAllocator::DiagStorageAllocator() {
for (unsigned I = 0; I != NumCached; ++I)
FreeList[I] = Cached + I;
NumFreeListEntries = NumCached;
}

PartialDiagnostic::DiagStorageAllocator::~DiagStorageAllocator() {
DiagStorageAllocator::~DiagStorageAllocator() {
// Don't assert if we are in a CrashRecovery context, as this invariant may
// be invalidated during a crash.
assert((NumFreeListEntries == NumCached ||
Expand Down
21 changes: 11 additions & 10 deletions clang/lib/Basic/DiagnosticIDs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -566,7 +566,7 @@ DiagnosticIDs::getDiagnosticSeverity(unsigned DiagID, SourceLocation Loc,

// If explicitly requested, map fatal errors to errors.
if (Result == diag::Severity::Fatal &&
Diag.CurDiagID != diag::fatal_too_many_errors && Diag.FatalsAsError)
DiagID != diag::fatal_too_many_errors && Diag.FatalsAsError)
Result = diag::Severity::Error;

bool ShowInSystemHeader =
Expand Down Expand Up @@ -800,8 +800,9 @@ StringRef DiagnosticIDs::getNearestOption(diag::Flavor Flavor,

/// ProcessDiag - This is the method used to report a diagnostic that is
/// finally fully formed.
bool DiagnosticIDs::ProcessDiag(DiagnosticsEngine &Diag) const {
Diagnostic Info(&Diag);
bool DiagnosticIDs::ProcessDiag(DiagnosticsEngine &Diag,
const DiagnosticBuilder &DiagBuilder) const {
Diagnostic Info(&Diag, DiagBuilder);

assert(Diag.getClient() && "DiagnosticClient not set!");

Expand Down Expand Up @@ -867,31 +868,31 @@ bool DiagnosticIDs::ProcessDiag(DiagnosticsEngine &Diag) const {
// stop a flood of bogus errors.
if (Diag.ErrorLimit && Diag.NumErrors > Diag.ErrorLimit &&
DiagLevel == DiagnosticIDs::Error) {
Diag.SetDelayedDiagnostic(diag::fatal_too_many_errors);
Diag.Report(diag::fatal_too_many_errors);
return false;
}
}

// Make sure we set FatalErrorOccurred to ensure that the notes from the
// diagnostic that caused `fatal_too_many_errors` won't be emitted.
if (Diag.CurDiagID == diag::fatal_too_many_errors)
if (Info.getID() == diag::fatal_too_many_errors)
Diag.FatalErrorOccurred = true;
// Finally, report it.
EmitDiag(Diag, DiagLevel);
EmitDiag(Diag, DiagBuilder, DiagLevel);
return true;
}

void DiagnosticIDs::EmitDiag(DiagnosticsEngine &Diag, Level DiagLevel) const {
Diagnostic Info(&Diag);
void DiagnosticIDs::EmitDiag(DiagnosticsEngine &Diag,
const DiagnosticBuilder &DiagBuilder,
Level DiagLevel) const {
Diagnostic Info(&Diag, DiagBuilder);
assert(DiagLevel != DiagnosticIDs::Ignored && "Cannot emit ignored diagnostics!");

Diag.Client->HandleDiagnostic((DiagnosticsEngine::Level)DiagLevel, Info);
if (Diag.Client->IncludeInDiagnosticCounts()) {
if (DiagLevel == DiagnosticIDs::Warning)
++Diag.NumWarnings;
}

Diag.CurDiagID = ~0U;
}

bool DiagnosticIDs::isUnrecoverable(unsigned DiagID) const {
Expand Down
23 changes: 4 additions & 19 deletions clang/lib/Basic/SourceManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,13 +130,8 @@ ContentCache::getBufferOrNone(DiagnosticsEngine &Diag, FileManager &FM,
// the file could also have been removed during processing. Since we can't
// really deal with this situation, just create an empty buffer.
if (!BufferOrError) {
if (Diag.isDiagnosticInFlight())
Diag.SetDelayedDiagnostic(diag::err_cannot_open_file,
ContentsEntry->getName(),
BufferOrError.getError().message());
else
Diag.Report(Loc, diag::err_cannot_open_file)
<< ContentsEntry->getName() << BufferOrError.getError().message();
Diag.Report(Loc, diag::err_cannot_open_file)
<< ContentsEntry->getName() << BufferOrError.getError().message();

return std::nullopt;
}
Expand All @@ -153,12 +148,7 @@ ContentCache::getBufferOrNone(DiagnosticsEngine &Diag, FileManager &FM,
// ContentsEntry::getSize() could have the wrong size. Use
// MemoryBuffer::getBufferSize() instead.
if (Buffer->getBufferSize() >= std::numeric_limits<unsigned>::max()) {
if (Diag.isDiagnosticInFlight())
Diag.SetDelayedDiagnostic(diag::err_file_too_large,
ContentsEntry->getName());
else
Diag.Report(Loc, diag::err_file_too_large)
<< ContentsEntry->getName();
Diag.Report(Loc, diag::err_file_too_large) << ContentsEntry->getName();

return std::nullopt;
}
Expand All @@ -168,12 +158,7 @@ ContentCache::getBufferOrNone(DiagnosticsEngine &Diag, FileManager &FM,
// have come from a stat cache).
if (!ContentsEntry->isNamedPipe() &&
Buffer->getBufferSize() != (size_t)ContentsEntry->getSize()) {
if (Diag.isDiagnosticInFlight())
Diag.SetDelayedDiagnostic(diag::err_file_modified,
ContentsEntry->getName());
else
Diag.Report(Loc, diag::err_file_modified)
<< ContentsEntry->getName();
Diag.Report(Loc, diag::err_file_modified) << ContentsEntry->getName();

return std::nullopt;
}
Expand Down
8 changes: 7 additions & 1 deletion clang/lib/Basic/Targets/AArch64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,13 @@ void AArch64TargetInfo::getTargetDefines(const LangOptions &Opts,
Builder.defineMacro("__AARCH64_CMODEL_" + CodeModel + "__");

// ACLE predefines. Many can only have one possible value on v8 AArch64.
Builder.defineMacro("__ARM_ACLE", "200");
Builder.defineMacro("__ARM_ACLE_VERSION(year, quarter, patch)",
"(100 * (year) + 10 * (quarter) + (patch))");
#define ARM_ACLE_VERSION(Y, Q, P) (100 * (Y) + 10 * (Q) + (P))
Builder.defineMacro("__ARM_ACLE", Twine(ARM_ACLE_VERSION(2024, 2, 0)));
Builder.defineMacro("__FUNCTION_MULTI_VERSIONING_SUPPORT_LEVEL",
Twine(ARM_ACLE_VERSION(2024, 2, 0)));
#undef ARM_ACLE_VERSION
Builder.defineMacro("__ARM_ARCH",
std::to_string(ArchInfo->Version.getMajor()));
Builder.defineMacro("__ARM_ARCH_PROFILE",
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/CodeGen/CGBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3834,6 +3834,9 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
case Builtin::BI__builtin_elementwise_floor:
return RValue::get(emitBuiltinWithOneOverloadedType<1>(
*this, E, llvm::Intrinsic::floor, "elt.floor"));
case Builtin::BI__builtin_elementwise_popcount:
return RValue::get(emitBuiltinWithOneOverloadedType<1>(
*this, E, llvm::Intrinsic::ctpop, "elt.ctpop"));
case Builtin::BI__builtin_elementwise_roundeven:
return RValue::get(emitBuiltinWithOneOverloadedType<1>(
*this, E, llvm::Intrinsic::roundeven, "elt.roundeven"));
Expand Down
7 changes: 3 additions & 4 deletions clang/lib/CodeGen/CGDebugInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -900,13 +900,12 @@ llvm::DIType *CGDebugInfo::CreateType(const BuiltinType *BT) {
return SingletonId; \
}
#include "clang/Basic/WebAssemblyReferenceTypes.def"
#define AMDGPU_OPAQUE_PTR_TYPE(Name, MangledName, AS, Width, Align, Id, \
SingletonId) \
#define AMDGPU_OPAQUE_PTR_TYPE(Name, AS, Width, Align, Id, SingletonId) \
case BuiltinType::Id: { \
if (!SingletonId) \
SingletonId = \
DBuilder.createForwardDecl(llvm::dwarf::DW_TAG_structure_type, \
MangledName, TheCU, TheCU->getFile(), 0); \
DBuilder.createForwardDecl(llvm::dwarf::DW_TAG_structure_type, Name, \
TheCU, TheCU->getFile(), 0); \
return SingletonId; \
}
#include "clang/Basic/AMDGPUTypes.def"
Expand Down
29 changes: 8 additions & 21 deletions clang/lib/CodeGen/CGExprComplex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -288,28 +288,15 @@ class ComplexExprEmitter
ComplexPairTy EmitComplexBinOpLibCall(StringRef LibCallName,
const BinOpInfo &Op);

QualType GetHigherPrecisionFPType(QualType ElementType) {
const auto *CurrentBT = cast<BuiltinType>(ElementType);
switch (CurrentBT->getKind()) {
case BuiltinType::Kind::Float16:
return CGF.getContext().FloatTy;
case BuiltinType::Kind::Float:
case BuiltinType::Kind::BFloat16:
return CGF.getContext().DoubleTy;
case BuiltinType::Kind::Double:
return CGF.getContext().LongDoubleTy;
default:
return ElementType;
}
}

QualType HigherPrecisionTypeForComplexArithmetic(QualType ElementType,
bool IsDivOpCode) {
QualType HigherElementType = GetHigherPrecisionFPType(ElementType);
ASTContext &Ctx = CGF.getContext();
const QualType HigherElementType =
Ctx.GetHigherPrecisionFPType(ElementType);
const llvm::fltSemantics &ElementTypeSemantics =
CGF.getContext().getFloatTypeSemantics(ElementType);
Ctx.getFloatTypeSemantics(ElementType);
const llvm::fltSemantics &HigherElementTypeSemantics =
CGF.getContext().getFloatTypeSemantics(HigherElementType);
Ctx.getFloatTypeSemantics(HigherElementType);
// Check that the promoted type can handle the intermediate values without
// overflowing. This can be interpreted as:
// (SmallerType.LargestFiniteVal * SmallerType.LargestFiniteVal) * 2 <=
Expand All @@ -320,10 +307,10 @@ class ComplexExprEmitter
if (llvm::APFloat::semanticsMaxExponent(ElementTypeSemantics) * 2 + 1 <=
llvm::APFloat::semanticsMaxExponent(HigherElementTypeSemantics)) {
FPHasBeenPromoted = true;
return CGF.getContext().getComplexType(HigherElementType);
return Ctx.getComplexType(HigherElementType);
} else {
DiagnosticsEngine &Diags = CGF.CGM.getDiags();
Diags.Report(diag::warn_next_larger_fp_type_same_size_than_fp);
// The intermediate values can't be represented in the promoted type
// without overflowing.
return QualType();
}
}
Expand Down
8 changes: 8 additions & 0 deletions clang/lib/CodeGen/CGHLSLRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,14 @@ void clang::CodeGen::CGHLSLRuntime::setHLSLEntryAttributes(
NumThreadsAttr->getZ());
Fn->addFnAttr(NumThreadsKindStr, NumThreadsStr);
}
if (HLSLWaveSizeAttr *WaveSizeAttr = FD->getAttr<HLSLWaveSizeAttr>()) {
const StringRef WaveSizeKindStr = "hlsl.wavesize";
std::string WaveSizeStr =
formatv("{0},{1},{2}", WaveSizeAttr->getMin(), WaveSizeAttr->getMax(),
WaveSizeAttr->getPreferred());
Fn->addFnAttr(WaveSizeKindStr, WaveSizeStr);
}
Fn->addFnAttr(llvm::Attribute::NoInline);
}

static Value *buildVectorInput(IRBuilder<> &B, Function *F, llvm::Type *Ty) {
Expand Down
20 changes: 14 additions & 6 deletions clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2473,11 +2473,14 @@ void CodeGenModule::SetLLVMFunctionAttributesForDefinition(const Decl *D,
B.addAttribute(llvm::Attribute::StackProtectReq);

if (!D) {
// Non-entry HLSL functions must always be inlined.
if (getLangOpts().HLSL && !F->hasFnAttribute(llvm::Attribute::NoInline))
B.addAttribute(llvm::Attribute::AlwaysInline);
// If we don't have a declaration to control inlining, the function isn't
// explicitly marked as alwaysinline for semantic reasons, and inlining is
// disabled, mark the function as noinline.
if (!F->hasFnAttribute(llvm::Attribute::AlwaysInline) &&
CodeGenOpts.getInlining() == CodeGenOptions::OnlyAlwaysInlining)
else if (!F->hasFnAttribute(llvm::Attribute::AlwaysInline) &&
CodeGenOpts.getInlining() == CodeGenOptions::OnlyAlwaysInlining)
B.addAttribute(llvm::Attribute::NoInline);

F->addFnAttrs(B);
Expand All @@ -2504,9 +2507,13 @@ void CodeGenModule::SetLLVMFunctionAttributesForDefinition(const Decl *D,
ShouldAddOptNone &= !D->hasAttr<MinSizeAttr>();
ShouldAddOptNone &= !D->hasAttr<AlwaysInlineAttr>();

// Add optnone, but do so only if the function isn't always_inline.
if ((ShouldAddOptNone || D->hasAttr<OptimizeNoneAttr>()) &&
!F->hasFnAttribute(llvm::Attribute::AlwaysInline)) {
// Non-entry HLSL functions must always be inlined.
if (getLangOpts().HLSL && !F->hasFnAttribute(llvm::Attribute::NoInline) &&
!D->hasAttr<NoInlineAttr>()) {
B.addAttribute(llvm::Attribute::AlwaysInline);
} else if ((ShouldAddOptNone || D->hasAttr<OptimizeNoneAttr>()) &&
!F->hasFnAttribute(llvm::Attribute::AlwaysInline)) {
// Add optnone, but do so only if the function isn't always_inline.
B.addAttribute(llvm::Attribute::OptimizeNone);

// OptimizeNone implies noinline; we should not be inlining such functions.
Expand All @@ -2526,7 +2533,8 @@ void CodeGenModule::SetLLVMFunctionAttributesForDefinition(const Decl *D,
B.addAttribute(llvm::Attribute::NoInline);
} else if (D->hasAttr<NoDuplicateAttr>()) {
B.addAttribute(llvm::Attribute::NoDuplicate);
} else if (D->hasAttr<NoInlineAttr>() && !F->hasFnAttribute(llvm::Attribute::AlwaysInline)) {
} else if (D->hasAttr<NoInlineAttr>() &&
!F->hasFnAttribute(llvm::Attribute::AlwaysInline)) {
// Add noinline if the function isn't always_inline.
B.addAttribute(llvm::Attribute::NoInline);
} else if (D->hasAttr<AlwaysInlineAttr>() &&
Expand Down
3 changes: 1 addition & 2 deletions clang/lib/CodeGen/CodeGenTypes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -558,8 +558,7 @@ llvm::Type *CodeGenTypes::ConvertType(QualType T) {
llvm_unreachable("Unexpected wasm reference builtin type!"); \
} break;
#include "clang/Basic/WebAssemblyReferenceTypes.def"
#define AMDGPU_OPAQUE_PTR_TYPE(Name, MangledName, AS, Width, Align, Id, \
SingletonId) \
#define AMDGPU_OPAQUE_PTR_TYPE(Name, AS, Width, Align, Id, SingletonId) \
case BuiltinType::Id: \
return llvm::PointerType::get(getLLVMContext(), AS);
#include "clang/Basic/AMDGPUTypes.def"
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/Driver/ToolChains/AIX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,8 @@ void aix::Linker::ConstructJob(Compilation &C, const JobAction &JA,
}
}

if (D.IsFlangMode()) {
if (D.IsFlangMode() &&
!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) {
addFortranRuntimeLibraryPath(ToolChain, Args, CmdArgs);
addFortranRuntimeLibs(ToolChain, Args, CmdArgs);
CmdArgs.push_back("-lm");
Expand Down
Loading