【问题标题】:Ignore system headers in clang-tidy忽略 clang-tidy 中的系统头文件
【发布时间】:2018-03-20 04:21:42
【问题描述】:

tldr;> 如何在 clang-tidy 中隐藏来自系统标头的警告?

我有以下最小示例源文件,它会在系统标头中触发一个整洁的警告:

#include <future>

int main() {
  std::promise<int> p;
  p.set_value(3);
}

在 Ubuntu 17.04 上使用 clang-tidy 4.0.0 使用 libstdc++ 7.0.1 调用它:

$ clang-tidy main.cpp -extra-arg=-std=c++14

产量

Running without flags.
1 warning generated.
/usr/lib/gcc/x86_64-linux-gnu/7.0.1/../../../../include/c++/7.0.1/mutex:693:5: warning: Address of stack memory associated with local variable '__callable' is still referred to by the global variable '__once_callable' upon returning to the caller.  This will be a dangling reference [clang-analyzer-core.StackAddressEscape]
    }
    ^
/home/user/main.cpp:5:3: note: Calling 'promise::set_value'
  p.set_value(3);
  ^
/usr/lib/gcc/x86_64-linux-gnu/7.0.1/../../../../include/c++/7.0.1/future:1094:9: note: Calling '_State_baseV2::_M_set_result'
      { _M_future->_M_set_result(_State::__setter(this, std::move(__r))); }
        ^
/usr/lib/gcc/x86_64-linux-gnu/7.0.1/../../../../include/c++/7.0.1/future:401:2: note: Calling 'call_once'
        call_once(_M_once, &_State_baseV2::_M_do_set, this,
        ^
/usr/lib/gcc/x86_64-linux-gnu/7.0.1/../../../../include/c++/7.0.1/mutex:691:11: note: Assuming '__e' is 0
      if (__e)
          ^
/usr/lib/gcc/x86_64-linux-gnu/7.0.1/../../../../include/c++/7.0.1/mutex:691:7: note: Taking false branch
      if (__e)
      ^
/usr/lib/gcc/x86_64-linux-gnu/7.0.1/../../../../include/c++/7.0.1/mutex:693:5: note: Address of stack memory associated with local variable '__callable' is still referred to by the global variable '__once_callable' upon returning to the caller.  This will be a dangling reference
    }

我想在系统标题中隐藏警告。我尝试了以下方法:

$ clang-tidy -extra-arg=-std=c++14 main.cpp -header-filter=$(realpath .) -system-headers=0

但警告仍然显示。

【问题讨论】:

  • 旁白:该警告在 GCC 7.3 中被抑制(您的 PR 82481 错误),但最新版本的 clang-tidy 似乎并没有给出该警告,即使使用 -system-headers。跨度>
  • 这通常不起作用,但您可以使用定义 __clang_analyzer__ 来避免解析。如果是qt moc之类的机器生成代码,往往一些简单的声明就可以避免用clang-tidy处理机器生成的文件。它也可能适用于某些头文件。它不适用于带有模板的示例。但对于其他情况,它可能很有用。

标签: c++ clang clang-static-analyzer libtooling clang-tidy


【解决方案1】:

我也遇到了这个问题,并花了一些时间试图解决这个问题,但我看不到在 clang-tidy 中禁用此类警告的方法。

通过阅读this discussion on the LLVM issue tracker regarding a similar issue,我得到的印象是,从clang-tidy的角度来看,警告实际上位于main.cpp,因为对set_value的调用来自那里。

我的解决方法是禁用clang-tidy中的静态分析检查,并使用scan-build utility运行clang的静态分析,这似乎可以避免这些问题。例如,使用您的main.cpp

$ scan-build-3.9 clang++ -std=c++14 main.cpp 
scan-build: Using '/usr/lib/llvm-3.9/bin/clang' for static analysis
In file included from main.cpp:1:
In file included from /usr/lib/gcc/x86_64-linux-gnu/6.3.0/../../../../include/c++/6.3.0/future:39:
/usr/lib/gcc/x86_64-linux-gnu/6.3.0/../../../../include/c++/6.3.0/mutex:621:11: warning: Address of stack memory associated with local variable '__callable' is still referred to by the global variable '__once_callable' upon returning to the caller.  This will be a dangling reference
      if (__e)
          ^~~
1 warning generated.
scan-build: Removing directory '/tmp/scan-build-2017-12-02-112018-13035-1' because it contains no reports.
scan-build: No bugs found.

分析器在系统标头中发现了相同的错误,但它足够聪明,不会将其包含在最终报告中。 (“未发现错误”)

如果您对样式指南类型的警告感兴趣,您仍然需要单独运行 clang-tidy,例如 modernize-*readability-*

【讨论】:

  • disabled the static analysis checks in clang-tidy 是怎么做到的?
  • scan-build 如果您尝试通过将其作为-isystem 传递来排除非系统标头,则仍会报告该错误。
【解决方案2】:

我最初得出的结论是这是不可能的,但我有一个非常接近的 hack。

当我编译时,我使用 CMAKE_EXPORT_COMPILE_COMMANDS=ON 生成一个 compile_commands.json 文件,显示为每个编译的 cpp 文件传递​​给编译器的命令。当我运行 clang-tidy 时,我给它 -p 选项以指向包含此文件的目录。

此文件中的典型条目如下所示:

{
  "directory": "/project/build/package1",
  "command": "/usr/bin/clang++-9 -I/opt/thirdparty/include -isystem /usr/include . . . /project/src/package1/src/foo.cpp",
  "file": "/project/src/package1/src/foo.cpp"
},

