doxygen icon indicating copy to clipboard operation
doxygen copied to clipboard

External documented functions not found (functions generated by macros).

Open TommyPec opened this issue 2 years ago • 3 comments

Describe the bug This is... weird, and I can't figure it out. In Doxygen 1.9.6 everything is parsed correctly. Starting from 1.9.7 (I checked), some functions documented in an external file are not found. Not all of them - just some.

We (ns-3) have some extremely weird code in some points - so bear with us. In some points we have to generate accessor functions, and we do it using macros, so stuff like ATTRIBUTE_ACCESSOR_DEFINE(Boolean); is expanded in several functions like MakeBooleanAccessor. These functions can not be documented in the code, because they're generated by the C++ preprocessor, so we have to generate an introspected documentation file, i.e., the Doxygen documentation itself is generated by a script in a different file named introspected-doxygen.h.

So far so good. Until 1.9.6 (included) the documentation was generated flawlessly.

Starting from 1.9.7, Doxygen issues warning about missing documentation for some functions, in particular all the MakeXxxChecker and makeXxxAccessor, even though they're documented.

Expected behavior The same behaviour as 1.9.6 - find them all.

Screenshots No screenshot, as they generate warnings.

To Reproduce

Sorry for the messy sources, I tried to change as little as possible of the original files. Just check the warnings relative to boolean.{cc,h} and string.{cc,h}, the others are there because I didn't touch the introspected file generated by ns-3.

DoxyTestMacros.tgz

Version MacOS M2, Doxygen 1.10.0, but it happens also with ArchLinux.

Stack trace No crash

Additional context If I can help tracking (or fixing) the problem, I'll be more than happy to help.

TommyPec avatar Jan 29 '24 03:01 TommyPec

I assume you refer to the warnings like:

.../boolean.cc:110: warning: Member MakeBooleanChecker() (function) of namespace ns3 is not documented.
.../boolean.cc:110: warning: Member MakeBooleanChecker() (function) of namespace ns3 is not documented.
.../boolean.h:86: warning: Member MakeBooleanAccessor(T1 a1) (function) of namespace ns3 is not documented.
.../boolean.h:86: warning: Member MakeBooleanAccessor(T1 a1, T2 a2) (function) of namespace ns3 is not documented.
.../string.cc:30: warning: Member MakeStringChecker() (function) of namespace ns3 is not documented.
.../string.h:57: warning: Member MakeStringAccessor(T1 a1) (function) of namespace ns3 is not documented.
.../string.h:57: warning: Member MakeStringAccessor(T1 a1, T2 a2) (function) of namespace ns3 is not documented.
.../string.cc:30: warning: Member MakeStringChecker() (function) of namespace ns3 is not documented.

I looked at the first warning a bit in detail and in the source code of boolean.cc we see here:

ATTRIBUTE_CHECKER_IMPLEMENT_WITH_NAME(Boolean, "bool");

and in the file attribute-helper.h:

/**
 * \ingroup attributehelper
 *
 * Define the \c MaketypeChecker function for class \pname{type}.
 *
 * \param [in] type The name of the class.
 * \param [in] name The string name of the underlying type.
 *
 * This macro implements the \c MaketypeChecker function
 * for class \pname{type}.
 *
 * Typically invoked in the source file..
 */
#define ATTRIBUTE_CHECKER_IMPLEMENT_WITH_NAME(type, name)                                          \
    Ptr<const AttributeChecker> Make##type##Checker()                                              \
    {                                                                                              \
        return MakeSimpleAttributeChecker<type##Value, type##Checker>(#type "Value", name);        \
    }

In the source code this translates (using the debug option preprocessor i.e. doxygen -d preprocessor) into (for all versions):

Ptr<const AttributeChecker> MakeBooleanChecker()   {   return MakeSimpleAttributeChecker< BooleanValue,  BooleanChecker>("Boolean" "Value",  "bool" );   };

so it is not documented and I think it was an omission that there was no warning.

Interesting discussion in this might also be #7359.

(Note I don't think the closing ; is necessary and some pedantic setting checkers) might complain about it.

albert-github avatar Jan 29 '24 09:01 albert-github

Looks like to be even a bit more complicated as I see now there should be documentation in introspected-doxygen.h as:

/*!
\fn ns3::Ptr<const ns3::AttributeChecker> ns3::MakeBooleanChecker ()
\returns The AttributeChecker.
\see AttributeChecker
*/

albert-github avatar Jan 29 '24 10:01 albert-github

Thanks for the quick replies.

Your analysis is correct. It's not documented in the code (because it's a generated function), but the documentation should have been picked from introspected-doxygen.h

The signature of the functions looks right to me, except maybe the missing "ns3" namespace. Note that there are more functions and classes documented there, and these are totally ok.

The only ones that are creating warnings are Make[something]Checker and Make[something]Accessor.

My best (and wild) guess is that the Doxygen preprocessor doesn't add the "ns3" namespace to the classes/functions, and this might create a signature mismatch.

TommyPec avatar Jan 29 '24 18:01 TommyPec

@TommyPec The preprocessor should not add the ns3 namespace. The parser should find the result of preprocessing in the correct namespace (you can see this by running doxygen with the -d preprocessor option).

Note that if I set EXTRACT_ALL to YES the problem disappears, so it looks like a symbol is not documented, that should be documented in order to find the match between declaration and definition. Haven't checked in detail which one.

doxygen avatar Feb 01 '24 20:02 doxygen

I've been able to condense the example to this:

/*!
 * @defgroup mygrp My Group
 * My own group
 */

/*!
\fn ns3::Ptr<const ns3::AttributeChecker> ns3::MakeBooleanChecker()
\returns The AttributeChecker.
\see AttributeChecker
\ingroup mygrp
*/

