roslyn icon indicating copy to clipboard operation
roslyn copied to clipboard

Add an analyzer to recommend using named parameters when passing literals as arguments

Open DaRosenberg opened this issue 5 years ago • 30 comments

We employ a guideline to always use named parameters when the meaning of an argument is not obvious from the argument itself.

For example, the intent is not very clear here, from just looking at the code:

var employees = await FetchEmployeesAsync(true);

If you're in an IDE, you can of course hover over the method call for a few seconds to get a tooltip, or go to the definition of the method, but it adds friction and delay when reading code. And oftentimes, code is read outside of an IDE (for example, when reviewing a PR or perusing code on GitHub).

But it's much clearer if you do:

var employees = await FetchEmployeesAsync(includeContractors: true);

Or:

var includeContractors = true;
var employees = await FetchEmployeesAsync(includeContractors);

For most cases, this could be formalized as "always use named parameters when passing literal values as arguments in a method call".

It would be nice to have an analyzer for this. Let me know if you think this is a good idea or not. I wouldn't mind taking a stab at implementing it myself, if it doesn't already exist and nobody else is already working on it.

DaRosenberg avatar Mar 02 '20 13:03 DaRosenberg

This seems related to https://github.com/dotnet/roslyn/pull/22010, which was attempted by @jcouv. I think the last comment on the PR https://github.com/dotnet/roslyn/pull/22010#issuecomment-355157341 mentions the issue with this request - the analyzer could end up being extremely demanding. So, I think we would need to implement this with following additions:

  1. Ability to exclude certain methods: Have an .editorconfig option to ignore validation for specific method or property symbol, with reasonable defaults for methods/properties always excluded from this analyzer. For example, Debug.Assert would be a classic case that users don't care about and do not want named argument (maybe we could ignore all methods with a single boolean parameter by default, but that would be too aggressive I think)
  2. Ability to exclude parameter types: Have an editorconfig option to ignore specific parameter types, such as bool, which can come in lot of methods. I am not sure if users would use this option as much as the previous one, so probably this can be implemented later based on feedback.

Tagging @Evangelink in case he is interested in implementing this analyzer. I have always wanted to enable this analyzer for this repo itself :-)

mavasani avatar Mar 02 '20 15:03 mavasani

If I were to do it again today, maybe I would suppress those diagnostics (or just not enable the enforcement) on test projects altogether. Then maybe we don't need additional configuration (excluding certain methods or types).

@mavasani Let me re-open my old PR, then test on Roslyn with above rule (only enforce in product code) to see if that would be workable.

jcouv avatar Mar 02 '20 23:03 jcouv

If the analyzer excluded certain very common cases, that'd be ideal IMO.

Besides any method with "Assert" in its name as already mentioned, also the case ConfigureAwait(false) comes to mind as one that is so common and well-understood that it would make sense to exclude it.

DaRosenberg avatar Mar 02 '20 23:03 DaRosenberg

@vatsalyaagrawal @jinujoseph @sharwell Can we port this issue to Roslyn? This is a code style analyzer proposal.

mavasani avatar May 05 '20 18:05 mavasani

@jcouv Would you like to take this up or would prefer someone from the IDE team to take it up? Thanks!

mavasani avatar May 05 '20 23:05 mavasani

