提交 d817b222 编写于 作者: C Cyrus Najmabadi

Simplify remove-cast implementation.

上级 218b6ed9
......@@ -59,7 +59,7 @@ static void Main()
{
int x = 2;
int i = 1;
Goo((x < i), x > (2 + 3));
Goo(x < (i), x > (2 + 3));
}
static void Goo(bool a, bool b) { }
......@@ -3550,7 +3550,7 @@ class C
{
void Goo(Task<Action> x)
{
x.Result();
(x.Result)();
}
}
");
......
......@@ -367,7 +367,7 @@ class C
{
void M()
{
System.Action a = (() => { });
System.Action a = () => { };
}
}
</code>
......@@ -401,13 +401,12 @@ class C
void M()
{
System.Action a = null;
a = (() => { });
a = () => { };
}
}
</code>
Await TestAsync(input, expected)
End Function
<Fact, Trait(Traits.Feature, Traits.Features.Simplification)>
......@@ -435,7 +434,7 @@ class C
{
void M()
{
Goo((() => "Goo"));
Goo(() => "Goo");
}
void Goo&lt;T&gt;(System.Func&lt;T&gt; f) { }
......@@ -471,7 +470,7 @@ class C
{
void M()
{
Goo(f: (() => "Goo"));
Goo(f: () => "Goo");
}
void Goo&lt;T&gt;(System.Func&lt;T&gt; f) { }
......@@ -3207,7 +3206,7 @@ static class Program
{
static void Main()
{
new Action<string>((y => Goo(1)))(null);
new Action<string>(y => Goo(1))(null);
}
static void Goo(this object x) { Console.WriteLine(1); }
......@@ -3254,7 +3253,7 @@ static class Program
{
static void Main()
{
new Action<string>(((y) => { string x = y; x.Goo(); }))(null);
new Action<string>((y) => { string x = y; x.Goo(); })(null);
}
static void Goo(this object x) { Console.WriteLine(1); }
......@@ -3397,7 +3396,7 @@ static class Program
{
static void Main()
{
new Action<string, object>(((y, z) => { object x = y; z.Goo(); }))(null, null);
new Action<string, object>((y, z) => { object x = y; z.Goo(); })(null, null);
}
static void Goo(this object x) { Console.WriteLine(1); }
......@@ -3533,7 +3532,7 @@ class Program
{
static void Main(string[] args)
{
Func<Exception> f = (() => new ArgumentException());
Func<Exception> f = () => new ArgumentException();
}
}
]]>
......
......@@ -215,15 +215,6 @@ internal class CSharpFeaturesResources {
}
}
/// <summary>
/// Looks up a localized string similar to Cast is redundant.
/// </summary>
internal static string Cast_is_redundant {
get {
return ResourceManager.GetString("Cast_is_redundant", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to catch clause.
/// </summary>
......@@ -827,15 +818,6 @@ internal class CSharpFeaturesResources {
}
}
/// <summary>
/// Looks up a localized string similar to Remove Unnecessary Cast.
/// </summary>
internal static string Remove_Unnecessary_Cast {
get {
return ResourceManager.GetString("Remove_Unnecessary_Cast", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Remove Unnecessary Usings.
/// </summary>
......
......@@ -159,12 +159,6 @@
<data name="Autoselect_disabled_due_to_potential_range_variable_declaration" xml:space="preserve">
<value>Autoselect disabled due to potential range variable declaration.</value>
</data>
<data name="Remove_Unnecessary_Cast" xml:space="preserve">
<value>Remove Unnecessary Cast</value>
</data>
<data name="Cast_is_redundant" xml:space="preserve">
<value>Cast is redundant</value>
</data>
<data name="Simplify_name_0" xml:space="preserve">
<value>Simplify name '{0}'</value>
</data>
......
// 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.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Options;
namespace Microsoft.CodeAnalysis.CSharp.CodeFixes.RemoveUnnecessaryCast
{
internal partial class RemoveUnnecessaryCastCodeFixProvider : CodeFixProvider
{
private class RemoveUnnecessaryCastFixAllProvider : BatchSimplificationFixAllProvider
{
internal static new readonly RemoveUnnecessaryCastFixAllProvider Instance = new RemoveUnnecessaryCastFixAllProvider();
protected override SyntaxNode GetNodeToSimplify(SyntaxNode root, SemanticModel model, Diagnostic diagnostic, DocumentOptionSet options, out string codeActionId, CancellationToken cancellationToken)
{
codeActionId = null;
return GetCastNode(root, model, diagnostic.Location.SourceSpan, cancellationToken);
}
protected override bool NeedsParentFixup => true;
protected override async Task<Document> AddSimplifyAnnotationsAsync(Document document, SyntaxNode nodeToSimplify, CancellationToken cancellationToken)
{
var cast = nodeToSimplify as CastExpressionSyntax;
if (cast == null)
{
return null;
}
return await RemoveUnnecessaryCastAsync(document, cast, cancellationToken).ConfigureAwait(false);
}
}
}
}
......@@ -11,6 +11,7 @@
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Simplification;
......@@ -21,89 +22,68 @@ namespace Microsoft.CodeAnalysis.CSharp.CodeFixes.RemoveUnnecessaryCast
{
[ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.RemoveUnnecessaryCast), Shared]
[ExtensionOrder(After = PredefinedCodeFixProviderNames.ImplementInterface)]
internal partial class RemoveUnnecessaryCastCodeFixProvider : CodeFixProvider
internal partial class RemoveUnnecessaryCastCodeFixProvider : SyntaxEditorBasedCodeFixProvider
{
public sealed override ImmutableArray<string> FixableDiagnosticIds
{
get { return ImmutableArray.Create(IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId); }
}
public sealed override FixAllProvider GetFixAllProvider()
{
return RemoveUnnecessaryCastFixAllProvider.Instance;
}
private static CastExpressionSyntax GetCastNode(SyntaxNode root, SemanticModel model, TextSpan span, CancellationToken cancellationToken)
{
var token = root.FindToken(span.Start);
if (!token.Span.IntersectsWith(span))
{
return null;
}
return token.GetAncestors<CastExpressionSyntax>()
.FirstOrDefault(c => c.Span.IntersectsWith(span) && c.IsUnnecessaryCast(model, cancellationToken));
}
public sealed override ImmutableArray<string> FixableDiagnosticIds { get; } =
ImmutableArray.Create(IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId);
public sealed override Task RegisterCodeFixesAsync(CodeFixContext context)
{
context.RegisterCodeFix(
new MyCodeAction(
CSharpFeaturesResources.Remove_Unnecessary_Cast,
c => RemoveUnnecessaryCastAsync(context.Document, context.Span, c)),
context.RegisterCodeFix(new MyCodeAction(
FeaturesResources.Remove_Unnecessary_Cast,
c => FixAsync(context.Document, context.Diagnostics.First(), c)),
context.Diagnostics);
return SpecializedTasks.EmptyTask;
}
private static async Task<Document> RemoveUnnecessaryCastAsync(
Document document, TextSpan span, CancellationToken cancellationToken)
protected override async Task FixAllAsync(
Document document, ImmutableArray<Diagnostic> diagnostics,
SyntaxEditor editor, CancellationToken cancellationToken)
{
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var cast = GetCastNode(root, model, span, cancellationToken);
return await RemoveUnnecessaryCastAsync(document, cast, cancellationToken).ConfigureAwait(false);
var castNodes = diagnostics.SelectAsArray(
d => (CastExpressionSyntax)d.AdditionalLocations[0].FindNode(getInnermostNodeForTie: true, cancellationToken));
await editor.ApplyExpressionLevelSemanticEditsAsync(
document, castNodes,
(semanticModel, castExpression) => castExpression.IsUnnecessaryCast(semanticModel, cancellationToken),
(_, currentRoot, castExpression) =>
{
var (oldParent, newParent) = Update(castExpression);
return currentRoot.ReplaceNode(oldParent, newParent);
},
cancellationToken).ConfigureAwait(false);
}
private static async Task<Document> RemoveUnnecessaryCastAsync(
Document document, CastExpressionSyntax cast, CancellationToken cancellationToken)
private (ExpressionSyntax, ExpressionSyntax) Update(CastExpressionSyntax castExpression)
{
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var oldParent = castExpression.WalkUpParentheses();
var newParent = Recurse(oldParent);
var annotatedCast = cast.WithAdditionalAnnotations(Simplifier.Annotation);
return (oldParent, newParent);
}
if (annotatedCast.Expression is ParenthesizedExpressionSyntax)
private ExpressionSyntax Recurse(ExpressionSyntax old)
{
if (old is ParenthesizedExpressionSyntax parenthesizedExpression)
{
annotatedCast = annotatedCast.WithExpression(
annotatedCast.Expression.WithAdditionalAnnotations(Simplifier.Annotation));
// It's common in C# to have to write ((Goo)expr).Etc(). we don't just want to
// remove the cast and produce (expr).Etc(). So we mark all parent parenthesized
// expressions as worthy of simplification. The simplifier will remove these
// if possible, or leave them alone if not.
return parenthesizedExpression.ReplaceNode(parenthesizedExpression.Expression, Recurse(parenthesizedExpression.Expression))
.WithAdditionalAnnotations(Simplifier.Annotation);
}
else
else if (old is CastExpressionSyntax castExpression)
{
annotatedCast = annotatedCast.WithExpression(
annotatedCast.Expression.Parenthesize());
// parenthesize the uncasted value to help ensure any proper parsing. The excess
// parens will be removed if unnecessary.
return castExpression.Uncast().WithAdditionalAnnotations(Formatter.Annotation)
.Parenthesize();
}
ExpressionSyntax oldNode = cast;
ExpressionSyntax newNode = annotatedCast;
// Ensure that we simplify any parenting parenthesized expressions not just on the syntax tree level but also on Token based
// Case 1:
// In the syntax, (((Task<Action>)x).Result)()
// oldNode = (Task<Action>)x
// newNode = (Task<Action>)(x)
// Final newNode will be (((Task<Action>)(x)).Result)
while (oldNode.Parent.IsKind(SyntaxKind.ParenthesizedExpression) || oldNode.GetFirstToken().GetPreviousToken().Parent.IsKind(SyntaxKind.ParenthesizedExpression))
else
{
var parenthesizedExpression = (ParenthesizedExpressionSyntax)oldNode.GetFirstToken().GetPreviousToken().Parent;
newNode = parenthesizedExpression.ReplaceNode(oldNode, newNode)
.WithAdditionalAnnotations(Simplifier.Annotation);
oldNode = parenthesizedExpression;
throw ExceptionUtilities.UnexpectedValue(old);
}
newNode = newNode.WithAdditionalAnnotations(Formatter.Annotation);
var newRoot = root.ReplaceNode(oldNode, newNode);
return document.WithSyntaxRoot(newRoot);
}
private class MyCodeAction : CodeAction.DocumentChangeAction
......
......@@ -11,22 +11,17 @@
namespace Microsoft.CodeAnalysis.CSharp.Diagnostics.RemoveUnnecessaryCast
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
internal sealed class CSharpRemoveUnnecessaryCastDiagnosticAnalyzer : RemoveUnnecessaryCastDiagnosticAnalyzerBase<SyntaxKind>
internal sealed class CSharpRemoveUnnecessaryCastDiagnosticAnalyzer
: RemoveUnnecessaryCastDiagnosticAnalyzerBase<SyntaxKind, CastExpressionSyntax>
{
private static readonly ImmutableArray<SyntaxKind> s_kindsOfInterest = ImmutableArray.Create(SyntaxKind.CastExpression);
public override ImmutableArray<SyntaxKind> SyntaxKindsOfInterest => s_kindsOfInterest;
protected override ImmutableArray<SyntaxKind> SyntaxKindsOfInterest => s_kindsOfInterest;
protected override bool IsUnnecessaryCast(SemanticModel model, SyntaxNode node, CancellationToken cancellationToken)
{
var cast = (CastExpressionSyntax)node;
return cast.IsUnnecessaryCast(model, cancellationToken);
}
protected override bool IsUnnecessaryCast(SemanticModel model, CastExpressionSyntax cast, CancellationToken cancellationToken)
=> cast.IsUnnecessaryCast(model, cancellationToken);
protected override TextSpan GetDiagnosticSpan(SyntaxNode node)
{
var cast = (CastExpressionSyntax)node;
return TextSpan.FromBounds(cast.OpenParenToken.SpanStart, cast.CloseParenToken.Span.End);
}
protected override TextSpan GetFadeSpan(CastExpressionSyntax node)
=> TextSpan.FromBounds(node.OpenParenToken.SpanStart, node.CloseParenToken.Span.End);
}
}
......@@ -67,16 +67,6 @@
<target state="translated">Automatický výběr je zakázaný kvůli možné deklaraci proměnné rozsahu.</target>
<note />
</trans-unit>
<trans-unit id="Remove_Unnecessary_Cast">
<source>Remove Unnecessary Cast</source>
<target state="translated">Odebrat nepotřebné přetypování</target>
<note />
</trans-unit>
<trans-unit id="Cast_is_redundant">
<source>Cast is redundant</source>
<target state="translated">Přetypování je redundantní.</target>
<note />
</trans-unit>
<trans-unit id="Simplify_name_0">
<source>Simplify name '{0}'</source>
<target state="translated">Zjednodušit název {0}</target>
......
......@@ -67,16 +67,6 @@
<target state="translated">Automatische Auswahl aufgrund einer potzenziellen Bereichvariablendeklaration deaktiviert.</target>
<note />
</trans-unit>
<trans-unit id="Remove_Unnecessary_Cast">
<source>Remove Unnecessary Cast</source>
<target state="translated">Nicht erforderliche Umwandlung entfernen</target>
<note />
</trans-unit>
<trans-unit id="Cast_is_redundant">
<source>Cast is redundant</source>
<target state="translated">Cast ist redundant</target>
<note />
</trans-unit>
<trans-unit id="Simplify_name_0">
<source>Simplify name '{0}'</source>
<target state="translated">Name '{0}' vereinfachen</target>
......
......@@ -67,16 +67,6 @@
<target state="translated">La selección automática se ha deshabilitado debido posiblemente a una declaración de variable de rango.</target>
<note />
</trans-unit>
<trans-unit id="Remove_Unnecessary_Cast">
<source>Remove Unnecessary Cast</source>
<target state="translated">Quitar conversión innecesaria</target>
<note />
</trans-unit>
<trans-unit id="Cast_is_redundant">
<source>Cast is redundant</source>
<target state="translated">Cast es redundante</target>
<note />
</trans-unit>
<trans-unit id="Simplify_name_0">
<source>Simplify name '{0}'</source>
<target state="translated">Simplificar nombre '{0}'</target>
......
......@@ -67,16 +67,6 @@
<target state="translated">Sélection automatique désactivée en raison d'une déclaration de variable de plage éventuelle.</target>
<note />
</trans-unit>
<trans-unit id="Remove_Unnecessary_Cast">
<source>Remove Unnecessary Cast</source>
<target state="translated">Supprimer le cast inutile</target>
<note />
</trans-unit>
<trans-unit id="Cast_is_redundant">
<source>Cast is redundant</source>
<target state="translated">Cast redondant</target>
<note />
</trans-unit>
<trans-unit id="Simplify_name_0">
<source>Simplify name '{0}'</source>
<target state="translated">Simplifier le nom '{0}'</target>
......
......@@ -67,16 +67,6 @@
<target state="translated">La selezione automatica è disabilitata a causa di una potenziale dichiarazione della variabile di intervallo.</target>
<note />
</trans-unit>
<trans-unit id="Remove_Unnecessary_Cast">
<source>Remove Unnecessary Cast</source>
<target state="translated">Rimuovi cast non necessario</target>
<note />
</trans-unit>
<trans-unit id="Cast_is_redundant">
<source>Cast is redundant</source>
<target state="translated">Il cast è ridondante</target>
<note />
</trans-unit>
<trans-unit id="Simplify_name_0">
<source>Simplify name '{0}'</source>
<target state="translated">Semplifica il nome '{0}'</target>
......
......@@ -67,16 +67,6 @@
<target state="translated">範囲変数宣言の可能性があるため、自動選択は無効になっています。</target>
<note />
</trans-unit>
<trans-unit id="Remove_Unnecessary_Cast">
<source>Remove Unnecessary Cast</source>
<target state="translated">不要なキャストの削除</target>
<note />
</trans-unit>
<trans-unit id="Cast_is_redundant">
<source>Cast is redundant</source>
<target state="translated">キャストが冗長です</target>
<note />
</trans-unit>
<trans-unit id="Simplify_name_0">
<source>Simplify name '{0}'</source>
<target state="translated">名前 '{0}' の単純化</target>
......
......@@ -67,16 +67,6 @@
<target state="translated">범위 변수가 선언될 수 있어 자동 선택을 사용하지 않도록 설정했습니다.</target>
<note />
</trans-unit>
<trans-unit id="Remove_Unnecessary_Cast">
<source>Remove Unnecessary Cast</source>
<target state="translated">불필요한 캐스트 제거</target>
<note />
</trans-unit>
<trans-unit id="Cast_is_redundant">
<source>Cast is redundant</source>
<target state="translated">캐스팅이 중복됩니다.</target>
<note />
</trans-unit>
<trans-unit id="Simplify_name_0">
<source>Simplify name '{0}'</source>
<target state="translated">{0}' 이름 단순화</target>
......
......@@ -67,16 +67,6 @@
<target state="translated">Automatyczne zaznaczanie zostało wyłączone z powodu możliwej deklaracji zmiennej zakresu.</target>
<note />
</trans-unit>
<trans-unit id="Remove_Unnecessary_Cast">
<source>Remove Unnecessary Cast</source>
<target state="translated">Usuń niepotrzebne rzutowanie</target>
<note />
</trans-unit>
<trans-unit id="Cast_is_redundant">
<source>Cast is redundant</source>
<target state="translated">Nadmiarowe określenie elementu Cast</target>
<note />
</trans-unit>
<trans-unit id="Simplify_name_0">
<source>Simplify name '{0}'</source>
<target state="translated">Uprość nazwę „{0}”</target>
......
......@@ -67,16 +67,6 @@
<target state="translated">Seleção automática desabilitada devido a uma possível declaração de variável de intervalo.</target>
<note />
</trans-unit>
<trans-unit id="Remove_Unnecessary_Cast">
<source>Remove Unnecessary Cast</source>
<target state="translated">Remover Conversão Desnecessária</target>
<note />
</trans-unit>
<trans-unit id="Cast_is_redundant">
<source>Cast is redundant</source>
<target state="translated">A conversão é redundante</target>
<note />
</trans-unit>
<trans-unit id="Simplify_name_0">
<source>Simplify name '{0}'</source>
<target state="translated">Simplificar nome '{0}'</target>
......
......@@ -67,16 +67,6 @@
<target state="translated">Автовыбор отключен из-за возможного объявления переменной диапазона.</target>
<note />
</trans-unit>
<trans-unit id="Remove_Unnecessary_Cast">
<source>Remove Unnecessary Cast</source>
<target state="translated">Удалить ненужное приведение</target>
<note />
</trans-unit>
<trans-unit id="Cast_is_redundant">
<source>Cast is redundant</source>
<target state="translated">Приведение избыточно</target>
<note />
</trans-unit>
<trans-unit id="Simplify_name_0">
<source>Simplify name '{0}'</source>
<target state="translated">Упрощение имени "{0}"</target>
......
......@@ -67,16 +67,6 @@
<target state="translated">Otomatik seçim, olası aralık değişkeni bildirimi nedeniyle devre dışı bırakıldı.</target>
<note />
</trans-unit>
<trans-unit id="Remove_Unnecessary_Cast">
<source>Remove Unnecessary Cast</source>
<target state="translated">Gereksiz Atamayı Kaldır</target>
<note />
</trans-unit>
<trans-unit id="Cast_is_redundant">
<source>Cast is redundant</source>
<target state="translated">Atama gereksiz</target>
<note />
</trans-unit>
<trans-unit id="Simplify_name_0">
<source>Simplify name '{0}'</source>
<target state="translated">{0}' adını basitleştir</target>
......
......@@ -67,16 +67,6 @@
<target state="translated">由于可能出现范围变量声明,已禁用自动选择。</target>
<note />
</trans-unit>
<trans-unit id="Remove_Unnecessary_Cast">
<source>Remove Unnecessary Cast</source>
<target state="translated">删除不必要的转换</target>
<note />
</trans-unit>
<trans-unit id="Cast_is_redundant">
<source>Cast is redundant</source>
<target state="translated">强制转换是多余的</target>
<note />
</trans-unit>
<trans-unit id="Simplify_name_0">
<source>Simplify name '{0}'</source>
<target state="translated">简化名称“{0}”</target>
......
......@@ -67,16 +67,6 @@
<target state="translated">由於可能的範圍變數宣告,所以停用自動選取。</target>
<note />
</trans-unit>
<trans-unit id="Remove_Unnecessary_Cast">
<source>Remove Unnecessary Cast</source>
<target state="translated">移除不必要的 Cast</target>
<note />
</trans-unit>
<trans-unit id="Cast_is_redundant">
<source>Cast is redundant</source>
<target state="translated">Cast 是多餘的</target>
<note />
</trans-unit>
<trans-unit id="Simplify_name_0">
<source>Simplify name '{0}'</source>
<target state="translated">簡化名稱 '{0}'</target>
......
......@@ -3,76 +3,70 @@
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.Diagnostics.RemoveUnnecessaryCast
{
internal abstract class RemoveUnnecessaryCastDiagnosticAnalyzerBase<TLanguageKindEnum> : DiagnosticAnalyzer, IBuiltInAnalyzer where TLanguageKindEnum : struct
internal abstract class RemoveUnnecessaryCastDiagnosticAnalyzerBase<
TLanguageKindEnum,
TCastExpression> : AbstractCodeStyleDiagnosticAnalyzer
where TLanguageKindEnum : struct
where TCastExpression : SyntaxNode
{
private static readonly LocalizableString s_localizableTitle = new LocalizableResourceString(nameof(FeaturesResources.Remove_Unnecessary_Cast), FeaturesResources.ResourceManager, typeof(FeaturesResources));
private static readonly LocalizableString s_localizableMessage = new LocalizableResourceString(nameof(WorkspacesResources.Cast_is_redundant), WorkspacesResources.ResourceManager, typeof(WorkspacesResources));
protected RemoveUnnecessaryCastDiagnosticAnalyzerBase()
: base(IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId,
new LocalizableResourceString(nameof(FeaturesResources.Remove_Unnecessary_Cast), FeaturesResources.ResourceManager, typeof(FeaturesResources)),
new LocalizableResourceString(nameof(WorkspacesResources.Cast_is_redundant), WorkspacesResources.ResourceManager, typeof(WorkspacesResources)))
{
}
protected abstract ImmutableArray<TLanguageKindEnum> SyntaxKindsOfInterest { get; }
protected abstract TextSpan GetFadeSpan(TCastExpression node);
protected abstract bool IsUnnecessaryCast(SemanticModel model, TCastExpression node, CancellationToken cancellationToken);
private static readonly DiagnosticDescriptor s_descriptor = new DiagnosticDescriptor(IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId,
s_localizableTitle,
s_localizableMessage,
DiagnosticCategory.Style,
DiagnosticSeverity.Hidden,
isEnabledByDefault: true,
customTags: DiagnosticCustomTags.Unnecessary);
public override bool OpenFileOnly(Workspace workspace)
=> false;
#region Interface methods
public override DiagnosticAnalyzerCategory GetAnalyzerCategory()
=> DiagnosticAnalyzerCategory.SemanticSpanAnalysis;
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(s_descriptor);
public bool OpenFileOnly(Workspace workspace) => false;
protected override void InitializeWorker(AnalysisContext context)
=> context.RegisterSyntaxNodeAction(AnalyzeSyntax, this.SyntaxKindsOfInterest);
public sealed override void Initialize(AnalysisContext context)
private void AnalyzeSyntax(SyntaxNodeAnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
var diagnostic = TryRemoveCastExpression(
context.SemanticModel,
(TCastExpression)context.Node,
context.CancellationToken);
context.RegisterSyntaxNodeAction(
nodeContext =>
{
if (TryRemoveCastExpression(nodeContext.SemanticModel, nodeContext.Node, out var diagnostic, nodeContext.CancellationToken))
{
nodeContext.ReportDiagnostic(diagnostic);
}
},
this.SyntaxKindsOfInterest.ToArray());
if (diagnostic != null)
{
context.ReportDiagnostic(diagnostic);
}
}
public abstract ImmutableArray<TLanguageKindEnum> SyntaxKindsOfInterest { get; }
#endregion
protected abstract bool IsUnnecessaryCast(SemanticModel model, SyntaxNode node, CancellationToken cancellationToken);
protected abstract TextSpan GetDiagnosticSpan(SyntaxNode node);
private bool TryRemoveCastExpression(
SemanticModel model, SyntaxNode node, out Diagnostic diagnostic, CancellationToken cancellationToken)
private Diagnostic TryRemoveCastExpression(SemanticModel model, TCastExpression node, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
diagnostic = default;
if (!IsUnnecessaryCast(model, node, cancellationToken))
{
return false;
return null;
}
var tree = model.SyntaxTree;
var span = GetDiagnosticSpan(node);
if (tree.OverlapsHiddenPosition(span, cancellationToken))
if (tree.OverlapsHiddenPosition(node.Span, cancellationToken))
{
return false;
return null;
}
diagnostic = Diagnostic.Create(s_descriptor, tree.GetLocation(span));
return true;
return Diagnostic.Create(
UnnecessaryWithSuggestionDescriptor,
node.SyntaxTree.GetLocation(GetFadeSpan(node)),
ImmutableArray.Create(node.GetLocation()));
}
public DiagnosticAnalyzerCategory GetAnalyzerCategory()
=> DiagnosticAnalyzerCategory.SemanticSpanAnalysis;
}
}
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports System.Threading
Imports Microsoft.CodeAnalysis.CodeFixes
Imports Microsoft.CodeAnalysis.Options
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.RemoveUnnecessaryCast
Partial Friend Class RemoveUnnecessaryCastCodeFixProvider
Inherits CodeFixProvider
Private Class RemoveUnnecessaryCastFixAllProvider
Inherits BatchSimplificationFixAllProvider
Friend Shared Shadows ReadOnly Instance As RemoveUnnecessaryCastFixAllProvider = New RemoveUnnecessaryCastFixAllProvider()
Protected Overrides Function GetNodeToSimplify(root As SyntaxNode, model As SemanticModel, diagnostic As Diagnostic, options As DocumentOptionSet, ByRef codeActionId As String, cancellationToken As CancellationToken) As SyntaxNode
codeActionId = Nothing
Return GetCastNode(root, model, diagnostic.Location.SourceSpan, cancellationToken)
End Function
Protected Overrides ReadOnly Property NeedsParentFixup As Boolean
Get
Return True
End Get
End Property
Protected Overrides Async Function AddSimplifyAnnotationsAsync(document As Document, nodeToSimplify As SyntaxNode, cancellationToken As CancellationToken) As Task(Of Document)
Dim cast = TryCast(nodeToSimplify, ExpressionSyntax)
If cast Is Nothing Then
Return document
End If
Return Await RemoveUnnecessaryCastAsync(document, cast, cancellationToken).ConfigureAwait(False)
End Function
End Class
End Class
End Namespace
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.Simplification
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.RemoveUnnecessaryCast
Partial Friend Class RemoveUnnecessaryCastCodeFixProvider
Partial Private Class Rewriter
Inherits VisualBasicSyntaxRewriter
Private ReadOnly _castExpression As ExpressionSyntax
Public Sub New(castExpression As ExpressionSyntax)
_castExpression = castExpression
End Sub
Private Shared Function GetExpression(expression As ExpressionSyntax) As ExpressionSyntax
If TypeOf expression Is ParenthesizedExpressionSyntax Then
Return expression.WithAdditionalAnnotations(Simplifier.Annotation)
Else
Return expression
End If
End Function
Public Overrides Function VisitCTypeExpression(node As CTypeExpressionSyntax) As SyntaxNode
If node Is _castExpression Then
Return node.WithExpression(GetExpression(node.Expression)) _
.WithAdditionalAnnotations(Simplifier.Annotation) _
.Parenthesize()
End If
Return MyBase.VisitCTypeExpression(node)
End Function
Public Overrides Function VisitDirectCastExpression(node As DirectCastExpressionSyntax) As SyntaxNode
If node Is _castExpression Then
Return node.WithExpression(GetExpression(node.Expression)) _
.WithAdditionalAnnotations(Simplifier.Annotation) _
.Parenthesize()
End If
Return MyBase.VisitDirectCastExpression(node)
End Function
Public Overrides Function VisitTryCastExpression(node As TryCastExpressionSyntax) As SyntaxNode
If node Is _castExpression Then
Return node.WithExpression(GetExpression(node.Expression)) _
.WithAdditionalAnnotations(Simplifier.Annotation) _
.Parenthesize()
End If
Return MyBase.VisitTryCastExpression(node)
End Function
Public Overrides Function VisitPredefinedCastExpression(node As PredefinedCastExpressionSyntax) As SyntaxNode
If node Is _castExpression Then
Return node.WithExpression(GetExpression(node.Expression)) _
.WithAdditionalAnnotations(Simplifier.Annotation) _
.Parenthesize()
End If
Return MyBase.VisitPredefinedCastExpression(node)
End Function
End Class
End Class
End Namespace
......@@ -3,57 +3,31 @@
Imports System.Collections.Immutable
Imports System.Composition
Imports System.Threading
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.CodeActions
Imports Microsoft.CodeAnalysis.CodeFixes
Imports Microsoft.CodeAnalysis.Diagnostics
Imports Microsoft.CodeAnalysis.Editing
Imports Microsoft.CodeAnalysis.Formatting
Imports Microsoft.CodeAnalysis.Simplification
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.RemoveUnnecessaryCast
<ExportCodeFixProviderAttribute(LanguageNames.VisualBasic, Name:=PredefinedCodeFixProviderNames.RemoveUnnecessaryCast), [Shared]>
<ExportCodeFixProvider(LanguageNames.VisualBasic, Name:=PredefinedCodeFixProviderNames.RemoveUnnecessaryCast), [Shared]>
<ExtensionOrder(After:=PredefinedCodeFixProviderNames.GenerateEndConstruct)>
Partial Friend Class RemoveUnnecessaryCastCodeFixProvider
Inherits CodeFixProvider
Inherits SyntaxEditorBasedCodeFixProvider
Public NotOverridable Overrides ReadOnly Property FixableDiagnosticIds As ImmutableArray(Of String)
Get
Return ImmutableArray.Create(IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)
End Get
End Property
Public NotOverridable Overrides ReadOnly Property FixableDiagnosticIds As ImmutableArray(Of String) =
ImmutableArray.Create(IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)
Public NotOverridable Overrides Async Function RegisterCodeFixesAsync(context As CodeFixContext) As Task
Dim document = context.Document
Dim span = context.Span
Dim cancellationToken = context.CancellationToken
Dim root = Await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(False)
Dim model = DirectCast(Await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(False), SemanticModel)
Dim node = GetCastNode(root, model, span, cancellationToken)
If node Is Nothing Then
Return
End If
context.RegisterCodeFix(
New MyCodeAction(
VBFeaturesResources.Remove_Unnecessary_Cast,
Function(c) RemoveUnnecessaryCastAsync(document, node, c)),
Public Overrides Function RegisterCodeFixesAsync(context As CodeFixContext) As Task
context.RegisterCodeFix(New MyCodeAction(
FeaturesResources.Remove_Unnecessary_Cast,
Function(c) FixAsync(context.Document, context.Diagnostics.First(), c)),
context.Diagnostics)
End Function
Private Shared Function GetCastNode(root As SyntaxNode, model As SemanticModel, span As TextSpan, cancellationToken As CancellationToken) As ExpressionSyntax
Dim token = root.FindToken(span.Start)
If Not token.Span.IntersectsWith(span) Then
Return Nothing
End If
Dim node = token.GetAncestors(Of ExpressionSyntax)() _
.Where(Function(c) TypeOf c Is CastExpressionSyntax OrElse TypeOf c Is PredefinedCastExpressionSyntax) _
.FirstOrDefault(Function(c) c.Span.IntersectsWith(span) AndAlso IsUnnecessaryCast(c, model, cancellationToken))
Return node
Return SpecializedTasks.EmptyTask
End Function
Private Shared Function IsUnnecessaryCast(node As ExpressionSyntax, model As SemanticModel, cancellationToken As CancellationToken) As Boolean
......@@ -70,117 +44,116 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.RemoveUnnecessaryCast
Return False
End Function
Private Shared Async Function RemoveUnnecessaryCastAsync(document As Document, node As ExpressionSyntax, cancellationToken As CancellationToken) As Task(Of Document)
' First, annotate our expression so that we can get back to it.
Dim updatedDocument = Await document.ReplaceNodeAsync(node, node.WithAdditionalAnnotations(s_expressionAnnotation), cancellationToken).ConfigureAwait(False)
Dim expression = Await FindNodeWithAnnotationAsync(Of ExpressionSyntax)(s_expressionAnnotation, updatedDocument, cancellationToken).ConfigureAwait(False)
' Next, make the parenting statement of the expression semantically explicit
Dim parentStatement = expression.FirstAncestorOrSelf(Of StatementSyntax)()
Dim explicitParentStatement = Await Simplifier.ExpandAsync(parentStatement, updatedDocument, cancellationToken:=cancellationToken).ConfigureAwait(False)
explicitParentStatement = explicitParentStatement.WithAdditionalAnnotations(Formatter.Annotation, s_statementAnnotation)
Protected Overrides Async Function FixAllAsync(
document As Document, diagnostics As ImmutableArray(Of Diagnostic),
editor As SyntaxEditor, cancellationToken As CancellationToken) As Task
' VB parsing is extremely hairy. Unlike C#, it can be very dangerous to go and remove a
' cast. For example, if the cast is at the statement level, it may contain an
' expression that itself is not legal on its own at the top level (see below for an
' example of this). Similarly, removing the cast may make VB parse following code
' differently.
'
' In order to deal with all these concerns safely, we first complexify the surrounding
' statements containing the casts we want to remove. *Then* we remove the casts from
' inside that.
'
' As an example, consider: DirectCast(New Goo(), IGoo).Blah() This is
' legal code, but this is not: New Goo().Blah()
'
' (because 'new' cannot start a statement).
' So we need to instead generate: Call New Goo().Blah()
Dim originalCastNodes = diagnostics.SelectAsArray(
Function(d) DirectCast(d.AdditionalLocations(0).FindNode(getInnermostNodeForTie:=True, cancellationToken), ExpressionSyntax))
' Keep track of the all the casts we want to fix up. We'll fix them up at the end
' after we've done all other manipulation.
Dim trackedRoot = editor.OriginalRoot.TrackNodes(originalCastNodes)
Dim trackedDocument = document.WithSyntaxRoot(trackedRoot)
' Now, go and expand all the containing statements of the nodes we want to edit.
' This is necessary to ensure that the code remains parseable and preserves semantics.
Dim expandedRoot = Await ExpandSurroundingStatementsAsync(trackedDocument, originalCastNodes, cancellationToken).ConfigureAwait(False)
Dim expandedDocument = document.WithSyntaxRoot(expandedRoot)
Dim removedRoot = Await RemoveCasts(
expandedDocument, originalCastNodes, cancellationToken).ConfigureAwait(False)
editor.ReplaceNode(editor.OriginalRoot, removedRoot)
End Function
updatedDocument = Await updatedDocument.ReplaceNodeAsync(parentStatement, explicitParentStatement, cancellationToken).ConfigureAwait(False)
Private Async Function RemoveCasts(
document As Document, originalCastNodes As ImmutableArray(Of ExpressionSyntax),
cancellationToken As CancellationToken) As Task(Of SyntaxNode)
' Next, make the statement after the parenting statement of the expression semantically explicit.
parentStatement = Await FindNodeWithAnnotationAsync(Of StatementSyntax)(s_statementAnnotation, updatedDocument, cancellationToken).ConfigureAwait(False)
Dim nextStatement = parentStatement.GetNextStatement()
If nextStatement IsNot Nothing Then
Dim explicitNextStatement = Await Simplifier.ExpandAsync(nextStatement, updatedDocument, cancellationToken:=cancellationToken).ConfigureAwait(False)
updatedDocument = Await updatedDocument.ReplaceNodeAsync(nextStatement, explicitNextStatement, cancellationToken).ConfigureAwait(False)
End If
Dim root = Await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(False)
updatedDocument = Await RewriteCoreAsync(updatedDocument, expression, cancellationToken).ConfigureAwait(False)
' Now, find the cast nodes again in the expanded document
Dim currentCastNodes = root.GetCurrentNodes(originalCastNodes)
' Remove added _expressionAnnotation and _statementAnnotation.
updatedDocument = Await RemoveNodesAndTokensWithAnnotationAsync(s_expressionAnnotation, updatedDocument, cancellationToken).ConfigureAwait(False)
updatedDocument = Await RemoveNodesAndTokensWithAnnotationAsync(s_statementAnnotation, updatedDocument, cancellationToken).ConfigureAwait(False)
Dim innerEditor = New SyntaxEditor(root, document.Project.Solution.Workspace)
Await innerEditor.ApplyExpressionLevelSemanticEditsAsync(
document, currentCastNodes.ToImmutableArray(),
Function(semanticModel, castExpression) IsUnnecessaryCast(castExpression, semanticModel, cancellationToken),
Function(unused, currentRoot, castExpression)
Dim newCastExpression = Uncast(castExpression).WithAdditionalAnnotations(Formatter.Annotation)
Return currentRoot.ReplaceNode(castExpression, newCastExpression)
End Function,
cancellationToken).ConfigureAwait(False)
Return updatedDocument
Return innerEditor.GetChangedRoot()
End Function
Private Shared Async Function RemoveNodesAndTokensWithAnnotationAsync(annotation As SyntaxAnnotation, document As Document, cancellationToken As CancellationToken) As Task(Of Document)
Private Shared Async Function ExpandSurroundingStatementsAsync(
document As Document, originalNodes As ImmutableArray(Of ExpressionSyntax),
cancellationToken As CancellationToken) As Task(Of SyntaxNode)
Dim semanticModel = Await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(False)
Dim root = Await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(False)
Dim nodesWithAnnotation = Await FindNodesWithAnnotationAsync(annotation, document, cancellationToken).ConfigureAwait(False)
root = root.ReplaceSyntax(
nodesWithAnnotation.Where(Function(n) n.IsNode).Select(Function(n) n.AsNode),
Function(o, n) o.WithoutAnnotations(annotation),
nodesWithAnnotation.Where(Function(n) n.IsToken).Select(Function(n) n.AsToken),
Function(o, n) o.WithoutAnnotations(annotation),
SpecializedCollections.EmptyEnumerable(Of SyntaxTrivia),
Nothing)
Return document.WithSyntaxRoot(root)
End Function
Private Shared Async Function RewriteCoreAsync(document As Document, originalExpr As ExpressionSyntax, cancellationToken As CancellationToken) As Task(Of Document)
' Finally, rewrite the cast expression
Dim exprToRewrite As ExpressionSyntax = Nothing
Dim annotatedNodes = Await FindNodesWithAnnotationAsync(s_expressionAnnotation, document, cancellationToken).ConfigureAwait(False)
For Each annotatedNode In annotatedNodes
exprToRewrite = TryCast(annotatedNode.AsNode, ExpressionSyntax)
If exprToRewrite IsNot Nothing AndAlso exprToRewrite.IsKind(originalExpr.Kind) Then
If annotatedNodes.Count > 1 Then
' Ensure cast is unnecessary
Dim model = Await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(False)
If IsUnnecessaryCast(exprToRewrite, model, cancellationToken) Then
Exit For
End If
Else
Exit For
End If
End If
exprToRewrite = Nothing
' Note: we not only get the containing statement, but also the next statement after
' that. That's because the removal of the parens in the cast may then cause parsing
' problems with VB consuming the following line into the current line. This is most
' common with query clauses. By complexifying the next statement, we prevent that from
' happening.
Dim trackedNodes = root.GetCurrentNodes(originalNodes)
Dim containingAndNextStatements = trackedNodes.SelectMany(
Function(n)
Dim containingStatement = n.GetAncestorOrThis(Of StatementSyntax)
Dim nextStatement = containingStatement.GetNextStatement()
Return If(nextStatement Is Nothing,
{containingStatement},
{containingStatement, nextStatement})
End Function).Distinct()
Dim workspace = document.Project.Solution.Workspace
Dim editor = New SyntaxEditor(root, workspace)
For Each containingStatement In containingAndNextStatements
Dim expandedStatement = Simplifier.Expand(
containingStatement, semanticModel, workspace,
cancellationToken:=cancellationToken)
editor.ReplaceNode(containingStatement, expandedStatement)
Next
If exprToRewrite Is Nothing Then
Return document
End If
Dim rewriter = New Rewriter(exprToRewrite)
Dim newExpression = rewriter.Visit(exprToRewrite)
' Remove the annotation from the expression so that it isn't hanging around later.
If newExpression.HasAnnotation(s_expressionAnnotation) Then
newExpression = newExpression.WithoutAnnotations(s_expressionAnnotation)
ElseIf newExpression.IsKind(SyntaxKind.ParenthesizedExpression) Then
Dim parenthesizedExpression = DirectCast(newExpression, ParenthesizedExpressionSyntax)
If parenthesizedExpression.Expression.HasAnnotation(s_expressionAnnotation) Then
newExpression = parenthesizedExpression _
.WithExpression(parenthesizedExpression.Expression.WithoutAnnotations(s_expressionAnnotation))
End If
Return editor.GetChangedRoot()
End Function
Private Function Uncast(old As ExpressionSyntax) As ExpressionSyntax
' parenthesize the uncasted value to help ensure any proper parsing. The excess
' parens will be removed if unnecessary.
Dim castExpression = TryCast(old, CastExpressionSyntax)
If castExpression IsNot Nothing Then
Return castExpression.Uncast().Parenthesize()
End If
document = Await document.ReplaceNodeAsync(exprToRewrite, newExpression, cancellationToken).ConfigureAwait(False)
If annotatedNodes.Count > 1 Then
Return Await RewriteCoreAsync(document, originalExpr, cancellationToken).ConfigureAwait(False)
Dim predefinedCastExpression = TryCast(old, PredefinedCastExpressionSyntax)
If predefinedCastExpression IsNot Nothing Then
Return predefinedCastExpression.Uncast().Parenthesize()
End If
Return document
End Function
Private Shared ReadOnly s_expressionAnnotation As New SyntaxAnnotation
Private Shared ReadOnly s_statementAnnotation As New SyntaxAnnotation
Private Shared Async Function FindNodeWithAnnotationAsync(Of T As SyntaxNode)(annotation As SyntaxAnnotation, document As Document, cancellationToken As CancellationToken) As Task(Of T)
Dim annotatedNodes = Await FindNodesWithAnnotationAsync(annotation, document, cancellationToken).ConfigureAwait(False)
Dim result = annotatedNodes.Single().AsNode()
Return DirectCast(result, T)
End Function
Private Shared Async Function FindNodesWithAnnotationAsync(annotation As SyntaxAnnotation, document As Document, cancellationToken As CancellationToken) As Task(Of IEnumerable(Of SyntaxNodeOrToken))
Dim root = Await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(False)
Return root.GetAnnotatedNodesAndTokens(annotation)
End Function
Public NotOverridable Overrides Function GetFixAllProvider() As FixAllProvider
Return RemoveUnnecessaryCastFixAllProvider.Instance
Throw ExceptionUtilities.UnexpectedValue(old)
End Function
Private Class MyCodeAction
......
......@@ -11,19 +11,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Diagnostics.RemoveUnnecessaryCast
<DiagnosticAnalyzer(LanguageNames.VisualBasic)>
Friend NotInheritable Class VisualBasicRemoveUnnecessaryCastDiagnosticAnalyzer
Inherits RemoveUnnecessaryCastDiagnosticAnalyzerBase(Of SyntaxKind)
Inherits RemoveUnnecessaryCastDiagnosticAnalyzerBase(Of SyntaxKind, ExpressionSyntax)
Private Shared ReadOnly s_kindsOfInterest As ImmutableArray(Of SyntaxKind) = ImmutableArray.Create(SyntaxKind.CTypeExpression,
SyntaxKind.DirectCastExpression,
SyntaxKind.TryCastExpression,
SyntaxKind.PredefinedCastExpression)
Public Overrides ReadOnly Property SyntaxKindsOfInterest As ImmutableArray(Of SyntaxKind)
Get
Return s_kindsOfInterest
End Get
End Property
Protected Overrides ReadOnly Property SyntaxKindsOfInterest As ImmutableArray(Of SyntaxKind) =
ImmutableArray.Create(SyntaxKind.CTypeExpression,
SyntaxKind.DirectCastExpression,
SyntaxKind.TryCastExpression,
SyntaxKind.PredefinedCastExpression)
Protected Overrides Function IsUnnecessaryCast(model As SemanticModel, node As SyntaxNode, cancellationToken As CancellationToken) As Boolean
Protected Overrides Function IsUnnecessaryCast(model As SemanticModel, node As ExpressionSyntax, cancellationToken As CancellationToken) As Boolean
Select Case node.Kind
Case SyntaxKind.CTypeExpression, SyntaxKind.DirectCastExpression, SyntaxKind.TryCastExpression
Return DirectCast(node, CastExpressionSyntax).IsUnnecessaryCast(model, assumeCallKeyword:=True, cancellationToken:=cancellationToken)
......@@ -34,15 +30,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Diagnostics.RemoveUnnecessaryCast
End Select
End Function
Protected Overrides Function GetDiagnosticSpan(node As SyntaxNode) As TextSpan
Select Case node.Kind
Case SyntaxKind.CTypeExpression, SyntaxKind.DirectCastExpression, SyntaxKind.TryCastExpression
Return DirectCast(node, CastExpressionSyntax).Keyword.Span
Case SyntaxKind.PredefinedCastExpression
Return DirectCast(node, PredefinedCastExpressionSyntax).Keyword.Span
Case Else
Throw ExceptionUtilities.UnexpectedValue(node.Kind)
End Select
Protected Overrides Function GetFadeSpan(node As ExpressionSyntax) As TextSpan
Return node.GetFirstToken().Span
End Function
End Class
End Namespace
......@@ -240,15 +240,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.VBFeaturesResources
End Get
End Property
'''<summary>
''' Looks up a localized string similar to Cast is redundant.
'''</summary>
Friend ReadOnly Property Cast_is_redundant() As String
Get
Return ResourceManager.GetString("Cast_is_redundant", resourceCulture)
End Get
End Property
'''<summary>
''' Looks up a localized string similar to Catch clause.
'''</summary>
......@@ -2009,15 +2000,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.VBFeaturesResources
End Get
End Property
'''<summary>
''' Looks up a localized string similar to Remove Unnecessary Cast.
'''</summary>
Friend ReadOnly Property Remove_Unnecessary_Cast() As String
Get
Return ResourceManager.GetString("Remove_Unnecessary_Cast", resourceCulture)
End Get
End Property
'''<summary>
''' Looks up a localized string similar to Remove Unnecessary Imports.
'''</summary>
......
......@@ -213,12 +213,6 @@
<data name="Fix_Incorrect_Function_Return_Type" xml:space="preserve">
<value>Fix Incorrect Function Return Type</value>
</data>
<data name="Remove_Unnecessary_Cast" xml:space="preserve">
<value>Remove Unnecessary Cast</value>
</data>
<data name="Cast_is_redundant" xml:space="preserve">
<value>Cast is redundant</value>
</data>
<data name="Simplify_name_0" xml:space="preserve">
<value>Simplify name '{0}'</value>
</data>
......
......@@ -157,16 +157,6 @@
<target state="translated">Opravit nesprávný návratový typ funkce</target>
<note />
</trans-unit>
<trans-unit id="Remove_Unnecessary_Cast">
<source>Remove Unnecessary Cast</source>
<target state="translated">Odebrat nepotřebné přetypování</target>
<note />
</trans-unit>
<trans-unit id="Cast_is_redundant">
<source>Cast is redundant</source>
<target state="translated">Přetypování je redundantní.</target>
<note />
</trans-unit>
<trans-unit id="Simplify_name_0">
<source>Simplify name '{0}'</source>
<target state="translated">Zjednodušit název {0}</target>
......
......@@ -157,16 +157,6 @@
<target state="translated">Rückgabetyp für unzulässige Funktion beheben</target>
<note />
</trans-unit>
<trans-unit id="Remove_Unnecessary_Cast">
<source>Remove Unnecessary Cast</source>
<target state="translated">Nicht erforderliche Umwandlung entfernen</target>
<note />
</trans-unit>
<trans-unit id="Cast_is_redundant">
<source>Cast is redundant</source>
<target state="translated">Cast ist redundant</target>
<note />
</trans-unit>
<trans-unit id="Simplify_name_0">
<source>Simplify name '{0}'</source>
<target state="translated">Name '{0}' vereinfachen</target>
......
......@@ -157,16 +157,6 @@
<target state="translated">Corregir tipo devuelto de función incorrecto</target>
<note />
</trans-unit>
<trans-unit id="Remove_Unnecessary_Cast">
<source>Remove Unnecessary Cast</source>
<target state="translated">Quitar conversión innecesaria</target>
<note />
</trans-unit>
<trans-unit id="Cast_is_redundant">
<source>Cast is redundant</source>
<target state="translated">Cast es redundante</target>
<note />
</trans-unit>
<trans-unit id="Simplify_name_0">
<source>Simplify name '{0}'</source>
<target state="translated">Simplificar nombre '{0}'</target>
......
......@@ -157,16 +157,6 @@
<target state="translated">Corriger le type de retour de fonction incorrect</target>
<note />
</trans-unit>
<trans-unit id="Remove_Unnecessary_Cast">
<source>Remove Unnecessary Cast</source>
<target state="translated">Supprimer le cast inutile</target>
<note />
</trans-unit>
<trans-unit id="Cast_is_redundant">
<source>Cast is redundant</source>
<target state="translated">Cast redondant</target>
<note />
</trans-unit>
<trans-unit id="Simplify_name_0">
<source>Simplify name '{0}'</source>
<target state="translated">Simplifier le nom '{0}'</target>
......
......@@ -157,16 +157,6 @@
<target state="translated">Correggi tipo errato restituito da funzione</target>
<note />
</trans-unit>
<trans-unit id="Remove_Unnecessary_Cast">
<source>Remove Unnecessary Cast</source>
<target state="translated">Rimuovi cast non necessario</target>
<note />
</trans-unit>
<trans-unit id="Cast_is_redundant">
<source>Cast is redundant</source>
<target state="translated">Il cast è ridondante</target>
<note />
</trans-unit>
<trans-unit id="Simplify_name_0">
<source>Simplify name '{0}'</source>
<target state="translated">Semplifica il nome '{0}'</target>
......
......@@ -157,16 +157,6 @@
<target state="translated">無効な関数の戻り値の型を修正する</target>
<note />
</trans-unit>
<trans-unit id="Remove_Unnecessary_Cast">
<source>Remove Unnecessary Cast</source>
<target state="translated">不要なキャストの削除</target>
<note />
</trans-unit>
<trans-unit id="Cast_is_redundant">
<source>Cast is redundant</source>
<target state="translated">キャストが冗長です</target>
<note />
</trans-unit>
<trans-unit id="Simplify_name_0">
<source>Simplify name '{0}'</source>
<target state="translated">名前 '{0}' の単純化</target>
......
......@@ -157,16 +157,6 @@
<target state="translated">잘못된 함수 반환 형식 수정</target>
<note />
</trans-unit>
<trans-unit id="Remove_Unnecessary_Cast">
<source>Remove Unnecessary Cast</source>
<target state="translated">불필요한 캐스트 제거</target>
<note />
</trans-unit>
<trans-unit id="Cast_is_redundant">
<source>Cast is redundant</source>
<target state="translated">캐스팅이 중복됩니다.</target>
<note />
</trans-unit>
<trans-unit id="Simplify_name_0">
<source>Simplify name '{0}'</source>
<target state="translated">{0}' 이름 단순화</target>
......
......@@ -157,16 +157,6 @@
<target state="translated">Napraw nieprawidłowy typ zwracany funkcji</target>
<note />
</trans-unit>
<trans-unit id="Remove_Unnecessary_Cast">
<source>Remove Unnecessary Cast</source>
<target state="translated">Usuń niepotrzebne rzutowanie</target>
<note />
</trans-unit>
<trans-unit id="Cast_is_redundant">
<source>Cast is redundant</source>
<target state="translated">Nadmiarowe określenie elementu Cast</target>
<note />
</trans-unit>
<trans-unit id="Simplify_name_0">
<source>Simplify name '{0}'</source>
<target state="translated">Uprość nazwę „{0}”</target>
......
......@@ -157,16 +157,6 @@
<target state="translated">Corrigir Tipo de Retorno de Função Incorreta</target>
<note />
</trans-unit>
<trans-unit id="Remove_Unnecessary_Cast">
<source>Remove Unnecessary Cast</source>
<target state="translated">Remover Conversão Desnecessária</target>
<note />
</trans-unit>
<trans-unit id="Cast_is_redundant">
<source>Cast is redundant</source>
<target state="translated">A conversão é redundante</target>
<note />
</trans-unit>
<trans-unit id="Simplify_name_0">
<source>Simplify name '{0}'</source>
<target state="translated">Simplificar nome '{0}'</target>
......
......@@ -157,16 +157,6 @@
<target state="translated">Исправить неверный тип возвращения функции</target>
<note />
</trans-unit>
<trans-unit id="Remove_Unnecessary_Cast">
<source>Remove Unnecessary Cast</source>
<target state="translated">Удалить ненужное приведение</target>
<note />
</trans-unit>
<trans-unit id="Cast_is_redundant">
<source>Cast is redundant</source>
<target state="translated">Приведение избыточно</target>
<note />
</trans-unit>
<trans-unit id="Simplify_name_0">
<source>Simplify name '{0}'</source>
<target state="translated">Упрощение имени "{0}"</target>
......
......@@ -157,16 +157,6 @@
<target state="translated">Hatalı İşlev Dönüş Türünü Düzelt</target>
<note />
</trans-unit>
<trans-unit id="Remove_Unnecessary_Cast">
<source>Remove Unnecessary Cast</source>
<target state="translated">Gereksiz Atamayı Kaldır</target>
<note />
</trans-unit>
<trans-unit id="Cast_is_redundant">
<source>Cast is redundant</source>
<target state="translated">Atama gereksiz</target>
<note />
</trans-unit>
<trans-unit id="Simplify_name_0">
<source>Simplify name '{0}'</source>
<target state="translated">{0}' adını basitleştir</target>
......
......@@ -157,16 +157,6 @@
<target state="translated">修复不正确的函数返回类型</target>
<note />
</trans-unit>
<trans-unit id="Remove_Unnecessary_Cast">
<source>Remove Unnecessary Cast</source>
<target state="translated">删除不必要的转换</target>
<note />
</trans-unit>
<trans-unit id="Cast_is_redundant">
<source>Cast is redundant</source>
<target state="translated">强制转换是多余的</target>
<note />
</trans-unit>
<trans-unit id="Simplify_name_0">
<source>Simplify name '{0}'</source>
<target state="translated">简化名称“{0}”</target>
......
......@@ -157,16 +157,6 @@
<target state="translated">修正不正確的函式傳回類型</target>
<note />
</trans-unit>
<trans-unit id="Remove_Unnecessary_Cast">
<source>Remove Unnecessary Cast</source>
<target state="translated">移除不必要的 Cast</target>
<note />
</trans-unit>
<trans-unit id="Cast_is_redundant">
<source>Cast is redundant</source>
<target state="translated">Cast 是多餘的</target>
<note />
</trans-unit>
<trans-unit id="Simplify_name_0">
<source>Simplify name '{0}'</source>
<target state="translated">簡化名稱 '{0}'</target>
......
// 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.Diagnostics;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Utilities;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Simplification;
namespace Microsoft.CodeAnalysis.CSharp.Extensions
{
......@@ -621,5 +623,28 @@ private static bool IsRequiredImplicitNumericConversion(ITypeSymbol sourceType,
return false;
}
}
public static ExpressionSyntax Uncast(this CastExpressionSyntax node)
{
var leadingTrivia = node.OpenParenToken.LeadingTrivia
.Concat(node.OpenParenToken.TrailingTrivia)
.Concat(node.Type.GetLeadingTrivia())
.Concat(node.Type.GetTrailingTrivia())
.Concat(node.CloseParenToken.LeadingTrivia)
.Concat(node.CloseParenToken.TrailingTrivia)
.Concat(node.Expression.GetLeadingTrivia())
.Where(t => !t.IsElastic());
var trailingTrivia = node.GetTrailingTrivia().Where(t => !t.IsElastic());
var resultNode = node.Expression
.WithLeadingTrivia(leadingTrivia)
.WithTrailingTrivia(trailingTrivia)
.WithAdditionalAnnotations(Simplifier.Annotation);
resultNode = SimplificationHelpers.CopyAnnotations(from: node, to: resultNode);
return resultNode;
}
}
}
......@@ -31,24 +31,7 @@ private static ExpressionSyntax SimplifyCast(CastExpressionSyntax node, Semantic
return node;
}
var leadingTrivia = node.OpenParenToken.LeadingTrivia
.Concat(node.OpenParenToken.TrailingTrivia)
.Concat(node.Type.GetLeadingTrivia())
.Concat(node.Type.GetTrailingTrivia())
.Concat(node.CloseParenToken.LeadingTrivia)
.Concat(node.CloseParenToken.TrailingTrivia)
.Concat(node.Expression.GetLeadingTrivia())
.Where(t => !t.IsElastic());
var trailingTrivia = node.GetTrailingTrivia().Where(t => !t.IsElastic());
var resultNode = node.Expression
.WithLeadingTrivia(leadingTrivia)
.WithTrailingTrivia(trailingTrivia);
resultNode = SimplificationHelpers.CopyAnnotations(from: node, to: resultNode);
return resultNode;
return node.Uncast();
}
}
}
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports System.Runtime.CompilerServices
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions
......
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports System.Runtime.CompilerServices
Imports Microsoft.CodeAnalysis.Simplification
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions
Friend Module CastExpressionSyntaxExtensions
<Extension>
Public Function Uncast(cast As CastExpressionSyntax) As ExpressionSyntax
Return Uncast(cast, cast.Expression)
End Function
<Extension>
Public Function Uncast(cast As PredefinedCastExpressionSyntax) As ExpressionSyntax
Return Uncast(cast, cast.Expression)
End Function
Private Function Uncast(castNode As ExpressionSyntax, innerNode As ExpressionSyntax) As ExpressionSyntax
Dim resultNode = innerNode.WithTriviaFrom(castNode)
resultNode = SimplificationHelpers.CopyAnnotations(castNode, resultNode)
Return resultNode
End Function
End Module
End Namespace
......@@ -17,22 +17,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification
MyBase.New(s_pool)
End Sub
Private Overloads Shared Function SimplifyCast(
castNode As ExpressionSyntax,
innerNode As ExpressionSyntax,
optionSet As OptionSet,
cancellationToken As CancellationToken
) As ExpressionSyntax
Dim resultNode = innerNode _
.WithLeadingTrivia(castNode.GetLeadingTrivia()) _
.WithTrailingTrivia(castNode.GetTrailingTrivia())
resultNode = SimplificationHelpers.CopyAnnotations(castNode, resultNode)
Return resultNode
End Function
Private Shared ReadOnly s_simplifyCast As Func(Of CastExpressionSyntax, SemanticModel, OptionSet, CancellationToken, SyntaxNode) = AddressOf SimplifyCast
Private Overloads Shared Function SimplifyCast(
......@@ -46,7 +30,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification
Return node
End If
Return SimplifyCast(node, node.Expression, optionSet, cancellationToken)
Return node.Uncast()
End Function
Private Shared ReadOnly s_simplifyPredefinedCast As Func(Of PredefinedCastExpressionSyntax, SemanticModel, OptionSet, CancellationToken, SyntaxNode) = AddressOf SimplifyPredefinedCast
......@@ -62,7 +46,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification
Return node
End If
Return SimplifyCast(node, node.Expression, optionSet, cancellationToken)
Return node.Uncast()
End Function
End Class
End Namespace
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册