代码风格指南

缩进

使用空格,而不是制表符。制表符只应出现在因语义需要而必须使用的文件中,例如 Makefiles。

缩进大小为 4 个空格。

正确
int main()
{
    return 0;
}
错误
int main() 
{
        return 0;
}

命名空间的内容不应缩进。

正确
// Document.h
namespace WebCore {

class Document {
    Document();
    ...
};

namespace NestedNamespace {

class OtherDocument {
    OtherDocument();
    ...
};

}

} // namespace WebCore

// Document.cpp
namespace WebCore {

Document::Document()
{
    ...
}

namespace NestedNamespace {

OtherDocument::OtherDocument()
{
    ...
}

} // namespace NestedNamespace

} // namespace WebCore
正确
// PrivateClickMeasurementDatabase.h
namespace WebKit::PCM {

class Database {
    ...
};

} // namespace WebKit::PCM
错误
// Document.h
namespace WebCore {

    class Document {
        Document();
        ...
    };

    namespace NestedNamespace {
    ...
    }

} // namespace WebCore

// Document.cpp
namespace WebCore {

    Document::Document()
    {
        ...
    }

} // namespace WebCore

case 标签应与对应的 switch 语句对齐。case 语句应缩进。

正确
switch (condition) {
case fooCondition:
case barCondition:
    i++;
    break;
default:
    i--;
}
错误
switch (condition) {
    case fooCondition:
    case barCondition:
        i++;
        break;
    default:
        i--;
}

同一嵌套级别上跨多行的布尔表达式,其运算符应位于行的左侧而非右侧。

正确
return attribute.name() == srcAttr
    || attribute.name() == lowsrcAttr
    || (attribute.name() == usemapAttr && attribute.value().string()[0] != '#');
错误
return attribute.name() == srcAttr ||
    attribute.name() == lowsrcAttr ||
    (attribute.name() == usemapAttr && attr->value().string()[0] != '#');

空格

一元运算符周围不应有空格。

正确
i++;
错误
i ++;

在二元和三元运算符周围放置空格。

正确
y = m * x + b;
f(a, b);
c = a | b;
return condition ? 1 : 0;
错误
y=m*x+b;
f(a,b);
c = a|b;
return condition ? 1:0;

在基于范围的 for 循环中,冒号周围应放置空格。

正确
Vector<PluginModuleInfo> plugins;
for (auto& plugin : plugins)
    registerPlugin(plugin);
错误
Vector<PluginModuleInfo> plugins;
for (auto& plugin: plugins)
    registerPlugin(plugin);

逗号和分号前不应放置空格。

正确
for (int i = 0; i < 10; ++i)
    doSomething();

f(a, b);
错误
for (int i = 0 ; i < 10 ; ++i)
    doSomething();

f(a , b) ;

控制语句与其括号之间应放置空格。

正确
if (condition)
    doIt();
错误
if(condition)
    doIt();

函数声明或调用中的名称、尖括号和括号之间不应放置空格。

正确
f();
void g() { ... }
h<int>();
错误
f ();
void g () { ... }
h <int> ();

函数声明或调用中的括号及其参数之间,或尖括号及其参数之间不应放置空格。

正确
f(a, b);
void g(int a) { ... }
h<int>();
错误
f( a, b );
void g( int a ) { ... }
h< int >();

lambda 函数的方括号、尖括号和括号之间不应放置空格,但大括号前应放置一个空格。

正确
[](int x) { return x; }
[this] { return m_member; }
[=]<typename T> { return T(); }
[&]<typename X>(X parameter) { return parameter; }
错误
[] (int x) { return x; }
[this]{ return m_member; }
[=] <typename T> { return T(); }
[&]<typename X> (X parameter) { return parameter; }

标识符 template 及其尖括号之间不应放置空格。

正确
template<typename T> T foo();
template<typename U> struct Bar { };
错误
template <typename T> T foo();
template <typename U> struct Bar { };

初始化对象时,在左大括号前以及大括号及其内容之间放置一个空格。

正确
Foo foo { bar };
错误
Foo foo{ bar };
Foo foo {bar};

在 Objective-C 中,块的开头与其参数之间,或块的开头与其开大括号之间不应放置空格。在参数列表和块的开大括号之间放置一个空格。

正确
block = ^{
...
};

