Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,9 @@ void OptionalValueConversionCheck::registerMatchers(MatchFinder *Finder) {
ofClass(matchers::matchesAnyListedName(OptionalTypes)))),
hasType(ConstructTypeMatcher),
hasArgument(0U, ignoringImpCasts(anyOf(OptionalDereferenceMatcher,
StdMoveCallMatcher))))
StdMoveCallMatcher))),
unless(anyOf(hasAncestor(typeLoc()),
hasAncestor(expr(matchers::hasUnevaluatedContext())))))
.bind("expr"),
this);
}
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ add_clang_library(clangTidyModernizeModule
UseNullptrCheck.cpp
UseOverrideCheck.cpp
UseStartsEndsWithCheck.cpp
UseStdFormatCheck.cpp
UseStdNumbersCheck.cpp
UseStdPrintCheck.cpp
UseTrailingReturnTypeCheck.cpp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
#include "UseNullptrCheck.h"
#include "UseOverrideCheck.h"
#include "UseStartsEndsWithCheck.h"
#include "UseStdFormatCheck.h"
#include "UseStdNumbersCheck.h"
#include "UseStdPrintCheck.h"
#include "UseTrailingReturnTypeCheck.h"
Expand Down Expand Up @@ -76,6 +77,7 @@ class ModernizeModule : public ClangTidyModule {
"modernize-use-designated-initializers");
CheckFactories.registerCheck<UseStartsEndsWithCheck>(
"modernize-use-starts-ends-with");
CheckFactories.registerCheck<UseStdFormatCheck>("modernize-use-std-format");
CheckFactories.registerCheck<UseStdNumbersCheck>(
"modernize-use-std-numbers");
CheckFactories.registerCheck<UseStdPrintCheck>("modernize-use-std-print");
Expand Down
107 changes: 107 additions & 0 deletions clang-tools-extra/clang-tidy/modernize/UseStdFormatCheck.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
//===--- UseStdFormatCheck.cpp - clang-tidy -------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "UseStdFormatCheck.h"
#include "../utils/FormatStringConverter.h"
#include "../utils/Matchers.h"
#include "../utils/OptionsUtils.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Lex/Lexer.h"
#include "clang/Tooling/FixIt.h"

using namespace clang::ast_matchers;

namespace clang::tidy::modernize {

namespace {
AST_MATCHER(StringLiteral, isOrdinary) { return Node.isOrdinary(); }
} // namespace

UseStdFormatCheck::UseStdFormatCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
StrictMode(Options.getLocalOrGlobal("StrictMode", false)),
StrFormatLikeFunctions(utils::options::parseStringList(
Options.get("StrFormatLikeFunctions", ""))),
ReplacementFormatFunction(
Options.get("ReplacementFormatFunction", "std::format")),
IncludeInserter(Options.getLocalOrGlobal("IncludeStyle",
utils::IncludeSorter::IS_LLVM),
areDiagsSelfContained()),
MaybeHeaderToInclude(Options.get("FormatHeader")) {
if (StrFormatLikeFunctions.empty())
StrFormatLikeFunctions.push_back("absl::StrFormat");

if (!MaybeHeaderToInclude && ReplacementFormatFunction == "std::format")
MaybeHeaderToInclude = "<format>";
}

void UseStdFormatCheck::registerPPCallbacks(const SourceManager &SM,
Preprocessor *PP,
Preprocessor *ModuleExpanderPP) {
IncludeInserter.registerPreprocessor(PP);
}

void UseStdFormatCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
callExpr(argumentCountAtLeast(1),
hasArgument(0, stringLiteral(isOrdinary())),
callee(functionDecl(unless(cxxMethodDecl()),
matchers::matchesAnyListedName(
StrFormatLikeFunctions))
.bind("func_decl")))
.bind("strformat"),
this);
}

void UseStdFormatCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
using utils::options::serializeStringList;
Options.store(Opts, "StrictMode", StrictMode);
Options.store(Opts, "StrFormatLikeFunctions",
serializeStringList(StrFormatLikeFunctions));
Options.store(Opts, "ReplacementFormatFunction", ReplacementFormatFunction);
Options.store(Opts, "IncludeStyle", IncludeInserter.getStyle());
if (MaybeHeaderToInclude)
Options.store(Opts, "FormatHeader", *MaybeHeaderToInclude);
}

void UseStdFormatCheck::check(const MatchFinder::MatchResult &Result) {
const unsigned FormatArgOffset = 0;
const auto *OldFunction = Result.Nodes.getNodeAs<FunctionDecl>("func_decl");
const auto *StrFormat = Result.Nodes.getNodeAs<CallExpr>("strformat");

utils::FormatStringConverter::Configuration ConverterConfig;
ConverterConfig.StrictMode = StrictMode;
utils::FormatStringConverter Converter(Result.Context, StrFormat,
FormatArgOffset, ConverterConfig,
getLangOpts());
const Expr *StrFormatCall = StrFormat->getCallee();
if (!Converter.canApply()) {
diag(StrFormat->getBeginLoc(),
"unable to use '%0' instead of %1 because %2")
<< StrFormatCall->getSourceRange() << ReplacementFormatFunction
<< OldFunction->getIdentifier()
<< Converter.conversionNotPossibleReason();
return;
}

DiagnosticBuilder Diag =
diag(StrFormatCall->getBeginLoc(), "use '%0' instead of %1")
<< ReplacementFormatFunction << OldFunction->getIdentifier();
Diag << FixItHint::CreateReplacement(
CharSourceRange::getTokenRange(StrFormatCall->getSourceRange()),
ReplacementFormatFunction);
Converter.applyFixes(Diag, *Result.SourceManager);

if (MaybeHeaderToInclude)
Diag << IncludeInserter.createIncludeInsertion(
Result.Context->getSourceManager().getFileID(
StrFormatCall->getBeginLoc()),
*MaybeHeaderToInclude);
}

} // namespace clang::tidy::modernize
51 changes: 51 additions & 0 deletions clang-tools-extra/clang-tidy/modernize/UseStdFormatCheck.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//===--- UseStdFormatCheck.h - clang-tidy -----------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESTDFORMATCHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESTDFORMATCHECK_H

#include "../ClangTidyCheck.h"
#include "../utils/IncludeInserter.h"

namespace clang::tidy::modernize {

/// Converts calls to absl::StrFormat, or other functions via configuration
/// options, to C++20's std::format, or another function via a configuration
/// option, modifying the format string appropriately and removing
/// now-unnecessary calls to std::string::c_str() and std::string::data().
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/modernize/use-std-format.html
class UseStdFormatCheck : public ClangTidyCheck {
public:
UseStdFormatCheck(StringRef Name, ClangTidyContext *Context);
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
if (ReplacementFormatFunction == "std::format")
return LangOpts.CPlusPlus20;
return LangOpts.CPlusPlus;
}
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
Preprocessor *ModuleExpanderPP) override;
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
std::optional<TraversalKind> getCheckTraversalKind() const override {
return TK_IgnoreUnlessSpelledInSource;
}

private:
bool StrictMode;
std::vector<StringRef> StrFormatLikeFunctions;
StringRef ReplacementFormatFunction;
utils::IncludeInserter IncludeInserter;
std::optional<StringRef> MaybeHeaderToInclude;
};

} // namespace clang::tidy::modernize

#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESTDFORMATCHECK_H
5 changes: 4 additions & 1 deletion clang-tools-extra/clang-tidy/modernize/UseStdPrintCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,11 @@ void UseStdPrintCheck::check(const MatchFinder::MatchResult &Result) {
FormatArgOffset = 1;
}

utils::FormatStringConverter::Configuration ConverterConfig;
ConverterConfig.StrictMode = StrictMode;
ConverterConfig.AllowTrailingNewlineRemoval = true;
utils::FormatStringConverter Converter(
Result.Context, Printf, FormatArgOffset, StrictMode, getLangOpts());
Result.Context, Printf, FormatArgOffset, ConverterConfig, getLangOpts());
const Expr *PrintfCall = Printf->getCallee();
const StringRef ReplacementFunction = Converter.usePrintNewlineFunction()
? ReplacementPrintlnFunction
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ static bool containsDeclInScope(const Stmt *Node) {
}

