用于Objective-C开发的警告标志


86

作为C和Objective-C程序员,我对编译器警告标志有些偏执。
我通常会尝试为我使用的编译器找到完整的警告标志列表,并打开大多数警告标志,除非我确实有很好的理由不打开它。

我个人认为,这实际上可能会提高编码技能以及潜在的代码可移植性,防止出现某些问题,因为它迫使您意识到每个小细节,潜在的实现和体系结构问题,等等。

我认为,即使您是经验丰富的程序员,它也是一种很好的日常学习工具。

对于此问题的主观部分,我有兴趣听取其他有关此主题的开发人员(主要是C,Objective-C和C ++)的信息。
您是否真的在乎诸如脚步警告之类的东西?如果是或否,为什么?

现在关于Objective-C,我最近完全切换到了LLVM工具链(使用Clang),而不是GCC。

在生产代码中,通常会设置以下警告标志(明确地,即使其中一些可能被-Wall覆盖):

  • -壁
  • -坏功能广播
  • -Wcast-align
  • -W转换
  • -W声明后声明
  • -W弃用的实现
  • -Wextra
  • -浮子等于
  • -Wformat = 2
  • -Wformat-非文字
  • -Wfour-char-constants
  • -隐式原子性质
  • -大括号
  • -遗失声明
  • -Wmissing-Field初始化程序
  • -遗漏格式属性
  • -不愿回报
  • -遗忘原型
  • -Wnested-externs
  • -Wnewline-eof
  • -老式风格的定义
  • -长弦
  • -希望
  • -Wpointer-arith
  • -多余的决定
  • -返回类型
  • -序列点
  • -影子
  • -将64缩短到32
  • -Wsign比较
  • -Wsign转换
  • -严格的原型
  • -严格选择匹配
  • -W开关
  • -Wswitch-默认
  • -Wswitch枚举
  • -绝对选择器
  • -未初始化
  • -Wonknown-pragmas
  • -不可达代码
  • -无用功能
  • -废标签
  • -无用参数
  • -无用价值
  • 无用变量
  • -Wwrite-strings

我很想听听其他开发人员对此有何评论。

例如,您认为我错过了Clang(Objective-C)的特定标志,为什么?
还是您认为特定的标志没有用(或根本不需要),为什么?

编辑

为了澄清这个问题,请注意-Wall仅提供了一些基本警告。
实际上,它们是更多警告标志,没有被涵盖-Wall,因此是问题以及我提供的列表。


9
我很想说这可能应该保留在Stack Overflow上,因为这是关于工具使用的问题,但是我们将看到社区的变化。
亚当李尔

感谢您的评论:)在某种程度上,我同意您的意见,这就是为什么我最初将其发布在StackOverflow上的原因(也是因为我不是这里的普通用户,所以让我感到羞耻)。得到了很多意见,支持等。但是没有一个答案。。。正如人们建议移动它。。。让我们来看
一下:)

好吧,希望我们能给您一个很好的答案。祝好运。:)
亚当·李尔

对于C和gcc,-Wwrite-strings使其成为一种非常相似但不是完全C的语言的编译器。仅出于这个原因,我不使用该选项。除了您指定的以外,我还使用-pedantic -Wstrict-overflow=5 -Winline -Wundef -Wcast-qual -Wlogical-op -Wstrict-aliasing=2-Wno-missing-braces -Wno-missing-field-initializers作为完全合理的初始化器struct whatever obj = {0};。我也发现它-Wconversion提供了比有用警告更多的“垃圾邮件” :)
pmg 2011年

Answers:


158

就上下文而言,我是在Google工作的Clang开发人员。在Google,我们已经将Clang的诊断程序(基本上)推广到了所有C ++开发人员,并且还将Clang的警告也视为错误。作为Clang开发人员和Clang诊断程序的较大用户之一,我将尝试阐明这些标志以及如何使用它们。请注意,我描述的所有内容通常都适用于Clang,而不特定于C,C ++或Objective-C。

TL; DR版本:请使用-Wall,并-Werror在您所开发的任何新代码最低。我们(编译器开发人员)出于充分的理由在此处添加警告:他们发现错误。如果您发现警告提示您发现错误,请也将其打开。-Wextra在这里尝试一堆好候选人。如果其中之一对您来说太吵而无法盈利,请提交错误报告。如果您编写的代码包含“明显的”错误,但是编译器未发出警告,请提交错误。

