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

Update Azure.ClientSdk.Analyzers to fix incorrect behaviors #6496

Closed
wants to merge 15 commits into from
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Threading.Tasks;
using Xunit;
using Verifier = Azure.ClientSdk.Analyzers.Tests.AzureAnalyzerVerifier<Azure.ClientSdk.Analyzers.ClientMethodsAnalyzer>;
Expand Down Expand Up @@ -496,5 +496,54 @@ await Verifier.CreateAnalyzer(code)
.WithDisabledDiagnostics("AZC0015")
.RunAsync();
}

[Fact]
public async Task AZC0004NotProducedForConstructorOrPropertyOrOverride()
{
const string code = @"
namespace RandomNamespace
{
public class SomeClient
{
private string _id;
public virtual string Id => _id;

public override bool Equals(object obj) => base.Equals(obj);

protected SomeClient()
{
}

public SomeClient(string id)
{
_id = id;
}
}
}";
await Verifier.CreateAnalyzer(code)
.RunAsync();
}

[Fact]
public async Task AZC0004NotProducedForGetSubClientMethod()
pshao25 marked this conversation as resolved.
Show resolved Hide resolved
{
const string code = @"
namespace RandomNamespace
{
public class SomeClient
{
public SubClient GetSubClient()
{
return null;
}
}

public class SubClient
{
}
}";
await Verifier.CreateAnalyzer(code)
.RunAsync();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,16 @@ public virtual Operation<BinaryData> GetOperationOfT(string s, RequestContext co
{
return null;
}

public virtual Task<Operation<AsyncPageable<BinaryData>>> GetOperationOfPageableAsync(string s, RequestContext context)
{
return null;
}

public virtual Operation<Pageable<BinaryData>> GetOperationOfPageable(string s, RequestContext context)
{
return null;
}
}
}";
await Verifier.CreateAnalyzer(code)
Expand Down Expand Up @@ -192,6 +202,38 @@ await Verifier.CreateAnalyzer(code)
.RunAsync();
}

[Fact]
public async Task AZC0018ProducedForMethodsWithOperationOfPageableModel()
pshao25 marked this conversation as resolved.
Show resolved Hide resolved
{
const string code = @"
using Azure;
using Azure.Core;
using System.Threading;
using System.Threading.Tasks;

namespace RandomNamespace
{
public class Model
{
string a;
}
public class SomeClient
{
public virtual Task<Operation<AsyncPageable<Model>>> {|AZC0018:GetAsync|}(string s, RequestContext context)
{
return null;
}

public virtual Operation<Pageable<Model>> {|AZC0018:Get|}(string s, RequestContext context)
{
return null;
}
}
}";
await Verifier.CreateAnalyzer(code)
.RunAsync();
}

[Fact]
public async Task AZC0018ProducedForMethodsWithParameterModel()
{
Expand Down Expand Up @@ -226,7 +268,7 @@ await Verifier.CreateAnalyzer(code)
}

[Fact]
public async Task AZC0018ProducedForMethodsWithNoRequestContentAndOptionalRequestContext()
public async Task AZC0018NotProducedForMethodsWithNoRequestContentAndRequiredContext()
{
const string code = @"
using Azure;
Expand All @@ -239,6 +281,74 @@ namespace RandomNamespace
{
public class SomeClient
{
public virtual Task<Response> GetAsync(string a, Azure.RequestContext context)
{
return null;
}

public virtual Response Get(string a, Azure.RequestContext context)
{
return null;
}
}
}";
await Verifier.CreateAnalyzer(code)
.RunAsync();
}

[Fact]
public async Task AZC0018NotProducedForMethodsWithNoRequestContentButOnlyProtocol()
{
const string code = @"
using Azure;
using Azure.Core;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;

namespace RandomNamespace
{
public class SomeClient
{
public virtual Task<Response> GetAsync(string a, Azure.RequestContext context = null)
{
return null;
}

public virtual Response Get(string a, Azure.RequestContext context = null)
{
return null;
}
}
}";
await Verifier.CreateAnalyzer(code)
.RunAsync();
}

