0

In my cpp codebase, we have a set of return codes. (let's say: enum class RC: UINT32 { kSucess, kNoMem, kIoError, kNetworkError ...} )

We use Doxygen and clang to enforce all kinds of coding conventions, Specifically, every function must follow a documentation paragraph that references (using @param) every parameter, and also @return to explain the return value.

I want to know how can I use Doxygen/clang/any other lint tool, to enforce the function to declare all the different return codes it might return. (kind of like java's "throws")

example 1: (here all possible return codes are directly returned from the function, this is the naïve example)

    /**
     * findIdx searches for an item in the array and returns its index in the array 
     * (if found).
     *
     * @param pItem item to search its index in the array
     * @param pArr the array to look search the item it
     * @param len length of the array
     * @param pOut output parameter, used to pass the item array index (if found)
     *
     * @return kInvalidArgs - if tried to search for null, or the array is null
     * @return kNotFound    - if searched array and does not contain pItem
     * @return kSuccess     - if the item was found and its index was passed via pOut
     */
    RC findIdx(Item *pItem, Item *pArr[], unsigned int len, unsigned int *pOut)
    {
        if (pItem == nullptr || pArr == nullptr) {
            return RC::kInvalidArgs;
        }
        for (int i = 0; i < len; i++) {
            if (pArr[i] == pItem) {
                *pOut = i;
                return RC::kSuccess;
            }
        }
        return RC::kNotFound;
    }

example 2: (here some possible return codes might come from an indirect source, e.g. another function) NOTICE: the function declares in its return documentation all possible RC values, both direct and indirect

    /**
     * this is a wrapper of findIdx that also checks if a random number is even.
     *
     * @param ... same a before
     *
     * @return kInvalidArgs - ... same a before ...
     * @return kNotFound    - ... same a before ...
     * @return kSuccess     - ... same a before ...
     * **@return kEvenRand    - if the request encountered an even random value**
     */
    RC findIdxWrapper(Item *pItem, Item *pArr[], unsigned int len, unsigned int *pOut)
    {
        if(std::rand() %2 == 0){
            return RC::kEvenRand;
        }else{
            return findIdx(pItem, pArr, len, pOut);
        }
    }

Currently, we only enforce the appearance of a single @return and not the following content. Our code base is missing a lot of possible error code, since without an automated check it's very hard to maintain: if someone edits a function, all callers (including indirectly) must edit their documentation. e.g. in example 2, if findIdx changes adds return codes, findIdxWrapper must also change it's documentation.

Tried going around clang-tidy, Doxygen and other lint tools in search a way to configure them to do so, but didn't find what I needed.

  • I have no experience with this, but it is possible to write custom clang-tidy checks. I don't know if you can get the indirect returns with it. – sweenish Jan 31 '23 at 14:45
  • Which version of doxygen did you use? Maybe you ca have a look at the `\snippet{doc}` command to get some information from the `findIdx` function. – albert Jan 31 '23 at 14:45
  • @albert Doxygen version is 1.8.5 – Eilon Kornboim Jan 31 '23 at 14:51
  • it is up to the caller to ensure the validity of parameters - in general, the function cannot do this – Neil Butterworth Jan 31 '23 at 14:55
  • 1
    Doxygen version 1.8.5?? this is from August 23, 2013 the current doxygen version is 1.9.6 – albert Jan 31 '23 at 14:56
  • @NeilButterworth of course we want each developer to document fully, but we want a way to enforce it, and also notify him about places effected that he might not be aware of – Eilon Kornboim Jan 31 '23 at 15:01
  • Doxygen can dump its documentation in a convenient XML format, which makes it possible to write additional tools to chew on that, and come up with one's own scaffolding for these sorts of things. There is no single button, somewhere, that can be pushed and make all of this happen, but with lot of sweat and tears one can implement their own scaffolding, scripts, and CI testing tools in order to enforce this kind of documentation. – Sam Varshavchik Jan 31 '23 at 15:02
  • You want some kind of transitive check? The automated tool should be able to understand that (1) `findIdx` returns x, y, or z, (2) `findIdxWrapper` returns either `findIdx` or `a`, `b`, or `x`, and (3) therefore, `findIdxWrapper` returns either `x`, `y`, `z`, `a`, or `b` (and the documentation should match)? – JohnFilleau Jan 31 '23 at 15:03
  • @eilon but, in general, we can't. try for example checking the non-null parameter of strlen(). the caller is always the thing that has the information that the called function does not, and cannot have. – Neil Butterworth Jan 31 '23 at 15:06
  • @JohnFilleau exactly, If we settled for documenting that findIdxWrapper returns either kEvenRand or whatever findIdx returns only, the future code reader might go down a rabbit hole searching for possible return codes, I want findIdxWrapper to state all it's return codes and it's checked transitively (you only need one level of depth at each check, by induction assumption) – Eilon Kornboim Jan 31 '23 at 15:08
  • @EilonKornboim this requires that the static checker be able to reason about the possible return values of `findIdx` based on its inputs and program state. The `findIdx` call in `findIdxWrapper` might only return a subset of its possible return values because its only called with certain inputs. I _want this product_ but the lack of builtin language support probably means its not going to happen. Maybe Rust supplies this with contracts? I need to read more about Rust... – JohnFilleau Jan 31 '23 at 15:12
  • @JohnFilleau I see what you mean, but I'm okay with not ruling out impossible error codes and having the complete set of possibly returned values, by going strict and stating "each function you call, might return any of its possible returns codes" i.e. the union of directly returned and all possibly indirect – Eilon Kornboim Jan 31 '23 at 15:18
  • @EilonKornboim it's a hypothetical system anyway. If we're only imagining it, I'll imagine the one that gives no false positives or false negatives. – JohnFilleau Jan 31 '23 at 19:55

0 Answers0