doxygen icon indicating copy to clipboard operation
doxygen copied to clipboard

documented symbol (C++ constructor) was not declared or defined

Open beinhaerter opened this issue 3 years ago • 12 comments

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 {}
}

win-localdox.zip

Version 1.9.4 (5d15657a55555e6181a7830a5c723af75e7577e2) Windows 10 64 bit

Additional context Maybe unrelated, but the problem looks very similar to this clang-format issue.

beinhaerter avatar Jun 07 '22 12:06 beinhaerter

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.

albert-github avatar Jun 07 '22 14:06 albert-github

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?

beinhaerter avatar Jun 07 '22 16:06 beinhaerter

You wrote:

Indeed, SKIP_FUNCTION_MACROS=NO makes this part work, but breaks other files.

Can you give an example of what is broken in other files?

albert-github avatar Jun 07 '22 16:06 albert-github

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.

beinhaerter avatar Jun 08 '22 14:06 beinhaerter

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.

albert-github avatar Jun 08 '22 15:06 albert-github

strong_typedef.h

strong_typedef.h(813): warning: Found ';' while parsing initializer list! (doxygen could be confused by a macro call without semicolon)

beinhaerter avatar Jun 13 '22 08:06 beinhaerter

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

albert-github avatar Jun 13 '22 11:06 albert-github

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 avatar Jun 13 '22 12:06 albert-github

@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.

doxygen avatar Aug 01 '22 08:08 doxygen

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 avatar Aug 01 '22 09:08 albert-github

@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.

doxygen avatar Aug 02 '22 07:08 doxygen

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)

albert-github avatar Aug 02 '22 08:08 albert-github