现在是长版。首先是关于警告标志分组的一些背景。在Clang中有很多警告“分组”(在GCC中是有限的)。一些与此讨论相关的内容:

  • 默认情况下:这些警告始终处于启用状态,除非您明确禁用它们。
  • -Wall:这些警告是开发人员对其价值和较低的假阳性率具有高度信心。
  • -Wextra:这些警告被认为是有价值且合理的(即它们不是越野车),但它们可能有很高的假阳性率或常见的哲学异议。
  • -Weverything:这是一个疯狂的组织,从字面上启用 了Clang中的每个警告。不要在您的代码中使用它。它仅适用于Clang开发人员或探索存在的警告

上面提到了两个主要标准,它们指导警告在Clang中的使用位置,并让我们阐明这些警告的真正含义。第一个是特定警告发生的潜在 。当警告触发并正确 识别代码问题时,这是对用户(开发人员)的预期收益。

第二个标准是假阳性报告的想法。在这些情况下,警告会在代码上触发,但是由于程序的上下文或某些其他约束,实际上并未发生被引用的潜在问题。警告的代码实际上行为正确。当警告从未打算在该代码模式上触发时,这些问题尤其严重。而是,警告的实现中存在缺陷,导致警告在那里触发。

对于Clang警告,该必须是正确性,而不是样式,口味或编码约定。这限制了可用的警告集,排除了经常请求的警告,例如{}if语句主体周围不使用s时发出的警告。克朗对假阳性也很不宽容。与大多数其他编译器不同,它将使用各种信息源来修剪误报,包括构造的确切拼写,是否存在多余的'()',强制转换甚至预处理器宏!

现在,让我们来看一些来自Clang的真实示例警告,并查看它们的分类方式。首先,默认警告:

% nl x.cc
     1  class C { const int x; };

% clang -fsyntax-only x.cc
x.cc:1:7: warning: class 'C' does not declare any constructor to initialize its non-modifiable members
class C { const int x; };
      ^
x.cc:1:21: note: const member 'x' will never be initialized
class C { const int x; };
                    ^
1 warning generated.

这里不需要标志来得到这个警告。理由是,这永远不是真正正确的代码,给出警告高的,并且警告仅在Clang可以证明属于此存储区的代码上触发,从而使假阳性率为零。

% nl x2.cc
     1  int f(int x_) {
     2    int x = x;
     3    return x;
     4  }

% clang -fsyntax-only -Wall x2.cc
x2.cc:2:11: warning: variable 'x' is uninitialized when used within its own initialization [-Wuninitialized]
  int x = x;
      ~   ^
1 warning generated.

Clang需要-Wall此警告标志。原因是那里有大量的代码使用了(无论好坏)我们警告过的有意产生未初始化值的代码模式。从哲学上讲,我认为没有任何意义,但是许多其他人不同意,意见分歧的现实是警告的发出 -Wall。它仍然具有很高的价值和非常低的 假阳性率,但是在某些代码库上,它是一个入门者。

% nl x3.cc
     1  void g(int x);
     2  void f(int arr[], unsigned int size) {
     3    for (int i = 0; i < size; ++i)
     4      g(arr[i]);
     5  }

% clang -fsyntax-only -Wextra x3.cc
x3.cc:3:21: warning: comparison of integers of different signs: 'int' and 'unsigned int' [-Wsign-compare]
  for (int i = 0; i < size; ++i)
                  ~ ^ ~~~~
1 warning generated.

此警告需要-Wextra标志。原因是存在非常 大的代码库,其中比较不匹配的符号极为常见。尽管此警告确实发现了一些错误,但平均而言,当用户编写代码时,该代码成为错误的可能性很小。结果是极高的假阳性率。但是,如果由于奇怪的升级规则而导致程序中存在错误,则该警告标志着一个错误具有相对较高的价值时,通常会非常微妙地发出此警告 。结果,Clang提供了它,并在一个标志下公开了它。

通常,警告不会在-Wextra标志之外持续很长时间。Clang竭尽全力不实施警告,而不会看到常规使用和测试的警告。所打开的其他警告-Weverything通常是处于活动开发中或带有活动错误的警告。要么将它们固定并放置在适当的标记下,要么将其删除。

既然我们已经了解了这些东西如何与Clang一起工作,让我们回到最初的问题:您应该为开发打开什么警告?不幸的是,答案取决于它。考虑以下问题,以帮助确定哪种警告最适合您的情况。

  • 您可以控制所有代码,还是其中一些是外部代码?
  • 你的目标是什么?捕获错误或编写更好的代码?
  • 您的假阳性容忍度是多少?您是否愿意编写额外的代码来定期使警告静音?

首先,如果您不控制代码,请不要尝试在上面打开额外的警告。准备关闭一些。世界上有很多错误的代码,您可能无法修复所有错误代码。那没问题。努力找到一个方式来集中你的代码的努力,你的控制。

