mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
The exact cause of this is a mystery to me - each test case should be finished (and thus any ASTs fully built) before the next runs/we exit. But one of the recent failures contained a stacktrace: ``` ... 20 [ra] 0x0000aaaad9d182f0 llvm::opt::OptTable::internalParseOneArg(llvm::opt::ArgList const&, unsigned int&, std::function<bool (llvm::opt::Option const&)>) const + 127 in SourceKitSwiftLangTests 21 [ra] 0x0000aaaad9d188d0 llvm::opt::OptTable::internalParseArgs(llvm::ArrayRef<char const*>, unsigned int&, unsigned int&, std::function<bool (llvm::opt::Option const&)>) const + 303 in SourceKitSwiftLangTests 22 [ra] 0x0000aaaad9d1877c llvm::opt::OptTable::ParseArgs(llvm::ArrayRef<char const*>, unsigned int&, unsigned int&, llvm::opt::Visibility) const + 47 in SourceKitSwiftLangTests 23 [ra] 0x0000aaaad6fad0d0 getLibcFileMapping[abi:cxx11](swift::ASTContext&, llvm::StringRef, std::optional<llvm::ArrayRef<llvm::StringRef> >, llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> const&, bool) + 311 in SourceKitSwiftLangTests 24 [ra] 0x0000aaaad6fab304 swift::getClangInvocationFileMapping(swift::ASTContext&, llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>, bool) + 895 in SourceKitSwiftLangTests 25 [ra] 0x0000aaaad6f4dcac swift::ClangImporter::create(swift::ASTContext&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, swift::DependencyTracker*, swift::DWARFImporterDelegate*, bool) + 587 in SourceKitSwiftLangTests 26 [ra] 0x0000aaaad5ba07ac swift::CompilerInstance::setUpModuleLoaders() + 531 in SourceKitSwiftLangTests 27 [ra] 0x0000aaaad5ba0530 swift::CompilerInstance::setUpASTContextIfNeeded() + 447 in SourceKitSwiftLangTests 28 [ra] 0x0000aaaad5ba269c swift::CompilerInstance::setup(swift::CompilerInvocation const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&, llvm::ArrayRef<char const*>) + 231 in SourceKitSwiftLangTests 29 [ra] 0x0000aaaad593d634 void SourceKit::WorkQueue::DispatchData::callAndDelete<(anonymous namespace)::ASTBuildOperation::schedule(SourceKit::WorkQueue)::$_11>(void*) + 2155 in SourceKitSwiftLangTests 30 [ra] 0x0000aaaad597c2fc executeBlock(void*) + 27 in SourceKitSwiftLangTests 31 [ra] 0x0000aaaad597c330 void* llvm::thread::ThreadProxy<std::tuple<void (*)(void*), void*> >(void*) + 27 in SourceKitSwiftLangTests 32 [ra] 0x0000ffffab37595c <unknown> in libc.so.6 ``` So the assertion above clearly isn't true - we are definitely still building an AST on exit here. Regardless, this test is not worth CI failing as often as it is. Let's disable it for now while we look into the cause.
454 lines
17 KiB
C++
454 lines
17 KiB
C++
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "NullEditorConsumer.h"
|
|
#include "SourceKit/Core/Context.h"
|
|
#include "SourceKit/Core/LangSupport.h"
|
|
#include "SourceKit/Core/NotificationCenter.h"
|
|
#include "SourceKit/Support/Concurrency.h"
|
|
#include "SourceKit/SwiftLang/Factory.h"
|
|
#include "swift/Basic/LLVMInitialize.h"
|
|
#include "llvm/Support/MemoryBuffer.h"
|
|
#include "llvm/Support/Path.h"
|
|
#include "llvm/Support/TargetSelect.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
using namespace SourceKit;
|
|
using namespace llvm;
|
|
|
|
static StringRef getRuntimeLibPath() {
|
|
return sys::path::parent_path(SWIFTLIB_DIR);
|
|
}
|
|
|
|
static SmallString<128> getSwiftExecutablePath() {
|
|
SmallString<128> path = sys::path::parent_path(getRuntimeLibPath());
|
|
sys::path::append(path, "bin", "swift-frontend");
|
|
return path;
|
|
}
|
|
|
|
static void *createCancellationToken() {
|
|
static std::atomic<size_t> handle(1000);
|
|
return reinterpret_cast<void *>(
|
|
handle.fetch_add(1, std::memory_order_relaxed));
|
|
}
|
|
|
|
namespace {
|
|
|
|
struct TestCursorInfo {
|
|
// Empty if no error.
|
|
std::string Error;
|
|
std::string InternalDiagnostic;
|
|
std::string Name;
|
|
std::string Typename;
|
|
std::string Filename;
|
|
unsigned Offset;
|
|
unsigned Length;
|
|
};
|
|
|
|
class CursorInfoTest : public ::testing::Test {
|
|
SourceKit::Context &Ctx;
|
|
std::atomic<int> NumTasks;
|
|
NullEditorConsumer Consumer;
|
|
|
|
public:
|
|
SourceKit::Context &getContext() { return Ctx; }
|
|
LangSupport &getLang() { return getContext().getSwiftLangSupport(); }
|
|
|
|
void SetUp() override {
|
|
NumTasks = 0;
|
|
}
|
|
|
|
CursorInfoTest()
|
|
: Ctx(*new SourceKit::Context(getSwiftExecutablePath(),
|
|
getRuntimeLibPath(),
|
|
/*diagnosticDocumentationPath*/ "",
|
|
SourceKit::createSwiftLangSupport,
|
|
[](SourceKit::Context &Ctx){ return nullptr; },
|
|
/*dispatchOnMain=*/false)) {
|
|
INITIALIZE_LLVM();
|
|
// This is avoiding destroying \p SourceKit::Context because another
|
|
// thread may be active trying to use it to post notifications.
|
|
// FIXME: Use shared_ptr ownership to avoid such issues.
|
|
}
|
|
|
|
void addNotificationReceiver(DocumentUpdateNotificationReceiver Receiver) {
|
|
Ctx.getNotificationCenter()->addDocumentUpdateNotificationReceiver(Receiver);
|
|
}
|
|
|
|
void open(const char *DocName, StringRef Text,
|
|
std::optional<ArrayRef<const char *>> CArgs = std::nullopt) {
|
|
auto Args = CArgs.has_value() ? makeArgs(DocName, *CArgs)
|
|
: std::vector<const char *>{};
|
|
auto Buf = MemoryBuffer::getMemBufferCopy(Text, DocName);
|
|
getLang().editorOpen(DocName, Buf.get(), Consumer, Args, std::nullopt);
|
|
}
|
|
|
|
void replaceText(StringRef DocName, unsigned Offset, unsigned Length,
|
|
StringRef Text) {
|
|
auto Buf = MemoryBuffer::getMemBufferCopy(Text, DocName);
|
|
getLang().editorReplaceText(DocName, Buf.get(), Offset, Length, Consumer);
|
|
}
|
|
|
|
TestCursorInfo
|
|
getCursor(const char *DocName, unsigned Offset, ArrayRef<const char *> CArgs,
|
|
SourceKitCancellationToken CancellationToken = nullptr,
|
|
bool CancelOnSubsequentRequest = false) {
|
|
auto Args = makeArgs(DocName, CArgs);
|
|
Semaphore sema(0);
|
|
|
|
TestCursorInfo TestInfo;
|
|
getLang().getCursorInfo(
|
|
DocName, DocName, Offset, /*Length=*/0, /*Actionables=*/false,
|
|
/*SymbolGraph=*/false, CancelOnSubsequentRequest, Args,
|
|
/*vfsOptions=*/std::nullopt, CancellationToken,
|
|
[&](const RequestResult<CursorInfoData> &Result) {
|
|
assert(!Result.isCancelled());
|
|
if (Result.isError()) {
|
|
TestInfo.Error = Result.getError().str();
|
|
sema.signal();
|
|
return;
|
|
}
|
|
const CursorInfoData &Info = Result.value();
|
|
TestInfo.InternalDiagnostic = Info.InternalDiagnostic.str();
|
|
if (!Info.Symbols.empty()) {
|
|
const CursorSymbolInfo &MainSymbol = Info.Symbols[0];
|
|
TestInfo.Name = std::string(MainSymbol.Name.str());
|
|
TestInfo.Typename = MainSymbol.TypeName.str();
|
|
TestInfo.Filename = MainSymbol.Location.Filename.str();
|
|
TestInfo.Offset = MainSymbol.Location.Offset;
|
|
TestInfo.Length = MainSymbol.Location.Length;
|
|
}
|
|
sema.signal();
|
|
});
|
|
|
|
bool expired = sema.wait(60 * 1000);
|
|
if (expired)
|
|
llvm::report_fatal_error("check took too long");
|
|
return TestInfo;
|
|
}
|
|
|
|
unsigned findOffset(StringRef Val, StringRef Text) {
|
|
auto pos = Text.find(Val);
|
|
assert(pos != StringRef::npos);
|
|
return pos;
|
|
}
|
|
|
|
void setNeedsSema(bool needsSema) { Consumer.needsSema = needsSema; }
|
|
|
|
private:
|
|
std::vector<const char *> makeArgs(const char *DocName,
|
|
ArrayRef<const char *> CArgs) {
|
|
std::vector<const char *> Args = CArgs;
|
|
Args.push_back(DocName);
|
|
return Args;
|
|
}
|
|
};
|
|
|
|
} // anonymous namespace
|
|
|
|
TEST_F(CursorInfoTest, FileNotExist) {
|
|
const char *DocName = "test.swift";
|
|
const char *Contents =
|
|
"let foo = 0\n";
|
|
const char *Args[] = { "<not-existent-file>" };
|
|
|
|
open(DocName, Contents);
|
|
auto FooOffs = findOffset("foo =", Contents);
|
|
auto Info = getCursor(DocName, FooOffs, Args);
|
|
EXPECT_STREQ("foo", Info.Name.c_str());
|
|
EXPECT_STREQ("Int", Info.Typename.c_str());
|
|
}
|
|
|
|
static const char *ExpensiveInit =
|
|
"[0:0,0:0,0:0,0:0,0:0,0:0,0:0]";
|
|
|
|
TEST_F(CursorInfoTest, EditAfter) {
|
|
const char *DocName = "test.swift";
|
|
const char *Contents =
|
|
"let value = foo\n"
|
|
"let foo = 0\n";
|
|
const char *Args[] = { "-parse-as-library" };
|
|
|
|
open(DocName, Contents);
|
|
auto FooRefOffs = findOffset("foo", Contents);
|
|
auto FooOffs = findOffset("foo =", Contents);
|
|
auto Info = getCursor(DocName, FooRefOffs, Args);
|
|
EXPECT_STREQ("foo", Info.Name.c_str());
|
|
EXPECT_STREQ("Int", Info.Typename.c_str());
|
|
EXPECT_STREQ(DocName, Info.Filename.c_str());
|
|
EXPECT_EQ(FooOffs, Info.Offset);
|
|
EXPECT_EQ(strlen("foo"), Info.Length);
|
|
|
|
StringRef TextToReplace = "0";
|
|
replaceText(DocName, findOffset(TextToReplace, Contents), TextToReplace.size(),
|
|
ExpensiveInit);
|
|
// Insert a space in front of 'foo' decl.
|
|
replaceText(DocName, FooOffs, 0, " ");
|
|
++FooOffs;
|
|
|
|
// Should not wait for the new AST, it should give the previous answer.
|
|
Info = getCursor(DocName, FooRefOffs, Args);
|
|
EXPECT_STREQ("foo", Info.Name.c_str());
|
|
EXPECT_STREQ("Int", Info.Typename.c_str());
|
|
EXPECT_STREQ(DocName, Info.Filename.c_str());
|
|
EXPECT_EQ(FooOffs, Info.Offset);
|
|
EXPECT_EQ(strlen("foo"), Info.Length);
|
|
}
|
|
|
|
TEST_F(CursorInfoTest, EditBefore) {
|
|
const char *DocName = "test.swift";
|
|
const char *Contents =
|
|
"let foo = 0\n"
|
|
"let value = foo;\n";
|
|
const char *Args[] = { "-parse-as-library" };
|
|
|
|
open(DocName, Contents);
|
|
auto FooRefOffs = findOffset("foo;", Contents);
|
|
auto FooOffs = findOffset("foo =", Contents);
|
|
auto Info = getCursor(DocName, FooRefOffs, Args);
|
|
EXPECT_STREQ("", Info.Error.c_str());
|
|
EXPECT_STREQ("", Info.InternalDiagnostic.c_str());
|
|
EXPECT_STREQ("foo", Info.Name.c_str());
|
|
EXPECT_STREQ("Int", Info.Typename.c_str());
|
|
EXPECT_STREQ(DocName, Info.Filename.c_str());
|
|
EXPECT_EQ(FooOffs, Info.Offset);
|
|
EXPECT_EQ(strlen("foo"), Info.Length);
|
|
|
|
StringRef TextToReplace = "0";
|
|
replaceText(DocName, findOffset(TextToReplace, Contents), TextToReplace.size(),
|
|
ExpensiveInit);
|
|
FooRefOffs += StringRef(ExpensiveInit).size() - TextToReplace.size();
|
|
// Insert a space in front of 'foo' decl.
|
|
replaceText(DocName, FooOffs, 0, " ");
|
|
++FooOffs;
|
|
++FooRefOffs;
|
|
|
|
// Should not wait for the new AST, it should give the previous answer.
|
|
Info = getCursor(DocName, FooRefOffs, Args);
|
|
EXPECT_STREQ("", Info.Error.c_str());
|
|
EXPECT_STREQ("", Info.InternalDiagnostic.c_str());
|
|
EXPECT_STREQ("foo", Info.Name.c_str());
|
|
EXPECT_STREQ("Int", Info.Typename.c_str());
|
|
EXPECT_STREQ(DocName, Info.Filename.c_str());
|
|
EXPECT_EQ(FooOffs, Info.Offset);
|
|
EXPECT_EQ(strlen("foo"), Info.Length);
|
|
}
|
|
|
|
TEST_F(CursorInfoTest, CursorInfoMustWaitDueDeclLoc) {
|
|
const char *DocName = "test.swift";
|
|
const char *Contents =
|
|
"let value = foo\n"
|
|
"let foo = 0\n";
|
|
const char *Args[] = { "-parse-as-library" };
|
|
|
|
open(DocName, Contents);
|
|
auto FooRefOffs = findOffset("foo", Contents);
|
|
auto FooOffs = findOffset("foo =", Contents);
|
|
auto Info = getCursor(DocName, FooRefOffs, Args);
|
|
EXPECT_STREQ("", Info.Error.c_str());
|
|
EXPECT_STREQ("", Info.InternalDiagnostic.c_str());
|
|
EXPECT_STREQ("foo", Info.Name.c_str());
|
|
EXPECT_STREQ("Int", Info.Typename.c_str());
|
|
|
|
StringRef TextToReplace = "0";
|
|
replaceText(DocName, findOffset(TextToReplace, Contents), TextToReplace.size(),
|
|
ExpensiveInit);
|
|
// Edit over the 'foo' decl.
|
|
replaceText(DocName, FooOffs, strlen("foo"), "foo");
|
|
|
|
// Should wait for the new AST, because the declaration location for the 'foo'
|
|
// reference has been edited out.
|
|
Info = getCursor(DocName, FooRefOffs, Args);
|
|
EXPECT_STREQ("", Info.Error.c_str());
|
|
EXPECT_STREQ("", Info.InternalDiagnostic.c_str());
|
|
EXPECT_STREQ("foo", Info.Name.c_str());
|
|
EXPECT_STREQ("[Int : Int]", Info.Typename.c_str());
|
|
EXPECT_EQ(FooOffs, Info.Offset);
|
|
EXPECT_EQ(strlen("foo"), Info.Length);
|
|
}
|
|
|
|
TEST_F(CursorInfoTest, CursorInfoMustWaitDueOffset) {
|
|
const char *DocName = "test.swift";
|
|
const char *Contents =
|
|
"let value = foo\n"
|
|
"let foo = 0\n";
|
|
const char *Args[] = { "-parse-as-library" };
|
|
|
|
open(DocName, Contents);
|
|
auto FooRefOffs = findOffset("foo", Contents);
|
|
auto FooOffs = findOffset("foo =", Contents);
|
|
auto Info = getCursor(DocName, FooRefOffs, Args);
|
|
EXPECT_STREQ("foo", Info.Name.c_str());
|
|
EXPECT_STREQ("Int", Info.Typename.c_str());
|
|
|
|
StringRef TextToReplace = "0";
|
|
replaceText(DocName, findOffset(TextToReplace, Contents), TextToReplace.size(),
|
|
ExpensiveInit);
|
|
// Edit over the 'foo' reference.
|
|
replaceText(DocName, FooRefOffs, strlen("foo"), "foo");
|
|
|
|
// Should wait for the new AST, because the cursor location has been edited
|
|
// out.
|
|
Info = getCursor(DocName, FooRefOffs, Args);
|
|
EXPECT_STREQ("foo", Info.Name.c_str());
|
|
EXPECT_STREQ("[Int : Int]", Info.Typename.c_str());
|
|
EXPECT_EQ(FooOffs, Info.Offset);
|
|
EXPECT_EQ(strlen("foo"), Info.Length);
|
|
}
|
|
|
|
TEST_F(CursorInfoTest, CursorInfoMustWaitDueToken) {
|
|
const char *DocName = "test.swift";
|
|
const char *Contents =
|
|
"let value = foo\n"
|
|
"let foo = 0\n";
|
|
const char *Args[] = { "-parse-as-library" };
|
|
|
|
open(DocName, Contents);
|
|
auto FooRefOffs = findOffset("foo", Contents);
|
|
auto FooOffs = findOffset("foo =", Contents);
|
|
auto Info = getCursor(DocName, FooRefOffs, Args);
|
|
EXPECT_STREQ("foo", Info.Name.c_str());
|
|
EXPECT_STREQ("Int", Info.Typename.c_str());
|
|
|
|
StringRef TextToReplace = "0";
|
|
replaceText(DocName, findOffset(TextToReplace, Contents), TextToReplace.size(),
|
|
ExpensiveInit);
|
|
// Change 'foo' to 'fog' by replacing the last character.
|
|
replaceText(DocName, FooOffs+2, 1, "g");
|
|
replaceText(DocName, FooRefOffs+2, 1, "g");
|
|
|
|
// Should wait for the new AST, because the cursor location points to a
|
|
// different token.
|
|
Info = getCursor(DocName, FooRefOffs, Args);
|
|
EXPECT_STREQ("fog", Info.Name.c_str());
|
|
EXPECT_STREQ("[Int : Int]", Info.Typename.c_str());
|
|
EXPECT_EQ(FooOffs, Info.Offset);
|
|
EXPECT_EQ(strlen("fog"), Info.Length);
|
|
}
|
|
|
|
TEST_F(CursorInfoTest, CursorInfoMustWaitDueTokenRace) {
|
|
const char *DocName = "test.swift";
|
|
const char *Contents = "let value = foo\n"
|
|
"let foo = 0\n";
|
|
const char *Args[] = {"-parse-as-library"};
|
|
|
|
auto FooRefOffs = findOffset("foo", Contents);
|
|
auto FooOffs = findOffset("foo =", Contents);
|
|
|
|
// Open with args, kicking off an ast build. The hope of this tests is for
|
|
// this AST to still be in the process of building when we start the cursor
|
|
// info, to ensure the ASTManager doesn't try to handle this cursor info with
|
|
// the wrong AST.
|
|
setNeedsSema(true);
|
|
open(DocName, Contents, llvm::ArrayRef(Args));
|
|
// Change 'foo' to 'fog' by replacing the last character.
|
|
replaceText(DocName, FooOffs + 2, 1, "g");
|
|
replaceText(DocName, FooRefOffs + 2, 1, "g");
|
|
|
|
// Should wait for the new AST, because the cursor location points to a
|
|
// different token.
|
|
auto Info = getCursor(DocName, FooRefOffs, Args);
|
|
EXPECT_STREQ("fog", Info.Name.c_str());
|
|
EXPECT_STREQ("Int", Info.Typename.c_str());
|
|
EXPECT_EQ(FooOffs, Info.Offset);
|
|
EXPECT_EQ(strlen("fog"), Info.Length);
|
|
}
|
|
|
|
TEST_F(CursorInfoTest, CursorInfoCancelsPreviousRequest) {
|
|
// TODO: This test case relies on the following snippet being slow to type
|
|
// check so that the first cursor info request takes longer to execute than it
|
|
// takes time to schedule the second request. If that is fixed, we need to
|
|
// find a new way to cause slow type checking. rdar://80582770
|
|
const char *SlowDocName = "slow.swift";
|
|
const char *SlowContents = "func foo(x: Invalid1, y: Invalid2) {\n"
|
|
" x / y / x / y / x / y / x / y\n"
|
|
"}\n";
|
|
auto SlowOffset = findOffset("x", SlowContents);
|
|
const char *Args[] = {"-parse-as-library"};
|
|
std::vector<const char *> ArgsForSlow = llvm::ArrayRef(Args).vec();
|
|
ArgsForSlow.push_back(SlowDocName);
|
|
|
|
const char *FastDocName = "fast.swift";
|
|
const char *FastContents = "func bar() {\n"
|
|
" let foo = 123\n"
|
|
"}\n";
|
|
auto FastOffset = findOffset("foo", FastContents);
|
|
std::vector<const char *> ArgsForFast = llvm::ArrayRef(Args).vec();
|
|
ArgsForFast.push_back(FastDocName);
|
|
|
|
open(SlowDocName, SlowContents, llvm::ArrayRef(Args));
|
|
open(FastDocName, FastContents, llvm::ArrayRef(Args));
|
|
|
|
// Schedule a cursor info request that takes long to execute. This should be
|
|
// cancelled as the next cursor info (which is faster) gets requested.
|
|
Semaphore FirstCursorInfoSema(0);
|
|
getLang().getCursorInfo(
|
|
SlowDocName, SlowDocName, SlowOffset, /*Length=*/0, /*Actionables=*/false,
|
|
/*SymbolGraph=*/false, /*CancelOnSubsequentRequest=*/true, ArgsForSlow,
|
|
/*vfsOptions=*/std::nullopt, /*CancellationToken=*/nullptr,
|
|
[&](const RequestResult<CursorInfoData> &Result) {
|
|
EXPECT_TRUE(Result.isCancelled());
|
|
FirstCursorInfoSema.signal();
|
|
});
|
|
|
|
auto Info = getCursor(FastDocName, FastOffset, Args,
|
|
/*CancellationToken=*/nullptr,
|
|
/*CancelOnSubsequentRequest=*/true);
|
|
EXPECT_STREQ("foo", Info.Name.c_str());
|
|
EXPECT_STREQ("Int", Info.Typename.c_str());
|
|
EXPECT_EQ(FastOffset, Info.Offset);
|
|
EXPECT_EQ(strlen("foo"), Info.Length);
|
|
|
|
bool expired = FirstCursorInfoSema.wait(30 * 1000);
|
|
if (expired)
|
|
llvm::report_fatal_error("Did not receive a response for the first request");
|
|
}
|
|
|
|
TEST_F(CursorInfoTest, DISABLED_CursorInfoCancellation) {
|
|
// Disabled due to a race condition (rdar://88652757)
|
|
|
|
// TODO: This test case relies on the following snippet being slow to type
|
|
// check so that the first cursor info request takes longer to execute than it
|
|
// takes time to schedule the second request. If that is fixed, we need to
|
|
// find a new way to cause slow type checking. rdar://80582770
|
|
const char *SlowDocName = "slow.swift";
|
|
const char *SlowContents = "func foo(x: Invalid1, y: Invalid2) {\n"
|
|
" x / y / x / y / x / y / x / y\n"
|
|
"}\n";
|
|
auto SlowOffset = findOffset("x", SlowContents);
|
|
const char *Args[] = {"-parse-as-library"};
|
|
std::vector<const char *> ArgsForSlow = llvm::ArrayRef(Args).vec();
|
|
ArgsForSlow.push_back(SlowDocName);
|
|
|
|
open(SlowDocName, SlowContents, llvm::ArrayRef(Args));
|
|
|
|
SourceKitCancellationToken CancellationToken = createCancellationToken();
|
|
|
|
// Schedule a cursor info request that takes long to execute. This should be
|
|
// cancelled as the next cursor info (which is faster) gets requested.
|
|
Semaphore CursorInfoSema(0);
|
|
getLang().getCursorInfo(
|
|
SlowDocName, SlowDocName, SlowOffset, /*Length=*/0, /*Actionables=*/false,
|
|
/*SymbolGraph=*/false, /*CancelOnSubsequentRequest=*/false, ArgsForSlow,
|
|
/*vfsOptions=*/std::nullopt, /*CancellationToken=*/CancellationToken,
|
|
[&](const RequestResult<CursorInfoData> &Result) {
|
|
EXPECT_TRUE(Result.isCancelled());
|
|
CursorInfoSema.signal();
|
|
});
|
|
|
|
getContext().getRequestTracker()->cancel(CancellationToken);
|
|
|
|
bool expired = CursorInfoSema.wait(30 * 1000);
|
|
if (expired)
|
|
llvm::report_fatal_error("Did not receive a response for the first request");
|
|
}
|