block = ^(int, int) {
...
};

错误
block = ^ {
...
};

block = ^ (int, int){
...
};

在 Objective-C 中,类型名称和协议名称之间不应放置空格。

正确
id<MTLDevice> device = ...;
错误
id <MTLDevice> device = ...;

换行

每条语句都应独占一行。

正确
x++;
y++;
if (condition)
    doIt();
错误
x++; y++;
if (condition) doIt();

链式 = 赋值应拆分为多条语句。

正确
rightSpacing = totalSpacing / 2;
leftSpacing = rightSpacing;
错误
leftSpacing = rightSpacing = totalSpacing / 2;

如果前面有闭合大括号,else 语句应与该闭合大括号在同一行;否则,它应与 if 语句对齐。

正确
if (condition) {
    ...
} else {
    ...
}

if (condition)
    doSomething();
else
    doSomethingElse();

if (condition)
    doSomething();
else {
    ...
}
错误
if (condition) {
    ...
}
else {
    ...
}

if (condition) doSomething(); else doSomethingElse();

if (condition) doSomething(); else {
    ...
}

当之前的 if 语句以 return 语句结束时,else if 语句应写成一个 if 语句。

正确
if (condition) {
    ...
    return someValue;
}
if (condition) {
    ...
}
错误
if (condition) {
    ...
    return someValue;
} else if (condition) {
    ...
}

大括号

函数定义:每个大括号独占一行。

正确
int main()
{
    ...
}
错误
int main() {
    ...
}

其他大括号:开大括号放在代码块前的行上;闭大括号独占一行。

正确
class MyClass {
    ...
};

namespace WebCore {
    ...
}

for (int i = 0; i < 10; ++i) {
    ...
}
错误
class MyClass 
{
    ...
};

单行控制语句不应使用大括号,除非包含注释或单个语句跨越多行。

正确
if (condition)
    doIt();

if (condition) {
    // Some comment
    doIt();
}

if (condition) {
    myFunction(reallyLongParam1, reallyLongParam2, ...
        reallyLongParam5);
}
错误
if (condition) {
    doIt();
}

if (condition)
    // Some comment
    doIt();

if (condition)
    myFunction(reallyLongParam1, reallyLongParam2, ...
        reallyLongParam5);

没有主体内容的控制语句应使用空大括号

正确
for ( ; current; current = current->next) { }
错误
for ( ; current; current = current->next);

任何空大括号内都应包含一个空格。

正确
void f() { }
struct Unit { };
union Unit { };
class Unit { };
enum Unit { };
int x { };
auto a = [] { };
while (true) { }
错误
void f() {}
struct Unit {};
union Unit {};
class Unit {};
enum Unit {};
int x {};
auto a = [] {};
while (true) {}

空值、假值和零值

在 C++ 中,空指针值应写为 nullptr。在 C 中,应写为 NULL。在 Objective-C 和 Objective-C++ 中,分别遵循 C 或 C++ 的指导原则,但使用 nil 表示空 Objective-C 对象。

C++ 和 C 的 bool 值应写为 truefalse。Objective-C 的 BOOL 值应写为 YESNO

对真/假、空/非空、零/非零的测试都应在不使用相等比较的情况下完成。

正确
if (condition)
    doIt();

if (!ptr)
    return;

if (!count)
    return;
错误
if (condition == true)
    doIt();

if (ptr == NULL)
    return;

if (count == 0)
    return;

在 Objective-C 中,实例变量会自动初始化为零。在 init 方法中不要添加显式初始化为 nil 或 NO。

浮点字面量

除非为了强制进行浮点运算,否则不应在浮点字面量后添加 .0.f.0f

正确
const double duration = 60;

void setDiameter(float diameter)
{
    radius = diameter / 2;
}

setDiameter(10);

const int framesPerSecond = 12;
double frameDuration = 1.0 / framesPerSecond;
错误
const double duration = 60.0;

void setDiameter(float diameter)
{
    radius = diameter / 2.f;
}

setDiameter(10.f);

const int framesPerSecond = 12;
double frameDuration = 1 / framesPerSecond; // integer division

命名

使用驼峰命名法。在类、结构体、协议或命名空间名称中,首字母(包括首字母缩写中的所有字母)应大写。在变量或函数名称中,首字母(包括首字母缩写中的所有字母)应小写。