static void removeElseAndBrackets(DiagnosticBuilder &Diag, ASTContext &Context,
const Stmt *Else, SourceLocation ElseLoc) {
const Stmt *Else, SourceLocation ElseLoc) {
auto Remap = [&](SourceLocation Loc) {
return Context.getSourceManager().getExpansionLoc(Loc);
};
Expand Down Expand Up @@ -172,7 +172,7 @@ void ElseAfterReturnCheck::registerMatchers(MatchFinder *Finder) {
breakStmt().bind(InterruptingStr), cxxThrowExpr().bind(InterruptingStr)));
Finder->addMatcher(
compoundStmt(
forEach(ifStmt(unless(isConstexpr()),
forEach(ifStmt(unless(isConstexpr()), unless(isConsteval()),
hasThen(stmt(
anyOf(InterruptsControlFlow,
compoundStmt(has(InterruptsControlFlow))))),
Expand Down
16 changes: 10 additions & 6 deletions clang-tools-extra/clang-tidy/utils/FormatStringConverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -198,10 +198,11 @@ static bool castMismatchedIntegerTypes(const CallExpr *Call, bool StrictMode) {
FormatStringConverter::FormatStringConverter(ASTContext *ContextIn,
const CallExpr *Call,
unsigned FormatArgOffset,
bool StrictMode,
const Configuration ConfigIn,
const LangOptions &LO)
: Context(ContextIn),
CastMismatchedIntegerTypes(castMismatchedIntegerTypes(Call, StrictMode)),
: Context(ContextIn), Config(ConfigIn),
CastMismatchedIntegerTypes(
castMismatchedIntegerTypes(Call, ConfigIn.StrictMode)),
Args(Call->getArgs()), NumArgs(Call->getNumArgs()),
ArgsOffset(FormatArgOffset + 1), LangOpts(LO) {
assert(ArgsOffset <= NumArgs);
Expand Down Expand Up @@ -627,9 +628,12 @@ void FormatStringConverter::finalizeFormatText() {

// It's clearer to convert printf("Hello\r\n"); to std::print("Hello\r\n")
// than to std::println("Hello\r");
if (StringRef(StandardFormatString).ends_with("\\n") &&
!StringRef(StandardFormatString).ends_with("\\\\n") &&
!StringRef(StandardFormatString).ends_with("\\r\\n")) {
// Use StringRef until C++20 std::string::ends_with() is available.
const auto StandardFormatStringRef = StringRef(StandardFormatString);
if (Config.AllowTrailingNewlineRemoval &&
StandardFormatStringRef.ends_with("\\n") &&
!StandardFormatStringRef.ends_with("\\\\n") &&
!StandardFormatStringRef.ends_with("\\r\\n")) {
UsePrintNewlineFunction = true;
FormatStringNeededRewriting = true;
StandardFormatString.erase(StandardFormatString.end() - 2,
Expand Down
9 changes: 8 additions & 1 deletion clang-tools-extra/clang-tidy/utils/FormatStringConverter.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,14 @@ class FormatStringConverter
public:
using ConversionSpecifier = clang::analyze_format_string::ConversionSpecifier;
using PrintfSpecifier = analyze_printf::PrintfSpecifier;

struct Configuration {
bool StrictMode = false;
bool AllowTrailingNewlineRemoval = false;
};

FormatStringConverter(ASTContext *Context, const CallExpr *Call,
unsigned FormatArgOffset, bool StrictMode,
unsigned FormatArgOffset, Configuration Config,
const LangOptions &LO);

bool canApply() const { return ConversionNotPossibleReason.empty(); }
Expand All @@ -45,6 +51,7 @@ class FormatStringConverter

private:
ASTContext *Context;
const Configuration Config;
const bool CastMismatchedIntegerTypes;
const Expr *const *Args;
const unsigned NumArgs;
Expand Down
21 changes: 20 additions & 1 deletion clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,9 @@ Improvements to clang-doc
Improvements to clang-query
---------------------------

The improvements are...
- Added the `file` command to dynamically load a list of commands and matchers
from an external file, allowing the cost of reading the compilation database
and building the AST to be imposed just once for faster prototyping.

Improvements to clang-rename
----------------------------
Expand Down Expand Up @@ -148,6 +150,15 @@ New checks
Finds initializer lists for aggregate types that could be
written as designated initializers instead.

- New :doc:`modernize-use-std-format
<clang-tidy/checks/modernize/use-std-format>` check.

Converts calls to ``absl::StrFormat``, or other functions via
configuration options, to C++20's ``std::format``, or another function
via a configuration option, modifying the format string appropriately and
removing now-unnecessary calls to ``std::string::c_str()`` and
``std::string::data()``.

- New :doc:`readability-enum-initial-value
<clang-tidy/checks/readability/enum-initial-value>` check.

Expand Down Expand Up @@ -202,6 +213,10 @@ Changes in existing checks
eliminating false positives resulting from direct usage of bitwise operators
within parentheses.

- Improved :doc:`bugprone-optional-value-conversion
<clang-tidy/checks/bugprone/optional-value-conversion>` check by eliminating
false positives resulting from use of optionals in unevaluated context.

- Improved :doc:`bugprone-suspicious-include
<clang-tidy/checks/bugprone/suspicious-include>` check by replacing the local
options `HeaderFileExtensions` and `ImplementationFileExtensions` by the
Expand Down Expand Up @@ -337,6 +352,10 @@ Changes in existing checks
<clang-tidy/checks/readability/duplicate-include>` check by excluding include
directives that form the filename using macro.

- Improved :doc:`readability-else-after-return
<clang-tidy/checks/readability/else-after-return>` check to ignore
`if consteval` statements, for which the `else` branch must not be removed.

- Improved :doc:`readability-identifier-naming
<clang-tidy/checks/readability/identifier-naming>` check in `GetConfigPerFile`
mode by resolving symbolic links to header files. Fixed handling of Hungarian
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/docs/clang-tidy/checks/list.rst
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,7 @@ Clang-Tidy Checks
:doc:`modernize-use-nullptr <modernize/use-nullptr>`, "Yes"
:doc:`modernize-use-override <modernize/use-override>`, "Yes"
:doc:`modernize-use-starts-ends-with <modernize/use-starts-ends-with>`, "Yes"
:doc:`modernize-use-std-format <modernize/use-std-format>`, "Yes"
:doc:`modernize-use-std-numbers <modernize/use-std-numbers>`, "Yes"
:doc:`modernize-use-std-print <modernize/use-std-print>`, "Yes"
:doc:`modernize-use-trailing-return-type <modernize/use-trailing-return-type>`, "Yes"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
.. title:: clang-tidy - modernize-use-std-format

modernize-use-std-format
========================

Converts calls to ``absl::StrFormat``, or other functions via
configuration options, to C++20's ``std::format``, or another function
via a configuration option, modifying the format string appropriately and
removing now-unnecessary calls to ``std::string::c_str()`` and
``std::string::data()``.

For example, it turns lines like

.. code-block:: c++

return absl::StrFormat("The %s is %3d", description.c_str(), value);

into:

.. code-block:: c++

return std::format("The {} is {:3}", description, value);

The check uses the same format-string-conversion algorithm as
`modernize-use-std-print <../modernize/use-std-print.html>`_ and its
shortcomings are described in the documentation for that check.

Options
-------

.. option:: StrictMode

When `true`, the check will add casts when converting from variadic
functions and printing signed or unsigned integer types (including
fixed-width integer types from ``<cstdint>``, ``ptrdiff_t``, ``size_t``
and ``ssize_t``) as the opposite signedness to ensure that the output
would matches that of a simple wrapper for ``std::sprintf`` that
accepted a C-style variable argument list. For example, with
`StrictMode` enabled,

.. code-block:: c++

extern std::string strprintf(const char *format, ...);
int i = -42;
unsigned int u = 0xffffffff;
return strprintf("%d %u\n", i, u);
would be converted to

.. code-block:: c++

return std::format("{} {}\n", static_cast<unsigned int>(i), static_cast<int>(u));

to ensure that the output will continue to be the unsigned representation
of -42 and the signed representation of 0xffffffff (often 4294967254
and -1 respectively). When `false` (which is the default), these casts
will not be added which may cause a change in the output. Note that this
option makes no difference for the default value of
`StrFormatLikeFunctions` since ``absl::StrFormat`` takes a function
parameter pack and is not a variadic function.

.. option:: StrFormatLikeFunctions

A semicolon-separated list of (fully qualified) function names to
replace, with the requirement that the first parameter contains the
printf-style format string and the arguments to be formatted follow
immediately afterwards. The default value for this option is
`absl::StrFormat`.

.. option:: ReplacementFormatFunction

The function that will be used to replace the function set by the
`StrFormatLikeFunctions` option rather than the default
`std::format`. It is expected that the function provides an interface
that is compatible with ``std::format``. A suitable candidate would be
`fmt::format`.

.. option:: FormatHeader

The header that must be included for the declaration of
`ReplacementFormatFunction` so that a ``#include`` directive can be added if
required. If `ReplacementFormatFunction` is `std::format` then this option will
default to ``<format>``, otherwise this option will default to nothing
and no ``#include`` directive will be added.
1 change: 1 addition & 0 deletions clang-tools-extra/test/clang-query/Inputs/empty.script
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# This file intentionally has no queries
1 change: 1 addition & 0 deletions clang-tools-extra/test/clang-query/Inputs/file.script
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
f DIRECTORY/runtime_file.script
5 changes: 5 additions & 0 deletions clang-tools-extra/test/clang-query/Inputs/runtime_file.script
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
set bind-root false

l func functionDecl(hasName("bar"))
m func.bind("f")
m varDecl().bind("v")
2 changes: 2 additions & 0 deletions clang-tools-extra/test/clang-query/errors.c
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
// RUN: not clang-query -c foo -c bar %s -- | FileCheck %s
// RUN: not clang-query -f %S/Inputs/foo.script %s -- | FileCheck %s
// RUN: not clang-query -f %S/Inputs/nonexistent.script %s -- 2>&1 | FileCheck --check-prefix=CHECK-NONEXISTENT %s
// RUN: not clang-query -c 'file %S/Inputs/nonexistent.script' %s -- 2>&1 | FileCheck --check-prefix=CHECK-NONEXISTENT-FILEQUERY %s
// RUN: not clang-query -c foo -f foo %s -- 2>&1 | FileCheck --check-prefix=CHECK-BOTH %s

// CHECK: unknown command: foo
// CHECK-NOT: unknown command: bar

// CHECK-NONEXISTENT: cannot open {{.*}}nonexistent.script
// CHECK-NONEXISTENT-FILEQUERY: cannot open {{.*}}nonexistent.script
// CHECK-BOTH: cannot specify both -c and -f
2 changes: 2 additions & 0 deletions clang-tools-extra/test/clang-query/file-empty.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// RUN: clang-query -c 'file %S/Inputs/empty.script' %s --
// COM: no output expected; nothing to CHECK
14 changes: 14 additions & 0 deletions clang-tools-extra/test/clang-query/file-query.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// RUN: rm -rf %/t
// RUN: mkdir %/t
// RUN: cp %/S/Inputs/file.script %/t/file.script
// RUN: cp %/S/Inputs/runtime_file.script %/t/runtime_file.script
// Need to embed the correct temp path in the actual JSON-RPC requests.
// RUN: sed -e "s|DIRECTORY|%/t|" %/t/file.script > %/t/file.script.temp

// RUN: clang-query -c 'file %/t/file.script.temp' %s -- | FileCheck %s

// CHECK: file-query.c:11:1: note: "f" binds here
void bar(void) {}

// CHECK: file-query.c:14:1: note: "v" binds here
int baz{1};
Original file line number Diff line number Diff line change
Expand Up @@ -210,4 +210,6 @@ void correct(std::optional<int> param)
std::optional<long>* p2 = &p;
takeOptionalValue(p2->value_or(5U));
takeOptionalRef(p2->value_or(5U));

using Type = decltype(takeOptionalValue(*param));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// RUN: %check_clang_tidy -check-suffixes=,STRICT \
// RUN: -std=c++20 %s modernize-use-std-format %t -- \
// RUN: -config="{CheckOptions: { \
// RUN: modernize-use-std-format.StrictMode: true, \
// RUN: modernize-use-std-format.StrFormatLikeFunctions: '::strprintf; mynamespace::strprintf2', \
// RUN: modernize-use-std-format.ReplacementFormatFunction: 'fmt::format', \
// RUN: modernize-use-std-format.FormatHeader: '<fmt/core.h>' \
// RUN: }}" \
// RUN: -- -isystem %clang_tidy_headers
// RUN: %check_clang_tidy -check-suffixes=,NOTSTRICT \
// RUN: -std=c++20 %s modernize-use-std-format %t -- \
// RUN: -config="{CheckOptions: { \
// RUN: modernize-use-std-format.StrFormatLikeFunctions: '::strprintf; mynamespace::strprintf2', \
// RUN: modernize-use-std-format.ReplacementFormatFunction: 'fmt::format', \
// RUN: modernize-use-std-format.FormatHeader: '<fmt/core.h>' \
// RUN: }}" \
// RUN: -- -isystem %clang_tidy_headers

#include <cstdio>
#include <string>
// CHECK-FIXES: #include <fmt/core.h>

std::string strprintf(const char *, ...);

namespace mynamespace {
std::string strprintf2(const char *, ...);
}

std::string strprintf_test(const std::string &name, double value) {
return strprintf("'%s'='%f'\n", name.c_str(), value);
// CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'fmt::format' instead of 'strprintf' [modernize-use-std-format]
// CHECK-FIXES: return fmt::format("'{}'='{:f}'\n", name, value);

return mynamespace::strprintf2("'%s'='%f'\n", name.c_str(), value);
// CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'fmt::format' instead of 'strprintf2' [modernize-use-std-format]
// CHECK-FIXES: return fmt::format("'{}'='{:f}'\n", name, value);
}

std::string StrFormat_strict_conversion() {
const unsigned char uc = 'A';
return strprintf("Integer %hhd from unsigned char\n", uc);
// CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'fmt::format' instead of 'strprintf' [modernize-use-std-format]
// CHECK-FIXES-NOTSTRICT: return fmt::format("Integer {} from unsigned char\n", uc);
// CHECK-FIXES-STRICT: return fmt::format("Integer {} from unsigned char\n", static_cast<signed char>(uc));
}

// Ensure that MatchesAnyListedNameMatcher::NameMatcher::match() can cope with a
// NamedDecl that has no name when we're trying to match unqualified_strprintf.
std::string A(const std::string &in)
{
return "_" + in;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// RUN: %check_clang_tidy %s modernize-use-std-format %t -- \
// RUN: -config="{CheckOptions: { \
// RUN: StrictMode: true, \
// RUN: modernize-use-std-format.StrFormatLikeFunctions: 'fmt::sprintf', \
// RUN: modernize-use-std-format.ReplacementFormatFunction: 'fmt::format', \
// RUN: modernize-use-std-format.FormatHeader: '<fmt/core.h>' \
// RUN: }}" \
// RUN: -- -isystem %clang_tidy_headers

// CHECK-FIXES: #include <fmt/core.h>
#include <string>

namespace fmt
{
// Use const char * for the format since the real type is hard to mock up.
template <typename... Args>
std::string sprintf(const char *format, const Args&... args);
} // namespace fmt

std::string fmt_sprintf_simple() {
return fmt::sprintf("Hello %s %d", "world", 42);
// CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'fmt::format' instead of 'sprintf' [modernize-use-std-format]
// CHECK-FIXES: fmt::format("Hello {} {}", "world", 42);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// RUN: %check_clang_tidy \
// RUN: -std=c++20 %s modernize-use-std-format %t -- \
// RUN: -config="{CheckOptions: {StrictMode: true}}" \
// RUN: -- -isystem %clang_tidy_headers
// RUN: %check_clang_tidy \
// RUN: -std=c++20 %s modernize-use-std-format %t -- \
// RUN: -config="{CheckOptions: {StrictMode: false}}" \
// RUN: -- -isystem %clang_tidy_headers
#include <string>
// CHECK-FIXES: #include <format>

namespace absl
{
// Use const char * for the format since the real type is hard to mock up.
template <typename... Args>
std::string StrFormat(const char *format, const Args&... args);
} // namespace absl

template <typename T>
struct iterator {
T *operator->();
T &operator*();
};

std::string StrFormat_simple() {
return absl::StrFormat("Hello");
// CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
// CHECK-FIXES: return std::format("Hello");
}

std::string StrFormat_complex(const char *name, double value) {
return absl::StrFormat("'%s'='%f'", name, value);
// CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
// CHECK-FIXES: return std::format("'{}'='{:f}'", name, value);
}

std::string StrFormat_integer_conversions() {
return absl::StrFormat("int:%d int:%d char:%c char:%c", 65, 'A', 66, 'B');
// CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
// CHECK-FIXES: return std::format("int:{} int:{:d} char:{:c} char:{}", 65, 'A', 66, 'B');
}

// FormatConverter is capable of removing newlines from the end of the format
// string. Ensure that isn't incorrectly happening for std::format.
std::string StrFormat_no_newline_removal() {
return absl::StrFormat("a line\n");
// CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
// CHECK-FIXES: return std::format("a line\n");
}

// FormatConverter is capable of removing newlines from the end of the format
// string. Ensure that isn't incorrectly happening for std::format.
std::string StrFormat_cstr_removal(const std::string &s1, const std::string *s2) {
return absl::StrFormat("%s %s %s %s", s1.c_str(), s1.data(), s2->c_str(), s2->data());
// CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
// CHECK-FIXES: return std::format("{} {} {} {}", s1, s1, *s2, *s2);
}

std::string StrFormat_strict_conversion() {
const unsigned char uc = 'A';
return absl::StrFormat("Integer %hhd from unsigned char\n", uc);
// CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
// CHECK-FIXES: return std::format("Integer {} from unsigned char\n", uc);
}

std::string StrFormat_field_width_and_precision() {
auto s1 = absl::StrFormat("width only:%*d width and precision:%*.*f precision only:%.*f", 3, 42, 4, 2, 3.14159265358979323846, 5, 2.718);
// CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
// CHECK-FIXES: std::format("width only:{:{}} width and precision:{:{}.{}f} precision only:{:.{}f}", 42, 3, 3.14159265358979323846, 4, 2, 2.718, 5);

auto s2 = absl::StrFormat("width and precision positional:%1$*2$.*3$f after", 3.14159265358979323846, 4, 2);
// CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
// CHECK-FIXES: std::format("width and precision positional:{0:{1}.{2}f} after", 3.14159265358979323846, 4, 2);

const int width = 10, precision = 3;
const unsigned int ui1 = 42, ui2 = 43, ui3 = 44;
auto s3 = absl::StrFormat("casts width only:%*d width and precision:%*.*d precision only:%.*d\n", 3, ui1, 4, 2, ui2, 5, ui3);
// CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
// CHECK-FIXES-NOTSTRICT: std::format("casts width only:{:{}} width and precision:{:{}.{}} precision only:{:.{}}", ui1, 3, ui2, 4, 2, ui3, 5);
// CHECK-FIXES-STRICT: std::format("casts width only:{:{}} width and precision:{:{}.{}} precision only:{:.{}}", static_cast<int>(ui1), 3, static_cast<int>(ui2), 4, 2, static_cast<int>(ui3), 5);

auto s4 = absl::StrFormat("c_str removal width only:%*s width and precision:%*.*s precision only:%.*s", 3, s1.c_str(), 4, 2, s2.c_str(), 5, s3.c_str());
// CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
// CHECK-FIXES: std::format("c_str removal width only:{:>{}} width and precision:{:>{}.{}} precision only:{:.{}}", s1, 3, s2, 4, 2, s3, 5);

const std::string *ps1 = &s1, *ps2 = &s2, *ps3 = &s3;
auto s5 = absl::StrFormat("c_str() removal pointer width only:%-*s width and precision:%-*.*s precision only:%-.*s", 3, ps1->c_str(), 4, 2, ps2->c_str(), 5, ps3->c_str());
// CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
// CHECK-FIXES: std::format("c_str() removal pointer width only:{:{}} width and precision:{:{}.{}} precision only:{:.{}}", *ps1, 3, *ps2, 4, 2, *ps3, 5);

iterator<std::string> is1, is2, is3;
auto s6 = absl::StrFormat("c_str() removal iterator width only:%-*s width and precision:%-*.*s precision only:%-.*s", 3, is1->c_str(), 4, 2, is2->c_str(), 5, is3->c_str());
// CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
// CHECK-FIXES: std::format("c_str() removal iterator width only:{:{}} width and precision:{:{}.{}} precision only:{:.{}}", *is1, 3, *is2, 4, 2, *is3, 5);

return s1 + s2 + s3 + s4 + s5 + s6;
}

std::string StrFormat_macros() {
// The function call is replaced even though it comes from a macro.
#define FORMAT absl::StrFormat
auto s1 = FORMAT("Hello %d", 42);
// CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
// CHECK-FIXES: std::format("Hello {}", 42);

// The format string is replaced even though it comes from a macro, this
// behaviour is required so that that <inttypes.h> macros are replaced.
#define FORMAT_STRING "Hello %s"
auto s2 = absl::StrFormat(FORMAT_STRING, 42);
// CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
// CHECK-FIXES: std::format("Hello {}", 42);

// Arguments that are macros aren't replaced with their value, even if they are rearranged.
#define VALUE 3.14159265358979323846
#define WIDTH 10
#define PRECISION 4
auto s3 = absl::StrFormat("Hello %*.*f", WIDTH, PRECISION, VALUE);
// CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
// CHECK-FIXES: std::format("Hello {:{}.{}f}", VALUE, WIDTH, PRECISION);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// RUN: %check_clang_tidy -std=c++23 %s readability-else-after-return %t

// Consteval if is an exception to the rule, we cannot remove the else.
void f() {
if (sizeof(int) > 4) {
return;
} else {
return;
}
// CHECK-MESSAGES: [[@LINE-3]]:5: warning: do not use 'else' after 'return'

if consteval {
return;
} else {
return;
}
}
4 changes: 3 additions & 1 deletion clang-tools-extra/unittests/clang-query/QueryParserTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ TEST_F(QueryParserTest, Comment) {
TEST_F(QueryParserTest, Complete) {
std::vector<llvm::LineEditor::Completion> Comps =
QueryParser::complete("", 0, QS);
ASSERT_EQ(8u, Comps.size());
ASSERT_EQ(9u, Comps.size());
EXPECT_EQ("help ", Comps[0].TypedText);
EXPECT_EQ("help", Comps[0].DisplayText);
EXPECT_EQ("let ", Comps[1].TypedText);
Expand All @@ -214,6 +214,8 @@ TEST_F(QueryParserTest, Complete) {
EXPECT_EQ("disable", Comps[6].DisplayText);
EXPECT_EQ("unlet ", Comps[7].TypedText);
EXPECT_EQ("unlet", Comps[7].DisplayText);
EXPECT_EQ("file ", Comps[8].TypedText);
EXPECT_EQ("file", Comps[8].DisplayText);

Comps = QueryParser::complete("set o", 5, QS);
ASSERT_EQ(1u, Comps.size());
Expand Down
3 changes: 3 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -707,6 +707,9 @@ Bug Fixes to C++ Support
initialized, rather than evaluating them as a part of the larger manifestly constant evaluated
expression.
- Fix a bug in access control checking due to dealyed checking of friend declaration. Fixes (#GH12361).
- Correctly treat the compound statement of an ``if consteval`` as an immediate context. Fixes (#GH91509).
- When partial ordering alias templates against template template parameters,
allow pack expansions when the alias has a fixed-size parameter list. Fixes (#GH62529).

Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
59 changes: 59 additions & 0 deletions clang/include/clang/AST/OpenACCClause.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
#include "clang/AST/StmtIterator.h"
#include "clang/Basic/OpenACCKinds.h"

#include <utility>

namespace clang {
/// This is the base type for all OpenACC Clauses.
class OpenACCClause {
Expand Down Expand Up @@ -75,6 +77,63 @@ class OpenACCClauseWithParams : public OpenACCClause {
}
};

using DeviceTypeArgument = std::pair<IdentifierInfo *, SourceLocation>;
/// A 'device_type' or 'dtype' clause, takes a list of either an 'asterisk' or
/// an identifier. The 'asterisk' means 'the rest'.
class OpenACCDeviceTypeClause final
: public OpenACCClauseWithParams,
public llvm::TrailingObjects<OpenACCDeviceTypeClause,
DeviceTypeArgument> {
// Data stored in trailing objects as IdentifierInfo* /SourceLocation pairs. A
// nullptr IdentifierInfo* represents an asterisk.
unsigned NumArchs;
OpenACCDeviceTypeClause(OpenACCClauseKind K, SourceLocation BeginLoc,
SourceLocation LParenLoc,
ArrayRef<DeviceTypeArgument> Archs,
SourceLocation EndLoc)
: OpenACCClauseWithParams(K, BeginLoc, LParenLoc, EndLoc),
NumArchs(Archs.size()) {
assert(
(K == OpenACCClauseKind::DeviceType || K == OpenACCClauseKind::DType) &&
"Invalid clause kind for device-type");

assert(!llvm::any_of(Archs, [](const DeviceTypeArgument &Arg) {
return Arg.second.isInvalid();
}) && "Invalid SourceLocation for an argument");

assert(
(Archs.size() == 1 || !llvm::any_of(Archs,
[](const DeviceTypeArgument &Arg) {
return Arg.first == nullptr;
})) &&
"Only a single asterisk version is permitted, and must be the "
"only one");

std::uninitialized_copy(Archs.begin(), Archs.end(),
getTrailingObjects<DeviceTypeArgument>());
}

public:
static bool classof(const OpenACCClause *C) {
return C->getClauseKind() == OpenACCClauseKind::DType ||
C->getClauseKind() == OpenACCClauseKind::DeviceType;
}
bool hasAsterisk() const {
return getArchitectures().size() > 0 &&
getArchitectures()[0].first == nullptr;
}

ArrayRef<DeviceTypeArgument> getArchitectures() const {
return ArrayRef<DeviceTypeArgument>(
getTrailingObjects<DeviceTypeArgument>(), NumArchs);
}

static OpenACCDeviceTypeClause *
Create(const ASTContext &C, OpenACCClauseKind K, SourceLocation BeginLoc,
SourceLocation LParenLoc, ArrayRef<DeviceTypeArgument> Archs,
SourceLocation EndLoc);
};

/// A 'default' clause, has the optional 'none' or 'present' argument.
class OpenACCDefaultClause : public OpenACCClauseWithParams {
friend class ASTReaderStmt;
Expand Down
5 changes: 5 additions & 0 deletions clang/include/clang/Basic/DiagnosticParseKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -1438,13 +1438,18 @@ def err_omp_decl_in_declare_simd_variant : Error<
def err_omp_sink_and_source_iteration_not_allowd: Error<" '%0 %select{sink:|source:}1' must be with '%select{omp_cur_iteration - 1|omp_cur_iteration}1'">;
def err_omp_unknown_map_type : Error<
"incorrect map type, expected one of 'to', 'from', 'tofrom', 'alloc', 'release', or 'delete'">;
def err_omp_more_one_map_type : Error<"map type is already specified">;
def note_previous_map_type_specified_here
: Note<"map type '%0' is previous specified here">;
def err_omp_unknown_map_type_modifier : Error<
"incorrect map type modifier, expected one of: 'always', 'close', 'mapper'"
"%select{|, 'present'|, 'present', 'iterator'}0%select{|, 'ompx_hold'}1">;
def err_omp_map_type_missing : Error<
"missing map type">;
def err_omp_map_type_modifier_missing : Error<
"missing map type modifier">;
def err_omp_map_modifier_specification_list : Error<
"empty modifier-specification-list is not allowed">;
def err_omp_declare_simd_inbranch_notinbranch : Error<
"unexpected '%0' clause, '%1' is specified already">;
def err_omp_expected_clause_argument
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -12344,4 +12344,8 @@ def warn_acc_deprecated_alias_name
def err_acc_var_not_pointer_type
: Error<"expected pointer in '%0' clause, type is %1">;
def note_acc_expected_pointer_var : Note<"expected variable of pointer type">;
def err_acc_clause_after_device_type
: Error<"OpenACC clause '%0' may not follow a '%1' clause in a "
"compute construct">;

} // end of sema component.
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/OpenACCClauses.def
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ CLAUSE_ALIAS(PCreate, Create)
CLAUSE_ALIAS(PresentOrCreate, Create)
VISIT_CLAUSE(Default)
VISIT_CLAUSE(DevicePtr)
VISIT_CLAUSE(DeviceType)
CLAUSE_ALIAS(DType, DeviceType)
VISIT_CLAUSE(FirstPrivate)
VISIT_CLAUSE(If)
VISIT_CLAUSE(NoCreate)
Expand Down
4 changes: 0 additions & 4 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -5457,10 +5457,6 @@ def pg : Flag<["-"], "pg">, HelpText<"Enable mcount instrumentation">,
MarshallingInfoFlag<CodeGenOpts<"InstrumentForProfiling">>;
def pipe : Flag<["-", "--"], "pipe">,
HelpText<"Use pipes between commands, when possible">;
// Facebook T92898286
def post_link_optimize : Flag<["--"], "post-link-optimize">,
HelpText<"Apply post-link optimizations using BOLT">;
// End Facebook T92898286
def prebind__all__twolevel__modules : Flag<["-"], "prebind_all_twolevel_modules">;
def prebind : Flag<["-"], "prebind">;
def preload : Flag<["-"], "preload">;
Expand Down
7 changes: 5 additions & 2 deletions clang/include/clang/ExtractAPI/API.h
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,8 @@ struct APIRecord {

AccessControl Access;

RecordKind KindForDisplay;

private:
const RecordKind Kind;
friend class RecordContext;
Expand All @@ -277,6 +279,7 @@ struct APIRecord {
APIRecord *getNextInContext() const { return NextInContext; }

RecordKind getKind() const { return Kind; }
RecordKind getKindForDisplay() const { return KindForDisplay; }

static APIRecord *castFromRecordContext(const RecordContext *Ctx);
static RecordContext *castToRecordContext(const APIRecord *Record);
Expand All @@ -293,10 +296,10 @@ struct APIRecord {
Availability(std::move(Availability)), Linkage(Linkage),
Comment(Comment), Declaration(Declaration), SubHeading(SubHeading),
IsFromSystemHeader(IsFromSystemHeader), Access(std::move(Access)),
Kind(Kind) {}
KindForDisplay(Kind), Kind(Kind) {}

APIRecord(RecordKind Kind, StringRef USR, StringRef Name)
: USR(USR), Name(Name), Kind(Kind) {}
: USR(USR), Name(Name), KindForDisplay(Kind), Kind(Kind) {}

// Pure virtual destructor to make APIRecord abstract
virtual ~APIRecord() = 0;
Expand Down
25 changes: 15 additions & 10 deletions clang/include/clang/ExtractAPI/ExtractAPIVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,15 @@ class ExtractAPIVisitorBase : public RecursiveASTVisitor<Derived> {
return Bases;
}

APIRecord::RecordKind getKindForDisplay(const CXXRecordDecl *Decl) {
if (Decl->isUnion())
return APIRecord::RK_Union;
if (Decl->isStruct())
return APIRecord::RK_Struct;

return APIRecord::RK_CXXClass;
}

StringRef getOwningModuleName(const Decl &D) {
if (auto *OwningModule = D.getImportedOwningModule())
return OwningModule->Name;
Expand Down Expand Up @@ -599,13 +608,6 @@ bool ExtractAPIVisitorBase<Derived>::VisitCXXRecordDecl(
DeclarationFragments SubHeading =
DeclarationFragmentsBuilder::getSubHeading(Decl);

APIRecord::RecordKind Kind;
if (Decl->isUnion())
Kind = APIRecord::RecordKind::RK_Union;
else if (Decl->isStruct())
Kind = APIRecord::RecordKind::RK_Struct;
else
Kind = APIRecord::RecordKind::RK_CXXClass;
auto Access = DeclarationFragmentsBuilder::getAccessControl(Decl);

CXXClassRecord *Record;
Expand All @@ -619,13 +621,15 @@ bool ExtractAPIVisitorBase<Derived>::VisitCXXRecordDecl(
AvailabilityInfo::createFromDecl(Decl), Comment, Declaration,
SubHeading, Template(Decl->getDescribedClassTemplate()), Access,
isInSystemHeader(Decl));
} else
} else {
Record = API.createRecord<CXXClassRecord>(
USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
AvailabilityInfo::createFromDecl(Decl), Comment, Declaration,
SubHeading, Kind, Access, isInSystemHeader(Decl),
isEmbeddedInVarDeclarator(*Decl));
SubHeading, APIRecord::RecordKind::RK_CXXClass, Access,
isInSystemHeader(Decl), isEmbeddedInVarDeclarator(*Decl));
}

Record->KindForDisplay = getKindForDisplay(Decl);
Record->Bases = getBases(Decl);

return true;
Expand Down Expand Up @@ -849,6 +853,7 @@ bool ExtractAPIVisitorBase<Derived>::
Template(Decl), DeclarationFragmentsBuilder::getAccessControl(Decl),
isInSystemHeader(Decl));

CTPSR->KindForDisplay = getKindForDisplay(Decl);
CTPSR->Bases = getBases(Decl);

return true;
Expand Down
31 changes: 25 additions & 6 deletions clang/include/clang/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "clang/Lex/CodeCompletionHandler.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Sema/Sema.h"
#include "clang/Sema/SemaObjC.h"
#include "clang/Sema/SemaOpenMP.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Frontend/OpenMP/OMPContext.h"
Expand Down Expand Up @@ -445,8 +446,8 @@ class Parser : public CodeCompletionHandler {
/// True if we are within an Objective-C container while parsing C-like decls.
///
/// This is necessary because Sema thinks we have left the container
/// to parse the C-like decls, meaning Actions.getObjCDeclContext() will
/// be NULL.
/// to parse the C-like decls, meaning Actions.ObjC().getObjCDeclContext()
/// will be NULL.
bool ParsingInObjCContainer;

/// Whether to skip parsing of function bodies.
Expand Down Expand Up @@ -497,7 +498,7 @@ class Parser : public CodeCompletionHandler {
}

ObjCContainerDecl *getObjCDeclContext() const {
return Actions.getObjCDeclContext();
return Actions.ObjC().getObjCDeclContext();
}

// Type forwarding. All of these are statically 'void*', but they may all be
Expand Down Expand Up @@ -1083,11 +1084,11 @@ class Parser : public CodeCompletionHandler {
: P(p), DC(p.getObjCDeclContext()),
WithinObjCContainer(P.ParsingInObjCContainer, DC != nullptr) {
if (DC)
P.Actions.ActOnObjCTemporaryExitContainerContext(DC);
P.Actions.ObjC().ActOnObjCTemporaryExitContainerContext(DC);
}
~ObjCDeclContextSwitch() {
if (DC)
P.Actions.ActOnObjCReenterContainerContext(DC);
P.Actions.ObjC().ActOnObjCReenterContainerContext(DC);
}
};

Expand Down Expand Up @@ -3553,6 +3554,23 @@ class Parser : public CodeCompletionHandler {
OMPClause *ParseOpenMPVarListClause(OpenMPDirectiveKind DKind,
OpenMPClauseKind Kind, bool ParseOnly);

/// Parses a clause consisting of a list of expressions.
///
/// \param Kind The clause to parse.
/// \param ClauseNameLoc [out] The location of the clause name.
/// \param OpenLoc [out] The location of '('.
/// \param CloseLoc [out] The location of ')'.
/// \param Exprs [out] The parsed expressions.
/// \param ReqIntConst If true, each expression must be an integer constant.
///
/// \return Whether the clause was parsed successfully.
bool ParseOpenMPExprListClause(OpenMPClauseKind Kind,
SourceLocation &ClauseNameLoc,
SourceLocation &OpenLoc,
SourceLocation &CloseLoc,
SmallVectorImpl<Expr *> &Exprs,
bool ReqIntConst = false);

/// Parses and creates OpenMP 5.0 iterators expression:
/// <iterators> = 'iterator' '(' { [ <iterator-type> ] identifier =
/// <range-specification> }+ ')'
Expand Down Expand Up @@ -3703,7 +3721,8 @@ class Parser : public CodeCompletionHandler {
SourceLocation Loc,
llvm::SmallVectorImpl<Expr *> &IntExprs);
/// Parses the 'device-type-list', which is a list of identifiers.
bool ParseOpenACCDeviceTypeList();
bool ParseOpenACCDeviceTypeList(
llvm::SmallVector<std::pair<IdentifierInfo *, SourceLocation>> &Archs);
/// Parses the 'async-argument', which is an integral value with two
/// 'special' values that are likely negative (but come from Macros).
OpenACCIntExprParseResult ParseOpenACCAsyncArgument(OpenACCDirectiveKind DK,
Expand Down
1,010 changes: 43 additions & 967 deletions clang/include/clang/Sema/Sema.h

Large diffs are not rendered by default.

1,014 changes: 1,014 additions & 0 deletions clang/include/clang/Sema/SemaObjC.h

Large diffs are not rendered by default.

23 changes: 22 additions & 1 deletion clang/include/clang/Sema/SemaOpenACC.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ class OpenACCClause;

class SemaOpenACC : public SemaBase {
public:
// Redeclaration of the version in OpenACCClause.h.
using DeviceTypeArgument = std::pair<IdentifierInfo *, SourceLocation>;

/// A type to represent all the data for an OpenACC Clause that has been
/// parsed, but not yet created/semantically analyzed. This is effectively a
/// discriminated union on the 'Clause Kind', with all of the individual
Expand Down Expand Up @@ -60,8 +63,12 @@ class SemaOpenACC : public SemaBase {
SmallVector<Expr *> QueueIdExprs;
};

struct DeviceTypeDetails {
SmallVector<DeviceTypeArgument> Archs;
};

std::variant<std::monostate, DefaultDetails, ConditionDetails,
IntExprDetails, VarListDetails, WaitDetails>
IntExprDetails, VarListDetails, WaitDetails, DeviceTypeDetails>
Details = std::monostate{};

public:
Expand Down Expand Up @@ -209,6 +216,13 @@ class SemaOpenACC : public SemaBase {
return std::get<VarListDetails>(Details).IsZero;
}

ArrayRef<DeviceTypeArgument> getDeviceTypeArchitectures() const {
assert((ClauseKind == OpenACCClauseKind::DeviceType ||
ClauseKind == OpenACCClauseKind::DType) &&
"Only 'device_type'/'dtype' has a device-type-arg list");
return std::get<DeviceTypeDetails>(Details).Archs;
}

void setLParenLoc(SourceLocation EndLoc) { LParenLoc = EndLoc; }
void setEndLoc(SourceLocation EndLoc) { ClauseRange.setEnd(EndLoc); }

Expand Down Expand Up @@ -326,6 +340,13 @@ class SemaOpenACC : public SemaBase {
"Parsed clause kind does not have a wait-details");
Details = WaitDetails{DevNum, QueuesLoc, std::move(IntExprs)};
}

void setDeviceTypeDetails(llvm::SmallVector<DeviceTypeArgument> &&Archs) {
assert((ClauseKind == OpenACCClauseKind::DeviceType ||
ClauseKind == OpenACCClauseKind::DType) &&
"Only 'device_type'/'dtype' has a device-type-arg list");
Details = DeviceTypeDetails{std::move(Archs)};
}
};

SemaOpenACC(Sema &S);
Expand Down
32 changes: 16 additions & 16 deletions clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def
Original file line number Diff line number Diff line change
Expand Up @@ -413,22 +413,6 @@ ANALYZER_OPTION(
"analysis is too low, it is meaningful to provide a minimum value that "
"serves as an upper bound instead.", 10000)

ANALYZER_OPTION(
StringRef, CTUPhase1InliningMode, "ctu-phase1-inlining",
"Controls which functions will be inlined during the first phase of the ctu "
"analysis. "
"If the value is set to 'all' then all foreign functions are inlinied "
"immediately during the first phase, thus rendering the second phase a noop. "
"The 'ctu-max-nodes-*' budge has no effect in this case. "
"If the value is 'small' then only functions with a linear CFG and with a "
"limited number of statements would be inlined during the first phase. The "
"long and/or nontrivial functions are handled in the second phase and are "
"controlled by the 'ctu-max-nodes-*' budge. "
"The value 'none' means that all foreign functions are inlined only in the "
"second phase, 'ctu-max-nodes-*' budge limits the second phase. "
"Value: \"none\", \"small\", \"all\".",
"small")

ANALYZER_OPTION(
unsigned, RegionStoreSmallStructLimit, "region-store-small-struct-limit",
"The largest number of fields a struct can have and still be considered "
Expand Down Expand Up @@ -478,6 +462,22 @@ ANALYZER_OPTION(
"where to look for those alternative implementations (called models).",
"")

ANALYZER_OPTION(
StringRef, CTUPhase1InliningMode, "ctu-phase1-inlining",
"Controls which functions will be inlined during the first phase of the ctu "
"analysis. "
"If the value is set to 'all' then all foreign functions are inlinied "
"immediately during the first phase, thus rendering the second phase a noop. "
"The 'ctu-max-nodes-*' budge has no effect in this case. "
"If the value is 'small' then only functions with a linear CFG and with a "
"limited number of statements would be inlined during the first phase. The "
"long and/or nontrivial functions are handled in the second phase and are "
"controlled by the 'ctu-max-nodes-*' budge. "
"The value 'none' means that all foreign functions are inlined only in the "
"second phase, 'ctu-max-nodes-*' budge limits the second phase. "
"Value: \"none\", \"small\", \"all\".",
"small")

ANALYZER_OPTION(
StringRef, CXXMemberInliningMode, "c++-inlining",
"Controls which C++ member functions will be considered for inlining. "
Expand Down
5 changes: 3 additions & 2 deletions clang/lib/ARCMigrate/Transforms.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "clang/Lex/Lexer.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Sema/Sema.h"
#include "clang/Sema/SemaObjC.h"

using namespace clang;
using namespace arcmt;
Expand All @@ -26,8 +27,8 @@ ASTTraverser::~ASTTraverser() { }

bool MigrationPass::CFBridgingFunctionsDefined() {
if (!EnableCFBridgeFns)
EnableCFBridgeFns = SemaRef.isKnownName("CFBridgingRetain") &&
SemaRef.isKnownName("CFBridgingRelease");
EnableCFBridgeFns = SemaRef.ObjC().isKnownName("CFBridgingRetain") &&
SemaRef.ObjC().isKnownName("CFBridgingRelease");
return *EnableCFBridgeFns;
}

Expand Down
7 changes: 5 additions & 2 deletions clang/lib/AST/Interp/ByteCodeExprGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2159,7 +2159,9 @@ bool ByteCodeExprGen<Emitter>::VisitCXXConstructExpr(
if (T->isArrayType()) {
const ConstantArrayType *CAT =
Ctx.getASTContext().getAsConstantArrayType(E->getType());
assert(CAT);
if (!CAT)
return false;

size_t NumElems = CAT->getZExtSize();
const Function *Func = getFunction(E->getConstructor());
if (!Func || !Func->isConstexpr())
Expand Down Expand Up @@ -2861,7 +2863,8 @@ bool ByteCodeExprGen<Emitter>::visitExpr(const Expr *E) {
return this->emitRetValue(E) && RootScope.destroyLocals();
}

return RootScope.destroyLocals();
RootScope.destroyLocals();
return false;
}

/// Toplevel visitDecl().
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/AST/Interp/Program.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,8 @@ Descriptor *Program::createDescriptor(const DeclTy &D, const Type *Ty,

// Array of unknown bounds - cannot be accessed and pointer arithmetic
// is forbidden on pointers to such objects.
if (isa<IncompleteArrayType>(ArrayType)) {
if (isa<IncompleteArrayType>(ArrayType) ||
isa<VariableArrayType>(ArrayType)) {
if (std::optional<PrimType> T = Ctx.classify(ElemTy)) {
return allocateDescriptor(D, *T, MDSize, IsTemporary,
Descriptor::UnknownSize{});
Expand Down
28 changes: 27 additions & 1 deletion clang/lib/AST/OpenACCClause.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
using namespace clang;

bool OpenACCClauseWithParams::classof(const OpenACCClause *C) {
return OpenACCClauseWithCondition::classof(C) ||
return OpenACCDeviceTypeClause::classof(C) ||
OpenACCClauseWithCondition::classof(C) ||
OpenACCClauseWithExprs::classof(C);
}
bool OpenACCClauseWithExprs::classof(const OpenACCClause *C) {
Expand Down Expand Up @@ -298,6 +299,17 @@ OpenACCCreateClause::Create(const ASTContext &C, OpenACCClauseKind Spelling,
VarList, EndLoc);
}

OpenACCDeviceTypeClause *OpenACCDeviceTypeClause::Create(
const ASTContext &C, OpenACCClauseKind K, SourceLocation BeginLoc,
SourceLocation LParenLoc, ArrayRef<DeviceTypeArgument> Archs,
SourceLocation EndLoc) {
void *Mem =
C.Allocate(OpenACCDeviceTypeClause::totalSizeToAlloc<DeviceTypeArgument>(
Archs.size()));
return new (Mem)
OpenACCDeviceTypeClause(K, BeginLoc, LParenLoc, Archs, EndLoc);
}

//===----------------------------------------------------------------------===//
// OpenACC clauses printing methods
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -451,3 +463,17 @@ void OpenACCClausePrinter::VisitWaitClause(const OpenACCWaitClause &C) {
OS << ")";
}
}

void OpenACCClausePrinter::VisitDeviceTypeClause(
const OpenACCDeviceTypeClause &C) {
OS << C.getClauseKind();
OS << "(";
llvm::interleaveComma(C.getArchitectures(), OS,
[&](const DeviceTypeArgument &Arch) {
if (Arch.first == nullptr)
OS << "*";
else
OS << Arch.first;
});
OS << ")";
}
3 changes: 3 additions & 0 deletions clang/lib/AST/StmtProfile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2585,6 +2585,9 @@ void OpenACCClauseProfiler::VisitWaitClause(const OpenACCWaitClause &Clause) {
for (auto *E : Clause.getQueueIdExprs())
Profiler.VisitStmt(E);
}
/// Nothing to do here, there are no sub-statements.
void OpenACCClauseProfiler::VisitDeviceTypeClause(
const OpenACCDeviceTypeClause &Clause) {}
} // namespace

void StmtProfiler::VisitOpenACCComputeConstruct(
Expand Down
13 changes: 13 additions & 0 deletions clang/lib/AST/TextNodeDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,19 @@ void TextNodeDumper::Visit(const OpenACCClause *C) {
if (cast<OpenACCWaitClause>(C)->hasQueuesTag())
OS << " has queues tag";
break;
case OpenACCClauseKind::DeviceType:
case OpenACCClauseKind::DType:
OS << "(";
llvm::interleaveComma(
cast<OpenACCDeviceTypeClause>(C)->getArchitectures(), OS,
[&](const DeviceTypeArgument &Arch) {
if (Arch.first == nullptr)
OS << "*";
else
OS << Arch.first->getName();
});
OS << ")";
break;
default:
// Nothing to do here.
break;
Expand Down
14 changes: 7 additions & 7 deletions clang/lib/Driver/ToolChains/Clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7249,15 +7249,15 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
Args.addOptOutFlag(CmdArgs, options::OPT_fassume_unique_vtables,
options::OPT_fno_assume_unique_vtables);

// -fno-relaxed-template-template-args is deprecated.
if (Arg *A = Args.getLastArg(options::OPT_frelaxed_template_template_args,
options::OPT_fno_relaxed_template_template_args);
A &&
A->getOption().matches(options::OPT_fno_relaxed_template_template_args))
// -frelaxed-template-template-args is deprecated.
if (Arg *A =
Args.getLastArg(options::OPT_frelaxed_template_template_args,
options::OPT_fno_relaxed_template_template_args)) {
D.Diag(diag::warn_drv_deprecated_arg)
<< A->getAsString(Args) << /*hasReplacement=*/false;
else
CmdArgs.push_back("-fno-relaxed-template-template-args");
if (A->getOption().matches(options::OPT_fno_relaxed_template_template_args))
CmdArgs.push_back("-fno-relaxed-template-template-args");
}

// -fsized-deallocation is off by default, as it is an ABI-breaking change for
// most platforms.
Expand Down
29 changes: 0 additions & 29 deletions clang/lib/Driver/ToolChains/Gnu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -672,41 +672,12 @@ void tools::gnutools::Linker::ConstructJob(Compilation &C, const JobAction &JA,
}
}

// Facebook T92898286
if (Args.hasArg(options::OPT_post_link_optimize))
CmdArgs.push_back("-q");
// End Facebook T92898286

Args.AddAllArgs(CmdArgs, options::OPT_T);

const char *Exec = Args.MakeArgString(ToolChain.GetLinkerPath());
C.addCommand(std::make_unique<Command>(JA, *this,
ResponseFileSupport::AtFileCurCP(),
Exec, CmdArgs, Inputs, Output));
// Facebook T92898286
if (!Args.hasArg(options::OPT_post_link_optimize) || !Output.isFilename())
return;

const char *MvExec = Args.MakeArgString(ToolChain.GetProgramPath("mv"));
ArgStringList MoveCmdArgs;
MoveCmdArgs.push_back(Output.getFilename());
const char *PreBoltBin =
Args.MakeArgString(Twine(Output.getFilename()) + ".pre-bolt");
MoveCmdArgs.push_back(PreBoltBin);
C.addCommand(std::make_unique<Command>(JA, *this, ResponseFileSupport::None(),
MvExec, MoveCmdArgs, std::nullopt));

ArgStringList BoltCmdArgs;
const char *BoltExec =
Args.MakeArgString(ToolChain.GetProgramPath("llvm-bolt"));
BoltCmdArgs.push_back(PreBoltBin);
BoltCmdArgs.push_back("-reorder-blocks=reverse");
BoltCmdArgs.push_back("-update-debug-sections");
BoltCmdArgs.push_back("-o");
BoltCmdArgs.push_back(Output.getFilename());
C.addCommand(std::make_unique<Command>(JA, *this, ResponseFileSupport::None(),
BoltExec, BoltCmdArgs, std::nullopt));
// End Facebook T92898286
}

void tools::gnutools::Assembler::ConstructJob(Compilation &C,
Expand Down
6 changes: 3 additions & 3 deletions clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,7 @@ Object serializeSymbolKind(APIRecord::RecordKind RK, Language Lang) {
/// which is prefixed by the source language name, useful for tooling to parse
/// the kind, and a \c displayName for rendering human-readable names.
Object serializeSymbolKind(const APIRecord &Record, Language Lang) {
return serializeSymbolKind(Record.getKind(), Lang);
return serializeSymbolKind(Record.KindForDisplay, Lang);
}

/// Serialize the function signature field, as specified by the
Expand Down Expand Up @@ -591,8 +591,8 @@ Array generateParentContexts(const SmallVectorImpl<SymbolReference> &Parents,
Elem["usr"] = Parent.USR;
Elem["name"] = Parent.Name;
if (Parent.Record)
Elem["kind"] =
serializeSymbolKind(Parent.Record->getKind(), Lang)["identifier"];
Elem["kind"] = serializeSymbolKind(Parent.Record->KindForDisplay,
Lang)["identifier"];
else
Elem["kind"] =
serializeSymbolKind(APIRecord::RK_Unknown, Lang)["identifier"];
Expand Down
7 changes: 4 additions & 3 deletions clang/lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "clang/Sema/Scope.h"
#include "clang/Sema/SemaCUDA.h"
#include "clang/Sema/SemaDiagnostic.h"
#include "clang/Sema/SemaObjC.h"
#include "clang/Sema/SemaOpenMP.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/SmallString.h"
Expand Down Expand Up @@ -3945,7 +3946,7 @@ void Parser::ParseDeclarationSpecifiers(

if (DSContext == DeclSpecContext::DSC_objc_method_result &&
isObjCInstancetype()) {
ParsedType TypeRep = Actions.ActOnObjCInstanceType(Loc);
ParsedType TypeRep = Actions.ObjC().ActOnObjCInstanceType(Loc);
assert(TypeRep);
isInvalid = DS.SetTypeSpecType(DeclSpec::TST_typename, Loc, PrevSpec,
DiagID, TypeRep, Policy);
Expand Down Expand Up @@ -5002,8 +5003,8 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc,
continue;
}
SmallVector<Decl *, 16> Fields;
Actions.ActOnDefs(getCurScope(), TagDecl, Tok.getLocation(),
Tok.getIdentifierInfo(), Fields);
Actions.ObjC().ActOnDefs(getCurScope(), TagDecl, Tok.getLocation(),
Tok.getIdentifierInfo(), Fields);
ConsumeToken();
ExpectAndConsume(tok::r_paren);
}
Expand Down
17 changes: 9 additions & 8 deletions clang/lib/Parse/ParseExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include "clang/Sema/ParsedTemplate.h"
#include "clang/Sema/Scope.h"
#include "clang/Sema/SemaCUDA.h"
#include "clang/Sema/SemaObjC.h"
#include "clang/Sema/SemaOpenACC.h"
#include "clang/Sema/SemaOpenMP.h"
#include "clang/Sema/SemaSYCL.h"
Expand Down Expand Up @@ -1227,8 +1228,8 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind,
IdentifierInfo &PropertyName = *Tok.getIdentifierInfo();
SourceLocation PropertyLoc = ConsumeToken();

Res = Actions.ActOnClassPropertyRefExpr(II, PropertyName,
ILoc, PropertyLoc);
Res = Actions.ObjC().ActOnClassPropertyRefExpr(II, PropertyName, ILoc,
PropertyLoc);
break;
}

Expand Down Expand Up @@ -3093,9 +3094,9 @@ Parser::ParseParenExpression(ParenParseOption &ExprType, bool stopIfCastExpr,
if (Ty.isInvalid() || SubExpr.isInvalid())
return ExprError();

return Actions.ActOnObjCBridgedCast(getCurScope(), OpenLoc, Kind,
BridgeKeywordLoc, Ty.get(),
RParenLoc, SubExpr.get());
return Actions.ObjC().ActOnObjCBridgedCast(getCurScope(), OpenLoc, Kind,
BridgeKeywordLoc, Ty.get(),
RParenLoc, SubExpr.get());
} else if (ExprType >= CompoundLiteral &&
isTypeIdInParens(isAmbiguousTypeId)) {

Expand Down Expand Up @@ -3811,7 +3812,7 @@ ExprResult Parser::ParseBlockLiteralExpression() {
/// '__objc_no'
ExprResult Parser::ParseObjCBoolLiteral() {
tok::TokenKind Kind = Tok.getKind();
return Actions.ActOnObjCBoolLiteral(ConsumeToken(), Kind);
return Actions.ObjC().ActOnObjCBoolLiteral(ConsumeToken(), Kind);
}

/// Validate availability spec list, emitting diagnostics if necessary. Returns
Expand Down Expand Up @@ -3932,6 +3933,6 @@ ExprResult Parser::ParseAvailabilityCheckExpr(SourceLocation BeginLoc) {
if (Parens.consumeClose())
return ExprError();

return Actions.ActOnObjCAvailabilityCheckExpr(AvailSpecs, BeginLoc,
Parens.getCloseLocation());
return Actions.ObjC().ActOnObjCAvailabilityCheckExpr(
AvailSpecs, BeginLoc, Parens.getCloseLocation());
}
9 changes: 5 additions & 4 deletions clang/lib/Parse/ParseInit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "clang/Sema/EnterExpressionEvaluationContext.h"
#include "clang/Sema/Ownership.h"
#include "clang/Sema/Scope.h"
#include "clang/Sema/SemaObjC.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
using namespace clang;
Expand Down Expand Up @@ -290,15 +291,15 @@ ExprResult Parser::ParseInitializerWithPotentialDesignator(
// Three cases. This is a message send to a type: [type foo]
// This is a message send to super: [super foo]
// This is a message sent to an expr: [super.bar foo]
switch (Actions.getObjCMessageKind(
switch (Actions.ObjC().getObjCMessageKind(
getCurScope(), II, IILoc, II == Ident_super,
NextToken().is(tok::period), ReceiverType)) {
case Sema::ObjCSuperMessage:
case SemaObjC::ObjCSuperMessage:
CheckArrayDesignatorSyntax(*this, StartLoc, Desig);
return ParseAssignmentExprWithObjCMessageExprStart(
StartLoc, ConsumeToken(), nullptr, nullptr);

case Sema::ObjCClassMessage:
case SemaObjC::ObjCClassMessage:
CheckArrayDesignatorSyntax(*this, StartLoc, Desig);
ConsumeToken(); // the identifier
if (!ReceiverType) {
Expand Down Expand Up @@ -326,7 +327,7 @@ ExprResult Parser::ParseInitializerWithPotentialDesignator(
ReceiverType,
nullptr);

case Sema::ObjCInstanceMessage:
case SemaObjC::ObjCInstanceMessage:
// Fall through; we'll just parse the expression and
// (possibly) treat this like an Objective-C message send
// later.
Expand Down
255 changes: 118 additions & 137 deletions clang/lib/Parse/ParseObjc.cpp

Large diffs are not rendered by default.

23 changes: 15 additions & 8 deletions clang/lib/Parse/ParseOpenACC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -711,24 +711,27 @@ bool Parser::ParseOpenACCIntExprList(OpenACCDirectiveKind DK,
/// device_type( device-type-list )
///
/// The device_type clause may be abbreviated to dtype.
bool Parser::ParseOpenACCDeviceTypeList() {
bool Parser::ParseOpenACCDeviceTypeList(
llvm::SmallVector<std::pair<IdentifierInfo *, SourceLocation>> &Archs) {

if (expectIdentifierOrKeyword(*this)) {
SkipUntil(tok::r_paren, tok::annot_pragma_openacc_end,
Parser::StopBeforeMatch);
return false;
return true;
}
ConsumeToken();
IdentifierInfo *Ident = getCurToken().getIdentifierInfo();
Archs.emplace_back(Ident, ConsumeToken());

while (!getCurToken().isOneOf(tok::r_paren, tok::annot_pragma_openacc_end)) {
ExpectAndConsume(tok::comma);

if (expectIdentifierOrKeyword(*this)) {
SkipUntil(tok::r_paren, tok::annot_pragma_openacc_end,
Parser::StopBeforeMatch);
return false;
return true;
}
ConsumeToken();
Ident = getCurToken().getIdentifierInfo();
Archs.emplace_back(Ident, ConsumeToken());
}
return false;
}
Expand Down Expand Up @@ -1021,16 +1024,20 @@ Parser::OpenACCClauseParseResult Parser::ParseOpenACCClauseParams(
break;
}
case OpenACCClauseKind::DType:
case OpenACCClauseKind::DeviceType:
case OpenACCClauseKind::DeviceType: {
llvm::SmallVector<std::pair<IdentifierInfo *, SourceLocation>> Archs;
if (getCurToken().is(tok::star)) {
// FIXME: We want to mark that this is an 'everything else' type of
// device_type in Sema.
ConsumeToken();
} else if (ParseOpenACCDeviceTypeList()) {
ParsedClause.setDeviceTypeDetails({{nullptr, ConsumeToken()}});
} else if (!ParseOpenACCDeviceTypeList(Archs)) {
ParsedClause.setDeviceTypeDetails(std::move(Archs));
} else {
Parens.skipToEnd();
return OpenACCCanContinue();
}
break;
}
case OpenACCClauseKind::Tile:
if (ParseOpenACCSizeExprList()) {
Parens.skipToEnd();
Expand Down
116 changes: 83 additions & 33 deletions clang/lib/Parse/ParseOpenMP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3107,34 +3107,14 @@ bool Parser::ParseOpenMPSimpleVarList(
}

OMPClause *Parser::ParseOpenMPSizesClause() {
SourceLocation ClauseNameLoc = ConsumeToken();
SourceLocation ClauseNameLoc, OpenLoc, CloseLoc;
SmallVector<Expr *, 4> ValExprs;

BalancedDelimiterTracker T(*this, tok::l_paren, tok::annot_pragma_openmp_end);
if (T.consumeOpen()) {
Diag(Tok, diag::err_expected) << tok::l_paren;
if (ParseOpenMPExprListClause(OMPC_sizes, ClauseNameLoc, OpenLoc, CloseLoc,
ValExprs))
return nullptr;
}

while (true) {
ExprResult Val = ParseConstantExpression();
if (!Val.isUsable()) {
T.skipToEnd();
return nullptr;
}

ValExprs.push_back(Val.get());

if (Tok.is(tok::r_paren) || Tok.is(tok::annot_pragma_openmp_end))
break;

ExpectAndConsume(tok::comma);
}

T.consumeClose();

return Actions.OpenMP().ActOnOpenMPSizesClause(
ValExprs, ClauseNameLoc, T.getOpenLocation(), T.getCloseLocation());
return Actions.OpenMP().ActOnOpenMPSizesClause(ValExprs, ClauseNameLoc,
OpenLoc, CloseLoc);
}

OMPClause *Parser::ParseOpenMPUsesAllocatorClause(OpenMPDirectiveKind DKind) {
Expand Down Expand Up @@ -4228,13 +4208,20 @@ bool Parser::parseMapperModifier(SemaOpenMP::OpenMPVarListDataTy &Data) {
return T.consumeClose();
}

static OpenMPMapClauseKind isMapType(Parser &P);

/// Parse map-type-modifiers in map clause.
/// map([ [map-type-modifier[,] [map-type-modifier[,] ...] map-type : ] list)
/// map([ [map-type-modifier[,] [map-type-modifier[,] ...] [map-type] : ] list)
/// where, map-type-modifier ::= always | close | mapper(mapper-identifier) |
/// present
/// where, map-type ::= alloc | delete | from | release | to | tofrom
bool Parser::parseMapTypeModifiers(SemaOpenMP::OpenMPVarListDataTy &Data) {
bool HasMapType = false;
SourceLocation PreMapLoc = Tok.getLocation();
StringRef PreMapName = "";
while (getCurToken().isNot(tok::colon)) {
OpenMPMapModifierKind TypeModifier = isMapModifier(*this);
OpenMPMapClauseKind MapKind = isMapType(*this);
if (TypeModifier == OMPC_MAP_MODIFIER_always ||
TypeModifier == OMPC_MAP_MODIFIER_close ||
TypeModifier == OMPC_MAP_MODIFIER_present ||
Expand All @@ -4257,6 +4244,19 @@ bool Parser::parseMapTypeModifiers(SemaOpenMP::OpenMPVarListDataTy &Data) {
Diag(Data.MapTypeModifiersLoc.back(), diag::err_omp_missing_comma)
<< "map type modifier";

} else if (getLangOpts().OpenMP >= 60 && MapKind != OMPC_MAP_unknown) {
if (!HasMapType) {
HasMapType = true;
Data.ExtraModifier = MapKind;
MapKind = OMPC_MAP_unknown;
PreMapLoc = Tok.getLocation();
PreMapName = Tok.getIdentifierInfo()->getName();
} else {
Diag(Tok, diag::err_omp_more_one_map_type);
Diag(PreMapLoc, diag::note_previous_map_type_specified_here)
<< PreMapName;
}
ConsumeToken();
} else {
// For the case of unknown map-type-modifier or a map-type.
// Map-type is followed by a colon; the function returns when it
Expand All @@ -4267,8 +4267,14 @@ bool Parser::parseMapTypeModifiers(SemaOpenMP::OpenMPVarListDataTy &Data) {
continue;
}
// Potential map-type token as it is followed by a colon.
if (PP.LookAhead(0).is(tok::colon))
return false;
if (PP.LookAhead(0).is(tok::colon)) {
if (getLangOpts().OpenMP >= 60) {
break;
} else {
return false;
}
}

Diag(Tok, diag::err_omp_unknown_map_type_modifier)
<< (getLangOpts().OpenMP >= 51 ? (getLangOpts().OpenMP >= 52 ? 2 : 1)
: 0)
Expand All @@ -4278,6 +4284,14 @@ bool Parser::parseMapTypeModifiers(SemaOpenMP::OpenMPVarListDataTy &Data) {
if (getCurToken().is(tok::comma))
ConsumeToken();
}
if (getLangOpts().OpenMP >= 60 && !HasMapType) {
if (!Tok.is(tok::colon)) {
Diag(Tok, diag::err_omp_unknown_map_type);
ConsumeToken();
} else {
Data.ExtraModifier = OMPC_MAP_unknown;
}
}
return false;
}

Expand All @@ -4289,13 +4303,12 @@ static OpenMPMapClauseKind isMapType(Parser &P) {
if (!Tok.isOneOf(tok::identifier, tok::kw_delete))
return OMPC_MAP_unknown;
Preprocessor &PP = P.getPreprocessor();
OpenMPMapClauseKind MapType =
static_cast<OpenMPMapClauseKind>(getOpenMPSimpleClauseType(
OMPC_map, PP.getSpelling(Tok), P.getLangOpts()));
unsigned MapType =
getOpenMPSimpleClauseType(OMPC_map, PP.getSpelling(Tok), P.getLangOpts());
if (MapType == OMPC_MAP_to || MapType == OMPC_MAP_from ||
MapType == OMPC_MAP_tofrom || MapType == OMPC_MAP_alloc ||
MapType == OMPC_MAP_delete || MapType == OMPC_MAP_release)
return MapType;
return static_cast<OpenMPMapClauseKind>(MapType);
return OMPC_MAP_unknown;
}

Expand Down Expand Up @@ -4679,8 +4692,10 @@ bool Parser::ParseOpenMPVarList(OpenMPDirectiveKind DKind,
// Only parse map-type-modifier[s] and map-type if a colon is present in
// the map clause.
if (ColonPresent) {
if (getLangOpts().OpenMP >= 60 && getCurToken().is(tok::colon))
Diag(Tok, diag::err_omp_map_modifier_specification_list);
IsInvalidMapperModifier = parseMapTypeModifiers(Data);
if (!IsInvalidMapperModifier)
if (getLangOpts().OpenMP < 60 && !IsInvalidMapperModifier)
parseMapType(*this, Data);
else
SkipUntil(tok::colon, tok::annot_pragma_openmp_end, StopBeforeMatch);
Expand Down Expand Up @@ -4991,3 +5006,38 @@ OMPClause *Parser::ParseOpenMPVarListClause(OpenMPDirectiveKind DKind,
OMPVarListLocTy Locs(Loc, LOpen, Data.RLoc);
return Actions.OpenMP().ActOnOpenMPVarListClause(Kind, Vars, Locs, Data);
}

bool Parser::ParseOpenMPExprListClause(OpenMPClauseKind Kind,
SourceLocation &ClauseNameLoc,
SourceLocation &OpenLoc,
SourceLocation &CloseLoc,
SmallVectorImpl<Expr *> &Exprs,
bool ReqIntConst) {
assert(getOpenMPClauseName(Kind) == PP.getSpelling(Tok) &&
"Expected parsing to start at clause name");
ClauseNameLoc = ConsumeToken();

// Parse inside of '(' and ')'.
BalancedDelimiterTracker T(*this, tok::l_paren, tok::annot_pragma_openmp_end);
if (T.consumeOpen()) {
Diag(Tok, diag::err_expected) << tok::l_paren;
return true;
}

// Parse the list with interleaved commas.
do {
ExprResult Val =
ReqIntConst ? ParseConstantExpression() : ParseAssignmentExpression();
if (!Val.isUsable()) {
// Encountered something other than an expression; abort to ')'.
T.skipToEnd();
return true;
}
Exprs.push_back(Val.get());
} while (TryConsumeToken(tok::comma));

bool Result = T.consumeClose();
OpenLoc = T.getOpenLocation();
CloseLoc = T.getCloseLocation();
return Result;
}
11 changes: 5 additions & 6 deletions clang/lib/Parse/ParseStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "clang/Sema/DeclSpec.h"
#include "clang/Sema/EnterExpressionEvaluationContext.h"
#include "clang/Sema/Scope.h"
#include "clang/Sema/SemaObjC.h"
#include "clang/Sema/SemaOpenMP.h"
#include "clang/Sema/TypoCorrection.h"
#include "llvm/ADT/STLExtras.h"
Expand Down Expand Up @@ -2294,10 +2295,8 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) {
} else if (ForEach) {
// Similarly, we need to do the semantic analysis for a for-range
// statement immediately in order to close over temporaries correctly.
ForEachStmt = Actions.ActOnObjCForCollectionStmt(ForLoc,
FirstPart.get(),
Collection.get(),
T.getCloseLocation());
ForEachStmt = Actions.ObjC().ActOnObjCForCollectionStmt(
ForLoc, FirstPart.get(), Collection.get(), T.getCloseLocation());
} else {
// In OpenMP loop region loop control variable must be captured and be
// private. Perform analysis of first part (if any).
Expand Down Expand Up @@ -2345,8 +2344,8 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) {
return StmtError();

if (ForEach)
return Actions.FinishObjCForCollectionStmt(ForEachStmt.get(),
Body.get());
return Actions.ObjC().FinishObjCForCollectionStmt(ForEachStmt.get(),
Body.get());

if (ForRangeInfo.ParsedForRangeDecl())
return Actions.FinishCXXForRangeStmt(ForRangeStmt.get(), Body.get());
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Sema/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ add_clang_library(clangSema
SemaLambda.cpp
SemaLookup.cpp
SemaModule.cpp
SemaObjC.cpp
SemaObjCProperty.cpp
SemaOpenACC.cpp
SemaOpenMP.cpp
Expand Down
14 changes: 6 additions & 8 deletions clang/lib/Sema/Sema.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
#include "clang/Sema/SemaConsumer.h"
#include "clang/Sema/SemaHLSL.h"
#include "clang/Sema/SemaInternal.h"
#include "clang/Sema/SemaObjC.h"
#include "clang/Sema/SemaOpenACC.h"
#include "clang/Sema/SemaOpenMP.h"
#include "clang/Sema/SemaSYCL.h"
Expand Down Expand Up @@ -203,6 +204,7 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
CurScope(nullptr), Ident_super(nullptr),
CUDAPtr(std::make_unique<SemaCUDA>(*this)),
HLSLPtr(std::make_unique<SemaHLSL>(*this)),
ObjCPtr(std::make_unique<SemaObjC>(*this)),
OpenACCPtr(std::make_unique<SemaOpenACC>(*this)),
OpenMPPtr(std::make_unique<SemaOpenMP>(*this)),
SYCLPtr(std::make_unique<SemaSYCL>(*this)),
Expand All @@ -224,20 +226,16 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
AccessCheckingSFINAE(false), CurrentInstantiationScope(nullptr),
InNonInstantiationSFINAEContext(false), NonInstantiationEntries(0),
ArgumentPackSubstitutionIndex(-1), SatisfactionCache(Context),
NSNumberDecl(nullptr), NSValueDecl(nullptr), NSStringDecl(nullptr),
StringWithUTF8StringMethod(nullptr),
ValueWithBytesObjCTypeMethod(nullptr), NSArrayDecl(nullptr),
ArrayWithObjectsMethod(nullptr), NSDictionaryDecl(nullptr),
DictionaryWithObjectsMethod(nullptr), CodeCompleter(CodeCompleter) {
CodeCompleter(CodeCompleter) {
assert(pp.TUKind == TUKind);
TUScope = nullptr;

LoadedExternalKnownNamespaces = false;
for (unsigned I = 0; I != NSAPI::NumNSNumberLiteralMethods; ++I)
NSNumberLiteralMethods[I] = nullptr;
ObjC().NSNumberLiteralMethods[I] = nullptr;

if (getLangOpts().ObjC)
NSAPIObj.reset(new NSAPI(Context));
ObjC().NSAPIObj.reset(new NSAPI(Context));

if (getLangOpts().CPlusPlus)
FieldCollector.reset(new CXXFieldCollector());
Expand Down Expand Up @@ -1129,7 +1127,7 @@ void Sema::ActOnEndOfTranslationUnit() {
// Complete translation units and modules define vtables and perform implicit
// instantiations. PCH files do not.
if (TUKind != TU_Prefix) {
DiagnoseUseOfUnimplementedSelectors();
ObjC().DiagnoseUseOfUnimplementedSelectors();

ActOnEndOfTranslationUnitFragment(
!ModuleScopes.empty() && ModuleScopes.back().Module->Kind ==
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/Sema/SemaAPINotes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "clang/Basic/SourceLocation.h"
#include "clang/Lex/Lexer.h"
#include "clang/Sema/SemaInternal.h"
#include "clang/Sema/SemaObjC.h"

using namespace clang;

Expand Down Expand Up @@ -372,7 +373,7 @@ static void ProcessAPINotes(Sema &S, Decl *D,
if (auto Var = dyn_cast<VarDecl>(D)) {
// Make adjustments to parameter types.
if (isa<ParmVarDecl>(Var)) {
Type = S.AdjustParameterTypeForObjCAutoRefCount(
Type = S.ObjC().AdjustParameterTypeForObjCAutoRefCount(
Type, D->getLocation(), TypeInfo);
Type = S.Context.getAdjustedParameterType(Type);
}
Expand Down
16 changes: 0 additions & 16 deletions clang/lib/Sema/SemaAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -860,22 +860,6 @@ void Sema::ActOnPragmaUnused(const Token &IdTok, Scope *curScope,
UnusedAttr::GNU_unused));
}

void Sema::AddCFAuditedAttribute(Decl *D) {
IdentifierInfo *Ident;
SourceLocation Loc;
std::tie(Ident, Loc) = PP.getPragmaARCCFCodeAuditedInfo();
if (!Loc.isValid()) return;

// Don't add a redundant or conflicting attribute.
if (D->hasAttr<CFAuditedTransferAttr>() ||
D->hasAttr<CFUnknownTransferAttr>())
return;

AttributeCommonInfo Info(Ident, SourceRange(Loc),
AttributeCommonInfo::Form::Pragma());
D->addAttr(CFAuditedTransferAttr::CreateImplicit(Context, Info));
}

namespace {

std::optional<attr::SubjectMatchRule>
Expand Down
7 changes: 4 additions & 3 deletions clang/lib/Sema/SemaAvailability.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "clang/Sema/DelayedDiagnostic.h"
#include "clang/Sema/ScopeInfo.h"
#include "clang/Sema/Sema.h"
#include "clang/Sema/SemaObjC.h"
#include <optional>

using namespace clang;
Expand Down Expand Up @@ -98,11 +99,11 @@ ShouldDiagnoseAvailabilityOfDecl(Sema &S, const NamedDecl *D,

// For +new, infer availability from -init.
if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) {
if (S.NSAPIObj && ClassReceiver) {
if (S.ObjC().NSAPIObj && ClassReceiver) {
ObjCMethodDecl *Init = ClassReceiver->lookupInstanceMethod(
S.NSAPIObj->getInitSelector());
S.ObjC().NSAPIObj->getInitSelector());
if (Init && Result == AR_Available && MD->isClassMethod() &&
MD->getSelector() == S.NSAPIObj->getNewSelector() &&
MD->getSelector() == S.ObjC().NSAPIObj->getNewSelector() &&
MD->definedInNSObject(S.getASTContext())) {
Result = Init->getAvailability(Message);
D = Init;
Expand Down
13 changes: 7 additions & 6 deletions clang/lib/Sema/SemaCast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "clang/Lex/Preprocessor.h"
#include "clang/Sema/Initialization.h"
#include "clang/Sema/SemaInternal.h"
#include "clang/Sema/SemaObjC.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
#include <set>
Expand Down Expand Up @@ -159,8 +160,8 @@ namespace {
assert(Self.getLangOpts().allowsNonTrivialObjCLifetimeQualifiers());

Expr *src = SrcExpr.get();
if (Self.CheckObjCConversion(OpRange, DestType, src, CCK) ==
Sema::ACR_unbridged)
if (Self.ObjC().CheckObjCConversion(OpRange, DestType, src, CCK) ==
SemaObjC::ACR_unbridged)
IsARCUnbridgedCast = true;
SrcExpr = src;
}
Expand Down Expand Up @@ -1499,7 +1500,7 @@ static TryCastResult TryStaticCast(Sema &Self, ExprResult &SrcExpr,
// Allow ns-pointer to cf-pointer conversion in either direction
// with static casts.
if (!CStyle &&
Self.CheckTollFreeBridgeStaticCast(DestType, SrcExpr.get(), Kind))
Self.ObjC().CheckTollFreeBridgeStaticCast(DestType, SrcExpr.get(), Kind))
return TC_Success;

// See if it looks like the user is trying to convert between
Expand Down Expand Up @@ -2524,7 +2525,7 @@ static TryCastResult TryReinterpretCast(Sema &Self, ExprResult &SrcExpr,
} else if (IsLValueCast) {
Kind = CK_LValueBitCast;
} else if (DestType->isObjCObjectPointerType()) {
Kind = Self.PrepareCastToObjCObjectPointer(SrcExpr);
Kind = Self.ObjC().PrepareCastToObjCObjectPointer(SrcExpr);
} else if (DestType->isBlockPointerType()) {
if (!SrcType->isBlockPointerType()) {
Kind = CK_AnyPointerToBlockPointerCast;
Expand Down Expand Up @@ -3217,8 +3218,8 @@ void CastOperation::CheckCStyleCast() {
return;
}
}
}
else if (!Self.CheckObjCARCUnavailableWeakConversion(DestType, SrcType)) {
} else if (!Self.ObjC().CheckObjCARCUnavailableWeakConversion(DestType,
SrcType)) {
Self.Diag(SrcExpr.get()->getBeginLoc(),
diag::err_arc_convesion_of_weak_unavailable)
<< 1 << SrcType << DestType << SrcExpr.get()->getSourceRange();
Expand Down
528 changes: 12 additions & 516 deletions clang/lib/Sema/SemaChecking.cpp

Large diffs are not rendered by default.

40 changes: 22 additions & 18 deletions clang/lib/Sema/SemaCodeComplete.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
#include "clang/Sema/ScopeInfo.h"
#include "clang/Sema/Sema.h"
#include "clang/Sema/SemaInternal.h"
#include "clang/Sema/SemaObjC.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/SmallBitVector.h"
Expand Down Expand Up @@ -5862,7 +5863,8 @@ void Sema::CodeCompleteObjCClassPropertyRefExpr(Scope *S,
SourceLocation ClassNameLoc,
bool IsBaseExprStatement) {
const IdentifierInfo *ClassNamePtr = &ClassName;
ObjCInterfaceDecl *IFace = getObjCInterfaceDecl(ClassNamePtr, ClassNameLoc);
ObjCInterfaceDecl *IFace =
ObjC().getObjCInterfaceDecl(ClassNamePtr, ClassNameLoc);
if (!IFace)
return;
CodeCompletionContext CCContext(
Expand Down Expand Up @@ -8176,15 +8178,16 @@ AddClassMessageCompletions(Sema &SemaRef, Scope *S, ParsedType Receiver,
N = SemaRef.getExternalSource()->GetNumExternalSelectors();
I != N; ++I) {
Selector Sel = SemaRef.getExternalSource()->GetExternalSelector(I);
if (Sel.isNull() || SemaRef.MethodPool.count(Sel))
if (Sel.isNull() || SemaRef.ObjC().MethodPool.count(Sel))
continue;

SemaRef.ReadMethodPool(Sel);
SemaRef.ObjC().ReadMethodPool(Sel);
}
}

for (Sema::GlobalMethodPool::iterator M = SemaRef.MethodPool.begin(),
MEnd = SemaRef.MethodPool.end();
for (SemaObjC::GlobalMethodPool::iterator
M = SemaRef.ObjC().MethodPool.begin(),
MEnd = SemaRef.ObjC().MethodPool.end();
M != MEnd; ++M) {
for (ObjCMethodList *MethList = &M->second.second;
MethList && MethList->getMethod(); MethList = MethList->getNext()) {
Expand Down Expand Up @@ -8346,15 +8349,15 @@ void Sema::CodeCompleteObjCInstanceMessage(
for (uint32_t I = 0, N = ExternalSource->GetNumExternalSelectors();
I != N; ++I) {
Selector Sel = ExternalSource->GetExternalSelector(I);
if (Sel.isNull() || MethodPool.count(Sel))
if (Sel.isNull() || ObjC().MethodPool.count(Sel))
continue;

ReadMethodPool(Sel);
ObjC().ReadMethodPool(Sel);
}
}

for (GlobalMethodPool::iterator M = MethodPool.begin(),
MEnd = MethodPool.end();
for (SemaObjC::GlobalMethodPool::iterator M = ObjC().MethodPool.begin(),
MEnd = ObjC().MethodPool.end();
M != MEnd; ++M) {
for (ObjCMethodList *MethList = &M->second.first;
MethList && MethList->getMethod(); MethList = MethList->getNext()) {
Expand Down Expand Up @@ -8417,19 +8420,19 @@ void Sema::CodeCompleteObjCSelector(
for (uint32_t I = 0, N = ExternalSource->GetNumExternalSelectors(); I != N;
++I) {
Selector Sel = ExternalSource->GetExternalSelector(I);
if (Sel.isNull() || MethodPool.count(Sel))
if (Sel.isNull() || ObjC().MethodPool.count(Sel))
continue;

ReadMethodPool(Sel);
ObjC().ReadMethodPool(Sel);
}
}

ResultBuilder Results(*this, CodeCompleter->getAllocator(),
CodeCompleter->getCodeCompletionTUInfo(),
CodeCompletionContext::CCC_SelectorName);
Results.EnterNewScope();
for (GlobalMethodPool::iterator M = MethodPool.begin(),
MEnd = MethodPool.end();
for (SemaObjC::GlobalMethodPool::iterator M = ObjC().MethodPool.begin(),
MEnd = ObjC().MethodPool.end();
M != MEnd; ++M) {

Selector Sel = M->first;
Expand Down Expand Up @@ -8497,7 +8500,8 @@ void Sema::CodeCompleteObjCProtocolReferences(
// already seen.
// FIXME: This doesn't work when caching code-completion results.
for (const IdentifierLocPair &Pair : Protocols)
if (ObjCProtocolDecl *Protocol = LookupProtocol(Pair.first, Pair.second))
if (ObjCProtocolDecl *Protocol =
ObjC().LookupProtocol(Pair.first, Pair.second))
Results.Ignore(Protocol);

// Add all protocols.
Expand Down Expand Up @@ -9755,10 +9759,10 @@ void Sema::CodeCompleteObjCMethodDeclSelector(
for (uint32_t I = 0, N = ExternalSource->GetNumExternalSelectors(); I != N;
++I) {
Selector Sel = ExternalSource->GetExternalSelector(I);
if (Sel.isNull() || MethodPool.count(Sel))
if (Sel.isNull() || ObjC().MethodPool.count(Sel))
continue;

ReadMethodPool(Sel);
ObjC().ReadMethodPool(Sel);
}
}

Expand All @@ -9772,8 +9776,8 @@ void Sema::CodeCompleteObjCMethodDeclSelector(
Results.setPreferredType(GetTypeFromParser(ReturnTy).getNonReferenceType());

Results.EnterNewScope();
for (GlobalMethodPool::iterator M = MethodPool.begin(),
MEnd = MethodPool.end();
for (SemaObjC::GlobalMethodPool::iterator M = ObjC().MethodPool.begin(),
MEnd = ObjC().MethodPool.end();
M != MEnd; ++M) {
for (ObjCMethodList *MethList = IsInstanceMethod ? &M->second.first
: &M->second.second;
Expand Down
292 changes: 14 additions & 278 deletions clang/lib/Sema/SemaDecl.cpp

Large diffs are not rendered by default.

11 changes: 6 additions & 5 deletions clang/lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
#include "clang/Sema/SemaCUDA.h"
#include "clang/Sema/SemaHLSL.h"
#include "clang/Sema/SemaInternal.h"
#include "clang/Sema/SemaObjC.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/STLForwardCompat.h"
#include "llvm/ADT/StringExtras.h"
Expand Down Expand Up @@ -6560,13 +6561,13 @@ static bool isErrorParameter(Sema &S, QualType QT) {
// Check for NSError**.
if (const auto *OPT = Pointee->getAs<ObjCObjectPointerType>())
if (const auto *ID = OPT->getInterfaceDecl())
if (ID->getIdentifier() == S.getNSErrorIdent())
if (ID->getIdentifier() == S.ObjC().getNSErrorIdent())
return true;

// Check for CFError**.
if (const auto *PT = Pointee->getAs<PointerType>())
if (const auto *RT = PT->getPointeeType()->getAs<RecordType>())
if (S.isCFError(RT->getDecl()))
if (S.ObjC().isCFError(RT->getDecl()))
return true;

return false;
Expand Down Expand Up @@ -6697,7 +6698,7 @@ static void checkSwiftAsyncErrorBlock(Sema &S, Decl *D,
// Check for NSError *.
if (const auto *ObjCPtrTy = Param->getAs<ObjCObjectPointerType>()) {
if (const auto *ID = ObjCPtrTy->getInterfaceDecl()) {
if (ID->getIdentifier() == S.getNSErrorIdent()) {
if (ID->getIdentifier() == S.ObjC().getNSErrorIdent()) {
AnyErrorParams = true;
break;
}
Expand All @@ -6706,7 +6707,7 @@ static void checkSwiftAsyncErrorBlock(Sema &S, Decl *D,
// Check for CFError *.
if (const auto *PtrTy = Param->getAs<PointerType>()) {
if (const auto *RT = PtrTy->getPointeeType()->getAs<RecordType>()) {
if (S.isCFError(RT->getDecl())) {
if (S.ObjC().isCFError(RT->getDecl())) {
AnyErrorParams = true;
break;
}
Expand Down Expand Up @@ -8837,7 +8838,7 @@ static bool tryMakeVariablePseudoStrong(Sema &S, VarDecl *VD,

Qualifiers::ObjCLifetime LifetimeQual = Ty.getQualifiers().getObjCLifetime();

// Sema::inferObjCARCLifetime must run after processing decl attributes
// SemaObjC::inferObjCARCLifetime must run after processing decl attributes
// (because __block lowers to an attribute), so if the lifetime hasn't been
// explicitly specified, infer it locally now.
if (LifetimeQual == Qualifiers::OCL_None)
Expand Down
58 changes: 2 additions & 56 deletions clang/lib/Sema/SemaDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
#include "clang/Sema/ScopeInfo.h"
#include "clang/Sema/SemaCUDA.h"
#include "clang/Sema/SemaInternal.h"
#include "clang/Sema/SemaObjC.h"
#include "clang/Sema/SemaOpenMP.h"
#include "clang/Sema/Template.h"
#include "llvm/ADT/ArrayRef.h"
Expand Down Expand Up @@ -17054,7 +17055,7 @@ VarDecl *Sema::BuildExceptionDeclaration(Scope *S, TypeSourceInfo *TInfo,
ExDecl->setExceptionVariable(true);

// In ARC, infer 'retaining' for variables of retainable type.
if (getLangOpts().ObjCAutoRefCount && inferObjCARCLifetime(ExDecl))
if (getLangOpts().ObjCAutoRefCount && ObjC().inferObjCARCLifetime(ExDecl))
Invalid = true;

if (!Invalid && !ExDeclType->isDependentType()) {
Expand Down Expand Up @@ -18853,61 +18854,6 @@ void Sema::MarkVirtualMembersReferenced(SourceLocation Loc,
}
}

/// SetIvarInitializers - This routine builds initialization ASTs for the
/// Objective-C implementation whose ivars need be initialized.
void Sema::SetIvarInitializers(ObjCImplementationDecl *ObjCImplementation) {
if (!getLangOpts().CPlusPlus)
return;
if (ObjCInterfaceDecl *OID = ObjCImplementation->getClassInterface()) {
SmallVector<ObjCIvarDecl*, 8> ivars;
CollectIvarsToConstructOrDestruct(OID, ivars);
if (ivars.empty())
return;
SmallVector<CXXCtorInitializer*, 32> AllToInit;
for (unsigned i = 0; i < ivars.size(); i++) {
FieldDecl *Field = ivars[i];
if (Field->isInvalidDecl())
continue;

CXXCtorInitializer *Member;
InitializedEntity InitEntity = InitializedEntity::InitializeMember(Field);
InitializationKind InitKind =
InitializationKind::CreateDefault(ObjCImplementation->getLocation());

InitializationSequence InitSeq(*this, InitEntity, InitKind, std::nullopt);
ExprResult MemberInit =
InitSeq.Perform(*this, InitEntity, InitKind, std::nullopt);
MemberInit = MaybeCreateExprWithCleanups(MemberInit);
// Note, MemberInit could actually come back empty if no initialization
// is required (e.g., because it would call a trivial default constructor)
if (!MemberInit.get() || MemberInit.isInvalid())
continue;

Member =
new (Context) CXXCtorInitializer(Context, Field, SourceLocation(),
SourceLocation(),
MemberInit.getAs<Expr>(),
SourceLocation());
AllToInit.push_back(Member);

// Be sure that the destructor is accessible and is marked as referenced.
if (const RecordType *RecordTy =
Context.getBaseElementType(Field->getType())
->getAs<RecordType>()) {
CXXRecordDecl *RD = cast<CXXRecordDecl>(RecordTy->getDecl());
if (CXXDestructorDecl *Destructor = LookupDestructor(RD)) {
MarkFunctionReferenced(Field->getLocation(), Destructor);
CheckDestructorAccess(Field->getLocation(), Destructor,
PDiag(diag::err_access_dtor_ivar)
<< Context.getBaseElementType(Field->getType()));
}
}
}
ObjCImplementation->setIvarInitializers(Context,
AllToInit.data(), AllToInit.size());
}
}

static
void DelegatingCycleHelper(CXXConstructorDecl* Ctor,
llvm::SmallPtrSet<CXXConstructorDecl*, 4> &Valid,
Expand Down
Loading