Skip to content

Commit af78895

Browse files
committed
[gardening] Sink compiler invocation code into libIDE
1 parent cf87ad8 commit af78895

File tree

3 files changed

+194
-165
lines changed

3 files changed

+194
-165
lines changed

include/swift/IDE/Utils.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,14 @@ SourceCompleteResult
8181
isSourceInputComplete(std::unique_ptr<llvm::MemoryBuffer> MemBuf, SourceFileKind SFKind);
8282
SourceCompleteResult isSourceInputComplete(StringRef Text, SourceFileKind SFKind);
8383

84+
bool initCompilerInvocation(
85+
CompilerInvocation &Invocation, ArrayRef<const char *> OrigArgs,
86+
DiagnosticEngine &Diags, StringRef UnresolvedPrimaryFile,
87+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FileSystem,
88+
const std::string &runtimeResourcePath,
89+
const std::string &diagnosticDocumentationPath,
90+
bool shouldOptimizeForIDE, time_t sessionTimestamp, std::string &Error);
91+
8492
bool initInvocationByClangArguments(ArrayRef<const char *> ArgList,
8593
CompilerInvocation &Invok,
8694
std::string &Error);

lib/IDE/Utils.cpp

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "swift/Basic/Edit.h"
1515
#include "swift/Basic/SourceManager.h"
1616
#include "swift/ClangImporter/ClangModule.h"
17+
#include "swift/Driver/FrontendUtil.h"
1718
#include "swift/Frontend/Frontend.h"
1819
#include "swift/Parse/Parser.h"
1920
#include "swift/Subsystems.h"
@@ -226,6 +227,190 @@ static std::string adjustClangTriple(StringRef TripleStr) {
226227
return Result;
227228
}
228229