正确
struct Data;
size_t bufferSize;
class HTMLDocument;
String mimeType();
错误
struct data;
size_t buffer_size;
class HtmlDocument;
String MIMEType();

使用完整的单词,除非在极少数情况下,缩写更具规范性且更易于理解。

正确
size_t characterSize;
size_t length;
short tabIndex; // more canonical
错误
size_t charSize;
size_t len;
short tabulationIndex; // bizarre

C++ 类中的数据成员应为私有。静态数据成员应以“s_”为前缀。其他数据成员应以“m_”为前缀。

正确
class String {
public:
    ...

private:
    short m_length;
};
错误
class String {
public:
    ...

    short length;
};

Objective-C 实例变量应以“_”为前缀。

正确
@class String
    ...
    short _length;
@end
错误
@class String
    ...
    short length;
@end

布尔值前应使用“is”和“did”等词。

正确
bool isValid;
bool didSendData;
错误
bool valid;
bool sentData;

setter 方法应以“set”开头。getter 方法应使用裸词(即不加前缀)。setter 和 getter 的名称应与被设置/获取的变量名称匹配。

正确
void setCount(size_t); // sets m_count
size_t count(); // returns m_count
错误
void setCount(size_t); // sets m_theCount
size_t getCount();

通过输出参数返回值(out arguments)的 getter 方法应以“get”开头。

正确
void getInlineBoxAndOffset(InlineBox*&, int& caretOffset) const;
错误
void inlineBoxAndOffset(InlineBox*&, int& caretOffset) const;

函数名称中应使用描述性动词。

正确
bool convertToASCII(short*, size_t);
错误
bool toASCII(short*, size_t);

成员变量的 getter 函数不应有任何后缀或前缀来指示该函数可以可选地创建或初始化成员变量。如果存在一个会自动创建对象的变体,则应将不自动创建对象的 getter 函数后缀为 IfExists

正确
StyleResolver* styleResolverIfExists();
StyleResolver& styleResolver();
错误
StyleResolver* styleResolver();
StyleResolver& ensureStyleResolver();
正确
Frame* frame();
错误
Frame* frameIfExists();

函数声明中应省略无意义的变量名。一个好的经验法则是,如果参数类型名包含参数名(不带尾随数字或复数),则不需要参数名。通常,布尔型、字符串型和数值型参数应有参数名。

正确
void setCount(size_t);

void doSomething(ScriptExecutionContext*);
错误
void setCount(size_t count);

void doSomething(ScriptExecutionContext* context);

如果调用者可能传递常量,则函数参数应优先使用枚举而不是布尔值,因为命名常量在调用点更容易阅读。此规则的一个例外是 setter 函数,其中函数名称已经清楚地表明了布尔值的含义。

正确
doSomething(something, AllowFooBar);
paintTextWithShadows(context, ..., textStrokeWidth > 0, isHorizontal());
setResizable(false);
错误
doSomething(something, false);
setResizable(NotResizable);

Objective-C 方法名称应遵循 Cocoa 命名指南——它们应读起来像一个短语,并且选择器的每个部分都应以小写字母开头并使用内部大写字母。

枚举成员应使用首字母大写的内部大写(InterCaps)命名法。

优先使用 const 而非 #define。优先使用内联函数而非宏。

#defined 常量应使用全大写字母,单词之间用下划线分隔。

展开为函数调用或其他非常量计算的宏:这些宏应像函数一样命名,并且末尾应有括号,即使它们不接受任何参数(除了某些特殊宏,如 ASSERT)。请注意,在这种情况下通常优先使用内联函数而不是宏。

正确
#define WBStopButtonTitle() 
        NSLocalizedString(@"Stop", @"Stop button title")
错误
#define WB_STOP_BUTTON_TITLE 
        NSLocalizedString(@"Stop", @"Stop button title")

#define WBStopButtontitle 
        NSLocalizedString(@"Stop", @"Stop button title")

头文件防护应使用 #pragma once 而非 #define#ifdef

正确
// HTMLDocument.h
#pragma once
错误
// HTMLDocument.h
#ifndef HTMLDocument_h
#define HTMLDocument_h

用于保护 this 免遭删除的 Ref 和 RefPtr 对象应命名为“protectedThis”。