It looks like my earlier PR needed more work, as Jason felt we should convert the existing refactoring into a fixer (so that we don't have duplicate fixer and refactoring). I'll leave this to the IDE team. The tests from that PR could probably be re-used though.

jcouv avatar May 05 '20 23:05 jcouv

Suggestions from @alrz from https://github.com/dotnet/roslyn/pull/45989#discussion_r455133315

My opinion: #43988 is too much noise, even though it's "usually" a good idea to use named args, it really actualy depends on the context where you need to add named args for literals. Hardcoding a list of methods doesn't seem to be a solution neither. But since out _ is actually hiding a piece of info (while saying that this is not being used), named args can be always helpful.

One heuristic for #43988 I think is that only offer when literals are mixed with other kinds of expressions. so Add(1) or ConfigureAwait(false) won't get flagged but M(a, true, b) does.

mavasani avatar Jul 15 '20 15:07 mavasani

Agree that out _ should always be suggested to use named args.

Not quite sure what to think about @alrz suggests though... I guess it would be better than nothing. I just think there would be a lot of missed cases. I would venture to guess that method calls with only one or more literals as args, are a very common occurrence, which would make this analyzer kind of "toothless" from a guideline enforcement standpoint... but if no-one has a better idea, definitely better than nothing.

DaRosenberg avatar Jul 15 '20 22:07 DaRosenberg

Agree that out _ should always be suggested to use named args.

I personally disagree on this. I haven't run into a case ever where i've wanted this, and i'm generally the person on my team asking people to use named params for clarity.

CyrusNajmabadi avatar Jul 16 '20 21:07 CyrusNajmabadi

I don't believe this analyzer is any longer needed given the work recently done by @akhera99 on inline parameter name hints. It is an awesome feature, and kind of makes the named arguments feature itself redundant. I am going to propose that we close this issue as won't fix.

Marking as needs design review to discuss in next design meeting.

mavasani avatar Aug 06 '20 21:08 mavasani

@mavasani That looks awesome indeed.

In fact, at first glance it might seem as if that even negates the whole idea of named parameters as a language construct. But the fact is, a VS editor adornment (while really cool) is not a real substitute and named parameters will of course remain in the language - for good reason.

Before closing this, I'd consider the following:

  • What about when viewing code outside the context of an IDE, such as when reviewing a PR in GitHub or Azure DevOps?
  • Or viewing code in third-party tools like profilers etc?
  • Is this editor feature coming to other C# IDEs as well in the short term (i.e. VS Code and VS for Mac)?

DaRosenberg avatar Aug 06 '20 22:08 DaRosenberg

Design Meeting Notes

The proposed resolution is intentionally deferred, we are going to wait and see what's the adoption feedback is from the user-facing feature (inline parameter name hints) to understand if it's showing up in the right places and if that meets people's needs and to what degree, we do understand that there are certain areas where it can't be used and there will be the clear value of having some analysis for specific cases and If we could find certain rules/patterns which wouldn't be too intrusive to the way projects are maintained over time

jinujoseph avatar Aug 13 '20 07:08 jinujoseph

I had mixed feelings with Inline parameter name hints feature in Resharper, and now I have same mixed feelings with it in VS. While it is very convenient, it encourages to write code which is unreadable outside of IDE.

Therefore, I would really like to have this analyzer implemented anyway, disregarding Inline parameter hints feature.

Dreamescaper avatar Aug 13 '20 09:08 Dreamescaper

Therefore, I would really like to have this analyzer implemented anyway, disregarding Inline parameter hints feature.

Note: analyzers are an open ecosystem. Roslyn has introduced and adopted some for some very contentious areas where there are deep community splits on how people want to write and what they want enforced. This style does not seem to be in the same category. As such, i'm not pushing for roslyn to provide thsi. it would actually be extremely easy and straightforward to just write your own analyzer here and use taht to ensure your own code matched whatever rules you think are sensible in your domain.

CyrusNajmabadi avatar Aug 13 '20 19:08 CyrusNajmabadi

@DaRosenberg may i ask how you implemented this rule? i would like to enforce it in our codebase

ci-vamp avatar Jan 04 '23 22:01 ci-vamp

@ms-vamp I did not implement anything related to this (yet).

DaRosenberg avatar Jan 05 '23 09:01 DaRosenberg

Winforms would absolutely use the rule. It's a part of the code style.

elachlan avatar Jan 05 '23 10:01 elachlan

@ms-vamp I did not implement anything related to this (yet).

i see. i am not as familiar with the linting/formatting ecosystem on the c# side of things. i have been trying to wrap my head around how it works. it looks like some pieces are available but i dont know how to use them.

here is some info i found on my search that may help:

ci-vamp avatar Jan 05 '23 15:01 ci-vamp

@ms-vamp I would recommend reading up on DiagnosticAnalyzer and CodeFixProvider. There are also lots of examples in Roslyn and in the wild. I do not think this is necessarily something we would do ourselves in Roslyn as it likely requires just a lot of customizability on the part of the user. For example, some ability to whitelist certain APIs as not needing named args since the code is so self-evident.

However, if an external team or individual wanted to do this, they could go wild with customization here if they so wanted.

CyrusNajmabadi avatar Jan 05 '23 15:01 CyrusNajmabadi

@CyrusNajmabadi just to double-check here since this issue is tagged as "help needed" — are you saying that if we made the effort to contribute an analyzer and fix for this as a PR, it would still not be accepted into Roslyn?

DaRosenberg avatar Jan 05 '23 16:01 DaRosenberg

@DaRosenberg because of hte complexity here, i would advise not implementing this until a design has been put forth (which you are welcome to do) that the team accepts. If such a design can be made that is simple and clean, then that would greatly increase the odds that we would take this in roslyn. If, however, no such design can be agreed upon (perhaps because there would just need to be far too much customizability here than we're comfortable with), then we would likely not take it.

To give an example, one of our most complex features is use var, which basically only has 3 knobs and which still has a ton of requests to make it even more flexible which we are very resistant to do. I could easily see this feature going far beyond that (including needing some way to whitelist APIs) which would make this very complex and far out of bounds with what we're comfortable with wrt complexity and maintenance costs.

But I could def be wrong here and we'd def consider a proposal.

CyrusNajmabadi avatar Jan 05 '23 16:01 CyrusNajmabadi

@CyrusNajmabadi I might give it a think actually. Is there any specific format or place in which a design proposal should be put forth?

DaRosenberg avatar Jan 05 '23 16:01 DaRosenberg

This issue would be the right place. THere's no specific format, but i would be looking for a fairly detailed listing of how users would configure this in editorconfig and hte sorts of cases where a blanket rule would likely be quite poor for. As mentioned above, there are tons of apis like Debug.Assert, ConfigureAwait (and many more) that this feature would likely be a poor fit for. Currently skeptical about a suitably flexible, while simple, solution here. But would def be up to hearing ideas.

CyrusNajmabadi avatar Jan 05 '23 16:01 CyrusNajmabadi

@ms-vamp I would recommend reading up on DiagnosticAnalyzer and CodeFixProvider. There are also lots of examples in Roslyn and in the wild. I do not think this is necessarily something we would do ourselves in Roslyn as it likely requires just a lot of customizability on the part of the user. For example, some ability to whitelist certain APIs as not needing named args since the code is so self-evident.

However, if an external team or individual wanted to do this, they could go wild with customization here if they so wanted.

pardon my ignorance but it has been hard to understand all the components of linting/formatting on the c# side of things (coming from eslint/prettier).

in general i would like to make use of existing roslyn code refactor providers (such as the one i linked as well as line wrapping for methods and chained calls). i see that they are used in VS refactoring suggestions but i would like to execute across the entire solution and then enforce it with rules.

is there information about how to integrate these features into an editorconfig that dotnet format can utilize to operate across an entire solution?

ci-vamp avatar Jan 05 '23 16:01 ci-vamp

but i would like to execute across the entire solution and then enforce it with rules.

That's what a DiagnosticAnalyzer/CodeFixProvider gets you. :-)

Code refactorings are targeted, user-invoked, code transformations. They have no rules or enforcement associated with them.

In a case like this, we would likely extract out common helper code, then use that code from both the refactoring and the analyzer/fixer pair.

CyrusNajmabadi avatar Jan 05 '23 17:01 CyrusNajmabadi

Winforms is specifically looking for a DiagnosticAnalyzer/CodeFixProvider that detects a null value passed to a parameter and changes it to have a named parameter.

elachlan avatar Jan 05 '23 21:01 elachlan

I would definitely like an editorconfig rule that can encourage people to use named literals or variables for false/true and perhaps also null/default (though these are perhaps more prone to annoyance). I strongly dislike IDE annotations because they disrupt spacing alignment and don't work in external contexts such as github code reviews. I'm less concerned with other literal values; they're usually a lot more self-evident (and in particular I would generally oppose doing it for string literals).

Perhaps to reduce the potential annoyance a little, there could be an opt-out attribute on the target parameter (or method as a whole), to handle the "sufficiently obvious" cases. Though this will only be effective once the attributes disperse into library implementations (or for tools like ReSharper where such attributes can be applied via external configuration).

uecasm avatar Feb 27 '23 00:02 uecasm

Maybe a bit late but I just released one that analyzes and fixes named arguments as a nuget package : https://www.nuget.org/packages/Posseth.NamedArguments.AnalyzerAndFixer you can find the source here: https://github.com/MPCoreDeveloper/Posseth.NamedArguments.AnalyzerAndFixer

MPCoreDeveloper avatar Sep 04 '24 11:09 MPCoreDeveloper

Just putting my two cents in there: I would prefer an .editorconfig solution where this could be enabled like: boolean_parameters_must_be_named or something

also a bit of discussion here about it: clean-code-tip-avoid-booleans-as-function-activity

I don't like the suggestion of using an enum in general (for only two options, it has it's own issues with default initialization to 0 and such) and you have to do extra boilerplate code then.

JimWolff avatar Mar 15 '25 13:03 JimWolff

Just putting my two cents in there: I would prefer an .editorconfig solution where this could be enabled like: boolean_parameters_must_be_named or something

also a bit of discussion here about it: clean-code-tip-avoid-booleans-as-function-activity

I don't like the suggestion of using an enum in general (for only two options, it has it's own issues with default initialization to 0 and such) and you have to do extra boilerplate code then.

I just do everything with an exclusion list and options that can enable/ disable or just ad your own namespaces or method names in the latest version.

MPCoreDeveloper avatar Jun 13 '25 15:06 MPCoreDeveloper