Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/mykolav/must-use-ret-val-fs
Must use return value: a Roslyn code analyzer and code-fix provider for C#
https://github.com/mykolav/must-use-ret-val-fs
fsharp functional-programming nodiscard return-value roslyn roslyn-analyzer
Last synced: 9 days ago
JSON representation
Must use return value: a Roslyn code analyzer and code-fix provider for C#
- Host: GitHub
- URL: https://github.com/mykolav/must-use-ret-val-fs
- Owner: mykolav
- License: mit
- Created: 2018-10-05T18:25:04.000Z (about 6 years ago)
- Default Branch: master
- Last Pushed: 2024-01-24T01:22:03.000Z (11 months ago)
- Last Synced: 2024-11-20T00:05:57.467Z (about 1 month ago)
- Topics: fsharp, functional-programming, nodiscard, return-value, roslyn, roslyn-analyzer
- Language: F#
- Homepage:
- Size: 4.16 MB
- Stars: 8
- Watchers: 3
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Emit a diagnostic message if a method's return value is ignored
[![Build status](https://ci.appveyor.com/api/projects/status/nqx9jyt0q2hlep98?svg=true)](https://ci.appveyor.com/project/mykolav/must-use-ret-val-fs)
This project contains a Roslyn code analyzer that lets you make sure a method's return value is not silently ignored/discarded.
![The MustUseRetVal analyzer in action](./must-use-ret-val-demo.gif)
## How to use it?
Install the [nuget package](https://www.nuget.org/packages/MustUseRetVal).
Introduce a `MustUseReturnValueAttribute` attribute to your solution. In other words, place the following C# code in an appropriate spot in your solution.
```csharp
[AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Struct)]
class MustUseReturnValueAttribute : Attribute { }
```Or use an existing `MustUseReturnValueAttribute`. For example, from [JetBrains.Annotations](https://www.nuget.org/packages/JetBrains.Annotations).
If you decide to go with `JetBrains.Annotations`, make sure to define the [`JETBRAINS_ANNOTATIONS`](https://blog.jetbrains.com/dotnet/2015/08/12/how-to-use-jetbrains-annotations-to-improve-resharper-inspections/) symbol — so that the `MustUseReturnValue` attribute is compiled into the resulting assembly.
Apply the `[MustUseReturnValue]` attribute to the methods that must not have their return values silently ignored.
```csharp
[MustUseReturnValue]
public bool IsGrantedDatabaseWritePermission() {
// ...
}// Elsewhere in your code:
// The value returned from `IsGrantedDatabaseWritePermission` must be
// assigned to a variable or checked in an if statement, etc.
// Otherwise, the analyzer will emit an error.
var isGranted = IsGrantedDatabaseWritePermission();
if (isGranted)
WriteToDatabase();
```### Supported method kinds
The analyzer supports the following method kinds
- Regular instance and static methods
- Extension methods
- Regular constructors
- Primary constructorsTo mark a record's primary constructor, apply `[MustUseReturnValue]` to the record itself.
```csharp
[MustUseReturnValue]
record Character(string Name, int PowerLevel) {}[MustUseReturnValue]
record struct CharacterStruct(string Name, int PowerLevel) {}// Elsewhere in your code:
// if the object created by primary constructor of `Character` or `CharacterStruct` is discarded,
// the analyzer will emit an error.
var character = new Character("Goku", 9001);
var characterStruct = new CharacterStruct("Goku", 9001);
```Please note, in the code above the attribute only applies to the primary constructors of the records. If a record has additional constructors, you can mark them with this attribute individually in a usual way.
## Download and install
Install the [MustUseRetVal](https://www.nuget.org/packages/MustUseRetVal) nuget package.
For example, run the following command in the [NuGet Package Manager Console](https://docs.microsoft.com/en-us/nuget/tools/package-manager-console).```powershell
Install-Package MustUseRetVal
```This will download all the binaries, and add necessary analyzer references to your project.
## Configuration
Starting in Visual Studio 2019 version 16.3, you can [configure the severity of analyzer rules, or diagnostics](https://learn.microsoft.com/en-us/visualstudio/code-quality/use-roslyn-analyzers?view=vs-2022#configure-severity-levels), in an EditorConfig file, from the light bulb menu, and the error list.
You can add the following to the `[*.cs]` section of your .editorconfig.
```ini
[*.cs]
dotnet_diagnostic.MustUseReturnValue.severity = warning
```The possible severity values are:
- `error`
- `warning`
- `suggestion`
- `silent`
- `none`
- `default` (in case of this analyzer, it's equal to `error`)Please take a look at [the documentation](https://learn.microsoft.com/en-us/visualstudio/code-quality/use-roslyn-analyzers?view=vs-2022#configure-severity-levels) for a detailed description.
## The finishing problem of fluent interfaces
This analyzer can help mitigate [the finishing problem of fluent interfaces](https://daveaglick.com/posts/method-chaining-fluent-interfaces-and-the-finishing-problem). Quoting the relevant portions from the linked post:
> To illustrate [the finishing problem], consider a logging framework. It might allow some number of chained methods such as `Severity()`, `Source()`, `User()`, `CallSite()`, etc.:
>
> `Log.Message("Oh, noes!").Severity(Severity.Bad).User("jsmith");`
>
> Looks nice, right? The problem here is that the logging framework doesn’t know when to write the log message to the log file.
>
> Do I do it in the `User()` method? What if I don’t use the `User()` method or I put it before the `Severity()` method, then when do I write to the file?
>
> This problem occurs any time you want the entire result of a method chain to take some external action other than manipulating the context of the chain.
>
> [...]
>
> ## Terminating Method
>
> [Addressing the problem described above] requires the introduction of a method that serves to complete the chain and act on it’s final context. For example:
>
> `Log.Message("Oh, noes!").Severity(Severity.Bad).User("jsmith").Write();`
>
> See how we added the `Write()` method there at the end? That `Write()` method takes the chain context, writes it to disk, and doesn’t return anything (effectively stopping the chain).
>
> So why is this so bad? For one, it would be very easy to forget the `Write()` method at the end of the chain. This technique requires the programmer to remember something that **the compiler can’t check** and that wouldn’t be picked up at runtime if they forgot.Lets apply the analyzer to the logging example and see how it helps enforce a call to the terminating method.
```csharp
public class Log
{
// ...
[MustUseReturnValue]
public static Log Message(string message) { return new Log(message); }
[MustUseReturnValue]
public Log Severity(SeverityKind severity) { /* ... */ return this; }
[MustUseReturnValue]
public Log User(string userName) { /* ... */ return this; }
// This method is supposed to be called to indicate a chain of fluent calls is complete.
// Therefore, it does not return anything and is not marked with [MustUseReturnValue].
public void Write() { /* ... */ }
}// Elsewhere in the code:
Log.Message("Oh, noes!").Severity(Severity.Bad).User("jsmith");// As the programmer forgets to call `Write` in the line above,
// the analyzer will emit a compile-time error:
// "The return value of `Log.User` must be used"
```# Thank you!
- [Richard Gibson](https://github.com/Richiban) for [ReturnValueUsageAnalyzer](https://github.com/Richiban/Richiban.Analyzer/tree/master/ReturnValueUsageAnalyzer/ReturnValueUsageAnalyzer) which [MustUseRetVal](https://github.com/mykolav/must-use-ret-val-fs) is based on.
- [John Koerner](https://github.com/johnkoerner) for [Creating a Code Analyzer using F#](https://johnkoerner.com/code-analysis/creating-a-code-analyzer-using-f/)
- [Dustin Campbell](https://github.com/DustinCampbell) for [CSharpEssentials](https://github.com/DustinCampbell/CSharpEssentials)# License
The analyzer and code-fix provider are licensed under the MIT license.