接下来,找出警告中想要的内容。对于不同的人来说这是不同的。Clang会尝试警告严重的错误,或我们有悠久历史的代码模式,这些错误表明错误率极高,因此没有任何选择。通过启用,-Wall您将获得一套更具攻击性的警告,以捕获Clang开发人员在C ++代码中发现的最常见错误。但同时使用这两种方法, 假阳性率仍应保持较低水平。

最后,如果您完全愿意在任何时候都使* 假阳性 * 安静下来,请继续-Wextra。如果您发现警告捕获了很多实际的错误,但这些警告具有愚蠢或毫无意义的误报,请归档错误。我们一直在努力寻找方法,将越来越多的错误发现逻辑-Wextra带入-Wall我们可以避免错误肯定的地方。

许多人会发现这些选择都不适合他们。在Google,-Wall由于许多现有代码违反了警告,因此我们已关闭了一些警告。即使未通过启用警告,我们也已明确启用了某些警告-Wall,因为它们对我们而言具有特别高的价值。您的里程会有所不同,但可能会以类似的方式发生变化。启用一些关键警告而不是全部警告通常会好得多 -Wextra

我鼓励所有人打开-Wall任何非遗留代码。对于新代码,这里的警告几乎总是有价值的,并确实使开发代码的经验更好。相反,我鼓励所有人 不要启用以外的标志-Wextra。如果您发现-Wextra 不包含但对您完全有价值的Clang警告,只需提交一个错误,我们可能会将其放在下方-Wextra。是否明确启用警告中的某些子集将在-Wextra很大程度上取决于您的代码,您的编码风格以及维护该列表是否比修复由覆盖的所有内容更容易-Wextra

在OP的警告列表(包括-Wall-Wextra)中,只有以下警告未被这两个组覆盖(或默认情况下处于启用状态)。第一组强调为什么过度依赖显式警告标志可能会很糟糕:在Clang 中甚至都没有实现!仅出于GCC兼容性在命令行上接受它们。

  • -Wbad-function-cast
  • -Wdeclaration-after-statement
  • -Wmissing-format-attribute
  • -Wmissing-noreturn
  • -Wnested-externs
  • -Wnewline-eof
  • -Wold-style-definition
  • -Wredundant-decls
  • -Wsequence-point
  • -Wstrict-prototypes
  • -Wswitch-default

原始列表中的下一个不必要警告是与该列表中的其他警告多余的警告:

  • -Wformat-nonliteral -的子集 -Wformat=2
  • -Wshorten-64-to-32 -的子集 -Wconversion
  • -Wsign-conversion -的子集 -Wconversion

还有一些警告,它们在分类上更加不同。这些处理语言的方言变体,而不是儿童车或非越野车代码。除了-Wwrite-strings,这些都是Clang提供的语言扩展警告。Clang是否警告使用它们取决于扩展的普遍性。Clang旨在实现GCC兼容性,因此在许多情况下,它通过广泛使用的隐式语言扩展而得以缓解。-Wwrite-strings如对OP所述,它是GCC的兼容性标志,它实际上改变了程序的语义。我对这个标志深表遗憾,但是由于它的遗产,我们必须予以支持。

  • -Wfour-char-constants
  • -Wpointer-arith
  • -Wwrite-strings

实际启用潜在有趣警告的其余选项如下:

  • -Wcast-align
  • -Wconversion
  • -Wfloat-equal
  • -Wformat=2
  • -Wimplicit-atomic-properties
  • -Wmissing-declarations
  • -Wmissing-prototypes
  • -Woverlength-strings
  • -Wshadow
  • -Wstrict-selector-match
  • -Wundeclared-selector
  • -Wunreachable-code

这些不存在-Wall-Wextra不总是很清楚的原因。对于许多的这些,其实都是基于GCC警告(-Wconversion-Wshadow,等),因此锵试图模仿GCC的行为。我们正在将其中的一些细分为更细粒度和有用的警告。然后,那些人更有可能成为高层警告组之一。也就是说,要提一个警告,范围-Wconversion如此之广,以至于在可预见的未来,它很可能仍将是其自己的“顶级”类别。海湾合作委员会拥有的其他一些警告,但其价值低假阳性率高,可能会被归类为类似的无人区。

为什么不在大型存储桶之一中的其他原因包括简单的错误,非常明显的假阳性问题以及开发中的警告。我将研究可以识别的错误的归档。它们最终都应该迁移到适当的大存储桶标志中,或者从Clang中删除。

