forked from qt-creator/qt-creator
ClangCodeModel: Check for AST nodes from foreign files
... when highlighting with clangd. This can happen due to macro invocations or unusually placed includes. Task-number: QTCREATORBUG-26396 Change-Id: I471faee13c62fa511bdedfdd5b864327e858f6b8 Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
@@ -291,6 +291,58 @@ public:
|
|||||||
- openingQuoteOffset - 1);
|
- openingQuoteOffset - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class FileStatus { Ours, Foreign, Mixed, Unknown };
|
||||||
|
FileStatus fileStatus(const Utils::FilePath &thisFile) const
|
||||||
|
{
|
||||||
|
const Utils::optional<QString> arcanaString = arcana();
|
||||||
|
if (!arcanaString)
|
||||||
|
return FileStatus::Unknown;
|
||||||
|
|
||||||
|
// Example arcanas:
|
||||||
|
// "FunctionDecl 0x7fffb5d0dbd0 </tmp/test.cpp:1:1, line:5:1> line:1:6 func 'void ()'"
|
||||||
|
// "VarDecl 0x7fffb5d0dcf0 </tmp/test.cpp:2:5, /tmp/test.h:1:1> /tmp/test.cpp:2:10 b 'bool' cinit"
|
||||||
|
// The second one is for a particularly silly construction where the RHS of an
|
||||||
|
// initialization comes from an included header.
|
||||||
|
const int openPos = arcanaString->indexOf('<');
|
||||||
|
if (openPos == -1)
|
||||||
|
return FileStatus::Unknown;
|
||||||
|
const int closePos = arcanaString->indexOf('>', openPos + 1);
|
||||||
|
if (closePos == -1)
|
||||||
|
return FileStatus::Unknown;
|
||||||
|
bool hasOurs = false;
|
||||||
|
bool hasOther = false;
|
||||||
|
for (int startPos = openPos + 1; startPos < closePos;) {
|
||||||
|
int colon1Pos = arcanaString->indexOf(':', startPos);
|
||||||
|
if (colon1Pos == -1 || colon1Pos > closePos)
|
||||||
|
break;
|
||||||
|
if (Utils::HostOsInfo::isWindowsHost())
|
||||||
|
colon1Pos = arcanaString->indexOf(':', colon1Pos + 1);
|
||||||
|
if (colon1Pos == -1 || colon1Pos > closePos)
|
||||||
|
break;
|
||||||
|
const int colon2Pos = arcanaString->indexOf(':', colon1Pos + 2);
|
||||||
|
if (colon2Pos == -1 || colon2Pos > closePos)
|
||||||
|
break;
|
||||||
|
const int line = subViewEnd(*arcanaString, colon1Pos + 1, colon2Pos).toString().toInt(); // TODO: Drop toString() once we require >= Qt 5.15
|
||||||
|
if (line == 0)
|
||||||
|
break;
|
||||||
|
const QStringView fileOrLineString = subViewEnd(*arcanaString, startPos, colon1Pos);
|
||||||
|
if (fileOrLineString != QLatin1String("line")) {
|
||||||
|
if (Utils::FilePath::fromUserInput(fileOrLineString.toString()) == thisFile)
|
||||||
|
hasOurs = true;
|
||||||
|
else
|
||||||
|
hasOther = true;
|
||||||
|
}
|
||||||
|
const int commaPos = arcanaString->indexOf(',', colon2Pos + 2);
|
||||||
|
if (commaPos != -1)
|
||||||
|
startPos = commaPos + 2;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (hasOurs)
|
||||||
|
return hasOther ? FileStatus::Mixed : FileStatus::Ours;
|
||||||
|
return hasOther ? FileStatus::Foreign : FileStatus::Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
// For debugging.
|
// For debugging.
|
||||||
void print(int indent = 0) const
|
void print(int indent = 0) const
|
||||||
{
|
{
|
||||||
@@ -2164,7 +2216,8 @@ class ExtraHighlightingResultsCollector
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ExtraHighlightingResultsCollector(QFutureInterface<HighlightingResult> &future,
|
ExtraHighlightingResultsCollector(QFutureInterface<HighlightingResult> &future,
|
||||||
HighlightingResults &results, const AstNode &ast,
|
HighlightingResults &results,
|
||||||
|
const Utils::FilePath &filePath, const AstNode &ast,
|
||||||
const QTextDocument *doc, const QString &docContent);
|
const QTextDocument *doc, const QString &docContent);
|
||||||
|
|
||||||
void collect();
|
void collect();
|
||||||
@@ -2181,6 +2234,7 @@ private:
|
|||||||
|
|
||||||
QFutureInterface<HighlightingResult> &m_future;
|
QFutureInterface<HighlightingResult> &m_future;
|
||||||
HighlightingResults &m_results;
|
HighlightingResults &m_results;
|
||||||
|
const Utils::FilePath m_filePath;
|
||||||
const AstNode &m_ast;
|
const AstNode &m_ast;
|
||||||
const QTextDocument * const m_doc;
|
const QTextDocument * const m_doc;
|
||||||
const QString &m_docContent;
|
const QString &m_docContent;
|
||||||
@@ -2246,6 +2300,7 @@ static QList<BlockRange> cleanupDisabledCode(HighlightingResults &results, const
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void semanticHighlighter(QFutureInterface<HighlightingResult> &future,
|
static void semanticHighlighter(QFutureInterface<HighlightingResult> &future,
|
||||||
|
const Utils::FilePath &filePath,
|
||||||
const QList<ExpandedSemanticToken> &tokens,
|
const QList<ExpandedSemanticToken> &tokens,
|
||||||
const QString &docContents, const AstNode &ast,
|
const QString &docContents, const AstNode &ast,
|
||||||
const QPointer<TextEditorWidget> &widget,
|
const QPointer<TextEditorWidget> &widget,
|
||||||
@@ -2377,7 +2432,7 @@ static void semanticHighlighter(QFutureInterface<HighlightingResult> &future,
|
|||||||
if (widget && widget->textDocument()->document()->revision() == docRevision)
|
if (widget && widget->textDocument()->document()->revision() == docRevision)
|
||||||
widget->setIfdefedOutBlocks(ifdefedOutBlocks);
|
widget->setIfdefedOutBlocks(ifdefedOutBlocks);
|
||||||
}, Qt::QueuedConnection);
|
}, Qt::QueuedConnection);
|
||||||
ExtraHighlightingResultsCollector(future, results, ast, &doc, docContents).collect();
|
ExtraHighlightingResultsCollector(future, results, filePath, ast, &doc, docContents).collect();
|
||||||
if (!future.isCanceled()) {
|
if (!future.isCanceled()) {
|
||||||
qCDebug(clangdLog) << "reporting" << results.size() << "highlighting results";
|
qCDebug(clangdLog) << "reporting" << results.size() << "highlighting results";
|
||||||
future.reportResults(QVector<HighlightingResult>(results.cbegin(),
|
future.reportResults(QVector<HighlightingResult>(results.cbegin(),
|
||||||
@@ -2417,10 +2472,12 @@ void ClangdClient::Private::handleSemanticTokens(TextDocument *doc,
|
|||||||
IEditor * const editor = Utils::findOrDefault(EditorManager::visibleEditors(),
|
IEditor * const editor = Utils::findOrDefault(EditorManager::visibleEditors(),
|
||||||
[doc](const IEditor *editor) { return editor->document() == doc; });
|
[doc](const IEditor *editor) { return editor->document() == doc; });
|
||||||
const auto editorWidget = TextEditorWidget::fromEditor(editor);
|
const auto editorWidget = TextEditorWidget::fromEditor(editor);
|
||||||
const auto runner = [tokens, text = doc->document()->toPlainText(), ast,
|
const auto runner = [tokens, filePath = doc->filePath(),
|
||||||
|
text = doc->document()->toPlainText(), ast,
|
||||||
w = QPointer(editorWidget), rev = doc->document()->revision(),
|
w = QPointer(editorWidget), rev = doc->document()->revision(),
|
||||||
clangdVersion = q->versionNumber()] {
|
clangdVersion = q->versionNumber()] {
|
||||||
return Utils::runAsync(semanticHighlighter, tokens, text, ast, w, rev, clangdVersion);
|
return Utils::runAsync(semanticHighlighter, filePath, tokens, text, ast, w, rev,
|
||||||
|
clangdVersion);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (isTesting) {
|
if (isTesting) {
|
||||||
@@ -2872,10 +2929,11 @@ MessageId ClangdClient::Private::getAndHandleAst(const TextDocOrFile &doc,
|
|||||||
|
|
||||||
ExtraHighlightingResultsCollector::ExtraHighlightingResultsCollector(
|
ExtraHighlightingResultsCollector::ExtraHighlightingResultsCollector(
|
||||||
QFutureInterface<HighlightingResult> &future, HighlightingResults &results,
|
QFutureInterface<HighlightingResult> &future, HighlightingResults &results,
|
||||||
const AstNode &ast, const QTextDocument *doc, const QString &docContent)
|
const Utils::FilePath &filePath, const AstNode &ast, const QTextDocument *doc,
|
||||||
: m_future(future), m_results(results), m_ast(ast), m_doc(doc), m_docContent(docContent)
|
const QString &docContent)
|
||||||
|
: m_future(future), m_results(results), m_filePath(filePath), m_ast(ast), m_doc(doc),
|
||||||
|
m_docContent(docContent)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExtraHighlightingResultsCollector::collect()
|
void ExtraHighlightingResultsCollector::collect()
|
||||||
@@ -3320,12 +3378,22 @@ void ExtraHighlightingResultsCollector::visitNode(const AstNode &node)
|
|||||||
{
|
{
|
||||||
if (m_future.isCanceled())
|
if (m_future.isCanceled())
|
||||||
return;
|
return;
|
||||||
collectFromNode(node);
|
switch (node.fileStatus(m_filePath)) {
|
||||||
const auto children = node.children();
|
case AstNode::FileStatus::Foreign:
|
||||||
if (!children)
|
|
||||||
return;
|
return;
|
||||||
for (const AstNode &childNode : *children)
|
case AstNode::FileStatus::Ours:
|
||||||
visitNode(childNode);
|
case AstNode::FileStatus::Unknown:
|
||||||
|
collectFromNode(node);
|
||||||
|
[[fallthrough]];
|
||||||
|
case ClangCodeModel::Internal::AstNode::FileStatus::Mixed: {
|
||||||
|
const auto children = node.children();
|
||||||
|
if (!children)
|
||||||
|
return;
|
||||||
|
for (const AstNode &childNode : *children)
|
||||||
|
visitNode(childNode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
|
Reference in New Issue
Block a user