如果我重写此文件,使 -I/opt/thirdparty/include 变为 -isystem /opt/thirdparty/include,则 /opt/third-party/include 中先前有问题的标头将被忽略,因为 clang-tidy 会将它们视为系统标头。

我用sed重写文件

# Trick clang-tidy into thinking anything in /opt/thirdparty/include is a system header
sed -i 's|-I/opt/thirdparty/include|-isystem /opt/thirdparty/include|g' build/compile_commands.json
# Run clang-tidy using the run-clang-tidy python wrapper
run-clang-tidy.py -p build -header-filter .* $(find src -iname "*.cpp")

【讨论】:

  • 如果您使用 CMake,在您的 target_include_directories 调用中为您的自定义第三方包含目录使用 SYSTEM 关键字应该可以执行您想要的操作,而无需编辑编译命令。 cmake.org/cmake/help/v3.16/command/…cmake.org/cmake/help/v3.16/manual/…
  • 不幸的是,我正在处理一个包含许多 CMake 项目的构建,其中一些来自无法轻易更改的第三方。
  • 刚刚使用 LLVM 6.0.0 中的 clang-tidy 在一个测试项目上尝试过这个;它不工作。即使我尝试使用 -isystem--system-header-prefix,我仍然看到正在生成警告。
  • 抱歉,这太糟糕了——它对我有用,但我目前没有为需要它的公司做任何工作,所以我不能回去玩它来试图弄清楚是什么尝试时可能会出错。我什至不记得他们使用的 clang-tidy 版本。
【解决方案3】:

我对这个问题的解决方案是从脚本执行clang-tidy,然后根据文件位置过滤所有问题,因为来自用户代码的问题位于例如C:\Repos\myLib,而来自系统头文件的问题位于 C:\Program Files (x86) 或类似位置。

脚本是powershell 脚本,也许你可以用bash 做类似的事情:

# clang-filter-user-code.ps1
Param(
    [parameter(Mandatory=$true)]
    [string]$CLANG_TIDY,
    [parameter(Mandatory=$true)]
    [string[]]$SOURCE_FILES,
    [parameter(Mandatory=$true)]
    [string]$SOURCE_DIR,
    [parameter(Mandatory=$true)]
    [string]$TARGET_DIR
)

$TMP = $TARGET_DIR + "/tmp-clang-output.txt"
$PATTERN = "(?s)(" + $SOURCE_DIR.Replace("/", "\\") + ".*)"

# Reading the content as Raw text, where everything is stored within one line, is only possible with specifying a path to a file.
# Therefore we store the clang-tidy output in a temporary file, that is deleted afterwards.
&($CLANG_TIDY) $SOURCE_FILES > $TMP
Get-Content -Raw -Path $TMP | Select-String -Pattern $PATTERN -AllMatches | Select-Object -ExpandProperty Matches | % {$_.Groups[1].Value }
Remove-Item $TMP

您必须将 clang-tidy 的路径、源文件、源目录的路径和目标目录的路径传递给脚本。

使用CMAKE,您可以将其集成到您的构建中:

# clang-dev-tools.cmake
function(PREPARE_SOURCE_FILE_LIST SOURCE_FILES OUTPUT)
    # Put each entry between single quotes
    foreach(SOURCE_FILE ${SOURCE_FILES})
        list(APPEND SOURCE_FILES_LIST '${SOURCE_FILE}')
    endforeach()

    # Join all entries using comma as delimiter - based on https://stackoverflow.com/questions/7172670/best-shortest-way-to-join-a-list-in-cmake
    string(REGEX REPLACE "([^\\]|^);" "\\1," TMP_STR "${SOURCE_FILES_LIST}")
    string(REGEX REPLACE "[\\](.)" "\\1" TMP_STR "${TMP_STR}") #fixes escaping
    set(${OUTPUT} "${TMP_STR}" PARENT_SCOPE)
endfunction()

# Detect all source files
file(GLOB_RECURSE
        SOURCE_FILES
        ${CMAKE_SOURCE_DIR}/Src/Libs/*/Src/*.cpp
        ${CMAKE_SOURCE_DIR}/Src/Libs/*/Src/*.h)

find_program(CLANG_TIDY NAMES clang-tidy clang-tidy-6.0)
if (CLANG_TIDY)
    PREPARE_SOURCE_FILE_LIST("${SOURCE_FILES}" SOURCE_FILES_LIST)
    set(SCRIPT_ARGUMENTS "-CLANG_TIDY" '${CLANG_TIDY}' "-SOURCE_FILES" ("${SOURCE_FILES_LIST}") "-SOURCE_DIR" '${CMAKE_SOURCE_DIR}' "-TARGET_DIR" '${CMAKE_BINARY_DIR}')
    
    # Because clang-tidy also detects warnings/errors in non-user code, we need to filter its output via this script.
    add_custom_command(
        OUTPUT clang-output
        COMMAND PowerShell (${CMAKE_CURRENT_SOURCE_DIR}/cmake/clang-filter-user-code.ps1 ${SCRIPT_ARGUMENTS})
        WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
    )
    add_custom_target(
        FilterClang ALL 
        DEPENDS clang-output
    )
endif ()

我对@9​​87654330@ 和powershell 都不是很熟悉,所以如果我没有找到最优雅的解决方案,请多多包涵。

还有一个open PR to an even better solution on LLVM,但由于它已经开放快4年了,我们不知道它是否以及何时会到来。

【讨论】:

    猜你喜欢
    • 2022-07-12
    • 2021-06-19
    • 2020-07-20
    • 2020-12-30
    • 2018-10-25
    • 2016-05-17
    • 2020-02-16
    • 2017-01-20
    • 1970-01-01
    相关资源
    最近更新 更多