Skip to content

C++ class method definitions not parsed as Function nodes (scoped Class::method() and Qt macros) #463

@yongwonkim-atsens

Description

@yongwonkim-atsens

Summary

After a full rebuild with v2.3.2 (which includes the PR #371 scoped-method-name fix), Function nodes
are still not created for C++ class method definitions. This affects both plain C++ and Qt-based projects.
Additionally, Qt macros (Q_OBJECT, QT_BEGIN_NAMESPACE) are misidentified as Function nodes.

Two distinct bugs are confirmed by running CodeParser().parse_file() directly.


Environment

  • code-review-graph version: 2.3.2
  • OS: Windows 11 (26200)
  • Python: 3.12.10

Reproduction

Files used

MyWidgetPlain.h — pure C++, no Qt macros:

#pragma once

class MyWidgetPlain {
 public:
  MyWidgetPlain();
  ~MyWidgetPlain();
  void doSomething();
  int calculateValue(int a, int b);

 protected:
  void onButtonClicked();
  void onDataReceived(int value);
  void onReset();
};

MyWidgetPlain.cpp:

#include "MyWidgetPlain.h"

MyWidgetPlain::MyWidgetPlain() {}
MyWidgetPlain::~MyWidgetPlain() {}

void MyWidgetPlain::doSomething() { onReset(); }
int MyWidgetPlain::calculateValue(int a, int b) { return a + b; }
void MyWidgetPlain::onButtonClicked() { int result = calculateValue(1, 2); }
void MyWidgetPlain::onDataReceived(int value) { if (value < 0) return; doSomething(); }
void MyWidgetPlain::onReset() {}

MyWidget.h — Qt-based:

#pragma once
#include <QMainWindow>

QT_BEGIN_NAMESPACE namespace Ui { class MyWidgetClass; };
QT_END_NAMESPACE

class MyWidget : public QMainWindow {
  Q_OBJECT

 public:
  MyWidget(QWidget* parent = nullptr);
  ~MyWidget();
  void doSomething();
  int calculateValue(int a, int b);

 protected Q_SLOTS:
  void onButtonClicked();
  void onDataReceived(int value);

 public Q_SLOTS:
  void onReset();

 Q_SIGNALS:
  void dataReady(int result);
  void errorOccurred(const QString& msg);
};

MyWidget.cpp:

#include "MyWidget.h"

MyWidget::MyWidget(QWidget* parent) : QMainWindow(parent) {}
MyWidget::~MyWidget() {}
void MyWidget::doSomething() { onReset(); }
int MyWidget::calculateValue(int a, int b) { return a + b; }
void MyWidget::onButtonClicked() { Q_EMIT dataReady(calculateValue(1, 2)); }
void MyWidget::onDataReceived(int value) { if (value < 0) { Q_EMIT errorOccurred("err"); return; } doSomething(); }
void MyWidget::onReset() { Q_EMIT dataReady(0); }

Test script

import sys
sys.path.insert(0, r'<path-to-crg-site-packages>')

from pathlib import Path
from code_review_graph.parser import CodeParser

for fname in ['MyWidgetPlain.cpp', 'MyWidgetPlain.h', 'MyWidget.cpp', 'MyWidget.h']:
    nodes, edges = CodeParser().parse_file(Path(fname))
    print(f'\n=== {fname} -> {len(nodes)} nodes, {len(edges)} edges ===')
    for n in nodes:
        print(f'  [{n.kind:10}] {n.name}')

Actual output

=== MyWidgetPlain.cpp -> 1 nodes, 1 edges ===
  [File      ] MyWidgetPlain.cpp

=== MyWidgetPlain.h -> 2 nodes, 3 edges ===
  [File      ] MyWidgetPlain.h
  [Function  ] class

=== MyWidget.cpp -> 1 nodes, 1 edges ===
  [File      ] MyWidget.cpp

=== MyWidget.h -> 2 nodes, 2 edges ===
  [File      ] MyWidget.h
  [Function  ] QT_BEGIN_NAMESPACE

Expected output

=== MyWidgetPlain.cpp -> 8 nodes ===
  [File      ] MyWidgetPlain.cpp
  [Function  ] MyWidgetPlain       (constructor)
  [Function  ] ~MyWidgetPlain      (destructor)
  [Function  ] doSomething
  [Function  ] calculateValue
  [Function  ] onButtonClicked
  [Function  ] onDataReceived
  [Function  ] onReset

=== MyWidgetPlain.h -> 9 nodes ===
  [File      ] MyWidgetPlain.h
  [Class     ] MyWidgetPlain
  [Function  ] MyWidgetPlain
  ... (all declared methods)

=== MyWidget.cpp -> 8 nodes ===
  [File      ] MyWidget.cpp
  [Function  ] MyWidget
  [Function  ] ~MyWidget
  [Function  ] doSomething
  ... (all defined methods)

=== MyWidget.h -> 10 nodes ===
  [File      ] MyWidget.h
  [Class     ] MyWidget
  [Function  ] MyWidget
  ... (all declared methods and signals)

Bug breakdown

Bug 1 — .cpp scoped method definitions produce no Function nodes

MyWidgetPlain.cpp has zero Qt dependencies, but none of its 7 method definitions
(MyWidgetPlain::MyWidgetPlain, void MyWidgetPlain::doSomething, etc.) are emitted as nodes.
This means the fix in PR #371 (qualified_identifier / destructor_name / operator_name handling)
is either not applied in this version or does not cover this case.

Note: PR #371 was merged 2026-05-07 and v2.3.2 was released after that date.
The scoped method definitions in .cpp files are still not parsed.

Bug 2 — Wrong node emitted for .h class definition

MyWidgetPlain.h emits a Function node named "class" instead of a Class node named
"MyWidgetPlain". The parser appears to be picking up the class keyword token as the node name.

Bug 3 — Qt macro QT_BEGIN_NAMESPACE emitted as a Function node

MyWidget.h emits a Function node named "QT_BEGIN_NAMESPACE".
The following pattern confuses the parser:

QT_BEGIN_NAMESPACE namespace Ui { class MyWidgetClass; };
QT_END_NAMESPACE

Tree-sitter cannot expand macros, so it treats QT_BEGIN_NAMESPACE as a function-like token.

Bug 4 — Qt class body macros prevent all member parsing

Q_OBJECT, Q_SIGNALS:, and protected Q_SLOTS: inside the class body cause the Tree-sitter
AST for the class body to be malformed. As a result, zero member Function nodes are emitted for
MyWidget.cpp — same outcome as MyWidgetPlain.cpp, suggesting Bug 1 is the primary cause and
Bug 4 adds further breakage for Qt projects specifically.


Impact

  • Any C++ project using out-of-line method definitions (Class::method() in .cpp) gets zero
    Function nodes — which is the standard pattern for all non-trivial C++ codebases.
  • Qt projects (QObject-derived classes) are additionally affected by macro misidentification.
  • detect-changes always returns changed_functions: [] and risk_score: 0.0 for C++ projects,
    making it effectively unusable for C++.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions