|
14 | 14 | #include "swift/Basic/Edit.h"
|
15 | 15 | #include "swift/Basic/SourceManager.h"
|
16 | 16 | #include "swift/ClangImporter/ClangModule.h"
|
| 17 | +#include "swift/Driver/FrontendUtil.h" |
17 | 18 | #include "swift/Frontend/Frontend.h"
|
18 | 19 | #include "swift/Parse/Parser.h"
|
19 | 20 | #include "swift/Subsystems.h"
|
@@ -226,6 +227,190 @@ static std::string adjustClangTriple(StringRef TripleStr) {
|
226 | 227 | return Result;
|
227 | 228 | }
|
228 | 229 |
|
| 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 | + |
229 | 414 | bool ide::initInvocationByClangArguments(ArrayRef<const char *> ArgList,
|
230 | 415 | CompilerInvocation &Invok,
|
231 | 416 | std::string &Error) {
|
|
0 commit comments