diff --git a/src/Diagnostics/FxCop/System.Runtime.Analyzers/CSharp/CSharpSystemRuntimeAnalyzers.csproj b/src/Diagnostics/FxCop/System.Runtime.Analyzers/CSharp/CSharpSystemRuntimeAnalyzers.csproj index 6a816466266bcf761db61d73777c93bb865ba43c..d838fc4d6d9f8a7b1099f8f100cc0123a667d779 100644 --- a/src/Diagnostics/FxCop/System.Runtime.Analyzers/CSharp/CSharpSystemRuntimeAnalyzers.csproj +++ b/src/Diagnostics/FxCop/System.Runtime.Analyzers/CSharp/CSharpSystemRuntimeAnalyzers.csproj @@ -74,6 +74,7 @@ + diff --git a/src/Diagnostics/FxCop/System.Runtime.Analyzers/CSharp/Design/OverrideMethodsOnComparableTypes.Fixer.cs b/src/Diagnostics/FxCop/System.Runtime.Analyzers/CSharp/Design/OverrideMethodsOnComparableTypes.Fixer.cs new file mode 100644 index 0000000000000000000000000000000000000000..2c632e3a70fe0b9634a00ed14bbff8baf9cfaf3c --- /dev/null +++ b/src/Diagnostics/FxCop/System.Runtime.Analyzers/CSharp/Design/OverrideMethodsOnComparableTypes.Fixer.cs @@ -0,0 +1,51 @@ +using System.Collections.Generic; +using System.Composition; +using System.Diagnostics; +// 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.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace System.Runtime.Analyzers +{ + [ExportCodeFixProvider(LanguageNames.CSharp), Shared] + public class CSharpOverrideMethodsOnComparableTypesFixer : OverrideMethodsOnComparableTypesFixer + { + protected override SyntaxNode GenerateOperatorDeclaration(SyntaxNode returnType, string operatorName, IEnumerable parameters, SyntaxNode notImplementedStatement) + { + Debug.Assert(returnType is TypeSyntax); + + SyntaxToken operatorToken; + switch (operatorName) + { + case WellKnownMemberNames.EqualityOperatorName: + operatorToken = SyntaxFactory.Token(SyntaxKind.EqualsEqualsToken); + break; + case WellKnownMemberNames.InequalityOperatorName: + operatorToken = SyntaxFactory.Token(SyntaxKind.ExclamationEqualsToken); + break; + case WellKnownMemberNames.LessThanOperatorName: + operatorToken = SyntaxFactory.Token(SyntaxKind.LessThanToken); + break; + case WellKnownMemberNames.GreaterThanOperatorName: + operatorToken = SyntaxFactory.Token(SyntaxKind.GreaterThanToken); + break; + default: + return null; + } + + return SyntaxFactory.OperatorDeclaration( + default(SyntaxList), + SyntaxFactory.TokenList(new[] { SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword) }), + (TypeSyntax)returnType, + SyntaxFactory.Token(SyntaxKind.OperatorKeyword), + operatorToken, + SyntaxFactory.ParameterList(SyntaxFactory.SeparatedList(parameters.Cast())), + SyntaxFactory.Block((StatementSyntax)notImplementedStatement), + default(SyntaxToken)); + } + } +} diff --git a/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/Design/DefineAccessorsForAttributeArguments.Fixer.cs b/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/Design/DefineAccessorsForAttributeArguments.Fixer.cs index d5b79ea040dfc850c48214d3eb39462841825c66..6979007997b33b7a33fada503ad96ae82fe68a39 100644 --- a/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/Design/DefineAccessorsForAttributeArguments.Fixer.cs +++ b/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/Design/DefineAccessorsForAttributeArguments.Fixer.cs @@ -31,20 +31,20 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) var parameter = generator.GetDeclaration(node, DeclarationKind.Parameter); if (parameter != null) { - context.RegisterCodeFix(new MyCodeAction(SystemRuntimeAnalyzersResources.DefineAccessorsForAttributeArguments, + context.RegisterCodeFix(new MyCodeAction(SystemRuntimeAnalyzersResources.CreatePropertyAccessorForParameter, async ct => await AddAccessor(context.Document, parameter, ct).ConfigureAwait(false)), diagnostic); } return; case DefineAccessorsForAttributeArgumentsAnalyzer.MakePublicCase: - context.RegisterCodeFix(new MyCodeAction(SystemRuntimeAnalyzersResources.DefineAccessorsForAttributeArguments, + context.RegisterCodeFix(new MyCodeAction(SystemRuntimeAnalyzersResources.MakeGetterPublic, async ct => await MakePublic(context.Document, node, ct).ConfigureAwait(false)), diagnostic); return; case DefineAccessorsForAttributeArgumentsAnalyzer.RemoveSetterCase: - context.RegisterCodeFix(new MyCodeAction(SystemRuntimeAnalyzersResources.DefineAccessorsForAttributeArguments, + context.RegisterCodeFix(new MyCodeAction(SystemRuntimeAnalyzersResources.MakeSetterNonPublic, async ct => await RemoveSetter(context.Document, node, ct).ConfigureAwait(false)), diagnostic); return; diff --git a/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/Design/OverrideMethodsOnComparableTypes.Fixer.cs b/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/Design/OverrideMethodsOnComparableTypes.Fixer.cs new file mode 100644 index 0000000000000000000000000000000000000000..b63209ceb280899f12ebd48fe1f4c0a6bdabdb36 --- /dev/null +++ b/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/Design/OverrideMethodsOnComparableTypes.Fixer.cs @@ -0,0 +1,140 @@ +// 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.Collections.Generic; +using System.Collections.Immutable; +using System.Composition; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Editing; + +namespace System.Runtime.Analyzers +{ + public abstract class OverrideMethodsOnComparableTypesFixer : CodeFixProvider + { + public override ImmutableArray FixableDiagnosticIds => ImmutableArray.Create(OverrideMethodsOnComparableTypesAnalyzer.RuleId); + + public override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + var generator = SyntaxGenerator.GetGenerator(context.Document); + var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + + var declaration = root.FindNode(context.Span); + declaration = generator.GetDeclaration(declaration); + if (declaration == null) + { + return; + } + + var model = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); + var typeSymbol = model.GetDeclaredSymbol(declaration) as INamedTypeSymbol; + if (typeSymbol == null) + { + return; + } + + // We cannot have multiple overlapping diagnostics of this id. + var diagnostic = context.Diagnostics.Single(); + + context.RegisterCodeFix(new MyCodeAction(SystemRuntimeAnalyzersResources.ImplementComparable, + async ct => await ImplementComparable(context.Document, declaration, typeSymbol, ct).ConfigureAwait(false)), + diagnostic); + } + + protected abstract SyntaxNode GenerateOperatorDeclaration(SyntaxNode returnType, string operatorName, IEnumerable parameters, SyntaxNode notImplementedStatement); + + private async Task ImplementComparable(Document document, SyntaxNode declaration, INamedTypeSymbol typeSymbol, CancellationToken cancellationToken) + { + DocumentEditor editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); + var generator = editor.Generator; + + var throwStatement = generator.ThrowStatement(generator.ObjectCreationExpression(generator.DottedName("System.NotImplementedException"))); + + if (!OverrideMethodsOnComparableTypesAnalyzer.DoesOverrideEquals(typeSymbol)) + { + var equalsMethod = generator.MethodDeclaration(WellKnownMemberNames.ObjectEquals, + new[] { generator.ParameterDeclaration("obj", generator.TypeExpression(SpecialType.System_Object)) }, + returnType: generator.TypeExpression(SpecialType.System_Boolean), + accessibility: Accessibility.Public, + modifiers: DeclarationModifiers.Override, + statements: new[] { generator.ThrowStatement(generator.ObjectCreationExpression(generator.DottedName("System.NotImplementedException")))}); + editor.AddMember(declaration, equalsMethod); + } + + if (!OverrideMethodsOnComparableTypesAnalyzer.DoesOverrideGetHashCode(typeSymbol)) + { + var getHashCodeMethod = generator.MethodDeclaration(WellKnownMemberNames.ObjectGetHashCode, + returnType: generator.TypeExpression(SpecialType.System_Int32), + accessibility: Accessibility.Public, + modifiers: DeclarationModifiers.Override, + statements: new[] { generator.ThrowStatement(generator.ObjectCreationExpression(generator.DottedName("System.NotImplementedException"))) }); + editor.AddMember(declaration, getHashCodeMethod); + } + + if (!OverrideMethodsOnComparableTypesAnalyzer.IsOperatorImplemented(typeSymbol, WellKnownMemberNames.EqualityOperatorName)) + { + var equalityOperator = GenerateOperatorDeclaration(generator.TypeExpression(SpecialType.System_Boolean), + WellKnownMemberNames.EqualityOperatorName, + new[] + { + generator.ParameterDeclaration("left", generator.TypeExpression(typeSymbol)), + generator.ParameterDeclaration("right", generator.TypeExpression(typeSymbol)), + }, + generator.ThrowStatement(generator.ObjectCreationExpression(generator.DottedName("System.NotImplementedException")))); + editor.AddMember(declaration, equalityOperator); + } + + if (!OverrideMethodsOnComparableTypesAnalyzer.IsOperatorImplemented(typeSymbol, WellKnownMemberNames.InequalityOperatorName)) + { + var inequalityOperator = GenerateOperatorDeclaration(generator.TypeExpression(SpecialType.System_Boolean), + WellKnownMemberNames.InequalityOperatorName, + new[] + { + generator.ParameterDeclaration("left", generator.TypeExpression(typeSymbol)), + generator.ParameterDeclaration("right", generator.TypeExpression(typeSymbol)), + }, + generator.ThrowStatement(generator.ObjectCreationExpression(generator.DottedName("System.NotImplementedException")))); + editor.AddMember(declaration, inequalityOperator); + } + + if (!OverrideMethodsOnComparableTypesAnalyzer.IsOperatorImplemented(typeSymbol, WellKnownMemberNames.LessThanOperatorName)) + { + var lessThanOperator = GenerateOperatorDeclaration(generator.TypeExpression(SpecialType.System_Boolean), + WellKnownMemberNames.LessThanOperatorName, + new[] + { + generator.ParameterDeclaration("left", generator.TypeExpression(typeSymbol)), + generator.ParameterDeclaration("right", generator.TypeExpression(typeSymbol)), + }, + generator.ThrowStatement(generator.ObjectCreationExpression(generator.DottedName("System.NotImplementedException")))); + editor.AddMember(declaration, lessThanOperator); + } + + if (!OverrideMethodsOnComparableTypesAnalyzer.IsOperatorImplemented(typeSymbol, WellKnownMemberNames.GreaterThanOperatorName)) + { + var greaterThanOperator = GenerateOperatorDeclaration(generator.TypeExpression(SpecialType.System_Boolean), + WellKnownMemberNames.GreaterThanOperatorName, + new[] + { + generator.ParameterDeclaration("left", generator.TypeExpression(typeSymbol)), + generator.ParameterDeclaration("right", generator.TypeExpression(typeSymbol)), + }, + generator.ThrowStatement(generator.ObjectCreationExpression(generator.DottedName("System.NotImplementedException")))); + editor.AddMember(declaration, greaterThanOperator); + } + + return editor.GetChangedDocument(); + } + + private class MyCodeAction : DocumentChangeAction + { + public MyCodeAction(string title, Func> createChangedDocument) + : base(title, createChangedDocument) + { + } + } + } +} diff --git a/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/Design/OverrideMethodsOnComparableTypes.cs b/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/Design/OverrideMethodsOnComparableTypes.cs index e3d5c1d3195b2755f507363389a4183d476d38b8..5dd70dd7db522b05102c885b01f43c0ec3be9376 100644 --- a/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/Design/OverrideMethodsOnComparableTypes.cs +++ b/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/Design/OverrideMethodsOnComparableTypes.cs @@ -2,12 +2,17 @@ using System.Collections.Immutable; using System.Linq; -using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; namespace System.Runtime.Analyzers { + /// + /// CA1036: A public or protected type implements the System.IComparable interface and + /// does not override Object.Equals or does not overload the language-specific operator + /// for equality, inequality, less than, or greater than. The rule does not report a + /// violation if the type inherits only an implementation of the interface. + /// [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public sealed class OverrideMethodsOnComparableTypesAnalyzer : DiagnosticAnalyzer { @@ -66,25 +71,33 @@ private static void AnalyzeSymbol(INamedTypeSymbol namedTypeSymbol, INamedTypeSy } } - private static bool DoesOverrideEquals(INamedTypeSymbol symbol) + internal static bool DoesOverrideEquals(INamedTypeSymbol symbol) { // Does the symbol override Object.Equals? return symbol.GetMembers(WellKnownMemberNames.ObjectEquals).OfType().Where(m => IsEqualsOverride(m)).Any(); } - // Rule: A public or protected type implements the System.IComparable interface and - // does not override Object.Equals or does not overload the language-specific operator - // for equality, inequality, less than, or greater than. The rule does not report a - // violation if the type inherits only an implementation of the interface. private static bool IsEqualsOverride(IMethodSymbol method) { - // TODO: reimplement using OverriddenMethods, possibly exposing that property if needed return method.IsOverride && method.ReturnType.SpecialType == SpecialType.System_Boolean && method.Parameters.Length == 1 && method.Parameters[0].Type.SpecialType == SpecialType.System_Object; } + internal static bool DoesOverrideGetHashCode(INamedTypeSymbol symbol) + { + // Does the symbol override Object.GetHashCode? + return symbol.GetMembers(WellKnownMemberNames.ObjectGetHashCode).OfType().Where(m => IsGetHashCodeOverride(m)).Any(); + } + + private static bool IsGetHashCodeOverride(IMethodSymbol method) + { + return method.IsOverride && + method.ReturnType.SpecialType == SpecialType.System_Int32 && + method.Parameters.Length == 0; + } + private static bool IsEqualityOperatorImplemented(INamedTypeSymbol symbol) { // Does the symbol overload all of the equality operators? (All are required per http://msdn.microsoft.com/en-us/library/ms182163.aspx example.) @@ -94,7 +107,7 @@ private static bool IsEqualityOperatorImplemented(INamedTypeSymbol symbol) IsOperatorImplemented(symbol, WellKnownMemberNames.GreaterThanOperatorName); } - private static bool IsOperatorImplemented(INamedTypeSymbol symbol, string op) + internal static bool IsOperatorImplemented(INamedTypeSymbol symbol, string op) { // TODO: should this filter on the right-hand-side operator type? return symbol.GetMembers(op).OfType().Where(m => m.MethodKind == MethodKind.UserDefinedOperator).Any(); diff --git a/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/SystemRuntimeAnalyzers.csproj b/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/SystemRuntimeAnalyzers.csproj index a92bdd0f1b2a8682af26bd67bc73daa1e16f235b..b0e1d8e83f4de1a91ec71b77889108b437e7efc2 100644 --- a/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/SystemRuntimeAnalyzers.csproj +++ b/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/SystemRuntimeAnalyzers.csproj @@ -57,6 +57,7 @@ + diff --git a/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/SystemRuntimeAnalyzersResources.Designer.cs b/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/SystemRuntimeAnalyzersResources.Designer.cs index a103026ae59fc4b11b5100df86115a931d592574..420000e1c416794cf5b449a97fa085dab7f0be99 100644 --- a/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/SystemRuntimeAnalyzersResources.Designer.cs +++ b/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/SystemRuntimeAnalyzersResources.Designer.cs @@ -124,6 +124,15 @@ internal class SystemRuntimeAnalyzersResources { } } + /// + /// Looks up a localized string similar to Create a property accessor.. + /// + internal static string CreatePropertyAccessorForParameter { + get { + return ResourceManager.GetString("CreatePropertyAccessorForParameter", resourceCulture); + } + } + /// /// Looks up a localized string similar to Custom attributes should have AttributeUsage attribute defined.. /// @@ -169,6 +178,15 @@ internal class SystemRuntimeAnalyzersResources { } } + /// + /// Looks up a localized string similar to Implement Equality and Comparison methods and operators. + /// + internal static string ImplementComparable { + get { + return ResourceManager.GetString("ImplementComparable", resourceCulture); + } + } + /// /// Looks up a localized string similar to Implement IDisposable Interface. /// @@ -178,6 +196,24 @@ internal class SystemRuntimeAnalyzersResources { } } + /// + /// Looks up a localized string similar to Make the getter of the property public. + /// + internal static string MakeGetterPublic { + get { + return ResourceManager.GetString("MakeGetterPublic", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Make the setter of the property non-public. + /// + internal static string MakeSetterNonPublic { + get { + return ResourceManager.GetString("MakeSetterNonPublic", resourceCulture); + } + } + /// /// Looks up a localized string similar to Specify AttributeUsage attribute on '{0}' attribute class.. /// diff --git a/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/SystemRuntimeAnalyzersResources.resx b/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/SystemRuntimeAnalyzersResources.resx index 29907925bfb4b6476af741e964f2f6249640c5f3..9649a0feecc9b4e52913985f3f48c9fb40f6d049 100644 --- a/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/SystemRuntimeAnalyzersResources.resx +++ b/src/Diagnostics/FxCop/System.Runtime.Analyzers/Core/SystemRuntimeAnalyzersResources.resx @@ -171,4 +171,16 @@ Types that implement IComparable should redefine Equals and comparison operators to keep the meanings of less than, greater than, and equals consistent throughout the type. + + Create a property accessor. + + + Implement Equality and Comparison methods and operators + + + Make the getter of the property public + + + Make the setter of the property non-public + \ No newline at end of file diff --git a/src/Diagnostics/FxCop/System.Runtime.Analyzers/Test/Design/OverrideMethodsOnComparableTypesTests.Fixer.cs b/src/Diagnostics/FxCop/System.Runtime.Analyzers/Test/Design/OverrideMethodsOnComparableTypesTests.Fixer.cs new file mode 100644 index 0000000000000000000000000000000000000000..f898ca9ea0d0fc13f4d29b148c0ffc24ab5b13d7 --- /dev/null +++ b/src/Diagnostics/FxCop/System.Runtime.Analyzers/Test/Design/OverrideMethodsOnComparableTypesTests.Fixer.cs @@ -0,0 +1,248 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Test.Utilities; +using Xunit; + +namespace System.Runtime.Analyzers.UnitTests +{ + public partial class OverrideMethodsOnComparableTypesTests + { + protected override CodeFixProvider GetCSharpCodeFixProvider() + { + return new CSharpOverrideMethodsOnComparableTypesFixer(); + } + + protected override CodeFixProvider GetBasicCodeFixProvider() + { + return new BasicOverrideMethodsOnComparableTypesFixer(); + } + + [Fact, Trait(Traits.Feature, Traits.Features.Diagnostics)] + public void CA1036GenerateAllCSharp() + { + VerifyCSharpFix(@" +using System; + +public class A : IComparable +{ + public int CompareTo(object obj) + { + return 1; + } +} +", @" +using System; + +public class A : IComparable +{ + public int CompareTo(object obj) + { + return 1; + } + + public override bool Equals(object obj) + { + throw new NotImplementedException(); + } + + public override int GetHashCode() + { + throw new NotImplementedException(); + } + + public static bool operator ==(A left, A right) + { + throw new NotImplementedException(); + } + + public static bool operator !=(A left, A right) + { + throw new NotImplementedException(); + } + + public static bool operator <(A left, A right) + { + throw new NotImplementedException(); + } + + public static bool operator >(A left, A right) + { + throw new NotImplementedException(); + } +} +"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.Diagnostics)] + public void CA1036GenerateSomeCSharp() + { + VerifyCSharpFix(@" +using System; + +public class A : IComparable +{ + public override int GetHashCode() + { + return 1234; + } + + public int CompareTo(object obj) + { + return 1; + } + + public static bool operator !=(A objLeft, A objRight) + { + return true; + } +} +", @" +using System; + +public class A : IComparable +{ + public override int GetHashCode() + { + return 1234; + } + + public int CompareTo(object obj) + { + return 1; + } + + public static bool operator !=(A objLeft, A objRight) + { + return true; + } + + public override bool Equals(object obj) + { + throw new NotImplementedException(); + } + + public static bool operator ==(A left, A right) + { + throw new NotImplementedException(); + } + + public static bool operator <(A left, A right) + { + throw new NotImplementedException(); + } + + public static bool operator >(A left, A right) + { + throw new NotImplementedException(); + } +} +"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.Diagnostics)] + public void CA1036GenerateAllVisualBasic() + { + VerifyBasicFix(@" +Imports System + +Public Class A : Implements IComparable + + Public Function CompareTo(obj As Object) As Integer + Return 1 + End Function + +End Class", @" +Imports System + +Public Class A : Implements IComparable + + Public Function CompareTo(obj As Object) As Integer + Return 1 + End Function + + Public Overrides Function Equals(obj As Object) As Boolean + Throw New NotImplementedException() + End Function + + Public Overrides Function GetHashCode() As Integer + Throw New NotImplementedException() + End Function + + Public Shared Operator =(left As A, right As A) As Boolean + Throw New NotImplementedException() + End Operator + + Public Shared Operator <>(left As A, right As A) As Boolean + Throw New NotImplementedException() + End Operator + + Public Shared Operator <(left As A, right As A) As Boolean + Throw New NotImplementedException() + End Operator + + Public Shared Operator >(left As A, right As A) As Boolean + Throw New NotImplementedException() + End Operator +End Class"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.Diagnostics)] + public void CA1036GenerateSomeVisualBasic() + { + VerifyBasicFix(@" +Imports System + +Public Class A : Implements IComparable + + Public Overrides Function GetHashCode() As Integer + Return 1234 + End Function + + Public Shared Operator <(objLeft As A, objRight As A) As Boolean + Return True + End Operator + + Public Function CompareTo(obj As Object) As Integer + Return 1 + End Function + +End Class", @" +Imports System + +Public Class A : Implements IComparable + + Public Overrides Function GetHashCode() As Integer + Return 1234 + End Function + + Public Shared Operator <(objLeft As A, objRight As A) As Boolean + Return True + End Operator + + Public Function CompareTo(obj As Object) As Integer + Return 1 + End Function + + Public Overrides Function Equals(obj As Object) As Boolean + Throw New NotImplementedException() + End Function + + Public Shared Operator =(left As A, right As A) As Boolean + Throw New NotImplementedException() + End Operator + + Public Shared Operator <>(left As A, right As A) As Boolean + Throw New NotImplementedException() + End Operator + + Public Shared Operator >(left As A, right As A) As Boolean + Throw New NotImplementedException() + End Operator +End Class"); + } + } +} diff --git a/src/Diagnostics/FxCop/System.Runtime.Analyzers/Test/Design/OverrideMethodsOnComparableTypesTests.cs b/src/Diagnostics/FxCop/System.Runtime.Analyzers/Test/Design/OverrideMethodsOnComparableTypesTests.cs index 5a814c83914d388ba7b8b3128677200db684375d..88d66e04c5a8e8dc4932bed5ab99dd4dc797ede7 100644 --- a/src/Diagnostics/FxCop/System.Runtime.Analyzers/Test/Design/OverrideMethodsOnComparableTypesTests.cs +++ b/src/Diagnostics/FxCop/System.Runtime.Analyzers/Test/Design/OverrideMethodsOnComparableTypesTests.cs @@ -9,7 +9,7 @@ namespace System.Runtime.Analyzers.UnitTests { [WorkItem(858659, "DevDiv")] - public partial class OverrideMethodsOnComparableTypesTests : DiagnosticAnalyzerTestBase + public partial class OverrideMethodsOnComparableTypesTests : CodeFixTestBase { protected override DiagnosticAnalyzer GetBasicDiagnosticAnalyzer() { diff --git a/src/Diagnostics/FxCop/System.Runtime.Analyzers/Test/SystemRuntimeAnalyzersTest.csproj b/src/Diagnostics/FxCop/System.Runtime.Analyzers/Test/SystemRuntimeAnalyzersTest.csproj index 8f7ad581799587409d179b40989c1a4f890d88e6..47e1902627734f1cec0a28434c04cfa6fc7a4657 100644 --- a/src/Diagnostics/FxCop/System.Runtime.Analyzers/Test/SystemRuntimeAnalyzersTest.csproj +++ b/src/Diagnostics/FxCop/System.Runtime.Analyzers/Test/SystemRuntimeAnalyzersTest.csproj @@ -101,6 +101,7 @@ + diff --git a/src/Diagnostics/FxCop/System.Runtime.Analyzers/VisualBasic/BasicSystemRuntimeAnalyzers.vbproj b/src/Diagnostics/FxCop/System.Runtime.Analyzers/VisualBasic/BasicSystemRuntimeAnalyzers.vbproj index 6fde3b40edc8141e97d51c04072bd8cc3f2a9c3a..8c60d7075f206f8d935d410e83954c4d8c486dd6 100644 --- a/src/Diagnostics/FxCop/System.Runtime.Analyzers/VisualBasic/BasicSystemRuntimeAnalyzers.vbproj +++ b/src/Diagnostics/FxCop/System.Runtime.Analyzers/VisualBasic/BasicSystemRuntimeAnalyzers.vbproj @@ -72,6 +72,7 @@ + diff --git a/src/Diagnostics/FxCop/System.Runtime.Analyzers/VisualBasic/Design/OverrideMethodsOnComparableTypes.Fixer.vb b/src/Diagnostics/FxCop/System.Runtime.Analyzers/VisualBasic/Design/OverrideMethodsOnComparableTypes.Fixer.vb new file mode 100644 index 0000000000000000000000000000000000000000..104cc30bf0042b0dc74496234f786896a9270de1 --- /dev/null +++ b/src/Diagnostics/FxCop/System.Runtime.Analyzers/VisualBasic/Design/OverrideMethodsOnComparableTypes.Fixer.vb @@ -0,0 +1,42 @@ +' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +Imports System.Composition +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.CodeFixes +Imports Microsoft.CodeAnalysis.VisualBasic +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax + +Namespace System.Runtime.Analyzers + + Public Class BasicOverrideMethodsOnComparableTypesFixer + Inherits OverrideMethodsOnComparableTypesFixer + + Protected Overrides Function GenerateOperatorDeclaration(returnType As SyntaxNode, operatorName As String, parameters As IEnumerable(Of SyntaxNode), notImplementedStatement As SyntaxNode) As SyntaxNode + Debug.Assert(TypeOf returnType Is TypeSyntax) + + Dim operatorToken As SyntaxToken + Select Case operatorName + Case WellKnownMemberNames.EqualityOperatorName + operatorToken = SyntaxFactory.Token(SyntaxKind.EqualsToken) + Case WellKnownMemberNames.InequalityOperatorName + operatorToken = SyntaxFactory.Token(SyntaxKind.LessThanGreaterThanToken) + Case WellKnownMemberNames.LessThanOperatorName + operatorToken = SyntaxFactory.Token(SyntaxKind.LessThanToken) + Case WellKnownMemberNames.GreaterThanOperatorName + operatorToken = SyntaxFactory.Token(SyntaxKind.GreaterThanToken) + Case Else + Return Nothing + End Select + + Dim operatorStatement = SyntaxFactory.OperatorStatement(Nothing, + SyntaxFactory.TokenList(New SyntaxToken() {SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.SharedKeyword)}), + SyntaxFactory.Token(SyntaxKind.OperatorKeyword), + operatorToken, + SyntaxFactory.ParameterList(SyntaxFactory.SeparatedList(parameters.Cast(Of ParameterSyntax)())), + SyntaxFactory.SimpleAsClause(DirectCast(returnType, TypeSyntax))) + + Return SyntaxFactory.OperatorBlock(operatorStatement, + SyntaxFactory.SingletonList(DirectCast(notImplementedStatement, StatementSyntax))) + End Function + End Class +End Namespace