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:
Christian Kandeler
2021-10-14 12:47:24 +02:00
parent 4b245bbb49
commit e77e57420a

View File

@@ -291,6 +291,58 @@ public:
- 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.
void print(int indent = 0) const
{
@@ -2164,7 +2216,8 @@ class ExtraHighlightingResultsCollector
{
public:
ExtraHighlightingResultsCollector(QFutureInterface<HighlightingResult> &future,
HighlightingResults &results, const AstNode &ast,
HighlightingResults &results,
const Utils::FilePath &filePath, const AstNode &ast,
const QTextDocument *doc, const QString &docContent);
void collect();
@@ -2181,6 +2234,7 @@ private:
QFutureInterface<HighlightingResult> &m_future;
HighlightingResults &m_results;
const Utils::FilePath m_filePath;
const AstNode &m_ast;
const QTextDocument * const m_doc;
const QString &m_docContent;
@@ -2246,6 +2300,7 @@ static QList<BlockRange> cleanupDisabledCode(HighlightingResults &results, const
}
static void semanticHighlighter(QFutureInterface<HighlightingResult> &future,
const Utils::FilePath &filePath,
const QList<ExpandedSemanticToken> &tokens,
const QString &docContents, const AstNode &ast,
const QPointer<TextEditorWidget> &widget,
@@ -2377,7 +2432,7 @@ static void semanticHighlighter(QFutureInterface<HighlightingResult> &future,
if (widget && widget->textDocument()->document()->revision() == docRevision)
widget->setIfdefedOutBlocks(ifdefedOutBlocks);
}, Qt::QueuedConnection);
ExtraHighlightingResultsCollector(future, results, ast, &doc, docContents).collect();
ExtraHighlightingResultsCollector(future, results, filePath, ast, &doc, docContents).collect();
if (!future.isCanceled()) {
qCDebug(clangdLog) << "reporting" << results.size() << "highlighting results";
future.reportResults(QVector<HighlightingResult>(results.cbegin(),
@@ -2417,10 +2472,12 @@ void ClangdClient::Private::handleSemanticTokens(TextDocument *doc,
IEditor * const editor = Utils::findOrDefault(EditorManager::visibleEditors(),
[doc](const IEditor *editor) { return editor->document() == doc; });
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(),
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) {
@@ -2872,10 +2929,11 @@ MessageId ClangdClient::Private::getAndHandleAst(const TextDocOrFile &doc,
ExtraHighlightingResultsCollector::ExtraHighlightingResultsCollector(
QFutureInterface<HighlightingResult> &future, HighlightingResults &results,
const AstNode &ast, const QTextDocument *doc, const QString &docContent)
: m_future(future), m_results(results), m_ast(ast), m_doc(doc), m_docContent(docContent)
const Utils::FilePath &filePath, const AstNode &ast, const QTextDocument *doc,
const QString &docContent)
: m_future(future), m_results(results), m_filePath(filePath), m_ast(ast), m_doc(doc),
m_docContent(docContent)
{
}
void ExtraHighlightingResultsCollector::collect()
@@ -3320,12 +3378,22 @@ void ExtraHighlightingResultsCollector::visitNode(const AstNode &node)
{
if (m_future.isCanceled())
return;
switch (node.fileStatus(m_filePath)) {
case AstNode::FileStatus::Foreign:
return;
case AstNode::FileStatus::Ours:
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