从断言中调用时,llvm-cov覆盖范围丢失

时间:2019-02-01 17:13:56

标签: c++ llvm code-coverage llvm-clang llvm-cov

我有以下头文件:

power.hpp:

#pragma once

#include <type_traits>

template <typename T, typename R = decltype(std::declval<T>() * std::declval<T>())>
constexpr inline R square(const T& x_) noexcept;

power.inl:

#pragma once

#include "power.hpp"

template <typename T, typename R>
constexpr inline R square(const T& x_) noexcept
{
    return (x_ * x_);
}

power_unit_test.cpp:

#include <power.inl>

int main()
{
    static_assert(square(2) == 4);
    assert(square(2) == 4);
    square(2);

    return (0);
}

在使用clang ++编译标志-fprofile-instr-generate-fcoverage-mapping之后。运行单元测试二进制文件,我得到一个报告,告诉我main中的三行都被调用了,但是函数内容仅被使用了一次。这种用法来自对square(2)的独立调用,断言似乎无法正确生成覆盖率报告。

如果我删除独立的square(2),则覆盖率达不到100%,因为断言由于某种原因错过了覆盖率。

覆盖率报告显示为:

power.inl:

   22|       |        template <typename T, typename R>
   23|       |        constexpr inline R square(const T& x_) noexcept
   24|      0|        {
   25|      0|            return (x_ * x_);
   26|      0|        }

power_unit_test.cpp

   29|       |int main()
   30|      1|{
   31|      1|    static_assert(arc::math::sq(2) == 4);
   32|      1|    assert(arc::math::sq(2) == 4);
   33|      1|    // arc::math::sq(2);
   34|      1|
   35|      1|    return (0);
   36|      1|}

请您能帮助我了解为什么未按我期望的那样报告承保范围吗?这是llvm-cov中的错误,还是我不了解保险范围的意图?

使用自制的铛7.0.1在MacOS编译。将CMake 3.13.2用于构建系统。

1 个答案:

答案 0 :(得分:1)

您遇到的问题是编译器为两个assert方法都内联了square()函数。由于代码是内联的,因此它永远不会调用您的外部代码。

您的第一个想法可能是删除inline标识符,但这不会起作用。这是因为您的编译器很可能足够聪明,可以识别出square()函数 可以被内联并且可以继续执行它。最终结果是外部代码没有被调用。

因此,您需要一种绕过square()函数的内联的方法。您可以使用函数指针执行此操作。请参见对main函数的以下修改:

int main()
{
    int (*f_ptr)(const int&);       // Ptr to func that takes 'const int&' and returns 'int' 
    f_ptr = &square;

    static_assert(square(2) == 4);  // Cant use 'f_ptr' here
    assert(f_ptr(2) == 4);
    f_ptr(2);

    return (0);
}

在上面的代码中,我们用指向函数square(const int&)的指针替换了对f_ptr的显式调用。结果,编译器将不会自动内联断言中的函数,并且代码将被成功调用两次。结果:

power.cpp:

    4|       |template <typename T, typename R>
    5|       |constexpr inline R square(const T& x_) noexcept
    6|      2|{
    7|      2|    return (x_ * x_);
    8|      2|}

power_unit_test.cpp:

    5|       |int main()
    6|      1|{
    7|      1|    int (*f_ptr)(const int&);
    8|      1|    f_ptr = &square;
    9|      1|
   10|      1|    static_assert(square(2) == 4);
   11|      1|    assert(f_ptr(2) == 4);
   12|      1|    f_ptr(2);
   13|      1|
   14|      1|    return (0);
   15|      1|}

快速笔记。由于static_assert本质上是一个编译时断言,因此我们不能用函数指针替换对square()的调用,因为函数指针不是constant expressions。但是请放心,如果您尝试在此处将square(2)替换为函数指针f_ptr(2),则编译器足够聪明,可以抱怨。