Skip to content

Commit 92be269

Browse files
committed
Extract and fix some special member function helpers
This moves isSpecialMemberFunction() out of FunctionObjectFinalizer's unnamed namespace into Function.hpp/cpp as a shared API, and introduces individual helpers (isDefaultConstructor(), isCopyConstructor(), isMoveConstructor(), isCopyAssignment(), isMoveAssignment())---isSpecialMemberFunction() becomes a composition of these individual helpers. Contextually, this fixes three bugs in the existing detection logic in DocComment/Function.hpp: - Default constructors with all-default parameters were not recognized (only empty parameter lists were handled). - Default constructors with parameter packs were not recognized. - By-value copy assignment operator=(X) was not recognized.
1 parent d969649 commit 92be269

File tree

5 files changed

+205
-167
lines changed

5 files changed

+205
-167
lines changed

include/mrdocs/Metadata/Symbol/Function.hpp

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,87 @@ tag_invoke(
208208
v = dom::LazyArray(params, domCorpus);
209209
}
210210

211+
/** Check whether a function is a default constructor.
212+
213+
A default constructor is a constructor for which each
214+
parameter that is not a function parameter pack has a
215+
default argument (including the case of a constructor
216+
with no parameters) ([class.default.ctor]).
217+
218+
@param func The function to check.
219+
@return Whether @p func is a default constructor.
220+
*/
221+
MRDOCS_DECL
222+
bool
223+
isDefaultConstructor(FunctionSymbol const& func);
224+
225+
/** Check whether a function is a copy constructor.
226+
227+
A copy constructor is a non-template constructor whose
228+
first parameter is an lvalue reference to the possibly
229+
cv-qualified record type, with all remaining parameters
230+
having defaults ([class.copy.ctor]).
231+
232+
@param func The function to check.
233+
@return Whether @p func is a copy constructor.
234+
*/
235+
MRDOCS_DECL
236+
bool
237+
isCopyConstructor(FunctionSymbol const& func);
238+
239+
/** Check whether a function is a move constructor.
240+
241+
A move constructor is a non-template constructor whose
242+
first parameter is an rvalue reference to the possibly
243+
cv-qualified record type, with all remaining parameters
244+
having defaults ([class.copy.ctor]).
245+
246+
@param func The function to check.
247+
@return Whether @p func is a move constructor.
248+
*/
249+
MRDOCS_DECL
250+
bool
251+
isMoveConstructor(FunctionSymbol const& func);
252+
253+
/** Check whether a function is a copy assignment operator.
254+
255+
A copy assignment operator is a non-template operator=
256+
whose parameter is of type X, X&, const X&, volatile X&,
257+
or const volatile X& ([class.copy.assign]).
258+
259+
@param func The function to check.
260+
@return Whether @p func is a copy assignment operator.
261+
*/
262+
MRDOCS_DECL
263+
bool
264+
isCopyAssignment(FunctionSymbol const& func);
265+
266+
/** Check whether a function is a move assignment operator.
267+
268+
A move assignment operator is a non-template operator=
269+
whose parameter is an rvalue reference to the possibly
270+
cv-qualified record type ([class.copy.assign]).
271+
272+
@param func The function to check.
273+
@return Whether @p func is a move assignment operator.
274+
*/
275+
MRDOCS_DECL
276+
bool
277+
isMoveAssignment(FunctionSymbol const& func);
278+
279+
/** Check whether a function is a special member function.
280+
281+
A special member function is a default constructor,
282+
copy/move constructor, copy/move assignment operator,
283+
or destructor ([special]).
284+
285+
@param func The function to check.
286+
@return Whether @p func is a special member function.
287+
*/
288+
MRDOCS_DECL
289+
bool
290+
isSpecialMemberFunction(FunctionSymbol const& func);
291+
211292
/** Determine if one function would override the other
212293
213294
@param base The base function

src/lib/Metadata/Finalizers/DocComment/Function.hpp

Lines changed: 0 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -29,76 +29,6 @@ isSpecialFunction(FunctionSymbol const& I)
2929
I.OverloadedOperator != OperatorKind::None;
3030
}
3131

32-
bool
33-
isDefaultConstructor(FunctionSymbol const& I)
34-
{
35-
return I.FuncClass == FunctionClass::Constructor && I.Params.empty();
36-
}
37-
38-
template <bool move, bool assignment>
39-
bool
40-
isCopyOrMoveConstructorOrAssignment(FunctionSymbol const& I)
41-
{
42-
if constexpr (!assignment)
43-
{
44-
MRDOCS_CHECK_OR(I.FuncClass == FunctionClass::Constructor, false);
45-
}
46-
else
47-
{
48-
MRDOCS_CHECK_OR(I.OverloadedOperator == OperatorKind::Equal, false);
49-
}
50-
MRDOCS_CHECK_OR(I.Params.size() == 1, false);
51-
auto const& param = I.Params[0];
52-
Polymorphic<Type> const& paramType = param.Type;
53-
MRDOCS_ASSERT(!paramType.valueless_after_move());
54-
if constexpr (!move)
55-
{
56-
MRDOCS_CHECK_OR(paramType->isLValueReference(), false);
57-
}
58-
else
59-
{
60-
MRDOCS_CHECK_OR(paramType->isRValueReference(), false);
61-
}
62-
using RefType = std::conditional_t<move, RValueReferenceType, LValueReferenceType>;
63-
auto const& paramRefType = static_cast<RefType const &>(*paramType);
64-
auto const& paramRefPointeeOpt = paramRefType.PointeeType;
65-
MRDOCS_CHECK_OR(paramRefPointeeOpt, false);
66-
Type const& paramRefPointee = *paramRefPointeeOpt;
67-
auto const *paramRefPointeeNamed = paramRefPointee.asNamedPtr();
68-
MRDOCS_CHECK_OR(paramRefPointeeNamed, false);
69-
MRDOCS_CHECK_OR(paramRefPointeeNamed->Name, false);
70-
auto const& paramName = paramRefPointeeNamed->Name;
71-
MRDOCS_CHECK_OR(paramName, false);
72-
auto const& paramRefPointeeNamedName = paramName->Identifier;
73-
MRDOCS_CHECK_OR(!paramRefPointeeNamedName.empty(), false);
74-
SymbolID const& id = paramName->id;
75-
MRDOCS_CHECK_OR(id, false);
76-
return id == I.Parent;
77-
}
78-
79-
bool
80-
isCopyConstructor(FunctionSymbol const& I)
81-
{
82-
return isCopyOrMoveConstructorOrAssignment<false, false>(I);
83-
}
84-
85-
bool
86-
isMoveConstructor(FunctionSymbol const& I)
87-
{
88-
return isCopyOrMoveConstructorOrAssignment<true, false>(I);
89-
}
90-
91-
bool
92-
isCopyAssignment(FunctionSymbol const& I)
93-
{
94-
return isCopyOrMoveConstructorOrAssignment<false, true>(I);
95-
}
96-
97-
bool
98-
isMoveAssignment(FunctionSymbol const& I)
99-
{
100-
return isCopyOrMoveConstructorOrAssignment<true, true>(I);
101-
}
10232

10333
Optional<std::string_view>
10434
innermostTypenameString(Polymorphic<Type> const& T)

src/lib/Metadata/Finalizers/FunctionObjectFinalizer.cpp

Lines changed: 1 addition & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@
2121
#include <mrdocs/Metadata/Symbol/Record.hpp>
2222
#include <mrdocs/Metadata/Symbol/RecordTranche.hpp>
2323
#include <mrdocs/Metadata/Symbol/Variable.hpp>
24-
#include <mrdocs/Metadata/Type/LValueReferenceType.hpp>
25-
#include <mrdocs/Metadata/Type/RValueReferenceType.hpp>
2624
#include <mrdocs/Support/Assert.hpp>
2725
#include <algorithm>
2826
#include <format>
@@ -31,98 +29,6 @@ namespace mrdocs {
3129

3230
namespace {
3331

34-
// Check whether a parameter's type is a (possibly cv-qualified)
35-
// lvalue or rvalue reference to the given record type.
36-
bool
37-
isReferenceToRecord(
38-
Param const& param,
39-
SymbolID recordId)
40-
{
41-
Type const& ptype = *param.Type;
42-
Polymorphic<Type> const* pointee = nullptr;
43-
if (ptype.isLValueReference())
44-
{
45-
pointee = &ptype.asLValueReference().PointeeType;
46-
}
47-
else if (ptype.isRValueReference())
48-
{
49-
pointee = &ptype.asRValueReference().PointeeType;
50-
}
51-
else
52-
{
53-
return false;
54-
}
55-
return (**pointee).isNamed()
56-
&& (**pointee).namedSymbol() == recordId;
57-
}
58-
59-
// A special member function is a default constructor, copy/move
60-
// constructor, copy/move assignment operator, or destructor
61-
// ([special]).
62-
bool
63-
isSpecialMemberFunction(
64-
FunctionSymbol const& func,
65-
SymbolID recordId)
66-
{
67-
if (func.FuncClass == FunctionClass::Destructor)
68-
{
69-
return true;
70-
}
71-
72-
if (func.FuncClass == FunctionClass::Constructor)
73-
{
74-
// Default constructor: callable with no arguments.
75-
// TODO: the standard also allows a function parameter
76-
// pack ([class.default.ctor]), but Param has no way to
77-
// represent that, so we miss that case.
78-
if (func.Params.empty() ||
79-
std::ranges::all_of(func.Params,
80-
[](Param const& p) { return p.Default.has_value(); }))
81-
{
82-
return true;
83-
}
84-
85-
// Copy/move constructors are non-template
86-
// ([class.copy.ctor]).
87-
if (func.Template)
88-
{
89-
return false;
90-
}
91-
92-
// Copy/move constructor: first param is reference to the
93-
// record type, remaining params (if any) have defaults
94-
// ([class.copy.ctor]).
95-
return isReferenceToRecord(func.Params[0], recordId)
96-
&& std::ranges::all_of(
97-
func.Params | std::views::drop(1),
98-
[](Param const& p) { return p.Default.has_value(); });
99-
}
100-
101-
// Copy/move assignment operators are non-template
102-
// ([class.copy.assign]).
103-
if (func.Template)
104-
{
105-
return false;
106-
}
107-
108-
// Copy/move assignment operator: operator= with exactly one
109-
// non-object parameter ([class.copy.assign]).
110-
if (func.OverloadedOperator == OperatorKind::Equal &&
111-
func.Params.size() == 1)
112-
{
113-
if (isReferenceToRecord(func.Params[0], recordId))
114-
{
115-
return true;
116-
}
117-
// Copy assignment by value: operator=(X).
118-
Type const& ptype = *func.Params[0].Type;
119-
return ptype.isNamed()
120-
&& ptype.namedSymbol() == recordId;
121-
}
122-
123-
return false;
124-
}
125-
12632
// Reset the CXXMethodDecl-specific fields that do not apply
12733
// to a free function synthesized from an operator() overload.
12834
void
@@ -226,7 +132,7 @@ isFunctionObjectType(
226132
return false;
227133
}
228134

229-
if (!isSpecialMemberFunction(*func, R.id))
135+
if (!isSpecialMemberFunction(*func))
230136
{
231137
if (func->OverloadedOperator != OperatorKind::Call)
232138
{

0 commit comments

Comments
 (0)