| File: ConvertCast\CSharpConvertDirectCastToTryCastCodeRefactoringProvider.cs | Web Access |
| Project: src\roslyn\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 System.Linq; using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.ConvertCast; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Shared.Extensions; namespace Microsoft.CodeAnalysis.CSharp.ConvertCast; using static CSharpSyntaxTokens; using static SyntaxFactory; /// <summary> /// Refactor: /// var o = (object)1; /// /// Into: /// var o = 1 as object; /// </summary> [ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = PredefinedCodeRefactoringProviderNames.ConvertDirectCastToTryCast), Shared] [method: ImportingConstructor] [method: SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] internal sealed partial class CSharpConvertDirectCastToTryCastCodeRefactoringProvider() : AbstractConvertCastCodeRefactoringProvider<TypeSyntax, CastExpressionSyntax, BinaryExpressionSyntax> { 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 newTypeNode = castExpression.Type; // Cannot use nullable reference types in `as` expression // This check ensures we unwrap any nullables, e.g. // `(string?)null` -> `null as string` if (newTypeNode is NullableTypeSyntax nullableType && isReferenceType) newTypeNode = nullableType.ElementType; // (T)expr --> expr as T // 'expr' is moving to front. Move the existing leading trivia to it, and follow it with a <space> before the 'as'. var newExpression = castExpression.Expression .WithLeadingTrivia(GetCommentTrivia(castExpression.CloseParenToken.TrailingTrivia)) .WithPrependedLeadingTrivia(castExpression.GetLeadingTrivia()) .WithTrailingTrivia(Space); // 'as' is in the middle. Ensure it has no elastic trivia, is followed by a space, and includes any random // comments that might have come before the 'T' in the cast. var newAsKeyword = AsKeyword .WithoutTrivia() .WithTrailingTrivia(Space) .WithAppendedTrailingTrivia(GetCommentTrivia(castExpression.OpenParenToken.TrailingTrivia)); // 'T' is moving to the end. Move the existing trailing trivia to it, and ensure it has no leading trivia. newTypeNode = newTypeNode .WithoutTrivia() .WithAppendedTrailingTrivia(GetCommentTrivia(newTypeNode.GetTrailingTrivia())) .WithAppendedTrailingTrivia(castExpression.GetTrailingTrivia()); return BinaryExpression(SyntaxKind.AsExpression, newExpression, newAsKeyword, newTypeNode); // If there is a comment in the trivia, keep the whole list. Otherwise, return nothing. static SyntaxTriviaList GetCommentTrivia(SyntaxTriviaList triviaList) => triviaList.Any(t => t.IsRegularComment()) ? triviaList : []; } }