doxygen
doxygen copied to clipboard
documented symbol (C++ constructor) was not declared or defined
Describe the bug
Given the C++ code in the sample below I get a warning
documented symbol 'anonymous_namespace{my.cpp}::BAD::BAD' was not declared or defined.
Please note that the warning vanishes if the noexcept is on the same line as BAD() (-> GOOD3). And it vanishes if the class name is not all CAPS (-> Good2).
Expected behavior
The warning should not be emitted. Good1, Good2, GOOD3 and BAD should all not emit a warning.
To Reproduce
namespace {
struct Good1 {
/// @brief Test.
Good1() noexcept;
};
struct Good2 {
/// @brief Test.
Good2()
noexcept;
};
struct GOOD3 {
/// @brief Test.
GOOD3() noexcept;
};
struct BAD {
/// @brief Test.
BAD()
noexcept;
};
Good1::Good1() noexcept {}
Good2::Good2() noexcept {}
GOOD3::GOOD3() noexcept {}
BAD::BAD() noexcept {}
}
Version 1.9.4 (5d15657a55555e6181a7830a5c723af75e7577e2) Windows 10 64 bit
Additional context Maybe unrelated, but the problem looks very similar to this clang-format issue.
As far as I can see this has to do with so called "Function Macros". From the documentation:
SKIP_FUNCTION_MACROS If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will remove all references to function-like macros that are alone on a line, have an all uppercase name, and do not end with a semicolon. Such function macros are typically used for boiler-plate code, and will confuse the parser if not removed. The default value is: YES. This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
So setting SKIP_FUNCTION_MACROS=NO should solve this issue.
Indeed, SKIP_FUNCTION_MACROS=NO makes this part work, but breaks other files.
Shouldn't Doxygen do the same as the compiler and resolve all #includes and macros and so see if the class name is a build macro or not?
You wrote:
Indeed,
SKIP_FUNCTION_MACROS=NOmakes this part work, but breaks other files.
Can you give an example of what is broken in other files?
You wrote:
Can you give an example of what is broken in other files?
Yes. I am including strong_typedef.h from https://github.com/foonathan/type_safe and then get
warning: Found ';' while parsing initializer list! (doxygen could be confused by a macro call without semicolon)
in this line. But please note that the file is a local copy in my project and clang-formatted in my project's style (which might affect the doxygen results).
As I can't change the type_safe library and don't want to fork from it SKIP_FUNCTION_MACROS is not a good workaround for me.
My current solution is to prevent clang-format from putting the noexcept on the second line by adding clang-format: off comments. That makes the code prettier and also eliminates the doxygen issue.
Looks a bit like the fact that you should PREDEFINED the TYPE_SAFE_MSC_EMPTY_BASES or maybe __declspec(empty_bases).
- Can you please attach a, small, self contained example (source+configuration file in a, compressed, tar or zip file) that allows us to reproduce the problem? Please don't add external links as they might not be persistent.
strong_typedef.h(813): warning: Found ';' while parsing initializer list! (doxygen could be confused by a macro call without semicolon)
The problem looks like to be resulting from another defined entity.
When having the example:
/// @file
TYPE_SAFE_DETAIL_MAKE_STRONG_TYPEDEF_OP(bitwise_or, |)
//TYPE_SAFE_DETAIL_MAKE_STRONG_TYPEDEF_OP(bitwise_or, |)
struct Argument : complement
{
};
with the Doxyfile
QUIET = YES
EXTRACT_ALL = YES
SKIP_FUNCTION_MACROS = NO
MACRO_EXPANSION = YES
When having an odd number of TYPE_SAFE_DETAIL_MAKE_STRONG_TYPEDEF_OP we get the warning, with an even (consecutive?) number of these the warning is gone.
The warning can also be removed, currently probably the best solution, by setting:
PREDEFINED = TYPE_SAFE_DETAIL_MAKE_STRONG_TYPEDEF_OP(x,y)=
Example: example.tar.gz
I did some investigations and it looks like the code in scanner.l:
<FuncQual>{ID} {
if (yyextra->insideCpp && qstrcmp(yytext,"requires")==0)
{
// c++20 trailing requires clause
yyextra->requiresContext = YY_START;
yyextra->current->req+=' ';
BEGIN(RequiresClause);
}
else if (yyextra->insideCS && qstrcmp(yytext,"where")==0)
{
// type constraint for a method
yyextra->current->typeConstr.clear();
yyextra->current->typeConstr.push_back(Argument());
yyextra->lastCSConstraint = YY_START;
BEGIN( CSConstraintName );
}
else if (checkForKnRstyleC(yyscanner)) // K&R style C function
{
yyextra->current->args = yytext;
yyextra->oldStyleArgType.resize(0);
BEGIN(OldStyleArgs);
}
else
{
yyextra->current->args += yytext;
}
}
"causes" the problems and specifically the code:
else
{
yyextra->current->args += yytext;
}
when changing this into:
else
{
int i;for (i=(int)yyleng-1;i>=0;i--)
{
unput(yytext[i]);
}
BEGIN(FindMembers);
}
the problem looks like to be solved for this case, but I'm not sure that I'm not overlooking another corner case that now will give problems.
@doxygen do you see any corner cases?
@albert-github I think this would break constructions like
class Test : public Base
{
public:
virtual void func() OVERRIDE {}
};
where OVERRIDE is a macro that is made empty prior to C++11 and set to override for C++11 and higher.
In general function like macros without semicolon are hard/impossible to handle correctly without configuring the preprocessor.
Indeed "function macros" are a bit of a problem and it is hard for doxygen (which is not a compiler) to fix.
@doxygen
The thing I don't get in your example is that OVERRIDE is a macro but the issue is about "function macros"
Example: example.tar.gz
@albert-github I was referring to your "do you see any corner cases?". With your fix to unputs the identifier (OVERRIDE in my example) and then start with state FindMembers, the example would not be processed correctly anymore.
Indeed the case that the macro is not defined (i.e. NOT_DEF_OVERRIDE is not defined) the function virtual void funcnd() NOT_DEF_OVERRIDE {} is missing from the output. So this is definitely a corner case.
(I don't know why I missed that yesterday)