Skip to content

Commit 4c1e267

Browse files
committed
fix: 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 6221db5 commit 4c1e267

5 files changed

Lines changed: 697 additions & 165 deletions

File tree

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
@@ -22,8 +22,6 @@
2222
#include <mrdocs/Metadata/Symbol/RecordTranche.hpp>
2323
#include <mrdocs/Metadata/Symbol/SymbolBase.hpp>
2424
#include <mrdocs/Metadata/Symbol/Variable.hpp>
25-
#include <mrdocs/Metadata/Type/LValueReferenceType.hpp>
26-
#include <mrdocs/Metadata/Type/RValueReferenceType.hpp>
2725
#include <mrdocs/Support/Assert.hpp>
2826
#include <mrdocs/Support/Report.hpp>
2927
#include <algorithm>
@@ -33,98 +31,6 @@ namespace mrdocs {
3331

3432
namespace {
3533

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

231-
if (!isSpecialMemberFunction(*func, R.id))
137+
if (!isSpecialMemberFunction(*func))
232138
{
233139
if (func->OverloadedOperator != OperatorKind::Call)
234140
{

0 commit comments

Comments
 (0)