bugprone-easily-swappable-parameters

查找函数定义,其中可转换类型的参数直接相邻,这使得调用站点容易调用函数,而参数交换(或顺序错误)。

void drawPoint(int X, int Y) { /* ... */ }
FILE *open(const char *Dir, const char *Name, Flags Mode) { /* ... */ }

从语言的角度来看,像 drawPoint(-2, 5)openPath("a.txt", "tmp", Read) 这样的潜在调用是完全合法的,但可能不是函数开发者预期的。

应使用更复杂且类型安全的结构,例如不透明类型定义或强类型,以防止参数顺序错误。

struct Coord2D { int X; int Y; };
void drawPoint(const Coord2D Pos) { /* ... */ }

FILE *open(const Path &Dir, const Filename &Name, Flags Mode) { /* ... */ }

由于可能需要进行复杂的重构和破坏 API 的更改来加强项目的类型安全性,因此不提供自动修复。

选项

扩展/放松选项

放松(或扩展)选项可用于扩展分析范围,并微调更多类型混合的启用。某些混合可能取决于项目的编码风格或偏好,但是,需要注意的是,启用所有这些放松模型在调用站点混合的方式。这些选项预计将使检查报告更多函数,并报告更长的可混合范围。

QualifiersMix

是否将某些cvr 限定T 的参数和一个不同cvr 限定T (即 Tconst Tconst Tvolatile T 等)视为彼此可混合。如果为 false,则检查将认为不同限定的类型不可混合。True 将打开警告。默认值为 false

以下示例仅在启用 QualifiersMix 时才会产生诊断

void *memcpy(const void *Destination, void *Source, std::size_t N) { /* ... */ }
ModelImplicitConversions

是否将类型 TU 的参数视为可混合,如果从 TU 和从 UT 存在隐式转换。如果为 false,则检查将不考虑隐式可转换的类型以进行可混合性检查。True 将打开隐式转换的警告。默认值为 true

以下示例仅在启用 ModelImplicitConversions 时才会产生诊断

void fun(int Int, double Double) { /* ... */ }
void compare(const char *CharBuf, std::string String) { /* ... */ }

注意

更改表达式的类型的限定符(例如,从 intconst int)在 C++ 标准中定义为隐式转换。但是,检查会根据是否启用了 QualifiersMix 来分离有关不同限定类型的可混合性的决策。

例如,以下代码段仅在启用 QualifiersMixModelImplicitConversions 两者时才会产生诊断

void fun2(int Int, const double Double) { /* ... */ }

过滤选项

过滤选项可用于减小检查器发出的诊断的大小,无论目的是忽略某些结构还是减轻噪音。

MinimumLength

诊断所需的相邻参数序列的最小长度。默认值为 2。可以是任何大于或等于 2 的正整数。如果给出 01,则将使用默认值 2

例如,如果指定 3,则上面的示例将不匹配。

IgnoredParameterNames

永远不应被视为可交换相邻参数序列一部分的参数**名称**列表。该值为一个以 ; 分隔的名称列表。要忽略未命名的参数,请将 “” 原样添加到列表中(不是空字符串,而是两个引号,可能需要转义!)。**此选项区分大小写!**

默认情况下,以下参数名称及其大写初始变体将被忽略:“”(未命名的参数)、iteratorbeginendfirstlastlhsrhs

IgnoredParameterTypeSuffixes

永远不应被视为可交换相邻参数序列一部分的参数**类型名称后缀**列表。其类型在源代码中写成的参数以该选项的一个元素结尾将被忽略。该值为一个以 ; 分隔的名称列表。**此选项区分大小写!**

默认情况下,以下内容及其小写初始变体将被忽略:boolItIteratorInputItForwardItBidirItRandomItrandom_iteratorReverseItreverse_iteratorreverse_const_iteratorRandomItrandom_iteratorReverseItreverse_iteratorreverse_const_iteratorConst_IteratorConstIteratorconst_reverse_iteratorConstReverseIterator。此外,_Bool(但不包括 _bool)也是默认值的一部分。

SuppressParametersUsedTogether

