Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ConstructorParamShouldMatchPropNames analyzer #4877

Draft
wants to merge 64 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
3dedbb2
Add ConstructorParamShouldMatchPropNames analyzer
psxvoid Feb 11, 2021
6dc3ef9
Fix - remove unused package reference
psxvoid Feb 24, 2021
6b71577
Fix json-ctor order in well-known-names
psxvoid Feb 24, 2021
52360bb
Fix code alignment in tests
psxvoid Feb 25, 2021
d9308a4
Mark the diagnostic as non-fx-cop-ported
psxvoid Feb 25, 2021
700c4ac
Add missing msbuild autogenerated assets
psxvoid Feb 25, 2021
d3769d6
Fix - remove unused package reference, part 2
psxvoid Feb 25, 2021
5c82803
Update rule's message
psxvoid Feb 25, 2021
43ad9b1
Rollback unrelated analyzer to the original state
psxvoid Feb 25, 2021
41d44b2
Update the description message for the analyzer
psxvoid Feb 26, 2021
e3db946
Fix misplaced comma and update the message
psxvoid Feb 26, 2021
e9bc939
Fix incorrect helper test method name prefix
psxvoid Feb 26, 2021
481f5ed
Update the message title with the recommended one
psxvoid Feb 26, 2021
93c0429
Use net50 references instead netstandard20 in tests
psxvoid Feb 26, 2021
3efee13
Replace IsJsonCtor extension-method with private method
psxvoid Feb 26, 2021
2b49119
Rename the analyzer - add "field" in its name
psxvoid Feb 26, 2021
c2051bf
Remove unused using in tests
psxvoid Feb 26, 2021
e9aa041
Replace banned-API with non-banned in tests
psxvoid Feb 27, 2021
9579d93
Add a code fix provider, partially tested with C#
psxvoid Feb 27, 2021
5a62547
[POC] Verify tuple assignment is working with CA1071
psxvoid Mar 1, 2021
33dccdd
Revert "[POC] Verify tuple assignment is working with CA1071"
psxvoid Mar 1, 2021
57437ba
Add C# tests for classes with fields and props
psxvoid Mar 1, 2021
727423b
Add VB tests for classes with fields and props
psxvoid Mar 1, 2021
6c83c6e
The analyzer - ensure bound properties are public
psxvoid Mar 3, 2021
2482c4c
Fix minor dis-alignment in tests
psxvoid Mar 3, 2021
d4bec8b
Fix tuple assignment reference not detected
psxvoid Mar 4, 2021
9089e0a
Simplify member reference retrieval
psxvoid Mar 4, 2021
5aff394
Merge reference symbol null and static checks
psxvoid Mar 4, 2021
5fba883
Rename local variable in member reference retrival
psxvoid Mar 4, 2021
cd373f6
Add tuple assignment test for records
psxvoid Mar 4, 2021
1fb8c27
Analyzer - group tests for records below tests for classes
psxvoid Mar 7, 2021
da07af5
Analyzer - sort tests for classes
psxvoid Mar 7, 2021
0c3e2a9
Analyzer - ensure bound fields are public
psxvoid Mar 7, 2021
2309cb0
Fix parameter alignment in analyzer tests
psxvoid Mar 7, 2021
7285658
Analyzer - add tests for private fields with JsonInclude
psxvoid Mar 8, 2021
52dce8e
Analyzer - add tests with JsonPropertyName
psxvoid Mar 8, 2021
c603b66
Analyzer - report unreferenced params (no messages)
psxvoid Mar 8, 2021
afb760a
Analyzer - add messages for unreferenced parameters
psxvoid Mar 8, 2021
4d30c8f
Analyzer - free unused parameter set on symbol-end
psxvoid Mar 8, 2021
641721d
Analyzer - sort methods by visibility and logically
psxvoid Mar 8, 2021
65ea1ad
Analyzer - fix reversed words match (should not)
psxvoid Mar 9, 2021
defb3c2
Analyzer - add regions, cleanup and organize tests
psxvoid Mar 9, 2021
d4345d5
Analyzer - simplify return in ShouldAnalyzerMethod
psxvoid Mar 9, 2021
2008be6
Analyzer - simplify supported members matching
psxvoid Mar 9, 2021
c0687c6
Add TODO - referenced members are not static diagnostic
psxvoid Mar 9, 2021
313bdfd
Analyzer - format compilation start action registration
psxvoid Mar 9, 2021
fa0fd41
Analyzer - merge name match logic into a single method
psxvoid Mar 9, 2021
4d4a1ec
Add "key" postfix to diagnostic parameter keys
psxvoid Mar 9, 2021
e059159
Analyzer - remove unused parameter analysis
psxvoid Jun 23, 2021
a95f803
Analyzer - init using scoped context variable
psxvoid Jun 23, 2021
1e62744
Analyzer - fix build: invalid whitespace in tests
psxvoid Jun 23, 2021
1e1d382
Analyzer - inline ExportFixProvider and Shared attributes
psxvoid Jun 28, 2021
9a727e4
Revert "Analyzer - inline ExportFixProvider and Shared attributes"
psxvoid Jun 28, 2021
8dacfcb
Fixer - inline ExportFixProvider and Shared attributes (fixed)
psxvoid Jun 28, 2021
ab492ca
Analyzer - use single diagnostic rule per analyzer
psxvoid Jun 28, 2021
b1deb98
CodeFix - make bound field public (no message, not aligned)
psxvoid Jul 8, 2021
2008bfe
Analyzer - remove unreferenced parameter diagnostic reason
psxvoid Jul 8, 2021
194c3a2
Analyzer - fix broken tests due to additional diagnostic location
psxvoid Jul 13, 2021
a487621
CodeFix - add tests for single private property
psxvoid Jul 13, 2021
f38390d
CodeFix - add tests for multiple private fields and props
psxvoid Jul 13, 2021
051d067
Analyzer - use "Add" instead of "SetItem"
psxvoid Jul 27, 2021
f86bf43
Analyzer - init jsonConstructorAttribute symbol using out var
psxvoid Jul 27, 2021
35b2346
Analyzer - Use ContainingType.Name instead of ToDisplayString
psxvoid Jul 27, 2021
aafe0de
Analyzer - Remove parentheses from lambda param while passing context
psxvoid Jul 27, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
; Please do not edit this file manually, it should only be updated through code fix application.
### New Rules
Rule ID | Category | Severity | Notes
--------|----------|----------|-------
CA1071 | Design | Warning | ConstructorParametersShouldMatchPropertyNamesAnalyzer, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1071)
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ private bool ShouldAnalyzeMethod(
}

