CppEditor: Add support for showing pre-processed source files

Fixes: QTCREATORBUG-4
Change-Id: I819709e69e604849264e745da98065829f7cb228
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
Christian Kandeler
2022-09-06 17:52:22 +02:00
parent 0867d6f6a5
commit 8beeea5b5e
11 changed files with 226 additions and 18 deletions

View File

@@ -161,6 +161,9 @@ void CompilerOptionsBuilder::add(const QStringList &args, bool gccOnlyOptions)
void CompilerOptionsBuilder::addSyntaxOnly()
{
if (m_nativeMode)
return;
isClStyle() ? add("/Zs") : add("-fsyntax-only");
}
@@ -232,6 +235,11 @@ void CompilerOptionsBuilder::addWordWidth()
void CompilerOptionsBuilder::addTargetTriple()
{
if (m_nativeMode && m_projectPart.toolchainType != Constants::CLANG_TOOLCHAIN_TYPEID
&& m_projectPart.toolchainType != Constants::CLANG_CL_TOOLCHAIN_TYPEID) {
return;
}
const QString target = m_explicitTarget.isEmpty() || m_projectPart.targetTripleIsAuthoritative
? m_projectPart.toolChainTargetTriple : m_explicitTarget;
@@ -242,6 +250,9 @@ void CompilerOptionsBuilder::addTargetTriple()
void CompilerOptionsBuilder::addExtraCodeModelFlags()
{
if (m_nativeMode)
return;
// extraCodeModelFlags keep build architecture for cross-compilation.
// In case of iOS build target triple has aarch64 archtecture set which makes
// code model fail with CXError_Failure. To fix that we explicitly provide architecture.
@@ -272,6 +283,11 @@ void CompilerOptionsBuilder::addMsvcExceptions()
void CompilerOptionsBuilder::enableExceptions()
{
if (m_nativeMode)
return;
// FIXME: Shouldn't this be dependent on the build system settings?
// With "--driver-mode=cl" exceptions are disabled (clang 8).
// This is most likely due to incomplete exception support of clang.
// However, as we need exception support only in the frontend,
@@ -466,10 +482,15 @@ void CompilerOptionsBuilder::addLanguageVersionAndExtensions()
}
if (!option.isEmpty()) {
if (m_nativeMode)
option.replace("-clang:-std=", "/std:").replace("c++2b", "c++latest");
add(option);
return;
}
if (m_nativeMode)
return;
// Continue in case no cl-style option could be chosen.
}
@@ -616,6 +637,9 @@ static QStringList languageFeatureMacros()
void CompilerOptionsBuilder::undefineCppLanguageFeatureMacrosForMsvc2015()
{
if (m_nativeMode)
return;
if (m_projectPart.toolchainType == ProjectExplorer::Constants::MSVC_TOOLCHAIN_TYPEID
&& m_projectPart.isMsvc2015Toolchain) {
// Undefine the language feature macros that are pre-defined in clang-cl,
@@ -879,25 +903,27 @@ void CompilerOptionsBuilder::evaluateCompilerFlags()
containsDriverMode = true;
}
// Transfrom the "/" starting commands into "-" commands, which if
// unknown will not cause clang to fail because it thinks
// it's a missing file.
if (theOption.startsWith("/") &&
(toolChain == ProjectExplorer::Constants::MSVC_TOOLCHAIN_TYPEID ||
toolChain == ProjectExplorer::Constants::CLANG_CL_TOOLCHAIN_TYPEID)) {
theOption[0] = '-';
}
if (!m_nativeMode) {
// Transform the "/" starting commands into "-" commands, which if
// unknown will not cause clang to fail because it thinks
// it's a missing file.
if (theOption.startsWith("/") &&
(toolChain == ProjectExplorer::Constants::MSVC_TOOLCHAIN_TYPEID ||
toolChain == ProjectExplorer::Constants::CLANG_CL_TOOLCHAIN_TYPEID)) {
theOption[0] = '-';
}
if (toolChain == ProjectExplorer::Constants::MSVC_TOOLCHAIN_TYPEID ||
toolChain == ProjectExplorer::Constants::CLANG_CL_TOOLCHAIN_TYPEID) {
theOption.replace("-std:c++latest", "-clang:-std=c++2b");
theOption.replace("-std:c++", "-clang:-std=c++");
if (toolChain == ProjectExplorer::Constants::MSVC_TOOLCHAIN_TYPEID ||
toolChain == ProjectExplorer::Constants::CLANG_CL_TOOLCHAIN_TYPEID) {
theOption.replace("-std:c++latest", "-clang:-std=c++2b");
theOption.replace("-std:c++", "-clang:-std=c++");
}
}
m_compilerFlags.flags.append(theOption);
}
if (!containsDriverMode
if (!m_nativeMode && !containsDriverMode
&& (toolChain == ProjectExplorer::Constants::MSVC_TOOLCHAIN_TYPEID
|| toolChain == ProjectExplorer::Constants::CLANG_CL_TOOLCHAIN_TYPEID)) {
m_clStyle = true;

View File

@@ -74,6 +74,7 @@ public:
void evaluateCompilerFlags();
bool isClStyle() const;
void setClStyle(bool clStyle) { m_clStyle = clStyle; }
void setNativeMode() { m_nativeMode = true; }
const ProjectPart &projectPart() const { return m_projectPart; }
@@ -106,6 +107,7 @@ private:
QStringList m_options;
QString m_explicitTarget;
bool m_clStyle = false;
bool m_nativeMode = false;
};
} // namespace CppEditor

View File

@@ -45,6 +45,9 @@ static QString interpretAmbiguousHeadersAsCHeadersKey()
static QString skipIndexingBigFilesKey()
{ return QLatin1String(Constants::CPPEDITOR_SKIP_INDEXING_BIG_FILES); }
static QString useBuiltinPreprocessorKey()
{ return QLatin1String(Constants::CPPEDITOR_USE_BUILTIN_PREPROCESSOR); }
static QString indexerFileSizeLimitKey()
{ return QLatin1String(Constants::CPPEDITOR_INDEXER_FILE_SIZE_LIMIT); }
@@ -87,6 +90,8 @@ void CppCodeModelSettings::fromSettings(QSettings *s)
const QVariant skipIndexingBigFiles = s->value(skipIndexingBigFilesKey(), true);
setSkipIndexingBigFiles(skipIndexingBigFiles.toBool());
setUseBuiltinPreprocessor(s->value(useBuiltinPreprocessorKey(), true).toBool());
const QVariant indexerFileSizeLimit = s->value(indexerFileSizeLimitKey(), 5);
setIndexerFileSizeLimitInMb(indexerFileSizeLimit.toInt());
@@ -104,6 +109,7 @@ void CppCodeModelSettings::toSettings(QSettings *s)
s->setValue(interpretAmbiguousHeadersAsCHeadersKey(), interpretAmbigiousHeadersAsCHeaders());
s->setValue(skipIndexingBigFilesKey(), skipIndexingBigFiles());
s->setValue(useBuiltinPreprocessorKey(), useBuiltinPreprocessor());
s->setValue(indexerFileSizeLimitKey(), indexerFileSizeLimitInMb());
s->endGroup();

View File

@@ -48,6 +48,9 @@ public:
bool skipIndexingBigFiles() const;
void setSkipIndexingBigFiles(bool yesno);
bool useBuiltinPreprocessor() const { return m_useBuiltinPreprocessor; }
void setUseBuiltinPreprocessor(bool useBuiltin) { m_useBuiltinPreprocessor = useBuiltin; }
int indexerFileSizeLimitInMb() const;
void setIndexerFileSizeLimitInMb(int sizeInMB);
@@ -62,6 +65,7 @@ private:
PCHUsage m_pchUsage = PchUse_BuildSystem;
bool m_interpretAmbigiousHeadersAsCHeaders = false;
bool m_skipIndexingBigFiles = true;
bool m_useBuiltinPreprocessor = true;
int m_indexerFileSizeLimitInMB = 5;
bool m_enableLowerClazyLevels = true; // For UI behavior only
bool m_categorizeFindReferences = false; // Ephemeral!

View File

@@ -50,6 +50,7 @@ private:
CppCodeModelSettings *m_settings = nullptr;
QCheckBox *m_interpretAmbiguousHeadersAsCHeaders;
QCheckBox *m_ignorePchCheckBox;
QCheckBox *m_useBuiltinPreprocessorCheckBox;
QCheckBox *m_skipIndexingBigFilesCheckBox;
QSpinBox *m_bigFilesLimitSpinBox;
};
@@ -74,10 +75,17 @@ CppCodeModelSettingsWidget::CppCodeModelSettingsWidget(CppCodeModelSettings *s)
"completion and semantic highlighting will process the precompiled header before "
"processing any file.</p></body></html>"));
m_useBuiltinPreprocessorCheckBox = new QCheckBox(tr("Use built-in preprocessor to show "
"pre-processed files"));
m_useBuiltinPreprocessorCheckBox->setToolTip
(tr("Uncheck this to invoke the actual compiler "
"to show a pre-processed source file in the editor."));
m_interpretAmbiguousHeadersAsCHeaders->setChecked(
m_settings->interpretAmbigiousHeadersAsCHeaders());
m_ignorePchCheckBox->setChecked(m_settings->pchUsage() == CppCodeModelSettings::PchUse_None);
m_useBuiltinPreprocessorCheckBox->setChecked(m_settings->useBuiltinPreprocessor());
using namespace Utils::Layouting;
@@ -87,6 +95,7 @@ CppCodeModelSettingsWidget::CppCodeModelSettingsWidget(CppCodeModelSettings *s)
Column {
m_interpretAmbiguousHeadersAsCHeaders,
m_ignorePchCheckBox,
m_useBuiltinPreprocessorCheckBox,
Row { m_skipIndexingBigFilesCheckBox, m_bigFilesLimitSpinBox, st },
}
},
@@ -117,6 +126,11 @@ bool CppCodeModelSettingsWidget::applyGeneralWidgetsToSettings() const
m_settings->setSkipIndexingBigFiles(newSkipIndexingBigFiles);
settingsChanged = true;
}
const bool newUseBuiltinPreprocessor = m_useBuiltinPreprocessorCheckBox->isChecked();
if (m_settings->useBuiltinPreprocessor() != newUseBuiltinPreprocessor) {
m_settings->setUseBuiltinPreprocessor(newUseBuiltinPreprocessor);
settingsChanged = true;
}
const int newFileSizeLimit = m_bigFilesLimitSpinBox->value();
if (m_settings->indexerFileSizeLimitInMb() != newFileSizeLimit) {
m_settings->setIndexerFileSizeLimitInMb(newFileSizeLimit);

View File

@@ -59,6 +59,8 @@ const char QUICK_FIX_SETTING_CUSTOM_TEMPLATE_ASSIGNMENT[] = "Assignment";
const char M_TOOLS_CPP[] = "CppTools.Tools.Menu";
const char SWITCH_HEADER_SOURCE[] = "CppTools.SwitchHeaderSource";
const char OPEN_HEADER_SOURCE_IN_NEXT_SPLIT[] = "CppTools.OpenHeaderSourceInNextSplit";
const char SHOW_PREPROCESSED_FILE[] = "CppTools.ShowPreprocessedFile";
const char SHOW_PREPROCESSED_FILE_SPLIT[] = "CppTools.ShowPreprocessedFileSplit";
const char TASK_INDEX[] = "CppTools.Task.Index";
const char TASK_SEARCH[] = "CppTools.Task.Search";
const char C_SOURCE_MIMETYPE[] = "text/x-csrc";
@@ -80,6 +82,7 @@ const char CPPEDITOR_SORT_EDITOR_DOCUMENT_OUTLINE[] = "SortedMethodOverview";
const char CPPEDITOR_MODEL_MANAGER_PCH_USAGE[] = "PCHUsage";
const char CPPEDITOR_INTERPRET_AMBIGIUOUS_HEADERS_AS_C_HEADERS[]
= "InterpretAmbiguousHeadersAsCHeaders";
const char CPPEDITOR_USE_BUILTIN_PREPROCESSOR[] = "UseBuiltinPreprocessor";
const char CPPEDITOR_SKIP_INDEXING_BIG_FILES[] = "SkipIndexingBigFiles";
const char CPPEDITOR_INDEXER_FILE_SIZE_LIMIT[] = "IndexerFileSizeLimit";

View File

@@ -260,6 +260,21 @@ bool CppEditorPlugin::initialize(const QStringList & /*arguments*/, QString *err
connect(openInNextSplitAction, &QAction::triggered,
this, [] { CppModelManager::switchHeaderSource(true); });
QAction * const showPreprocessedAction = new QAction(tr("Show Preprocessed Source"), this);
command = ActionManager::registerAction(showPreprocessedAction,
Constants::SHOW_PREPROCESSED_FILE, context);
mcpptools->addAction(command);
connect(showPreprocessedAction, &QAction::triggered,
this, [] { CppModelManager::showPreprocessedFile(false); });
QAction * const showPreprocessedInSplitAction = new QAction
(tr("Show Preprocessed Source in Next Split"), this);
command = ActionManager::registerAction(showPreprocessedInSplitAction,
Constants::SHOW_PREPROCESSED_FILE_SPLIT, context);
mcpptools->addAction(command);
connect(showPreprocessedInSplitAction, &QAction::triggered,
this, [] { CppModelManager::showPreprocessedFile(true); });
MacroExpander *expander = globalMacroExpander();
expander->registerVariable("Cpp:LicenseTemplate",
tr("The license template."),
@@ -300,6 +315,9 @@ bool CppEditorPlugin::initialize(const QStringList & /*arguments*/, QString *err
contextMenu->addAction(cmd, Constants::G_CONTEXT_FIRST);
touchBar->addAction(cmd, Core::Constants::G_TOUCHBAR_NAVIGATION);
cmd = ActionManager::command(Constants::SHOW_PREPROCESSED_FILE);
contextMenu->addAction(cmd, Constants::G_CONTEXT_FIRST);
cmd = ActionManager::command(TextEditor::Constants::FOLLOW_SYMBOL_UNDER_CURSOR);
cmd->setTouchBarText(tr("Follow", "text on macOS touch bar"));
contextMenu->addAction(cmd, Constants::G_CONTEXT_FIRST);

View File

@@ -4,18 +4,21 @@
#include "cppmodelmanager.h"
#include "abstracteditorsupport.h"
#include "cppoutlinemodel.h"
#include "baseeditordocumentprocessor.h"
#include "builtinindexingsupport.h"
#include "compileroptionsbuilder.h"
#include "cppcodemodelinspectordumper.h"
#include "cppcodemodelsettings.h"
#include "cppcurrentdocumentfilter.h"
#include "cppeditorconstants.h"
#include "cppeditortr.h"
#include "cppfindreferences.h"
#include "cppincludesfilter.h"
#include "cppindexingsupport.h"
#include "cpplocatordata.h"
#include "cpplocatorfilter.h"
#include "cppbuiltinmodelmanagersupport.h"
#include "cppprojectfile.h"
#include "cppsourceprocessor.h"
#include "cpptoolsjsextension.h"
#include "cpptoolsreuse.h"
@@ -23,10 +26,12 @@
#include "symbolfinder.h"
#include "symbolsfindfilter.h"
#include <coreplugin/coreconstants.h>
#include <coreplugin/documentmanager.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/icore.h>
#include <coreplugin/jsexpander.h>
#include <coreplugin/messagemanager.h>
#include <coreplugin/progressmanager/progressmanager.h>
#include <coreplugin/vcsmanager.h>
#include <cplusplus/ASTPath.h>
@@ -34,13 +39,17 @@
#include <cplusplus/TypeOfExpression.h>
#include <extensionsystem/pluginmanager.h>
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/gcctoolchain.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/kitmanager.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/projectmacro.h>
#include <projectexplorer/projecttree.h>
#include <projectexplorer/session.h>
#include <projectexplorer/target.h>
#include <texteditor/textdocument.h>
@@ -48,6 +57,9 @@
#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
#include <utils/qtcassert.h>
#include <utils/qtcprocess.h>
#include <utils/savefile.h>
#include <utils/temporarydirectory.h>
#include <QCoreApplication>
#include <QDebug>
@@ -320,6 +332,127 @@ void CppModelManager::switchHeaderSource(bool inNextSplit, Backend backend)
inNextSplit);
}
void CppModelManager::showPreprocessedFile(bool inNextSplit)
{
const Core::IDocument *doc = Core::EditorManager::currentDocument();
QTC_ASSERT(doc, return);
static const auto showError = [](const QString &reason) {
Core::MessageManager::writeFlashing(Tr::tr("Cannot show preprocessed file: %1")
.arg(reason));
};
static const auto showFallbackWarning = [](const QString &reason) {
Core::MessageManager::writeSilently(Tr::tr("%1, falling back to built-in preprocessor")
.arg(reason));
};
static const auto saveAndOpen = [](const FilePath &filePath, const QByteArray &contents,
bool inNextSplit) {
SaveFile f(filePath.toString());
if (!f.open()) {
showError(Tr::tr("Failed to open output file \"%1\"").arg(filePath.toUserOutput()));
return;
}
f.write(contents);
if (!f.commit()) {
showError(Tr::tr("Failed to write output file \"%1\"").arg(filePath.toUserOutput()));
return;
}
f.close();
openEditor(filePath, inNextSplit, Core::Constants::K_DEFAULT_TEXT_EDITOR_ID);
};
const FilePath &filePath = doc->filePath();
const QString outFileName = filePath.completeBaseName() + "_preprocessed." + filePath.suffix();
const auto outFilePath = FilePath::fromString(
TemporaryDirectory::masterTemporaryDirectory()->filePath(outFileName));
const auto useBuiltinPreprocessor = [filePath, outFilePath, inNextSplit,
contents = doc->contents()] {
const Document::Ptr preprocessedDoc = instance()->snapshot()
.preprocessedDocument(contents, filePath);
QByteArray content = R"(/* Created using Qt Creator's built-in preprocessor. */
/* See Tools -> Debug Qt Creator -> Inspect C++ Code Model for the parameters used.
* Adapt the respective setting in Edit -> Preferences -> C++ -> Code Model to invoke
* the actual compiler instead.
*/
)";
saveAndOpen(outFilePath, content.append(preprocessedDoc->utf8Source()), inNextSplit);
};
if (codeModelSettings()->useBuiltinPreprocessor()) {
useBuiltinPreprocessor();
return;
}
const Project * const project = ProjectTree::currentProject();
if (!project || !project->activeTarget()
|| !project->activeTarget()->activeBuildConfiguration()) {
showFallbackWarning(Tr::tr("Could not determine which compiler to invoke"));
useBuiltinPreprocessor();
return;
}
const ToolChain * tc = nullptr;
const ProjectFile classifier(filePath.toString(), ProjectFile::classify(filePath.toString()));
if (classifier.isC()) {
tc = ToolChainKitAspect::cToolChain(project->activeTarget()->kit());
} else if (classifier.isCxx() || classifier.isHeader()) {
tc = ToolChainKitAspect::cxxToolChain(project->activeTarget()->kit());
} else {
showFallbackWarning(Tr::tr("Could not determine which compiler to invoke"));
useBuiltinPreprocessor();
return;
}
const bool isGcc = dynamic_cast<const GccToolChain *>(tc);
const bool isMsvc = !isGcc
&& (tc->typeId() == ProjectExplorer::Constants::MSVC_TOOLCHAIN_TYPEID
|| tc->typeId() == ProjectExplorer::Constants::CLANG_CL_TOOLCHAIN_TYPEID);
if (!isGcc && !isMsvc) {
showFallbackWarning(Tr::tr("Could not determine compiler command line"));
useBuiltinPreprocessor();
return;
}
const ProjectPart::ConstPtr projectPart = Utils::findOrDefault(
instance()->projectPart(filePath), [](const ProjectPart::ConstPtr &pp) {
return pp->belongsToProject(ProjectTree::currentProject());
});
if (!projectPart) {
showFallbackWarning(Tr::tr("Could not determine compiler command line"));
useBuiltinPreprocessor();
return;
}
CompilerOptionsBuilder optionsBuilder(*projectPart);
optionsBuilder.setNativeMode();
optionsBuilder.setClStyle(isMsvc);
optionsBuilder.build(classifier.kind, UsePrecompiledHeaders::No);
QStringList compilerArgs = optionsBuilder.options();
if (isGcc)
compilerArgs.append({"-E", "-o", outFilePath.toUserOutput()});
else
compilerArgs.append("/E");
compilerArgs.append(filePath.toUserOutput());
const CommandLine compilerCommandLine(tc->compilerCommand(), compilerArgs);
const auto compiler = new QtcProcess(instance());
compiler->setCommand(compilerCommandLine);
compiler->setEnvironment(project->activeTarget()->activeBuildConfiguration()->environment());
connect(compiler, &QtcProcess::done, instance(), [compiler, outFilePath, inNextSplit,
useBuiltinPreprocessor, isMsvc] {
compiler->deleteLater();
if (compiler->result() != ProcessResult::FinishedWithSuccess) {
showFallbackWarning("Compiler failed to run");
useBuiltinPreprocessor();
return;
}
if (isMsvc)
saveAndOpen(outFilePath, compiler->readAllStandardOutput(), inNextSplit);
else
openEditor(outFilePath, inNextSplit, Core::Constants::K_DEFAULT_TEXT_EDITOR_ID);
});
compiler->start();
}
int argumentPositionOf(const AST *last, const CallAST *callAst)
{
if (!callAst || !callAst->expression_list)

View File

@@ -177,6 +177,7 @@ public:
Backend backend = Backend::Best);
static void findUsages(const CursorInEditor &data, Backend backend = Backend::Best);
static void switchHeaderSource(bool inNextSplit, Backend backend = Backend::Best);
static void showPreprocessedFile(bool inNextSplit);
static Core::ILocatorFilter *createAuxiliaryCurrentDocumentFilter();

View File

@@ -607,11 +607,11 @@ ProjectExplorer::Project *projectForProjectInfo(const ProjectInfo &info)
return ProjectExplorer::SessionManager::projectWithProjectFilePath(info.projectFilePath());
}
void openEditor(const Utils::FilePath &filePath, bool inNextSplit)
void openEditor(const Utils::FilePath &filePath, bool inNextSplit, Utils::Id editorId)
{
using Core::EditorManager;
EditorManager::openEditor(filePath, {}, inNextSplit ? EditorManager::OpenInOtherSplit
: EditorManager::NoFlags);
EditorManager::openEditor(filePath, editorId, inNextSplit ? EditorManager::OpenInOtherSplit
: EditorManager::NoFlags);
}
namespace Internal {

View File

@@ -65,7 +65,8 @@ enum class CacheUsage { ReadWrite, ReadOnly };
QString CPPEDITOR_EXPORT correspondingHeaderOrSource(const QString &fileName, bool *wasHeader = nullptr,
CacheUsage cacheUsage = CacheUsage::ReadWrite);
void CPPEDITOR_EXPORT openEditor(const Utils::FilePath &filePath, bool inNextSplit);
void CPPEDITOR_EXPORT openEditor(const Utils::FilePath &filePath, bool inNextSplit,
Utils::Id editorId = {});
class CppCodeModelSettings;
CppCodeModelSettings CPPEDITOR_EXPORT *codeModelSettings();