File: ConvertCast\CSharpConvertDirectCastToTryCastCodeRefactoringProvider.cs
Web Access
Project: src\src\Features\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.Features.csproj (Microsoft.CodeAnalysis.CSharp.Features)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System.Composition;
using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.ConvertCast;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
 
namespace Microsoft.CodeAnalysis.CSharp.ConvertCast;
 
/// <summary>
/// Refactor:
///     var o = (object)1;
///
/// Into:
///     var o = 1 as object;
/// </summary>
[ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = PredefinedCodeRefactoringProviderNames.ConvertDirectCastToTryCast), Shared]
internal sealed partial class CSharpConvertDirectCastToTryCastCodeRefactoringProvider
    : AbstractConvertCastCodeRefactoringProvider<TypeSyntax, CastExpressionSyntax, BinaryExpressionSyntax>
{
    [ImportingConstructor]
    [SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")]
    public CSharpConvertDirectCastToTryCastCodeRefactoringProvider()
    {
    }
 
    protected override string GetTitle()
        => CSharpFeaturesResources.Change_to_as_expression;
 
    protected override int FromKind => (int)SyntaxKind.CastExpression;
 
    protected override TypeSyntax GetTypeNode(CastExpressionSyntax from)
        => from.Type;
 
    protected override BinaryExpressionSyntax ConvertExpression(CastExpressionSyntax castExpression, NullableContext nullableContext, bool isReferenceType)
    {
        var typeNode = castExpression.Type;
        var expression = castExpression.Expression;
 
        // Cannot use nullable reference types in `as` expression
        // This check ensures we unwrap any nullables, e.g.
        // `(string?)null` -> `null as string`
        if (typeNode is NullableTypeSyntax nullableType && isReferenceType)
            typeNode = nullableType.ElementType;
 
        // Trivia handling
        // #0 ( #1 Type #2 ) #3 expr #4
        // #0 #3 expr as #1 Type #2 #4
        // If #1 is present a new line is added after "as" because of elastic trivia on "as"
        // #3 is kept with the expression and moves
        typeNode = typeNode.WithLeadingTrivia(castExpression.OpenParenToken.TrailingTrivia);
        var middleTrivia = castExpression.CloseParenToken.TrailingTrivia.SkipInitialWhitespace();
        var newLeadingTrivia = castExpression.GetLeadingTrivia().AddRange(middleTrivia);
        var newTrailingTrivia = typeNode.GetTrailingTrivia().WithoutLeadingBlankLines().AddRange(expression.GetTrailingTrivia().WithoutLeadingBlankLines());
        expression = expression.WithoutTrailingTrivia();
        typeNode = typeNode.WithoutTrailingTrivia();
 
        var asExpression = BinaryExpression(SyntaxKind.AsExpression, expression, typeNode)
            .WithLeadingTrivia(newLeadingTrivia)
            .WithTrailingTrivia(newTrailingTrivia);
 
        return asExpression;
    }
}