Skip to content

Commit

Permalink
CA1068: Allow caller attributes to come after cancellation token (#4228)
Browse files Browse the repository at this point in the history
* CA1068: Allow caller attributes to come after cancellation token

* Add caller attributes tests for CA1068

* Ignore caller attribute only if optional

* Move Parameter.IsOptional check to HasCallerInformationAttribute

* Fix typo

* typo

* Use GetOrCreateTypeByMetadataName

Co-authored-by: Amaury Levé <evangelink@gmail.com>

* Use AddIfNotNull

* Apply suggestions from code review

Co-authored-by: Amaury Levé <evangelink@gmail.com>

* Update CancellationTokenParametersMustComeLastTests.cs

* Update CancellationTokenParametersMustComeLast.cs

* Fix

* Apply suggestions from code review

* Apply suggestions from code review

Co-authored-by: Amaury Levé <evangelink@gmail.com>

* Update src/NetAnalyzers/Core/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/CancellationTokenParametersMustComeLast.cs

* Update WellKnownTypeNames.cs

* Use WellKnownTypeNames and move to compilation start

Co-authored-by: Amaury Levé <evangelink@gmail.com>
  • Loading branch information
Youssef1313 and Evangelink authored Sep 30, 2020
1 parent ca12f14 commit 193a3b6
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// 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.Immutable;
using System.Linq;
using Analyzer.Utilities;
using Analyzer.Utilities.Extensions;
using Microsoft.CodeAnalysis;
Expand Down Expand Up @@ -41,6 +42,13 @@ public override void Initialize(AnalysisContext context)
var wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(compilationContext.Compilation);
INamedTypeSymbol? cancellationTokenType = wellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingCancellationToken);
INamedTypeSymbol? iprogressType = wellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemIProgress1);
var builder = ImmutableHashSet.CreateBuilder<INamedTypeSymbol>();
builder.AddIfNotNull(compilationContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeCompilerServicesCallerFilePathAttribute));
builder.AddIfNotNull(compilationContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeCompilerServicesCallerLineNumberAttribute));
builder.AddIfNotNull(compilationContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeCompilerServicesCallerMemberNameAttribute));
var callerInformationAttributes = builder.ToImmutable();
if (cancellationTokenType == null)
{
return;
Expand All @@ -61,6 +69,14 @@ public override void Initialize(AnalysisContext context)
last--;
}
// Ignore parameters that have any of these attributes.
// C# reserved attributes: https://docs.microsoft.com/dotnet/csharp/language-reference/attributes/caller-information
while (last >= 0
&& HasCallerInformationAttribute(methodSymbol.Parameters[last], callerInformationAttributes))
{
last--;
}
// Skip optional parameters, UNLESS one of them is a CancellationToken
// AND it's not the last one.
if (last >= 0 && methodSymbol.Parameters[last].IsOptional
Expand Down Expand Up @@ -120,5 +136,10 @@ public override void Initialize(AnalysisContext context)
SymbolKind.Method);
});
}

private static bool HasCallerInformationAttribute(IParameterSymbol parameter, ImmutableHashSet<INamedTypeSymbol> callerAttributes)
=> parameter.GetAttributes().Any(
attribute => callerAttributes.Any(
callerAttribute => SymbolEqualityComparer.Default.Equals(callerAttribute, attribute.AttributeClass)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -334,5 +334,89 @@ End Function
VerifyVB.Diagnostic().WithLocation(7, 21)
.WithArguments("Public Function SomeAsync(cancellationToken As System.Threading.CancellationToken, progress1 As System.IProgress(Of Integer), progress2 As System.IProgress(Of Integer)) As System.Threading.Tasks.Task"));
}

[Fact, WorkItem(4227, "https://github.com/dotnet/roslyn-analyzers/issues/4227")]
public async Task CA1068_CallerAttributesWithNonOptionalCancellationToken()
{
await VerifyCS.VerifyAnalyzerAsync(@"
using System;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
public class C
{
public Task SomeAsync(CancellationToken cancellationToken,
[CallerMemberName] string memberName = """",
[CallerFilePath] string sourceFilePath = """",
[CallerLineNumber] int sourceLineNumber = 0)
{
throw new NotImplementedException();
}
}");
}

[Fact, WorkItem(4227, "https://github.com/dotnet/roslyn-analyzers/issues/4227")]
public async Task CA1068_CallerAttributesWithOptionalCancellationToken()
{
await VerifyCS.VerifyAnalyzerAsync(@"
using System;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
public class C
{
public Task SomeAsync(CancellationToken cancellationToken = default,
[CallerMemberName] string memberName = """",
[CallerFilePath] string sourceFilePath = """",
[CallerLineNumber] int sourceLineNumber = 0)
{
throw new NotImplementedException();
}
}");
}

[Fact, WorkItem(4227, "https://github.com/dotnet/roslyn-analyzers/issues/4227")]
public async Task CA1068_CallerAttributesWithOptionalCancellationTokenAsLastParameter()
{
await VerifyCS.VerifyAnalyzerAsync(@"
using System;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
public class C
{
public Task SomeAsync([CallerMemberName] string memberName = """",
[CallerFilePath] string sourceFilePath = """",
[CallerLineNumber] int sourceLineNumber = 0,
CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
}");
}

[Fact, WorkItem(4227, "https://github.com/dotnet/roslyn-analyzers/issues/4227")]
public async Task CA1068_CallerAttributesWithOptionalCancellationTokenAsMiddleParameter()
{
await VerifyCS.VerifyAnalyzerAsync(@"
using System;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
public class C
{
public Task SomeAsync([CallerMemberName] string memberName = """",
[CallerFilePath] string sourceFilePath = """",
CancellationToken cancellationToken = default,
[CallerLineNumber] int sourceLineNumber = 0)
{
throw new NotImplementedException();
}
}");
}
}
}
3 changes: 3 additions & 0 deletions src/Utilities/Compiler/WellKnownTypeNames.cs
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,9 @@ internal static class WellKnownTypeNames
public const string SystemReflectionParameterInfo = "System.Reflection.ParameterInfo";
public const string SystemResourcesNeutralResourcesLanguageAttribute = "System.Resources.NeutralResourcesLanguageAttribute";
public const string SystemResourcesResourceManager = "System.Resources.ResourceManager";
public const string SystemRuntimeCompilerServicesCallerFilePathAttribute = "System.Runtime.CompilerServices.CallerFilePathAttribute";
public const string SystemRuntimeCompilerServicesCallerLineNumberAttribute = "System.Runtime.CompilerServices.CallerLineNumberAttribute";
public const string SystemRuntimeCompilerServicesCallerMemberNameAttribute = "System.Runtime.CompilerServices.CallerMemberNameAttribute";
public const string SystemRuntimeCompilerServicesCompilerGeneratedAttribute = "System.Runtime.CompilerServices.CompilerGeneratedAttribute";
public const string SystemRuntimeCompilerServicesConfiguredValueTaskAwaitable1 = "System.Runtime.CompilerServices.ConfiguredValueTaskAwaitable`1";
public const string SystemRuntimeCompilerServicesInternalsVisibleToAttribute = "System.Runtime.CompilerServices.InternalsVisibleToAttribute";
Expand Down

0 comments on commit 193a3b6

Please sign in to comment.