unique_ptr<Module> #endif compile(LLVMContext* context, const vector<Chars_const> compileArgs) { vector<Chars_const> args = { "-Xclang", "-resource-dir", "-Xclang", "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/clang/6.0", }; for (auto const& s : compileArgs) { args.push_back(s); } const IntrusiveRefCntPtr<clang::CompilerInvocation> invocation = clang::createInvocationFromCommandLine(args); clang::CompilerInstance compiler; compiler.setInvocation(invocation.get()); auto diagConsumer = new clang::TextDiagnosticPrinter(errs(), &compiler.getDiagnosticOpts()); compiler.createDiagnostics(diagConsumer, true); // shouldOwnClient=true. clang::EmitLLVMOnlyAction action(context); check(compiler.ExecuteAction(action), "ExecuteAction failed"); return action.takeModule(); }
TEST_F(VFSFromYAMLTest, CaseSensitive) { IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); Lower->addRegularFile("//root/foo/bar/a"); IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString("{ 'case-sensitive': 'true',\n" " 'roots': [\n" "{\n" " 'type': 'directory',\n" " 'name': '//root/',\n" " 'contents': [ {\n" " 'type': 'file',\n" " 'name': 'XX',\n" " 'external-contents': '//root/foo/bar/a'\n" " }\n" " ]\n" "}]}", Lower); ASSERT_TRUE(FS.getPtr() != nullptr); IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( new vfs::OverlayFileSystem(Lower)); O->pushOverlay(FS); ErrorOr<vfs::Status> SS = O->status("//root/xx"); EXPECT_EQ(std::errc::no_such_file_or_directory, SS.getError()); SS = O->status("//root/xX"); EXPECT_EQ(std::errc::no_such_file_or_directory, SS.getError()); SS = O->status("//root/Xx"); EXPECT_EQ(std::errc::no_such_file_or_directory, SS.getError()); EXPECT_EQ(0, NumDiagnostics); }
TEST(VirtualFileSystemTest, BasicRealFSIteration) { ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/true); IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem(); std::error_code EC; vfs::directory_iterator I = FS->dir_begin(Twine(TestDirectory), EC); ASSERT_FALSE(EC); EXPECT_EQ(vfs::directory_iterator(), I); // empty directory is empty ScopedDir _a(TestDirectory+"/a"); ScopedDir _ab(TestDirectory+"/a/b"); ScopedDir _c(TestDirectory+"/c"); ScopedDir _cd(TestDirectory+"/c/d"); I = FS->dir_begin(Twine(TestDirectory), EC); ASSERT_FALSE(EC); ASSERT_NE(vfs::directory_iterator(), I); // Check either a or c, since we can't rely on the iteration order. EXPECT_TRUE(I->getName().endswith("a") || I->getName().endswith("c")); I.increment(EC); ASSERT_FALSE(EC); ASSERT_NE(vfs::directory_iterator(), I); EXPECT_TRUE(I->getName().endswith("a") || I->getName().endswith("c")); I.increment(EC); EXPECT_EQ(vfs::directory_iterator(), I); }
TEST_F(VFSFromYAMLTest, BasicVFSFromYAML) { IntrusiveRefCntPtr<vfs::FileSystem> FS; FS = getFromYAMLString(""); EXPECT_EQ(nullptr, FS.getPtr()); FS = getFromYAMLString("[]"); EXPECT_EQ(nullptr, FS.getPtr()); FS = getFromYAMLString("'string'"); EXPECT_EQ(nullptr, FS.getPtr()); EXPECT_EQ(3, NumDiagnostics); }
TEST_F(VFSFromYAMLTest, MappedFiles) { IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); Lower->addRegularFile("//root/foo/bar/a"); IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString("{ 'roots': [\n" "{\n" " 'type': 'directory',\n" " 'name': '//root/',\n" " 'contents': [ {\n" " 'type': 'file',\n" " 'name': 'file1',\n" " 'external-contents': '//root/foo/bar/a'\n" " },\n" " {\n" " 'type': 'file',\n" " 'name': 'file2',\n" " 'external-contents': '//root/foo/b'\n" " }\n" " ]\n" "}\n" "]\n" "}", Lower); ASSERT_TRUE(FS.getPtr() != nullptr); IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( new vfs::OverlayFileSystem(Lower)); O->pushOverlay(FS); // file ErrorOr<vfs::Status> S = O->status("//root/file1"); ASSERT_FALSE(S.getError()); EXPECT_EQ("//root/foo/bar/a", S->getName()); ErrorOr<vfs::Status> SLower = O->status("//root/foo/bar/a"); EXPECT_EQ("//root/foo/bar/a", SLower->getName()); EXPECT_TRUE(S->equivalent(*SLower)); // directory S = O->status("//root/"); ASSERT_FALSE(S.getError()); EXPECT_TRUE(S->isDirectory()); EXPECT_TRUE(S->equivalent(*O->status("//root/"))); // non-volatile UniqueID // broken mapping EXPECT_EQ(std::errc::no_such_file_or_directory, O->status("//root/file2").getError()); EXPECT_EQ(0, NumDiagnostics); }
TEST_F(VFSFromYAMLTest, DirectoryIteration) { IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); Lower->addDirectory("//root/"); Lower->addDirectory("//root/foo"); Lower->addDirectory("//root/foo/bar"); Lower->addRegularFile("//root/foo/bar/a"); Lower->addRegularFile("//root/foo/bar/b"); Lower->addRegularFile("//root/file3"); IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString("{ 'use-external-names': false,\n" " 'roots': [\n" "{\n" " 'type': 'directory',\n" " 'name': '//root/',\n" " 'contents': [ {\n" " 'type': 'file',\n" " 'name': 'file1',\n" " 'external-contents': '//root/foo/bar/a'\n" " },\n" " {\n" " 'type': 'file',\n" " 'name': 'file2',\n" " 'external-contents': '//root/foo/bar/b'\n" " }\n" " ]\n" "}\n" "]\n" "}", Lower); ASSERT_TRUE(FS.get() != NULL); IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( new vfs::OverlayFileSystem(Lower)); O->pushOverlay(FS); std::error_code EC; { const char *Contents[] = {"//root/file1", "//root/file2", "//root/file3", "//root/foo"}; checkContents(O->dir_begin("//root/", EC), makeStringRefVector(Contents)); } { const char *Contents[] = {"//root/foo/bar/a", "//root/foo/bar/b"}; checkContents(O->dir_begin("//root/foo/bar", EC), makeStringRefVector(Contents)); } }
IntrusiveRefCntPtr<VarDecl> Parser::resolveLocal( IntrusiveRefCntPtr<Scope> sc, string const& name) { if (!sc) return IntrusiveRefCntPtr<VarDecl>(); return sc->resolveLocal(name); }
TEST_F(VFSFromYAMLTest, UseExternalName) { IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); Lower->addRegularFile("//root/external/file"); IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( "{ 'roots': [\n" " { 'type': 'file', 'name': '//root/A',\n" " 'external-contents': '//root/external/file'\n" " },\n" " { 'type': 'file', 'name': '//root/B',\n" " 'use-external-name': true,\n" " 'external-contents': '//root/external/file'\n" " },\n" " { 'type': 'file', 'name': '//root/C',\n" " 'use-external-name': false,\n" " 'external-contents': '//root/external/file'\n" " }\n" "] }", Lower); ASSERT_TRUE(nullptr != FS.getPtr()); // default true EXPECT_EQ("//root/external/file", FS->status("//root/A")->getName()); // explicit EXPECT_EQ("//root/external/file", FS->status("//root/B")->getName()); EXPECT_EQ("//root/C", FS->status("//root/C")->getName()); // global configuration FS = getFromYAMLString( "{ 'use-external-names': false,\n" " 'roots': [\n" " { 'type': 'file', 'name': '//root/A',\n" " 'external-contents': '//root/external/file'\n" " },\n" " { 'type': 'file', 'name': '//root/B',\n" " 'use-external-name': true,\n" " 'external-contents': '//root/external/file'\n" " },\n" " { 'type': 'file', 'name': '//root/C',\n" " 'use-external-name': false,\n" " 'external-contents': '//root/external/file'\n" " }\n" "] }", Lower); ASSERT_TRUE(nullptr != FS.getPtr()); // default EXPECT_EQ("//root/A", FS->status("//root/A")->getName()); // explicit EXPECT_EQ("//root/external/file", FS->status("//root/B")->getName()); EXPECT_EQ("//root/C", FS->status("//root/C")->getName()); }
TEST_F(VFSFromYAMLTest, TrailingSlashes) { IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); Lower->addRegularFile("//root/other"); // file in roots IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( "{ 'roots': [\n" " { 'type': 'directory', 'name': '//root/path/to////',\n" " 'contents': [ { 'type': 'file', 'name': 'file',\n" " 'external-contents': '//root/other' }]}]\n" "}", Lower); ASSERT_TRUE(nullptr != FS.getPtr()); EXPECT_FALSE(FS->status("//root/path/to/file").getError()); EXPECT_FALSE(FS->status("//root/path/to").getError()); EXPECT_FALSE(FS->status("//root/path").getError()); EXPECT_FALSE(FS->status("//root/").getError()); }
ExpandModularHeadersPPCallbacks::ExpandModularHeadersPPCallbacks( CompilerInstance *CI, IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFS) : Recorder(llvm::make_unique<FileRecorder>()), Compiler(*CI), InMemoryFs(new llvm::vfs::InMemoryFileSystem), Sources(Compiler.getSourceManager()), // Forward the new diagnostics to the original DiagnosticConsumer. Diags(new DiagnosticIDs, new DiagnosticOptions, new ForwardingDiagnosticConsumer(Compiler.getDiagnosticClient())), LangOpts(Compiler.getLangOpts()) { // Add a FileSystem containing the extra files needed in place of modular // headers. OverlayFS->pushOverlay(InMemoryFs); Diags.setSourceManager(&Sources); LangOpts.Modules = false; auto HSO = std::make_shared<HeaderSearchOptions>(); *HSO = Compiler.getHeaderSearchOpts(); HeaderInfo = llvm::make_unique<HeaderSearch>(HSO, Sources, Diags, LangOpts, &Compiler.getTarget()); auto PO = std::make_shared<PreprocessorOptions>(); *PO = Compiler.getPreprocessorOpts(); PP = llvm::make_unique<clang::Preprocessor>(PO, Diags, LangOpts, Sources, *HeaderInfo, ModuleLoader, /*IILookup=*/nullptr, /*OwnsHeaderSearch=*/false); PP->Initialize(Compiler.getTarget(), Compiler.getAuxTarget()); InitializePreprocessor(*PP, *PO, Compiler.getPCHContainerReader(), Compiler.getFrontendOpts()); ApplyHeaderSearchOptions(*HeaderInfo, *HSO, LangOpts, Compiler.getTarget().getTriple()); }
TEST_F(VFSFromYAMLTest, IllegalVFSFile) { IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); // invalid YAML at top-level IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString("{]", Lower); EXPECT_EQ(nullptr, FS.getPtr()); // invalid YAML in roots FS = getFromYAMLString("{ 'roots':[}", Lower); // invalid YAML in directory FS = getFromYAMLString( "{ 'roots':[ { 'name': 'foo', 'type': 'directory', 'contents': [}", Lower); EXPECT_EQ(nullptr, FS.getPtr()); // invalid configuration FS = getFromYAMLString("{ 'knobular': 'true', 'roots':[] }", Lower); EXPECT_EQ(nullptr, FS.getPtr()); FS = getFromYAMLString("{ 'case-sensitive': 'maybe', 'roots':[] }", Lower); EXPECT_EQ(nullptr, FS.getPtr()); // invalid roots FS = getFromYAMLString("{ 'roots':'' }", Lower); EXPECT_EQ(nullptr, FS.getPtr()); FS = getFromYAMLString("{ 'roots':{} }", Lower); EXPECT_EQ(nullptr, FS.getPtr()); // invalid entries FS = getFromYAMLString( "{ 'roots':[ { 'type': 'other', 'name': 'me', 'contents': '' }", Lower); EXPECT_EQ(nullptr, FS.getPtr()); FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': [], " "'external-contents': 'other' }", Lower); EXPECT_EQ(nullptr, FS.getPtr()); FS = getFromYAMLString( "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': [] }", Lower); EXPECT_EQ(nullptr, FS.getPtr()); FS = getFromYAMLString( "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': {} }", Lower); EXPECT_EQ(nullptr, FS.getPtr()); FS = getFromYAMLString( "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': {} }", Lower); EXPECT_EQ(nullptr, FS.getPtr()); FS = getFromYAMLString( "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': '' }", Lower); EXPECT_EQ(nullptr, FS.getPtr()); FS = getFromYAMLString( "{ 'roots':[ { 'thingy': 'directory', 'name': 'me', 'contents': [] }", Lower); EXPECT_EQ(nullptr, FS.getPtr()); // missing mandatory fields FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': 'me' }", Lower); EXPECT_EQ(nullptr, FS.getPtr()); FS = getFromYAMLString( "{ 'roots':[ { 'type': 'file', 'external-contents': 'other' }", Lower); EXPECT_EQ(nullptr, FS.getPtr()); FS = getFromYAMLString("{ 'roots':[ { 'name': 'me', 'contents': [] }", Lower); EXPECT_EQ(nullptr, FS.getPtr()); // duplicate keys FS = getFromYAMLString("{ 'roots':[], 'roots':[] }", Lower); EXPECT_EQ(nullptr, FS.getPtr()); FS = getFromYAMLString( "{ 'case-sensitive':'true', 'case-sensitive':'true', 'roots':[] }", Lower); EXPECT_EQ(nullptr, FS.getPtr()); FS = getFromYAMLString("{ 'roots':[{'name':'me', 'name':'you', 'type':'file', " "'external-contents':'blah' } ] }", Lower); EXPECT_EQ(nullptr, FS.getPtr()); // missing version FS = getFromYAMLRawString("{ 'roots':[] }", Lower); EXPECT_EQ(nullptr, FS.getPtr()); // bad version number FS = getFromYAMLRawString("{ 'version':'foo', 'roots':[] }", Lower); EXPECT_EQ(nullptr, FS.getPtr()); FS = getFromYAMLRawString("{ 'version':-1, 'roots':[] }", Lower); EXPECT_EQ(nullptr, FS.getPtr()); FS = getFromYAMLRawString("{ 'version':100000, 'roots':[] }", Lower); EXPECT_EQ(nullptr, FS.getPtr()); EXPECT_EQ(24, NumDiagnostics); }
/// \brief Compile a module file for the given module, using the options /// provided by the importing compiler instance. static void compileModule(CompilerInstance &ImportingInstance, SourceLocation ImportLoc, Module *Module, StringRef ModuleFileName) { llvm::LockFileManager Locked(ModuleFileName); switch (Locked) { case llvm::LockFileManager::LFS_Error: return; case llvm::LockFileManager::LFS_Owned: // We're responsible for building the module ourselves. Do so below. break; case llvm::LockFileManager::LFS_Shared: // Someone else is responsible for building the module. Wait for them to // finish. Locked.waitForUnlock(); return; } ModuleMap &ModMap = ImportingInstance.getPreprocessor().getHeaderSearchInfo().getModuleMap(); // Construct a compiler invocation for creating this module. IntrusiveRefCntPtr<CompilerInvocation> Invocation (new CompilerInvocation(ImportingInstance.getInvocation())); PreprocessorOptions &PPOpts = Invocation->getPreprocessorOpts(); // For any options that aren't intended to affect how a module is built, // reset them to their default values. Invocation->getLangOpts()->resetNonModularOptions(); PPOpts.resetNonModularOptions(); // Note the name of the module we're building. Invocation->getLangOpts()->CurrentModule = Module->getTopLevelModuleName(); // Make sure that the failed-module structure has been allocated in // the importing instance, and propagate the pointer to the newly-created // instance. PreprocessorOptions &ImportingPPOpts = ImportingInstance.getInvocation().getPreprocessorOpts(); if (!ImportingPPOpts.FailedModules) ImportingPPOpts.FailedModules = new PreprocessorOptions::FailedModulesSet; PPOpts.FailedModules = ImportingPPOpts.FailedModules; // If there is a module map file, build the module using the module map. // Set up the inputs/outputs so that we build the module from its umbrella // header. FrontendOptions &FrontendOpts = Invocation->getFrontendOpts(); FrontendOpts.OutputFile = ModuleFileName.str(); FrontendOpts.DisableFree = false; FrontendOpts.Inputs.clear(); InputKind IK = getSourceInputKindFromOptions(*Invocation->getLangOpts()); // Get or create the module map that we'll use to build this module. SmallString<128> TempModuleMapFileName; if (const FileEntry *ModuleMapFile = ModMap.getContainingModuleMapFile(Module)) { // Use the module map where this module resides. FrontendOpts.Inputs.push_back(FrontendInputFile(ModuleMapFile->getName(), IK)); } else { // Create a temporary module map file. TempModuleMapFileName = Module->Name; TempModuleMapFileName += "-%%%%%%%%.map"; int FD; if (llvm::sys::fs::unique_file(TempModuleMapFileName.str(), FD, TempModuleMapFileName, /*makeAbsolute=*/true) != llvm::errc::success) { ImportingInstance.getDiagnostics().Report(diag::err_module_map_temp_file) << TempModuleMapFileName; return; } // Print the module map to this file. llvm::raw_fd_ostream OS(FD, /*shouldClose=*/true); Module->print(OS); FrontendOpts.Inputs.push_back( FrontendInputFile(TempModuleMapFileName.str().str(), IK)); } // Don't free the remapped file buffers; they are owned by our caller. PPOpts.RetainRemappedFileBuffers = true; Invocation->getDiagnosticOpts().VerifyDiagnostics = 0; assert(ImportingInstance.getInvocation().getModuleHash() == Invocation->getModuleHash() && "Module hash mismatch!"); // Construct a compiler instance that will be used to actually create the // module. CompilerInstance Instance; Instance.setInvocation(&*Invocation); Instance.createDiagnostics(/*argc=*/0, /*argv=*/0, &ImportingInstance.getDiagnosticClient(), /*ShouldOwnClient=*/true, /*ShouldCloneClient=*/true); // Note that this module is part of the module build stack, so that we // can detect cycles in the module graph. Instance.createFileManager(); // FIXME: Adopt file manager from importer? Instance.createSourceManager(Instance.getFileManager()); SourceManager &SourceMgr = Instance.getSourceManager(); SourceMgr.setModuleBuildStack( ImportingInstance.getSourceManager().getModuleBuildStack()); SourceMgr.pushModuleBuildStack(Module->getTopLevelModuleName(), FullSourceLoc(ImportLoc, ImportingInstance.getSourceManager())); // Construct a module-generating action. GenerateModuleAction CreateModuleAction; // Execute the action to actually build the module in-place. Use a separate // thread so that we get a stack large enough. const unsigned ThreadStackSize = 8 << 20; llvm::CrashRecoveryContext CRC; CompileModuleMapData Data = { Instance, CreateModuleAction }; CRC.RunSafelyOnThread(&doCompileMapModule, &Data, ThreadStackSize); // Delete the temporary module map file. // FIXME: Even though we're executing under crash protection, it would still // be nice to do this with RemoveFileOnSignal when we can. However, that // doesn't make sense for all clients, so clean this up manually. Instance.clearOutputFiles(/*EraseFiles=*/true); if (!TempModuleMapFileName.empty()) llvm::sys::Path(TempModuleMapFileName).eraseFromDisk(); }
void clang::ProcessWarningOptions(DiagnosticsEngine &Diags, const DiagnosticOptions &Opts, bool ReportDiags) { Diags.setSuppressSystemWarnings(true); // Default to -Wno-system-headers Diags.setIgnoreAllWarnings(Opts.IgnoreWarnings); Diags.setShowOverloads(Opts.getShowOverloads()); Diags.setElideType(Opts.ElideType); Diags.setPrintTemplateTree(Opts.ShowTemplateTree); Diags.setWarnOnSpellCheck(Opts.WarnOnSpellCheck); Diags.setShowColors(Opts.ShowColors); // Handle -ferror-limit if (Opts.ErrorLimit) Diags.setErrorLimit(Opts.ErrorLimit); if (Opts.TemplateBacktraceLimit) Diags.setTemplateBacktraceLimit(Opts.TemplateBacktraceLimit); if (Opts.ConstexprBacktraceLimit) Diags.setConstexprBacktraceLimit(Opts.ConstexprBacktraceLimit); // If -pedantic or -pedantic-errors was specified, then we want to map all // extension diagnostics onto WARNING or ERROR unless the user has futz'd // around with them explicitly. if (Opts.PedanticErrors) Diags.setExtensionHandlingBehavior(DiagnosticsEngine::Ext_Error); else if (Opts.Pedantic) Diags.setExtensionHandlingBehavior(DiagnosticsEngine::Ext_Warn); else Diags.setExtensionHandlingBehavior(DiagnosticsEngine::Ext_Ignore); SmallVector<diag::kind, 10> _Diags; const IntrusiveRefCntPtr< DiagnosticIDs > DiagIDs = Diags.getDiagnosticIDs(); // We parse the warning options twice. The first pass sets diagnostic state, // while the second pass reports warnings/errors. This has the effect that // we follow the more canonical "last option wins" paradigm when there are // conflicting options. for (unsigned Report = 0, ReportEnd = 2; Report != ReportEnd; ++Report) { bool SetDiagnostic = (Report == 0); // If we've set the diagnostic state and are not reporting diagnostics then // we're done. if (!SetDiagnostic && !ReportDiags) break; for (unsigned i = 0, e = Opts.Warnings.size(); i != e; ++i) { StringRef Opt = Opts.Warnings[i]; StringRef OrigOpt = Opts.Warnings[i]; // Treat -Wformat=0 as an alias for -Wno-format. if (Opt == "format=0") Opt = "no-format"; // Check to see if this warning starts with "no-", if so, this is a // negative form of the option. bool isPositive = true; if (Opt.startswith("no-")) { isPositive = false; Opt = Opt.substr(3); } // Figure out how this option affects the warning. If -Wfoo, map the // diagnostic to a warning, if -Wno-foo, map it to ignore. diag::Mapping Mapping = isPositive ? diag::MAP_WARNING : diag::MAP_IGNORE; // -Wsystem-headers is a special case, not driven by the option table. It // cannot be controlled with -Werror. if (Opt == "system-headers") { if (SetDiagnostic) Diags.setSuppressSystemWarnings(!isPositive); continue; } // -Weverything is a special case as well. It implicitly enables all // warnings, including ones not explicitly in a warning group. if (Opt == "everything") { if (SetDiagnostic) { if (isPositive) { Diags.setEnableAllWarnings(true); } else { Diags.setEnableAllWarnings(false); Diags.setMappingToAllDiagnostics(diag::MAP_IGNORE); } } continue; } // -Werror/-Wno-error is a special case, not controlled by the option // table. It also has the "specifier" form of -Werror=foo and -Werror-foo. if (Opt.startswith("error")) { StringRef Specifier; if (Opt.size() > 5) { // Specifier must be present. if ((Opt[5] != '=' && Opt[5] != '-') || Opt.size() == 6) { if (Report) Diags.Report(diag::warn_unknown_warning_specifier) << "-Werror" << ("-W" + OrigOpt.str()); continue; } Specifier = Opt.substr(6); } if (Specifier.empty()) { if (SetDiagnostic) Diags.setWarningsAsErrors(isPositive); continue; } if (SetDiagnostic) { // Set the warning as error flag for this specifier. Diags.setDiagnosticGroupWarningAsError(Specifier, isPositive); } else if (DiagIDs->getDiagnosticsInGroup(Specifier, _Diags)) { EmitUnknownDiagWarning(Diags, "-Werror=", Specifier, isPositive); } continue; } // -Wfatal-errors is yet another special case. if (Opt.startswith("fatal-errors")) { StringRef Specifier; if (Opt.size() != 12) { if ((Opt[12] != '=' && Opt[12] != '-') || Opt.size() == 13) { if (Report) Diags.Report(diag::warn_unknown_warning_specifier) << "-Wfatal-errors" << ("-W" + OrigOpt.str()); continue; } Specifier = Opt.substr(13); } if (Specifier.empty()) { if (SetDiagnostic) Diags.setErrorsAsFatal(isPositive); continue; } if (SetDiagnostic) { // Set the error as fatal flag for this specifier. Diags.setDiagnosticGroupErrorAsFatal(Specifier, isPositive); } else if (DiagIDs->getDiagnosticsInGroup(Specifier, _Diags)) { EmitUnknownDiagWarning(Diags, "-Wfatal-errors=", Specifier, isPositive); } continue; } if (Report) { if (DiagIDs->getDiagnosticsInGroup(Opt, _Diags)) EmitUnknownDiagWarning(Diags, isPositive ? "-W" : "-Wno-", Opt, isPositive); } else { Diags.setDiagnosticGroupMapping(Opt, Mapping); } } } }
int ShowEnabledWarnings::run(unsigned int argc, char **argv, raw_ostream &Out) { // First check our one flag (--levels). bool ShouldShowLevels = true; if (argc > 0) { StringRef FirstArg(*argv); if (FirstArg.equals("--no-levels")) { ShouldShowLevels = false; --argc; ++argv; } else if (FirstArg.equals("--levels")) { ShouldShowLevels = true; --argc; ++argv; } } // Create the diagnostic engine. IntrusiveRefCntPtr<DiagnosticsEngine> Diags = createDiagnostics(argc, argv); if (!Diags) { printUsage(); return EXIT_FAILURE; } // Now we have our diagnostics. Iterate through EVERY diagnostic and see // which ones are turned on. // FIXME: It would be very nice to print which flags are turning on which // diagnostics, but this can be done with a diff. ArrayRef<DiagnosticRecord> AllDiagnostics = getBuiltinDiagnosticsByName(); std::vector<PrettyDiag> Active; for (ArrayRef<DiagnosticRecord>::iterator I = AllDiagnostics.begin(), E = AllDiagnostics.end(); I != E; ++I) { unsigned DiagID = I->DiagID; if (DiagnosticIDs::isBuiltinNote(DiagID)) continue; if (!DiagnosticIDs::isBuiltinWarningOrExtension(DiagID)) continue; DiagnosticsEngine::Level DiagLevel = Diags->getDiagnosticLevel(DiagID, SourceLocation()); if (DiagLevel == DiagnosticsEngine::Ignored) continue; StringRef WarningOpt = DiagnosticIDs::getWarningOptionForDiag(DiagID); Active.push_back(PrettyDiag(I->getName(), WarningOpt, DiagLevel)); } // Print them all out. for (std::vector<PrettyDiag>::const_iterator I = Active.begin(), E = Active.end(); I != E; ++I) { if (ShouldShowLevels) Out << getCharForLevel(I->Level) << " "; Out << I->Name; if (!I->Flag.empty()) Out << " [-W" << I->Flag << "]"; Out << '\n'; } return EXIT_SUCCESS; }
ASTUnit* TranslationUnitManager::ParseProjectFile(ProjectFile* file,bool allowAdd) { cbAssert (file && file->file.FileExists() && "File not exists"); wxString fileName = file->file.GetFullPath(); auto project = file->GetParentProject(); { //if the file is already being parsed return immediately. std::lock_guard<std::mutex> lock(m_FilesBeingParsedMutex); if (std::find(m_FilesBeingParsed.begin(), m_FilesBeingParsed.end(), file) != m_FilesBeingParsed.end()) return nullptr; m_FilesBeingParsed.push_back(file); } auto iter = m_CompilationDatabases.find(project); if(iter == m_CompilationDatabases.end()) { LoggerAccess::Get()->Log("Could not find the compilation database for project : " + project->GetTitle()); return nullptr; } auto compileCommands = iter->second->getCompileCommands(wx2std(file->file.GetFullPath())); if (compileCommands.empty()) { LoggerAccess::Get()->Log("No compile commands for the file" + file->file.GetName()); return nullptr; } auto args = compileCommands[0].CommandLine; if (!Options::Get().ShouldSpellCheck()) args.push_back("-fno-spell-checking"); //Get Additional compile options from the config panel auto addOptions = Options::Get().GetCompilerOptions(); args.insert(args.end(), addOptions.begin(), addOptions.end()); #ifdef CLANGCC_DEBUG_LOGGING wxString commandLine = "Command Line is "; std::for_each(args.begin(),args.end(), [&](const std::string s){ commandLine.Append(s); commandLine.append(' '); }); LoggerAccess::Get()->Log(commandLine); #endif //Parsing started event. ccEvent startEvent(ccEVT_PARSE_START, fileName, nullptr, file); AddPendingEvent(startEvent); #ifdef CLANGCC_TIMING wxStopWatch watch; #endif // CLANGCC_TIMING std::vector<const char*> argsinChar; argsinChar.reserve(args.size()); std::transform(args.begin(), args.end(), std::back_inserter(argsinChar), [](const std::string& strings){return strings.c_str();}); DiagnosticOptions* diagOpts = new DiagnosticOptions(); IntrusiveRefCntPtr<DiagnosticsEngine> diags = CompilerInstance::createDiagnostics(diagOpts,0); diags->setFatalsAsError(true); auto ast = ASTUnit::LoadFromCommandLine(&*argsinChar.begin(),&*argsinChar.end(), m_PCHContainerOps, diags, StringRef(), true, /* OnlyLocalDecls */ true, /*CaptureDiagnostics*/ None, /*Remapped Files*/ true, /*RemappedFiles keep original name*/ 1, /*Precompile preamble*/ TU_Complete, Options::Get().ShouldCacheCompletionResults(),/*CacheCodeCompletionResults*/ true, /*Include Brief Comment*/ true, /*allow pch with compiler errors*/ Options::Get().ShouldSkipFunctionBodies(), true, false, m_PCHContainerOps->getRawReader().getFormat() ); #ifdef CLANGCC_TIMING LoggerAccess::Get()->Log(wxString::Format("Parsing %s completed in %ldms", file->file.GetFullName().c_str(), watch.Time()),Logger::info); #endif // CLANGCC_TIMING if (!ast) LoggerAccess::Get()->Log("\t Cannot Create ASTUnit for " + file->file.GetFullName()); if (ast) { if (allowAdd) AddASTUnitForProjectFile(file,ast); } { //File is free again std::lock_guard<std::mutex> lock(m_FilesBeingParsedMutex); m_FilesBeingParsed.erase(std::remove(m_FilesBeingParsed.begin(), m_FilesBeingParsed.end(), file),m_FilesBeingParsed.end()); } //Parsing ended event ccEvent endEvent(ccEVT_PARSE_END, fileName, ast, file); AddPendingEvent(endEvent); return ast; }
bool FrontendAction::BeginSourceFile(CompilerInstance &CI, const FrontendInputFile &Input) { assert(!Instance && "Already processing a source file!"); assert(!Input.isEmpty() && "Unexpected empty filename!"); setCurrentInput(Input); setCompilerInstance(&CI); StringRef InputFile = Input.getFile(); bool HasBegunSourceFile = false; if (!BeginInvocation(CI)) goto failure; // AST files follow a very different path, since they share objects via the // AST unit. if (Input.getKind() == IK_AST) { assert(!usesPreprocessorOnly() && "Attempt to pass AST file to preprocessor only action!"); assert(hasASTFileSupport() && "This action does not have AST file support!"); IntrusiveRefCntPtr<DiagnosticsEngine> Diags(&CI.getDiagnostics()); ASTUnit *AST = ASTUnit::LoadFromASTFile(InputFile, Diags, CI.getFileSystemOpts()); if (!AST) goto failure; setCurrentInput(Input, AST); // Inform the diagnostic client we are processing a source file. CI.getDiagnosticClient().BeginSourceFile(CI.getLangOpts(), 0); HasBegunSourceFile = true; // Set the shared objects, these are reset when we finish processing the // file, otherwise the CompilerInstance will happily destroy them. CI.setFileManager(&AST->getFileManager()); CI.setSourceManager(&AST->getSourceManager()); CI.setPreprocessor(&AST->getPreprocessor()); CI.setASTContext(&AST->getASTContext()); // Initialize the action. if (!BeginSourceFileAction(CI, InputFile)) goto failure; // Create the AST consumer. CI.setASTConsumer(CreateWrappedASTConsumer(CI, InputFile)); if (!CI.hasASTConsumer()) goto failure; return true; } if (!CI.hasVirtualFileSystem()) { if (IntrusiveRefCntPtr<vfs::FileSystem> VFS = createVFSFromCompilerInvocation(CI.getInvocation(), CI.getDiagnostics())) CI.setVirtualFileSystem(VFS); else goto failure; } // Set up the file and source managers, if needed. if (!CI.hasFileManager()) CI.createFileManager(); if (!CI.hasSourceManager()) CI.createSourceManager(CI.getFileManager()); // IR files bypass the rest of initialization. if (Input.getKind() == IK_LLVM_IR) { assert(hasIRSupport() && "This action does not have IR file support!"); // Inform the diagnostic client we are processing a source file. CI.getDiagnosticClient().BeginSourceFile(CI.getLangOpts(), 0); HasBegunSourceFile = true; // Initialize the action. if (!BeginSourceFileAction(CI, InputFile)) goto failure; // Initialize the main file entry. if (!CI.InitializeSourceManager(CurrentInput)) goto failure; return true; } // If the implicit PCH include is actually a directory, rather than // a single file, search for a suitable PCH file in that directory. if (!CI.getPreprocessorOpts().ImplicitPCHInclude.empty()) { FileManager &FileMgr = CI.getFileManager(); PreprocessorOptions &PPOpts = CI.getPreprocessorOpts(); StringRef PCHInclude = PPOpts.ImplicitPCHInclude; if (const DirectoryEntry *PCHDir = FileMgr.getDirectory(PCHInclude)) { llvm::error_code EC; SmallString<128> DirNative; llvm::sys::path::native(PCHDir->getName(), DirNative); bool Found = false; for (llvm::sys::fs::directory_iterator Dir(DirNative.str(), EC), DirEnd; Dir != DirEnd && !EC; Dir.increment(EC)) { // Check whether this is an acceptable AST file. if (ASTReader::isAcceptableASTFile(Dir->path(), FileMgr, CI.getLangOpts(), CI.getTargetOpts(), CI.getPreprocessorOpts())) { PPOpts.ImplicitPCHInclude = Dir->path(); Found = true; break; } } if (!Found) { CI.getDiagnostics().Report(diag::err_fe_no_pch_in_dir) << PCHInclude; return true; } } } // Set up the preprocessor. CI.createPreprocessor(getTranslationUnitKind()); // Inform the diagnostic client we are processing a source file. CI.getDiagnosticClient().BeginSourceFile(CI.getLangOpts(), &CI.getPreprocessor()); HasBegunSourceFile = true; // Initialize the action. if (!BeginSourceFileAction(CI, InputFile)) goto failure; // Initialize the main file entry. It is important that this occurs after // BeginSourceFileAction, which may change CurrentInput during module builds. if (!CI.InitializeSourceManager(CurrentInput)) goto failure; // Create the AST context and consumer unless this is a preprocessor only // action. if (!usesPreprocessorOnly()) { CI.createASTContext(); std::unique_ptr<ASTConsumer> Consumer( CreateWrappedASTConsumer(CI, InputFile)); if (!Consumer) goto failure; CI.getASTContext().setASTMutationListener(Consumer->GetASTMutationListener()); if (!CI.getPreprocessorOpts().ChainedIncludes.empty()) { // Convert headers to PCH and chain them. IntrusiveRefCntPtr<ChainedIncludesSource> source; source = ChainedIncludesSource::create(CI); if (!source) goto failure; CI.setModuleManager(static_cast<ASTReader*>(&source->getFinalReader())); CI.getASTContext().setExternalSource(source); } else if (!CI.getPreprocessorOpts().ImplicitPCHInclude.empty()) { // Use PCH. assert(hasPCHSupport() && "This action does not have PCH support!"); ASTDeserializationListener *DeserialListener = Consumer->GetASTDeserializationListener(); if (CI.getPreprocessorOpts().DumpDeserializedPCHDecls) DeserialListener = new DeserializedDeclsDumper(DeserialListener); if (!CI.getPreprocessorOpts().DeserializedPCHDeclsToErrorOn.empty()) DeserialListener = new DeserializedDeclsChecker(CI.getASTContext(), CI.getPreprocessorOpts().DeserializedPCHDeclsToErrorOn, DeserialListener); CI.createPCHExternalASTSource( CI.getPreprocessorOpts().ImplicitPCHInclude, CI.getPreprocessorOpts().DisablePCHValidation, CI.getPreprocessorOpts().AllowPCHWithCompilerErrors, DeserialListener); if (!CI.getASTContext().getExternalSource()) goto failure; } CI.setASTConsumer(Consumer.release()); if (!CI.hasASTConsumer()) goto failure; } // Initialize built-in info as long as we aren't using an external AST // source. if (!CI.hasASTContext() || !CI.getASTContext().getExternalSource()) { Preprocessor &PP = CI.getPreprocessor(); PP.getBuiltinInfo().InitializeBuiltins(PP.getIdentifierTable(), PP.getLangOpts()); } // If there is a layout overrides file, attach an external AST source that // provides the layouts from that file. if (!CI.getFrontendOpts().OverrideRecordLayoutsFile.empty() && CI.hasASTContext() && !CI.getASTContext().getExternalSource()) { IntrusiveRefCntPtr<ExternalASTSource> Override(new LayoutOverrideSource( CI.getFrontendOpts().OverrideRecordLayoutsFile)); CI.getASTContext().setExternalSource(Override); } return true; // If we failed, reset state since the client will not end up calling the // matching EndSourceFile(). failure: if (isCurrentFileAST()) { CI.setASTContext(0); CI.setPreprocessor(0); CI.setSourceManager(0); CI.setFileManager(0); } if (HasBegunSourceFile) CI.getDiagnosticClient().EndSourceFile(); CI.clearOutputFiles(/*EraseFiles=*/true); setCurrentInput(FrontendInputFile()); setCompilerInstance(0); return false; }
void PlistDiagnostics::FlushDiagnosticsImpl( std::vector<const PathDiagnostic *> &Diags, FilesMade *filesMade) { // Build up a set of FIDs that we use by scanning the locations and // ranges of the diagnostics. FIDMap FM; SmallVector<FileID, 10> Fids; const SourceManager* SM = 0; if (!Diags.empty()) SM = &(*(*Diags.begin())->path.begin())->getLocation().getManager(); for (std::vector<const PathDiagnostic*>::iterator DI = Diags.begin(), DE = Diags.end(); DI != DE; ++DI) { const PathDiagnostic *D = *DI; llvm::SmallVector<const PathPieces *, 5> WorkList; WorkList.push_back(&D->path); while (!WorkList.empty()) { const PathPieces &path = *WorkList.back(); WorkList.pop_back(); for (PathPieces::const_iterator I = path.begin(), E = path.end(); I!=E; ++I) { const PathDiagnosticPiece *piece = I->getPtr(); AddFID(FM, Fids, SM, piece->getLocation().asLocation()); ArrayRef<SourceRange> Ranges = piece->getRanges(); for (ArrayRef<SourceRange>::iterator I = Ranges.begin(), E = Ranges.end(); I != E; ++I) { AddFID(FM, Fids, SM, I->getBegin()); AddFID(FM, Fids, SM, I->getEnd()); } if (const PathDiagnosticCallPiece *call = dyn_cast<PathDiagnosticCallPiece>(piece)) { IntrusiveRefCntPtr<PathDiagnosticEventPiece> callEnterWithin = call->getCallEnterWithinCallerEvent(); if (callEnterWithin) AddFID(FM, Fids, SM, callEnterWithin->getLocation().asLocation()); WorkList.push_back(&call->path); } else if (const PathDiagnosticMacroPiece *macro = dyn_cast<PathDiagnosticMacroPiece>(piece)) { WorkList.push_back(¯o->subPieces); } } } } // Open the file. std::string ErrMsg; llvm::raw_fd_ostream o(OutputFile.c_str(), ErrMsg); if (!ErrMsg.empty()) { llvm::errs() << "warning: could not create file: " << OutputFile << '\n'; return; } // Write the plist header. o << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" " "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" "<plist version=\"1.0\">\n"; // Write the root object: a <dict> containing... // - "files", an <array> mapping from FIDs to file names // - "diagnostics", an <array> containing the path diagnostics o << "<dict>\n" " <key>files</key>\n" " <array>\n"; for (SmallVectorImpl<FileID>::iterator I=Fids.begin(), E=Fids.end(); I!=E; ++I) { o << " "; EmitString(o, SM->getFileEntryForID(*I)->getName()) << '\n'; } o << " </array>\n" " <key>diagnostics</key>\n" " <array>\n"; for (std::vector<const PathDiagnostic*>::iterator DI=Diags.begin(), DE = Diags.end(); DI!=DE; ++DI) { o << " <dict>\n" " <key>path</key>\n"; const PathDiagnostic *D = *DI; o << " <array>\n"; for (PathPieces::const_iterator I = D->path.begin(), E = D->path.end(); I != E; ++I) ReportDiag(o, **I, FM, *SM, LangOpts); o << " </array>\n"; // Output the bug type and bug category. o << " <key>description</key>"; EmitString(o, D->getShortDescription()) << '\n'; o << " <key>category</key>"; EmitString(o, D->getCategory()) << '\n'; o << " <key>type</key>"; EmitString(o, D->getBugType()) << '\n'; // Output information about the semantic context where // the issue occurred. if (const Decl *DeclWithIssue = D->getDeclWithIssue()) { // FIXME: handle blocks, which have no name. if (const NamedDecl *ND = dyn_cast<NamedDecl>(DeclWithIssue)) { StringRef declKind; switch (ND->getKind()) { case Decl::CXXRecord: declKind = "C++ class"; break; case Decl::CXXMethod: declKind = "C++ method"; break; case Decl::ObjCMethod: declKind = "Objective-C method"; break; case Decl::Function: declKind = "function"; break; default: break; } if (!declKind.empty()) { const std::string &declName = ND->getDeclName().getAsString(); o << " <key>issue_context_kind</key>"; EmitString(o, declKind) << '\n'; o << " <key>issue_context</key>"; EmitString(o, declName) << '\n'; } // Output the bug hash for issue unique-ing. Currently, it's just an // offset from the beginning of the function. if (const Stmt *Body = DeclWithIssue->getBody()) { FullSourceLoc Loc(SM->getExpansionLoc(D->getLocation().asLocation()), *SM); FullSourceLoc FunLoc(SM->getExpansionLoc(Body->getLocStart()), *SM); o << " <key>issue_hash</key><integer>" << Loc.getExpansionLineNumber() - FunLoc.getExpansionLineNumber() << "</integer>\n"; } } } // Output the location of the bug. o << " <key>location</key>\n"; EmitLocation(o, *SM, LangOpts, D->getLocation(), FM, 2); // Output the diagnostic to the sub-diagnostic client, if any. if (!filesMade->empty()) { StringRef lastName; PDFileEntry::ConsumerFiles *files = filesMade->getFiles(*D); if (files) { for (PDFileEntry::ConsumerFiles::const_iterator CI = files->begin(), CE = files->end(); CI != CE; ++CI) { StringRef newName = CI->first; if (newName != lastName) { if (!lastName.empty()) { o << " </array>\n"; } lastName = newName; o << " <key>" << lastName << "_files</key>\n"; o << " <array>\n"; } o << " <string>" << CI->second << "</string>\n"; } o << " </array>\n"; } } // Close up the entry. o << " </dict>\n"; } o << " </array>\n"; // Finish. o << "</dict>\n</plist>"; }
void PlistDiagnostics::FlushDiagnosticsImpl( std::vector<const PathDiagnostic *> &Diags, FilesMade *filesMade) { // Build up a set of FIDs that we use by scanning the locations and // ranges of the diagnostics. FIDMap FM; SmallVector<FileID, 10> Fids; const SourceManager* SM = nullptr; if (!Diags.empty()) SM = &(*(*Diags.begin())->path.begin())->getLocation().getManager(); for (std::vector<const PathDiagnostic*>::iterator DI = Diags.begin(), DE = Diags.end(); DI != DE; ++DI) { const PathDiagnostic *D = *DI; SmallVector<const PathPieces *, 5> WorkList; WorkList.push_back(&D->path); while (!WorkList.empty()) { const PathPieces &path = *WorkList.pop_back_val(); for (PathPieces::const_iterator I = path.begin(), E = path.end(); I != E; ++I) { const PathDiagnosticPiece *piece = I->get(); AddFID(FM, Fids, *SM, piece->getLocation().asLocation()); ArrayRef<SourceRange> Ranges = piece->getRanges(); for (ArrayRef<SourceRange>::iterator I = Ranges.begin(), E = Ranges.end(); I != E; ++I) { AddFID(FM, Fids, *SM, I->getBegin()); AddFID(FM, Fids, *SM, I->getEnd()); } if (const PathDiagnosticCallPiece *call = dyn_cast<PathDiagnosticCallPiece>(piece)) { IntrusiveRefCntPtr<PathDiagnosticEventPiece> callEnterWithin = call->getCallEnterWithinCallerEvent(); if (callEnterWithin) AddFID(FM, Fids, *SM, callEnterWithin->getLocation().asLocation()); WorkList.push_back(&call->path); } else if (const PathDiagnosticMacroPiece *macro = dyn_cast<PathDiagnosticMacroPiece>(piece)) { WorkList.push_back(¯o->subPieces); } } } } // Open the file. std::error_code EC; llvm::raw_fd_ostream o(OutputFile, EC, llvm::sys::fs::F_Text); if (EC) { llvm::errs() << "warning: could not create file: " << EC.message() << '\n'; return; } EmitPlistHeader(o); // Write the root object: a <dict> containing... // - "clang_version", the string representation of clang version // - "files", an <array> mapping from FIDs to file names // - "diagnostics", an <array> containing the path diagnostics o << "<dict>\n" << " <key>clang_version</key>\n"; EmitString(o, getClangFullVersion()) << '\n'; o << " <key>files</key>\n" " <array>\n"; for (FileID FID : Fids) EmitString(o << " ", SM->getFileEntryForID(FID)->getName()) << '\n'; o << " </array>\n" " <key>diagnostics</key>\n" " <array>\n"; for (std::vector<const PathDiagnostic*>::iterator DI=Diags.begin(), DE = Diags.end(); DI!=DE; ++DI) { o << " <dict>\n" " <key>path</key>\n"; const PathDiagnostic *D = *DI; o << " <array>\n"; for (PathPieces::const_iterator I = D->path.begin(), E = D->path.end(); I != E; ++I) ReportDiag(o, **I, FM, *SM, LangOpts); o << " </array>\n"; // Output the bug type and bug category. o << " <key>description</key>"; EmitString(o, D->getShortDescription()) << '\n'; o << " <key>category</key>"; EmitString(o, D->getCategory()) << '\n'; o << " <key>type</key>"; EmitString(o, D->getBugType()) << '\n'; o << " <key>check_name</key>"; EmitString(o, D->getCheckName()) << '\n'; o << " <!-- This hash is experimental and going to change! -->\n"; o << " <key>issue_hash_content_of_line_in_context</key>"; PathDiagnosticLocation UPDLoc = D->getUniqueingLoc(); FullSourceLoc L(SM->getExpansionLoc(UPDLoc.isValid() ? UPDLoc.asLocation() : D->getLocation().asLocation()), *SM); const Decl *DeclWithIssue = D->getDeclWithIssue(); EmitString(o, GetIssueHash(*SM, L, D->getCheckName(), D->getBugType(), DeclWithIssue)) << '\n'; // Output information about the semantic context where // the issue occurred. if (const Decl *DeclWithIssue = D->getDeclWithIssue()) { // FIXME: handle blocks, which have no name. if (const NamedDecl *ND = dyn_cast<NamedDecl>(DeclWithIssue)) { StringRef declKind; switch (ND->getKind()) { case Decl::CXXRecord: declKind = "C++ class"; break; case Decl::CXXMethod: declKind = "C++ method"; break; case Decl::ObjCMethod: declKind = "Objective-C method"; break; case Decl::Function: declKind = "function"; break; default: break; } if (!declKind.empty()) { const std::string &declName = ND->getDeclName().getAsString(); o << " <key>issue_context_kind</key>"; EmitString(o, declKind) << '\n'; o << " <key>issue_context</key>"; EmitString(o, declName) << '\n'; } // Output the bug hash for issue unique-ing. Currently, it's just an // offset from the beginning of the function. if (const Stmt *Body = DeclWithIssue->getBody()) { // If the bug uniqueing location exists, use it for the hash. // For example, this ensures that two leaks reported on the same line // will have different issue_hashes and that the hash will identify // the leak location even after code is added between the allocation // site and the end of scope (leak report location). if (UPDLoc.isValid()) { FullSourceLoc UFunL(SM->getExpansionLoc( D->getUniqueingDecl()->getBody()->getLocStart()), *SM); o << " <key>issue_hash_function_offset</key><string>" << L.getExpansionLineNumber() - UFunL.getExpansionLineNumber() << "</string>\n"; // Otherwise, use the location on which the bug is reported. } else { FullSourceLoc FunL(SM->getExpansionLoc(Body->getLocStart()), *SM); o << " <key>issue_hash_function_offset</key><string>" << L.getExpansionLineNumber() - FunL.getExpansionLineNumber() << "</string>\n"; } } } } // Output the location of the bug. o << " <key>location</key>\n"; EmitLocation(o, *SM, D->getLocation().asLocation(), FM, 2); // Output the diagnostic to the sub-diagnostic client, if any. if (!filesMade->empty()) { StringRef lastName; PDFileEntry::ConsumerFiles *files = filesMade->getFiles(*D); if (files) { for (PDFileEntry::ConsumerFiles::const_iterator CI = files->begin(), CE = files->end(); CI != CE; ++CI) { StringRef newName = CI->first; if (newName != lastName) { if (!lastName.empty()) { o << " </array>\n"; } lastName = newName; o << " <key>" << lastName << "_files</key>\n"; o << " <array>\n"; } o << " <string>" << CI->second << "</string>\n"; } o << " </array>\n"; } } // Close up the entry. o << " </dict>\n"; } o << " </array>\n"; // Finish. o << "</dict>\n</plist>"; }
/// createInvocationFromCommandLine - Construct a compiler invocation object for /// a command line argument vector. /// /// \return A CompilerInvocation, or 0 if none was built for the given /// argument vector. std::unique_ptr<CompilerInvocation> clang::createInvocationFromCommandLine( ArrayRef<const char *> ArgList, IntrusiveRefCntPtr<DiagnosticsEngine> Diags, IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) { if (!Diags.get()) { // No diagnostics engine was provided, so create our own diagnostics object // with the default options. Diags = CompilerInstance::createDiagnostics(new DiagnosticOptions); } SmallVector<const char *, 16> Args(ArgList.begin(), ArgList.end()); // FIXME: Find a cleaner way to force the driver into restricted modes. Args.push_back("-fsyntax-only"); // FIXME: We shouldn't have to pass in the path info. driver::Driver TheDriver(Args[0], llvm::sys::getDefaultTargetTriple(), *Diags, VFS); // Don't check that inputs exist, they may have been remapped. TheDriver.setCheckInputsExist(false); std::unique_ptr<driver::Compilation> C(TheDriver.BuildCompilation(Args)); if (!C) return nullptr; // Just print the cc1 options if -### was present. if (C->getArgs().hasArg(driver::options::OPT__HASH_HASH_HASH)) { C->getJobs().Print(llvm::errs(), "\n", true); return nullptr; } // We expect to get back exactly one command job, if we didn't something // failed. Offload compilation is an exception as it creates multiple jobs. If // that's the case, we proceed with the first job. If caller needs a // particular job, it should be controlled via options (e.g. // --cuda-{host|device}-only for CUDA) passed to the driver. const driver::JobList &Jobs = C->getJobs(); bool OffloadCompilation = false; if (Jobs.size() > 1) { for (auto &A : C->getActions()){ // On MacOSX real actions may end up being wrapped in BindArchAction if (isa<driver::BindArchAction>(A)) A = *A->input_begin(); if (isa<driver::OffloadAction>(A)) { OffloadCompilation = true; break; } } } if (Jobs.size() == 0 || !isa<driver::Command>(*Jobs.begin()) || (Jobs.size() > 1 && !OffloadCompilation)) { SmallString<256> Msg; llvm::raw_svector_ostream OS(Msg); Jobs.Print(OS, "; ", true); Diags->Report(diag::err_fe_expected_compiler_job) << OS.str(); return nullptr; } const driver::Command &Cmd = cast<driver::Command>(*Jobs.begin()); if (StringRef(Cmd.getCreator().getName()) != "clang") { Diags->Report(diag::err_fe_expected_clang_command); return nullptr; } const ArgStringList &CCArgs = Cmd.getArguments(); auto CI = llvm::make_unique<CompilerInvocation>(); if (!CompilerInvocation::CreateFromArgs(*CI, const_cast<const char **>(CCArgs.data()), const_cast<const char **>(CCArgs.data()) + CCArgs.size(), *Diags)) return nullptr; return CI; }