正确
RefPtr<Node> protectedThis(this);
Ref<Element> protectedThis(*this);
RefPtr<Widget> protectedThis = this;
错误
RefPtr<Node> protector(this);
Ref<Node> protector = *this;
RefPtr<Widget> self(this);
Ref<Element> elementRef(*this);

用于保护 this 以外的变量免遭删除的 Ref 和 RefPtr 对象应命名为“protector”,或“protected”与变量名的大写形式结合。

正确
RefPtr<Element> protector(&element);
RefPtr<Element> protector = &element;
RefPtr<Node> protectedNode(node);
RefPtr<Widget> protectedMainWidget(m_mainWidget);
RefPtr<Loader> protectedFontLoader = m_fontLoader;
错误
RefPtr<Node> nodeRef(&rootNode);
Ref<Element> protect(*element);
RefPtr<Node> protectorNode(node);
RefPtr<Widget> protected = widget;

其他标点符号

C++ 类的构造函数应使用 C++ 初始化器语法初始化其所有成员。每个成员(和超类)应单独缩进一行,冒号或逗号在该行成员之前。

正确
MyClass::MyClass(Document* document)
    : MySuperClass()
    , m_myMember(0)
    , m_document(document)
{
}

MyOtherClass::MyOtherClass()
    : MySuperClass()
{
}
错误
MyClass::MyClass(Document* document) : MySuperClass()
{
    m_myMember = 0;
    m_document = document;
}

MyOtherClass::MyOtherClass() : MySuperClass() {}

在 Vector 迭代中,为了代码简洁易读,优先使用索引而非迭代器。

正确
for (auto& frameView : frameViews)
    frameView->updateLayoutAndStyleIfNeededRecursive();

正确

unsigned frameViewsCount = frameViews.size();
for (unsigned i = 0; i < frameViewsCount; ++i)
    frameViews[i]->updateLayoutAndStyleIfNeededRecursive();
错误
const Vector<RefPtr<FrameView> >::iterator end = frameViews.end();
for (Vector<RefPtr<FrameView> >::iterator it = frameViews.begin(); it != end; ++it)
    (*it)->updateLayoutAndStyleIfNeededRecursive();

在 C++ lambda 中,尽可能省略括号。

正确
[this] { return m_member; }
[this]() mutable { return doWork(WTFMove(m_object)); }
错误
[this]() { return m_member; }
[]() { return static_cast<unsigned>(-1); }

只有在可以省略冗余信息时,才在函数返回类型中使用箭头。

正确
int foo()
{
    ...
}
错误
auto foo() -> int
{
    ...
}
正确
auto Foo::bar() -> Baz
{
    ...
}
错误
Foo::Baz Foo::bar()
{
    ...
}

指针和引用

非 C++ 代码中的指针类型
指针类型应在类型和 * 之间放置一个空格(这样 * 就紧邻其后的标识符,如果有的话)。

C++ 代码中的指针和引用类型
指针类型和引用类型在类型名称与 *& 之间不应有空格。

正确
Image* SVGStyledElement::doSomething(PaintInfo& paintInfo)
{
    SVGStyledElement* element = static_cast<SVGStyledElement*>(node());
    const KCDashArray& dashes = dashArray();
错误
Image *SVGStyledElement::doSomething(PaintInfo &paintInfo)
{
    SVGStyledElement *element = static_cast<SVGStyledElement *>(node());
    const KCDashArray &dashes = dashArray();

函数的输出参数应通过引用传递,除非在极少数情况下它是可选的,此时应通过指针传递。

正确
void MyClass::getSomeValue(OutArgumentType& outArgument) const
{
    outArgument = m_value;
}

void MyClass::doSomething(OutArgumentType* outArgument) const
{
    doSomething();
    if (outArgument)
        *outArgument = m_value;
}
错误
void MyClass::getSomeValue(OutArgumentType* outArgument) const
{
    *outArgument = m_value;
}

#include 语句

所有实现文件必须首先 #include config.h。头文件不应包含 config.h

正确
// RenderLayer.h
#include "Node.h"
#include "RenderObject.h"
#include "RenderView.h"
错误
// RenderLayer.h
#include "config.h"

#include "RenderObject.h"
#include "RenderView.h"
#include "Node.h"

所有实现文件必须第二个 #include 主头文件,紧跟在 config.h 之后。例如,Node.cpp 应首先包含 Node.h,然后才是其他文件。这确保了每个头文件的完整性得到测试。这也保证了每个头文件都可以在不先包含任何其他头文件的情况下编译。

其他 #include 语句应按排序顺序排列(区分大小写,如同命令行排序工具或 Xcode 排序选择命令所做的那样)。无需按逻辑顺序组织它们。

正确
// HTMLDivElement.cpp
#include "config.h"
#include "HTMLDivElement.h"

#include "Attribute.h"
#include "HTMLElement.h"
#include "QualifiedName.h"
错误
// HTMLDivElement.cpp
#include "HTMLElement.h"
#include "HTMLDivElement.h"
#include "QualifiedName.h"
#include "Attribute.h"

系统头文件的包含必须在其他头文件包含之后。

正确
// ConnectionQt.cpp
#include "ArgumentEncoder.h"
#include "ProcessLauncher.h"
#include "WebPageProxyMessageKinds.h"
#include "WorkItem.h"
#include <QApplication>
#include <QLocalServer>
#include <QLocalSocket>
错误
// ConnectionQt.cpp
#include "ArgumentEncoder.h"
#include "ProcessLauncher.h"
#include <QApplication>
#include <QLocalServer>
#include <QLocalSocket>
#include "WebPageProxyMessageKinds.h"
#include "WorkItem.h"

“using” 语句

在头文件中,不要在命名空间(或全局)作用域中使用“using”语句。

正确
// wtf/Vector.h

namespace WTF {

class VectorBuffer {
    using std::min;
    ...
};

} // namespace WTF
错误
// wtf/Vector.h

namespace WTF {

using std::min;

class VectorBuffer {
    ...
};

} // namespace WTF

然而,在 WTF 子库的头文件中,允许在文件末尾使用“using”声明,将 WTF 命名空间中的一个或多个名称导入到全局作用域。

正确
// wtf/Vector.h

namespace WTF {

} // namespace WTF

using WTF::Vector;
错误
// wtf/Vector.h

namespace WTF {

} // namespace WTF

using namespace WTF;
错误
// runtime/JSObject.h

namespace WTF {

} // namespace WTF

using WTF::PlacementNewAdopt;

在 C++ 实现文件中,不要使用任何类型的“using”声明来导入标准模板库中的名称。而应在使用它们的地方直接限定名称。

正确
// HTMLBaseElement.cpp

namespace WebCore {

  std::swap(a, b);
  c = std::numeric_limits<int>::max()

} // namespace WebCore
错误
// HTMLBaseElement.cpp

using std::swap;

namespace WebCore {

  swap(a, b);

} // namespace WebCore
错误
// HTMLBaseElement.cpp

using namespace std;

namespace WebCore {

  swap(a, b);

} // namespace WebCore

在实现文件中,如果“using namespace”语句是针对其父命名空间在该文件中定义的嵌套命名空间,则将该语句放在该命名空间定义内部。

正确
// HTMLBaseElement.cpp

namespace WebCore {

using namespace HTMLNames;

} // namespace WebCore
错误
// HTMLBaseElement.cpp

using namespace WebCore::HTMLNames;

namespace WebCore {

} // namespace WebCore

在实现文件中,将所有“using namespace”语句放在命名空间定义内部。

正确
// HTMLSelectElement.cpp

namespace WebCore {

using namespace other;

} // namespace WebCore
错误
// HTMLSelectElement.cpp

using namespace other;

namespace WebCore {

} // namespace WebCore

Lambda 表达式

当函数体中需要参数的显式类型时,优先使用带有显式模板参数列表的 lambda 表达式。

正确
[]<typename T>(T arg) { 
    if constexpr (T::isGood)
        go();
}

错误
[](auto arg) {
    using T = std::decay_t<decltype(arg)>;
    if constexpr (T::isGood)
        go();
}

类型

使用“unsigned”修饰符时省略“int”。不要使用“signed”修饰符。直接使用“int”即可。

正确
unsigned a;
int b;
错误
unsigned int a; // Doesn't omit "int".
signed b; // Uses "signed" instead of "int".
signed int c; // Doesn't omit "signed".

当参数合理地被认为是类型转换且类型转换很快时,使用构造函数进行隐式转换。否则,使用 explicit 关键字或返回该类型的函数。这仅适用于单参数构造函数。

正确
class LargeInt {
public:
    LargeInt(int);
...

class Vector {
public:
    explicit Vector(int size); // Not a type conversion.
    Vector create(Array); // Costly conversion.
...

错误
class Task {
public:
    Task(ScriptExecutionContext&); // Not a type conversion.
    explicit Task(); // No arguments.
    explicit Task(ScriptExecutionContext&, Other); // More than one argument.
...

单例模式

使用名为“singleton()”的静态成员函数来访问单例实例。

正确
class MySingleton {
public:
    static MySingleton& singleton();
...
错误
class MySingleton {
public:
    static MySingleton& shared();
...
错误
class MySingleton {
...
};

MySingleton& mySingleton(); // free function.

注释

行末注释前以及注释中的句子之间只使用一个空格。

正确
f(a, b); // This explains why the function call was done. This is another sentence.
错误
int i;    // This is a comment with several spaces before it, which is a non-conforming style.
double f; // This is another comment.  There are two spaces before this sentence which is a non-conforming style.

注释应像句子一样,以大写字母开头,并以句号(标点符号)结尾。一个例外是行末注释,例如 if (x == y) // false for NaN

使用 FIXME:(不带归属)来表示将来需要处理的事项。

正确
drawJpg(); // FIXME: Make this code handle jpg in addition to the png support.
错误
drawJpg(); // FIXME(joe): Make this code handle jpg in addition to the png support.
drawJpg(); // TODO: Make this code handle jpg in addition to the png support.

如果为了清晰起见,在一段虚函数重写声明前添加包含源类名称的注释。

正确
class GPUProcessConnection : public RefCounted<GPUProcessConnection>, public IPC::Connection::Client {
public:
    /// ...

    // IPC::Connection::Client
    void didClose(IPC::Connection&) override;
    void didReceiveMessage(IPC::Connection&, IPC::Decoder&) final;
    bool didReceiveSyncMessage(IPC::Connection&, IPC::Decoder&, UniqueRef<IPC::Encoder>&) final;
    void didReceiveInvalidMessage(IPC::Connection&, IPC::MessageName) override;

    // ...
};

如果为了清晰起见,在一组 IPC 消息函数声明前添加 // Messages

正确
class GPUProcessConnection : public RefCounted<GPUProcessConnection>, public IPC::Connection::Client {
public:
    /// ...

    // Messages
    void didReceiveRemoteCommand(WebCore::PlatformMediaSession::RemoteControlCommandType, const WebCore::PlatformMediaSession::RemoteCommandArgument&);
    void didInitialize(std::optional<GPUProcessConnectionInfo>&&);

    // ...
};

重写虚方法

类中虚方法的基层声明必须使用 virtual 关键字声明。该类的所有子类在重写虚方法时必须指定 override 关键字,或者在重写虚方法并要求后续子类不能再重写它时指定 final 关键字。切勿用 virtualoverridefinal 关键字中的多个来修饰一个方法。

正确
class Person {
public:
    virtual String description() { ... };
}

class Student : public Person {
public:
    String description() override { ... }; // This is correct because it only contains the "override" keyword to indicate that the method is overridden.
}

class Person {
public:
    virtual String description() { ... };
}

class Student : public Person {
public:
    String description() final { ... }; // This is correct because it only contains the "final" keyword to indicate that the method is overridden and that no subclasses of "Student" can override "description".
}

错误
class Person {
public:
    virtual String description() { ... };
}

class Student : public Person {
public:
    virtual String description() override { ... }; // This is incorrect because it uses both the "virtual" and "override" keywords to indicate that the method is overridden. Instead, it should only use the "override" keyword.
}
class Person {
public:
    virtual String description() { ... };
}

class Student : public Person {
public:
    virtual String description() final { ... }; // This is incorrect because it uses both the "virtual" and "final" keywords to indicate that the method is overridden and final. Instead, it should only use the "final" keyword.
}
class Person {
public:
    virtual String description() { ... };
}

class Student : public Person {
public:
    virtual String description() { ... }; // This is incorrect because it uses the "virtual" keyword to indicate that the method is overridden.
}

Python

Python 代码使用 PEP8 风格。