Clang-tidy-18 `hicpp-signed-bitwise`“使用有符号整数”误报?

问题描述 投票:0回答:1

这可能会成为一个愚蠢的问题,但我真的不明白为什么

clang-tidy
在这里抱怨。

考虑以下配置:

# .clang-tidy
---
FormatStyle: file
WarningsAsErrors: '*'
Checks: >
  -*,
  hicpp-signed-bitwise,
CheckOptions:
  hicpp-signed-bitwise.IgnorePositiveIntegerLiterals: true

以及以下最小示例:

#include "stdint.h"
#include "stdio.h"
#include "inttypes.h"

typedef uint8_t  mytypeU8_t;
typedef uint32_t  mytypeU32_t;

int main(void) {
    mytypeU8_t mytypeU8 = 0;
    mytypeU32_t mytypeU32 = 0;
    unsigned char testChar = 0;

    printf("%"PRIu8, testChar & 1);
    printf("%"PRIu8, testChar << 2);
    printf("%"PRIu8, (testChar << 2 ) | (testChar >> 2));

    printf("%"PRIu8, mytypeU8 & 1);
    printf("%"PRIu8, mytypeU8 << 2);
    printf("%"PRIu8, (mytypeU8 << 2 ) | (mytypeU8 >> 2));

    printf("%"PRIu32, mytypeU32 & 1);
    printf("%"PRIu32, mytypeU32 << 2);
    printf("%"PRIu32, (mytypeU32 << 2 ) | (mytypeU32 >> 2));

    return 0;
}

为什么

clang-tidy
打印这些错误:

test.c:15:22: error: use of a signed integer operand with a binary bitwise operator [hicpp-signed-bitwise,-warnings-as-errors]
   15 |     printf("%"PRIu8, (testChar << 2 ) | (testChar >> 2));
      |                      ^~~~~~~~~~~~~~~~ ~
test.c:19:22: error: use of a signed integer operand with a binary bitwise operator [hicpp-signed-bitwise,-warnings-as-errors]
   19 |     printf("%"PRIu8, (mytypeU8 << 2 ) | (mytypeU8 >> 2));
      |                      ^~~~~~~~~~~~~~~~ ~
test.c:21:35: error: use of a signed integer operand with a binary bitwise operator [hicpp-signed-bitwise,-warnings-as-errors]
   21 |     printf("%"PRIu32, mytypeU32 & 1);
      |                                 ~ ^

编辑:

更多地考虑这一点,我认为前两个错误可能源于这样一个事实:8 位值在被

int
之前隐式提升为
|
,这是可以理解的。

但是为什么要抱怨

mytypeU32 & 1
而不是其他任何人 -
mytypeU32 << 2
testChar & 1
等?

clang bitwise-operators bit-shift clang-tidy
1个回答
0
投票

检查器规格

hicpp-signed-bitwise
检查器实现 规则5.6.1 Perforce 发布的高完整性 C++ 编码标准:

5.6.1 不要对带符号操作数使用按位运算符

在某些情况下,将带符号操作数与按位运算符一起使用会受到未定义或实现定义的行为的影响。因此,按位运算符只能与无符号整数类型的操作数一起使用。

这太模糊了,无法回答问题的某些方面,所以我会集中精力 关于 Clang 的实现。

检查器实施

IgnorePositiveIntegerLiterals
设置为
true
(根据 问题中的
.clang-tidy
文件),
hicpp-signed-bitwise
检查 (文档来源) 在以下情况下报告按位运算符的使用:

  • 任一操作数匹配

    SignedIntegerOperand

  • 两个操作数都有整型,这意味着 类型::isIntegerType 返回

    true

SignedIntegerOperand
(在此配置中)定义为 Clang AST 匹配器:

 expr(ignoringImpCasts(hasType(isSignedInteger())),
      unless(integerLiteral()))

这意味着操作数必须在跳过隐式转换后, 有符号整数类型 (Type::isSignedIntegerType

true
), 但不是整数文字。请注意,第二次检查确实 跳过隐式转换。

(我忽略了一些与问题无关的其他检查器详细信息。)

我不知道为什么 Clang 实现者选择了特定的 解释他们所做的规则,但我们现在可以继续使用 它来分析示例。

测试用例中的示例

我会一次一个地查看测试中的每个相关表达式,看看 为什么它被报道或未被报道。

首先,我会注意到所有示例都满足第二个条件, 因为两个操作数在每种情况下都具有整数类型。所以有趣的是 方面是

SignedIntegerOperand
匹配器,必须满足 由至少一个操作数来报告操作员。

  testChar & 1                                   // not reported

此处,

testChar
隐式提升为
int
(C99 6.5.10 p3, 6.3.1.8 p1、6.3.1.1 p2)。但是当跳过隐式转换时,
testChar
未签名,因此不匹配。同时,
1
没有 隐式转换,并且由于它是整数文字,因此也是如此 不匹配,因此没有报告。

  testChar << 2                                  // not reported

类似地,

testChar
被提升(C99 6.5.7 p3),但在下面未签名 那;并且
2
未升级(它已经是
int
),并且是 字面意思。

  (testChar << 2 ) | (testChar >> 2)             // reported

按照上述逻辑不会报告移位表达式。但对于 按位或表达式,两个操作数都具有类型

int
,并且都不是 字面意思,所以被报道。

  mytypeU8 << 2                                  // not reported

testChar << 2
相同。

  (mytypeU8 << 2 ) | (mytypeU8 >> 2)             // reported

(testChar << 2 ) | (testChar >> 2)
相同。

  mytypeU32 & 1                                  // reported

这是第一个棘手的问题。

mytypeU32
是无符号的,所以不 匹配。但是
1
看起来像一个整数文字,所以报告应该是 压制了,对吧?但由于 LHS 是
unsigned int
,所以 RHS 是 也转换为
unsigned int
!这增加了隐式转换, 并且该转换不是整数。

可以通过将匹配表达式更改为来确认这一点:

  expr(ignoringImpCasts(hasType(isSignedInteger())),
       unless(ignoringImpCasts(integerLiteral())))
           // ^^^^^^^^^^^^^^^^
           //     inserted

在这种情况下,它将不再匹配,因此不会被报告。 (我 使用

clang-query
检查这一点,而不是实际修改
clang-tidy
,为了方便起见。)

  mytypeU32 << 2                                 // not reported

这是第二个棘手的问题。这里,

mytypeU32
是无符号的,也是如此 不满足
SignedIntegerOperand
。但是
2
呢?难道不是 用于
mytypeU32 & 1
的逻辑适用吗?不——因为虽然
&
使用 “通常的算术转换”以达到common类型,
<<
仅仅是 独立地提升两个操作数,因为类型不需要是 相同。因此,
2
已经具有类型
int
(因此不会 升级下的更改),是 not 隐式转换,因此 is 整数文字,因此导致
SignedIntegerOperand
也不会 匹配。

  (mytypeU32 << 2 ) | (mytypeU32 >> 2)           // not reported

两个操作数均无符号,因此

SignedIntegerOperand
不匹配。

相关讨论

问题 “使用带符号整数操作数和二进制按位运算符” - 使用无符号短整型时 及其答案有一些有趣的相关讨论,包括 该规则的基本原理以及 Clang 对其的解释。

© www.soinside.com 2019 - 2024. All rights reserved.