forked from qt-creator/qt-creator
ClangCodeModel: Mark output arguments also for lambdas
... with clangd. This required rewriting getAstPath(), because the previous implementation did not do the necessary backtracking and could therefore miss the AST branch containing the node fully matching the input range. Task-number: QTCREATORBUG-22381 Change-Id: Id5caf2a401b920c0e76f742bec97b5ca6977b4df Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
@@ -359,49 +359,89 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
static QList<AstNode> getAstPath(const AstNode &root, const Range &range)
|
||||
class AstPathCollector
|
||||
{
|
||||
QList<AstNode> path;
|
||||
QList<AstNode> queue{root};
|
||||
bool isRoot = true;
|
||||
public:
|
||||
AstPathCollector(const AstNode &root, const Range &range) : m_root(root), m_range(range) {}
|
||||
|
||||
while (!queue.isEmpty()) {
|
||||
AstNode curNode = queue.takeFirst();
|
||||
if (!isRoot && !curNode.hasRange())
|
||||
continue;
|
||||
if (curNode.range() == range)
|
||||
return path << curNode;
|
||||
if (isRoot || curNode.range().contains(range)) {
|
||||
path << curNode;
|
||||
const auto children = curNode.children();
|
||||
QList<AstNode> collectPath()
|
||||
{
|
||||
if (!m_root.isValid())
|
||||
return {};
|
||||
visitNode(m_root, true);
|
||||
return m_done ? m_path : m_longestSubPath;
|
||||
}
|
||||
|
||||
private:
|
||||
void visitNode(const AstNode &node, bool isRoot = false)
|
||||
{
|
||||
if (!isRoot && (!node.hasRange() || !node.range().contains(m_range)))
|
||||
return;
|
||||
m_path << node;
|
||||
|
||||
class PathDropper {
|
||||
public:
|
||||
PathDropper(AstPathCollector &collector) : m_collector(collector) {};
|
||||
~PathDropper() {
|
||||
if (m_collector.m_done)
|
||||
return;
|
||||
if (m_collector.m_path.size() > m_collector.m_longestSubPath.size())
|
||||
m_collector.m_longestSubPath = m_collector.m_path;
|
||||
m_collector.m_path.removeLast();
|
||||
}
|
||||
private:
|
||||
AstPathCollector &m_collector;
|
||||
} pathDropper(*this);
|
||||
|
||||
// Still traverse the children, because they could have the same range.
|
||||
if (node.range() == m_range)
|
||||
m_done = true;
|
||||
|
||||
const auto children = node.children();
|
||||
if (!children)
|
||||
break;
|
||||
if (curNode.kind() == "Function" || curNode.role() == "expression") {
|
||||
return;
|
||||
|
||||
QList<AstNode> childrenToCheck;
|
||||
if (node.kind() == "Function" || node.role() == "expression") {
|
||||
// Functions and expressions can contain implicit nodes that make the list unsorted.
|
||||
// They cannot be ignored, as we need to consider them in certain contexts.
|
||||
// Therefore, the binary search cannot be used here.
|
||||
queue = *children;
|
||||
childrenToCheck = *children;
|
||||
} else {
|
||||
queue.clear();
|
||||
for (auto it = std::lower_bound(children->cbegin(), children->cend(), m_range,
|
||||
leftOfRange);
|
||||
it != children->cend() && !m_range.isLeftOf(it->range()); ++it) {
|
||||
childrenToCheck << *it;
|
||||
}
|
||||
}
|
||||
|
||||
const bool wasDone = m_done;
|
||||
for (const AstNode &child : qAsConst(childrenToCheck)) {
|
||||
visitNode(child);
|
||||
if (m_done && !wasDone)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static bool leftOfRange(const AstNode &node, const Range &range)
|
||||
{
|
||||
// Class and struct nodes can contain implicit constructors, destructors and
|
||||
// operators, which appear at the end of the list, but whose range is the same
|
||||
// as the class name. Therefore, we must force them not to compare less to
|
||||
// anything else.
|
||||
static const auto leftOfRange = [](const AstNode &node, const Range &range) {
|
||||
return node.range().isLeftOf(range) && !node.arcanaContains(" implicit ");
|
||||
};
|
||||
|
||||
for (auto it = std::lower_bound(children->cbegin(), children->cend(), range,
|
||||
leftOfRange);
|
||||
it != children->cend() && !range.isLeftOf(it->range()); ++it) {
|
||||
queue << *it;
|
||||
}
|
||||
}
|
||||
}
|
||||
isRoot = false;
|
||||
}
|
||||
return path;
|
||||
const AstNode &m_root;
|
||||
const Range &m_range;
|
||||
QList<AstNode> m_path;
|
||||
QList<AstNode> m_longestSubPath;
|
||||
bool m_done = false;
|
||||
};
|
||||
|
||||
static QList<AstNode> getAstPath(const AstNode &root, const Range &range)
|
||||
{
|
||||
return AstPathCollector(root, range).collectPath();
|
||||
}
|
||||
|
||||
static QList<AstNode> getAstPath(const AstNode &root, const Position &pos)
|
||||
@@ -2334,7 +2374,7 @@ static void semanticHighlighter(QFutureInterface<HighlightingResult> &future,
|
||||
return false;
|
||||
for (auto it = path.rbegin() + 1; it != path.rend(); ++it) {
|
||||
if (it->kind() == "Call" || it->kind() == "CXXConstruct"
|
||||
|| it->kind() == "MemberInitializer") {
|
||||
|| it->kind() == "MemberInitializer" || it->kind() == "CXXOperatorCall") {
|
||||
return true;
|
||||
}
|
||||
if (it->kind().endsWith("Cast") && it->hasConstType())
|
||||
|
@@ -1040,7 +1040,7 @@ void ClangdTestHighlighting::test_data()
|
||||
QTest::newRow("typedef as underlying type in enum declaration") << 424 << 21 << 424 << 39
|
||||
<< QList<int>{C_TYPE} << 0;
|
||||
QTest::newRow("argument to user-defined subscript operator") << 434 << 12 << 434 << 17
|
||||
<< QList<int>{C_PARAMETER} << 0;
|
||||
<< QList<int>{C_PARAMETER, C_OUTPUT_ARGUMENT} << 0;
|
||||
QTest::newRow("partial class template specialization") << 553 << 25 << 553 << 28
|
||||
<< QList<int>{C_TYPE, C_DECLARATION} << 0;
|
||||
QTest::newRow("using declaration for function") << 556 << 10 << 556 << 13
|
||||
@@ -1237,6 +1237,14 @@ void ClangdTestHighlighting::test_data()
|
||||
<< QList<int>{C_LOCAL, C_OUTPUT_ARGUMENT} << 0;
|
||||
QTest::newRow("override attribute") << 186 << 28 << 186 << 36 << QList<int>{C_KEYWORD} << 0;
|
||||
QTest::newRow("final attribute") << 187 << 33 << 187 << 38 << QList<int>{C_KEYWORD} << 0;
|
||||
QTest::newRow("non-const argument to named lambda") << 827 << 10 << 827 << 13
|
||||
<< QList<int>{C_LOCAL, C_OUTPUT_ARGUMENT} << 0;
|
||||
QTest::newRow("const argument to named lambda") << 828 << 10 << 828 << 13
|
||||
<< QList<int>{C_LOCAL} << 0;
|
||||
QTest::newRow("non-const argument to unnamed lambda") << 829 << 18 << 829 << 21
|
||||
<< QList<int>{C_LOCAL, C_OUTPUT_ARGUMENT} << 0;
|
||||
QTest::newRow("const argument to unnamed lambda") << 830 << 16 << 830 << 19
|
||||
<< QList<int>{C_LOCAL} << 0;
|
||||
}
|
||||
|
||||
void ClangdTestHighlighting::test()
|
||||
|
@@ -818,3 +818,14 @@ void staticMemberFuncTest() {
|
||||
int i;
|
||||
s.staticFunc(i);
|
||||
}
|
||||
|
||||
void lambdaArgTest()
|
||||
{
|
||||
const auto foo1 = [](int &) {};
|
||||
const auto foo2 = [](int) {};
|
||||
int val;
|
||||
foo1(val);
|
||||
foo2(val);
|
||||
[](int &) {}(val);
|
||||
[](int) {}(val);
|
||||
}
|
||||
|
Reference in New Issue
Block a user