Skip to content

[Cpp Parser] Implement free function declarations - #476

Open
melodyoncode wants to merge 4 commits into
eclipse-score:mainfrom
melodyoncode:melody_function_declaration
Open

melodyoncode wants to merge 4 commits into
eclipse-score:mainfrom
melodyoncode:melody_function_declaration

Conversation

@melodyoncode

Copy link
Copy Markdown
Contributor

No description provided.

@melodyoncode
melodyoncode force-pushed the melody_function_declaration branch 3 times, most recently from 0a9ffc0 to f2da397 Compare September 21, 2026 06:52
@melodyoncode
melodyoncode force-pushed the melody_function_declaration branch from f2da397 to 7a6290c Compare September 21, 2026 07:38

@hoe-jo hoe-jo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe add tests for:

  • overloads
  • static/anon-ns, variadic ...,
  • namespace-scope operator==
  • extern "C", decl/def const mismatch
  • multi-TU internal linkage
  • ctor/dtor bodies

Comment on lines +50 to +59
if existing.source_location == Default::default() {
existing.source_location = incoming.source_location.clone();
}

if existing.entity_type != incoming.entity_type {
warn!(
"conflicting entity types while merging '{}': keeping {:?}, dropping {:?}",
existing.id, existing.entity_type, incoming.entity_type
);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

merge keeps first-seen entity_type and source_location, so a forward decl in an earlier TU wins over the real definition.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's correct. At the moment, source_location is intentionally modeled as a single representative location for the entity, so the merge keeps the first-seen value rather than trying to retain every declaration/definition site. The downside is that, when a forward declaration is seen first, it can win over the full definition.

For example,

// a.h
namespace util {
     class Widget;
}

// b.h
namespace util {
    class Widget {
      public:
        int value;
    };
}

If we see a.h first and b.h afterward, the merged entity may end up with the type-level source_location pointing to the forward declaration in a.h, while member-level source locations still point to their actual definitions in b.h.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

entity_type is different, since a later TU may refine the classification, and keeping the first-seen value can therefore be incorrect.
I will update the logic for entity_type

* SPDX-License-Identifier: Apache-2.0
********************************************************************************/

#include "widget_forward.h"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currect Include?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is intentional.
The purpose of this test case is to cover the scenario where the parser sees a forward declaration in one TU and the full definition in another, so first.cpp only includes the forward declaration on purpose.

Comment on lines +50 to +60
pub struct CallableArgumentIdentityKey {
pub param_type: Option<String>,
pub is_variadic: bool,
pub is_pack_expansion: bool,
}

impl From<&FunctionArgument> for CallableArgumentIdentityKey {
fn from(argument: &FunctionArgument) -> Self {
Self {
param_type: argument.param_type.clone(),
is_variadic: argument.is_variadic,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CallableArgumentIdentityKey.param_type uses Type::get_display_name(), not the canonical type.
Top-level const on a param is not part of a C++ signature, so void topconst(int); + void topconst(const int v) {} emits 2 declarations for 1 function

})
}

fn extract_free_function_declaration(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

namespace_id() returns None for both global scope and anonymous namespaces, and seen_free_function_declarations spans all TUs — so static void f() / anon-ns f() in two different .cpp files collapse to one declaration while both functions entries survive

Comment on lines +125 to +134
if !Self::insert_callable_identity(
seen_method_declarations,
CallableOwnerIdentityKey::Method {
class_id: class_id.clone(),
},
&id.name,
&parameters,
) {
return None;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extract_method_declaration inserts into seen_method_declarations before add_method_declaration runs, so when that bails with "incompletely registered owning class" the entry is burned

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants