This repository has been archived by the owner on Jun 6, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
/
STYLE
169 lines (121 loc) · 6.61 KB
/
STYLE
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
== MCI: Managed Compiler Infrastructure ==
-- Style --
This is a guide to the general code style used in the MCI source base.
If you're contributing code to the project, you should make sure to
follow these. We are rather strict about these rules as we believe that
they help us keep a clean and consistent source base.
++ Indentation ++
We indent with 4 spaces, always, everywhere. Do not use hard tabs.
++ Braces ++
Braces always go on a separate line. Never put them on the same line as
anything else except in lambda bodies. In other words, use Allman style
everywhere.
++ Spacing ++
One place where spacing is very important is in expressions. You should
always put a space on all sides of binary and ternary operators, for example.
Note that this is not necessary for unary operators, including typeof, typeid,
is, assert, and so on.
Spacing is not necessary in function calls either (other than after a comma).
For example, you should do:
* foo();
* foo(bar);
* foo(bar, baz);
* foo!bar(baz);
* foo!(bar, baz)();
Additionally, always put a space after a block statement keyword. That is:
* if (...)
* while (...)
* for (...)
It's recommended to use empty lines between statements that are not closely
related. This helps a lot when skimming code.
++ Naming ++
In general, we follow the naming conventions of Phobos, the standard
library for D:
* Module and package names should be all lower case, and should not
use underscores. All module names must start with 'mci' followed by
the name of the library/executable (for example, 'mci.core.tuple').
* Type names should be PascalCase. We do use C-style naming for some
primitive type aliases, though.
* Function and property names should be camelCase.
* Parameter names and local variable names should be camelCase.
* Public field names should be camelCase, while private field names
should be _camelCase. Note that all fields at module level should be
camelCase regardless of visibility.
* Enum member names should be camelCase.
* Template mixin names should be PascalCase.
* The name of templates that resolve to types should be PascalCase.
Otherwise, they should be camelCase.
* Template parameters that represent types should be PascalCase. All
other template parameters should be camelCase.
You should have a VERY good reason if you're going to deviate from these
naming rules. We want to keep the MCI source base consistent with standard
naming in D so that it feels natural to use it together with Phobos.
++ Comments ++
Comments should be written in clear, concise English with correct grammar
(yes, this includes punctuation). Avoid using abbreviations and acronyms
unless they are generally well-understood in the context. Also, you should
avoid overly verbose comments (often referred to as literate programming),
as they make it harder to read the code. In general, don't comment on the
obvious or on things that are considered common sense.
Bad comments:
* // do some space opts
* // revert to prev tok
Good comments:
* // Do some space optimizations.
* // Revert to the previous token.
++ Documentation ++
Every externally accessible element in the API surface should have a Ddoc
documentation comment. Since we generate documentation for the API, this
is important so that users of the libraries can easily look up a function
or type in the documentation, rather than having to read the source.
++ Contracts ++
One of the most important goals of the MCI is to provide a clear and easy
to use API surface. Therefore, we make heavy use of D's support for design
by contract. In general, ALL public entry points to the API should contain
contracts. It also helps to use them internally.
In other words: Spam in/out contracts, invariants, and asserts everywhere
as you see fit. Better safe than sorry. Keep in mind that these checks are
removed from release builds, so performance is not extremely important.
That being said, try not to assert the obvious. Too many redundant asserts
can also clutter code. Strike a right balance.
If a method implements an abstract method in a class or a method in an
interface, don't copy the contracts from those into the implementation.
Contracts belong to the declaring type, not the implementing type.
++ Storage ++
One problem with global state is that it may be hard to decide whether it
goes into thread-local or global storage. In general, go by the rule that
if you cannot come up with a reason to put a variable in global storage,
it should probably be in TLS.
++ Visiblity Modifiers ++
Use visibility modifiers on all declarations except imports (which are private
by default). It is better to be explicit about visibility so that mistakes
such as accidentally exposing private code won't happen.
++ Const/Immutable/Shared ++
You should generally avoid these type modifiers.
There are lots of problems with shared, in particular. It only ensures safety
on a low level, which is almost always useless in the MCI. Apart from that,
it is a highly viral type modifier which tends to make it extremely annoying
to work with the API. A cast can remove it, but that goes against the entire
safety it's supposed to offer. So, avoid this modifier by all means.
The const and immutable modifiers are much less problematic, but unfortunately
are not implemented correctly and without bugs in any current compilers. For
this reason, we generally avoid them as well. We do plan to make more use of
them once compilers get less broken support for them.
++ Scope Modifier ++
Using the scope modifier on delegate parameters when possible is, in general,
good practice. It helps ensure that delegates aren't unnecessarily allocated
in the GC heap when they aren't escaped from the function using them.
++ Initialization Types ++
When initializing fields or global variables, it is generally preferable to
use typeof:
* foo = new typeof(foo)(); // For classes.
* bar = typeof(bar)(); // For structs.
This usually results in less typing and makes it easier to refactor code.
This is, of course, only usable when the field or variable is not declared as
an interface or abstract class, so in those cases, it is fine to use an
explicit type.
++ Synchronization ++
Avoid using the synchronized statement. Not only does it have heavy overhead
due to having to allocate a monitor if not already done, but it also results
in virtual calls and in some cases memory leaks (due to bugs in the runtime).
Instead, use the synchronization classes from the mci.core.sync module.