抑制有关在函数主体中一起使用或以类似方式使用的参数的诊断。默认值为 true。指定 false 将关闭启发式方法。

目前,已实现以下启发式方法,它们将抑制有关所涉及的参数对的警告

  • 参数在同一个表达式中使用,例如 f(a, b)a < b

  • 参数进一步传递给同一个函数,传递给该函数的同一个参数,传递给同一个重载。例如,f(a, 1)f(b, 2) 传递给某些 f(T, int)

    注意

    检查不执行路径敏感分析,因此,在此上下文中“同一个函数”是指同一个函数声明。如果在两个不同的实例上对类型的同一个成员函数进行调用,并将参数传递给该函数,则它仍然被视为“同一个函数”。

  • 访问同一个成员字段,或调用两个参数的成员方法,例如 a.foo()b.foo()

  • 独立的 return 语句在不同的代码路径上返回这两个参数中的任何一个。

NamePrefixSuffixSilenceDissimilarityTreshold

两个参数名称在头部尾部可能不同的字符数量,而名称的其余部分相同,以便关于这两个参数的警告被抑制。默认值为 1。可以是任何正整数。如果为 0,则基于参数名称的过滤启发式方法将被关闭。

此选项可用于抑制有关参数的警告,其中命名方案表明这些参数的顺序无关紧要。

例如,参数 LHSRHS 彼此 1 个后缀不同:LR 是不同的字符,而 HS 是共同的后缀。类似地,参数 text1, text2, text3 彼此 1 个前缀不同,末尾的数字是不同的部分。如果该值至少为 1,则不会报告此类情况。

限制

此检查旨在检查函数签名!

检查不会调查由编译器在仅从调用站点确定的上下文中生成的函数。这些情况包括可变参数函数、没有参数列表的 C 代码中的函数以及 C++ 模板实例化。大多数这些情况(从调用者的角度来看可以互换),在定义点没有办法进行“修复”。在 C++ 模板的情况下,只匹配和分析主模板定义和显式特化。

以下情况都不会产生诊断

int printf(const char *Format, ...) { /* ... */ }
int someOldCFunction() { /* ... */ }

template <typename T, typename U>
int add(T X, U Y) { return X + Y };

void theseAreNotWarnedAbout() {
    printf("%d %d\n", 1, 2);   // Two ints passed, they could be swapped.
    someOldCFunction(1, 2, 3); // Similarly, multiple ints passed.

    add(1, 2); // Instantiates 'add<int, int>', but that's not a user-defined function.
}

由于上述限制,其类型进一步依赖于模板实例化以证明它们与另一个参数的类型混合的参数不会被诊断。

template <typename T>
struct Vector {
  typedef T element_type;
};

// Diagnosed: Explicit instantiation was done by the user, we can prove it
// is the same type.
void instantiated(int A, Vector<int>::element_type B) { /* ... */ }

// Diagnosed: The two parameter types are exactly the same.
template <typename T>
void exact(typename Vector<T>::element_type A,
           typename Vector<T>::element_type B) { /* ... */ }

// Skipped: The two parameters are both 'T' but we cannot prove this
// without actually instantiating.
template <typename T>
void falseNegative(T A, typename Vector<T>::element_type B) { /* ... */ }

隐式转换(启用 ModelImplicitConversions 时)的上下文中,检查执行的建模会在参数可交换且交换后的顺序与隐式转换匹配的情况下发出警告。它不会模拟是否存在从两个参数都可以从其传递到函数调用的无关的第三种类型。这意味着在以下示例中,即使 strs() 明显具有以交换后的参数进行调用的可能性(只要参数是字符串字面量),也不会发出警告。

struct String {
    String(const char *Buf);
};

struct StringView {
    StringView(const char *Buf);
    operator const char *() const;
};

// Skipped: Directly swapping expressions of the two type cannot mix.
// (Note: StringView -> const char * -> String would be **two**
// user-defined conversions, which is disallowed by the language.)
void strs(String Str, StringView SV) { /* ... */ }

// Diagnosed: StringView implicitly converts to and from a buffer.
void cStr(StringView SV, const char *Buf() { /* ... */ }