未验证 提交 9cc5798b 编写于 作者: D David 提交者: GitHub

Merge pull request #45546 from dotnet/dev/dibarbet/merge_168-vs-deps

Merge 16.8 preview 1 vs-deps back into master-vs-deps
......@@ -115,6 +115,7 @@
<MicrosoftVisualStudioGraphModelVersion>16.0.28226-alpha</MicrosoftVisualStudioGraphModelVersion>
<MicrosoftVisualStudioImageCatalogVersion>16.6.29925.50-pre</MicrosoftVisualStudioImageCatalogVersion>
<MicrosoftVisualStudioImagingVersion>16.6.29925.50-pre</MicrosoftVisualStudioImagingVersion>
<MicrosoftVisualStudioImagingInterop140DesignTimeVersion>15.0.25726-preview5</MicrosoftVisualStudioImagingInterop140DesignTimeVersion>
<MicrosoftVisualStudioInteractiveWindowVersion>2.8.0</MicrosoftVisualStudioInteractiveWindowVersion>
<MicrosoftVisualStudioLanguageVersion>$(VisualStudioEditorPackagesVersion)</MicrosoftVisualStudioLanguageVersion>
<MicrosoftVisualStudioLanguageCallHierarchyVersion>15.8.27812-alpha</MicrosoftVisualStudioLanguageCallHierarchyVersion>
......
......@@ -88,6 +88,9 @@
<Compile Include="$(MSBuildThisFileDirectory)UseLocalFunction\CSharpUseLocalFunctionDiagnosticAnalyzer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)UseNullPropagation\CSharpUseNullPropagationDiagnosticAnalyzer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)UseObjectInitializer\CSharpUseObjectInitializerDiagnosticAnalyzer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)UsePatternCombinators\AnalyzedPattern.cs" />
<Compile Include="$(MSBuildThisFileDirectory)UsePatternCombinators\CSharpUsePatternCombinatorsAnalyzer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)UsePatternCombinators\CSharpUsePatternCombinatorsDiagnosticAnalyzer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)UsePatternMatching\CSharpAsAndNullCheckDiagnosticAnalyzer.Analyzer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)UsePatternMatching\CSharpAsAndNullCheckDiagnosticAnalyzer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)UsePatternMatching\CSharpIsAndCastCheckDiagnosticAnalyzer.cs" />
......
// 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.
#nullable enable
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Operations;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp.UsePatternCombinators
{
/// <summary>
/// Base class to represent a pattern constructed from various checks
/// </summary>
internal abstract class AnalyzedPattern
{
public readonly IOperation Target;
private AnalyzedPattern(IOperation target)
=> Target = target;
/// <summary>
/// Represents a type-pattern, constructed from an is-expression
/// </summary>
internal sealed class Type : AnalyzedPattern
{
public readonly TypeSyntax TypeSyntax;
public Type(TypeSyntax type, IOperation target) : base(target)
=> TypeSyntax = type;
}
/// <summary>
/// Represents a source-pattern, constructed from C# patterns
/// </summary>
internal sealed class Source : AnalyzedPattern
{
public readonly PatternSyntax PatternSyntax;
public Source(PatternSyntax patternSyntax, IOperation target) : base(target)
=> PatternSyntax = patternSyntax;
}
/// <summary>
/// Represents a constant-pattern, constructed from an equality check
/// </summary>
internal sealed class Constant : AnalyzedPattern
{
public readonly ExpressionSyntax ExpressionSyntax;
public Constant(ExpressionSyntax expression, IOperation target) : base(target)
=> ExpressionSyntax = expression;
}
/// <summary>
/// Represents a relational-pattern, constructed from relational operators
/// </summary>
internal sealed class Relational : AnalyzedPattern
{
public readonly BinaryOperatorKind OperatorKind;
public readonly ExpressionSyntax Value;
public Relational(BinaryOperatorKind operatorKind, ExpressionSyntax value, IOperation target) : base(target)
{
OperatorKind = operatorKind;
Value = value;
}
}
/// <summary>
/// Represents an and/or pattern, constructed from a logical and/or expression.
/// </summary>
internal sealed class Binary : AnalyzedPattern
{
public readonly AnalyzedPattern Left;
public readonly AnalyzedPattern Right;
public readonly bool IsDisjunctive;
public readonly SyntaxToken Token;
private Binary(AnalyzedPattern leftPattern, AnalyzedPattern rightPattern, bool isDisjunctive, SyntaxToken token, IOperation target) : base(target)
{
Left = leftPattern;
Right = rightPattern;
IsDisjunctive = isDisjunctive;
Token = token;
}
public static AnalyzedPattern? TryCreate(AnalyzedPattern leftPattern, AnalyzedPattern rightPattern, bool isDisjunctive, SyntaxToken token)
{
var target = leftPattern.Target;
if (!SyntaxFactory.AreEquivalent(target.Syntax, rightPattern.Target.Syntax))
return null;
return new Binary(leftPattern, rightPattern, isDisjunctive, token, target);
}
}
/// <summary>
/// Represents a not-pattern, constructed from inequality check or a logical-not expression.
/// </summary>
internal sealed class Not : AnalyzedPattern
{
public readonly AnalyzedPattern Pattern;
private Not(AnalyzedPattern pattern, IOperation target) : base(target)
=> Pattern = pattern;
private static BinaryOperatorKind Negate(BinaryOperatorKind kind)
{
return kind switch
{
BinaryOperatorKind.LessThan => BinaryOperatorKind.GreaterThanOrEqual,
BinaryOperatorKind.GreaterThan => BinaryOperatorKind.LessThanOrEqual,
BinaryOperatorKind.LessThanOrEqual => BinaryOperatorKind.GreaterThan,
BinaryOperatorKind.GreaterThanOrEqual => BinaryOperatorKind.LessThan,
var v => throw ExceptionUtilities.UnexpectedValue(v)
};
}
public static AnalyzedPattern? TryCreate(AnalyzedPattern? pattern)
{
return pattern switch
{
null => null,
Not p => p.Pattern, // Avoid double negative
Relational p => new Relational(Negate(p.OperatorKind), p.Value, p.Target),
Binary { Left: Not left, Right: Not right } p // Apply demorgans's law
=> Binary.TryCreate(left.Pattern, right.Pattern, !p.IsDisjunctive, p.Token),
_ => new Not(pattern, pattern.Target)
};
}
}
}
}
// 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.
#nullable enable
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Operations;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp.UsePatternCombinators
{
using static BinaryOperatorKind;
using static AnalyzedPattern;
internal static class CSharpUsePatternCombinatorsAnalyzer
{
public static AnalyzedPattern? Analyze(IOperation operation)
{
var pattern = ParsePattern(operation);
return pattern?.Target.Syntax is ExpressionSyntax ? pattern : null;
}
private enum ConstantResult
{
/// <summary>
/// None of operands were constant.
/// </summary>
None,
/// <summary>
/// The left operand is the constant.
/// </summary>
Left,
/// <summary>
/// The right operand is the constant.
/// </summary>
Right,
}
private static AnalyzedPattern? ParsePattern(IOperation operation)
{
switch (operation)
{
case IBinaryOperation { OperatorKind: BinaryOperatorKind.Equals } op:
return ParseConstantPattern(op);
case IBinaryOperation { OperatorKind: NotEquals } op:
return Not.TryCreate(ParseConstantPattern(op));
case IBinaryOperation { OperatorKind: ConditionalOr, Syntax: BinaryExpressionSyntax syntax } op:
return ParseBinaryPattern(op, isDisjunctive: true, syntax.OperatorToken);
case IBinaryOperation { OperatorKind: ConditionalAnd, Syntax: BinaryExpressionSyntax syntax } op:
return ParseBinaryPattern(op, isDisjunctive: false, syntax.OperatorToken);
case IBinaryOperation op when IsRelationalOperator(op.OperatorKind):
return ParseRelationalPattern(op);
case IUnaryOperation { OperatorKind: UnaryOperatorKind.Not } op:
return Not.TryCreate(ParsePattern(op.Operand));
case IIsTypeOperation { Syntax: BinaryExpressionSyntax { Right: TypeSyntax type } } op:
return new Type(type, op.ValueOperand);
case IIsPatternOperation { Pattern: { Syntax: PatternSyntax pattern } } op:
return new Source(pattern, op.Value);
case IParenthesizedOperation op:
return ParsePattern(op.Operand);
}
return null;
}
private static AnalyzedPattern? ParseBinaryPattern(IBinaryOperation op, bool isDisjunctive, SyntaxToken token)
{
var leftPattern = ParsePattern(op.LeftOperand);
if (leftPattern is null)
return null;
var rightPattern = ParsePattern(op.RightOperand);
if (rightPattern is null)
return null;
return Binary.TryCreate(leftPattern, rightPattern, isDisjunctive, token);
}
private static ConstantResult DetermineConstant(IBinaryOperation op)
{
return (op.LeftOperand, op.RightOperand) switch
{
var (_, v) when IsConstant(v) => ConstantResult.Right,
var (v, _) when IsConstant(v) => ConstantResult.Left,
_ => ConstantResult.None,
};
}
private static AnalyzedPattern? ParseRelationalPattern(IBinaryOperation op)
{
return DetermineConstant(op) switch
{
ConstantResult.Left when op.LeftOperand.Syntax is ExpressionSyntax left
// We need to flip the operator if the constant is on the left-hand-side.
// This is because relational patterns only come in the prefix form.
// For instance: `123 > x` would be rewritten as `x is < 123`.
=> new Relational(Flip(op.OperatorKind), left, op.RightOperand),
ConstantResult.Right when op.RightOperand.Syntax is ExpressionSyntax right
=> new Relational(op.OperatorKind, right, op.LeftOperand),
_ => null
};
}
private static AnalyzedPattern? ParseConstantPattern(IBinaryOperation op)
{
return DetermineConstant(op) switch
{
ConstantResult.Left when op.LeftOperand.Syntax is ExpressionSyntax left
=> new Constant(left, op.RightOperand),
ConstantResult.Right when op.RightOperand.Syntax is ExpressionSyntax right
=> new Constant(right, op.LeftOperand),
_ => null
};
}
private static bool IsRelationalOperator(BinaryOperatorKind operatorKind)
{
switch (operatorKind)
{
case LessThan:
case LessThanOrEqual:
case GreaterThanOrEqual:
case GreaterThan:
return true;
default:
return false;
}
}
/// <summary>
/// Changes the direction the operator is pointing at.
/// </summary>
/// <remarks>
/// Relational patterns only come in the prefix form so we'll have to
/// flip the operator if the constant happens to be on the left-hand-side.
/// For instance: `123 &gt; x` would be rewritten as `x is &lt; 123`.
/// </remarks>
public static BinaryOperatorKind Flip(BinaryOperatorKind operatorKind)
{
return operatorKind switch
{
LessThan => GreaterThan,
LessThanOrEqual => GreaterThanOrEqual,
GreaterThanOrEqual => LessThanOrEqual,
GreaterThan => LessThan,
var v => throw ExceptionUtilities.UnexpectedValue(v)
};
}
private static bool IsConstant(IOperation operation)
{
// By-design, constants will not propagate to conversions.
return operation is IConversionOperation op
? IsConstant(op.Operand)
: operation.ConstantValue.HasValue;
}
}
}
// 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.
#nullable enable
using System.Linq;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Shared.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
namespace Microsoft.CodeAnalysis.CSharp.UsePatternCombinators
{
using static AnalyzedPattern;
[DiagnosticAnalyzer(LanguageNames.CSharp)]
internal sealed class CSharpUsePatternCombinatorsDiagnosticAnalyzer :
AbstractBuiltInCodeStyleDiagnosticAnalyzer
{
public CSharpUsePatternCombinatorsDiagnosticAnalyzer()
: base(IDEDiagnosticIds.UsePatternCombinatorsDiagnosticId,
CSharpCodeStyleOptions.PreferPatternMatching,
LanguageNames.CSharp,
new LocalizableResourceString(nameof(CSharpAnalyzersResources.Use_pattern_matching), CSharpAnalyzersResources.ResourceManager, typeof(CSharpAnalyzersResources)),
new LocalizableResourceString(nameof(CSharpAnalyzersResources.Use_pattern_matching), CSharpAnalyzersResources.ResourceManager, typeof(CSharpAnalyzersResources)))
{
}
protected override void InitializeWorker(AnalysisContext context)
=> context.RegisterSyntaxNodeAction(AnalyzeNode,
SyntaxKind.LogicalAndExpression,
SyntaxKind.LogicalOrExpression,
SyntaxKind.LogicalNotExpression);
private void AnalyzeNode(SyntaxNodeAnalysisContext context)
{
var expression = (ExpressionSyntax)context.Node;
if (expression.GetDiagnostics().Any(diagnostic => diagnostic.Severity == DiagnosticSeverity.Error))
return;
// Bail if this is not a topmost expression
// to avoid overlapping diagnostics.
if (!IsTopmostExpression(expression))
return;
var syntaxTree = expression.SyntaxTree;
if (!((CSharpParseOptions)syntaxTree.Options).LanguageVersion.IsCSharp9OrAbove())
return;
var cancellationToken = context.CancellationToken;
var styleOption = context.Options.GetOption(CSharpCodeStyleOptions.PreferPatternMatching, syntaxTree, cancellationToken);
if (!styleOption.Value)
return;
var semanticModel = context.SemanticModel;
var expressionTypeOpt = semanticModel.Compilation.GetTypeByMetadataName("System.Linq.Expressions.Expression`1");
if (expression.IsInExpressionTree(semanticModel, expressionTypeOpt, cancellationToken))
return;
var operation = semanticModel.GetOperation(expression, cancellationToken);
if (operation is null)
return;
var pattern = CSharpUsePatternCombinatorsAnalyzer.Analyze(operation);
if (pattern is null)
return;
// Avoid rewriting trivial patterns, such as a single relational or a constant pattern.
if (IsTrivial(pattern))
return;
// C# 9.0 does not support pattern variables under `not` and `or` combinators,
// except for top-level `not` patterns.
if (HasIllegalPatternVariables(pattern, isTopLevel: true))
return;
context.ReportDiagnostic(DiagnosticHelper.Create(
Descriptor,
expression.GetLocation(),
styleOption.Notification.Severity,
additionalLocations: null,
properties: null));
}
private static bool HasIllegalPatternVariables(AnalyzedPattern pattern, bool permitDesignations = true, bool isTopLevel = false)
{
switch (pattern)
{
case Not p:
return HasIllegalPatternVariables(p.Pattern, permitDesignations: isTopLevel);
case Binary p:
if (p.IsDisjunctive)
permitDesignations = false;
return HasIllegalPatternVariables(p.Left, permitDesignations) ||
HasIllegalPatternVariables(p.Right, permitDesignations);
case Source p when !permitDesignations:
return p.PatternSyntax.DescendantNodes()
.OfType<SingleVariableDesignationSyntax>()
.Any(variable => !variable.Identifier.IsMissing);
default:
return false;
}
}
private static bool IsTopmostExpression(ExpressionSyntax node)
{
return node.WalkUpParentheses().Parent switch
{
LambdaExpressionSyntax _ => true,
AssignmentExpressionSyntax _ => true,
ConditionalExpressionSyntax _ => true,
ExpressionSyntax _ => false,
_ => true
};
}
private static bool IsTrivial(AnalyzedPattern pattern)
{
return pattern switch
{
Not { Pattern: Constant _ } => true,
Not { Pattern: Source { PatternSyntax: ConstantPatternSyntax _ } } => true,
Not _ => false,
Binary _ => false,
_ => true
};
}
public override DiagnosticAnalyzerCategory GetAnalyzerCategory()
=> DiagnosticAnalyzerCategory.SemanticSpanAnalysis;
}
}
......@@ -133,7 +133,10 @@ internal static class IDEDiagnosticIds
public const string InvalidSuppressMessageAttributeDiagnosticId = "IDE0076";
public const string LegacyFormatSuppressMessageAttributeDiagnosticId = "IDE0077";
public const string UsePatternCombinatorsDiagnosticId = "IDE0078";
public const string RemoveConfusingSuppressionForIsExpressionDiagnosticId = "IDE0080";
public const string RemoveUnnecessaryByValDiagnosticId = "IDE0081";
// Analyzer error Ids
public const string AnalyzerChangedId = "IDE1001";
......
' 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.
Imports Microsoft.CodeAnalysis.CodeStyle
Imports Microsoft.CodeAnalysis.Diagnostics
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic.RemoveUnnecessaryByVal
<DiagnosticAnalyzer(LanguageNames.VisualBasic)>
Friend NotInheritable Class VisualBasicRemoveUnnecessaryByValDiagnosticAnalyzer
Inherits AbstractBuiltInCodeStyleDiagnosticAnalyzer
Public Sub New()
MyBase.New(
diagnosticId:=IDEDiagnosticIds.RemoveUnnecessaryByValDiagnosticId,
[option]:=Nothing,
title:=New LocalizableResourceString(NameOf(VisualBasicAnalyzersResources.Remove_ByVal), VisualBasicAnalyzersResources.ResourceManager, GetType(VisualBasicAnalyzersResources)))
End Sub
Protected Overrides Sub InitializeWorker(context As AnalysisContext)
context.RegisterSyntaxNodeAction(
Sub(syntaxContext As SyntaxNodeAnalysisContext)
Dim parameterSyntax = DirectCast(syntaxContext.Node, ParameterSyntax)
For Each modifier In parameterSyntax.Modifiers
If modifier.IsKind(SyntaxKind.ByValKeyword) Then
syntaxContext.ReportDiagnostic(Diagnostic.Create(UnnecessaryWithSuggestionDescriptor, modifier.GetLocation(), additionalLocations:={parameterSyntax.GetLocation()}))
End If
Next
End Sub, SyntaxKind.Parameter)
End Sub
Public Overrides Function GetAnalyzerCategory() As DiagnosticAnalyzerCategory
Return DiagnosticAnalyzerCategory.SemanticSpanAnalysis
End Function
End Class
End Namespace
......@@ -23,6 +23,7 @@
<Compile Include="$(MSBuildThisFileDirectory)OrderModifiers\VisualBasicOrderModifiersHelper.vb" />
<Compile Include="$(MSBuildThisFileDirectory)PopulateSwitch\VisualBasicPopulateSwitchStatementDiagnosticAnalyzer.vb" />
<Compile Include="$(MSBuildThisFileDirectory)QualifyMemberAccess\VisualBasicQualifyMemberAccessDiagnosticAnalyzer.vb" />
<Compile Include="$(MSBuildThisFileDirectory)RemoveUnnecessaryByVal\VisualBasicRemoveUnnecessaryByValDiagnosticAnalyzer.vb" />
<Compile Include="$(MSBuildThisFileDirectory)RemoveUnnecessaryCast\VisualBasicRemoveUnnecessaryCastDiagnosticAnalyzer.vb" />
<Compile Include="$(MSBuildThisFileDirectory)RemoveUnnecessarySuppressions\VisualBasicRemoveUnnecessarySuppressionsDiagnosticAnalyzer.vb" />
<Compile Include="$(MSBuildThisFileDirectory)RemoveUnnecessaryImports\VisualBasicRemoveUnnecessaryImportsDiagnosticAnalyzer.vb" />
......
......@@ -129,4 +129,8 @@
<data name="If_statement_can_be_simplified" xml:space="preserve">
<value>'If' statement can be simplified</value>
</data>
</root>
\ No newline at end of file
<data name="Remove_ByVal" xml:space="preserve">
<value>'ByVal' keyword is unnecessary and can be removed.</value>
<comment>{locked: ByVal}This is a Visual Basic keyword and should not be localized or have its casing changed</comment>
</data>
</root>
......@@ -12,6 +12,11 @@
<target state="translated">Příkaz Imports není potřebný.</target>
<note />
</trans-unit>
<trans-unit id="Remove_ByVal">
<source>'ByVal' keyword is unnecessary and can be removed.</source>
<target state="new">'ByVal' keyword is unnecessary and can be removed.</target>
<note>{locked: ByVal}This is a Visual Basic keyword and should not be localized or have its casing changed</note>
</trans-unit>
<trans-unit id="Use_IsNot_Nothing_check">
<source>Use 'IsNot Nothing' check</source>
<target state="translated">Použít kontrolu „IsNot Nothing“</target>
......
......@@ -12,6 +12,11 @@
<target state="translated">Imports-Anweisung ist unnötig.</target>
<note />
</trans-unit>
<trans-unit id="Remove_ByVal">
<source>'ByVal' keyword is unnecessary and can be removed.</source>
<target state="new">'ByVal' keyword is unnecessary and can be removed.</target>
<note>{locked: ByVal}This is a Visual Basic keyword and should not be localized or have its casing changed</note>
</trans-unit>
<trans-unit id="Use_IsNot_Nothing_check">
<source>Use 'IsNot Nothing' check</source>
<target state="translated">Prüfung "IsNot Nothing" verwenden</target>
......
......@@ -12,6 +12,11 @@
<target state="translated">La declaración de importaciones no es necesaria.</target>
<note />
</trans-unit>
<trans-unit id="Remove_ByVal">
<source>'ByVal' keyword is unnecessary and can be removed.</source>
<target state="new">'ByVal' keyword is unnecessary and can be removed.</target>
<note>{locked: ByVal}This is a Visual Basic keyword and should not be localized or have its casing changed</note>
</trans-unit>
<trans-unit id="Use_IsNot_Nothing_check">
<source>Use 'IsNot Nothing' check</source>
<target state="translated">Usar comprobación "IsNot Nothing"</target>
......
......@@ -12,6 +12,11 @@
<target state="translated">L'instruction Imports n'est pas utile.</target>
<note />
</trans-unit>
<trans-unit id="Remove_ByVal">
<source>'ByVal' keyword is unnecessary and can be removed.</source>
<target state="new">'ByVal' keyword is unnecessary and can be removed.</target>
<note>{locked: ByVal}This is a Visual Basic keyword and should not be localized or have its casing changed</note>
</trans-unit>
<trans-unit id="Use_IsNot_Nothing_check">
<source>Use 'IsNot Nothing' check</source>
<target state="translated">Utiliser la vérification 'IsNot Nothing'</target>
......
......@@ -12,6 +12,11 @@
<target state="translated">L'istruzione Imports non è necessaria.</target>
<note />
</trans-unit>
<trans-unit id="Remove_ByVal">
<source>'ByVal' keyword is unnecessary and can be removed.</source>
<target state="new">'ByVal' keyword is unnecessary and can be removed.</target>
<note>{locked: ByVal}This is a Visual Basic keyword and should not be localized or have its casing changed</note>
</trans-unit>
<trans-unit id="Use_IsNot_Nothing_check">
<source>Use 'IsNot Nothing' check</source>
<target state="translated">Usa controllo 'IsNot Nothing'</target>
......
......@@ -12,6 +12,11 @@
<target state="translated">Imports ステートメントは不要です。</target>
<note />
</trans-unit>
<trans-unit id="Remove_ByVal">
<source>'ByVal' keyword is unnecessary and can be removed.</source>
<target state="new">'ByVal' keyword is unnecessary and can be removed.</target>
<note>{locked: ByVal}This is a Visual Basic keyword and should not be localized or have its casing changed</note>
</trans-unit>
<trans-unit id="Use_IsNot_Nothing_check">
<source>Use 'IsNot Nothing' check</source>
<target state="translated">IsNot Nothing' チェックを使用します</target>
......
......@@ -12,6 +12,11 @@
<target state="translated">Imports 문은 필요하지 않습니다.</target>
<note />
</trans-unit>
<trans-unit id="Remove_ByVal">
<source>'ByVal' keyword is unnecessary and can be removed.</source>
<target state="new">'ByVal' keyword is unnecessary and can be removed.</target>
<note>{locked: ByVal}This is a Visual Basic keyword and should not be localized or have its casing changed</note>
</trans-unit>
<trans-unit id="Use_IsNot_Nothing_check">
<source>Use 'IsNot Nothing' check</source>
<target state="translated">IsNot Nothing' 검사 사용</target>
......
......@@ -12,6 +12,11 @@
<target state="translated">Instrukcja imports jest niepotrzebna.</target>
<note />
</trans-unit>
<trans-unit id="Remove_ByVal">
<source>'ByVal' keyword is unnecessary and can be removed.</source>
<target state="new">'ByVal' keyword is unnecessary and can be removed.</target>
<note>{locked: ByVal}This is a Visual Basic keyword and should not be localized or have its casing changed</note>
</trans-unit>
<trans-unit id="Use_IsNot_Nothing_check">
<source>Use 'IsNot Nothing' check</source>
<target state="translated">Użyj sprawdzania „IsNot Nothing”</target>
......
......@@ -12,6 +12,11 @@
<target state="translated">A instrução Imports é desnecessária.</target>
<note />
</trans-unit>
<trans-unit id="Remove_ByVal">
<source>'ByVal' keyword is unnecessary and can be removed.</source>
<target state="new">'ByVal' keyword is unnecessary and can be removed.</target>
<note>{locked: ByVal}This is a Visual Basic keyword and should not be localized or have its casing changed</note>
</trans-unit>
<trans-unit id="Use_IsNot_Nothing_check">
<source>Use 'IsNot Nothing' check</source>
<target state="translated">Usar a verificação 'IsNot Nothing'</target>
......
......@@ -12,6 +12,11 @@
<target state="translated">Оператор Imports не нужен.</target>
<note />
</trans-unit>
<trans-unit id="Remove_ByVal">
<source>'ByVal' keyword is unnecessary and can be removed.</source>
<target state="new">'ByVal' keyword is unnecessary and can be removed.</target>
<note>{locked: ByVal}This is a Visual Basic keyword and should not be localized or have its casing changed</note>
</trans-unit>
<trans-unit id="Use_IsNot_Nothing_check">
<source>Use 'IsNot Nothing' check</source>
<target state="translated">Использовать флажок "IsNot Nothing"</target>
......
......@@ -12,6 +12,11 @@
<target state="translated">Imports deyimi gerekli değildir.</target>
<note />
</trans-unit>
<trans-unit id="Remove_ByVal">
<source>'ByVal' keyword is unnecessary and can be removed.</source>
<target state="new">'ByVal' keyword is unnecessary and can be removed.</target>
<note>{locked: ByVal}This is a Visual Basic keyword and should not be localized or have its casing changed</note>
</trans-unit>
<trans-unit id="Use_IsNot_Nothing_check">
<source>Use 'IsNot Nothing' check</source>
<target state="translated">IsNot Nothing' denetimi kullan</target>
......
......@@ -12,6 +12,11 @@
<target state="translated">Imports 语句是不需要的。</target>
<note />
</trans-unit>
<trans-unit id="Remove_ByVal">
<source>'ByVal' keyword is unnecessary and can be removed.</source>
<target state="new">'ByVal' keyword is unnecessary and can be removed.</target>
<note>{locked: ByVal}This is a Visual Basic keyword and should not be localized or have its casing changed</note>
</trans-unit>
<trans-unit id="Use_IsNot_Nothing_check">
<source>Use 'IsNot Nothing' check</source>
<target state="translated">使用 "IsNot Nothing" 检查</target>
......
......@@ -12,6 +12,11 @@
<target state="translated">無須 Imports 陳述式。</target>
<note />
</trans-unit>
<trans-unit id="Remove_ByVal">
<source>'ByVal' keyword is unnecessary and can be removed.</source>
<target state="new">'ByVal' keyword is unnecessary and can be removed.</target>
<note>{locked: ByVal}This is a Visual Basic keyword and should not be localized or have its casing changed</note>
</trans-unit>
<trans-unit id="Use_IsNot_Nothing_check">
<source>Use 'IsNot Nothing' check</source>
<target state="translated">使用 'IsNot Nothing' 檢查</target>
......
' 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.
Imports System.Collections.Immutable
Imports System.Composition
Imports System.Diagnostics.CodeAnalysis
Imports System.Threading
Imports Microsoft.CodeAnalysis.CodeActions
Imports Microsoft.CodeAnalysis.CodeFixes
Imports Microsoft.CodeAnalysis.Diagnostics
Imports Microsoft.CodeAnalysis.Editing
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic.RemoveUnnecessaryByVal
<ExportCodeFixProvider(LanguageNames.VisualBasic), [Shared]>
Friend Class VisualBasicRemoveUnnecessaryByValCodeFixProvider
Inherits SyntaxEditorBasedCodeFixProvider
<ImportingConstructor>
<SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification:="Used in test code: https://github.com/dotnet/roslyn/issues/42814")>
Public Sub New()
End Sub
Public Overrides ReadOnly Property FixableDiagnosticIds As ImmutableArray(Of String) =
ImmutableArray.Create(IDEDiagnosticIds.RemoveUnnecessaryByValDiagnosticId)
Friend Overrides ReadOnly Property CodeFixCategory As CodeFixCategory = CodeFixCategory.CodeStyle
Public Overrides Function RegisterCodeFixesAsync(context As CodeFixContext) As Task
For Each diagnostic In context.Diagnostics
context.RegisterCodeFix(New MyCodeAction(
VisualBasicAnalyzersResources.Remove_ByVal,
Function(ct) FixAsync(context.Document, diagnostic, ct)),
diagnostic)
Next
Return Task.CompletedTask
End Function
Protected Overrides Async Function FixAllAsync(document As Document, diagnostics As ImmutableArray(Of Diagnostic), editor As SyntaxEditor, cancellationToken As CancellationToken) As Task
Dim root = Await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(False)
For Each diagnostic In diagnostics
Dim node = DirectCast(root.FindNode(diagnostic.AdditionalLocations(0).SourceSpan), ParameterSyntax)
Dim tokenList = SyntaxFactory.TokenList(node.Modifiers.Where(Function(m) Not m.IsKind(SyntaxKind.ByValKeyword)))
editor.ReplaceNode(node, node.WithModifiers(tokenList))
Next
End Function
Private Class MyCodeAction
Inherits CustomCodeActions.DocumentChangeAction
Friend Sub New(title As String, createChangedDocument As Func(Of CancellationToken, Task(Of Document)))
MyBase.New(title, createChangedDocument)
End Sub
End Class
End Class
End Namespace
......@@ -20,6 +20,7 @@
<Compile Include="$(MSBuildThisFileDirectory)OrderModifiers\VisualBasicOrderModifiersCodeFixProvider.vb" />
<Compile Include="$(MSBuildThisFileDirectory)PopulateSwitch\VisualBasicPopulateSwitchStatementCodeFixProvider.vb" />
<Compile Include="$(MSBuildThisFileDirectory)QualifyMemberAccess\VisualBasicQualifyMemberAccessCodeFixProvider.vb" />
<Compile Include="$(MSBuildThisFileDirectory)RemoveUnnecessaryByVal\VisualBasicRemoveUnnecessaryByValCodeFixProvider.vb" />
<Compile Include="$(MSBuildThisFileDirectory)RemoveUnnecessaryCast\VisualBasicRemoveUnnecessaryCastCodeFixProvider.vb" />
<Compile Include="$(MSBuildThisFileDirectory)RemoveUnnecessaryImports\VisualBasicRemoveUnnecessaryImportsCodeFixProvider.vb" />
<Compile Include="$(MSBuildThisFileDirectory)RemoveUnnecessaryParentheses\VisualBasicRemoveUnnecessaryParenthesesCodeFixProvider.vb" />
......
' 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.
Imports Microsoft.CodeAnalysis.CodeFixes
Imports Microsoft.CodeAnalysis.Diagnostics
Imports Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics
Imports Microsoft.CodeAnalysis.VisualBasic.RemoveUnnecessaryByVal
Imports VerifyVB = Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions.VisualBasicCodeFixVerifier(Of
Microsoft.CodeAnalysis.VisualBasic.RemoveUnnecessaryByVal.VisualBasicRemoveUnnecessaryByValDiagnosticAnalyzer,
Microsoft.CodeAnalysis.VisualBasic.RemoveUnnecessaryByVal.VisualBasicRemoveUnnecessaryByValCodeFixProvider)
Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.RemoveUnnecessaryByVal
Public Class RemoveUnnecessaryByValTests
Private Shared Async Function VerifyCodeFixAsync(source As String, fixedSource As String) As Task
Await New VerifyVB.Test With
{
.TestCode = source,
.FixedCode = fixedSource
}.RunAsync()
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveByVal)>
Public Async Function TestRemoveByVal() As Task
Await VerifyCodeFixAsync(
"Public Class Program
Public Sub MySub([|ByVal|] arg As String)
End Sub
End Class
",
"Public Class Program
Public Sub MySub(arg As String)
End Sub
End Class
")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveByVal)>
Public Async Function TestRemoveByValLowerCase() As Task
Await VerifyCodeFixAsync(
"Public Class Program
Public Sub MySub([|byval|] arg As String)
End Sub
End Class
",
"Public Class Program
Public Sub MySub(arg As String)
End Sub
End Class
")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveByVal)>
Public Async Function TestRemoveByValMoreThanOneModifier() As Task
Await VerifyCodeFixAsync(
"Public Class Program
Public Sub MySub(Optional [|ByVal|] arg As String = ""Default"")
End Sub
End Class
",
"Public Class Program
Public Sub MySub(Optional arg As String = ""Default"")
End Sub
End Class
")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveByVal)>
Public Async Function TestRemoveByValCodeHasError() As Task
Await VerifyCodeFixAsync(
"Public Class Program
Public Sub MySub([|ByVal|] arg)
End Sub
End Class
",
"Public Class Program
Public Sub MySub(arg)
End Sub
End Class
")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveByVal)>
Public Async Function TestRemoveByValInConstructor() As Task
Await VerifyCodeFixAsync(
"Public Class Program
Public Sub New([|ByVal|] arg As String)
End Sub
End Class
",
"Public Class Program
Public Sub New(arg As String)
End Sub
End Class
")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveByVal)>
Public Async Function TestRemoveByValInOperator() As Task
Await VerifyCodeFixAsync(
"Public Class Program
Public Shared Operator +([|ByVal|] arg1 As Program, [|ByVal|] arg2 As Program) As Program
Return New Program()
End Operator
End Class
",
"Public Class Program
Public Shared Operator +(arg1 As Program, arg2 As Program) As Program
Return New Program()
End Operator
End Class
")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveByVal)>
Public Async Function TestRemoveByValParameterizedProperty() As Task
Await VerifyCodeFixAsync(
"Public Class Program
Public ReadOnly Property Test([|ByVal|] v as String) As Integer
Get
Return 0
End Get
End Property
End Class
",
"Public Class Program
Public ReadOnly Property Test(v as String) As Integer
Get
Return 0
End Get
End Property
End Class
")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveByVal)>
Public Async Function TestRemoveByValInDelegate() As Task
Await VerifyCodeFixAsync(
"Public Class Program
Delegate Function CompareNumbers([|ByVal|] num1 As Integer, [|ByVal|] num2 As Integer) As Boolean
End Class
",
"Public Class Program
Delegate Function CompareNumbers(num1 As Integer, num2 As Integer) As Boolean
End Class
")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveByVal)>
Public Async Function TestRemoveByValInLambdaSingleLine() As Task
Await VerifyCodeFixAsync(
"Public Class Program
Public Shared Sub Main()
Dim add1 = Function([|ByVal|] num As Integer) num + 1
Dim print = Sub([|ByVal|] str As String) System.Console.WriteLine(str)
End Sub
End Class
",
"Public Class Program
Public Shared Sub Main()
Dim add1 = Function(num As Integer) num + 1
Dim print = Sub(str As String) System.Console.WriteLine(str)
End Sub
End Class
")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveByVal)>
Public Async Function TestRemoveByValInLambdaMultiLine() As Task
Await VerifyCodeFixAsync(
"Public Class Program
Public Shared Sub Main()
Dim add1 = Function([|ByVal|] num As Integer)
Return num + 1
End Function
Dim print = Sub([|ByVal|] str As String)
System.Console.WriteLine(str)
End Sub
End Sub
End Class
",
"Public Class Program
Public Shared Sub Main()
Dim add1 = Function(num As Integer)
Return num + 1
End Function
Dim print = Sub(str As String)
System.Console.WriteLine(str)
End Sub
End Sub
End Class
")
End Function
End Class
End Namespace
......@@ -13,6 +13,7 @@
<Compile Include="$(MSBuildThisFileDirectory)AddRequiredParentheses\AddRequiredParenthesesTests.vb" />
<Compile Include="$(MSBuildThisFileDirectory)ConvertAnonymousTypeToTuple\ConvertAnonymousTypeToTupleTests.vb" />
<Compile Include="$(MSBuildThisFileDirectory)FileHeaders\FileHeaderTests.vb" />
<Compile Include="$(MSBuildThisFileDirectory)RemoveUnnecessaryByVal\RemoveUnnecessaryByValTests.vb" />
<Compile Include="$(MSBuildThisFileDirectory)UpdateLegacySuppressions\UpdateLegacySuppressionsTests.vb" />
<Compile Include="$(MSBuildThisFileDirectory)RemoveUnnecessarySuppressions\RemoveUnnecessarySuppressionsTests.vb" />
<Compile Include="$(MSBuildThisFileDirectory)MakeFieldReadonly\MakeFieldReadonlyTests.vb" />
......
......@@ -4,10 +4,14 @@
<PropertyGroup>
<OutputType>Library</OutputType>
<RootNamespace>Microsoft.CodeAnalysis.Editor.CSharp</RootNamespace>
<TargetFramework>net472</TargetFramework>
<TargetFrameworks>netcoreapp3.1;net472</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<ApplyNgenOptimization>partial</ApplyNgenOptimization>
<!-- Workaround dependencies that do not yet support netcoreapp3.1 https://github.com/dotnet/roslyn/issues/45114 -->
<NoWarn>NU1701;$(NoWarn)</NoWarn>
<AssetTargetFallback Condition="'$(TargetFramework)' == 'netcoreapp3.1'">net472;$(AssetTargetFallback)</AssetTargetFallback>
<!-- NuGet -->
<IsPackable>true</IsPackable>
<PackageDescription>
......@@ -38,11 +42,13 @@
<InternalsVisibleTo Include="Microsoft.VisualStudio.LanguageServices.LiveShare" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.ComponentModel.Composition" />
<PackageReference Include="Microsoft.VisualStudio.Language.Intellisense" Version="$(MicrosoftVisualStudioLanguageIntellisenseVersion)" />
<PackageReference Include="ICSharpCode.Decompiler" Version="$(ICSharpCodeDecompilerVersion)" />
<PackageReference Include="Microsoft.VisualStudio.Threading" Version="$(MicrosoftVisualStudioThreadingVersion)" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net472'">
<Reference Include="System.ComponentModel.Composition" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="CSharpEditorResources.resx" GenerateSource="true" />
</ItemGroup>
......
// 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.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.Shared.Extensions;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.CSharp.UsePatternCombinators;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics;
using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions;
using Microsoft.CodeAnalysis.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UsePatternCombinators
{
public class CSharpUsePatternCombinatorsDiagnosticAnalyzerTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest
{
private static readonly ParseOptions CSharp9 = TestOptions.RegularPreview.WithLanguageVersion(LanguageVersionExtensions.CSharp9);
private static readonly OptionsCollection s_disabled = new OptionsCollection(LanguageNames.CSharp)
{
{ CSharpCodeStyleOptions.PreferPatternMatching, new CodeStyleOption2<bool>(false, NotificationOption2.None) }
};
internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace)
=> (new CSharpUsePatternCombinatorsDiagnosticAnalyzer(), new CSharpUsePatternCombinatorsCodeFixProvider());
private Task TestAllMissingOnExpressionAsync(string expression, ParseOptions parseOptions = null, bool enabled = true)
=> TestMissingAsync(FromExpression(expression), parseOptions, enabled);
private Task TestMissingAsync(string initialMarkup, ParseOptions parseOptions = null, bool enabled = true)
=> TestMissingAsync(initialMarkup, new TestParameters(
parseOptions: parseOptions ?? CSharp9, options: enabled ? null : s_disabled));
private Task TestAllAsync(string initialMarkup, string expectedMarkup)
=> TestInRegularAndScriptAsync(initialMarkup, expectedMarkup,
parseOptions: CSharp9, options: null);
private Task TestAllOnExpressionAsync(string expression, string expected)
=> TestAllAsync(FromExpression(expression), FromExpression(expected));
private static string FromExpression(string expression)
{
const string initialMarkup = @"
using System;
using System.Collections.Generic;
class C
{
static bool field = {|FixAllInDocument:EXPRESSION|};
static bool Method() => EXPRESSION;
static bool Prop1 => EXPRESSION;
static bool Prop2 { get; } = EXPRESSION;
static void If() { if (EXPRESSION) ; }
static void Argument1() => Test(EXPRESSION);
static void Argument2() => Test(() => EXPRESSION);
static void Argument3() => Test(_ => EXPRESSION);
static void Test(bool b) {}
static void Test(Func<bool> b) {}
static void Test(Func<object, bool> b) {}
static void For() { for (; EXPRESSION; ); }
static void Local() { var local = EXPRESSION; }
static void Conditional() { _ = EXPRESSION ? EXPRESSION : EXPRESSION; }
static void Assignment() { _ = EXPRESSION; }
static void Do() { do ; while (EXPRESSION); }
static void While() { while (EXPRESSION) ; }
static bool When() => o switch { _ when EXPRESSION => EXPRESSION };
static bool Return() { return EXPRESSION; }
static IEnumerable<bool> YieldReturn() { yield return EXPRESSION; }
static Func<object, bool> SimpleLambda() => o => EXPRESSION;
static Func<bool> ParenthesizedLambda() => () => EXPRESSION;
static void LocalFunc() { bool LocalFunction() => EXPRESSION; }
static int i;
static int? nullable;
static object o;
}
";
return initialMarkup.Replace("EXPRESSION", expression);
}
[InlineData("i == 0")]
[InlineData("i > 0")]
[InlineData("o is C")]
[InlineData("o is C c")]
[InlineData("o != null")]
[InlineData("!(o is null)")]
[InlineData("o is int ii || o is long jj")]
[Theory, Trait(Traits.Feature, Traits.Features.CodeActionsUsePatternCombinators)]
public async Task TestMissingOnExpression(string expression)
{
await TestAllMissingOnExpressionAsync(expression);
}
[InlineData("i == default || i > default(int)", "i is default(int) or > (default(int))")]
[InlineData("!(o is C c)", "o is not C c")]
[InlineData("o is int ii && o is long jj", "o is int ii and long jj")]
[InlineData("!(o is C)", "o is not C")]
[InlineData("!(o is C _)", "o is not C _")]
[InlineData("i == (0x02 | 0x04) || i != 0", "i is (0x02 | 0x04) or not 0")]
[InlineData("i == 1 || 2 == i", "i is 1 or 2")]
[InlineData("i == (short)1 || (short)2 == i", "i is ((short)1) or ((short)2)")]
[InlineData("nullable == 1 || 2 == nullable", "nullable is 1 or 2")]
[InlineData("i != 1 || 2 != i", "i is not 1 or not 2")]
[InlineData("i != 1 && 2 != i", "i is not 1 and not 2")]
[InlineData("!(i != 1 && 2 != i)", "i is 1 or 2")]
[InlineData("i < 1 && 2 <= i", "i is < 1 and >= 2")]
[InlineData("i < 1 && 2 <= i && i is not 0", "i is < 1 and >= 2 and not 0")]
[InlineData("(int.MaxValue - 1D) < i && i > 0", "i is > (int.MaxValue - 1D) and > 0")]
[Theory, Trait(Traits.Feature, Traits.Features.CodeActionsUsePatternCombinators)]
public async Task TestOnExpression(string expression, string expected)
{
await TestAllOnExpressionAsync(expression, expected);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUsePatternCombinators)]
public async Task TestMissingIfDisabled()
{
await TestAllMissingOnExpressionAsync("o == 1 || o == 2", enabled: false);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUsePatternCombinators)]
public async Task TestMissingOnCSharp8()
{
await TestAllMissingOnExpressionAsync("o == 1 || o == 2", parseOptions: TestOptions.Regular8);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUsePatternCombinators)]
public async Task TestMultilineTrivia_01()
{
await TestAllAsync(
@"class C
{
bool M0(int variable)
{
return {|FixAllInDocument:variable == 0 || /*1*/
variable == 1 || /*2*/
variable == 2|}; /*3*/
}
bool M1(int variable)
{
return variable != 0 && /*1*/
variable != 1 && /*2*/
variable != 2; /*3*/
}
}",
@"class C
{
bool M0(int variable)
{
return variable is 0 or /*1*/
1 or /*2*/
2; /*3*/
}
bool M1(int variable)
{
return variable is not 0 and /*1*/
not 1 and /*2*/
not 2; /*3*/
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUsePatternCombinators)]
public async Task TestMultilineTrivia_02()
{
await TestAllAsync(
@"class C
{
bool M0(int variable)
{
return {|FixAllInDocument:variable == 0 /*1*/
|| variable == 1 /*2*/
|| variable == 2|}; /*3*/
}
bool M1(int variable)
{
return variable != 0 /*1*/
&& variable != 1 /*2*/
&& variable != 2; /*3*/
}
}",
@"class C
{
bool M0(int variable)
{
return variable is 0 /*1*/
or 1 /*2*/
or 2; /*3*/
}
bool M1(int variable)
{
return variable is not 0 /*1*/
and not 1 /*2*/
and not 2; /*3*/
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUsePatternCombinators)]
public async Task TestParenthesized()
{
await TestAllAsync(
@"class C
{
bool M0(int v)
{
return {|FixAllInDocument:(v == 0 || v == 1 || v == 2)|};
}
bool M1(int v)
{
return (v == 0) || (v == 1) || (v == 2);
}
}",
@"class C
{
bool M0(int v)
{
return (v is 0 or 1 or 2);
}
bool M1(int v)
{
return v is 0 or 1 or 2;
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUsePatternCombinators)]
public async Task TestMissingInExpressionTree()
{
await TestMissingAsync(
@"using System.Linq;
class C
{
void M0(IQueryable<int> q)
{
q.Where(item => item == 1 [||]|| item == 2);
}
}");
}
}
}
......@@ -65,7 +65,7 @@ internal abstract partial class AbstractDiagnosticsTaggerProvider<TTag> : Asynch
_diagnosticService.DiagnosticsUpdated += OnDiagnosticsUpdated;
}
private void OnDiagnosticsUpdated(object sender, DiagnosticsUpdatedArgs e)
private void OnDiagnosticsUpdated(object? sender, DiagnosticsUpdatedArgs e)
{
if (e.Solution == null || e.DocumentId == null)
{
......
......@@ -140,7 +140,7 @@ public void EndTracking()
}
}
private void DocumentClosed(object sender, DocumentEventArgs e)
private void DocumentClosed(object? sender, DocumentEventArgs e)
{
lock (_trackingSpans)
{
......@@ -148,7 +148,7 @@ private void DocumentClosed(object sender, DocumentEventArgs e)
}
}
private void DocumentOpened(object sender, DocumentEventArgs e)
private void DocumentOpened(object? sender, DocumentEventArgs e)
=> _ = TrackActiveSpansAsync(e.Document, _cancellationSource.Token);
private async Task TrackActiveSpansAsync(Document document, CancellationToken cancellationToken)
......
......@@ -151,7 +151,7 @@ public void Disconnect()
_selectedItemInfoTaskCancellationSource.Cancel();
}
private void OnWorkspaceChanged(object sender, WorkspaceChangeEventArgs args)
private void OnWorkspaceChanged(object? sender, WorkspaceChangeEventArgs args)
{
// We're getting an event for a workspace we already disconnected from
if (args.NewSolution.Workspace != _workspace)
......@@ -188,26 +188,26 @@ private void OnWorkspaceChanged(object sender, WorkspaceChangeEventArgs args)
}
}
private void OnSubjectBufferPostChanged(object sender, EventArgs e)
private void OnSubjectBufferPostChanged(object? sender, EventArgs e)
{
AssertIsForeground();
StartModelUpdateAndSelectedItemUpdateTasks(modelUpdateDelay: TaggerConstants.MediumDelay, selectedItemUpdateDelay: 0, updateUIWhenDone: true);
}
private void OnCaretMoved(object sender, EventArgs e)
private void OnCaretMoved(object? sender, EventArgs e)
{
AssertIsForeground();
StartSelectedItemUpdateTask(delay: TaggerConstants.NearImmediateDelay, updateUIWhenDone: true);
}
private void OnViewFocused(object sender, EventArgs e)
private void OnViewFocused(object? sender, EventArgs e)
{
AssertIsForeground();
StartSelectedItemUpdateTask(delay: TaggerConstants.ShortDelay, updateUIWhenDone: true);
}
private void OnDropDownFocused(object sender, EventArgs e)
private void OnDropDownFocused(object? sender, EventArgs e)
{
AssertIsForeground();
......@@ -348,7 +348,7 @@ private void PushSelectedItemsToPresenter(NavigationBarSelectedTypeAndMember sel
_versionStampOfFullListPushedToPresenter = null;
}
private void OnItemSelected(object sender, NavigationBarItemSelectedEventArgs e)
private void OnItemSelected(object? sender, NavigationBarItemSelectedEventArgs e)
{
AssertIsForeground();
......
......@@ -4,11 +4,15 @@
<PropertyGroup>
<OutputType>Library</OutputType>
<RootNamespace>Microsoft.CodeAnalysis.Editor</RootNamespace>
<TargetFramework>net472</TargetFramework>
<TargetFrameworks>netcoreapp3.1;net472</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DefineConstants>$(DefineConstants);EDITOR_FEATURES</DefineConstants>
<ApplyNgenOptimization>partial</ApplyNgenOptimization>
<!-- Workaround dependencies that do not yet support netcoreapp3.1 https://github.com/dotnet/roslyn/issues/45114 -->
<NoWarn>NU1701;$(NoWarn)</NoWarn>
<AssetTargetFallback Condition="'$(TargetFramework)' == 'netcoreapp3.1'">net472;$(AssetTargetFallback)</AssetTargetFallback>
<!-- NuGet -->
<PackageId>Microsoft.CodeAnalysis.EditorFeatures.Common</PackageId>
<IsPackable>true</IsPackable>
......@@ -24,12 +28,6 @@
<ProjectReference Include="..\Text\Microsoft.CodeAnalysis.EditorFeatures.Text.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.ComponentModel.Composition" />
<Reference Include="System.Core" />
<PackageReference Include="System.Collections.Immutable" Version="$(SystemCollectionsImmutableVersion)" />
<PackageReference Include="Microsoft.VisualStudio.ImageCatalog" Version="$(MicrosoftVisualStudioImageCatalogVersion)" />
<PackageReference Include="Microsoft.VisualStudio.Imaging" Version="$(MicrosoftVisualStudioImagingVersion)" />
<PackageReference Include="Microsoft.VisualStudio.Language.StandardClassification" Version="$(MicrosoftVisualStudioLanguageStandardClassificationVersion)" />
<PackageReference Include="Microsoft.VisualStudio.Language.Intellisense" Version="$(MicrosoftVisualStudioLanguageIntellisenseVersion)" />
<PackageReference Include="Microsoft.VisualStudio.Text.UI" Version="$(MicrosoftVisualStudioTextUIVersion)" />
......@@ -37,6 +35,18 @@
<PackageReference Include="Microsoft.CodeAnalysis.Elfie" Version="$(MicrosoftCodeAnalysisElfieVersion)" />
<PackageReference Include="Microsoft.VisualStudio.SDK.EmbedInteropTypes" Version="$(MicrosoftVisualStudioSDKEmbedInteropTypesVersion)" />
<PackageReference Include="Newtonsoft.Json" Version="$(NewtonsoftJsonVersion)" />
<PackageReference Include="Microsoft.VisualStudio.ImageCatalog" Version="$(MicrosoftVisualStudioImageCatalogVersion)" />
<PackageReference Include="Microsoft.VisualStudio.Imaging" Version="$(MicrosoftVisualStudioImagingVersion)" />
<PackageReference Include="Microsoft.VisualStudio.Imaging.Interop.14.0.DesignTime" Version="$(MicrosoftVisualStudioImagingInterop140DesignTimeVersion)" />
<PackageReference Include="Microsoft.VisualStudio.Language" Version="$(MicrosoftVisualStudioLanguageVersion)" />
<PackageReference Include="Microsoft.VisualStudio.Language.Intellisense" Version="$(MicrosoftVisualStudioLanguageIntellisenseVersion)" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net472'">
<Reference Include="System" />
<Reference Include="System.ComponentModel.Composition" />
<Reference Include="System.Core" />
<PackageReference Include="System.Collections.Immutable" Version="$(SystemCollectionsImmutableVersion)" />
<PackageReference Include="Microsoft.CodeAnalysis.Elfie" Version="$(MicrosoftCodeAnalysisElfieVersion)" />
</ItemGroup>
<ItemGroup>
<InternalsVisibleTo Include="Microsoft.CodeAnalysis.CSharp.EditorFeatures" />
......
......@@ -382,6 +382,9 @@ public void CSharp_VerifyIDEDiagnosticSeveritiesAreConfigurable()
# IDE0077
dotnet_diagnostic.IDE0077.severity = %value%
# IDE0078
csharp_style_prefer_pattern_matching = true:silent
# IDE0080
dotnet_diagnostic.IDE0080.severity = %value%
......@@ -531,6 +534,9 @@ public void VisualBasic_VerifyIDEDiagnosticSeveritiesAreConfigurable()
# IDE0077
dotnet_diagnostic.IDE0077.severity = %value%
# IDE0081
dotnet_diagnostic.IDE0081.severity = %value%
# IDE1006
dotnet_diagnostic.IDE1006.severity = %value%
......@@ -903,6 +909,9 @@ public void CSharp_VerifyIDECodeStyleOptionsAreConfigurable()
# IDE0077
No editorconfig based code style option
# IDE0078, PreferPatternMatching
csharp_style_prefer_pattern_matching = true:silent
# IDE0080
No editorconfig based code style option
......@@ -1085,6 +1094,9 @@ public void VisualBasic_VerifyIDECodeStyleOptionsAreConfigurable()
# IDE0077
No editorconfig based code style option
# IDE0081
No editorconfig based code style option
# IDE1006
No editorconfig based code style option
......
......@@ -3,10 +3,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>net472</TargetFramework>
<TargetFrameworks>netcoreapp3.1;net472</TargetFrameworks>
<RootNamespace></RootNamespace>
<ApplyNgenOptimization>partial</ApplyNgenOptimization>
<!-- Workaround dependencies that do not yet support netcoreapp3.1 https://github.com/dotnet/roslyn/issues/45114 -->
<NoWarn>NU1701;$(NoWarn)</NoWarn>
<AssetTargetFallback Condition="'$(TargetFramework)' == 'netcoreapp3.1'">net472;$(AssetTargetFallback)</AssetTargetFallback>
<!-- NuGet -->
<IsPackable>true</IsPackable>
<PackageDescription>
......@@ -24,11 +28,13 @@
<ProjectReference Include="..\Core\Microsoft.CodeAnalysis.EditorFeatures.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.ComponentModel.Composition" />
<PackageReference Include="Microsoft.VisualStudio.Language.Intellisense" Version="$(MicrosoftVisualStudioLanguageIntellisenseVersion)" />
<PackageReference Include="Microsoft.VisualStudio.Text.UI" Version="$(MicrosoftVisualStudioTextUIVersion)" />
<PackageReference Include="Microsoft.VisualStudio.Threading" Version="$(MicrosoftVisualStudioThreadingVersion)" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net472'">
<Reference Include="System.ComponentModel.Composition" />
</ItemGroup>
<ItemGroup>
<Import Include="Microsoft.CodeAnalysis.Editor.Shared.Extensions" />
<Import Include="Microsoft.CodeAnalysis.Editor.Shared.Options" />
......
// 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.
#nullable enable
using System;
using System.Collections.Immutable;
using System.Composition;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
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.Operations;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp.UsePatternCombinators
{
using static SyntaxFactory;
using static AnalyzedPattern;
[ExportCodeFixProvider(LanguageNames.CSharp), Shared]
internal class CSharpUsePatternCombinatorsCodeFixProvider : SyntaxEditorBasedCodeFixProvider
{
[ImportingConstructor]
[SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")]
public CSharpUsePatternCombinatorsCodeFixProvider()
{
}
private static SyntaxKind MapToSyntaxKind(BinaryOperatorKind kind)
{
return kind switch
{
BinaryOperatorKind.LessThan => SyntaxKind.LessThanToken,
BinaryOperatorKind.GreaterThan => SyntaxKind.GreaterThanToken,
BinaryOperatorKind.LessThanOrEqual => SyntaxKind.LessThanEqualsToken,
BinaryOperatorKind.GreaterThanOrEqual => SyntaxKind.GreaterThanEqualsToken,
_ => throw ExceptionUtilities.UnexpectedValue(kind)
};
}
public override ImmutableArray<string> FixableDiagnosticIds
=> ImmutableArray.Create(IDEDiagnosticIds.UsePatternCombinatorsDiagnosticId);
internal sealed override CodeFixCategory CodeFixCategory => CodeFixCategory.CodeStyle;
public override Task RegisterCodeFixesAsync(CodeFixContext context)
{
context.RegisterCodeFix(
new MyCodeAction(c => FixAsync(context.Document, context.Diagnostics.First(), c)),
context.Diagnostics);
return Task.CompletedTask;
}
protected override async Task FixAllAsync(
Document document, ImmutableArray<Diagnostic> diagnostics,
SyntaxEditor editor, CancellationToken cancellationToken)
{
var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false);
foreach (var diagnostic in diagnostics)
{
var location = diagnostic.Location;
var expression = editor.OriginalRoot.FindNode(location.SourceSpan, getInnermostNodeForTie: true);
var operation = semanticModel.GetOperation(expression, cancellationToken);
RoslynDebug.AssertNotNull(operation);
var pattern = CSharpUsePatternCombinatorsAnalyzer.Analyze(operation);
RoslynDebug.AssertNotNull(pattern);
var patternSyntax = AsPatternSyntax(pattern).WithAdditionalAnnotations(Formatter.Annotation);
editor.ReplaceNode(expression, IsPatternExpression((ExpressionSyntax)pattern.Target.Syntax, patternSyntax));
}
}
private static PatternSyntax AsPatternSyntax(AnalyzedPattern pattern)
{
return pattern switch
{
Binary p => BinaryPattern(
p.IsDisjunctive ? SyntaxKind.OrPattern : SyntaxKind.AndPattern,
AsPatternSyntax(p.Left).Parenthesize(),
Token(p.Token.LeadingTrivia, p.IsDisjunctive ? SyntaxKind.OrKeyword : SyntaxKind.AndKeyword,
TriviaList(p.Token.GetAllTrailingTrivia())),
AsPatternSyntax(p.Right).Parenthesize()),
Constant p => ConstantPattern(AsExpressionSyntax(p)),
Source p => p.PatternSyntax,
Type p => TypePattern(p.TypeSyntax),
Relational p => RelationalPattern(Token(MapToSyntaxKind(p.OperatorKind)), p.Value.Parenthesize()),
Not p => UnaryPattern(AsPatternSyntax(p.Pattern).Parenthesize()),
var p => throw ExceptionUtilities.UnexpectedValue(p)
};
}
private static ExpressionSyntax AsExpressionSyntax(Constant constant)
{
var expr = constant.ExpressionSyntax;
if (expr.IsKind(SyntaxKind.DefaultLiteralExpression))
{
// default literals are not permitted in patterns
var convertedType = constant.Target.SemanticModel.GetTypeInfo(expr).ConvertedType;
if (convertedType != null)
{
return DefaultExpression(convertedType.GenerateTypeSyntax());
}
}
return expr.Parenthesize();
}
private class MyCodeAction : CodeAction.DocumentChangeAction
{
public MyCodeAction(Func<CancellationToken, Task<Document>> createChangedDocument)
: base(CSharpAnalyzersResources.Use_pattern_matching, createChangedDocument)
{
}
internal override CodeActionPriority Priority => CodeActionPriority.Low;
}
}
}
......@@ -2,20 +2,23 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable enable
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.ErrorReporting;
namespace Microsoft.CodeAnalysis.Interactive
{
internal static class InteractiveHostEntryPoint
{
private static int Main(string[] args)
private static async Task<int> Main(string[] args)
{
FatalError.Handler = FailFast.OnFatalException;
try
{
InteractiveHost.Service.RunServer(args);
await InteractiveHost.Service.RunServerAsync(args).ConfigureAwait(false);
return 0;
}
catch (Exception e)
......
......@@ -5,11 +5,15 @@
#nullable enable
using System;
using System.Diagnostics;
using System.Globalization;
using System.IO.Pipes;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.ErrorReporting;
using Roslyn.Utilities;
using StreamJsonRpc;
namespace Microsoft.CodeAnalysis.Interactive
{
......@@ -17,8 +21,9 @@ internal partial class InteractiveHost
{
private sealed class LazyRemoteService
{
public readonly AsyncLazy<InitializedRemoteService> InitializedService;
public readonly CancellationTokenSource CancellationSource;
private readonly AsyncLazy<InitializedRemoteService> _lazyInitializedService;
private readonly CancellationTokenSource _cancellationSource;
public readonly InteractiveHostOptions Options;
public readonly InteractiveHost Host;
public readonly bool SkipInitialization;
......@@ -26,8 +31,8 @@ private sealed class LazyRemoteService
public LazyRemoteService(InteractiveHost host, InteractiveHostOptions options, int instanceId, bool skipInitialization)
{
InitializedService = new AsyncLazy<InitializedRemoteService>(TryStartAndInitializeProcessAsync, cacheResult: true);
CancellationSource = new CancellationTokenSource();
_lazyInitializedService = new AsyncLazy<InitializedRemoteService>(TryStartAndInitializeProcessAsync, cacheResult: true);
_cancellationSource = new CancellationTokenSource();
InstanceId = instanceId;
Options = options;
Host = host;
......@@ -38,15 +43,21 @@ public void Dispose()
{
// Cancel the creation of the process if it is in progress.
// If it is the cancellation will clean up all resources allocated during the creation.
CancellationSource.Cancel();
_cancellationSource.Cancel();
// If the value has been calculated already, dispose the service.
if (InitializedService.TryGetValue(out var initializedService))
if (_lazyInitializedService.TryGetValue(out var initializedService))
{
initializedService.Service?.Dispose();
}
}
internal Task<InitializedRemoteService> GetInitializedServiceAsync()
=> _lazyInitializedService.GetValueAsync(_cancellationSource.Token);
internal InitializedRemoteService? TryGetInitializedService()
=> _lazyInitializedService.TryGetValue(out var service) ? service : default;
private async Task<InitializedRemoteService> TryStartAndInitializeProcessAsync(CancellationToken cancellationToken)
{
try
......@@ -75,13 +86,9 @@ private async Task<InitializedRemoteService> TryStartAndInitializeProcessAsync(C
});
// try to execute initialization script:
var initializationResult = await Async<RemoteExecutionResult>(remoteService, (service, operation) =>
{
service.InitializeContext(operation, Options.InitializationFile, isRestarting: InstanceId > 1);
}).ConfigureAwait(false);
var isRestarting = InstanceId > 1;
var initializationResult = await InvokeRemoteAsync<RemoteExecutionResult>(remoteService, nameof(Service.InitializeContextAsync), Options.InitializationFile, isRestarting).ConfigureAwait(false);
initializing = false;
if (!initializationResult.Success)
{
Host.ReportProcessExited(remoteService.Process);
......@@ -103,9 +110,99 @@ private async Task<InitializedRemoteService> TryStartAndInitializeProcessAsync(C
}
}
private Task<RemoteService?> TryStartProcessAsync(string hostPath, CultureInfo culture, CancellationToken cancellationToken)
private async Task<RemoteService?> TryStartProcessAsync(string hostPath, CultureInfo culture, CancellationToken cancellationToken)
{
int currentProcessId = Process.GetCurrentProcess().Id;
var pipeName = typeof(InteractiveHost).FullName + Guid.NewGuid();
var newProcess = new Process
{
StartInfo = new ProcessStartInfo(hostPath)
{
Arguments = pipeName + " " + currentProcessId,
WorkingDirectory = Host._initialWorkingDirectory,
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
StandardErrorEncoding = Encoding.UTF8,
StandardOutputEncoding = Encoding.UTF8
},
// enables Process.Exited event to be raised:
EnableRaisingEvents = true
};
newProcess.Start();
Host.InteractiveHostProcessCreated?.Invoke(newProcess);
int newProcessId = -1;
try
{
newProcessId = newProcess.Id;
}
catch
{
newProcessId = 0;
}
var clientStream = new NamedPipeClientStream(".", pipeName, PipeDirection.InOut, PipeOptions.Asynchronous);
JsonRpc jsonRpc;
void ProcessExitedBeforeEstablishingConnection(object sender, EventArgs e)
=> _cancellationSource.Cancel();
// Connecting the named pipe client would hang if the process exits before the connection is established,
// as the client waits for the server to become available. We signal the cancellation token to abort.
newProcess.Exited += ProcessExitedBeforeEstablishingConnection;
try
{
if (!CheckAlive(newProcess, hostPath))
{
return null;
}
await clientStream.ConnectAsync(cancellationToken).ConfigureAwait(false);
jsonRpc = JsonRpc.Attach(clientStream);
await jsonRpc.InvokeWithCancellationAsync(
nameof(Service.InitializeAsync),
new object[] { Host._replServiceProviderType.AssemblyQualifiedName, culture.Name },
cancellationToken).ConfigureAwait(false);
}
catch
{
if (CheckAlive(newProcess, hostPath))
{
RemoteService.InitiateTermination(newProcess, newProcessId);
}
return null;
}
finally
{
newProcess.Exited -= ProcessExitedBeforeEstablishingConnection;
}
return new RemoteService(Host, newProcess, newProcessId, jsonRpc);
}
private bool CheckAlive(Process process, string hostPath)
{
return Task.Run(() => Host.TryStartProcess(hostPath, culture, cancellationToken));
bool alive = process.IsAlive();
if (!alive)
{
string errorString = process.StandardError.ReadToEnd();
Host.WriteOutputInBackground(
isError: true,
string.Format(InteractiveHostResources.Failed_to_launch_0_process_exit_code_colon_1_with_output_colon, hostPath, process.ExitCode),
errorString);
}
return alive;
}
}
}
......
// 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.
#nullable enable
using System;
using System.Diagnostics;
using System.Runtime.Remoting;
using System.Threading.Tasks;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Interactive
{
internal partial class InteractiveHost
{
internal sealed class RemoteAsyncOperation<TResult> : MarshalByRefObject
{
private readonly RemoteService _remoteService;
private readonly TaskCompletionSource<TResult> _completion;
private readonly EventHandler _processExitedHandler;
internal RemoteAsyncOperation(RemoteService service)
{
_remoteService = service;
_completion = new TaskCompletionSource<TResult>();
_processExitedHandler = new EventHandler((_, __) => ProcessExited());
}
public override object? InitializeLifetimeService() => null;
public Task<TResult> ExecuteAsync(Action<Service, RemoteAsyncOperation<TResult>> action)
{
try
{
// async call to remote process:
action(_remoteService.Service, this);
_remoteService.Process.Exited += _processExitedHandler;
if (!_remoteService.Process.IsAlive())
{
ProcessExited();
}
return _completion.Task;
}
catch (RemotingException) when (!_remoteService.Process.IsAlive())
{
// the operation might have terminated the process:
ProcessExited();
return _completion.Task;
}
}
/// <summary>
/// Might be called remotely from the service.
/// </summary>
/// <returns>Returns true if the operation hasn't been completed until this call.</returns>
public void Completed(TResult result)
{
_remoteService.Process.Exited -= _processExitedHandler;
SetResult(result);
}
private void ProcessExited()
{
Completed(result: default!);
}
public void SetResult(TResult result)
{
// Warning: bug 9466 describes a rare race condition where this method can be called
// more than once. If you just call SetResult the second time, it will throw an
// InvalidOperationException saying: An attempt was made to transition a task to a final
// state when it had already completed. To work around it without complicated locks, we
// just call TrySetResult which in case where the Task is already in one of the three
// states (RunToCompletion, Faulted, Canceled) will just do nothing instead of throwing.
_completion.TrySetResult(result);
}
}
}
}
......@@ -7,10 +7,12 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Security.RightsManagement;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.ErrorReporting;
using Roslyn.Utilities;
using StreamJsonRpc;
namespace Microsoft.CodeAnalysis.Interactive
{
......@@ -19,7 +21,7 @@ internal partial class InteractiveHost
internal sealed class RemoteService
{
public readonly Process Process;
public readonly Service Service;
public readonly JsonRpc JsonRpc;
private readonly int _processId;
private readonly SemaphoreSlim _disposeSemaphore = new SemaphoreSlim(initialCount: 1);
private readonly bool _joinOutputWritingThreadsOnDisposal;
......@@ -30,13 +32,13 @@ internal sealed class RemoteService
private Thread? _readErrorOutputThread; // nulled on dispose
private volatile ProcessExitHandlerStatus _processExitHandlerStatus; // set to Handled on dispose
internal RemoteService(InteractiveHost host, Process process, int processId, Service service)
internal RemoteService(InteractiveHost host, Process process, int processId, JsonRpc jsonRpc)
{
Process = process;
Service = service;
JsonRpc = jsonRpc;
_host = host;
_joinOutputWritingThreadsOnDisposal = host._joinOutputWritingThreadsOnDisposal;
_joinOutputWritingThreadsOnDisposal = _host._joinOutputWritingThreadsOnDisposal;
_processId = processId;
_processExitHandlerStatus = ProcessExitHandlerStatus.Uninitialized;
......
......@@ -5,18 +5,18 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Ipc;
using System.Runtime.Serialization.Formatters;
using System.IO.Pipes;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Microsoft.CodeAnalysis.ErrorReporting;
using Roslyn.Utilities;
using StreamJsonRpc;
namespace Microsoft.CodeAnalysis.Interactive
{
......@@ -26,7 +26,7 @@ namespace Microsoft.CodeAnalysis.Interactive
/// <remarks>
/// Handles spawning of the host process and communication between the local callers and the remote session.
/// </remarks>
internal sealed partial class InteractiveHost
internal sealed partial class InteractiveHost : IDisposable
{
internal const bool DefaultIs64Bit = true;
......@@ -39,10 +39,6 @@ internal sealed partial class InteractiveHost
private LazyRemoteService? _lazyRemoteService;
private int _remoteServiceInstanceId;
// Remoting channel to communicate with the remote service.
private IpcServerChannel? _serverChannel;
private TextWriter _output;
private TextWriter _errorOutput;
private readonly object _outputGuard;
......@@ -73,10 +69,6 @@ internal sealed partial class InteractiveHost
_initialWorkingDirectory = workingDirectory;
_outputGuard = new object();
_errorOutputGuard = new object();
var serverProvider = new BinaryServerFormatterSinkProvider { TypeFilterLevel = TypeFilterLevel.Full };
_serverChannel = new IpcServerChannel(GenerateUniqueChannelLocalName(), "ReplChannel-" + Guid.NewGuid(), serverProvider);
ChannelServices.RegisterChannel(_serverChannel, ensureSecurity: false);
}
#region Test hooks
......@@ -85,153 +77,17 @@ internal sealed partial class InteractiveHost
internal event Action<char[], int>? ErrorOutputReceived;
internal Process? TryGetProcess()
{
var lazyRemoteService = _lazyRemoteService;
return (lazyRemoteService?.InitializedService != null &&
lazyRemoteService.InitializedService.TryGetValue(out var initializedService)) ? initializedService.Service.Process : null;
}
=> _lazyRemoteService?.TryGetInitializedService()?.Service.Process;
internal Service? TryGetService()
{
var initializedService = TryGetOrCreateRemoteServiceAsync().Result;
return initializedService.Service?.Service;
}
internal async Task<RemoteService> TryGetServiceAsync()
=> (await TryGetOrCreateRemoteServiceAsync().ConfigureAwait(false)).Service;
// Triggered whenever we create a fresh process.
// The ProcessExited event is not hooked yet.
internal event Action<Process>? InteractiveHostProcessCreated;
internal IpcServerChannel? Test_ServerChannel
=> _serverChannel;
#endregion
private static string GenerateUniqueChannelLocalName()
=> typeof(InteractiveHost).FullName + Guid.NewGuid();
private RemoteService? TryStartProcess(string hostPath, CultureInfo culture, CancellationToken cancellationToken)
{
Process? newProcess = null;
int newProcessId = -1;
Semaphore? semaphore = null;
try
{
int currentProcessId = Process.GetCurrentProcess().Id;
bool semaphoreCreated;
string semaphoreName;
while (true)
{
semaphoreName = "InteractiveHostSemaphore-" + Guid.NewGuid();
semaphore = new Semaphore(0, 1, semaphoreName, out semaphoreCreated);
if (semaphoreCreated)
{
break;
}
semaphore.Close();
cancellationToken.ThrowIfCancellationRequested();
}
var remoteServerPort = "InteractiveHostChannel-" + Guid.NewGuid();
var processInfo = new ProcessStartInfo(hostPath);
processInfo.Arguments = remoteServerPort + " " + semaphoreName + " " + currentProcessId;
processInfo.WorkingDirectory = _initialWorkingDirectory;
processInfo.CreateNoWindow = true;
processInfo.UseShellExecute = false;
processInfo.RedirectStandardOutput = true;
processInfo.RedirectStandardError = true;
processInfo.StandardErrorEncoding = Encoding.UTF8;
processInfo.StandardOutputEncoding = Encoding.UTF8;
newProcess = new Process();
newProcess.StartInfo = processInfo;
// enables Process.Exited event to be raised:
newProcess.EnableRaisingEvents = true;
newProcess.Start();
InteractiveHostProcessCreated?.Invoke(newProcess);
cancellationToken.ThrowIfCancellationRequested();
try
{
newProcessId = newProcess.Id;
}
catch
{
newProcessId = 0;
}
// sync:
while (!semaphore.WaitOne(_millisecondsTimeout))
{
if (!CheckAlive(newProcess, hostPath))
{
return null;
}
WriteOutputInBackground(isError: false, string.Format(InteractiveHostResources.Attempt_to_connect_to_process_Sharp_0_failed_retrying, newProcessId));
cancellationToken.ThrowIfCancellationRequested();
}
// instantiate remote service:
Service newService;
try
{
newService = (Service)Activator.GetObject(
typeof(Service),
"ipc://" + remoteServerPort + "/" + Service.ServiceName);
cancellationToken.ThrowIfCancellationRequested();
newService.Initialize(_replServiceProviderType, culture.Name);
}
catch (RemotingException) when (!CheckAlive(newProcess, hostPath))
{
return null;
}
return new RemoteService(this, newProcess, newProcessId, newService);
}
catch (OperationCanceledException)
{
if (newProcess != null)
{
RemoteService.InitiateTermination(newProcess, newProcessId);
}
return null;
}
finally
{
if (semaphore != null)
{
semaphore.Close();
}
}
}
private bool CheckAlive(Process process, string hostPath)
{
bool alive = process.IsAlive();
if (!alive)
{
string errorString = process.StandardError.ReadToEnd();
WriteOutputInBackground(
isError: true,
string.Format(InteractiveHostResources.Failed_to_launch_0_process_exit_code_colon_1_with_output_colon, hostPath, process.ExitCode),
errorString);
}
return alive;
}
~InteractiveHost()
{
DisposeRemoteService();
......@@ -240,8 +96,6 @@ private bool CheckAlive(Process process, string hostPath)
// Dispose may be called anytime.
public void Dispose()
{
DisposeChannel();
// Run this in background to avoid deadlocking with UIThread operations performing with active outputs.
_ = Task.Run(() => SetOutputs(TextWriter.Null, TextWriter.Null));
......@@ -254,15 +108,6 @@ private void DisposeRemoteService()
Interlocked.Exchange(ref _lazyRemoteService, null)?.Dispose();
}
private void DisposeChannel()
{
var serverChannel = Interlocked.Exchange(ref _serverChannel, null);
if (serverChannel != null)
{
ChannelServices.UnregisterChannel(serverChannel);
}
}
public void SetOutputs(TextWriter output, TextWriter errorOutput)
{
if (output == null)
......@@ -366,7 +211,7 @@ private async Task<InitializedRemoteService> TryGetOrCreateRemoteServiceAsync()
return default;
}
var initializedService = await currentRemoteService.InitializedService.GetValueAsync(currentRemoteService.CancellationSource.Token).ConfigureAwait(false);
var initializedService = await currentRemoteService.GetInitializedServiceAsync().ConfigureAwait(false);
if (initializedService.Service != null && initializedService.Service.Process.IsAlive())
{
return initializedService;
......@@ -405,33 +250,24 @@ private async Task<InitializedRemoteService> TryGetOrCreateRemoteServiceAsync()
return default;
}
private async Task<TResult> Async<TResult>(Action<Service, RemoteAsyncOperation<TResult>> action)
private async Task<TResult> InvokeRemoteAsync<TResult>(string targetName, params object?[] arguments)
{
try
var initializedRemoteService = await TryGetOrCreateRemoteServiceAsync().ConfigureAwait(false);
if (initializedRemoteService.Service == null)
{
var initializedService = await TryGetOrCreateRemoteServiceAsync().ConfigureAwait(false);
if (initializedService.Service == null)
{
return default!;
}
return await new RemoteAsyncOperation<TResult>(initializedService.Service).ExecuteAsync(action).ConfigureAwait(false);
}
catch (Exception e) when (FatalError.Report(e))
{
throw ExceptionUtilities.Unreachable;
return default!;
}
return await InvokeRemoteAsync<TResult>(initializedRemoteService.Service, targetName, arguments).ConfigureAwait(false);
}
private static async Task<TResult> Async<TResult>(RemoteService remoteService, Action<Service, RemoteAsyncOperation<TResult>> action)
private static async Task<TResult> InvokeRemoteAsync<TResult>(RemoteService remoteService, string targetName, params object?[] arguments)
{
try
{
return await new RemoteAsyncOperation<TResult>(remoteService).ExecuteAsync(action).ConfigureAwait(false);
return await remoteService.JsonRpc.InvokeAsync<TResult>(targetName, arguments).ConfigureAwait(false);
}
catch (Exception e) when (FatalError.Report(e))
catch (Exception e) when (e is ObjectDisposedException || !remoteService.Process.IsAlive())
{
throw ExceptionUtilities.Unreachable;
return default!;
}
}
......@@ -482,7 +318,7 @@ public async Task<RemoteExecutionResult> ResetAsync(InteractiveHostOptions optio
public Task<RemoteExecutionResult> ExecuteAsync(string code)
{
Contract.ThrowIfNull(code);
return Async<RemoteExecutionResult>((service, operation) => service.Execute(operation, code));
return InvokeRemoteAsync<RemoteExecutionResult>(nameof(Service.ExecuteAsync), code);
}
/// <summary>
......@@ -497,11 +333,9 @@ public Task<RemoteExecutionResult> ExecuteAsync(string code)
public Task<RemoteExecutionResult> ExecuteFileAsync(string path)
{
Contract.ThrowIfNull(path);
return Async<RemoteExecutionResult>((service, operation) => service.ExecuteFile(operation, path));
return InvokeRemoteAsync<RemoteExecutionResult>(nameof(Service.ExecuteFileAsync), path);
}
/// <summary>
/// Asynchronously adds a reference to the set of available references for next submission.
/// <summary> /// Asynchronously adds a reference to the set of available references for next submission.
/// </summary>
/// <param name="reference">The reference to add.</param>
/// <remarks>
......@@ -511,7 +345,7 @@ public Task<RemoteExecutionResult> ExecuteFileAsync(string path)
public Task<bool> AddReferenceAsync(string reference)
{
Contract.ThrowIfNull(reference);
return Async<bool>((service, operation) => service.AddReference(operation, reference));
return InvokeRemoteAsync<bool>(nameof(Service.AddReferenceAsync), reference);
}
/// <summary>
......@@ -523,7 +357,7 @@ public Task<RemoteExecutionResult> SetPathsAsync(string[] referenceSearchPaths,
Contract.ThrowIfNull(sourceSearchPaths);
Contract.ThrowIfNull(baseDirectory);
return Async<RemoteExecutionResult>((service, operation) => service.SetPaths(operation, referenceSearchPaths, sourceSearchPaths, baseDirectory));
return InvokeRemoteAsync<RemoteExecutionResult>(nameof(Service.SetPathsAsync), referenceSearchPaths, sourceSearchPaths, baseDirectory);
}
#endregion
......
......@@ -60,7 +60,9 @@
<Compile Include="..\..\Compilers\Core\Portable\FileSystem\PathKind.cs" Link="Utilities\PathKind.cs" />
</ItemGroup>
<ItemGroup>
<Compile Update="InteractiveHostResources.Designer.cs">
<PackageReference Include="StreamJsonRpc" Version="$(StreamJsonRpcVersion)" />
</ItemGroup>
<ItemGroup> <Compile Update="InteractiveHostResources.Designer.cs">
<DependentUpon>InteractiveHostResources.resx</DependentUpon>
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
......
......@@ -7,12 +7,8 @@
extern alias InteractiveHost;
using System;
using System.Reflection;
using System.Runtime.Remoting.Channels.Ipc;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Interactive;
namespace Microsoft.CodeAnalysis.UnitTests.Interactive
{
......@@ -30,16 +26,5 @@ internal static string GetInteractiveHostDirectory()
typeof(InteractiveHost),
typeof(CSharpCompilation)
};
private static readonly FieldInfo s_ipcServerChannelListenerThread = typeof(IpcServerChannel).GetField("_listenerThread", BindingFlags.NonPublic | BindingFlags.Instance);
internal static void DisposeInteractiveHostProcess(InteractiveHost host)
{
var serverChannel = host.Test_ServerChannel;
host.Dispose();
var listenerThread = (Thread)s_ipcServerChannelListenerThread.GetValue(serverChannel);
listenerThread.Join();
}
}
}
......@@ -15,58 +15,29 @@
namespace Microsoft.CodeAnalysis.UnitTests.Interactive
{
using System.Threading.Tasks;
using InteractiveHost::Microsoft.CodeAnalysis.Interactive;
public sealed class StressTests : AbstractInteractiveHostTests
{
private readonly List<InteractiveHost> _processes = new List<InteractiveHost>();
private readonly List<Thread> _threads = new List<Thread>();
public override void Dispose()
{
try
{
foreach (var process in _processes)
{
DisposeInteractiveHostProcess(process);
}
foreach (var thread in _threads)
{
thread.Join();
}
}
finally
{
base.Dispose();
}
}
private InteractiveHost CreateProcess()
{
var p = new InteractiveHost(typeof(CSharpReplServiceProvider), ".", millisecondsTimeout: 1, joinOutputWritingThreadsOnDisposal: true);
_processes.Add(p);
return p;
}
[Fact]
public void TestKill()
public async Task TestKill()
{
for (int sleep = 0; sleep < 20; sleep++)
{
TestKillAfter(sleep);
await TestKillAfterAsync(sleep).ConfigureAwait(false);
}
}
private void TestKillAfter(int milliseconds)
private async Task TestKillAfterAsync(int milliseconds)
{
var p = CreateProcess();
using var host = new InteractiveHost(typeof(CSharpReplServiceProvider), ".", millisecondsTimeout: 1, joinOutputWritingThreadsOnDisposal: true);
p.InteractiveHostProcessCreated += new Action<Process>(proc =>
host.InteractiveHostProcessCreated += new Action<Process>(proc =>
{
var t = new Thread(() =>
_ = Task.Run(async () =>
{
Thread.Sleep(milliseconds);
await Task.Delay(milliseconds).ConfigureAwait(false);
try
{
......@@ -76,18 +47,13 @@ private void TestKillAfter(int milliseconds)
{
}
});
t.Name = "Test Thread";
_threads.Add(t);
t.Start();
});
p.ResetAsync(new InteractiveHostOptions(GetInteractiveHostDirectory())).Wait();
await host.ResetAsync(new InteractiveHostOptions(GetInteractiveHostDirectory())).ConfigureAwait(false);
for (int j = 0; j < 10; j++)
{
var rs = p.ExecuteAsync("1+1");
rs.Wait(CancellationToken.None);
await host.ExecuteAsync("1+1").ConfigureAwait(false);
}
}
}
......
......@@ -182,6 +182,7 @@ public static class Features
public const string CodeActionsUseIsNullCheck = "CodeActions.UseIsNullCheck";
public const string CodeActionsUseLocalFunction = "CodeActions.UseLocalFunction";
public const string CodeActionsUseNamedArguments = "CodeActions.UseNamedArguments";
public const string CodeActionsUsePatternCombinators = "CodeActions.UsePatternCombinators";
public const string CodeActionsUseNullPropagation = "CodeActions.UseNullPropagation";
public const string CodeActionsUseObjectInitializer = "CodeActions.UseObjectInitializer";
public const string CodeActionsUseRangeOperator = "CodeActions.UseRangeOperator";
......
......@@ -483,6 +483,9 @@
<data name="Prefer_conditional_delegate_call" xml:space="preserve">
<value>Prefer conditional delegate call</value>
</data>
<data name="Prefer_pattern_matching" xml:space="preserve">
<value>Prefer pattern matching</value>
</data>
<data name="Prefer_pattern_matching_over_is_with_cast_check" xml:space="preserve">
<value>Prefer pattern matching over 'is' with 'cast' check</value>
</data>
......
......@@ -667,6 +667,12 @@ public string Style_PreferSwitchExpression
set { SetXmlOption(CSharpCodeStyleOptions.PreferSwitchExpression, value); }
}
public string Style_PreferPatternMatching
{
get { return GetXmlOption(CSharpCodeStyleOptions.PreferPatternMatching); }
set { SetXmlOption(CSharpCodeStyleOptions.PreferPatternMatching, value); }
}
public string Style_PreferPatternMatchingOverAsWithNullCheck
{
get { return GetXmlOption(CSharpCodeStyleOptions.PreferPatternMatchingOverAsWithNullCheck); }
......
......@@ -360,6 +360,26 @@ void M2()
//]
}}
}}
";
private static readonly string s_preferPatternMatching = $@"
class C
{{
void M1()
{{
//[
// {ServicesVSResources.Prefer_colon}
return num is 1 or 2;
//]
}}
void M2()
{{
//[
// {ServicesVSResources.Over_colon}
return num == 1 || num == 2;
//]
}}
}}
";
private static readonly string s_preferPatternMatchingOverAsWithNullCheck = $@"
......@@ -1685,6 +1705,7 @@ internal StyleViewModel(OptionStore optionStore, IServiceProvider serviceProvide
CodeStyleItems.Add(new BooleanCodeStyleOptionViewModel(CodeStyleOptions2.PreferCollectionInitializer, ServicesVSResources.Prefer_collection_initializer, s_preferCollectionInitializer, s_preferCollectionInitializer, this, optionStore, expressionPreferencesGroupTitle));
CodeStyleItems.Add(new BooleanCodeStyleOptionViewModel(CodeStyleOptions2.PreferSimplifiedBooleanExpressions, ServicesVSResources.Prefer_simplified_boolean_expressions, s_preferSimplifiedConditionalExpression, s_preferSimplifiedConditionalExpression, this, optionStore, expressionPreferencesGroupTitle));
CodeStyleItems.Add(new BooleanCodeStyleOptionViewModel(CSharpCodeStyleOptions.PreferSwitchExpression, CSharpVSResources.Prefer_switch_expression, s_preferSwitchExpression, s_preferSwitchExpression, this, optionStore, expressionPreferencesGroupTitle));
CodeStyleItems.Add(new BooleanCodeStyleOptionViewModel(CSharpCodeStyleOptions.PreferPatternMatching, CSharpVSResources.Prefer_pattern_matching, s_preferPatternMatching, s_preferPatternMatching, this, optionStore, expressionPreferencesGroupTitle));
CodeStyleItems.Add(new BooleanCodeStyleOptionViewModel(CSharpCodeStyleOptions.PreferPatternMatchingOverIsWithCastCheck, CSharpVSResources.Prefer_pattern_matching_over_is_with_cast_check, s_preferPatternMatchingOverIsWithCastCheck, s_preferPatternMatchingOverIsWithCastCheck, this, optionStore, expressionPreferencesGroupTitle));
CodeStyleItems.Add(new BooleanCodeStyleOptionViewModel(CSharpCodeStyleOptions.PreferPatternMatchingOverAsWithNullCheck, CSharpVSResources.Prefer_pattern_matching_over_as_with_null_check, s_preferPatternMatchingOverAsWithNullCheck, s_preferPatternMatchingOverAsWithNullCheck, this, optionStore, expressionPreferencesGroupTitle));
CodeStyleItems.Add(new BooleanCodeStyleOptionViewModel(CodeStyleOptions2.PreferConditionalExpressionOverAssignment, ServicesVSResources.Prefer_conditional_expression_over_if_with_assignments, s_preferConditionalExpressionOverIfWithAssignments, s_preferConditionalExpressionOverIfWithAssignments, this, optionStore, expressionPreferencesGroupTitle));
......
......@@ -72,6 +72,11 @@
<target state="translated">U kontrol rovnosti odkazů dávat přednost možnosti is null</target>
<note>'is null' is a C# string and should not be localized.</note>
</trans-unit>
<trans-unit id="Prefer_pattern_matching">
<source>Prefer pattern matching</source>
<target state="new">Prefer pattern matching</target>
<note />
</trans-unit>
<trans-unit id="Prefer_switch_expression">
<source>Prefer switch expression</source>
<target state="translated">Preferovat výraz switch</target>
......
......@@ -72,6 +72,11 @@
<target state="translated">"is null" für Verweisübereinstimmungsprüfungen vorziehen</target>
<note>'is null' is a C# string and should not be localized.</note>
</trans-unit>
<trans-unit id="Prefer_pattern_matching">
<source>Prefer pattern matching</source>
<target state="new">Prefer pattern matching</target>
<note />
</trans-unit>
<trans-unit id="Prefer_switch_expression">
<source>Prefer switch expression</source>
<target state="translated">Switch-Ausdruck bevorzugen</target>
......
......@@ -72,6 +72,11 @@
<target state="translated">Preferir “is null” para comprobaciones de igualdad de referencias</target>
<note>'is null' is a C# string and should not be localized.</note>
</trans-unit>
<trans-unit id="Prefer_pattern_matching">
<source>Prefer pattern matching</source>
<target state="new">Prefer pattern matching</target>
<note />
</trans-unit>
<trans-unit id="Prefer_switch_expression">
<source>Prefer switch expression</source>
<target state="translated">Preferir expresión switch</target>
......
......@@ -72,6 +72,11 @@
<target state="translated">Préférer 'is nul' pour les vérifications d'égalité de référence</target>
<note>'is null' is a C# string and should not be localized.</note>
</trans-unit>
<trans-unit id="Prefer_pattern_matching">
<source>Prefer pattern matching</source>
<target state="new">Prefer pattern matching</target>
<note />
</trans-unit>
<trans-unit id="Prefer_switch_expression">
<source>Prefer switch expression</source>
<target state="translated">Préférer l'expression switch</target>
......
......@@ -72,6 +72,11 @@
<target state="translated">Preferisci 'is null' per i controlli di uguaglianza dei riferimenti</target>
<note>'is null' is a C# string and should not be localized.</note>
</trans-unit>
<trans-unit id="Prefer_pattern_matching">
<source>Prefer pattern matching</source>
<target state="new">Prefer pattern matching</target>
<note />
</trans-unit>
<trans-unit id="Prefer_switch_expression">
<source>Prefer switch expression</source>
<target state="translated">Preferisci espressione switch</target>
......
......@@ -72,6 +72,11 @@
<target state="translated">参照の等値性のチェックには 'is null' を優先する</target>
<note>'is null' is a C# string and should not be localized.</note>
</trans-unit>
<trans-unit id="Prefer_pattern_matching">
<source>Prefer pattern matching</source>
<target state="new">Prefer pattern matching</target>
<note />
</trans-unit>
<trans-unit id="Prefer_switch_expression">
<source>Prefer switch expression</source>
<target state="translated">switch 式を優先する</target>
......
......@@ -72,6 +72,11 @@
<target state="translated">참조 같음 검사에 대해 'is null' 선호</target>
<note>'is null' is a C# string and should not be localized.</note>
</trans-unit>
<trans-unit id="Prefer_pattern_matching">
<source>Prefer pattern matching</source>
<target state="new">Prefer pattern matching</target>
<note />
</trans-unit>
<trans-unit id="Prefer_switch_expression">
<source>Prefer switch expression</source>
<target state="translated">switch 식 선호</target>
......
......@@ -72,6 +72,11 @@
<target state="translated">Preferuj wyrażenie „is null” w przypadku sprawdzeń odwołań pod kątem równości</target>
<note>'is null' is a C# string and should not be localized.</note>
</trans-unit>
<trans-unit id="Prefer_pattern_matching">
<source>Prefer pattern matching</source>
<target state="new">Prefer pattern matching</target>
<note />
</trans-unit>
<trans-unit id="Prefer_switch_expression">
<source>Prefer switch expression</source>
<target state="translated">Preferuj wyrażenie switch</target>
......
......@@ -72,6 +72,11 @@
<target state="translated">Preferir 'is null' para as verificações de igualdade de referência</target>
<note>'is null' is a C# string and should not be localized.</note>
</trans-unit>
<trans-unit id="Prefer_pattern_matching">
<source>Prefer pattern matching</source>
<target state="new">Prefer pattern matching</target>
<note />
</trans-unit>
<trans-unit id="Prefer_switch_expression">
<source>Prefer switch expression</source>
<target state="translated">Preferir a expressão switch</target>
......
......@@ -72,6 +72,11 @@
<target state="translated">Использовать "is null" вместо проверки ссылок на равенство.</target>
<note>'is null' is a C# string and should not be localized.</note>
</trans-unit>
<trans-unit id="Prefer_pattern_matching">
<source>Prefer pattern matching</source>
<target state="new">Prefer pattern matching</target>
<note />
</trans-unit>
<trans-unit id="Prefer_switch_expression">
<source>Prefer switch expression</source>
<target state="translated">Предпочитать выражение switch</target>
......
......@@ -72,6 +72,11 @@
<target state="translated">Başvuru eşitliği denetimleri için 'is null'ı tercih et</target>
<note>'is null' is a C# string and should not be localized.</note>
</trans-unit>
<trans-unit id="Prefer_pattern_matching">
<source>Prefer pattern matching</source>
<target state="new">Prefer pattern matching</target>
<note />
</trans-unit>
<trans-unit id="Prefer_switch_expression">
<source>Prefer switch expression</source>
<target state="translated">Switch ifadesini tercih et</target>
......
......@@ -72,6 +72,11 @@
<target state="translated">引用相等检查偏好 “is null”</target>
<note>'is null' is a C# string and should not be localized.</note>
</trans-unit>
<trans-unit id="Prefer_pattern_matching">
<source>Prefer pattern matching</source>
<target state="new">Prefer pattern matching</target>
<note />
</trans-unit>
<trans-unit id="Prefer_switch_expression">
<source>Prefer switch expression</source>
<target state="translated">首选 switch 表达式</target>
......
......@@ -72,6 +72,11 @@
<target state="translated">參考相等檢查最好使用 'is null'</target>
<note>'is null' is a C# string and should not be localized.</note>
</trans-unit>
<trans-unit id="Prefer_pattern_matching">
<source>Prefer pattern matching</source>
<target state="new">Prefer pattern matching</target>
<note />
</trans-unit>
<trans-unit id="Prefer_switch_expression">
<source>Prefer switch expression</source>
<target state="translated">建議使用 switch 運算式</target>
......
......@@ -103,6 +103,7 @@ csharp_style_expression_bodied_properties = true:silent
# Pattern matching preferences
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
csharp_style_prefer_pattern_matching = true:silent
csharp_style_prefer_switch_expression = true:suggestion
# Null-checking preferences
......@@ -316,6 +317,7 @@ csharp_style_expression_bodied_properties = true:silent
# Pattern matching preferences
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
csharp_style_prefer_pattern_matching = true:silent
csharp_style_prefer_switch_expression = true:suggestion
# Null-checking preferences
......
......@@ -69,6 +69,21 @@
<NuGetPackageToIncludeInVsix Include="System.Reflection.Metadata" />
<NuGetPackageToIncludeInVsix Include="System.Text.Encoding.CodePages" />
<NuGetPackageToIncludeInVsix Include="System.Runtime.CompilerServices.Unsafe" />
<!-- JSON-RPC dependencies -->
<NuGetPackageToIncludeInVsix Include="MessagePack" />
<NuGetPackageToIncludeInVsix Include="Microsoft.Bcl.AsyncInterfaces" />
<NuGetPackageToIncludeInVsix Include="Microsoft.VisualStudio.Threading" />
<NuGetPackageToIncludeInVsix Include="Microsoft.VisualStudio.Validation" />
<NuGetPackageToIncludeInVsix Include="Nerdbank.Streams" />
<NuGetPackageToIncludeInVsix Include="Newtonsoft.Json" />
<NuGetPackageToIncludeInVsix Include="StreamJsonRpc" />
<NuGetPackageToIncludeInVsix Include="System.IO.Pipelines" />
<NuGetPackageToIncludeInVsix Include="System.Net.Http" />
<NuGetPackageToIncludeInVsix Include="System.Net.WebSockets" />
<NuGetPackageToIncludeInVsix Include="System.Reflection.Emit" />
<NuGetPackageToIncludeInVsix Include="System.Threading.Tasks.Dataflow" />
<NuGetPackageToIncludeInVsix Include="System.Threading.Tasks.Extensions" />
<NuGetPackageToIncludeInVsix Include="System.ValueTuple" />
</ItemGroup>
<Import Project="$(VSToolsPath)\vssdk\Microsoft.VsSDK.targets" Condition="Exists('$(VSToolsPath)\vssdk\Microsoft.VsSDK.targets')" />
......
......@@ -14,6 +14,7 @@ namespace Microsoft.CodeAnalysis.CSharp.CodeStyle
internal static partial class CSharpCodeStyleOptions
{
private static readonly CodeStyleOption2<bool> s_trueWithSuggestionEnforcement = new CodeStyleOption2<bool>(value: true, notification: NotificationOption2.Suggestion);
private static readonly CodeStyleOption2<bool> s_trueWithSilentEnforcement = new CodeStyleOption2<bool>(value: true, notification: NotificationOption2.Silent);
private static readonly ImmutableArray<IOption2>.Builder s_allOptionsBuilder = ImmutableArray.CreateBuilder<IOption2>();
......@@ -57,6 +58,13 @@ private static Option2<T> CreateOption<T>(OptionGroup group, string name, T defa
EditorConfigStorageLocation.ForBoolCodeStyleOption("csharp_style_prefer_switch_expression"),
new RoamingProfileStorageLocation($"TextEditor.CSharp.Specific.{nameof(PreferSwitchExpression)}")});
public static readonly Option2<CodeStyleOption2<bool>> PreferPatternMatching = CreateOption(
CSharpCodeStyleOptionGroups.PatternMatching, nameof(PreferPatternMatching),
defaultValue: s_trueWithSilentEnforcement,
storageLocations: new OptionStorageLocation2[] {
EditorConfigStorageLocation.ForBoolCodeStyleOption("csharp_style_prefer_pattern_matching"),
new RoamingProfileStorageLocation($"TextEditor.CSharp.Specific.{nameof(PreferPatternMatching)}")});
public static readonly Option2<CodeStyleOption2<bool>> PreferPatternMatchingOverAsWithNullCheck = CreateOption(
CSharpCodeStyleOptionGroups.PatternMatching, nameof(PreferPatternMatchingOverAsWithNullCheck),
defaultValue: s_trueWithSuggestionEnforcement,
......@@ -307,6 +315,7 @@ public static IEnumerable<Option2<CodeStyleOption2<bool>>> GetCodeStyleOptions()
yield return VarElsewhere;
yield return PreferConditionalDelegateCall;
yield return PreferSwitchExpression;
yield return PreferPatternMatching;
yield return PreferPatternMatchingOverAsWithNullCheck;
yield return PreferPatternMatchingOverIsWithCastCheck;
yield return PreferSimpleDefaultExpression;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册