我希望这可以澄清Clang的警告情况,并为那些试图为其使用或公司使用选择警告的人提供一些见识。


8
出色的答案!非常感谢。我需要重新阅读几次,但是稍后我会发表一些评论。再次感谢!
Macmade 2011年

1
@钱德勒·卡鲁斯:好答案!但是,如果发生任何更改,您是否可以更新它?我在上一个列表中使用了所有警告,但也许有些警告已移至Wextra?
gartenriese

这是一个很棒的帖子。但是,我发现找到新的警告标志非常有用,因此,令我失望的是,尽管您承认它对这种探索很有用,但您似乎对“一切”团体都持消极态度。
凯尔·斯特兰德

@钱德勒·卡鲁斯(Chandler Carruth):我同意警告“从未真正正确”的代码背后的理由。它只是没有-Wno关掉warn_no_constructor_for_refconst 使得PITA使用Boost.ConceptCheck和一致好评:github.com/boostorg/concept_check/blob/...
mloskot

2

我不使用Clang,但希望您也不要介意我的意见。我还使用最大警告级别(我假设这是-Wall的意思),并且将警告视为错误(我假设这是-Werror的意思),并且我只禁用了那些没有太大意义的警告。实际上,C#编译器未发出某些非常有用的警告使我非常恼火,并且必须运行特殊的代码分析工具才能看到它们。我喜欢警告,因为它们可以帮助我捕获代码中的小错误和错误。我非常喜欢它们,以至于当我为我所知道的正确代码发出警告时,无论如何我都会重构该代码,这样就不会发出警告,而是禁用警告,或者--even更糟的是-让它出现并忽略它。我认为警告很棒


1
感谢您的回答。不幸的是,-Wall仅提供基本警告。许多现有的警告标志没有被涵盖-Wall,因此我的问题清单。
Macmade 2011年

0

我通常会为新项目使用Xcode的默认设置。它在帮助与烦恼之间取得了良好的平衡。警告和其他编译器选项的任何特定列表都将主要基于个人喜好或项目要求,因此,我认为尝试浏览您的列表并主张或反对特定警告没有太大价值。如果它适合您,那就太好了。如果发现您犯了一些错误,编译器应该能够检测到该错误,并且希望在将来获得帮助,请寻找一个编译器选项来警告并打开它。

许多程序员主张的一件事是打开“将警告作为错误处理”(-Werror)选项,以防止在有任何警告的情况下成功完成构建。


感谢您的回答!我同意-Werror。实际上,当我使用Clang时,我都使用了编译指示(#pragma clang诊断)来设置所有这些警告,并且它们都设置为致命错误,所以这与-Werror:相同)
Macmade

0

我认为人们关心警告的最好证据是它们存在并且被广泛使用的事实。我强烈认为,编译期间捕获的错误越多越好。如果这不是一个普遍存在的信念,那么每个人都会使用弱动态类型的语言,因为它们的编译器或解释器为您提供了更多的回旋余地。相反,即使在脚本语言上,strict标志的使用也很普遍。在静态强类型语言上,人们不仅-Wall -Werror经常使用警告,而且经常发现警告如此有价值,以至于他们甚至想要更多。因此,他们购买了诸如Coverity之类的静态分析工具,其唯一目的是提供警告。

这并不意味着没有批评者。许多开发人员认为警告是短期内对他们不利,而不是长期内对他们不利,因此不要试图解决潜在的问题。因此,您会看到像我昨天遇到的这段代码一样的可爱代码:

node = node; // Shut up compiler warning

感谢您的回答。问题是我经常看到人们在没有的情况下工作-Werror,而实际上却忽略了编译器发出的警告。或者,如果这样做,通常只是坚持-Wall。我在问题中提供的大多数标志都没有被覆盖-Wall,我个人认为它们也是必不可少的。那我只是偏执吗?; )
Macmade 2011年

0

通常,我更倾向于-Wall,并且绝对相信也包括-Werror。模块永远不要有预期的警告。您最终将收到另一个警告,并且错过它。那将是一个实际存在的问题。

它还取决于您是否要在新项目或旧项目中添加“ -Wall -Werror”。如果是新产品,那就去做,并要求完美,否则您可能需要几天或几周的编辑和测试时间。我只是在一个稍旧的小型项目中执行了此操作,然后花了两三天时间删除了警告。我认为代码现在更干净了。

换句话说,尝试一下看看。

兰迪


1
如果您仅关注-Wall-Werror,我想您应该重新阅读该问题。许多警告标志不包含在范围内-Wall。无论如何,谢谢您的回答。
Macmade 2011年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.