|
// 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.Collections.Immutable;
using System.Composition;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CodeFixes.AddExplicitCast;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.LanguageService;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Extensions;
namespace Microsoft.CodeAnalysis.CSharp.CodeFixes.AddExplicitCast;
[ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.AddExplicitCast), 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 CSharpAddExplicitCastCodeFixProvider()
: AbstractAddExplicitCastCodeFixProvider<ExpressionSyntax>
{
/// <summary>
/// CS0266: Cannot implicitly convert from type 'x' to 'y'. An explicit conversion exists (are you missing a cast?)
/// </summary>
private const string CS0266 = nameof(CS0266);
/// <summary>
/// CS1503: Argument 1: cannot convert from 'x' to 'y'
/// </summary>
private const string CS1503 = nameof(CS1503);
private readonly ArgumentFixer _argumentFixer = new();
private readonly AttributeArgumentFixer _attributeArgumentFixer = new();
public override ImmutableArray<string> FixableDiagnosticIds => [CS0266, CS1503];
protected override void GetPartsOfCastOrConversionExpression(ExpressionSyntax expression, out SyntaxNode type, out ExpressionSyntax castedExpression)
{
var castExpression = (CastExpressionSyntax)expression;
type = castExpression.Type;
castedExpression = castExpression.Expression;
}
protected override ExpressionSyntax Cast(ExpressionSyntax expression, ITypeSymbol type)
=> expression.Cast(type);
protected override void AddPotentialTargetTypes(
Document document,
SemanticModel semanticModel,
SyntaxNode root,
string diagnosticId,
ExpressionSyntax spanNode,
ArrayBuilder<(ExpressionSyntax node, ITypeSymbol type)> candidates,
CancellationToken cancellationToken)
{
if (diagnosticId == CS0266)
{
var inferenceService = document.GetRequiredLanguageService<ITypeInferenceService>();
var conversionType = inferenceService.InferType(semanticModel, spanNode, objectAsDefault: false, cancellationToken);
if (conversionType is null)
return;
candidates.Add((spanNode, conversionType));
}
else if (diagnosticId == CS1503)
{
if (spanNode.GetAncestorOrThis<ArgumentSyntax>() is ArgumentSyntax targetArgument
&& targetArgument.Parent is ArgumentListSyntax argumentList
&& argumentList.Parent is SyntaxNode invocationNode)
{
// invocationNode could be Invocation Expression, Object Creation, Base Constructor...)
candidates.AddRange(_argumentFixer.GetPotentialConversionTypes(
document, semanticModel, root, targetArgument, argumentList, invocationNode, cancellationToken));
}
else if (spanNode.GetAncestorOrThis<AttributeArgumentSyntax>() is AttributeArgumentSyntax targetAttributeArgument
&& targetAttributeArgument.Parent is AttributeArgumentListSyntax attributeArgumentList
&& attributeArgumentList.Parent is AttributeSyntax attributeNode)
{
// attribute node
candidates.AddRange(_attributeArgumentFixer.GetPotentialConversionTypes(
document, semanticModel, root, targetAttributeArgument, attributeArgumentList, attributeNode, cancellationToken));
}
}
}
protected override (SyntaxNode finalTarget, SyntaxNode finalReplacement) Cast(
SemanticModel semanticModel, ExpressionSyntax targetNode, ITypeSymbol conversionType)
{
// The compiler is very ambiguous with assignment expressions `(a += b)`. An error on it may be an error on the
// entire expression or on the RHS of the assignment. Have to reverse engineer what it is doing here.
if (targetNode is AssignmentExpressionSyntax assignmentExpression &&
assignmentExpression.IsCompoundAssignExpression())
{
var leftType = semanticModel.GetTypeInfo(assignmentExpression.Left).Type;
var rightType = semanticModel.GetTypeInfo(assignmentExpression.Right).Type;
if (leftType != null && rightType != null)
{
var conversion = semanticModel.Compilation.ClassifyConversion(rightType, leftType);
if (conversion.Exists && conversion.IsExplicit)
return (assignmentExpression.Right, this.Cast(assignmentExpression.Right, leftType));
}
}
return base.Cast(semanticModel, targetNode, conversionType);
}
}
|