230+
static FrontendInputsAndOutputs resolveSymbolicLinksInInputs(
231+
FrontendInputsAndOutputs &inputsAndOutputs, StringRef UnresolvedPrimaryFile,
232+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FileSystem,
233+
std::string &Error) {
234+
assert(FileSystem);
235+
236+
llvm::SmallString<128> PrimaryFile;
237+
if (auto err = FileSystem->getRealPath(UnresolvedPrimaryFile, PrimaryFile))
238+
PrimaryFile = UnresolvedPrimaryFile;
239+
240+
unsigned primaryCount = 0;
241+
// FIXME: The frontend should be dealing with symlinks, maybe similar to
242+
// clang's FileManager ?
243+
FrontendInputsAndOutputs replacementInputsAndOutputs;
244+
for (const InputFile &input : inputsAndOutputs.getAllInputs()) {
245+
llvm::SmallString<128> newFilename;
246+
if (auto err = FileSystem->getRealPath(input.file(), newFilename))
247+
newFilename = input.file();
248+
llvm::sys::path::native(newFilename);
249+
bool newIsPrimary = input.isPrimary() ||
250+
(!PrimaryFile.empty() && PrimaryFile == newFilename);
251+
if (newIsPrimary) {
252+
++primaryCount;
253+
}
254+
assert(primaryCount < 2 && "cannot handle multiple primaries");
255+
replacementInputsAndOutputs.addInput(
256+
InputFile(newFilename.str(), newIsPrimary, input.buffer()));
257+
}
258+
259+
if (PrimaryFile.empty() || primaryCount == 1) {
260+
return replacementInputsAndOutputs;
261+
}
262+
263+
llvm::SmallString<64> Err;
264+
llvm::raw_svector_ostream OS(Err);
265+
OS << "'" << PrimaryFile << "' is not part of the input files";
266+
Error = std::string(OS.str());
267+
return replacementInputsAndOutputs;
268+
}
269+
270+
static void disableExpensiveSILOptions(SILOptions &Opts) {
271+
// Disable the sanitizers.
272+
Opts.Sanitizers = {};
273+
274+
// Disable PGO and code coverage.
275+
Opts.GenerateProfile = false;
276+
Opts.EmitProfileCoverageMapping = false;
277+
Opts.UseProfile = "";
278+
}
279+
280+
namespace {
281+
class StreamDiagConsumer : public DiagnosticConsumer {
282+
llvm::raw_ostream &OS;
283+
284+
public:
285+
StreamDiagConsumer(llvm::raw_ostream &OS) : OS(OS) {}
286+
287+
void handleDiagnostic(SourceManager &SM,
288+
const DiagnosticInfo &Info) override {
289+
// FIXME: Print location info if available.
290+
switch (Info.Kind) {
291+
case DiagnosticKind::Error:
292+
OS << "error: ";
293+
break;
294+
case DiagnosticKind::Warning:
295+
OS << "warning: ";
296+
break;
297+
case DiagnosticKind::Note:
298+
OS << "note: ";
299+
break;
300+
case DiagnosticKind::Remark:
301+
OS << "remark: ";
302+
break;
303+
}
304+
DiagnosticEngine::formatDiagnosticText(OS, Info.FormatString,
305+
Info.FormatArgs);
306+
}
307+
};
308+
} // end anonymous namespace
309+
310+
bool ide::initCompilerInvocation(
311+
CompilerInvocation &Invocation, ArrayRef<const char *> OrigArgs,
312+
DiagnosticEngine &Diags, StringRef UnresolvedPrimaryFile,
313+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FileSystem,
314+
const std::string &runtimeResourcePath,
315+
const std::string &diagnosticDocumentationPath,
316+
bool shouldOptimizeForIDE, time_t sessionTimestamp, std::string &Error) {
317+
SmallVector<const char *, 16> Args;
318+
// Make sure to put '-resource-dir' and '-diagnostic-documentation-path' at
319+
// the top to allow overriding them with the passed in arguments.
320+
Args.push_back("-resource-dir");
321+
Args.push_back(runtimeResourcePath.c_str());
322+
Args.push_back("-Xfrontend");
323+
Args.push_back("-diagnostic-documentation-path");
324+
Args.push_back("-Xfrontend");
325+
Args.push_back(diagnosticDocumentationPath.c_str());
326+
Args.append(OrigArgs.begin(), OrigArgs.end());
327+
328+
SmallString<32> ErrStr;
329+
llvm::raw_svector_ostream ErrOS(ErrStr);
330+
StreamDiagConsumer DiagConsumer(ErrOS);
331+
Diags.addConsumer(DiagConsumer);
332+
333+
bool HadError = driver::getSingleFrontendInvocationFromDriverArguments(
334+
Args, Diags, [&](ArrayRef<const char *> FrontendArgs) {
335+
return Invocation.parseArgs(FrontendArgs, Diags);
336+
}, /*ForceNoOutputs=*/true);
337+
338+
// Remove the StreamDiagConsumer as it's no longer needed.
339+
Diags.removeConsumer(DiagConsumer);
340+
341+
if (HadError) {
342+
Error = std::string(ErrOS.str());
343+
return true;
344+
}
345+
346+
Invocation.getFrontendOptions().InputsAndOutputs =
347+
resolveSymbolicLinksInInputs(
348+
Invocation.getFrontendOptions().InputsAndOutputs,
349+
UnresolvedPrimaryFile, FileSystem, Error);
350+
if (!Error.empty())
351+
return true;
352+
353+
ClangImporterOptions &ImporterOpts = Invocation.getClangImporterOptions();
354+
ImporterOpts.DetailedPreprocessingRecord = true;
355+
356+
assert(!Invocation.getModuleName().empty());
357+
358+
auto &LangOpts = Invocation.getLangOptions();
359+
LangOpts.AttachCommentsToDecls = true;
360+
LangOpts.DiagnosticsEditorMode = true;
361+
LangOpts.CollectParsedToken = true;
362+
if (LangOpts.PlaygroundTransform) {
363+
// The playground instrumenter changes the AST in ways that disrupt the
364+
// SourceKit functionality. Since we don't need the instrumenter, and all we
365+
// actually need is the playground semantics visible to the user, like
366+
// silencing the "expression resolves to an unused l-value" error, disable it.
367+
LangOpts.PlaygroundTransform = false;
368+
}
369+
370+
// Disable the index-store functionality for the sourcekitd requests.
371+
auto &FrontendOpts = Invocation.getFrontendOptions();
372+
FrontendOpts.IndexStorePath.clear();
373+
ImporterOpts.IndexStorePath.clear();
374+
375+
// Force the action type to be -typecheck. This affects importing the
376+
// SwiftONoneSupport module.
377+
FrontendOpts.RequestedAction = FrontendOptions::ActionType::Typecheck;
378+
379+
// We don't care about LLVMArgs
380+
FrontendOpts.LLVMArgs.clear();
381+
382+
// SwiftSourceInfo files provide source location information for decls coming
383+
// from loaded modules. For most IDE use cases it either has an undesirable
384+
// impact on performance with no benefit (code completion), results in stale
385+
// locations being used instead of more up-to-date indexer locations (cursor
386+
// info), or has no observable effect (diagnostics, which are filtered to just
387+
// those with a location in the primary file, and everything else).
388+
if (shouldOptimizeForIDE)
389+
FrontendOpts.IgnoreSwiftSourceInfo = true;
390+
391+
// To save the time for module validation, consider the lifetime of ASTManager
392+
// as a single build session.
393+
// NOTE: Do this only if '-disable-modules-validate-system-headers' is *not*
394+
// explicitly enabled.
395+
auto &SearchPathOpts = Invocation.getSearchPathOptions();
396+
if (!SearchPathOpts.DisableModulesValidateSystemDependencies) {
397+
// NOTE: 'SessionTimestamp - 1' because clang compares it with '<=' that may
398+
// cause unnecessary validations if they happens within one second
399+
// from the SourceKit startup.
400+
ImporterOpts.ExtraArgs.push_back("-fbuild-session-timestamp=" +
401+
std::to_string(sessionTimestamp - 1));
402+
ImporterOpts.ExtraArgs.push_back(
403+
"-fmodules-validate-once-per-build-session");
404+
405+
SearchPathOpts.DisableModulesValidateSystemDependencies = true;
406+
}
407+
408+
// Disable expensive SIL options to reduce time spent in SILGen.
409+
disableExpensiveSILOptions(Invocation.getSILOptions());
410+
411+
return false;
412+
}
413+
229414
bool ide::initInvocationByClangArguments(ArrayRef<const char *> ArgList,
230415
CompilerInvocation &Invok,
231416
std::string &Error) {

0 commit comments

Comments
 (0)