Skip to content

Commit

Permalink
Merge pull request #5082 from jmarolf/bugfix/pass-cancelation-token-w…
Browse files Browse the repository at this point in the history
…ith-params-array-overload

handle params array in the argument list
  • Loading branch information
jmarolf authored Aug 2, 2021
2 parents 87c20b9 + dc448ea commit c474643
Show file tree
Hide file tree
Showing 10 changed files with 759 additions and 20 deletions.
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information.

using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading;
using Analyzer.Utilities.Lightup;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Operations;
using Microsoft.NetCore.Analyzers.Runtime;
using NullableAnnotation = Analyzer.Utilities.Lightup.NullableAnnotation;

namespace Microsoft.NetCore.CSharp.Analyzers.Runtime
{
[ExportCodeFixProvider(LanguageNames.CSharp)]
public sealed class CSharpForwardCancellationTokenToInvocationsFixer : ForwardCancellationTokenToInvocationsFixer
public sealed partial class CSharpForwardCancellationTokenToInvocationsFixer : ForwardCancellationTokenToInvocationsFixer<ArgumentSyntax>
{
protected override bool TryGetInvocation(
SemanticModel model,
Expand Down Expand Up @@ -46,18 +51,50 @@ protected override SyntaxNode GetConditionalOperationInvocationExpression(Syntax
protected override bool TryGetExpressionAndArguments(
SyntaxNode invocationNode,
[NotNullWhen(returnValue: true)] out SyntaxNode? expression,
out ImmutableArray<SyntaxNode> arguments)
out ImmutableArray<ArgumentSyntax> arguments)
{
if (invocationNode is InvocationExpressionSyntax invocationExpression)
{
expression = invocationExpression.Expression;
arguments = ImmutableArray.CreateRange<SyntaxNode>(invocationExpression.ArgumentList.Arguments);
arguments = invocationExpression.ArgumentList.Arguments.ToImmutableArray();
return true;
}

expression = null;
arguments = ImmutableArray<SyntaxNode>.Empty;
arguments = ImmutableArray<ArgumentSyntax>.Empty;
return false;
}

protected override SyntaxNode GetTypeSyntaxForArray(IArrayTypeSymbol type)
{
var typeName = TypeNameVisitor.GetTypeSyntaxForSymbol(type.ElementType);
if (type.ElementType.IsReferenceType)
{
var additionalAnnotation = type.NullableAnnotation() switch
{
NullableAnnotation.None => NullableSyntaxAnnotationEx.Oblivious,
NullableAnnotation.Annotated => NullableSyntaxAnnotationEx.AnnotatedOrNotAnnotated,
NullableAnnotation.NotAnnotated => NullableSyntaxAnnotationEx.AnnotatedOrNotAnnotated,
_ => null,
};

if (additionalAnnotation is not null)
{
typeName = typeName.WithAdditionalAnnotations(additionalAnnotation);
}
}

return typeName;
}

protected override IEnumerable<SyntaxNode> GetExpressions(ImmutableArray<ArgumentSyntax> newArguments)
{
return newArguments.Select(x => x.Expression);
}

protected override SyntaxNode GetArrayCreationExpression(SyntaxGenerator generator, SyntaxNode typeSyntax, IEnumerable<SyntaxNode> expressions)
{
return generator.ArrayCreationExpression(typeSyntax, expressions);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information.

using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Analyzer.Utilities.Lightup;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Simplification;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
using NullableAnnotation = Analyzer.Utilities.Lightup.NullableAnnotation;

namespace Microsoft.NetCore.CSharp.Analyzers.Runtime
{
public sealed partial class CSharpForwardCancellationTokenToInvocationsFixer
{
private class TypeNameVisitor : SymbolVisitor<TypeSyntax>
{
public static TypeSyntax GetTypeSyntaxForSymbol(INamespaceOrTypeSymbol symbol)
{
return symbol.Accept(new TypeNameVisitor()).WithAdditionalAnnotations(Simplifier.Annotation);
}

public override TypeSyntax DefaultVisit(ISymbol symbol)
=> throw new NotImplementedException();

public override TypeSyntax VisitAlias(IAliasSymbol symbol)
{
return AddInformationTo(ToIdentifierName(symbol.Name));
}

public override TypeSyntax VisitDynamicType(IDynamicTypeSymbol symbol)
{
return AddInformationTo(IdentifierName("dynamic"));
}

public override TypeSyntax VisitNamedType(INamedTypeSymbol symbol)
{
if (TryCreateNativeIntegerType(symbol, out var typeSyntax))
return typeSyntax;

typeSyntax = CreateSimpleTypeSyntax(symbol);
if (!(typeSyntax is SimpleNameSyntax))
return typeSyntax;

var simpleNameSyntax = (SimpleNameSyntax)typeSyntax;
if (symbol.ContainingType is not null)
{
if (symbol.ContainingType.TypeKind != TypeKind.Submission)
{
var containingTypeSyntax = symbol.ContainingType.Accept(this);
if (containingTypeSyntax is NameSyntax name)
{
typeSyntax = AddInformationTo(
QualifiedName(name, simpleNameSyntax));
}
else
{
typeSyntax = AddInformationTo(simpleNameSyntax);
}
}
}
else if (symbol.ContainingNamespace is not null)
{
if (symbol.ContainingNamespace.IsGlobalNamespace)
{
if (symbol.TypeKind != TypeKind.Error)
{
typeSyntax = AddGlobalAlias(simpleNameSyntax);
}
}
else
{
var container = symbol.ContainingNamespace.Accept(this)!;
typeSyntax = AddInformationTo(QualifiedName(
(NameSyntax)container,
simpleNameSyntax));
}
}

if (symbol.NullableAnnotation() == NullableAnnotation.Annotated &&
!symbol.IsValueType)
{
typeSyntax = AddInformationTo(NullableType(typeSyntax));
}

return typeSyntax;
}

public override TypeSyntax VisitNamespace(INamespaceSymbol symbol)
{
var syntax = AddInformationTo(ToIdentifierName(symbol.Name));
if (symbol.ContainingNamespace == null)
{
return syntax;
}

if (symbol.ContainingNamespace.IsGlobalNamespace)
{
return AddGlobalAlias(syntax);
}
else
{
var container = symbol.ContainingNamespace.Accept(this)!;
return AddInformationTo(QualifiedName(
(NameSyntax)container,
syntax));
}
}

public override TypeSyntax VisitTypeParameter(ITypeParameterSymbol symbol)
{
TypeSyntax typeSyntax = AddInformationTo(ToIdentifierName(symbol.Name));
if (symbol.NullableAnnotation() == NullableAnnotation.Annotated)
typeSyntax = AddInformationTo(NullableType(typeSyntax));

return typeSyntax;
}

private TypeSyntax CreateSimpleTypeSyntax(INamedTypeSymbol symbol)
{
if (symbol.IsTupleType && symbol.TupleUnderlyingType != null && !symbol.Equals(symbol.TupleUnderlyingType))
{
return CreateSimpleTypeSyntax(symbol.TupleUnderlyingType);
}

if (string.IsNullOrEmpty(symbol.Name) || symbol.IsAnonymousType)
{
return CreateSystemObject();
}

if (symbol.TypeParameters.Length == 0)
{
if (symbol.TypeKind == TypeKind.Error && symbol.Name == "var")
{
return CreateSystemObject();
}

return ToIdentifierName(symbol.Name);
}

var typeArguments = symbol.IsUnboundGenericType
? Enumerable.Repeat((TypeSyntax)OmittedTypeArgument(), symbol.TypeArguments.Length)
: symbol.TypeArguments.Select(t => GetTypeSyntaxForSymbol(t));

return GenericName(
ToIdentifierToken(symbol.Name),
TypeArgumentList(SeparatedList(typeArguments)));
}

private static QualifiedNameSyntax CreateSystemObject()
{
return QualifiedName(
AliasQualifiedName(
CreateGlobalIdentifier(),
IdentifierName("System")),
IdentifierName("Object"));
}

private static TTypeSyntax AddInformationTo<TTypeSyntax>(TTypeSyntax syntax)
where TTypeSyntax : TypeSyntax
{
syntax = syntax.WithLeadingTrivia(ElasticMarker).WithTrailingTrivia(ElasticMarker);
return syntax;
}

/// <summary>
/// We always unilaterally add "global::" to all named types/namespaces. This
/// will then be trimmed off if possible by the simplifier
/// </summary>
private static TypeSyntax AddGlobalAlias(SimpleNameSyntax syntax)
{
return AddInformationTo(AliasQualifiedName(CreateGlobalIdentifier(), syntax));
}

private static IdentifierNameSyntax ToIdentifierName(string identifier)
=> IdentifierName(ToIdentifierToken(identifier));

private static IdentifierNameSyntax CreateGlobalIdentifier()
=> IdentifierName(Token(SyntaxKind.GlobalKeyword));

private static bool TryCreateNativeIntegerType(INamedTypeSymbol symbol, [NotNullWhen(true)] out TypeSyntax? syntax)
{
if (symbol.IsNativeIntegerType())
{
syntax = IdentifierName(symbol.SpecialType == SpecialType.System_IntPtr ? "nint" : "nuint");
return true;
}

syntax = null;
return false;
}

private static SyntaxToken ToIdentifierToken(string identifier)
{
var escaped = EscapeIdentifier(identifier);

if (escaped.Length == 0 || escaped[0] != '@')
{
return Identifier(escaped);
}

var unescaped = identifier.StartsWith("@", StringComparison.Ordinal)
? identifier[1..]
: identifier;

var token = Identifier(
default, SyntaxKind.None, "@" + unescaped, unescaped, default);

if (!identifier.StartsWith("@", StringComparison.Ordinal))
{
token = token.WithAdditionalAnnotations(Simplifier.Annotation);
}

return token;
}

private static string EscapeIdentifier(string identifier)
{
var nullIndex = identifier.IndexOf('\0');
if (nullIndex >= 0)
{
identifier = identifier.Substring(0, nullIndex);
}

var needsEscaping = SyntaxFacts.GetKeywordKind(identifier) != SyntaxKind.None;

return needsEscaping ? "@" + identifier : identifier;
}
}
}
}
Loading

0 comments on commit c474643

Please sign in to comment.