Files
qt-creator/src/tools/clangpchmanagerbackend/source/collectbuilddependencypreprocessorcallbacks.h
Marco Bubke 2a65be107b ClangPchManager: Filter missing includes recursively
Missing includes prevent the creation of PCHs. So removing includes which
directly or indirectly point to missing includes is a requirement for
working PCHs.

Task-number: QTCREATORBUG-21529
Change-Id: Id55be164df590149fe1ab55c2a3a90b8b5e3bfa7
Reviewed-by: Ivan Donchevskii <ivan.donchevskii@qt.io>
2018-11-20 14:25:08 +00:00

346 lines
14 KiB
C++

/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "builddependency.h"
#include "collectmacrospreprocessorcallbacks.h"
#include "sourcelocationsutils.h"
#include <filepathcachinginterface.h>
#include <filepathid.h>
#include <utils/smallstringvector.h>
#include <llvm/Support/MemoryBuffer.h>
#include <QFile>
#include <QDir>
#include <QTemporaryDir>
#include <algorithm>
namespace ClangBackEnd {
class CollectBuildDependencyPreprocessorCallbacks final : public clang::PPCallbacks,
public CollectUsedMacrosAndSourcesPreprocessorCallbacksBase
{
public:
CollectBuildDependencyPreprocessorCallbacks(BuildDependency &buildDependency,
const FilePathCachingInterface &filePathCache,
const std::vector<uint> &excludedIncludeUID,
std::vector<uint> &alreadyIncludedFileUIDs,
clang::SourceManager &sourceManager,
SourcesManager &sourcesManager,
std::shared_ptr<clang::Preprocessor> preprocessor)
: CollectUsedMacrosAndSourcesPreprocessorCallbacksBase(buildDependency.usedMacros,
filePathCache,
sourceManager,
sourcesManager,
preprocessor,
buildDependency.sourceDependencies,
buildDependency.sourceFiles,
buildDependency.fileStatuses),
m_buildDependency(buildDependency),
m_excludedIncludeUID(excludedIncludeUID),
m_alreadyIncludedFileUIDs(alreadyIncludedFileUIDs)
{}
void FileChanged(clang::SourceLocation sourceLocation,
clang::PPCallbacks::FileChangeReason reason,
clang::SrcMgr::CharacteristicKind,
clang::FileID) override
{
if (reason == clang::PPCallbacks::EnterFile)
{
const clang::FileEntry *fileEntry = m_sourceManager->getFileEntryForID(
m_sourceManager->getFileID(sourceLocation));
if (fileEntry) {
addFileStatus(fileEntry);
addSourceFile(fileEntry);
}
}
}
void InclusionDirective(clang::SourceLocation hashLocation,
const clang::Token & /*includeToken*/,
llvm::StringRef /*fileName*/,
bool /*isAngled*/,
clang::CharSourceRange /*fileNameRange*/,
const clang::FileEntry *file,
llvm::StringRef /*searchPath*/,
llvm::StringRef /*relativePath*/,
const clang::Module * /*imported*/,
clang::SrcMgr::CharacteristicKind fileType) override
{
if (file) {
addSourceDependency(file, hashLocation);
auto fileUID = file->getUID();
auto sourceFileUID = m_sourceManager
->getFileEntryForID(m_sourceManager->getFileID(hashLocation))
->getUID();
auto notAlreadyIncluded = isNotAlreadyIncluded(fileUID);
if (notAlreadyIncluded.first) {
m_alreadyIncludedFileUIDs.insert(notAlreadyIncluded.second, fileUID);
FilePath filePath = filePathFromFile(file);
if (!filePath.empty()) {
FilePathId includeId = m_filePathCache.filePathId(filePath);
time_t lastModified = file->getModificationTime();
SourceType sourceType = SourceType::UserInclude;
if (isSystem(fileType)) {
if (isInSystemHeader(hashLocation))
sourceType = SourceType::SystemInclude;
else
sourceType = SourceType::TopSystemInclude;
} else if (isNotInExcludedIncludeUID(fileUID)
&& isInExcludedIncludeUID(sourceFileUID)) {
sourceType = SourceType::TopInclude;
}
addInclude({includeId, sourceType, lastModified});
}
}
} else {
auto sourceFileId = filePathId(hashLocation);
m_containsMissingIncludes.emplace_back(sourceFileId);
}
}
void Ifndef(clang::SourceLocation,
const clang::Token &macroNameToken,
const clang::MacroDefinition &macroDefinition) override
{
addUsedMacro(macroNameToken, macroDefinition);
}
void Ifdef(clang::SourceLocation,
const clang::Token &macroNameToken,
const clang::MacroDefinition &macroDefinition) override
{
addUsedMacro( macroNameToken, macroDefinition);
}
void Defined(const clang::Token &macroNameToken,
const clang::MacroDefinition &macroDefinition,
clang::SourceRange) override
{
addUsedMacro(macroNameToken, macroDefinition);
}
void MacroExpands(const clang::Token &macroNameToken,
const clang::MacroDefinition &macroDefinition,
clang::SourceRange,
const clang::MacroArgs *) override
{
addUsedMacro(macroNameToken, macroDefinition);
}
void EndOfMainFile() override
{
filterOutHeaderGuards();
mergeUsedMacros();
m_sourcesManager.updateModifiedTimeStamps();
filterOutIncludesWithMissingIncludes();
}
static
void sortAndMakeUnique(FilePathIds &filePathIds)
{
std::sort(filePathIds.begin(), filePathIds.end());
auto newEnd = std::unique(filePathIds.begin(),
filePathIds.end());
filePathIds.erase(newEnd, filePathIds.end());
}
void appendContainsMissingIncludes(const FilePathIds &dependentSourceFilesWithMissingIncludes)
{
auto split = m_containsMissingIncludes
.insert(m_containsMissingIncludes.end(),
dependentSourceFilesWithMissingIncludes.begin(),
dependentSourceFilesWithMissingIncludes.end());
std::inplace_merge(m_containsMissingIncludes.begin(),
split,
m_containsMissingIncludes.end());
}
void removeAlreadyFoundSourcesWithMissingIncludes(FilePathIds &dependentSourceFilesWithMissingIncludes) const
{
FilePathIds filteredDependentSourceFilesWithMissingIncludes;
filteredDependentSourceFilesWithMissingIncludes.reserve(dependentSourceFilesWithMissingIncludes.size());
std::set_difference(dependentSourceFilesWithMissingIncludes.begin(),
dependentSourceFilesWithMissingIncludes.end(),
m_containsMissingIncludes.begin(),
m_containsMissingIncludes.end(),
std::back_inserter(filteredDependentSourceFilesWithMissingIncludes));
dependentSourceFilesWithMissingIncludes = filteredDependentSourceFilesWithMissingIncludes;
}
void collectSourceWithMissingIncludes(FilePathIds containsMissingIncludes,
const SourceDependencies &sourceDependencies)
{
if (containsMissingIncludes.empty())
return;
class Compare
{
public:
bool operator()(SourceDependency sourceDependency, FilePathId filePathId)
{
return sourceDependency.dependencyFilePathId < filePathId;
}
bool operator()(FilePathId filePathId, SourceDependency sourceDependency)
{
return filePathId < sourceDependency.dependencyFilePathId;
}
};
FilePathIds dependentSourceFilesWithMissingIncludes;
auto begin = sourceDependencies.begin();
for (FilePathId sourceWithMissingInclude : containsMissingIncludes) {
auto range = std::equal_range(begin,
sourceDependencies.end(),
sourceWithMissingInclude,
Compare{});
std::for_each(range.first, range.second, [&](auto entry) {
dependentSourceFilesWithMissingIncludes.emplace_back(entry.filePathId);
});
begin = range.second;
}
sortAndMakeUnique(dependentSourceFilesWithMissingIncludes);
removeAlreadyFoundSourcesWithMissingIncludes(dependentSourceFilesWithMissingIncludes);
appendContainsMissingIncludes(dependentSourceFilesWithMissingIncludes);
collectSourceWithMissingIncludes(dependentSourceFilesWithMissingIncludes,
sourceDependencies);
}
void removeSourceWithMissingIncludesFromIncludes()
{
class Compare
{
public:
bool operator()(SourceEntry entry, FilePathId filePathId)
{
return entry.sourceId < filePathId;
}
bool operator()(FilePathId filePathId, SourceEntry entry)
{
return filePathId < entry.sourceId;
}
};
auto &includes = m_buildDependency.includes;
SourceEntries newIncludes;
newIncludes.reserve(includes.size());
std::set_difference(includes.begin(),
includes.end(),
m_containsMissingIncludes.begin(),
m_containsMissingIncludes.end(),
std::back_inserter(newIncludes),
Compare{});
m_buildDependency.includes = newIncludes;
}
SourceDependencies sourceDependenciesSortedByDependendFilePathId() const
{
auto sourceDependencies = m_buildDependency.sourceDependencies;
std::sort(sourceDependencies.begin(), sourceDependencies.end(), [](auto first, auto second) {
return std::tie(first.dependencyFilePathId, first.filePathId)
< std::tie(second.dependencyFilePathId, second.filePathId);
});
return sourceDependencies;
}
void filterOutIncludesWithMissingIncludes()
{
sortAndMakeUnique(m_containsMissingIncludes);;
collectSourceWithMissingIncludes(m_containsMissingIncludes,
sourceDependenciesSortedByDependendFilePathId());
removeSourceWithMissingIncludesFromIncludes();
}
void ensureDirectory(const QString &directory, const QString &fileName)
{
QStringList directoryEntries = fileName.split('/');
directoryEntries.pop_back();
if (!directoryEntries.isEmpty())
QDir(directory).mkpath(directoryEntries.join('/'));
}
bool isNotInExcludedIncludeUID(uint uid) const
{
return !isInExcludedIncludeUID(uid);
}
bool isInExcludedIncludeUID(uint uid) const
{
return std::binary_search(m_excludedIncludeUID.begin(),
m_excludedIncludeUID.end(),
uid);
}
std::pair<bool, std::vector<uint>::iterator> isNotAlreadyIncluded(uint uid) const
{
auto range = std::equal_range(m_alreadyIncludedFileUIDs.begin(),
m_alreadyIncludedFileUIDs.end(),
uid);
return {range.first == range.second, range.first};
}
static FilePath filePathFromFile(const clang::FileEntry *file)
{
return FilePath::fromNativeFilePath(absolutePath(file->getName()));
}
void addInclude(SourceEntry sourceEntry)
{
auto &includes = m_buildDependency.includes;
auto found = std::lower_bound(includes.begin(),
includes.end(),
sourceEntry,
[](auto first, auto second) { return first < second; });
if (found == includes.end() || *found != sourceEntry)
includes.emplace(found, sourceEntry);
}
private:
FilePathIds m_containsMissingIncludes;
BuildDependency &m_buildDependency;
const std::vector<uint> &m_excludedIncludeUID;
std::vector<uint> &m_alreadyIncludedFileUIDs;
};
} // namespace ClangBackEnd