[Fact]
public async Task AZC0018ProducedForMethodsWithNoRequestContentButProtocolAndConvenience()
{
const string code = @"
using Azure;
using Azure.Core;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;

namespace RandomNamespace
{
public class SomeClient
{
public virtual Task<Response> GetAsync(string a, CancellationToken cancellationToken = default)
{
return null;
}

public virtual Response Get(string a, CancellationToken cancellationToken = default)
{
return null;
}

public virtual Task<Response> {|AZC0018:GetAsync|}(string a, Azure.RequestContext context = null)
{
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,26 @@ public override void Analyze(ISymbolAnalysisContext context)
AnalyzeCore(context);
}

protected class ParameterEquivalenceComparerOptionalIgnore : ParameterEquivalenceComparer
pshao25 marked this conversation as resolved.
Show resolved Hide resolved
{
public static new ParameterEquivalenceComparerOptionalIgnore Default { get; } = new ParameterEquivalenceComparerOptionalIgnore();

public override bool Equals(IParameterSymbol x, IParameterSymbol y)
{
return x.Name.Equals(y.Name) && TypeSymbolEquals(x.Type, y.Type);
}
}

protected class ParameterEquivalenceComparer : IEqualityComparer<IParameterSymbol>, IEqualityComparer<ITypeParameterSymbol>
{
public static ParameterEquivalenceComparer Default { get; } = new ParameterEquivalenceComparer();

public bool Equals(IParameterSymbol x, IParameterSymbol y)
public virtual bool Equals(IParameterSymbol x, IParameterSymbol y)
{
return x.Name.Equals(y.Name) && TypeSymbolEquals(x.Type, y.Type) && x.IsOptional == y.IsOptional;
}

private bool TypeSymbolEquals(ITypeSymbol x, ITypeSymbol y)
protected bool TypeSymbolEquals(ITypeSymbol x, ITypeSymbol y)
jsquire marked this conversation as resolved.
Show resolved Hide resolved
{
switch (x)
{
Expand Down Expand Up @@ -86,20 +96,19 @@ protected static IMethodSymbol FindMethod(IEnumerable<IMethodSymbol> methodSymbo
parameters.SequenceEqual(symbol.Parameters, ParameterEquivalenceComparer.Default));
}

protected static IMethodSymbol FindMethod(IEnumerable<IMethodSymbol> methodSymbols, ImmutableArray<ITypeParameterSymbol> genericParameters, ImmutableArray<IParameterSymbol> parameters, Func<IParameterSymbol, bool> lastParameter)
protected static IMethodSymbol FindMethod(IEnumerable<IMethodSymbol> methodSymbols, ImmutableArray<ITypeParameterSymbol> genericParameters, ImmutableArray<IParameterSymbol> parameters, Func<IParameterSymbol, bool> lastParameter, ParameterEquivalenceComparer comparer = null)
{

return methodSymbols.SingleOrDefault(symbol =>
{

pshao25 marked this conversation as resolved.
Show resolved Hide resolved
if (!symbol.Parameters.Any() || !genericParameters.SequenceEqual(symbol.TypeParameters, ParameterEquivalenceComparer.Default))
if (!symbol.Parameters.Any() || !genericParameters.SequenceEqual(symbol.TypeParameters, comparer ?? ParameterEquivalenceComparer.Default))
{
return false;
}

var allButLast = symbol.Parameters.RemoveAt(symbol.Parameters.Length - 1);

return allButLast.SequenceEqual(parameters, ParameterEquivalenceComparer.Default) && lastParameter(symbol.Parameters.Last());
return allButLast.SequenceEqual(parameters, comparer ?? ParameterEquivalenceComparer.Default) && lastParameter(symbol.Parameters.Last());
});
}

Expand Down
Loading