"Function-like macros should not be used" emitted for non-function macro

SonarQube:

  • Developer Edition
  • Version 9.2.4 (build 50792)

We are using googletest, which has a feature where you can use the macro SCOPED_TRACE to add a trace to the output from a test if any test checks fail in the same scope.

Building on that, we have written this macro

//Use this to see line numbers of calling code when calling a subroutine with
// assert/expect statements
#define SCOPE_TRACE(statement)                                               \
	do {                                                                     \
		SCOPED_TRACE("in " #statement);                                      \
		statement;                                                           \
	} while (false)

Which can be used like this from the top level test scope:

SCOPE_TRACE(someFunctionThatDoesSomeChecks());
SCOPE_TRACE(anotherFunctionThatDoesSomeChecks());

Which will make it so that if any check inside either of those functions fail, then the test report will document in which function call the failure(s) happened. An element that is probably relevant to the discussion, these functions return void, which means this is definitively not a function macro in the sense of being a pure function.

Also, the implementation of SCOPED_TRACE looks like this

#define SCOPED_TRACE(message) \
  ::testing::ScopedTrace GTEST_CONCAT_TOKEN_(gtest_trace_, __LINE__)(\
    __FILE__, __LINE__, (message))

Now the point is that this define is triggering “Function-like macros should not be used” (cpp:S960).

My understanding is that this macro can’t be rewritten as a C++ function, constexpr or otherwise, because the entire purpose of this macro is to wrap a call to a function while reporting both the line number and doing a textual replacement of the line. Even with the existence of std::source_location from C++20, I don’t think this can be rewritten.

Considering that the underlying macro from googletest is using the magic “LINE” and “FILE” macros internally, this is also speaking in favor of not being able to rewrite the wrapping macro as a function.

Also, I suspect that this rule is triggered in general for all macro definitions, while the references at the bottom of the issue description only suggest that macros should be avoided as constants and “functions”. I guess this wording is indicating that it is possible to write macros that aren’t considered either constants or functions. I would argue that the example I’ve provided falls outside of the definition of a function-like macro, because it’s using features that can’t be implemented with functions.

I guess a function macro would be one that evaluates to an expression. Unless there’s some esoteric grammatical property of C++ I’m not aware of, a do-while loop is not an expression syntactically.

Thank you for the report. The function-like is the term that is used for any macro that is invoked using the function syntax, i.e. is followed by arguments list that are placed inside parenthesis. This name is used regardless of code that macro expands to.

As you have rightfully noted, in some cases such macros cannot be replaced by the C++ function, which only highlights the issue: invocation of function-like macros looks like function calls in code, but does not match their behavior. In case when the benefits of the particular macro outweighs this concern, you can mark the issue as “Won’t Fix” in the connected instance of SonarQube/SonarCloud.