39 changes: 5 additions & 34 deletions clang-tools-extra/clang-tidy/misc/DefinitionsInHeadersCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,45 +28,16 @@ AST_MATCHER_P(NamedDecl, usesHeaderFileExtension, FileExtensionsSet,
DefinitionsInHeadersCheck::DefinitionsInHeadersCheck(StringRef Name,
ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
UseHeaderFileExtension(Options.get("UseHeaderFileExtension", true)) {
std::optional<StringRef> HeaderFileExtensionsOption =
Options.get("HeaderFileExtensions");
RawStringHeaderFileExtensions =
HeaderFileExtensionsOption.value_or(utils::defaultHeaderFileExtensions());
if (HeaderFileExtensionsOption) {
if (!utils::parseFileExtensions(RawStringHeaderFileExtensions,
HeaderFileExtensions,
utils::defaultFileExtensionDelimiters())) {
this->configurationDiag("Invalid header file extension: '%0'")
<< RawStringHeaderFileExtensions;
}
} else
HeaderFileExtensions = Context->getHeaderFileExtensions();
}

void DefinitionsInHeadersCheck::storeOptions(
ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "UseHeaderFileExtension", UseHeaderFileExtension);
Options.store(Opts, "HeaderFileExtensions", RawStringHeaderFileExtensions);
}
HeaderFileExtensions(Context->getHeaderFileExtensions()) {}

void DefinitionsInHeadersCheck::registerMatchers(MatchFinder *Finder) {
auto DefinitionMatcher =
anyOf(functionDecl(isDefinition(), unless(isDeleted())),
varDecl(isDefinition()));
if (UseHeaderFileExtension) {
Finder->addMatcher(namedDecl(DefinitionMatcher,
usesHeaderFileExtension(HeaderFileExtensions))
.bind("name-decl"),
this);
} else {
Finder->addMatcher(
namedDecl(DefinitionMatcher,
anyOf(usesHeaderFileExtension(HeaderFileExtensions),
unless(isExpansionInMainFile())))
.bind("name-decl"),
this);
}
Finder->addMatcher(namedDecl(DefinitionMatcher,
usesHeaderFileExtension(HeaderFileExtensions))
.bind("name-decl"),
this);
}

void DefinitionsInHeadersCheck::check(const MatchFinder::MatchResult &Result) {
Expand Down
9 changes: 0 additions & 9 deletions clang-tools-extra/clang-tidy/misc/DefinitionsInHeadersCheck.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,6 @@ namespace clang::tidy::misc {
/// The check supports these options:
/// - `UseHeaderFileExtension`: Whether to use file extension to distinguish
/// header files. True by default.
/// - `HeaderFileExtensions`: a semicolon-separated list of filename
/// extensions of header files (The filename extension should not contain
/// "." prefix). ";h;hh;hpp;hxx" by default.
///
/// For extension-less header files, using an empty string or leaving an
/// empty string between ";" if there are other filename extensions.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/misc/definitions-in-headers.html
Expand All @@ -35,13 +29,10 @@ class DefinitionsInHeadersCheck : public ClangTidyCheck {
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
return LangOpts.CPlusPlus11;
}
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;

private:
const bool UseHeaderFileExtension;
StringRef RawStringHeaderFileExtensions;
FileExtensionsSet HeaderFileExtensions;
};

Expand Down
17 changes: 2 additions & 15 deletions clang-tools-extra/clang-tidy/misc/UnusedUsingDeclsCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,21 +39,8 @@ static bool shouldCheckDecl(const Decl *TargetDecl) {

UnusedUsingDeclsCheck::UnusedUsingDeclsCheck(StringRef Name,
ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {
std::optional<StringRef> HeaderFileExtensionsOption =
Options.get("HeaderFileExtensions");
RawStringHeaderFileExtensions =
HeaderFileExtensionsOption.value_or(utils::defaultHeaderFileExtensions());
if (HeaderFileExtensionsOption) {
if (!utils::parseFileExtensions(RawStringHeaderFileExtensions,
HeaderFileExtensions,
utils::defaultFileExtensionDelimiters())) {
this->configurationDiag("Invalid header file extension: '%0'")
<< RawStringHeaderFileExtensions;
}
} else
HeaderFileExtensions = Context->getHeaderFileExtensions();
}
: ClangTidyCheck(Name, Context),
HeaderFileExtensions(Context->getHeaderFileExtensions()) {}

void UnusedUsingDeclsCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(usingDecl(isExpansionInMainFile()).bind("using"), this);
Expand Down
22 changes: 2 additions & 20 deletions clang-tools-extra/clang-tidy/misc/UseAnonymousNamespaceCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,26 +31,8 @@ AST_MATCHER(VarDecl, isStaticDataMember) { return Node.isStaticDataMember(); }

UseAnonymousNamespaceCheck::UseAnonymousNamespaceCheck(
StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {
std::optional<StringRef> HeaderFileExtensionsOption =
Options.get("HeaderFileExtensions");
RawStringHeaderFileExtensions =
HeaderFileExtensionsOption.value_or(utils::defaultHeaderFileExtensions());
if (HeaderFileExtensionsOption) {
if (!utils::parseFileExtensions(RawStringHeaderFileExtensions,
HeaderFileExtensions,
utils::defaultFileExtensionDelimiters())) {
this->configurationDiag("Invalid header file extension: '%0'")
<< RawStringHeaderFileExtensions;
}
} else
HeaderFileExtensions = Context->getHeaderFileExtensions();
}

void UseAnonymousNamespaceCheck::storeOptions(
ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "HeaderFileExtensions", RawStringHeaderFileExtensions);
}
: ClangTidyCheck(Name, Context),
HeaderFileExtensions(Context->getHeaderFileExtensions()) {}

void UseAnonymousNamespaceCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
Expand Down
10 changes: 0 additions & 10 deletions clang-tools-extra/clang-tidy/misc/UseAnonymousNamespaceCheck.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,6 @@ namespace clang::tidy::misc {
/// Warns when using 'static' functions or variables at global scope, and
/// suggests moving them to an anonymous namespace.
///
/// The check supports these options:
/// - `HeaderFileExtensions`: a semicolon-separated list of filename
/// extensions of header files (The filename extension should not contain
/// "." prefix). ";h;hh;hpp;hxx" by default.
///
/// For extension-less header files, using an empty string or leaving an
/// empty string between ";" if there are other filename extensions.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/misc/use-anonymous-namespace.html
class UseAnonymousNamespaceCheck : public ClangTidyCheck {
Expand All @@ -33,12 +25,10 @@ class UseAnonymousNamespaceCheck : public ClangTidyCheck {
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
return LangOpts.CPlusPlus;
}
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;

private:
StringRef RawStringHeaderFileExtensions;
FileExtensionsSet HeaderFileExtensions;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -658,7 +658,7 @@ std::string IdentifierNamingCheck::HungarianNotation::getEnumPrefix(
const auto *ED = cast<EnumDecl>(ECD->getDeclContext());

std::string Name = ED->getName().str();
if (std::string::npos != Name.find("enum")) {
if (StringRef(Name).contains("enum")) {
Name = Name.substr(strlen("enum"), Name.length() - strlen("enum"));
Name = Name.erase(0, Name.find_first_not_of(' '));
}
Expand Down
4 changes: 0 additions & 4 deletions clang-tools-extra/clang-tidy/utils/HeaderGuard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -269,10 +269,6 @@ class HeaderGuardPPCallbacks : public PPCallbacks {
};
} // namespace

void HeaderGuardCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "HeaderFileExtensions", RawStringHeaderFileExtensions);
}

void HeaderGuardCheck::registerPPCallbacks(const SourceManager &SM,
Preprocessor *PP,
Preprocessor *ModuleExpanderPP) {
Expand Down
27 changes: 3 additions & 24 deletions clang-tools-extra/clang-tidy/utils/HeaderGuard.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,32 +15,12 @@
namespace clang::tidy::utils {

/// Finds and fixes header guards.
/// The check supports these options:
/// - `HeaderFileExtensions`: a semicolon-separated list of filename
/// extensions of header files (The filename extension should not contain
/// "." prefix). ";h;hh;hpp;hxx" by default.
///
/// For extension-less header files, using an empty string or leaving an
/// empty string between ";" if there are other filename extensions.
class HeaderGuardCheck : public ClangTidyCheck {
public:
HeaderGuardCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {
std::optional<StringRef> HeaderFileExtensionsOption =
Options.get("HeaderFileExtensions");
RawStringHeaderFileExtensions = HeaderFileExtensionsOption.value_or(
utils::defaultHeaderFileExtensions());
if (HeaderFileExtensionsOption) {
if (!utils::parseFileExtensions(
RawStringHeaderFileExtensions, HeaderFileExtensions,
utils::defaultFileExtensionDelimiters())) {
this->configurationDiag("Invalid header file extension: '%0'")
<< RawStringHeaderFileExtensions;
}
} else
HeaderFileExtensions = Context->getHeaderFileExtensions();
}
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
: ClangTidyCheck(Name, Context),
HeaderFileExtensions(Context->getHeaderFileExtensions()) {}

void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
Preprocessor *ModuleExpanderPP) override;

Expand All @@ -65,7 +45,6 @@ class HeaderGuardCheck : public ClangTidyCheck {
StringRef OldGuard = StringRef()) = 0;

private:
std::string RawStringHeaderFileExtensions;
FileExtensionsSet HeaderFileExtensions;
};

Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clangd/SemanticHighlighting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ std::optional<HighlightingKind> kindForDecl(const NamedDecl *D,
if (auto *OMD = dyn_cast<ObjCMethodDecl>(D))
return OMD->isClassMethod() ? HighlightingKind::StaticMethod
: HighlightingKind::Method;
if (isa<FieldDecl, ObjCPropertyDecl>(D))
if (isa<FieldDecl, IndirectFieldDecl, ObjCPropertyDecl>(D))
return HighlightingKind::Field;
if (isa<EnumDecl>(D))
return HighlightingKind::Enum;
Expand Down
16 changes: 16 additions & 0 deletions clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1076,6 +1076,22 @@ sizeof...($TemplateParameter[[Elements]]);
using $Class[[Y]]$Bracket[[<]]0$Bracket[[>]]::$Unknown_dependentName[[xxx]];
};
};
)cpp",
// Heuristically resolved IndirectFieldDecl
R"cpp(
template $Bracket[[<]]typename $TemplateParameter_def[[T]]$Bracket[[>]]
struct $Class_def[[Base]] {
struct {
int $Field_decl[[waldo]];
};
};
template $Bracket[[<]]typename $TemplateParameter_def[[T]]$Bracket[[>]]
struct $Class_def[[Derived]] : $Class[[Base]]$Bracket[[<]]$TemplateParameter[[T]]$Bracket[[>]] {
using $Class[[Base]]$Bracket[[<]]$TemplateParameter[[T]]$Bracket[[>]]::$Field_dependentName[[waldo]];
void $Method_def[[foo]]() {
$Field_dependentName[[waldo]];
}
};
)cpp"};
for (const auto &TestCase : TestCases)
// Mask off scope modifiers to keep the tests manageable.
Expand Down
38 changes: 38 additions & 0 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,11 @@ New check aliases
Changes in existing checks
^^^^^^^^^^^^^^^^^^^^^^^^^^

- Improved :doc:`bugprone-suspicious-include
<clang-tidy/checks/bugprone/suspicious-include>` check by replacing the local
options `HeaderFileExtensions` and `ImplementationFileExtensions` by the
global options of the same name.

- Cleaned up :doc:`cppcoreguidelines-prefer-member-initializer
<clang-tidy/checks/cppcoreguidelines/prefer-member-initializer>`
by removing enforcement of rule `C.48
Expand All @@ -114,6 +119,32 @@ Changes in existing checks
by :doc:`cppcoreguidelines-use-default-member-init
<clang-tidy/checks/cppcoreguidelines/use-default-member-init>`.

- Improved :doc:`google-build-namespaces
<clang-tidy/checks/google/build-namespaces>` check by replacing the local
option `HeaderFileExtensions` by the global option of the same name.

- Improved :doc:`google-global-names-in-headers
<clang-tidy/checks/google/global-names-in-headers>` check by replacing the local
option `HeaderFileExtensions` by the global option of the same name.

- Improved :doc:`llvm-header-guard
<clang-tidy/checks/llvm/header-guard>` check by replacing the local
option `HeaderFileExtensions` by the global option of the same name.

- Improved :doc:`misc-definitions-in-headers
<clang-tidy/checks/misc/definitions-in-headers>` check by replacing the local
option `HeaderFileExtensions` by the global option of the same name.
Additionally, the option `UseHeaderFileExtensions` is removed, so that the
check uses the `HeaderFileExtensions` option unconditionally.

- Improved :doc:`misc-unused-using-decls
<clang-tidy/checks/misc/unused-using-decls>` check by replacing the local
option `HeaderFileExtensions` by the global option of the same name.

- Improved :doc:`misc-use-anonymous-namespace
<clang-tidy/checks/misc/use-anonymous-namespace>` check by replacing the local
option `HeaderFileExtensions` by the global option of the same name.

- Improved :doc:`modernize-avoid-c-arrays
<clang-tidy/checks/modernize/avoid-c-arrays>` check by introducing the new
`AllowStringArrays` option, enabling the exclusion of array types with deduced
Expand All @@ -122,9 +153,16 @@ Changes in existing checks
Removed checks
^^^^^^^^^^^^^^

Miscellaneous
^^^^^^^^^^^^^

- Removed `cert-dcl21-cpp`, which was deprecated since :program:`clang-tidy` 17,
since the rule DCL21-CPP has been removed from the CERT guidelines.

- Fixed incorrect formatting in ``clang-apply-repalcements`` when no ``--format``
option is specified. Now ``clang-apply-replacements`` applies formatting only with
the option.

Improvements to include-fixer
-----------------------------

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,27 +14,3 @@ Examples:
#include "Pterodactyl.h" // OK, .h files tend not to have definitions.
#include "Velociraptor.cpp" // Warning, filename is suspicious.
#include_next <stdio.c> // Warning, filename is suspicious.

Options
-------
.. option:: HeaderFileExtensions

Note: this option is deprecated, it will be removed in :program:`clang-tidy`
version 19. Please use the global configuration option
`HeaderFileExtensions`.

Default value: ``";h;hh;hpp;hxx"``
A semicolon-separated list of filename extensions of header files (the
filename extensions should not contain a "." prefix). For extension-less
header files, use an empty string or leave an empty string between ";"
if there are other filename extensions.

.. option:: ImplementationFileExtensions

Note: this option is deprecated, it will be removed in :program:`clang-tidy`
version 19. Please use the global configuration option
`ImplementationFileExtensions`.

Default value: ``"c;cc;cpp;cxx"``
Likewise, a semicolon-separated list of filename extensions of
implementation files.
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,3 @@ Finds anonymous namespaces in headers.
https://google.github.io/styleguide/cppguide.html#Namespaces

Corresponding cpplint.py check name: `build/namespaces`.

Options
-------

.. option:: HeaderFileExtensions

Note: this option is deprecated, it will be removed in :program:`clang-tidy`
version 19. Please use the global configuration option
`HeaderFileExtensions`.

A comma-separated list of filename extensions of header files (the filename
extensions should not include "." prefix). Default is "h,hh,hpp,hxx".
For header files without an extension, use an empty string (if there are no
other desired extensions) or leave an empty element in the list. E.g.,
"h,hh,hpp,hxx," (note the trailing comma).
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,3 @@ Flag global namespace pollution in header files. Right now it only triggers on

The relevant style guide section is
https://google.github.io/styleguide/cppguide.html#Namespaces.

Options
-------

.. option:: HeaderFileExtensions

Note: this option is deprecated, it will be removed in :program:`clang-tidy`
version 19. Please use the global configuration option
`HeaderFileExtensions`.

A comma-separated list of filename extensions of header files (the filename
extensions should not contain "." prefix). Default is "h".
For header files without an extension, use an empty string (if there are no
other desired extensions) or leave an empty element in the list. E.g.,
"h,hh,hpp,hxx," (note the trailing comma).
15 changes: 0 additions & 15 deletions clang-tools-extra/docs/clang-tidy/checks/llvm/header-guard.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,3 @@ llvm-header-guard
=================

Finds and fixes header guards that do not adhere to LLVM style.

Options
-------

.. option:: HeaderFileExtensions

Note: this option is deprecated, it will be removed in :program:`clang-tidy`
version 19. Please use the global configuration option
`HeaderFileExtensions`.

A comma-separated list of filename extensions of header files (the filename
extensions should not include "." prefix). Default is "h,hh,hpp,hxx".
For header files without an extension, use an empty string (if there are no
other desired extensions) or leave an empty element in the list. E.g.,
"h,hh,hpp,hxx," (note the trailing comma).
Original file line number Diff line number Diff line change
Expand Up @@ -92,27 +92,3 @@ When :program:`clang-tidy` is invoked with the `--fix-notes` option, this check
provides fixes that automatically add the ``inline`` keyword to discovered
functions. Please note that the addition of the ``inline`` keyword to variables
is not currently supported by this check.

Options
-------

.. option:: HeaderFileExtensions

Note: this option is deprecated, it will be removed in :program:`clang-tidy`
version 19. Please use the global configuration option
`HeaderFileExtensions`.

A comma-separated list of filename extensions of header files (the filename
extensions should not include "." prefix). Default is "h,hh,hpp,hxx".
For header files without an extension, use an empty string (if there are no
other desired extensions) or leave an empty element in the list. E.g.,
"h,hh,hpp,hxx," (note the trailing comma).

.. option:: UseHeaderFileExtension

Note: this option is deprecated, it will be removed in :program:`clang-tidy`
version 19. The check will unconditionally use the global option
`HeaderFileExtensions`.

When `true`, the check will use the file extension to distinguish header
files. Default is `true`.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Finds unused ``using`` declarations.

Unused ``using``` declarations in header files will not be diagnosed since these
using declarations are part of the header's public API. Allowed header file
extensions can be configured via the `HeaderFileExtensions` option (see below).
extensions can be configured via the global option `HeaderFileExtensions`.

Example:

Expand All @@ -16,17 +16,3 @@ Example:
// main.cpp
namespace n { class C; }
using n::C; // Never actually used.

Options
-------

.. option:: HeaderFileExtensions

Note: this option is deprecated, it will be removed in :program:`clang-tidy`
version 19. Please use the global configuration option
`HeaderFileExtensions`.

A semicolon-separated list of filename extensions of header files (the filename
extensions should not include "." prefix). Default is "h,hh,hpp,hxx".
For extension-less header files, use an empty string or leave an
empty string between "," if there are other filename extensions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ The following uses of ``static`` will *not* be diagnosed:

* Functions or variables in header files, since anonymous namespaces in headers
is considered an antipattern. Allowed header file extensions can be configured
via the `HeaderFileExtensions` option (see below).
via the global option `HeaderFileExtensions`.
* ``const`` or ``constexpr`` variables, since they already have implicit internal
linkage in C++.

Expand All @@ -33,18 +33,4 @@ Examples:
int x;
} // namespace

Options
-------

.. option:: HeaderFileExtensions

Note: this option is deprecated, it will be removed in :program:`clang-tidy`
version 19. Please use the global configuration option
`HeaderFileExtensions`.

A semicolon-separated list of filename extensions of header files (the filename
extensions should not include "." prefix). Default is ";h;hh;hpp;hxx".
For extension-less header files, using an empty string or leaving an
empty string between ";" if there are other filename extensions.

[1] `Undeprecating static <https://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1012>`_
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#include <string>
// CHECK: #include <string>
// CHECK-NEXT: #include <memory>
// CHECK-NEXT: #include "bar.h"
#include <memory>
#include "foo.h"
#include "bar.h"

void foo() {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
MainSourceFile: no.cpp
Diagnostics:
- DiagnosticName: test-header-format
DiagnosticMessage:
Message: Fix
FilePath: $(path)/no.cpp
FileOffset: 36
Replacements:
- FilePath: $(path)/no.cpp
Offset: 36
Length: 17
ReplacementText: ""
...
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#include <string>
// CHECK: #include "bar.h"
// CHECK-NEXT: #include <memory>
// CHECK-NEXT: #include <string>
#include <memory>
#include "foo.h"
#include "bar.h"

void foo() {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
MainSourceFile: yes.cpp
Diagnostics:
- DiagnosticName: test-header-format
DiagnosticMessage:
Message: Fix
FilePath: $(path)/yes.cpp
FileOffset: 36
Replacements:
- FilePath: $(path)/yes.cpp
Offset: 36
Length: 17
ReplacementText: ""
...
13 changes: 13 additions & 0 deletions clang-tools-extra/test/clang-apply-replacements/format-header.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// RUN: mkdir -p %T/Inputs/format_header_yes
// RUN: mkdir -p %T/Inputs/format_header_no
//
//
// RUN: grep -Ev "// *[A-Z-]+:" %S/Inputs/format_header/yes.cpp > %T/Inputs/format_header_yes/yes.cpp
// RUN: grep -Ev "// *[A-Z-]+:" %S/Inputs/format_header/no.cpp > %T/Inputs/format_header_no/no.cpp
// RUN: sed "s#\$(path)#%/T/Inputs/format_header_yes#" %S/Inputs/format_header/yes.yaml > %T/Inputs/format_header_yes/yes.yaml
// RUN: sed "s#\$(path)#%/T/Inputs/format_header_no#" %S/Inputs/format_header/no.yaml > %T/Inputs/format_header_no/no.yaml
// RUN: clang-apply-replacements -format -style="{BasedOnStyle: llvm, SortIncludes: CaseSensitive}" %T/Inputs/format_header_yes
// RUN: clang-apply-replacements %T/Inputs/format_header_no
// RUN: FileCheck --strict-whitespace -input-file=%T/Inputs/format_header_yes/yes.cpp %S/Inputs/format_header/yes.cpp
// RUN: FileCheck --strict-whitespace -input-file=%T/Inputs/format_header_no/no.cpp %S/Inputs/format_header/no.cpp
//
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
// RUN: %check_clang_tidy %s bugprone-implicit-widening-of-multiplication-result %t -- -- -target x86_64-unknown-unknown -x c++ -fsigned-char

// RUN: %check_clang_tidy -std=c99 %s bugprone-implicit-widening-of-multiplication-result %t -- -- -target x86_64-unknown-unknown -x c -funsigned-char
// RUN: %check_clang_tidy %s bugprone-implicit-widening-of-multiplication-result %t -- -- -target x86_64-unknown-unknown-x c++ -funsigned-char
// RUN: %check_clang_tidy %s bugprone-implicit-widening-of-multiplication-result %t -- -- -target x86_64 c++ -funsigned-char

long t0(char a, char b) {
return a * b;
Expand Down
30 changes: 28 additions & 2 deletions clang/docs/ClangFormatStyleOptions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1537,8 +1537,10 @@ the configuration (without a prefix: ``Auto``).
Possible values:

* ``RTBS_None`` (in configuration: ``None``)
Break after return type automatically.
``PenaltyReturnTypeOnItsOwnLine`` is taken into account.
This is **deprecated**. See ``Automatic`` below.

* ``RTBS_Automatic`` (in configuration: ``Automatic``)
Break after return type based on ``PenaltyReturnTypeOnItsOwnLine``.

.. code-block:: c++

Expand All @@ -1547,6 +1549,22 @@ the configuration (without a prefix: ``Auto``).
};
int f();
int f() { return 1; }
int
LongName::AnotherLongName();

* ``RTBS_ExceptShortType`` (in configuration: ``ExceptShortType``)
Same as ``Automatic`` above, except that there is no break after short
return types.

.. code-block:: c++

class A {
int f() { return 0; };
};
int f();
int f() { return 1; }
int LongName::
AnotherLongName();

* ``RTBS_All`` (in configuration: ``All``)
Always break after the return type.
Expand All @@ -1565,6 +1583,8 @@ the configuration (without a prefix: ``Auto``).
f() {
return 1;
}
int
LongName::AnotherLongName();

* ``RTBS_TopLevel`` (in configuration: ``TopLevel``)
Always break after the return types of top-level functions.
Expand All @@ -1580,6 +1600,8 @@ the configuration (without a prefix: ``Auto``).
f() {
return 1;
}
int
LongName::AnotherLongName();

* ``RTBS_AllDefinitions`` (in configuration: ``AllDefinitions``)
Always break after the return type of function definitions.
Expand All @@ -1597,6 +1619,8 @@ the configuration (without a prefix: ``Auto``).
f() {
return 1;
}
int
LongName::AnotherLongName();

* ``RTBS_TopLevelDefinitions`` (in configuration: ``TopLevelDefinitions``)
Always break after the return type of top-level definitions.
Expand All @@ -1611,6 +1635,8 @@ the configuration (without a prefix: ``Auto``).
f() {
return 1;
}
int
LongName::AnotherLongName();



Expand Down
9 changes: 9 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -190,8 +190,17 @@ Bug Fixes to C++ Support
- Fix for crash when using a erroneous type in a return statement.
Fixes (`#63244 <https://github.com/llvm/llvm-project/issues/63244>`_)
and (`#79745 <https://github.com/llvm/llvm-project/issues/79745>`_)
- Fixed an out-of-bounds error caused by building a recovery expression for ill-formed
function calls while substituting into constraints.
(`#58548 <https://github.com/llvm/llvm-project/issues/58548>`_)
- Fix incorrect code generation caused by the object argument of ``static operator()`` and ``static operator[]`` calls not being evaluated.
Fixes (`#67976 <https://github.com/llvm/llvm-project/issues/67976>`_)
- Fix crash and diagnostic with const qualified member operator new.
Fixes (`#79748 <https://github.com/llvm/llvm-project/issues/79748>`_)
- Fix a crash when specializing an out-of-line member function with a default
parameter where we did an incorrect specialization of the initialization of
the default parameter.
Fixes (`#68490 <https://github.com/llvm/llvm-project/issues/68490>`_)

Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/Basic/BuiltinsAMDGPU.def
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ BUILTIN(__builtin_amdgcn_mqsad_u32_u8, "V4UiWUiUiV4Ui", "nc")
//===----------------------------------------------------------------------===//

TARGET_BUILTIN(__builtin_amdgcn_ballot_w32, "ZUib", "nc", "wavefrontsize32")
TARGET_BUILTIN(__builtin_amdgcn_ballot_w64, "WUib", "nc", "wavefrontsize64")
BUILTIN(__builtin_amdgcn_ballot_w64, "WUib", "nc")

// Deprecated intrinsics in favor of __builtin_amdgn_ballot_{w32|w64}
BUILTIN(__builtin_amdgcn_uicmp, "WUiUiUiIi", "nc")
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Basic/DiagnosticDriverKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ def err_drv_hipspv_no_hip_path : Error<
"'--hip-path' must be specified when offloading to "
"SPIR-V%select{| unless %1 is given}0.">;

// TODO: Remove when COV6 is fully supported by ROCm.
def warn_drv_amdgpu_cov6: Warning<
"code object v6 is still in development and not ready for production use yet;"
" use at your own risk">;
def err_drv_undetermined_gpu_arch : Error<
"cannot determine %0 architecture: %1; consider passing it via "
"'%2'">;
Expand Down
31 changes: 31 additions & 0 deletions clang/include/clang/Basic/riscv_vector.td
Original file line number Diff line number Diff line change
Expand Up @@ -1730,12 +1730,28 @@ let ManualCodegen = [{
defm vfwnmacc : RVVFloatingWidenTerBuiltinSetRoundingMode;
defm vfwmsac : RVVFloatingWidenTerBuiltinSetRoundingMode;
defm vfwnmsac : RVVFloatingWidenTerBuiltinSetRoundingMode;

// Vector BF16 widening multiply-accumulate
let Log2LMUL = [-2, -1, 0, 1, 2],
RequiredFeatures = ["Zvfbfwma"],
HasMaskedOffOperand = false in
defm vfwmaccbf16 : RVVOutOp1Op2BuiltinSet<"vfwmaccbf16", "y",
[["vv", "Fw", "FwFwvvu"],
["vf", "Fw", "FwFwevu"]]>;
}
// 13.7. Vector Widening Floating-Point Fused Multiply-Add Instructions
defm vfwmacc : RVVFloatingWidenTerBuiltinSet;
defm vfwnmacc : RVVFloatingWidenTerBuiltinSet;
defm vfwmsac : RVVFloatingWidenTerBuiltinSet;
defm vfwnmsac : RVVFloatingWidenTerBuiltinSet;

// Vector BF16 widening multiply-accumulate
let Log2LMUL = [-2, -1, 0, 1, 2],
RequiredFeatures = ["Zvfbfwma"],
HasMaskedOffOperand = false in
defm vfwmaccbf16 : RVVOutOp1Op2BuiltinSet<"vfwmaccbf16", "y",
[["vv", "Fw", "FwFwvv"],
["vf", "Fw", "FwFwev"]]>;
}

}
Expand Down Expand Up @@ -1883,6 +1899,11 @@ let Log2LMUL = [-3, -2, -1, 0, 1, 2] in {
def vfncvt_rtz_x_f_w : RVVConvToNarrowingSignedBuiltin<"vfncvt_rtz_x">;
def vfncvt_rod_f_f_w : RVVConvBuiltin<"v", "vw", "xf", "vfncvt_rod_f">;
}

// Zvfbfmin - Vector convert BF16 to FP32
let Log2LMUL = [-2, -1, 0, 1, 2] in
def vfwcvtbf16_f_f_v : RVVConvBuiltin<"Fw", "Fwv", "y", "vfwcvtbf16_f">;

let ManualCodegen = [{
{
// LLVM intrinsic
Expand Down Expand Up @@ -1970,6 +1991,11 @@ let ManualCodegen = [{
defm : RVVConvBuiltinSet<"vfncvt_f_f_w", "x", [["v", "vwu"]]>;
}
}

// Zvfbfmin - Vector convert FP32 to BF16
let Log2LMUL = [-2, -1, 0, 1, 2],
OverloadedName = "vfncvtbf16_f" in
defm : RVVConvBuiltinSet<"vfncvtbf16_f_f_w", "y", [["v", "vFwu"]]>;
}

// 13.17. Single-Width Floating-Point/Integer Type-Convert Instructions
Expand Down Expand Up @@ -2015,6 +2041,11 @@ let ManualCodegen = [{
defm : RVVConvBuiltinSet<"vfncvt_f_f_w", "x", [["v", "vw"]]>;
}
}

// Zvfbfmin - Vector convert FP32 to BF16
let Log2LMUL = [-2, -1, 0, 1, 2],
OverloadedName = "vfncvtbf16_f" in
defm : RVVConvBuiltinSet<"vfncvtbf16_f_f_w", "y", [["v", "vFw"]]>;
}
}

Expand Down
8 changes: 4 additions & 4 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -4801,9 +4801,9 @@ defm amdgpu_ieee : BoolOption<"m", "amdgpu-ieee",
def mcode_object_version_EQ : Joined<["-"], "mcode-object-version=">, Group<m_Group>,
HelpText<"Specify code object ABI version. Defaults to 5. (AMDGPU only)">,
Visibility<[ClangOption, FlangOption, CC1Option, FC1Option]>,
Values<"none,4,5">,
Values<"none,4,5,6">,
NormalizedValuesScope<"llvm::CodeObjectVersionKind">,
NormalizedValues<["COV_None", "COV_4", "COV_5"]>,
NormalizedValues<["COV_None", "COV_4", "COV_5", "COV_6"]>,
MarshallingInfoEnum<TargetOpts<"CodeObjectVersion">, "COV_5">;

defm cumode : SimpleMFlag<"cumode",
Expand Down Expand Up @@ -4994,10 +4994,10 @@ def mno_fmv : Flag<["-"], "mno-fmv">, Group<f_clang_Group>,
Visibility<[ClangOption, CC1Option]>,
HelpText<"Disable function multiversioning">;
def moutline_atomics : Flag<["-"], "moutline-atomics">, Group<f_clang_Group>,
Visibility<[ClangOption, CC1Option]>,
Visibility<[ClangOption, CC1Option, FlangOption]>,
HelpText<"Generate local calls to out-of-line atomic operations">;
def mno_outline_atomics : Flag<["-"], "mno-outline-atomics">, Group<f_clang_Group>,
Visibility<[ClangOption, CC1Option]>,
Visibility<[ClangOption, CC1Option, FlangOption]>,
HelpText<"Don't generate local calls to out-of-line atomic operations">;
def mno_implicit_float : Flag<["-"], "mno-implicit-float">, Group<m_Group>,
HelpText<"Don't generate implicit floating point or vector instructions">;
Expand Down
29 changes: 26 additions & 3 deletions clang/include/clang/Format/Format.h
Original file line number Diff line number Diff line change
Expand Up @@ -914,16 +914,31 @@ struct FormatStyle {
/// Different ways to break after the function definition or
/// declaration return type.
enum ReturnTypeBreakingStyle : int8_t {
/// Break after return type automatically.
/// ``PenaltyReturnTypeOnItsOwnLine`` is taken into account.
/// This is **deprecated**. See ``Automatic`` below.
RTBS_None,
/// Break after return type based on ``PenaltyReturnTypeOnItsOwnLine``.
/// \code
/// class A {
/// int f() { return 0; };
/// };
/// int f();
/// int f() { return 1; }
/// int
/// LongName::AnotherLongName();
/// \endcode
RTBS_None,
RTBS_Automatic,
/// Same as ``Automatic`` above, except that there is no break after short
/// return types.
/// \code
/// class A {
/// int f() { return 0; };
/// };
/// int f();
/// int f() { return 1; }
/// int LongName::
/// AnotherLongName();
/// \endcode
RTBS_ExceptShortType,
/// Always break after the return type.
/// \code
/// class A {
Expand All @@ -938,6 +953,8 @@ struct FormatStyle {
/// f() {
/// return 1;
/// }
/// int
/// LongName::AnotherLongName();
/// \endcode
RTBS_All,
/// Always break after the return types of top-level functions.
Expand All @@ -951,6 +968,8 @@ struct FormatStyle {
/// f() {
/// return 1;
/// }
/// int
/// LongName::AnotherLongName();
/// \endcode
RTBS_TopLevel,
/// Always break after the return type of function definitions.
Expand All @@ -966,6 +985,8 @@ struct FormatStyle {
/// f() {
/// return 1;
/// }
/// int
/// LongName::AnotherLongName();
/// \endcode
RTBS_AllDefinitions,
/// Always break after the return type of top-level definitions.
Expand All @@ -978,6 +999,8 @@ struct FormatStyle {
/// f() {
/// return 1;
/// }
/// int
/// LongName::AnotherLongName();
/// \endcode
RTBS_TopLevelDefinitions,
};
Expand Down
3 changes: 2 additions & 1 deletion clang/include/clang/Support/RISCVVIntrinsicUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -501,7 +501,8 @@ enum RVVRequire : uint32_t {
RVV_REQ_Zvknhb = 1 << 13,
RVV_REQ_Zvksed = 1 << 14,
RVV_REQ_Zvksh = 1 << 15,
RVV_REQ_Experimental = 1 << 16,
RVV_REQ_Zvfbfwma = 1 << 16,
RVV_REQ_Experimental = 1 << 17,

LLVM_MARK_AS_BITMASK_ENUM(RVV_REQ_Experimental)
};
Expand Down
48 changes: 44 additions & 4 deletions clang/lib/AST/Interp/ByteCodeExprGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,14 @@ bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) {
case CK_NonAtomicToAtomic:
case CK_NoOp:
case CK_UserDefinedConversion:
return this->delegate(SubExpr);

case CK_BitCast:
if (CE->getType()->isAtomicType()) {
if (!this->discard(SubExpr))
return false;
return this->emitInvalidCast(CastKind::Reinterpret, CE);
}
return this->delegate(SubExpr);

case CK_IntegralToBoolean:
Expand Down Expand Up @@ -1122,7 +1129,7 @@ bool ByteCodeExprGen<Emitter>::VisitMemberExpr(const MemberExpr *E) {
if (DiscardResult)
return this->discard(Base);

if (!this->visit(Base))
if (!this->delegate(Base))
return false;

// Base above gives us a pointer on the stack.
Expand Down Expand Up @@ -1610,8 +1617,10 @@ bool ByteCodeExprGen<Emitter>::VisitMaterializeTemporaryExpr(
return this->emitGetPtrLocal(*LocalIndex, E);
}
} else {
const Expr *Inner = E->getSubExpr()->skipRValueSubobjectAdjustments();

if (std::optional<unsigned> LocalIndex =
allocateLocal(SubExpr, /*IsExtended=*/true)) {
allocateLocal(Inner, /*IsExtended=*/true)) {
if (!this->emitGetPtrLocal(*LocalIndex, E))
return false;
return this->visitInitializer(SubExpr);
Expand Down Expand Up @@ -1932,10 +1941,32 @@ bool ByteCodeExprGen<Emitter>::VisitCXXScalarValueInitExpr(
const CXXScalarValueInitExpr *E) {
QualType Ty = E->getType();

if (Ty->isVoidType())
if (DiscardResult || Ty->isVoidType())
return true;

return this->visitZeroInitializer(classifyPrim(Ty), Ty, E);
if (std::optional<PrimType> T = classify(Ty))
return this->visitZeroInitializer(*T, Ty, E);

assert(Ty->isAnyComplexType());
if (!Initializing) {
std::optional<unsigned> LocalIndex = allocateLocal(E, /*IsExtended=*/false);
if (!LocalIndex)
return false;
if (!this->emitGetPtrLocal(*LocalIndex, E))
return false;
}

// Initialize both fields to 0.
QualType ElemQT = Ty->getAs<ComplexType>()->getElementType();
PrimType ElemT = classifyPrim(ElemQT);

for (unsigned I = 0; I != 2; ++I) {
if (!this->visitZeroInitializer(ElemT, ElemQT, E))
return false;
if (!this->emitInitElem(ElemT, I, E))
return false;
}
return true;
}

template <class Emitter>
Expand All @@ -1954,6 +1985,15 @@ bool ByteCodeExprGen<Emitter>::VisitChooseExpr(const ChooseExpr *E) {
return this->delegate(E->getChosenSubExpr());
}

template <class Emitter>
bool ByteCodeExprGen<Emitter>::VisitObjCBoolLiteralExpr(
const ObjCBoolLiteralExpr *E) {
if (DiscardResult)
return true;

return this->emitConst(E->getValue(), E);
}

template <class Emitter> bool ByteCodeExprGen<Emitter>::discard(const Expr *E) {
if (E->containsErrors())
return false;
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/Interp/ByteCodeExprGen.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
bool VisitSizeOfPackExpr(const SizeOfPackExpr *E);
bool VisitGenericSelectionExpr(const GenericSelectionExpr *E);
bool VisitChooseExpr(const ChooseExpr *E);
bool VisitObjCBoolLiteralExpr(const ObjCBoolLiteralExpr *E);

protected:
bool visitExpr(const Expr *E) override;
Expand Down
2 changes: 0 additions & 2 deletions clang/lib/AST/Interp/EvaluationResult.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,6 @@ bool EvaluationResult::checkFullyInitialized(InterpState &S) const {
const auto *CAT =
cast<ConstantArrayType>(Ptr.getType()->getAsArrayTypeUnsafe());
return CheckArrayInitialized(S, InitLoc, Ptr, CAT);

return true;
}

void EvaluationResult::dump() const {
Expand Down
7 changes: 5 additions & 2 deletions clang/lib/AST/Interp/Interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -2020,8 +2020,11 @@ inline bool Invalid(InterpState &S, CodePtr OpPC) {
/// Same here, but only for casts.
inline bool InvalidCast(InterpState &S, CodePtr OpPC, CastKind Kind) {
const SourceLocation &Loc = S.Current->getLocation(OpPC);
S.FFDiag(Loc, diag::note_constexpr_invalid_cast)
<< static_cast<unsigned>(Kind) << S.Current->getRange(OpPC);

// FIXME: Support diagnosing other invalid cast kinds.
if (Kind == CastKind::Reinterpret)
S.FFDiag(Loc, diag::note_constexpr_invalid_cast)
<< static_cast<unsigned>(Kind) << S.Current->getRange(OpPC);
return false;
}

Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/Interp/InterpBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,7 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
S.Stk.push<Boolean>(Boolean::from(S.inConstantContext()));
break;
case Builtin::BI__builtin_assume:
case Builtin::BI__assume:
break;
case Builtin::BI__builtin_strcmp:
if (!interp__builtin_strcmp(S, OpPC, Frame))
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/AST/Interp/PrimType.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,17 @@ enum PrimType : unsigned {

enum class CastKind : uint8_t {
Reinterpret,
Atomic,
};
inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
interp::CastKind CK) {
switch (CK) {
case interp::CastKind::Reinterpret:
OS << "reinterpret_cast";
break;
case interp::CastKind::Atomic:
OS << "atomic";
break;
}
return OS;
}
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3854,6 +3854,12 @@ PackIndexingType::computeDependence(QualType Pattern, Expr *IndexExpr,

if (!(IndexD & TypeDependence::UnexpandedPack))
TD &= ~TypeDependence::UnexpandedPack;

// If the pattern does not contain an unexpended pack,
// the type is still dependent, and invalid
if (!Pattern->containsUnexpandedParameterPack())
TD |= TypeDependence::Error | TypeDependence::DependentInstantiation;

return TD;
}

Expand Down
6 changes: 3 additions & 3 deletions clang/lib/AST/TypePrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1195,10 +1195,10 @@ void TypePrinter::printDecltypeBefore(const DecltypeType *T, raw_ostream &OS) {

void TypePrinter::printPackIndexingBefore(const PackIndexingType *T,
raw_ostream &OS) {
if (T->isInstantiationDependentType())
OS << T->getPattern() << "...[" << T->getIndexExpr() << "]";
else
if (T->hasSelectedType())
OS << T->getSelectedType();
else
OS << T->getPattern() << "...[" << T->getIndexExpr() << "]";
spaceBeforePlaceHolder(OS);
}

Expand Down
51 changes: 47 additions & 4 deletions clang/lib/Analysis/ReachableCode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "clang/AST/ExprCXX.h"
#include "clang/AST/ExprObjC.h"
#include "clang/AST/ParentMap.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/StmtCXX.h"
#include "clang/Analysis/AnalysisDeclContext.h"
#include "clang/Analysis/CFG.h"
Expand Down Expand Up @@ -453,26 +454,68 @@ bool DeadCodeScan::isDeadCodeRoot(const clang::CFGBlock *Block) {
return isDeadRoot;
}

static bool isValidDeadStmt(const Stmt *S) {
// Check if the given `DeadStmt` is a coroutine statement and is a substmt of
// the coroutine statement. `Block` is the CFGBlock containing the `DeadStmt`.
static bool isInCoroutineStmt(const Stmt *DeadStmt, const CFGBlock *Block) {
// The coroutine statement, co_return, co_await, or co_yield.
const Stmt *CoroStmt = nullptr;
// Find the first coroutine statement after the DeadStmt in the block.
bool AfterDeadStmt = false;
for (CFGBlock::const_iterator I = Block->begin(), E = Block->end(); I != E;
++I)
if (std::optional<CFGStmt> CS = I->getAs<CFGStmt>()) {
const Stmt *S = CS->getStmt();
if (S == DeadStmt)
AfterDeadStmt = true;
if (AfterDeadStmt &&
// For simplicity, we only check simple coroutine statements.
(llvm::isa<CoreturnStmt>(S) || llvm::isa<CoroutineSuspendExpr>(S))) {
CoroStmt = S;
break;
}
}
if (!CoroStmt)
return false;
struct Checker : RecursiveASTVisitor<Checker> {
const Stmt *DeadStmt;
bool CoroutineSubStmt = false;
Checker(const Stmt *S) : DeadStmt(S) {}
bool VisitStmt(const Stmt *S) {
if (S == DeadStmt)
CoroutineSubStmt = true;
return true;
}
// Statements captured in the CFG can be implicit.
bool shouldVisitImplicitCode() const { return true; }
};
Checker checker(DeadStmt);
checker.TraverseStmt(const_cast<Stmt *>(CoroStmt));
return checker.CoroutineSubStmt;
}

static bool isValidDeadStmt(const Stmt *S, const clang::CFGBlock *Block) {
if (S->getBeginLoc().isInvalid())
return false;
if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(S))
return BO->getOpcode() != BO_Comma;
return true;
// Coroutine statements are never considered dead statements, because removing
// them may change the function semantic if it is the only coroutine statement
// of the coroutine.
return !isInCoroutineStmt(S, Block);
}

const Stmt *DeadCodeScan::findDeadCode(const clang::CFGBlock *Block) {
for (CFGBlock::const_iterator I = Block->begin(), E = Block->end(); I!=E; ++I)
if (std::optional<CFGStmt> CS = I->getAs<CFGStmt>()) {
const Stmt *S = CS->getStmt();
if (isValidDeadStmt(S))
if (isValidDeadStmt(S, Block))
return S;
}

CFGTerminator T = Block->getTerminator();
if (T.isStmtBranch()) {
const Stmt *S = T.getStmt();
if (S && isValidDeadStmt(S))
if (S && isValidDeadStmt(S, Block))
return S;
}

Expand Down
3 changes: 1 addition & 2 deletions clang/lib/Basic/Sarif.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,7 @@ static std::string percentEncodeURICharacter(char C) {
// should be written out directly. Otherwise, percent
// encode the character and write that out instead of the
// reserved character.
if (llvm::isAlnum(C) ||
StringRef::npos != StringRef("-._~:@!$&'()*+,;=").find(C))
if (llvm::isAlnum(C) || StringRef("-._~:@!$&'()*+,;=").contains(C))
return std::string(&C, 1);
return "%" + llvm::toHex(StringRef(&C, 1));
}
Expand Down
8 changes: 2 additions & 6 deletions clang/lib/Basic/Targets/AMDGPU.h
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,7 @@ class LLVM_LIBRARY_VISIBILITY AMDGPUTargetInfo final : public TargetInfo {
return true;
}

bool HasLeftParen = false;
if (S.consume_front("{"))
HasLeftParen = true;
bool HasLeftParen = S.consume_front("{");
if (S.empty())
return false;
if (S.front() != 'v' && S.front() != 's' && S.front() != 'a') {
Expand All @@ -196,9 +194,7 @@ class LLVM_LIBRARY_VISIBILITY AMDGPUTargetInfo final : public TargetInfo {
Name = S.data() - 1;
return true;
}
bool HasLeftBracket = false;
if (S.consume_front("["))
HasLeftBracket = true;
bool HasLeftBracket = S.consume_front("[");
unsigned long long N;
if (S.empty() || consumeUnsignedInteger(S, 10, N))
return false;
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Basic/Targets/X86.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ bool X86TargetInfo::initFeatureMap(
if (Feature.substr(1, 6) == "avx10.") {
if (Feature[0] == '+') {
HasAVX10 = true;
if (Feature.substr(Feature.size() - 3, 3) == "512")
if (StringRef(Feature).ends_with("512"))
HasAVX10_512 = true;
LastAVX10 = Feature;
} else if (HasAVX10 && Feature == "-avx10.1-256") {
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Basic/Targets/X86.h
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,7 @@ class LLVM_LIBRARY_VISIBILITY MCUX86_32TargetInfo : public X86_32TargetInfo {
MCUX86_32TargetInfo(const llvm::Triple &Triple, const TargetOptions &Opts)
: X86_32TargetInfo(Triple, Opts) {
LongDoubleWidth = 64;
DefaultAlignForAttributeAligned = 32;
LongDoubleFormat = &llvm::APFloat::IEEEdouble();
resetDataLayout("e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i64:32-"
"f64:32-f128:32-n8:16:32-a:0:32-S32");
Expand Down
7 changes: 4 additions & 3 deletions clang/lib/CodeGen/CGBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5710,6 +5710,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
Value *HalfVal = Builder.CreateLoad(Address);
return RValue::get(Builder.CreateFPExt(HalfVal, Builder.getFloatTy()));
}
case Builtin::BI__builtin_printf:
case Builtin::BIprintf:
if (getTarget().getTriple().isNVPTX() ||
getTarget().getTriple().isAMDGCN()) {
Expand Down Expand Up @@ -17756,9 +17757,9 @@ Value *EmitAMDGPUImplicitArgPtr(CodeGenFunction &CGF) {
// \p Index is 0, 1, and 2 for x, y, and z dimension, respectively.
/// Emit code based on Code Object ABI version.
/// COV_4 : Emit code to use dispatch ptr
/// COV_5 : Emit code to use implicitarg ptr
/// COV_5+ : Emit code to use implicitarg ptr
/// COV_NONE : Emit code to load a global variable "__oclc_ABI_version"
/// and use its value for COV_4 or COV_5 approach. It is used for
/// and use its value for COV_4 or COV_5+ approach. It is used for
/// compiling device libraries in an ABI-agnostic way.
///
/// Note: "__oclc_ABI_version" is supposed to be emitted and intialized by
Expand Down Expand Up @@ -17801,7 +17802,7 @@ Value *EmitAMDGPUWorkGroupSize(CodeGenFunction &CGF, unsigned Index) {
Address(Result, CGF.Int16Ty, CharUnits::fromQuantity(2)));
} else {
Value *GEP = nullptr;
if (Cov == CodeObjectVersionKind::COV_5) {
if (Cov >= CodeObjectVersionKind::COV_5) {
// Indexing the implicit kernarg segment.
GEP = CGF.Builder.CreateConstGEP1_32(
CGF.Int8Ty, EmitAMDGPUImplicitArgPtr(CGF), 12 + Index * 2);
Expand Down
40 changes: 37 additions & 3 deletions clang/lib/CodeGen/CGCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3216,6 +3216,25 @@ void CodeGenFunction::EmitFunctionProlog(const CGFunctionInfo &FI,
}
}

llvm::StructType *STy =
dyn_cast<llvm::StructType>(ArgI.getCoerceToType());
llvm::TypeSize StructSize;
llvm::TypeSize PtrElementSize;
if (ArgI.isDirect() && !ArgI.getCanBeFlattened() && STy &&
STy->getNumElements() > 1) {
StructSize = CGM.getDataLayout().getTypeAllocSize(STy);
PtrElementSize =
CGM.getDataLayout().getTypeAllocSize(ConvertTypeForMem(Ty));
if (STy->containsHomogeneousScalableVectorTypes()) {
assert(StructSize == PtrElementSize &&
"Only allow non-fractional movement of structure with"
"homogeneous scalable vector type");

ArgVals.push_back(ParamValue::forDirect(AI));
break;
}
}

Address Alloca = CreateMemTemp(Ty, getContext().getDeclAlign(Arg),
Arg->getName());

Expand All @@ -3224,7 +3243,6 @@ void CodeGenFunction::EmitFunctionProlog(const CGFunctionInfo &FI,

// Fast-isel and the optimizer generally like scalar values better than
// FCAs, so we flatten them if this is safe to do for this argument.
llvm::StructType *STy = dyn_cast<llvm::StructType>(ArgI.getCoerceToType());
if (ArgI.isDirect() && ArgI.getCanBeFlattened() && STy &&
STy->getNumElements() > 1) {
llvm::TypeSize StructSize = CGM.getDataLayout().getTypeAllocSize(STy);
Expand Down Expand Up @@ -5287,6 +5305,24 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
break;
}

llvm::StructType *STy =
dyn_cast<llvm::StructType>(ArgInfo.getCoerceToType());
llvm::Type *SrcTy = ConvertTypeForMem(I->Ty);
llvm::TypeSize SrcTypeSize;
llvm::TypeSize DstTypeSize;
if (STy && ArgInfo.isDirect() && !ArgInfo.getCanBeFlattened()) {
SrcTypeSize = CGM.getDataLayout().getTypeAllocSize(SrcTy);
DstTypeSize = CGM.getDataLayout().getTypeAllocSize(STy);
if (STy->containsHomogeneousScalableVectorTypes()) {
assert(SrcTypeSize == DstTypeSize &&
"Only allow non-fractional movement of structure with "
"homogeneous scalable vector type");

IRCallArgs[FirstIRArg] = I->getKnownRValue().getScalarVal();
break;
}
}

// FIXME: Avoid the conversion through memory if possible.
Address Src = Address::invalid();
if (!I->isAggregate()) {
Expand All @@ -5302,8 +5338,6 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,

// Fast-isel and the optimizer generally like scalar values better than
// FCAs, so we flatten them if this is safe to do for this argument.
llvm::StructType *STy =
dyn_cast<llvm::StructType>(ArgInfo.getCoerceToType());
if (STy && ArgInfo.isDirect() && ArgInfo.getCanBeFlattened()) {
llvm::Type *SrcTy = Src.getElementType();
llvm::TypeSize SrcTypeSize =
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/CodeGen/CGGPUBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,8 @@ RValue EmitDevicePrintfCallExpr(const CallExpr *E, CodeGenFunction *CGF,
llvm::Function *Decl, bool WithSizeArg) {
CodeGenModule &CGM = CGF->CGM;
CGBuilderTy &Builder = CGF->Builder;
assert(E->getBuiltinCallee() == Builtin::BIprintf);
assert(E->getBuiltinCallee() == Builtin::BIprintf ||
E->getBuiltinCallee() == Builtin::BI__builtin_printf);
assert(E->getNumArgs() >= 1); // printf always has at least one arg.

// Uses the same format as nvptx for the argument packing, but also passes
Expand Down
14 changes: 4 additions & 10 deletions clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1044,17 +1044,14 @@ void CodeGenModule::Release() {
llvm::MDString::get(VMContext, "ascii"));
}

llvm::Triple::ArchType Arch = Context.getTargetInfo().getTriple().getArch();
if ( Arch == llvm::Triple::arm
|| Arch == llvm::Triple::armeb
|| Arch == llvm::Triple::thumb
|| Arch == llvm::Triple::thumbeb) {
llvm::Triple T = Context.getTargetInfo().getTriple();
if (T.isARM() || T.isThumb()) {
// The minimum width of an enum in bytes
uint64_t EnumWidth = Context.getLangOpts().ShortEnums ? 1 : 4;
getModule().addModuleFlag(llvm::Module::Error, "min_enum_size", EnumWidth);
}

if (Arch == llvm::Triple::riscv32 || Arch == llvm::Triple::riscv64) {
if (T.isRISCV()) {
StringRef ABIStr = Target.getABI();
llvm::LLVMContext &Ctx = TheModule.getContext();
getModule().addModuleFlag(llvm::Module::Error, "target-abi",
Expand Down Expand Up @@ -1127,10 +1124,7 @@ void CodeGenModule::Release() {
getModule().addModuleFlag(llvm::Module::Override,
"tag-stack-memory-buildattr", 1);

if (Arch == llvm::Triple::thumb || Arch == llvm::Triple::thumbeb ||
Arch == llvm::Triple::arm || Arch == llvm::Triple::armeb ||
Arch == llvm::Triple::aarch64 || Arch == llvm::Triple::aarch64_32 ||
Arch == llvm::Triple::aarch64_be) {
if (T.isARM() || T.isThumb() || T.isAArch64()) {
if (LangOpts.BranchTargetEnforcement)
getModule().addModuleFlag(llvm::Module::Min, "branch-target-enforcement",
1);
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/CodeGen/ItaniumCXXABI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1347,9 +1347,10 @@ static llvm::FunctionCallee getItaniumDynamicCastFn(CodeGenFunction &CGF) {

llvm::FunctionType *FTy = llvm::FunctionType::get(Int8PtrTy, Args, false);

// Mark the function as nounwind readonly.
// Mark the function as nounwind willreturn readonly.
llvm::AttrBuilder FuncAttrs(CGF.getLLVMContext());
FuncAttrs.addAttribute(llvm::Attribute::NoUnwind);
FuncAttrs.addAttribute(llvm::Attribute::WillReturn);
FuncAttrs.addMemoryAttr(llvm::MemoryEffects::readOnly());
llvm::AttributeList Attrs = llvm::AttributeList::get(
CGF.getLLVMContext(), llvm::AttributeList::FunctionIndex, FuncAttrs);
Expand Down
8 changes: 7 additions & 1 deletion clang/lib/CodeGen/Targets/RISCV.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,13 @@ ABIArgInfo RISCVABIInfo::classifyArgumentType(QualType Ty, bool IsFixed,
return getNaturalAlignIndirect(Ty, /*ByVal=*/false);
}

return ABIArgInfo::getDirect();
ABIArgInfo Info = ABIArgInfo::getDirect();

// If it is tuple type, it can't be flattened.
if (llvm::StructType *STy = dyn_cast<llvm::StructType>(CGT.ConvertType(Ty)))
Info.setCanBeFlattened(!STy->containsHomogeneousScalableVectorTypes());

return Info;
}

if (const VectorType *VT = Ty->getAs<VectorType>())
Expand Down
19 changes: 10 additions & 9 deletions clang/lib/Driver/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1443,15 +1443,16 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) {
const ToolChain &TC = getToolChain(
*UArgs, computeTargetTriple(*this, TargetTriple, *UArgs));

if (TC.getTriple().isAndroid()) {
llvm::Triple Triple = TC.getTriple();
StringRef TripleVersionName = Triple.getEnvironmentVersionString();

if (Triple.getEnvironmentVersion().empty() && TripleVersionName != "") {
Diags.Report(diag::err_drv_triple_version_invalid)
<< TripleVersionName << TC.getTripleString();
ContainsError = true;
}
// Check if the environment version is valid.
llvm::Triple Triple = TC.getTriple();
StringRef TripleVersionName = Triple.getEnvironmentVersionString();
StringRef TripleObjectFormat =
Triple.getObjectFormatTypeName(Triple.getObjectFormat());
if (Triple.getEnvironmentVersion().empty() && TripleVersionName != "" &&
TripleVersionName != TripleObjectFormat) {
Diags.Report(diag::err_drv_triple_version_invalid)
<< TripleVersionName << TC.getTripleString();
ContainsError = true;
}

// Report warning when arm64EC option is overridden by specified target
Expand Down
29 changes: 9 additions & 20 deletions clang/lib/Driver/ToolChains/Clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5781,6 +5781,14 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
// NVPTX/AMDGPU does not care about the code model and will accept
// whatever works for the host.
Ok = true;
} else if (Triple.isSPARC64()) {
if (CM == "medlow")
CM = "small";
else if (CM == "medmid")
CM = "medium";
else if (CM == "medany")
CM = "large";
Ok = CM == "small" || CM == "medium" || CM == "large";
}
if (Ok) {
CmdArgs.push_back(Args.MakeArgString("-mcmodel=" + CM));
Expand Down Expand Up @@ -7665,26 +7673,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,

addMachineOutlinerArgs(D, Args, CmdArgs, Triple, /*IsLTO=*/false);

if (Arg *A = Args.getLastArg(options::OPT_moutline_atomics,
options::OPT_mno_outline_atomics)) {
// Option -moutline-atomics supported for AArch64 target only.
if (!Triple.isAArch64()) {
D.Diag(diag::warn_drv_moutline_atomics_unsupported_opt)
<< Triple.getArchName() << A->getOption().getName();
} else {
if (A->getOption().matches(options::OPT_moutline_atomics)) {
CmdArgs.push_back("-target-feature");
CmdArgs.push_back("+outline-atomics");
} else {
CmdArgs.push_back("-target-feature");
CmdArgs.push_back("-outline-atomics");
}
}
} else if (Triple.isAArch64() &&
getToolChain().IsAArch64OutlineAtomicsDefault(Args)) {
CmdArgs.push_back("-target-feature");
CmdArgs.push_back("+outline-atomics");
}
addOutlineAtomicsArgs(D, getToolChain(), Args, CmdArgs, Triple);

if (Triple.isAArch64() &&
(Args.hasArg(options::OPT_mno_fmv) ||
Expand Down
33 changes: 32 additions & 1 deletion clang/lib/Driver/ToolChains/CommonArgs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2650,7 +2650,7 @@ getAMDGPUCodeObjectArgument(const Driver &D, const llvm::opt::ArgList &Args) {
void tools::checkAMDGPUCodeObjectVersion(const Driver &D,
const llvm::opt::ArgList &Args) {
const unsigned MinCodeObjVer = 4;
const unsigned MaxCodeObjVer = 5;
const unsigned MaxCodeObjVer = 6;

if (auto *CodeObjArg = getAMDGPUCodeObjectArgument(D, Args)) {
if (CodeObjArg->getOption().getID() ==
Expand All @@ -2661,6 +2661,12 @@ void tools::checkAMDGPUCodeObjectVersion(const Driver &D,
if (Remnant || CodeObjVer < MinCodeObjVer || CodeObjVer > MaxCodeObjVer)
D.Diag(diag::err_drv_invalid_int_value)
<< CodeObjArg->getAsString(Args) << CodeObjArg->getValue();

// COV6 is only supported by LLVM at the time of writing this, and it's
// expected to take some time before all ROCm components fully
// support it. In the meantime, make sure users are aware of this.
if (CodeObjVer == 6)
D.Diag(diag::warn_drv_amdgpu_cov6);
}
}
}
Expand Down Expand Up @@ -2790,3 +2796,28 @@ void tools::addHIPRuntimeLibArgs(const ToolChain &TC, Compilation &C,
}
}
}

void tools::addOutlineAtomicsArgs(const Driver &D, const ToolChain &TC,
const llvm::opt::ArgList &Args,
llvm::opt::ArgStringList &CmdArgs,
const llvm::Triple &Triple) {
if (Arg *A = Args.getLastArg(options::OPT_moutline_atomics,
options::OPT_mno_outline_atomics)) {
// Option -moutline-atomics supported for AArch64 target only.
if (!Triple.isAArch64()) {
D.Diag(diag::warn_drv_moutline_atomics_unsupported_opt)
<< Triple.getArchName() << A->getOption().getName();
} else {
if (A->getOption().matches(options::OPT_moutline_atomics)) {
CmdArgs.push_back("-target-feature");
CmdArgs.push_back("+outline-atomics");
} else {
CmdArgs.push_back("-target-feature");
CmdArgs.push_back("-outline-atomics");
}
}
} else if (Triple.isAArch64() && TC.IsAArch64OutlineAtomicsDefault(Args)) {
CmdArgs.push_back("-target-feature");
CmdArgs.push_back("+outline-atomics");
}
}
6 changes: 6 additions & 0 deletions clang/lib/Driver/ToolChains/CommonArgs.h
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,12 @@ void addMachineOutlinerArgs(const Driver &D, const llvm::opt::ArgList &Args,
void addOpenMPDeviceRTL(const Driver &D, const llvm::opt::ArgList &DriverArgs,
llvm::opt::ArgStringList &CC1Args,
StringRef BitcodeSuffix, const llvm::Triple &Triple);

void addOutlineAtomicsArgs(const Driver &D, const ToolChain &TC,
const llvm::opt::ArgList &Args,
llvm::opt::ArgStringList &CmdArgs,
const llvm::Triple &Triple);

} // end namespace tools
} // end namespace driver
} // end namespace clang
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Driver/ToolChains/Flang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,8 @@ void Flang::addTargetOptions(const ArgList &Args,
CmdArgs.push_back(Args.MakeArgString(CPU));
}

addOutlineAtomicsArgs(D, getToolChain(), Args, CmdArgs, Triple);

// Add the target features.
switch (TC.getArch()) {
default:
Expand Down
14 changes: 11 additions & 3 deletions clang/lib/Format/ContinuationIndenter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -328,9 +328,17 @@ bool ContinuationIndenter::canBreak(const LineState &State) {

// Don't break after very short return types (e.g. "void") as that is often
// unexpected.
if (Current.is(TT_FunctionDeclarationName) && State.Column < 6) {
if (Style.AlwaysBreakAfterReturnType == FormatStyle::RTBS_None)
if (Current.is(TT_FunctionDeclarationName)) {
if (Style.AlwaysBreakAfterReturnType == FormatStyle::RTBS_None &&
State.Column < 6) {
return false;
}

if (Style.AlwaysBreakAfterReturnType == FormatStyle::RTBS_ExceptShortType) {
assert(State.Column >= State.FirstIndent);
if (State.Column - State.FirstIndent < 6)
return false;
}
}

// If binary operators are moved to the next line (including commas for some
Expand Down Expand Up @@ -587,7 +595,7 @@ bool ContinuationIndenter::mustBreak(const LineState &State) {
!State.Line->ReturnTypeWrapped &&
// Don't break before a C# function when no break after return type.
(!Style.isCSharp() ||
Style.AlwaysBreakAfterReturnType != FormatStyle::RTBS_None) &&
Style.AlwaysBreakAfterReturnType > FormatStyle::RTBS_ExceptShortType) &&
// Don't always break between a JavaScript `function` and the function
// name.
!Style.isJavaScript() && Previous.isNot(tok::kw_template) &&
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Format/Format.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,8 @@ template <>
struct ScalarEnumerationTraits<FormatStyle::ReturnTypeBreakingStyle> {
static void enumeration(IO &IO, FormatStyle::ReturnTypeBreakingStyle &Value) {
IO.enumCase(Value, "None", FormatStyle::RTBS_None);
IO.enumCase(Value, "Automatic", FormatStyle::RTBS_Automatic);
IO.enumCase(Value, "ExceptShortType", FormatStyle::RTBS_ExceptShortType);
IO.enumCase(Value, "All", FormatStyle::RTBS_All);
IO.enumCase(Value, "TopLevel", FormatStyle::RTBS_TopLevel);
IO.enumCase(Value, "TopLevelDefinitions",
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Format/TokenAnnotator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3434,6 +3434,8 @@ bool TokenAnnotator::mustBreakForReturnType(const AnnotatedLine &Line) const {

switch (Style.AlwaysBreakAfterReturnType) {
case FormatStyle::RTBS_None:
case FormatStyle::RTBS_Automatic:
case FormatStyle::RTBS_ExceptShortType:
return false;
case FormatStyle::RTBS_All:
case FormatStyle::RTBS_TopLevel:
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Parse/ParseDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1725,6 +1725,7 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
tok::kw___is_member_pointer,
tok::kw___is_nothrow_assignable,
tok::kw___is_nothrow_constructible,
tok::kw___is_nothrow_convertible,
tok::kw___is_nothrow_destructible,
tok::kw___is_nullptr,
tok::kw___is_object,
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/Sema/SemaOverload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7623,7 +7623,8 @@ void Sema::AddTemplateOverloadCandidate(
// functions. In such a case, the candidate functions generated from each
// function template are combined with the set of non-template candidate
// functions.
TemplateDeductionInfo Info(CandidateSet.getLocation());
TemplateDeductionInfo Info(CandidateSet.getLocation(),
FunctionTemplate->getTemplateDepth());
FunctionDecl *Specialization = nullptr;
ConversionSequenceList Conversions;
if (TemplateDeductionResult Result = DeduceTemplateArguments(
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Sema/SemaRISCVVectorLookup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ void RISCVIntrinsicManagerImpl::ConstructRVVIntrinsics(
{"zvknhb", RVV_REQ_Zvknhb},
{"zvksed", RVV_REQ_Zvksed},
{"zvksh", RVV_REQ_Zvksh},
{"zvfbfwma", RVV_REQ_Zvfbfwma},
{"experimental", RVV_REQ_Experimental}};

// Construction of RVVIntrinsicRecords need to sync with createRVVIntrinsics
Expand Down
14 changes: 12 additions & 2 deletions clang/lib/Sema/SemaTemplateInstantiate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3049,6 +3049,7 @@ bool Sema::SubstDefaultArgument(
// default argument expression appears.
ContextRAII SavedContext(*this, FD);
std::unique_ptr<LocalInstantiationScope> LIS;
MultiLevelTemplateArgumentList NewTemplateArgs = TemplateArgs;

if (ForCallExpr) {
// When instantiating a default argument due to use in a call expression,
Expand All @@ -3061,11 +3062,20 @@ bool Sema::SubstDefaultArgument(
/*ForDefinition*/ false);
if (addInstantiatedParametersToScope(FD, PatternFD, *LIS, TemplateArgs))
return true;
const FunctionTemplateDecl *PrimaryTemplate = FD->getPrimaryTemplate();
if (PrimaryTemplate && PrimaryTemplate->isOutOfLine()) {
TemplateArgumentList *CurrentTemplateArgumentList =
TemplateArgumentList::CreateCopy(getASTContext(),
TemplateArgs.getInnermost());
NewTemplateArgs = getTemplateInstantiationArgs(
FD, FD->getDeclContext(), /*Final=*/false,
CurrentTemplateArgumentList->asArray(), /*RelativeToPrimary=*/true);
}
}

runWithSufficientStackSpace(Loc, [&] {
Result = SubstInitializer(PatternExpr, TemplateArgs,
/*DirectInit*/false);
Result = SubstInitializer(PatternExpr, NewTemplateArgs,
/*DirectInit*/ false);
});
}
if (Result.isInvalid())
Expand Down
13 changes: 11 additions & 2 deletions clang/lib/Sema/SemaType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5907,15 +5907,24 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
// - the type-id in the default argument of a type-parameter, or
// - the type-id of a template-argument for a type-parameter
//
// C++23 [dcl.fct]p6 (P0847R7)
// ... A member-declarator with an explicit-object-parameter-declaration
// shall not include a ref-qualifier or a cv-qualifier-seq and shall not be
// declared static or virtual ...
//
// FIXME: Checking this here is insufficient. We accept-invalid on:
//
// template<typename T> struct S { void f(T); };
// S<int() const> s;
//
// ... for instance.
if (IsQualifiedFunction &&
!(Kind == Member && !D.isExplicitObjectMemberFunction() &&
D.getDeclSpec().getStorageClassSpec() != DeclSpec::SCS_static) &&
// Check for non-static member function and not and
// explicit-object-parameter-declaration
(Kind != Member || D.isExplicitObjectMemberFunction() ||
D.getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_static ||
(D.getContext() == clang::DeclaratorContext::Member &&
D.isStaticMember())) &&
!IsTypedefName && D.getContext() != DeclaratorContext::TemplateArg &&
D.getContext() != DeclaratorContext::TemplateTypeArg) {
SourceLocation Loc = D.getBeginLoc();
Expand Down
391 changes: 318 additions & 73 deletions clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ bool BuiltinFunctionChecker::evalCall(const CallEvent &Call,
default:
return false;

case Builtin::BI__builtin_assume: {
case Builtin::BI__builtin_assume:
case Builtin::BI__assume: {
assert (Call.getNumArgs() > 0);
SVal Arg = Call.getArgSVal(0);
if (Arg.isUndef())
Expand Down
13 changes: 13 additions & 0 deletions clang/test/AST/Interp/atomic.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -verify=both,expected %s
// RUN: %clang_cc1 -verify=both,ref %s

/// FIXME: Copied from test/Sema/atomic-expr.c.
/// this expression seems to be rejected for weird reasons,
/// but we imitate the current interpreter's behavior.
_Atomic int ai = 0;
// FIXME: &ai is an address constant, so this should be accepted as an
// initializer, but the bit-cast inserted due to the pointer conversion is
// tripping up the test for whether the initializer is a constant expression.
// The warning is correct but the error is not.
_Atomic(int *) aip3 = &ai; // both-warning {{incompatible pointer types initializing '_Atomic(int *)' with an expression of type '_Atomic(int) *'}} \
// both-error {{initializer element is not a compile-time constant}}
17 changes: 17 additions & 0 deletions clang/test/AST/Interp/atomic.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -verify=both,expected -std=c++11 %s
// RUN: %clang_cc1 -verify=both,ref -std=c++11 %s
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -verify=both,expected -std=c++98 %s
// RUN: %clang_cc1 -verify=both,ref -std=c++98 %s



// expected-no-diagnostics
// ref-no-diagnostics


/// Rejected in c++98
#if __cplusplus >= 201103L
constexpr _Atomic(bool) B = true;
static_assert(B, "");
#endif

10 changes: 6 additions & 4 deletions clang/test/AST/Interp/builtins.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter %s -verify
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter %s -S -emit-llvm -o - | FileCheck %s
// RUN: %clang_cc1 -verify=ref %s -Wno-constant-evaluated
// RUN: %clang_cc1 -verify=ref %s -Wno-constant-evaluated %s -S -emit-llvm -o - | FileCheck %s
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter %s -verify -fms-extensions
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter %s -fms-extensions -S -emit-llvm -o - | FileCheck %s
// RUN: %clang_cc1 -verify=ref %s -Wno-constant-evaluated -fms-extensions
// RUN: %clang_cc1 -verify=ref %s -Wno-constant-evaluated %s -fms-extensions -S -emit-llvm -o - | FileCheck %s

// expected-no-diagnostics
// ref-no-diagnostics
Expand All @@ -26,6 +26,8 @@ bool is_this_constant() {
constexpr bool assume() {
__builtin_assume(true);
__builtin_assume(false);
__assume(1);
__assume(false);
return true;
}
static_assert(assume(), "");
4 changes: 4 additions & 0 deletions clang/test/AST/Interp/c.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ typedef __INTPTR_TYPE__ intptr_t;
typedef __PTRDIFF_TYPE__ ptrdiff_t;

_Static_assert(1, "");

_Static_assert(__objc_yes, "");
_Static_assert(!__objc_no, "");

_Static_assert(0 != 1, "");
_Static_assert(1.0 == 1.0, ""); // pedantic-ref-warning {{not an integer constant expression}} \
// pedantic-expected-warning {{not an integer constant expression}}
Expand Down
15 changes: 15 additions & 0 deletions clang/test/AST/Interp/complex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,3 +179,18 @@ namespace Sub {
}

}

namespace ZeroInit {
typedef _Complex float fcomplex;
typedef _Complex unsigned icomplex;

constexpr fcomplex test7 = fcomplex();
static_assert(__real(test7) == 0.0f, "");
static_assert(__imag(test7) == 0.0f, "");

constexpr icomplex test8 = icomplex();
static_assert(__real(test8) == 0, "");
static_assert(__imag(test8) == 0, "");

constexpr int ignored = (fcomplex(), 0);
}
8 changes: 8 additions & 0 deletions clang/test/AST/Interp/cxx98.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,11 @@ int NCI; // both-note {{declared here}}
int NCIA[NCI]; // both-warning {{variable length array}} \
// both-error {{variable length array}} \\
// both-note {{read of non-const variable 'NCI'}}


struct V {
char c[1];
banana V() : c("i") {} // both-error {{unknown type name 'banana'}} \
// both-error {{constructor cannot have a return type}}
};
_Static_assert(V().c[0], ""); // both-error {{is not an integral constant expression}}
3 changes: 3 additions & 0 deletions clang/test/AST/Interp/literals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ static_assert(number != 10, ""); // expected-error{{failed}} \
// expected-note{{evaluates to}} \
// ref-note{{evaluates to}}

static_assert(__objc_yes, "");
static_assert(!__objc_no, "");

constexpr bool b = number;
static_assert(b, "");
constexpr int one = true;
Expand Down
25 changes: 25 additions & 0 deletions clang/test/AST/ast-dump-pack-indexing-crash.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// RUN: not %clang_cc1 -std=c++2c -ast-dump %s | FileCheck %s

namespace InvalidPacksShouldNotCrash {

struct NotAPack;
template <typename T, auto V, template<typename> typename Tp>
void not_pack() {
int i = 0;
i...[0]; // expected-error {{i does not refer to the name of a parameter pack}}
V...[0]; // expected-error {{V does not refer to the name of a parameter pack}}
NotAPack...[0] a; // expected-error{{'NotAPack' does not refer to the name of a parameter pack}}
T...[0] b; // expected-error{{'T' does not refer to the name of a parameter pack}}
Tp...[0] c; // expected-error{{'Tp' does not refer to the name of a parameter pack}}
}

// CHECK: FunctionDecl {{.*}} not_pack 'void ()'
// CHECK: DeclStmt {{.*}}
// CHECK: DeclStmt {{.*}}
// CHECK-NEXT: VarDecl {{.*}} a 'NotAPack...{{.*}}'
// CHECK-NEXT: DeclStmt {{.*}}
// CHECK-NEXT: VarDecl {{.*}} 'T...{{.*}}'
// CHECK-NEXT: DeclStmt {{.*}}
// CHECK-NEXT: VarDecl {{.*}} c 'Tp...{{.*}}'

}
18 changes: 18 additions & 0 deletions clang/test/Analysis/builtin-functions.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// RUN: %clang_analyze_cc1 -triple x86_64-apple-darwin10 -analyzer-checker=core,debug.ExprInspection %s -std=c++11 -verify
// RUN: %clang_analyze_cc1 -triple x86_64-pc-windows-msvc19.11.0 -fms-extensions -analyzer-checker=core,debug.ExprInspection %s -std=c++11 -verify

void clang_analyzer_eval(bool);
void clang_analyzer_warnIfReached();
Expand Down Expand Up @@ -65,6 +66,23 @@ void g(int i) {
}
}

#ifdef _WIN32
namespace ms {
void f(int i) {
__assume(i < 10);
clang_analyzer_eval(i < 15); // expected-warning {{TRUE}}
}

void g(int i) {
if (i > 5) {
__assume(i < 5);
clang_analyzer_warnIfReached(); // Assumtion contradicts constraints.
// We give up the analysis on this path.
}
}
} // namespace ms
#endif

void test_constant_p(void *ptr) {
int i = 1;
const int j = 2;
Expand Down
142 changes: 90 additions & 52 deletions clang/test/Analysis/out-of-bounds-diagnostics.c
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
// RUN: %clang_analyze_cc1 -Wno-array-bounds -analyzer-output=text \
// RUN: -analyzer-checker=core,alpha.security.ArrayBoundV2,unix.Malloc,alpha.security.taint -verify %s

int array[10];
int TenElements[10];

void arrayUnderflow(void) {
array[-3] = 5;
// expected-warning@-1 {{Out of bound access to memory preceding 'array'}}
// expected-note@-2 {{Access of 'array' at negative byte offset -12}}
TenElements[-3] = 5;
// expected-warning@-1 {{Out of bound access to memory preceding 'TenElements'}}
// expected-note@-2 {{Access of 'TenElements' at negative byte offset -12}}
}

int underflowWithDeref(void) {
int *p = array;
int *p = TenElements;
--p;
return *p;
// expected-warning@-1 {{Out of bound access to memory preceding 'array'}}
// expected-note@-2 {{Access of 'array' at negative byte offset -4}}
// expected-warning@-1 {{Out of bound access to memory preceding 'TenElements'}}
// expected-note@-2 {{Access of 'TenElements' at negative byte offset -4}}
}

int scanf(const char *restrict fmt, ...);
Expand All @@ -24,88 +24,89 @@ void taintedIndex(void) {
scanf("%d", &index);
// expected-note@-1 {{Taint originated here}}
// expected-note@-2 {{Taint propagated to the 2nd argument}}
array[index] = 5;
// expected-warning@-1 {{Potential out of bound access to 'array' with tainted index}}
// expected-note@-2 {{Access of 'array' with a tainted index that may be too large}}
TenElements[index] = 5;
// expected-warning@-1 {{Potential out of bound access to 'TenElements' with tainted index}}
// expected-note@-2 {{Access of 'TenElements' with a tainted index that may be too large}}
}

int *taintedIndexAfterTheEndPtr(void) {
// NOTE: Technically speaking, this testcase does not trigger any UB because
// &array[10] is the after-the-end pointer which is well-defined; but this is
// a bug-prone situation and far from the idiomatic use of `&array[size]`, so
// it's better to report an error. This report can be easily silenced by
// writing array+index instead of &array[index].
// &TenElements[10] is the after-the-end pointer which is well-defined; but
// this is a bug-prone situation and far from the idiomatic use of
// `&TenElements[size]`, so it's better to report an error. This report can
// be easily silenced by writing TenElements+index instead of
// &TenElements[index].
int index;
scanf("%d", &index);
// expected-note@-1 {{Taint originated here}}
// expected-note@-2 {{Taint propagated to the 2nd argument}}
if (index < 0 || index > 10)
return array;
return TenElements;
// expected-note@-2 {{Assuming 'index' is >= 0}}
// expected-note@-3 {{Left side of '||' is false}}
// expected-note@-4 {{Assuming 'index' is <= 10}}
// expected-note@-5 {{Taking false branch}}
return &array[index];
// expected-warning@-1 {{Potential out of bound access to 'array' with tainted index}}
// expected-note@-2 {{Access of 'array' with a tainted index that may be too large}}
return &TenElements[index];
// expected-warning@-1 {{Potential out of bound access to 'TenElements' with tainted index}}
// expected-note@-2 {{Access of 'TenElements' with a tainted index that may be too large}}
}

void taintedOffset(void) {
int index;
scanf("%d", &index);
// expected-note@-1 {{Taint originated here}}
// expected-note@-2 {{Taint propagated to the 2nd argument}}
int *p = array + index;
int *p = TenElements + index;
p[0] = 5;
// expected-warning@-1 {{Potential out of bound access to 'array' with tainted offset}}
// expected-note@-2 {{Access of 'array' with a tainted offset that may be too large}}
// expected-warning@-1 {{Potential out of bound access to 'TenElements' with tainted offset}}
// expected-note@-2 {{Access of 'TenElements' with a tainted offset that may be too large}}
}

void arrayOverflow(void) {
array[12] = 5;
// expected-warning@-1 {{Out of bound access to memory after the end of 'array'}}
// expected-note@-2 {{Access of 'array' at index 12, while it holds only 10 'int' elements}}
TenElements[12] = 5;
// expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}
// expected-note@-2 {{Access of 'TenElements' at index 12, while it holds only 10 'int' elements}}
}

void flippedOverflow(void) {
12[array] = 5;
// expected-warning@-1 {{Out of bound access to memory after the end of 'array'}}
// expected-note@-2 {{Access of 'array' at index 12, while it holds only 10 'int' elements}}
12[TenElements] = 5;
// expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}
// expected-note@-2 {{Access of 'TenElements' at index 12, while it holds only 10 'int' elements}}
}

int *afterTheEndPtr(void) {
// This is an unusual but standard-compliant way of writing (array + 10).
return &array[10]; // no-warning
// This is an unusual but standard-compliant way of writing (TenElements + 10).
return &TenElements[10]; // no-warning
}

int useAfterTheEndPtr(void) {
// ... but dereferencing the after-the-end pointer is still invalid.
return *afterTheEndPtr();
// expected-warning@-1 {{Out of bound access to memory after the end of 'array'}}
// expected-note@-2 {{Access of 'array' at index 10, while it holds only 10 'int' elements}}
// expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}
// expected-note@-2 {{Access of 'TenElements' at index 10, while it holds only 10 'int' elements}}
}

int *afterAfterTheEndPtr(void) {
// This is UB, it's invalid to form an after-after-the-end pointer.
return &array[11];
// expected-warning@-1 {{Out of bound access to memory after the end of 'array'}}
// expected-note@-2 {{Access of 'array' at index 11, while it holds only 10 'int' elements}}
return &TenElements[11];
// expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}
// expected-note@-2 {{Access of 'TenElements' at index 11, while it holds only 10 'int' elements}}
}

int *potentialAfterTheEndPtr(int idx) {
if (idx < 10) { /* ...do something... */ }
// expected-note@-1 {{Assuming 'idx' is >= 10}}
// expected-note@-2 {{Taking false branch}}
return &array[idx];
// expected-warning@-1 {{Out of bound access to memory after the end of 'array'}}
// expected-note@-2 {{Access of 'array' at an overflowing index, while it holds only 10 'int' elements}}
return &TenElements[idx];
// expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}
// expected-note@-2 {{Access of 'TenElements' at an overflowing index, while it holds only 10 'int' elements}}
// NOTE: On the idx >= 10 branch the normal "optimistic" behavior would've
// been continuing with the assumption that idx == 10 and the return value is
// a legitimate after-the-end pointer. The checker deviates from this by
// reporting an error because this situation is very suspicious and far from
// the idiomatic `&array[size]` expressions. If the report is FP, the
// developer can easily silence it by writing array+idx instead of
// &array[idx].
// the idiomatic `&TenElements[size]` expressions. If the report is FP, the
// developer can easily silence it by writing TenElements+idx instead of
// &TenElements[idx].
}

int scalar;
Expand Down Expand Up @@ -156,9 +157,9 @@ int arrayOfStructsArrow(void) {
}

short convertedArray(void) {
return ((short*)array)[47];
// expected-warning@-1 {{Out of bound access to memory after the end of 'array'}}
// expected-note@-2 {{Access of 'array' at index 47, while it holds only 20 'short' elements}}
return ((short*)TenElements)[47];
// expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}
// expected-note@-2 {{Access of 'TenElements' at index 47, while it holds only 20 'short' elements}}
}

struct two_bytes {
Expand Down Expand Up @@ -195,12 +196,22 @@ void *malloc(size_t size);

int *mallocRegion(void) {
int *mem = (int*)malloc(2*sizeof(int));

mem[3] = -2;
// expected-warning@-1 {{Out of bound access to memory after the end of the heap area}}
// expected-note@-2 {{Access of the heap area at index 3, while it holds only 2 'int' elements}}
return mem;
}

int *mallocRegionDeref(void) {
int *mem = (int*)malloc(2*sizeof(int));

*(mem + 3) = -2;
// expected-warning@-1 {{Out of bound access to memory after the end of the heap area}}
// expected-note@-2 {{Access of the heap area at index 3, while it holds only 2 'int' elements}}
return mem;
}

void *alloca(size_t size);

int allocaRegion(void) {
Expand All @@ -211,34 +222,61 @@ int allocaRegion(void) {
return *mem;
}

int *unknownExtent(int arg) {
if (arg >= 2)
int *symbolicExtent(int arg) {
// expected-note@+2 {{Assuming 'arg' is < 5}}
// expected-note@+1 {{Taking false branch}}
if (arg >= 5)
return 0;
int *mem = (int*)malloc(arg);

// TODO: without the following reference to 'arg', the analyzer would discard
// the range information about (the symbolic value of) 'arg'. This is
// incorrect because while the variable itself is inaccessible, it becomes
// the symbolic extent of 'mem', so we still want to reason about its
// potential values.
(void)arg;

mem[8] = -2;
// FIXME: this should produce
// {{Out of bound access to memory after the end of the heap area}}
// {{Access of 'int' element in the heap area at index 8}}
// expected-warning@-1 {{Out of bound access to memory after the end of the heap area}}
// expected-note@-2 {{Access of 'int' element in the heap area at index 8}}
return mem;
}

void unknownIndex(int arg) {
int *symbolicExtentDiscardedRangeInfo(int arg) {
// This is a copy of the case 'symbolicExtent' without the '(void)arg' hack.
// TODO: if the analyzer can detect the out-of-bounds access within this
// testcase, then remove this and the `(void)arg` hack from `symbolicExtent`.
if (arg >= 5)
return 0;
int *mem = (int*)malloc(arg);
mem[8] = -2;
return mem;
}

void symbolicIndex(int arg) {
// expected-note@+2 {{Assuming 'arg' is >= 12}}
// expected-note@+1 {{Taking true branch}}
if (arg >= 12)
array[arg] = -2;
// expected-warning@-1 {{Out of bound access to memory after the end of 'array'}}
// expected-note@-2 {{Access of 'array' at an overflowing index, while it holds only 10 'int' elements}}
TenElements[arg] = -2;
// expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}
// expected-note@-2 {{Access of 'TenElements' at an overflowing index, while it holds only 10 'int' elements}}
}

int *nothingIsCertain(int x, int y) {
if (x >= 2)
return 0;
int *mem = (int*)malloc(x);

if (y >= 8)
mem[y] = -2;
// FIXME: this should produce
// {{Out of bound access to memory after the end of the heap area}}
// {{Access of 'int' element in the heap area at an overflowing index}}
// but apparently the analyzer isn't smart enough to deduce this.

// Keep constraints alive. (Without this, the overeager garbage collection of
// constraints would _also_ prevent the intended behavior in this testcase.)
(void)x;

return mem;
}
199 changes: 199 additions & 0 deletions clang/test/Analysis/out-of-bounds-notes.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
// RUN: %clang_analyze_cc1 -Wno-array-bounds -analyzer-output=text \
// RUN: -analyzer-checker=core,alpha.security.ArrayBoundV2,unix.Malloc,alpha.security.taint -verify %s

int TenElements[10];

int irrelevantAssumptions(int arg) {
int a = TenElements[arg];
// Here the analyzer assumes that `arg` is in bounds, but doesn't report this
// because `arg` is not interesting for the bug.
int b = TenElements[13];
// expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}
// expected-note@-2 {{Access of 'TenElements' at index 13, while it holds only 10 'int' elements}}
return a + b;
}


int assumingBoth(int arg) {
int a = TenElements[arg];
// expected-note@-1 {{Assuming index is non-negative and less than 10, the number of 'int' elements in 'TenElements'}}
int b = TenElements[arg]; // no additional note, we already assumed that 'arg' is in bounds
int c = TenElements[arg + 10];
// expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}
// expected-note@-2 {{Access of 'TenElements' at an overflowing index, while it holds only 10 'int' elements}}
return a + b + c;
}

int assumingBothPointerToMiddle(int arg) {
// If we're accessing an TenElements through a pointer pointing to its middle, the checker
// will speak about the "byte offset" measured from the beginning of the TenElements.
int *p = TenElements + 2;
int a = p[arg];
// FIXME: The following note does not appear:
// {{Assuming byte offset is non-negative and less than 40, the extent of 'TenElements'}}
// It seems that the analyzer "gives up" modeling this pointer arithmetics
// and says that `p[arg]` is just an UnknownVal (instead of calculating that
// it's equivalent to `TenElements[2+arg]`).

int b = TenElements[arg]; // This is normal access, and only the lower bound is new.
// expected-note@-1 {{Assuming index is non-negative}}
int c = TenElements[arg + 10];
// expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}
// expected-note@-2 {{Access of 'TenElements' at an overflowing index, while it holds only 10 'int' elements}}
return a + b + c;
}

int assumingLower(int arg) {
// expected-note@+2 {{Assuming 'arg' is < 10}}
// expected-note@+1 {{Taking false branch}}
if (arg >= 10)
return 0;
int a = TenElements[arg];
// expected-note@-1 {{Assuming index is non-negative}}
int b = TenElements[arg + 10];
// expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}
// expected-note@-2 {{Access of 'TenElements' at an overflowing index, while it holds only 10 'int' elements}}
return a + b;
}

int assumingUpper(int arg) {
// expected-note@+2 {{Assuming 'arg' is >= 0}}
// expected-note@+1 {{Taking false branch}}
if (arg < 0)
return 0;
int a = TenElements[arg];
// expected-note@-1 {{Assuming index is less than 10, the number of 'int' elements in 'TenElements'}}
int b = TenElements[arg - 10];
// expected-warning@-1 {{Out of bound access to memory preceding 'TenElements'}}
// expected-note@-2 {{Access of 'TenElements' at negative byte offset}}
return a + b;
}

int assumingUpperIrrelevant(int arg) {
// FIXME: The assumption "assuming index is less than 10" is printed because
// it's assuming something about the interesting variable `arg`; however,
// it's irrelevant because in this testcase the out of bound access is
// deduced from the _lower_ bound on `arg`. Currently the analyzer cannot
// filter out assumptions that are logically irrelevant but "touch"
// interesting symbols; eventually it would be good to add support for this.

// expected-note@+2 {{Assuming 'arg' is >= 0}}
// expected-note@+1 {{Taking false branch}}
if (arg < 0)
return 0;
int a = TenElements[arg];
// expected-note@-1 {{Assuming index is less than 10, the number of 'int' elements in 'TenElements'}}
int b = TenElements[arg + 10];
// expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}
// expected-note@-2 {{Access of 'TenElements' at an overflowing index, while it holds only 10 'int' elements}}
return a + b;
}

int assumingUpperUnsigned(unsigned arg) {
int a = TenElements[arg];
// expected-note@-1 {{Assuming index is less than 10, the number of 'int' elements in 'TenElements'}}
int b = TenElements[(int)arg - 10];
// expected-warning@-1 {{Out of bound access to memory preceding 'TenElements'}}
// expected-note@-2 {{Access of 'TenElements' at negative byte offset}}
return a + b;
}

int assumingNothing(unsigned arg) {
// expected-note@+2 {{Assuming 'arg' is < 10}}
// expected-note@+1 {{Taking false branch}}
if (arg >= 10)
return 0;
int a = TenElements[arg]; // no note here, we already know that 'arg' is in bounds
int b = TenElements[(int)arg - 10];
// expected-warning@-1 {{Out of bound access to memory preceding 'TenElements'}}
// expected-note@-2 {{Access of 'TenElements' at negative byte offset}}
return a + b;
}

short assumingConvertedToCharP(int arg) {
// When indices are reported, the note will use the element type that's the
// result type of the subscript operator.
char *cp = (char*)TenElements;
char a = cp[arg];
// expected-note@-1 {{Assuming index is non-negative and less than 40, the number of 'char' elements in 'TenElements'}}
char b = cp[arg]; // no additional note, we already assumed that 'arg' is in bounds
char c = cp[arg + 40];
// expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}
// expected-note@-2 {{Access of 'TenElements' at an overflowing index, while it holds only 40 'char' elements}}
return a + b + c;
}

struct foo {
int num;
char a[8];
char b[5];
};

int assumingConvertedToIntP(struct foo f, int arg) {
// When indices are reported, the note will use the element type that's the
// result type of the subscript operator.
int a = ((int*)(f.a))[arg];
// expected-note@-1 {{Assuming index is non-negative and less than 2, the number of 'int' elements in 'f.a'}}
// However, if the extent of the memory region is not divisible by the
// element size, the checker measures the offset and extent in bytes.
int b = ((int*)(f.b))[arg];
// expected-note@-1 {{Assuming byte offset is less than 5, the extent of 'f.b'}}
int c = TenElements[arg-2];
// expected-warning@-1 {{Out of bound access to memory preceding 'TenElements'}}
// expected-note@-2 {{Access of 'TenElements' at negative byte offset}}
return a + b + c;
}

int assumingPlainOffset(struct foo f, int arg) {
// This TC is intended to check the corner case that the checker prints the
// shorter "offset" instead of "byte offset" when it's irrelevant that the
// offset is measured in bytes.

// expected-note@+2 {{Assuming 'arg' is < 2}}
// expected-note@+1 {{Taking false branch}}
if (arg >= 2)
return 0;

int b = ((int*)(f.b))[arg];
// expected-note@-1 {{Assuming byte offset is non-negative and less than 5, the extent of 'f.b'}}
// FIXME: this should be {{Assuming offset is non-negative}}
// but the current simplification algorithm doesn't realize that arg <= 1
// implies that the byte offset arg*4 will be less than 5.

int c = TenElements[arg+10];
// expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}
// expected-note@-2 {{Access of 'TenElements' at an overflowing index, while it holds only 10 'int' elements}}
return b + c;
}

typedef __typeof(sizeof(int)) size_t;
void *malloc(size_t size);
void free(void *ptr);

int assumingExtent(int arg) {
// Verify that the assumption note is printed when the extent is interesting
// (even if the index isn't interesting).
int *mem = (int*)malloc(arg);

mem[12] = 123;
// expected-note@-1 {{Assuming index '12' is less than the number of 'int' elements in the heap area}}

free(mem);

return TenElements[arg];
// expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}
// expected-note@-2 {{Access of 'TenElements' at an overflowing index, while it holds only 10 'int' elements}}
}

int *extentInterestingness(int arg) {
// Verify that in an out-of-bounds access issue the extent is marked as
// interesting (so assumptions about its value are printed).
int *mem = (int*)malloc(arg);

TenElements[arg] = 123;
// expected-note@-1 {{Assuming index is non-negative and less than 10, the number of 'int' elements in 'TenElements'}}

return &mem[12];
// expected-warning@-1 {{Out of bound access to memory after the end of the heap area}}
// expected-note@-2 {{Access of 'int' element in the heap area at index 12}}
}
Loading