forked from mirrors/linux
		
	Documentation: rust: discuss #[expect(...)] in the guidelines
				
					
				
			Discuss `#[expect(...)]` in the Lints sections of the coding guidelines document, which is an upcoming feature in Rust 1.81.0, and explain that it is generally to be preferred over `allow` unless there is a reason not to use it (e.g. conditional compilation being involved). Tested-by: Gary Guo <gary@garyguo.net> Reviewed-by: Gary Guo <gary@garyguo.net> Link: https://lore.kernel.org/r/20240904204347.168520-19-ojeda@kernel.org Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
This commit is contained in:
		
							parent
							
								
									1f9ed17254
								
							
						
					
					
						commit
						04866494e9
					
				
					 1 changed files with 110 additions and 0 deletions
				
			
		|  | @ -262,6 +262,116 @@ default (i.e. outside ``W=`` levels). In particular, those that may have some | |||
| false positives but that are otherwise quite useful to keep enabled to catch | ||||
| potential mistakes. | ||||
| 
 | ||||
| On top of that, Rust provides the ``expect`` attribute which takes this further. | ||||
| It makes the compiler warn if the warning was not produced. For instance, the | ||||
| following will ensure that, when ``f()`` is called somewhere, we will have to | ||||
| remove the attribute: | ||||
| 
 | ||||
| .. code-block:: rust | ||||
| 
 | ||||
| 	#[expect(dead_code)] | ||||
| 	fn f() {} | ||||
| 
 | ||||
| If we do not, we get a warning from the compiler:: | ||||
| 
 | ||||
| 	warning: this lint expectation is unfulfilled | ||||
| 	 --> x.rs:3:10 | ||||
| 	  | | ||||
| 	3 | #[expect(dead_code)] | ||||
| 	  |          ^^^^^^^^^ | ||||
| 	  | | ||||
| 	  = note: `#[warn(unfulfilled_lint_expectations)]` on by default | ||||
| 
 | ||||
| This means that ``expect``\ s do not get forgotten when they are not needed, which | ||||
| may happen in several situations, e.g.: | ||||
| 
 | ||||
| - Temporary attributes added while developing. | ||||
| 
 | ||||
| - Improvements in lints in the compiler, Clippy or custom tools which may | ||||
|   remove a false positive. | ||||
| 
 | ||||
| - When the lint is not needed anymore because it was expected that it would be | ||||
|   removed at some point, such as the ``dead_code`` example above. | ||||
| 
 | ||||
| It also increases the visibility of the remaining ``allow``\ s and reduces the | ||||
| chance of misapplying one. | ||||
| 
 | ||||
| Thus prefer ``except`` over ``allow`` unless: | ||||
| 
 | ||||
| - The lint attribute is intended to be temporary, e.g. while developing. | ||||
| 
 | ||||
| - Conditional compilation triggers the warning in some cases but not others. | ||||
| 
 | ||||
|   If there are only a few cases where the warning triggers (or does not | ||||
|   trigger) compared to the total number of cases, then one may consider using | ||||
|   a conditional ``expect`` (i.e. ``cfg_attr(..., expect(...))``). Otherwise, | ||||
|   it is likely simpler to just use ``allow``. | ||||
| 
 | ||||
| - Inside macros, when the different invocations may create expanded code that | ||||
|   triggers the warning in some cases but not in others. | ||||
| 
 | ||||
| - When code may trigger a warning for some architectures but not others, such | ||||
|   as an ``as`` cast to a C FFI type. | ||||
| 
 | ||||
| As a more developed example, consider for instance this program: | ||||
| 
 | ||||
| .. code-block:: rust | ||||
| 
 | ||||
| 	fn g() {} | ||||
| 
 | ||||
| 	fn main() { | ||||
| 	    #[cfg(CONFIG_X)] | ||||
| 	    g(); | ||||
| 	} | ||||
| 
 | ||||
| Here, function ``g()`` is dead code if ``CONFIG_X`` is not set. Can we use | ||||
| ``expect`` here? | ||||
| 
 | ||||
| .. code-block:: rust | ||||
| 
 | ||||
| 	#[expect(dead_code)] | ||||
| 	fn g() {} | ||||
| 
 | ||||
| 	fn main() { | ||||
| 	    #[cfg(CONFIG_X)] | ||||
| 	    g(); | ||||
| 	} | ||||
| 
 | ||||
| This would emit a lint if ``CONFIG_X`` is set, since it is not dead code in that | ||||
| configuration. Therefore, in cases like this, we cannot use ``expect`` as-is. | ||||
| 
 | ||||
| A simple possibility is using ``allow``: | ||||
| 
 | ||||
| .. code-block:: rust | ||||
| 
 | ||||
| 	#[allow(dead_code)] | ||||
| 	fn g() {} | ||||
| 
 | ||||
| 	fn main() { | ||||
| 	    #[cfg(CONFIG_X)] | ||||
| 	    g(); | ||||
| 	} | ||||
| 
 | ||||
| An alternative would be using a conditional ``expect``: | ||||
| 
 | ||||
| .. code-block:: rust | ||||
| 
 | ||||
| 	#[cfg_attr(not(CONFIG_X), expect(dead_code))] | ||||
| 	fn g() {} | ||||
| 
 | ||||
| 	fn main() { | ||||
| 	    #[cfg(CONFIG_X)] | ||||
| 	    g(); | ||||
| 	} | ||||
| 
 | ||||
| This would ensure that, if someone introduces another call to ``g()`` somewhere | ||||
| (e.g. unconditionally), then it would be spotted that it is not dead code | ||||
| anymore. However, the ``cfg_attr`` is more complex than a simple ``allow``. | ||||
| 
 | ||||
| Therefore, it is likely that it is not worth using conditional ``expect``\ s when | ||||
| more than one or two configurations are involved or when the lint may be | ||||
| triggered due to non-local changes (such as ``dead_code``). | ||||
| 
 | ||||
| For more information about diagnostics in Rust, please see: | ||||
| 
 | ||||
| 	https://doc.rust-lang.org/stable/reference/attributes/diagnostics.html | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Miguel Ojeda
						Miguel Ojeda