#include <string>

/**
 * Define the \c MaketypeChecker function for class \pname{type}.
 */
#define ATTRIBUTE_CHECKER_IMPLEMENT_WITH_NAME(type, name)                                          \
    Ptr<const AttributeChecker> Make##type##Checker()                                              \
    {                                                                                              \
        return MakeSimpleAttributeChecker<type##Value, type##Checker>(#type "Value", name);        \
    }

/**
 * Declare the AttributeChecker class \pname{typeChecker}
 * and the \c MaketypeChecker function for class \pname{type}.
 *
 */
#define ATTRIBUTE_CHECKER_DEFINE(type)                                                             \
    class type##Checker : public AttributeChecker                                                  \
    {                                                                                              \
    };                                                                                             \
    Ptr<const AttributeChecker> Make##type##Checker()

/// ns3
namespace ns3
{

/// smart pointer
template <typename T>
class Ptr { };

/// empty class
class Empty { };

/// default deleter
template <typename T>
struct DefaultDeleter { };

/// ref counter
template <typename T, typename PARENT = Empty, typename DELETER = DefaultDeleter<T>>
class SimpleRefCount : public PARENT { };

/// attribute value
class AttributeValue : public SimpleRefCount<AttributeValue> { };

/**
 * \brief Represent the type of an attribute
 */
class AttributeChecker : public SimpleRefCount<AttributeChecker> { };

/// make checker
template <typename T, typename BASE>
Ptr<const AttributeChecker> MakeSimpleAttributeChecker(std::string name, std::string underlying) { return Ptr<const AttributeChecker>(); };

// Doxygen for this class is auto-generated by
// utils/print-introspected-doxygen.h
class BooleanValue : public AttributeValue { };

ATTRIBUTE_CHECKER_DEFINE(Boolean);

ATTRIBUTE_CHECKER_IMPLEMENT_WITH_NAME(Boolean, "bool");

} // namespace ns3

If I remove the \ingroup command, the MakeBooleanChecker() is documented, otherwise it is not. Need to dig deeper.

doxygen avatar Feb 01 '24 20:02 doxygen

Re the "extra" ';' after the macro invocation: if we don't include it some editors get confused about indentation level, since they see the macro as a function call and unterminated statement. AFAIK extra ';' are just null statements; we haven't seen any linters complain (yet).

pdbj avatar Feb 02 '24 19:02 pdbj

@pdbj Some static analyzers give a warning about it (I'm not sure which ones but I think sonarcloud.) Also I think some compilers when using some pedantic mode might complain.

(It is still legal code and it just gives the "null" statement).

albert-github avatar Feb 03 '24 09:02 albert-github

Looks like https://github.com/doxygen/doxygen/pull/9952 introduced the problem, in particular the below guard in doxygen.cpp prevents the documentation to be associated with the namespace member.

      // If the entry has groups, then restrict the search to members which are
      // in one of the groups of the entry.
      if (!root->groups.empty() && !isEntryInGroupOfMember(root, md.get()))
      {
        continue;
      }

Need to find out why this was added. @sebhub any info you can add would be welcomed.

doxygen avatar Feb 03 '24 11:02 doxygen

@TommyPec Please verify if the referenced commit fixes the problem for you. Do not close the issue, this will be done automatically when the next official release becomes available.

doxygen avatar Feb 03 '24 13:02 doxygen

I have no idea of what the commit did. I tried to understand the changes in the code, and they're as obscure (to me) as something written in Malbolge. But I guess it's ok, you can't grasp the internal functions of Doxygen in 2 days while also grading your students.

Anyway, I checked the results.

  1. The warnings are gone.
  2. The output is correct (see attached screenshot).
  3. I'm super happy, and you rock.
Screenshot 2024-02-03 at 13 56 10

TommyPec avatar Feb 03 '24 20:02 TommyPec

@albert-github I have a small update on this, and it seems that there is a further issue (sorry).

We have some macros-of macros, like:

#define ATTRIBUTE_HELPER_HEADER(type)                                                              \
    ATTRIBUTE_VALUE_DEFINE(type);                                                                  \
    ATTRIBUTE_ACCESSOR_DEFINE(type);                                                               \
    ATTRIBUTE_CHECKER_DEFINE(type)

We have the following in our configuration:

EXPAND_AS_DEFINED      = ATTRIBUTE_ACCESSOR_DEFINE \
                         ATTRIBUTE_CHECKER_DEFINE \
                         ATTRIBUTE_CHECKER_IMPLEMENT \
                         ATTRIBUTE_CHECKER_IMPLEMENT_WITH_NAME \
                         ATTRIBUTE_CONVERTER_DEFINE \
                         ATTRIBUTE_HELPER_CPP \
                         ATTRIBUTE_HELPER_HEADER \
                         ATTRIBUTE_VALUE_DEFINE \
                         ATTRIBUTE_VALUE_DEFINE_WITH_NAME \
                         ATTRIBUTE_VALUE_IMPLEMENT \
                         ATTRIBUTE_VALUE_IMPLEMENT_WITH_NAME \
                         NS_UNUSED_GLOBAL

It looks like the macros are not expanded recursively. Shall I open another issue?

TommyPec avatar Feb 05 '24 14:02 TommyPec

It looks like the macros are not expanded recursively. Shall I open another issue?

Yes please open a new issue (with a small example) so this issue can stay to be fort the dedicated fixed issue.

albert-github avatar Feb 05 '24 14:02 albert-github

This issue was previously marked 'fixed but not released', which means it should be fixed in doxygen version 1.11.0. Please verify if this is indeed the case. Reopen the issue if you think it is not fixed and please include any additional information that you think can be relevant (preferably in the form of a self-contained example).

doxygen avatar May 20 '24 10:05 doxygen