// Ignore primary constructor (body-less) of positional records.
// TODO: I have to handle a similar situation
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To Do: remove

if (IsPositionalRecordPrimaryConstructor(method))
{
return false;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
<!--
Microsoft ResX Schema

Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes

The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.

Example:

... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
Expand All @@ -26,36 +26,36 @@
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple

There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the

Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not

The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can

Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.

mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.

mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.

mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
Expand Down Expand Up @@ -1519,4 +1519,13 @@
<value>and all other platforms</value>
<comment>This call site is reachable on: 'windows' 10.0.2000 and later, and all other platforms</comment>
</data>
<data name="ConstructorParameterShouldMatchPropertyOrFieldName" xml:space="preserve">
<value>For proper deserialization, change the name of parameter '{1}' in the constructor of '{0}' to match the bound property or field '{2}'</value>
</data>
<data name="ConstructorParametersShouldMatchPropertyOrFieldNamesDescription" xml:space="preserve">
<value>For a constructor having [JsonConstructor] attribute each parameter name must match with a public property or field name for proper deserialization.</value>
</data>
<data name="ConstructorParametersShouldMatchPropertyOrFieldNamesTitle" xml:space="preserve">
<value>Constructor parameter names should match the bound property or field names</value>
</data>
</root>
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Immutable;
using System.Composition;
using System.Globalization;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Rename;
using Microsoft.CodeQuality.Analyzers.ApiDesignGuidelines;
using static Microsoft.NetCore.Analyzers.Runtime.ConstructorParametersShouldMatchPropertyAndFieldNamesAnalyzer;

namespace Microsoft.NetCore.Analyzers.Runtime
{
/// <summary>
/// CA1071: Constructor parameters should match property and field names.
/// Based on <see cref="ParameterNamesShouldMatchBaseDeclarationFixer"/>.
/// </summary>
[ExportCodeFixProvider(LanguageNames.CSharp, LanguageNames.VisualBasic), Shared]
public sealed class ConstructorParametersShouldMatchPropertyAndFieldNamesFixer : CodeFixProvider
{
private static readonly Type DiagnosticReasonEnumType = typeof(ParameterDiagnosticReason);

public override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(RuleId);

public sealed override FixAllProvider GetFixAllProvider()
{
// See https://github.com/dotnet/roslyn/blob/master/docs/analyzers/FixAllProvider.md for more information on Fix All Providers
return WellKnownFixAllProviders.BatchFixer;
}

public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
SyntaxNode syntaxRoot = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
SemanticModel semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false);

foreach (Diagnostic diagnostic in context.Diagnostics)
{
SyntaxNode node = syntaxRoot.FindNode(context.Span);
ISymbol declaredSymbol = semanticModel.GetDeclaredSymbol(node, context.CancellationToken);

if (declaredSymbol.Kind != SymbolKind.Parameter)
{
continue;
}

var diagnosticReason = (ParameterDiagnosticReason)Enum.Parse(DiagnosticReasonEnumType, diagnostic.Properties[DiagnosticReasonKey]);

switch (diagnosticReason)
{
case ParameterDiagnosticReason.NameMismatch:
RegisterParameterRenameCodeFix(context, diagnostic, declaredSymbol);
break;
case ParameterDiagnosticReason.FieldInappropriateVisibility:
case ParameterDiagnosticReason.PropertyInappropriateVisibility:
SyntaxNode fieldOrProperty = syntaxRoot.FindNode(diagnostic.AdditionalLocations[0].SourceSpan);
RegisterMakeFieldOrPropertyPublicCodeFix(context, diagnostic, fieldOrProperty);
break;
default:
throw new InvalidOperationException();
};
}
}

private static void RegisterParameterRenameCodeFix(CodeFixContext context, Diagnostic diagnostic, ISymbol declaredSymbol)
{
// This approach is very naive. Most likely we want to support NamingStyleOptions, available in Roslyn.
string newName = LowerFirstLetter(diagnostic.Properties[ReferencedFieldOrPropertyNameKey]);

context.RegisterCodeFix(
CodeAction.Create(
string.Format(CultureInfo.CurrentCulture, MicrosoftNetCoreAnalyzersResources.ConstructorParametersShouldMatchPropertyOrFieldNamesTitle, newName),
cancellationToken => GetUpdatedDocumentForParameterRenameAsync(context.Document, declaredSymbol, newName, cancellationToken),
nameof(ConstructorParametersShouldMatchPropertyAndFieldNamesFixer)),
diagnostic);
}

private static void RegisterMakeFieldOrPropertyPublicCodeFix(CodeFixContext context, Diagnostic diagnostic, SyntaxNode fieldOrProperty)
{
context.RegisterCodeFix(
CodeAction.Create(
string.Format(CultureInfo.CurrentCulture, MicrosoftNetCoreAnalyzersResources.ConstructorParametersShouldMatchPropertyOrFieldNamesTitle, fieldOrProperty),
cancellationToken => GetUpdatedDocumentForMakingFieldOrPropertyPublicAsync(context.Document, fieldOrProperty, cancellationToken),
nameof(ConstructorParametersShouldMatchPropertyAndFieldNamesFixer)),
diagnostic);
}

private static string LowerFirstLetter(string targetName)
{
return $"{targetName[0].ToString().ToLower(CultureInfo.CurrentCulture)}{targetName[1..]}";
}

private static async Task<Document> GetUpdatedDocumentForParameterRenameAsync(Document document, ISymbol parameter, string newName, CancellationToken cancellationToken)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because it duplicates a method of ParameterNamesShouldMatchBaseDeclarationFixer, it can be extracted to a shared internal helper method. But I'm not completely sure if it follows repository contribution guidelines (do not make changes to unrelated analyzers).

{
Solution newSolution = await Renamer.RenameSymbolAsync(document.Project.Solution, parameter, newName, null, cancellationToken).ConfigureAwait(false);
return newSolution.GetDocument(document.Id)!;
}

private static async Task<Document> GetUpdatedDocumentForMakingFieldOrPropertyPublicAsync(Document document, SyntaxNode fieldOrProperty, CancellationToken cancellationToken)
{
DocumentEditor editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false);

editor.SetAccessibility(fieldOrProperty, Accessibility.Public);

return editor.GetChangedDocument();
}
}
}
Loading