Skip to content

Commit 0b9bd61

Browse files
committed
Merge from 'main' to 'sycl-web' (#162)
CONFLICT (content): Merge conflict in clang/lib/Frontend/CompilerInvocation.cpp CONFLICT (content): Merge conflict in clang/include/clang/Driver/Options.td
2 parents 1829b42 + ce8c59e commit 0b9bd61

File tree

787 files changed

+42291
-6272
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

787 files changed

+42291
-6272
lines changed

clang-tools-extra/clang-tidy/abseil/StringFindStrContainsCheck.cpp

Lines changed: 27 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,10 @@ namespace clang {
2424
namespace tidy {
2525
namespace abseil {
2626

27+
using ::clang::transformer::addInclude;
2728
using ::clang::transformer::applyFirst;
2829
using ::clang::transformer::cat;
29-
using ::clang::transformer::change;
30+
using ::clang::transformer::changeTo;
3031
using ::clang::transformer::makeRule;
3132
using ::clang::transformer::node;
3233
using ::clang::transformer::RewriteRule;
@@ -38,22 +39,9 @@ static const char DefaultStringLikeClasses[] = "::std::basic_string;"
3839
"::absl::string_view";
3940
static const char DefaultAbseilStringsMatchHeader[] = "absl/strings/match.h";
4041

41-
static llvm::Optional<transformer::RewriteRule>
42-
MakeRule(const LangOptions &LangOpts,
43-
const ClangTidyCheck::OptionsView &Options) {
44-
// Parse options.
45-
//
46-
// FIXME(tdl-g): These options are being parsed redundantly with the
47-
// constructor because TransformerClangTidyCheck forces us to provide MakeRule
48-
// before "this" is fully constructed, but StoreOptions requires us to store
49-
// the parsed options in "this". We need to fix TransformerClangTidyCheck and
50-
// then we can clean this up.
51-
const std::vector<std::string> StringLikeClassNames =
52-
utils::options::parseStringList(
53-
Options.get("StringLikeClasses", DefaultStringLikeClasses));
54-
const std::string AbseilStringsMatchHeader =
55-
Options.get("AbseilStringsMatchHeader", DefaultAbseilStringsMatchHeader);
56-
42+
static transformer::RewriteRule
43+
makeRewriteRule(const std::vector<std::string> &StringLikeClassNames,
44+
StringRef AbseilStringsMatchHeader) {
5745
auto StringLikeClass = cxxRecordDecl(hasAnyName(SmallVector<StringRef, 4>(
5846
StringLikeClassNames.begin(), StringLikeClassNames.end())));
5947
auto StringType =
@@ -75,29 +63,36 @@ MakeRule(const LangOptions &LangOpts,
7563
onImplicitObjectArgument(expr().bind("string_being_searched")));
7664

7765
RewriteRule rule = applyFirst(
78-
{makeRule(binaryOperator(hasOperatorName("=="),
79-
hasOperands(ignoringParenImpCasts(StringNpos),
80-
ignoringParenImpCasts(StringFind))),
81-
change(cat("!absl::StrContains(", node("string_being_searched"),
82-
", ", node("parameter_to_find"), ")")),
83-
cat("use !absl::StrContains instead of find() == npos")),
84-
makeRule(binaryOperator(hasOperatorName("!="),
85-
hasOperands(ignoringParenImpCasts(StringNpos),
86-
ignoringParenImpCasts(StringFind))),
87-
change(cat("absl::StrContains(", node("string_being_searched"),
88-
", ", node("parameter_to_find"), ")")),
89-
cat("use absl::StrContains instead of find() != npos"))});
90-
addInclude(rule, AbseilStringsMatchHeader);
66+
{makeRule(
67+
binaryOperator(hasOperatorName("=="),
68+
hasOperands(ignoringParenImpCasts(StringNpos),
69+
ignoringParenImpCasts(StringFind))),
70+
{changeTo(cat("!absl::StrContains(", node("string_being_searched"),
71+
", ", node("parameter_to_find"), ")")),
72+
addInclude(AbseilStringsMatchHeader)},
73+
cat("use !absl::StrContains instead of find() == npos")),
74+
makeRule(
75+
binaryOperator(hasOperatorName("!="),
76+
hasOperands(ignoringParenImpCasts(StringNpos),
77+
ignoringParenImpCasts(StringFind))),
78+
{changeTo(cat("absl::StrContains(", node("string_being_searched"),
79+
", ", node("parameter_to_find"), ")")),
80+
addInclude(AbseilStringsMatchHeader)},
81+
cat("use absl::StrContains instead "
82+
"of find() != npos"))});
9183
return rule;
9284
}
9385

9486
StringFindStrContainsCheck::StringFindStrContainsCheck(
9587
StringRef Name, ClangTidyContext *Context)
96-
: TransformerClangTidyCheck(&MakeRule, Name, Context),
88+
: TransformerClangTidyCheck(Name, Context),
9789
StringLikeClassesOption(utils::options::parseStringList(
9890
Options.get("StringLikeClasses", DefaultStringLikeClasses))),
9991
AbseilStringsMatchHeaderOption(Options.get(
100-
"AbseilStringsMatchHeader", DefaultAbseilStringsMatchHeader)) {}
92+
"AbseilStringsMatchHeader", DefaultAbseilStringsMatchHeader)) {
93+
setRule(
94+
makeRewriteRule(StringLikeClassesOption, AbseilStringsMatchHeaderOption));
95+
}
10196

10297
bool StringFindStrContainsCheck::isLanguageVersionSupported(
10398
const LangOptions &LangOpts) const {

clang-tools-extra/clangd/QueryDriverDatabase.cpp

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,10 +136,26 @@ llvm::Optional<DriverInfo> parseDriverOutput(llvm::StringRef Output) {
136136
}
137137

138138
llvm::Optional<DriverInfo>
139-
extractSystemIncludesAndTarget(PathRef Driver, llvm::StringRef Lang,
139+
extractSystemIncludesAndTarget(llvm::SmallString<128> Driver,
140+
llvm::StringRef Lang,
140141
llvm::ArrayRef<std::string> CommandLine,
141142
const llvm::Regex &QueryDriverRegex) {
142143
trace::Span Tracer("Extract system includes and target");
144+
145+
if (!llvm::sys::path::is_absolute(Driver)) {
146+
assert(llvm::none_of(
147+
Driver, [](char C) { return llvm::sys::path::is_separator(C); }));
148+
auto DriverProgram = llvm::sys::findProgramByName(Driver);
149+
if (DriverProgram) {
150+
vlog("System include extraction: driver {0} expanded to {1}", Driver,
151+
*DriverProgram);
152+
Driver = *DriverProgram;
153+
} else {
154+
elog("System include extraction: driver {0} not found in PATH", Driver);
155+
return llvm::None;
156+
}
157+
}
158+
143159
SPAN_ATTACH(Tracer, "driver", Driver);
144160
SPAN_ATTACH(Tracer, "lang", Lang);
145161

@@ -332,7 +348,11 @@ class QueryDriverDatabase : public GlobalCompilationDatabase {
332348
}
333349

334350
llvm::SmallString<128> Driver(Cmd->CommandLine.front());
335-
llvm::sys::fs::make_absolute(Cmd->Directory, Driver);
351+
if (llvm::any_of(Driver,
352+
[](char C) { return llvm::sys::path::is_separator(C); }))
353+
// Driver is a not a single executable name but instead a path (either
354+
// relative or absolute).
355+
llvm::sys::fs::make_absolute(Cmd->Directory, Driver);
336356

337357
if (auto Info =
338358
QueriedDrivers.get(/*Key=*/(Driver + ":" + Lang).str(), [&] {

clang-tools-extra/clangd/index/Merge.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,6 @@
2222
namespace clang {
2323
namespace clangd {
2424

25-
// FIXME: Deleted symbols in dirty files are still returned (from Static).
26-
// To identify these eliminate these, we should:
27-
// - find the generating file from each Symbol which is Static-only
28-
// - ask Dynamic if it has that file (needs new SymbolIndex method)
29-
// - if so, drop the Symbol.
3025
bool MergedIndex::fuzzyFind(
3126
const FuzzyFindRequest &Req,
3227
llvm::function_ref<void(const Symbol &)> Callback) const {
@@ -49,7 +44,13 @@ bool MergedIndex::fuzzyFind(
4944
SymbolSlab Dyn = std::move(DynB).build();
5045

5146
llvm::DenseSet<SymbolID> SeenDynamicSymbols;
47+
auto DynamicContainsFile = Dynamic->indexedFiles();
5248
More |= Static->fuzzyFind(Req, [&](const Symbol &S) {
49+
// We expect the definition to see the canonical declaration, so it seems
50+
// to be enough to check only the definition if it exists.
51+
if (DynamicContainsFile(S.Definition ? S.Definition.FileURI
52+
: S.CanonicalDeclaration.FileURI))
53+
return;
5354
auto DynS = Dyn.find(S.ID);
5455
++StaticCount;
5556
if (DynS == Dyn.end())

clang-tools-extra/clangd/index/Merge.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,6 @@ Symbol mergeSymbol(const Symbol &L, const Symbol &R);
2323
// - the Dynamic index covers few files, but is relatively up-to-date.
2424
// - the Static index covers a bigger set of files, but is relatively stale.
2525
// The returned index attempts to combine results, and avoid duplicates.
26-
//
27-
// FIXME: We don't have a mechanism in Index to track deleted symbols and
28-
// refs in dirty files, so the merged index may return stale symbols
29-
// and refs from Static index.
3026
class MergedIndex : public SymbolIndex {
3127
const SymbolIndex *Dynamic, *Static;
3228

clang-tools-extra/clangd/test/system-include-extractor.test

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,24 @@
33
# The mock driver below is a shell script:
44
# REQUIRES: shell
55

6+
# Create a bin directory to store the mock-driver and add it to the path
7+
# RUN: mkdir -p %t.dir/bin
8+
# RUN: export PATH=%t.dir/bin:$PATH
69
# Generate a mock-driver that will print %temp_dir%/my/dir and
710
# %temp_dir%/my/dir2 as include search paths.
8-
# RUN: echo '#!/bin/sh' >> %t.dir/my_driver.sh
9-
# RUN: echo '[ "$0" = "%t.dir/my_driver.sh" ] || exit' >> %t.dir/my_driver.sh
10-
# RUN: echo 'args="$*"' >> %t.dir/my_driver.sh
11-
# RUN: echo '[ -z "${args##*"-nostdinc"*}" ] || exit' >> %t.dir/my_driver.sh
12-
# RUN: echo '[ -z "${args##*"-isysroot=/isysroot"*}" ] || exit' >> %t.dir/my_driver.sh
13-
# RUN: echo 'echo " $* " | grep " --sysroot /my/sysroot/path " || exit' >> %t.dir/my_driver.sh
14-
# RUN: echo 'echo line to ignore >&2' >> %t.dir/my_driver.sh
15-
# RUN: echo 'printf "Target: arm-linux-gnueabihf\r\n" >&2' >> %t.dir/my_driver.sh
16-
# RUN: echo 'printf "#include <...> search starts here:\r\n" >&2' >> %t.dir/my_driver.sh
17-
# RUN: echo 'echo %t.dir/my/dir/ >&2' >> %t.dir/my_driver.sh
18-
# RUN: echo 'echo %t.dir/my/dir2/ >&2' >> %t.dir/my_driver.sh
19-
# RUN: echo 'printf "End of search list.\r\n" >&2' >> %t.dir/my_driver.sh
20-
# RUN: chmod +x %t.dir/my_driver.sh
11+
# RUN: echo '#!/bin/sh' >> %t.dir/bin/my_driver.sh
12+
# RUN: echo '[ "$0" = "%t.dir/bin/my_driver.sh" ] || exit' >> %t.dir/bin/my_driver.sh
13+
# RUN: echo 'args="$*"' >> %t.dir/bin/my_driver.sh
14+
# RUN: echo '[ -z "${args##*"-nostdinc"*}" ] || exit' >> %t.dir/bin/my_driver.sh
15+
# RUN: echo '[ -z "${args##*"-isysroot=/isysroot"*}" ] || exit' >> %t.dir/bin/my_driver.sh
16+
# RUN: echo 'echo " $* " | grep " --sysroot /my/sysroot/path " || exit' >> %t.dir/bin/my_driver.sh
17+
# RUN: echo 'echo line to ignore >&2' >> %t.dir/bin/my_driver.sh
18+
# RUN: echo 'printf "Target: arm-linux-gnueabihf\r\n" >&2' >> %t.dir/bin/my_driver.sh
19+
# RUN: echo 'printf "#include <...> search starts here:\r\n" >&2' >> %t.dir/bin/my_driver.sh
20+
# RUN: echo 'echo %t.dir/my/dir/ >&2' >> %t.dir/bin/my_driver.sh
21+
# RUN: echo 'echo %t.dir/my/dir2/ >&2' >> %t.dir/bin/my_driver.sh
22+
# RUN: echo 'printf "End of search list.\r\n" >&2' >> %t.dir/bin/my_driver.sh
23+
# RUN: chmod +x %t.dir/bin/my_driver.sh
2124

2225
# Create header files my/dir/a.h and my/dir2/b.h
2326
# RUN: mkdir -p %t.dir/my/dir
@@ -27,7 +30,7 @@
2730

2831
# Generate a compile_commands.json that will query the mock driver we've
2932
# created. Which should add a.h and b.h into include search path.
30-
# RUN: echo '[{"directory": "%/t.dir", "command": "%/t.dir/my_driver.sh the-file.cpp -nostdinc --sysroot /my/sysroot/path -isysroot=/isysroot", "file": "the-file.cpp"}]' > %t.dir/compile_commands.json
33+
# RUN: echo '[{"directory": "%/t.dir", "command": "my_driver.sh the-file.cpp -nostdinc --sysroot /my/sysroot/path -isysroot=/isysroot", "file": "the-file.cpp"}]' > %t.dir/compile_commands.json
3134

3235
# RUN: sed -e "s|INPUT_DIR|%/t.dir|g" %s > %t.test.1
3336
# On Windows, we need the URI in didOpen to look like "uri":"file:///C:/..."

clang-tools-extra/clangd/unittests/FindSymbolsTests.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,17 @@ std::vector<SymbolInformation> getSymbols(TestTU &TU, llvm::StringRef Query,
5252
return *SymbolInfos;
5353
}
5454

55-
TEST(WorkspaceSymbols, Macros) {
55+
// FIXME: We update two indexes during main file processing:
56+
// - preamble index (static)
57+
// - main-file index (dynamic)
58+
// The macro in this test appears to be in the preamble index and not
59+
// in the main-file index. According to our logic of indexes merging, we
60+
// do not take this macro from the static (preamble) index, because it
61+
// location within the file from the dynamic (main-file) index.
62+
//
63+
// Possible solution is to exclude main-file symbols from the preamble
64+
// index, after that we can enable this test again.
65+
TEST(WorkspaceSymbols, DISABLED_Macros) {
5666
TestTU TU;
5767
TU.Code = R"cpp(
5868
#define MACRO X

clang-tools-extra/clangd/unittests/IndexTests.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,39 @@ TEST(MergeIndexTest, FuzzyFind) {
335335
UnorderedElementsAre("ns::A", "ns::B", "ns::C"));
336336
}
337337

338+
TEST(MergeIndexTest, FuzzyFindRemovedSymbol) {
339+
FileIndex DynamicIndex, StaticIndex;
340+
MergedIndex Merge(&DynamicIndex, &StaticIndex);
341+
342+
const char *HeaderCode = "class Foo;";
343+
auto HeaderSymbols = TestTU::withHeaderCode(HeaderCode).headerSymbols();
344+
auto Foo = findSymbol(HeaderSymbols, "Foo");
345+
346+
// Build static index for test.cc with Foo symbol
347+
TestTU Test;
348+
Test.HeaderCode = HeaderCode;
349+
Test.Code = "class Foo {};";
350+
Test.Filename = "test.cc";
351+
auto AST = Test.build();
352+
StaticIndex.updateMain(testPath(Test.Filename), AST);
353+
354+
// Remove Foo symbol, i.e. build dynamic index for test.cc, which is empty.
355+
Test.HeaderCode = "";
356+
Test.Code = "";
357+
AST = Test.build();
358+
DynamicIndex.updateMain(testPath(Test.Filename), AST);
359+
360+
// Merged index should not return removed symbol.
361+
FuzzyFindRequest Req;
362+
Req.AnyScope = true;
363+
Req.Query = "Foo";
364+
unsigned SymbolCounter = 0;
365+
bool IsIncomplete =
366+
Merge.fuzzyFind(Req, [&](const Symbol &) { ++SymbolCounter; });
367+
EXPECT_FALSE(IsIncomplete);
368+
EXPECT_EQ(SymbolCounter, 0u);
369+
}
370+
338371
TEST(MergeTest, Merge) {
339372
Symbol L, R;
340373
L.ID = R.ID = SymbolID("hello");

clang-tools-extra/test/clang-tidy/checkers/bugprone-suspicious-semicolon-constexpr.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %check_clang_tidy %s bugprone-suspicious-semicolon %t -- -- -std=c++17
1+
// RUN: %check_clang_tidy -std=c++17-or-later %s bugprone-suspicious-semicolon %t
22

33
void fail()
44
{

clang-tools-extra/test/clang-tidy/checkers/bugprone-unused-raii.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,16 @@ struct Ctor {
3636
}
3737
};
3838

39+
template <typename T>
40+
void templ() {
41+
T();
42+
}
43+
44+
template <typename T>
45+
void neverInstantiated() {
46+
T();
47+
}
48+
3949
void test() {
4050
Foo(42);
4151
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: object destroyed immediately after creation; did you mean to name the object?
@@ -54,6 +64,9 @@ void test() {
5464
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: object destroyed immediately after creation; did you mean to name the object?
5565
// CHECK-FIXES: FooBar give_me_a_name;
5666

67+
templ<FooBar>();
68+
templ<Bar>();
69+
5770
Bar();
5871
f();
5972
qux<Foo>();

clang-tools-extra/test/clang-tidy/checkers/bugprone-use-after-move.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %check_clang_tidy %s bugprone-use-after-move %t -- -- -std=c++17 -fno-delayed-template-parsing
1+
// RUN: %check_clang_tidy -std=c++17-or-later %s bugprone-use-after-move %t -- -- -fno-delayed-template-parsing
22

33
typedef decltype(nullptr) nullptr_t;
44

clang-tools-extra/test/clang-tidy/checkers/modernize-raw-string-literal.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
// RUN: %check_clang_tidy -std=c++11,c++14,c++17 %s modernize-raw-string-literal %t -- -config="{CheckOptions: [{key: modernize-raw-string-literal.ReplaceShorterLiterals, value: 1}]}"
2-
// FIXME: Fix the checker to work in C++20 mode.
1+
// RUN: %check_clang_tidy %s modernize-raw-string-literal %t -- -config="{CheckOptions: [{key: modernize-raw-string-literal.ReplaceShorterLiterals, value: 1}]}"
32

43
char const *const BackSlash("goink\\frob");
54
// CHECK-MESSAGES: :[[@LINE-1]]:29: warning: escaped string literal can be written as a raw string literal [modernize-raw-string-literal]
@@ -47,8 +46,8 @@ char const *const MultibyteSnowman("\xE2\x98\x83");
4746
char const *const TrailingSpace("A line \\with space. \n");
4847
char const *const TrailingNewLine("A single \\line.\n");
4948
char const *const AlreadyRaw(R"(foobie\\bletch)");
50-
char const *const UTF8Literal(u8"foobie\\bletch");
51-
char const *const UTF8RawLiteral(u8R"(foobie\\bletch)");
49+
auto const *const UTF8Literal(u8"foobie\\bletch");
50+
auto const *const UTF8RawLiteral(u8R"(foobie\\bletch)");
5251
// TODO: enable these tests once all supported compilers
5352
// support char16_t and char32_t (VS2013 does not)
5453
// char16_t const *const UTF16Literal(u"foobie\\bletch");

clang-tools-extra/test/clang-tidy/checkers/modernize-use-nodiscard.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
// RUN: %check_clang_tidy %s modernize-use-nodiscard %t -- \
2-
// RUN: -config="{CheckOptions: [{key: modernize-use-nodiscard.ReplacementString, value: 'NO_DISCARD'}]}" \
3-
// RUN: -- -std=c++17
1+
// RUN: %check_clang_tidy -std=c++17-or-later %s modernize-use-nodiscard %t -- \
2+
// RUN: -config="{CheckOptions: [{key: modernize-use-nodiscard.ReplacementString, value: 'NO_DISCARD'}]}"
43

54
namespace std {
65
template <class>

clang-tools-extra/test/clang-tidy/checkers/performance-unnecessary-value-param-header.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
// RUN: rm -rf %t
22
// RUN: mkdir %t
33
// RUN: cp %S/Inputs/performance-unnecessary-value-param/header.h %t/header.h
4-
// RUN: %check_clang_tidy -std=c++11 %s performance-unnecessary-value-param %t/temp -- -- -I %t
4+
// RUN: %check_clang_tidy %s performance-unnecessary-value-param %t/temp -- -- -I %t
55
// RUN: diff %t/header.h %S/Inputs/performance-unnecessary-value-param/header-fixed.h
6-
// FIXME: Make the test work in all language modes.
76

87
#include "header.h"
98

clang-tools-extra/test/clang-tidy/checkers/readability-avoid-const-params-in-decls.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,29 @@ struct FooT {
8181
};
8282
FooT<int> f(1);
8383

84+
template <class T>
85+
struct BingT {
86+
BingT(const T i);
87+
// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: parameter 'i'
88+
// CHECK-FIXES: BingT(T i);
89+
90+
void operator()(const T i);
91+
// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: parameter 'i'
92+
// CHECK-FIXES: void operator()(T i);
93+
};
94+
BingT<int> f2(1);
95+
96+
template <class T>
97+
struct NeverInstantiatedT {
98+
NeverInstantiatedT(const T i);
99+
// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: parameter 'i'
100+
// CHECK-FIXES: NeverInstantiatedT(T i);
101+
102+
void operator()(const T i);
103+
// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: parameter 'i'
104+
// CHECK-FIXES: void operator()(T i);
105+
};
106+
84107
// Do not match on definitions
85108
void NF1(const int i) {}
86109
void NF2(const int *const i) {}
@@ -109,6 +132,13 @@ struct BarT {
109132
void operator()(const int i) {}
110133
};
111134
BarT<int> b(1);
135+
template <class T>
136+
struct BatT {
137+
BatT(const T i) {}
138+
139+
void operator()(const T i) {}
140+
};
141+
BatT<int> b2(1);
112142

113143
// Do not match on other stuff
114144
void NF(const alias_type& i);

0 commit comments

Comments
 (0)