//===--- ArgsToFrontendOptionsConverter -----------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2018 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 "ArgsToFrontendOptionsConverter.h" #include "ArgsToFrontendInputsConverter.h" #include "ArgsToFrontendOutputsConverter.h" #include "swift/AST/DiagnosticsFrontend.h" #include "swift/Basic/Platform.h" #include "swift/Frontend/Frontend.h" #include "swift/Option/Options.h" #include "swift/Option/SanitizerOptions.h" #include "swift/Parse/Lexer.h" #include "swift/Strings.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/Triple.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include "llvm/Option/Option.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/LineIterator.h" #include "llvm/Support/Path.h" using namespace swift; using namespace llvm::opt; // This is a separate function so that it shows up in stack traces. LLVM_ATTRIBUTE_NOINLINE static void debugFailWithAssertion() { // This assertion should always fail, per the user's request, and should // not be converted to llvm_unreachable. assert(0 && "This is an assertion!"); } // This is a separate function so that it shows up in stack traces. LLVM_ATTRIBUTE_NOINLINE static void debugFailWithCrash() { LLVM_BUILTIN_TRAP; } bool ArgsToFrontendOptionsConverter::convert( SmallVectorImpl> *buffers) { using namespace options; handleDebugCrashGroupArguments(); if (const Arg *A = Args.getLastArg(OPT_dump_api_path)) { Opts.DumpAPIPath = A->getValue(); } if (const Arg *A = Args.getLastArg(OPT_group_info_path)) { Opts.GroupInfoPath = A->getValue(); } if (const Arg *A = Args.getLastArg(OPT_index_store_path)) { Opts.IndexStorePath = A->getValue(); } if (const Arg *A = Args.getLastArg(OPT_prebuilt_module_cache_path)) { Opts.PrebuiltModuleCachePath = A->getValue(); } Opts.IndexSystemModules |= Args.hasArg(OPT_index_system_modules); Opts.EmitVerboseSIL |= Args.hasArg(OPT_emit_verbose_sil); Opts.EmitSortedSIL |= Args.hasArg(OPT_emit_sorted_sil); Opts.EnableTesting |= Args.hasArg(OPT_enable_testing); Opts.EnablePrivateImports |= Args.hasArg(OPT_enable_private_imports); Opts.EnableLibraryEvolution |= Args.hasArg(OPT_enable_library_evolution); // FIXME: Remove this flag Opts.EnableLibraryEvolution |= Args.hasArg(OPT_enable_resilience); Opts.EnableImplicitDynamic |= Args.hasArg(OPT_enable_implicit_dynamic); Opts.TrackSystemDeps |= Args.hasArg(OPT_track_system_dependencies); Opts.SerializeModuleInterfaceDependencyHashes |= Args.hasArg(OPT_serialize_module_interface_dependency_hashes); Opts.RemarkOnRebuildFromModuleInterface |= Args.hasArg(OPT_Rmodule_interface_rebuild); Opts.DisableInterfaceFileLock |= Args.hasArg(OPT_disable_interface_lockfile); computePrintStatsOptions(); computeDebugTimeOptions(); computeTBDOptions(); Opts.CheckOnoneSupportCompleteness = Args.hasArg(OPT_check_onone_completeness); Opts.DebuggerTestingTransform = Args.hasArg(OPT_debugger_testing_transform); computePlaygroundOptions(); // This can be enabled independently of the playground transform. Opts.PCMacro |= Args.hasArg(OPT_pc_macro); Opts.ParseStdlib |= Args.hasArg(OPT_parse_stdlib); Opts.IgnoreSwiftSourceInfo |= Args.hasArg(OPT_ignore_module_source_info); computeHelpOptions(); if (Args.hasArg(OPT_print_target_info)) { Opts.PrintTargetInfo = true; } if (const Arg *A = Args.getLastArg(OPT_verify_generic_signatures)) { Opts.VerifyGenericSignaturesInModule = A->getValue(); } computeDumpScopeMapLocations(); Optional inputsAndOutputs = ArgsToFrontendInputsConverter(Diags, Args).convert(buffers); // None here means error, not just "no inputs". Propagage unconditionally. if (!inputsAndOutputs) return true; // InputsAndOutputs can only get set up once; if it was set already when we // entered this function, we should not set it again (and should assert this // is not being done). Further, the computeMainAndSupplementaryOutputFilenames // call below needs to only happen when there was a new InputsAndOutputs, // since it clobbers the existing one rather than adding to it. bool HaveNewInputsAndOutputs = false; if (Opts.InputsAndOutputs.hasInputs()) { assert(!inputsAndOutputs->hasInputs()); } else { HaveNewInputsAndOutputs = true; Opts.InputsAndOutputs = std::move(inputsAndOutputs).getValue(); } if (Opts.RequestedAction == FrontendOptions::ActionType::NoneAction) { Opts.RequestedAction = determineRequestedAction(Args); } if (Opts.RequestedAction == FrontendOptions::ActionType::Immediate && Opts.InputsAndOutputs.hasPrimaryInputs()) { Diags.diagnose(SourceLoc(), diag::error_immediate_mode_primary_file); return true; } if (setUpInputKindAndImmediateArgs()) return true; if (computeModuleName()) return true; if (HaveNewInputsAndOutputs && computeMainAndSupplementaryOutputFilenames()) return true; if (checkUnusedSupplementaryOutputPaths()) return true; if (FrontendOptions::doesActionGenerateIR(Opts.RequestedAction) && Args.hasArg(OPT_experimental_skip_non_inlinable_function_bodies)) { Diags.diagnose(SourceLoc(), diag::cannot_emit_ir_skipping_function_bodies); return true; } if (const Arg *A = Args.getLastArg(OPT_module_link_name)) Opts.ModuleLinkName = A->getValue(); if (const Arg *A = Args.getLastArg(OPT_serialize_debugging_options, OPT_no_serialize_debugging_options)) { Opts.SerializeOptionsForDebugging = A->getOption().matches(OPT_serialize_debugging_options); } Opts.EnableSourceImport |= Args.hasArg(OPT_enable_source_import); Opts.ImportUnderlyingModule |= Args.hasArg(OPT_import_underlying_module); Opts.VerifyDependencies |= Args.hasArg(OPT_verify_incremental_dependencies); computeImportObjCHeaderOptions(); computeImplicitImportModuleNames(); computeLLVMArgs(); return false; } void ArgsToFrontendOptionsConverter::handleDebugCrashGroupArguments() { using namespace options; if (const Arg *A = Args.getLastArg(OPT_debug_crash_Group)) { Option Opt = A->getOption(); if (Opt.matches(OPT_debug_assert_immediately)) { debugFailWithAssertion(); } else if (Opt.matches(OPT_debug_crash_immediately)) { debugFailWithCrash(); } else if (Opt.matches(OPT_debug_assert_after_parse)) { // Set in FrontendOptions Opts.CrashMode = FrontendOptions::DebugCrashMode::AssertAfterParse; } else if (Opt.matches(OPT_debug_crash_after_parse)) { // Set in FrontendOptions Opts.CrashMode = FrontendOptions::DebugCrashMode::CrashAfterParse; } else { llvm_unreachable("Unknown debug_crash_Group option!"); } } } void ArgsToFrontendOptionsConverter::computePrintStatsOptions() { using namespace options; Opts.PrintStats |= Args.hasArg(OPT_print_stats); Opts.PrintClangStats |= Args.hasArg(OPT_print_clang_stats); #if defined(NDEBUG) && !defined(LLVM_ENABLE_STATS) if (Opts.PrintStats || Opts.PrintClangStats) Diags.diagnose(SourceLoc(), diag::stats_disabled); #endif } void ArgsToFrontendOptionsConverter::computeDebugTimeOptions() { using namespace options; Opts.DebugTimeCompilation |= Args.hasArg(OPT_debug_time_compilation); if (const Arg *A = Args.getLastArg(OPT_stats_output_dir)) { Opts.StatsOutputDir = A->getValue(); if (Args.getLastArg(OPT_trace_stats_events)) { Opts.TraceStats = true; } if (Args.getLastArg(OPT_profile_stats_events)) { Opts.ProfileEvents = true; } if (Args.getLastArg(OPT_profile_stats_entities)) { Opts.ProfileEntities = true; } } } void ArgsToFrontendOptionsConverter::computeTBDOptions() { using namespace options; if (const Arg *A = Args.getLastArg(OPT_validate_tbd_against_ir_EQ)) { using Mode = FrontendOptions::TBDValidationMode; StringRef value = A->getValue(); if (value == "none") { Opts.ValidateTBDAgainstIR = Mode::None; } else if (value == "missing") { Opts.ValidateTBDAgainstIR = Mode::MissingFromTBD; } else if (value == "all") { Opts.ValidateTBDAgainstIR = Mode::All; } else { Diags.diagnose(SourceLoc(), diag::error_unsupported_option_argument, A->getOption().getPrefixedName(), value); } } } void ArgsToFrontendOptionsConverter::computePlaygroundOptions() { using namespace options; Opts.PlaygroundTransform |= Args.hasArg(OPT_playground); if (Args.hasArg(OPT_disable_playground_transform)) Opts.PlaygroundTransform = false; Opts.PlaygroundHighPerformance |= Args.hasArg(OPT_playground_high_performance); } void ArgsToFrontendOptionsConverter::computeHelpOptions() { using namespace options; if (const Arg *A = Args.getLastArg(OPT_help, OPT_help_hidden)) { if (A->getOption().matches(OPT_help)) { Opts.PrintHelp = true; } else if (A->getOption().matches(OPT_help_hidden)) { Opts.PrintHelpHidden = true; } else { llvm_unreachable("Unknown help option parsed"); } } } void ArgsToFrontendOptionsConverter::computeDumpScopeMapLocations() { using namespace options; const Arg *A = Args.getLastArg(OPT_modes_Group); if (!A || !A->getOption().matches(OPT_dump_scope_maps)) return; StringRef value = A->getValue(); if (value == "expanded") { // Note: fully expanded the scope map. return; } // Parse a comma-separated list of line:column for lookups to // perform (and dump the result of). SmallVector locations; value.split(locations, ','); bool invalid = false; for (auto location : locations) { auto lineColumnStr = location.split(':'); unsigned line, column; if (lineColumnStr.first.getAsInteger(10, line) || lineColumnStr.second.getAsInteger(10, column)) { Diags.diagnose(SourceLoc(), diag::error_invalid_source_location_str, location); invalid = true; continue; } Opts.DumpScopeMapLocations.push_back({line, column}); } if (!invalid && Opts.DumpScopeMapLocations.empty()) Diags.diagnose(SourceLoc(), diag::error_no_source_location_scope_map); } FrontendOptions::ActionType ArgsToFrontendOptionsConverter::determineRequestedAction(const ArgList &args) { using namespace options; const Arg *A = args.getLastArg(OPT_modes_Group); if (!A) { // We don't have a mode, so determine a default. if (args.hasArg(OPT_emit_module, OPT_emit_module_path)) { // We've been told to emit a module, but have no other mode indicators. // As a result, put the frontend into EmitModuleOnly mode. // (Setting up module output will be handled below.) return FrontendOptions::ActionType::EmitModuleOnly; } return FrontendOptions::ActionType::NoneAction; } Option Opt = A->getOption(); if (Opt.matches(OPT_emit_object)) return FrontendOptions::ActionType::EmitObject; if (Opt.matches(OPT_emit_assembly)) return FrontendOptions::ActionType::EmitAssembly; if (Opt.matches(OPT_emit_ir)) return FrontendOptions::ActionType::EmitIR; if (Opt.matches(OPT_emit_bc)) return FrontendOptions::ActionType::EmitBC; if (Opt.matches(OPT_emit_sil)) return FrontendOptions::ActionType::EmitSIL; if (Opt.matches(OPT_emit_silgen)) return FrontendOptions::ActionType::EmitSILGen; if (Opt.matches(OPT_emit_sib)) return FrontendOptions::ActionType::EmitSIB; if (Opt.matches(OPT_emit_sibgen)) return FrontendOptions::ActionType::EmitSIBGen; if (Opt.matches(OPT_emit_pch)) return FrontendOptions::ActionType::EmitPCH; if (Opt.matches(OPT_emit_imported_modules)) return FrontendOptions::ActionType::EmitImportedModules; if (Opt.matches(OPT_parse)) return FrontendOptions::ActionType::Parse; if (Opt.matches(OPT_resolve_imports)) return FrontendOptions::ActionType::ResolveImports; if (Opt.matches(OPT_typecheck)) return FrontendOptions::ActionType::Typecheck; if (Opt.matches(OPT_dump_parse)) return FrontendOptions::ActionType::DumpParse; if (Opt.matches(OPT_dump_ast)) return FrontendOptions::ActionType::DumpAST; if (Opt.matches(OPT_emit_syntax)) return FrontendOptions::ActionType::EmitSyntax; if (Opt.matches(OPT_merge_modules)) return FrontendOptions::ActionType::MergeModules; if (Opt.matches(OPT_dump_scope_maps)) return FrontendOptions::ActionType::DumpScopeMaps; if (Opt.matches(OPT_dump_type_refinement_contexts)) return FrontendOptions::ActionType::DumpTypeRefinementContexts; if (Opt.matches(OPT_dump_interface_hash)) return FrontendOptions::ActionType::DumpInterfaceHash; if (Opt.matches(OPT_dump_type_info)) return FrontendOptions::ActionType::DumpTypeInfo; if (Opt.matches(OPT_print_ast)) return FrontendOptions::ActionType::PrintAST; if (Opt.matches(OPT_emit_pcm)) return FrontendOptions::ActionType::EmitPCM; if (Opt.matches(OPT_dump_pcm)) return FrontendOptions::ActionType::DumpPCM; if (Opt.matches(OPT_repl) || Opt.matches(OPT_deprecated_integrated_repl)) return FrontendOptions::ActionType::REPL; if (Opt.matches(OPT_interpret)) return FrontendOptions::ActionType::Immediate; if (Opt.matches(OPT_compile_module_from_interface)) return FrontendOptions::ActionType::CompileModuleFromInterface; llvm_unreachable("Unhandled mode option"); } bool ArgsToFrontendOptionsConverter::setUpInputKindAndImmediateArgs() { using namespace options; bool treatAsSIL = Args.hasArg(OPT_parse_sil) || Opts.InputsAndOutputs.shouldTreatAsSIL(); if (Opts.InputsAndOutputs.verifyInputs( Diags, treatAsSIL, Opts.RequestedAction == FrontendOptions::ActionType::REPL, Opts.RequestedAction == FrontendOptions::ActionType::NoneAction)) { return true; } if (Opts.RequestedAction == FrontendOptions::ActionType::Immediate) { Opts.ImmediateArgv.push_back( Opts.InputsAndOutputs.getFilenameOfFirstInput()); // argv[0] if (const Arg *A = Args.getLastArg(OPT__DASH_DASH)) { for (unsigned i = 0, e = A->getNumValues(); i != e; ++i) { Opts.ImmediateArgv.push_back(A->getValue(i)); } } } if (treatAsSIL) Opts.InputKind = InputFileKind::SIL; else if (Opts.InputsAndOutputs.shouldTreatAsLLVM()) Opts.InputKind = InputFileKind::LLVM; else if (Opts.InputsAndOutputs.shouldTreatAsModuleInterface()) Opts.InputKind = InputFileKind::SwiftModuleInterface; else if (Args.hasArg(OPT_parse_as_library)) Opts.InputKind = InputFileKind::SwiftLibrary; else if (Opts.RequestedAction == FrontendOptions::ActionType::REPL) Opts.InputKind = InputFileKind::SwiftREPL; else Opts.InputKind = InputFileKind::Swift; return false; } bool ArgsToFrontendOptionsConverter::computeModuleName() { const Arg *A = Args.getLastArg(options::OPT_module_name); if (A) { Opts.ModuleName = A->getValue(); } else if (Opts.ModuleName.empty()) { // The user did not specify a module name, so determine a default fallback // based on other options. // Note: this code path will only be taken when running the frontend // directly; the driver should always pass -module-name when invoking the // frontend. if (computeFallbackModuleName()) return true; } if (Lexer::isIdentifier(Opts.ModuleName) && (Opts.ModuleName != STDLIB_NAME || Opts.ParseStdlib)) { return false; } if (!FrontendOptions::needsProperModuleName(Opts.RequestedAction) || Opts.isCompilingExactlyOneSwiftFile()) { Opts.ModuleName = "main"; return false; } auto DID = (Opts.ModuleName == STDLIB_NAME) ? diag::error_stdlib_module_name : diag::error_bad_module_name; Diags.diagnose(SourceLoc(), DID, Opts.ModuleName, A == nullptr); Opts.ModuleName = "__bad__"; return false; // FIXME: Must continue to run to pass the tests, but should not // have to. } bool ArgsToFrontendOptionsConverter::computeFallbackModuleName() { if (Opts.RequestedAction == FrontendOptions::ActionType::REPL) { // Default to a module named "REPL" if we're in REPL mode. Opts.ModuleName = "REPL"; return false; } // In order to pass some tests, must leave ModuleName empty. if (!Opts.InputsAndOutputs.hasInputs()) { Opts.ModuleName = StringRef(); // FIXME: This is a bug that should not happen, but does in tests. // The compiler should bail out earlier, where "no frontend action was // selected". return false; } Optional> outputFilenames = OutputFilesComputer::getOutputFilenamesFromCommandLineOrFilelist(Args, Diags); std::string nameToStem = outputFilenames && outputFilenames->size() == 1 && outputFilenames->front() != "-" && !llvm::sys::fs::is_directory(outputFilenames->front()) ? outputFilenames->front() : Opts.InputsAndOutputs.getFilenameOfFirstInput(); Opts.ModuleName = llvm::sys::path::stem(nameToStem); return false; } bool ArgsToFrontendOptionsConverter:: computeMainAndSupplementaryOutputFilenames() { std::vector mainOutputs; std::vector supplementaryOutputs; const bool hadError = ArgsToFrontendOutputsConverter( Args, Opts.ModuleName, Opts.InputsAndOutputs, Diags) .convert(mainOutputs, supplementaryOutputs); if (hadError) return true; Opts.InputsAndOutputs.setMainAndSupplementaryOutputs(mainOutputs, supplementaryOutputs); return false; } bool ArgsToFrontendOptionsConverter::checkUnusedSupplementaryOutputPaths() const { if (!FrontendOptions::canActionEmitDependencies(Opts.RequestedAction) && Opts.InputsAndOutputs.hasDependenciesPath()) { Diags.diagnose(SourceLoc(), diag::error_mode_cannot_emit_dependencies); return true; } if (!FrontendOptions::canActionEmitReferenceDependencies(Opts.RequestedAction) && Opts.InputsAndOutputs.hasReferenceDependenciesPath()) { Diags.diagnose(SourceLoc(), diag::error_mode_cannot_emit_reference_dependencies); return true; } if (!FrontendOptions::canActionEmitSwiftRanges(Opts.RequestedAction) && Opts.InputsAndOutputs.hasSwiftRangesPath()) { Diags.diagnose(SourceLoc(), diag::error_mode_cannot_emit_swift_ranges); return true; } if (!FrontendOptions::canActionEmitCompiledSource(Opts.RequestedAction) && Opts.InputsAndOutputs.hasCompiledSourcePath()) { Diags.diagnose(SourceLoc(), diag::error_mode_cannot_emit_compiled_source); return true; } if (!FrontendOptions::canActionEmitObjCHeader(Opts.RequestedAction) && Opts.InputsAndOutputs.hasObjCHeaderOutputPath()) { Diags.diagnose(SourceLoc(), diag::error_mode_cannot_emit_header); return true; } if (!FrontendOptions::canActionEmitLoadedModuleTrace(Opts.RequestedAction) && Opts.InputsAndOutputs.hasLoadedModuleTracePath()) { Diags.diagnose(SourceLoc(), diag::error_mode_cannot_emit_loaded_module_trace); return true; } if (!FrontendOptions::canActionEmitModule(Opts.RequestedAction) && Opts.InputsAndOutputs.hasModuleOutputPath()) { Diags.diagnose(SourceLoc(), diag::error_mode_cannot_emit_module); return true; } if (!FrontendOptions::canActionEmitModuleDoc(Opts.RequestedAction) && Opts.InputsAndOutputs.hasModuleDocOutputPath()) { Diags.diagnose(SourceLoc(), diag::error_mode_cannot_emit_module_doc); return true; } // If we cannot emit module doc, we cannot emit source information file either. if (!FrontendOptions::canActionEmitModuleDoc(Opts.RequestedAction) && Opts.InputsAndOutputs.hasModuleSourceInfoOutputPath()) { Diags.diagnose(SourceLoc(), diag::error_mode_cannot_emit_module_source_info); return true; } if (!FrontendOptions::canActionEmitInterface(Opts.RequestedAction) && Opts.InputsAndOutputs.hasModuleInterfaceOutputPath()) { Diags.diagnose(SourceLoc(), diag::error_mode_cannot_emit_interface); return true; } return false; } void ArgsToFrontendOptionsConverter::computeImportObjCHeaderOptions() { using namespace options; if (const Arg *A = Args.getLastArgNoClaim(OPT_import_objc_header)) { Opts.ImplicitObjCHeaderPath = A->getValue(); Opts.SerializeBridgingHeader |= !Opts.InputsAndOutputs.hasPrimaryInputs(); } } void ArgsToFrontendOptionsConverter::computeImplicitImportModuleNames() { using namespace options; for (const Arg *A : Args.filtered(OPT_import_module)) { Opts.ImplicitImportModuleNames.push_back(A->getValue()); } } void ArgsToFrontendOptionsConverter::computeLLVMArgs() { using namespace options; for (const Arg *A : Args.filtered(OPT_Xllvm)) { Opts.LLVMArgs.push_back(A->getValue()); } }