This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.
When you submit a pull request, a CLA bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA.
This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact [email protected] with any additional questions or comments.
-
Every source file must start with a copyright header:
- Rust:
// Copyright (c) Microsoft. All rights reserved.
- C:
/* Copyright (c) Microsoft. All rights reserved. */
This is enforced by
make test
. - Rust:
-
Rust code must be formatted with
rustfmt
This is enforced by
make test-release
.Make sure to run
make test-release
before pushing to your remote so that you run all the checks that CI would run, asmake test
does not enforce rustfmt! -
Every crate root must enable the major lint groups and warnings-as-errors:
#![deny(rust_2018_idioms)] #![warn(clippy::all, clippy::pedantic)]
This is enforced by
make test
.A crate root is any file that is the entrypoint of a crate. This includes
lib.rs
,main.rs
,build.rs
(build scripts),tests/*.rs
(integration tests) andexamples/*.rs
(examples).To
#[deny]
a lint or lint group means that any diagnostics raised by that lint or lint group will be treated as errors and fail the build. Conversely, to#[allow]
a lint or lint group means that the diagnostics raised by that lint or lint group will be suppressed.-
rust_2018_idioms
: This lint group fires for code that could be written in a better style in Edition 2018. For example, it enforces thatextern crate
statements are not used, and that trait objects are written likedyn Trait
instead of justTrait
. -
clippy::all
: This lint group contains the mostly-uncontroversial clippy lints. -
clippy::pedantic
: This lint group contains more subjective clippy lints. While some of these lints are overly pedantic and okay toallow
(see below), there are some lints in this group that are useful, so we prefer towarn
this group by default.
When running in CI, the
make test-release
target is used, and all warnings are treated as errors.In general, if any of the above lint groups raises an error/warning, it should be fixed by modifying the code to satisfy the lint. However, there are some lints that it is acceptable to
allow
. The list below enumerates these lints. Note that it is okay toallow
these lints at the crate level with#![allow(...)]
, rather than at the smallest scope where the lint was raised with#[allow(...)]
, unless indicated otherwise.-
clippy::default_trait_access
: This lint fires for code that usesDefault::default()
and suggests usingConcreteType::default()
. However, in most cases, there is no benefit to naming theConcreteType
explicitly because it doesn't matter which type's default is being used, and it can create more noise from having touse path::to::ConcreteType
than to leave it unnamed. -
clippy::doc_markdown
: This lint looks for doc comments that contain things that look like idents or URLs, and suggests wrapping them in code fences and angle brackets respectively. Unfortunately it sometimes fires for words that look like idents but aren't, like "IoT" due to its use of mixed case. In such a case, it is okay toallow
the lint with a#[allow]
attached to the item whose doc comment raised the lint.Note that when the doc comment that raised the lint is a crate-level inner doc comment, there is unfortunately no way to suppress the lint for that doc comment other than by adding a crate-level
#![allow]
attribute for it, which also disables the lint for every other doc comment in the crate. -
clippy::let_and_return
: This lint fires for code that looks like{ ...; let ident = expr; ident }
and suggests writing it as{ ...; expr }
. However, the former style has its advantages: it makes backtraces and single-step debugging clearer by separating the evaluation ofexpr
from the line where it's returned, and also allows for easily adding logging or other side effects between those two steps. -
clippy::let_underscore_drop
: This lint fires for code that looks likelet _ = expr;
whereexpr
is of a type that implsDrop
. The lint believes the author might mistakenly believe that the value will remain alive for the rest of the scope instead of being dropped, since there is precedent for such code with mutex guards. It suggests changing it tolet _foo = expr;
ordrop(expr);
depending on which meaning is meant. The mutex guard mistake only happens if the mutex doesn't hold the data that the mutex is being locked for, which is mostly not the case in our code. For cases of the former type, fixing the lint is noisier than leaving it be, since the intent of the original code is clear already. -
clippy::let_unit_value
: This lint fires for code that looks likelet () = expr;
and suggests writing it asexpr;
. However, whenexpr
is a complicated expression like a function call, the former style is useful to document that the caller is not discarding some result ofexpr
that it ought to have considered, because there is no result to discard. It serves a similar purpose to what the#[must_use]
attribute does in standard Rust, except that it works for types and functions that don't have#[must_use]
. For example, it is useful to assert that the code inlet () = std::io::Read::read_exact(...)?;
is not ignoring any result fromRead::read_exact
(other than thestd::io::Error
already handled by?
). -
clippy::missing_errors_doc
: This lint fires forpub
functions that returnResult<_, _>
and whose doc comment doesn't have an# Errors
section. Such a section would be used to document all the ways the function can fail and what kind of error it would return in each of those cases. Unfortunately, such comments have a tendency of getting out of sync with the actual implementation, and so are either detailed but wrong, or overly simplistic (egfn foo()
's doc just says "Returns an error if the foo operation fails."). In either case, such a doc becomes useless. -
clippy::missing_panics_doc
: This lint fires forpub
functions that might panic from calling.unwrap()
or.expect()
and whose doc comment doesn't have a# Panics
section. We only use.unwrap()
or.expect()
for things that are guaranteed to not fail, such as parsing hard-coded constants into non-const types. Furthermore, this lint has the same problem asclippy::missing_errors_doc
vis-a-vis becoming out of sync with the actual code. -
clippy::must_use_candidate
: This lint is broken. It fires for mostpub
functions that have a non-()
result and wants them to be marked with the#[must_use]
attribute. This is not necessary in most cases since it's obvious that the function is being used to compute some value, so the caller has an interest in the function's result. Adding this attribute to satisfy the lint would be very noisy and not actually help that much. -
clippy::shadow_unrelated
: This lint fires when a binding shadows another binding but has a different type, and recommends renaming the second binding. However, shadowing is an important feature of Rust and it's often cleaner to take advantage of it.Consider a binding named
url
that starts off as a&str
, and is then parsed into aurl::Url
. It is useful to be able to assign the parsed value to a binding namedurl
rather than some other name, both because that is what it is, and because the previous unparsed&str
is no longer needed now that it has been parsed into aurl::Url
. -
clippy::similar_names
: This lint fires for a pair of idents that the lint's heuristic determines are named too similarly, and thus the programmer could easily confuse one with the other. It tends to fire for short idents likereq
andres
. However, using such short idents instead of full words likerequest
andresponse
is fairly common in Rust code. For example, iterators are usually callediter
, callbacks with an obvious usage are usually calledf
, and so on. -
clippy::struct_excessive_bools
: This lint fires for a struct that has more than threebool
fields, because it assumes the struct is representing a state machine where only onebool
will betrue
at any time, and wants anenum
to be used instead. This is overly presumptuous of it. -
clippy::too_many_lines
: This lint fires when a function has more lines of code than its heuristic thinks is acceptable. However this is subjective. Sometimes a function does one thing and needs many lines to do it, and it would not be suitable to split it into multiple smaller functions that are only called from the single parent caller. -
clippy::type_complexity
: This lint fires when a type has more characters than its heuristic thinks is acceptable. This is subjective just likeclippy::too_many_lines
. It is not necessarily appropriate to make new type aliases for parts of the original type if they're only going to be used once. -
clippy::use_self
: This lint fires for code that uses the name of the type instead ofSelf
whereSelf
would be valid, ie within impl blocks for the type. While it is true that usingSelf
is preferred by the community when it comes to writing function signature (egfn new() -> Self
), the ability to useSelf
for struct and enum literals is a relatively newer feature and is less commonly used (egSelf { foo: bar }
for structs andSelf::Foo { bar: baz }
for enums).Unfortunately,
allow
ing this lint also disables it when a function signature doesn't useSelf
.
-
- Consider opening a terminal and running
watch -c -- make -s test
in it. This lets you automatically see the result of compiling your code and runnings tests and lint checks on it as you're writing it.