diff --git a/model/CMakeLists.txt b/model/CMakeLists.txt index edcd10726..424bf4277 100644 --- a/model/CMakeLists.txt +++ b/model/CMakeLists.txt @@ -1,5 +1,6 @@ set(ODB_SOURCES include/model/buildaction.h + include/model/builddirectory.h include/model/buildlog.h include/model/buildsourcetarget.h include/model/filecontent.h diff --git a/model/include/model/builddirectory.h b/model/include/model/builddirectory.h new file mode 100644 index 000000000..b597f7734 --- /dev/null +++ b/model/include/model/builddirectory.h @@ -0,0 +1,37 @@ +#ifndef CC_MODEL_BUILDDIRECTORY_H +#define CC_MODEL_BUILDDIRECTORY_H + +#include + +#include + +#include +#include + +namespace cc +{ +namespace model +{ + +struct BuildAction; + +#pragma db object +struct BuildDirectory +{ + #pragma db id auto + std::uint64_t id; + + #pragma db not_null + std::string directory; + + #pragma db not_null + #pragma db on_delete(cascade) + std::shared_ptr action; +}; + +typedef std::shared_ptr BuildDirectoryPtr; + +} // model +} // cc + +#endif // CC_MODEL_BUILDDIRECTORY_H diff --git a/parser/include/parser/sourcemanager.h b/parser/include/parser/sourcemanager.h index 9ec5b7a8a..ed4893436 100644 --- a/parser/include/parser/sourcemanager.h +++ b/parser/include/parser/sourcemanager.h @@ -51,8 +51,8 @@ class SourceManager * based on the given path_. The object is read from a cache. If the file is * not in the cache yet then a model::File entry is created, persisted in the * database and placed in the cache. If the file doesn't exist then it returns - * nullptr. - * @param path_ The file path to look up. + * a file with no contents. + * @param path_ The absolute file path to look up. */ model::FilePtr getFile(const std::string& path_); diff --git a/parser/src/sourcemanager.cpp b/parser/src/sourcemanager.cpp index 114db9661..37f9d5162 100644 --- a/parser/src/sourcemanager.cpp +++ b/parser/src/sourcemanager.cpp @@ -166,7 +166,7 @@ model::FilePtr SourceManager::getFile(const std::string& path_) boost::filesystem::path canonicalPath = boost::filesystem::canonical(path_, ec); - //--- If the file can't be found on disk then return nullptr ---// + //--- If the file can't be found on disk then return a blank file ---// bool fileExists = true; if (ec) diff --git a/plugins/cpp/parser/include/cppparser/filelocutil.h b/plugins/cpp/parser/include/cppparser/filelocutil.h index ccfa96883..f342c20dd 100644 --- a/plugins/cpp/parser/include/cppparser/filelocutil.h +++ b/plugins/cpp/parser/include/cppparser/filelocutil.h @@ -79,23 +79,31 @@ class FileLocUtil } /** - * This function returns the file path in which loc_ location takes place. The - * location is meant to be the expanded location (in case of macro expansion). - * If the file can't be determined then empty string returns. + * This function returns the absolute path of the file in which loc_ location + * takes place. The location is meant to be the expanded location (in case of + * macro expansion). + * If the file can't be determined then an empty string is returned. */ std::string getFilePath(const clang::SourceLocation& loc_) { - clang::SourceLocation expLoc = _clangSrcMan.getExpansionLoc(loc_); - clang::FileID fid = _clangSrcMan.getFileID(expLoc); + return getFilePath( + _clangSrcMan.getFileID(_clangSrcMan.getExpansionLoc(loc_))); + } - if (fid.isInvalid()) + /** + * This function returns the absolute path of the file identified by fid_. + * If the file can't be determined then an empty string is returned. + */ + std::string getFilePath(const clang::FileID& fid_) + { + if (fid_.isInvalid()) return std::string(); - const clang::FileEntry* fileEntry = _clangSrcMan.getFileEntryForID(fid); + const clang::FileEntry* fileEntry = _clangSrcMan.getFileEntryForID(fid_); if (!fileEntry) return std::string(); - return fileEntry->getName(); + return fileEntry->tryGetRealPathName(); } private: diff --git a/plugins/cpp/parser/src/cppparser.cpp b/plugins/cpp/parser/src/cppparser.cpp index 3730cbdcc..657e89954 100644 --- a/plugins/cpp/parser/src/cppparser.cpp +++ b/plugins/cpp/parser/src/cppparser.cpp @@ -17,6 +17,8 @@ #include #include +#include +#include #include #include #include @@ -254,6 +256,7 @@ void CppParser::addCompileCommand( std::vector sources; std::vector targets; + model::BuildDirectory buildDir; for (const auto& srcTarget : extractInputOutputs(command_)) { @@ -277,6 +280,9 @@ void CppParser::addCompileCommand( targets.push_back(std::move(buildTarget)); } + + buildDir.directory = command_.Directory; + buildDir.action = buildAction_; _ctx.srcMgr.persistFiles(); @@ -285,6 +291,7 @@ void CppParser::addCompileCommand( _ctx.db->persist(buildSource); for (model::BuildTarget buildTarget : targets) _ctx.db->persist(buildTarget); + _ctx.db->persist(buildDir); }); } @@ -303,12 +310,21 @@ int CppParser::parseWorker(const clang::tooling::CompileCommand& command_) int argc = commandLine.size(); + std::string buildDir = command_.Directory; + if (!fs::is_directory(buildDir)) + { + LOG(debug) << "Compilation directory " << buildDir + << " is missing, using '/' instead."; + buildDir = "/"; + } + std::string compilationDbLoadError; std::unique_ptr compilationDb( clang::tooling::FixedCompilationDatabase::loadFromCommandLine( argc, commandLine.data(), - compilationDbLoadError)); + compilationDbLoadError, + buildDir)); if (!compilationDb) { @@ -324,12 +340,13 @@ int CppParser::parseWorker(const clang::tooling::CompileCommand& command_) //--- Start the tool ---// - fs::path sourceFullPath(command_.Filename); - if (!sourceFullPath.is_absolute()) - sourceFullPath = fs::path(command_.Directory) / command_.Filename; - VisitorActionFactory factory(_ctx); - clang::tooling::ClangTool tool(*compilationDb, sourceFullPath.string()); + + // Use a PhysicalFileSystem as it's thread-safe + + clang::tooling::ClangTool tool(*compilationDb, command_.Filename, + std::make_shared(), + llvm::vfs::createPhysicalFileSystem().release()); llvm::IntrusiveRefCntPtr diagOpts = new clang::DiagnosticOptions(); diff --git a/plugins/cpp/parser/src/ppincludecallback.cpp b/plugins/cpp/parser/src/ppincludecallback.cpp index b163732fe..b2f9b730c 100644 --- a/plugins/cpp/parser/src/ppincludecallback.cpp +++ b/plugins/cpp/parser/src/ppincludecallback.cpp @@ -58,10 +58,10 @@ model::CppAstNodePtr PPIncludeCallback::createFileAstNode( void PPIncludeCallback::InclusionDirective( clang::SourceLocation hashLoc_, const clang::Token&, - clang::StringRef fileName_, + clang::StringRef, bool, clang::CharSourceRange filenameRange_, - const clang::FileEntry*, + const clang::FileEntry* file_, clang::StringRef searchPath_, clang::StringRef, const clang::Module*, @@ -70,13 +70,16 @@ void PPIncludeCallback::InclusionDirective( if (searchPath_.empty()) return; + if (!file_) + return; + clang::SourceLocation expLoc = _clangSrcMgr.getExpansionLoc(hashLoc_); clang::PresumedLoc presLoc = _clangSrcMgr.getPresumedLoc(expLoc); //--- Included file ---// - std::string includedPath = searchPath_.str() + '/' + fileName_.str(); - model::FilePtr included = _ctx.srcMgr.getFile(includedPath); + model::FilePtr included = _ctx.srcMgr.getFile( + file_->tryGetRealPathName().str()); included->parseStatus = model::File::PSFullyParsed; if (included->type != model::File::DIRECTORY_TYPE && included->type != _cppSourceType) @@ -87,8 +90,8 @@ void PPIncludeCallback::InclusionDirective( //--- Includer file ---// - std::string includerPath = presLoc.getFilename(); - model::FilePtr includer = _ctx.srcMgr.getFile(includerPath); + model::FilePtr includer = _ctx.srcMgr.getFile( + _fileLocUtil.getFilePath(presLoc.getFileID())); includer->parseStatus = model::File::PSFullyParsed; if (includer->type != model::File::DIRECTORY_TYPE && includer->type != _cppSourceType) diff --git a/plugins/cpp/parser/src/ppmacrocallback.cpp b/plugins/cpp/parser/src/ppmacrocallback.cpp index d86532e32..b8d0c8508 100644 --- a/plugins/cpp/parser/src/ppmacrocallback.cpp +++ b/plugins/cpp/parser/src/ppmacrocallback.cpp @@ -254,10 +254,12 @@ std::string PPMacroCallback::getUSR(const clang::MacroInfo* mi_) = std::to_string(presLoc.getLine()) + ":" + std::to_string(presLoc.getColumn()) + ":"; + std::string filePath = _fileLocUtil.getFilePath(presLoc.getFileID()); + return locStr + (isBuiltInMacro(mi_) - ? presLoc.getFilename() - : std::to_string(_ctx.srcMgr.getFile(presLoc.getFilename())->id)); + ? filePath + : std::to_string(_ctx.srcMgr.getFile(filePath)->id)); } } // parser diff --git a/plugins/cpp_reparse/service/src/databasefilesystem.cpp b/plugins/cpp_reparse/service/src/databasefilesystem.cpp index fe560b628..b339918f9 100644 --- a/plugins/cpp_reparse/service/src/databasefilesystem.cpp +++ b/plugins/cpp_reparse/service/src/databasefilesystem.cpp @@ -201,7 +201,7 @@ DatabaseFileSystem::DatabaseFileSystem(std::shared_ptr db_) ErrorOr DatabaseFileSystem::status(const Twine& path_) { - model::FilePtr file = getFile(*_db, path_.str()); + model::FilePtr file = getFile(*_db, toCanonicalOrSame(path_)); if (!file) return std::error_code(ENOENT, std::generic_category()); @@ -211,7 +211,7 @@ ErrorOr DatabaseFileSystem::status(const Twine& path_) ErrorOr> DatabaseFileSystem::openFileForRead(const Twine& path_) { - model::FilePtr file = getFile(*_db, path_.str()); + model::FilePtr file = getFile(*_db, toCanonicalOrSame(path_)); if (!file) return std::error_code(ENOENT, std::generic_category()); if (file->type == model::File::DIRECTORY_TYPE) @@ -231,7 +231,7 @@ DatabaseFileSystem::openFileForRead(const Twine& path_) directory_iterator DatabaseFileSystem::dir_begin(const Twine& dir_, std::error_code& ec_) { - model::FilePtr dirFile = getFile(*_db, dir_.str()); + model::FilePtr dirFile = getFile(*_db, toCanonicalOrSame(dir_)); if (dirFile && dirFile->type == model::File::DIRECTORY_TYPE) return directory_iterator(std::make_shared( *_db, dirFile, ec_)); @@ -248,16 +248,36 @@ ErrorOr DatabaseFileSystem::getCurrentWorkingDirectory() const std::error_code DatabaseFileSystem::setCurrentWorkingDirectory(const Twine& path_) { - SmallString<128> fullPath; - sys::fs::make_absolute(_currentWorkingDirectory, fullPath); - sys::path::append(fullPath, path_); - std::error_code error = sys::fs::make_absolute(fullPath); + llvm::ErrorOr newWorkDir = toCanonical(path_); + if (!newWorkDir.getError()) + _currentWorkingDirectory = *newWorkDir; - if (!error) - _currentWorkingDirectory = fullPath.str().str(); + return newWorkDir.getError(); +} + +llvm::ErrorOr +DatabaseFileSystem::toCanonical(const llvm::Twine& relPath_) const +{ + llvm::SmallString<128> absPath, canonicalPath; + + relPath_.toVector(absPath); + + llvm::sys::fs::make_absolute(_currentWorkingDirectory, absPath); - getCurrentWorkingDirectory(); - return error; + if (std::error_code ec = llvm::sys::fs::real_path(absPath, canonicalPath)) + return ec; + + return canonicalPath.str(); +} + +std::string +DatabaseFileSystem::toCanonicalOrSame(const llvm::Twine& relPath_) const +{ + auto canonical = toCanonical(relPath_); + if (canonical.getError()) + return relPath_.str(); + else + return *canonical; } } // namespace reparse diff --git a/plugins/cpp_reparse/service/src/databasefilesystem.h b/plugins/cpp_reparse/service/src/databasefilesystem.h index 48a3ceb9b..51485e7c7 100644 --- a/plugins/cpp_reparse/service/src/databasefilesystem.h +++ b/plugins/cpp_reparse/service/src/databasefilesystem.h @@ -44,6 +44,10 @@ class DatabaseFileSystem : public llvm::vfs::FileSystem util::OdbTransaction _transaction; std::string _currentWorkingDirectory; + + llvm::ErrorOr toCanonical(const llvm::Twine& relPath_) const; + + std::string toCanonicalOrSame(const llvm::Twine& relPath_) const; }; } //namespace reparse diff --git a/plugins/cpp_reparse/service/src/reparser.cpp b/plugins/cpp_reparse/service/src/reparser.cpp index 478505061..41abf7ea8 100644 --- a/plugins/cpp_reparse/service/src/reparser.cpp +++ b/plugins/cpp_reparse/service/src/reparser.cpp @@ -1,9 +1,12 @@ #include +#include #include #include #include +#include +#include #include #include #include @@ -19,6 +22,7 @@ namespace { +typedef odb::query BuildDirQuery; typedef odb::query BuildSourceQuery; typedef odb::result BuildSourceResult; typedef odb::query FileQuery; @@ -64,14 +68,14 @@ boost::variant< CppReparser::getCompilationCommandForFile( const core::FileId& fileId_) { - std::string buildCommand; + cc::model::BuildActionPtr buildAction; _transaction([&, this](){ BuildSourceResult bss = _db->query( BuildSourceQuery::file == std::stoull(fileId_)); for (auto bs : bss) - if (buildCommand.empty()) - buildCommand = bs.action->command; + if (!buildAction || buildAction->command.empty()) + buildAction = bs.action; else { LOG(warning) << "Multiple build commands present for source file #" @@ -83,17 +87,39 @@ CppReparser::getCompilationCommandForFile( } }); - if (buildCommand.empty()) + if (!buildAction) { return "Build command not found for the file! Is this not a C++ file, " "or a header?"; } + std::string buildDir; + + _transaction([&, this](){ + cc::model::BuildDirectoryPtr bd = _db->query_one( + BuildDirQuery::action == buildAction->id); + if (bd) + buildDir = bd->directory; + else + { + LOG(debug) << "No build directory for source file #" + << std::stoull(fileId_) << ". Using '/'."; + buildDir = "/"; + } + }); + + if (!boost::filesystem::is_directory(buildDir)) + { + LOG(debug) << "Compilation directory " << buildDir + << " is missing, using '/' instead."; + buildDir = "/"; + } + //--- Assemble compiler command line ---// std::vector commandLine; std::vector split; - boost::split(split, buildCommand, boost::is_any_of(" ")); + boost::split(split, buildAction->command, boost::is_any_of(" ")); commandLine.reserve(split.size()); commandLine.push_back("--"); @@ -133,7 +159,8 @@ CppReparser::getCompilationCommandForFile( FixedCompilationDatabase::loadFromCommandLine( argc, commandLine.data(), - compilationDbLoadError)); + compilationDbLoadError, + buildDir)); if (!compilationDb) { @@ -164,7 +191,8 @@ CppReparser::getASTForTranslationUnitFile( IntrusiveRefCntPtr dbfs( new DatabaseFileSystem(_db)); IntrusiveRefCntPtr overlayFs( - new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem())); + new llvm::vfs::OverlayFileSystem( + llvm::vfs::createPhysicalFileSystem().release())); overlayFs->pushOverlay(dbfs); auto compileDb = std::move(