private bool TryDecomposeIfCondition( IConditionalOperation ifStatement, out ISymbol localOrParameter) { localOrParameter = null; var condition = ifStatement.Condition; if (!(condition is IBinaryOperation binaryOperator)) { return(false); } if (binaryOperator.OperatorKind != BinaryOperatorKind.Equals) { return(false); } if (IsNull(binaryOperator.LeftOperand)) { return(TryGetLocalOrParameterSymbol( binaryOperator.RightOperand, out localOrParameter)); } if (IsNull(binaryOperator.RightOperand)) { return(TryGetLocalOrParameterSymbol( binaryOperator.LeftOperand, out localOrParameter)); } return(false); }
private EvaluationResult AnalyzeConditional([NotNull] IConditionalOperation conditional) { EvaluationResult trueResult = AnalyzeExpression(conditional.WhenTrue); EvaluationResult falseResult = AnalyzeExpression(conditional.WhenFalse); return(EvaluationResult.Unify(trueResult, falseResult)); }
private void Remove([NotNull] IConditionalOperation ifStatementToRemove, [NotNull] IDictionary <Location, IConditionalOperation> ifStatements) { Location location = ifStatementToRemove.TryGetLocationForKeyword(); ifStatements.Remove(location); }
public static bool CanConvert( ISyntaxFacts syntaxFacts, IConditionalOperation ifOperation, IOperation whenTrue, IOperation whenFalse) { // Will likely screw things up if the if directive spans any preprocessor directives. So // do not offer for now. Note: we pass in both the node for the ifOperation and the // whenFalse portion. The whenFalse portion isn't necessary under the ifOperation. For // example in: // // ```c# // #if DEBUG // if (check) // return 3; // #endif // return 2; // ``` // // In this case, we want to see that this cross the `#endif` if (syntaxFacts.SpansPreprocessorDirective(ifOperation.Syntax, whenFalse.Syntax)) { return(false); } // User may have comments on the when-true/when-false statements. These statements can // be very important. Often they indicate why the true/false branches are important in // the first place. We don't have any place to put these, so we don't offer here. if (HasRegularComments(syntaxFacts, whenTrue.Syntax) || HasRegularComments(syntaxFacts, whenFalse.Syntax)) { return(false); } return(true); }
private bool TryConvertWhenAssignmentToLocalDeclaredImmediateAbove( ISyntaxFactsService syntaxFacts, SyntaxEditor editor, IConditionalOperation ifOperation, ISimpleAssignmentOperation?trueAssignment, ISimpleAssignmentOperation?falseAssignment, TExpressionSyntax conditionalExpression) { if (!TryFindMatchingLocalDeclarationImmediatelyAbove( ifOperation, trueAssignment, falseAssignment, out var localDeclarationOperation, out var declarator)) { return(false); } // We found a valid local declaration right above the if-statement. var localDeclaration = localDeclarationOperation.Syntax; var variable = GetDeclaratorSyntax(declarator); // Initialize that variable with the conditional expression. var updatedVariable = WithInitializer(variable, conditionalExpression); // Because we merged the initialization and the variable, the variable may now be able // to use 'var' (c#), or elide its type (vb). Add the simplification annotation // appropriately so that can happen later down the line. var updatedLocalDeclaration = localDeclaration.ReplaceNode(variable, updatedVariable); updatedLocalDeclaration = AddSimplificationToType( (TLocalDeclarationStatementSyntax)updatedLocalDeclaration); editor.ReplaceNode(localDeclaration, updatedLocalDeclaration); editor.RemoveNode(ifOperation.Syntax, GetRemoveOptions(syntaxFacts, ifOperation.Syntax)); return(true); }
/// <summary> /// Helper to create a conditional expression out of two original IOperation values /// corresponding to the whenTrue and whenFalse parts. The helper will add the appropriate /// annotations and casts to ensure that the conditional expression preserves semantics, but /// is also properly simplified and formatted. /// </summary> protected async Task <TExpressionSyntax> CreateConditionalExpressionAsync( Document document, IConditionalOperation ifOperation, IOperation trueStatement, IOperation falseStatement, IOperation trueValue, IOperation falseValue, bool isRef, CancellationToken cancellationToken) { var generator = SyntaxGenerator.GetGenerator(document); var syntaxFacts = document.GetLanguageService <ISyntaxFactsService>(); var condition = ifOperation.Condition.Syntax; var conditionalExpression = (TConditionalExpressionSyntax)generator.ConditionalExpression( condition.WithoutTrivia(), MakeRef(generator, isRef, CastValueIfNecessary(generator, trueValue)), MakeRef(generator, isRef, CastValueIfNecessary(generator, falseValue))); conditionalExpression = conditionalExpression.WithAdditionalAnnotations(Simplifier.Annotation); var makeMultiLine = await MakeMultiLineAsync( document, condition, trueValue.Syntax, falseValue.Syntax, cancellationToken).ConfigureAwait(false); if (makeMultiLine) { conditionalExpression = conditionalExpression.WithAdditionalAnnotations( SpecializedFormattingAnnotation); } return(MakeRef(generator, isRef, conditionalExpression)); }
private static bool ValueIsAccessed(SemanticModel semanticModel, IConditionalOperation ifOperation, IBlockOperation containingBlock, ISymbol localOrParameter, IExpressionStatementOperation expressionStatement, IAssignmentOperation assignmentExpression) { var statements = containingBlock.Operations; var ifOperationIndex = statements.IndexOf(ifOperation); var expressionStatementIndex = statements.IndexOf(expressionStatement); if (expressionStatementIndex > ifOperationIndex + 1) { // There are intermediary statements between the check and the assignment. // Make sure they don't try to access the local. var dataFlow = semanticModel.AnalyzeDataFlow( statements[ifOperationIndex + 1].Syntax, statements[expressionStatementIndex - 1].Syntax); if (dataFlow.ReadInside.Contains(localOrParameter) || dataFlow.WrittenInside.Contains(localOrParameter)) { return(true); } } // Also, have to make sure there is no read/write of the local/parameter on the left // of the assignment. For example: map[val.Id] = val; var exprDataFlow = semanticModel.AnalyzeDataFlow(assignmentExpression.Target.Syntax); return(exprDataFlow.ReadInside.Contains(localOrParameter) || exprDataFlow.WrittenInside.Contains(localOrParameter)); }
public static bool TryMatchPattern( ISyntaxFacts syntaxFacts, IConditionalOperation ifOperation, out ISimpleAssignmentOperation trueAssignment, out ISimpleAssignmentOperation falseAssignment) { trueAssignment = null; falseAssignment = null; var trueStatement = ifOperation.WhenTrue; var falseStatement = ifOperation.WhenFalse; trueStatement = UseConditionalExpressionHelpers.UnwrapSingleStatementBlock(trueStatement); falseStatement = UseConditionalExpressionHelpers.UnwrapSingleStatementBlock(falseStatement); if (!TryGetAssignment(trueStatement, out trueAssignment) || !TryGetAssignment(falseStatement, out falseAssignment)) { return(false); } // The left side of both assignment statements has to be syntactically identical (modulo // trivia differences). if (!syntaxFacts.AreEquivalent(trueAssignment.Target.Syntax, falseAssignment.Target.Syntax)) { return(false); } return(UseConditionalExpressionHelpers.CanConvert( syntaxFacts, ifOperation, trueAssignment, falseAssignment)); }
public IfElseIfConstructAnalyzer([NotNull] IfStatementAnalyzer owner, [NotNull] IConditionalOperation topIfStatement) { this.owner = owner; topIfKeywordLocation = topIfStatement.TryGetLocationForKeyword(); ifStatement = topIfStatement; }
private static AbstractExpression ReadConditional(IConditionalOperation op) { return(new ConditionalExpression { Condition = ReadExpression(op.Condition), WhenTrue = ReadExpression(op.WhenTrue), WhenFalse = ReadExpression(op.WhenFalse) }); }
public override Location VisitConditional([NotNull] IConditionalOperation operation, [CanBeNull] object argument) { if (operation.IsStatement()) { var syntax = (IfStatementSyntax)operation.Syntax; return(syntax.IfKeyword.GetLocation()); } return(base.VisitConditional(operation, argument)); }
public override void VisitConditional([NotNull] IConditionalOperation operation) { if (operation.IsStatement()) { Location location = operation.GetLocationForKeyword(); CollectedIfStatements.Add(location, operation); } base.VisitConditional(operation); }
private void AnalyzeTopIfStatement() { IConditionalOperation ifStatement = ConsumeNextIfStatement(); if (IsIfElseIfConstruct(ifStatement)) { var analyzer = new IfElseIfConstructAnalyzer(this, ifStatement); analyzer.Analyze(); } }
public override void VisitConditional([NotNull] IConditionalOperation operation) { if (operation.IsStatement()) { Visit(operation.Condition); } else { base.VisitConditional(operation); } }
protected override bool TryMatchPattern( IConditionalOperation ifOperation, ISymbol containingSymbol ) => UseConditionalExpressionForAssignmentHelpers.TryMatchPattern( GetSyntaxFacts(), ifOperation, out _, out _, out _, out _ );
public override TAbstractAnalysisValue VisitConditional(IConditionalOperation operation, object argument) { var _ = Visit(operation.Condition, argument); var whenTrue = Visit(operation.WhenTrue, argument); var whenFalse = Visit(operation.WhenFalse, argument); if (operation.Condition.ConstantValue.HasValue && operation.Condition.ConstantValue.Value is bool condition) { return(condition ? whenTrue : whenFalse); } return(ValueDomain.Merge(whenTrue, whenFalse)); }
public override void VisitConditional(IConditionalOperation operation) { Assert.Equal(OperationKind.Conditional, operation.Kind); bool isRef = operation.IsRef; if (operation.WhenFalse != null) { AssertEx.Equal(new[] { operation.Condition, operation.WhenTrue, operation.WhenFalse }, operation.Children); } else { AssertEx.Equal(new[] { operation.Condition, operation.WhenTrue }, operation.Children); } }
private bool TryFindAssignmentExpression( IBlockOperation containingBlock, IConditionalOperation ifOperation, ISymbol localOrParameter, out IExpressionStatementOperation expressionStatement, out IAssignmentOperation assignmentExpression ) { var ifOperationIndex = containingBlock.Operations.IndexOf(ifOperation); // walk forward until we find an assignment of this local/parameter into // something else. for (var i = ifOperationIndex + 1; i < containingBlock.Operations.Length; i++) { expressionStatement = containingBlock.Operations[i] as IExpressionStatementOperation; if (expressionStatement == null) { continue; } assignmentExpression = expressionStatement.Operation as IAssignmentOperation; if (assignmentExpression == null) { continue; } if ( !TryGetLocalOrParameterSymbol( assignmentExpression.Value, out var assignmentValue ) ) { continue; } if (!Equals(localOrParameter, assignmentValue)) { continue; } return(true); } expressionStatement = null; assignmentExpression = null; return(false); }
public override void VisitConditional([NotNull] IConditionalOperation operation) { EvaluationResult trueResult = owner.AnalyzeExpression(operation.WhenTrue); if (operation.WhenFalse == null || trueResult.IsDeferred) { Result.CopyFrom(trueResult); } else { EvaluationResult falseResult = owner.AnalyzeExpression(operation.WhenFalse); Result.CopyFrom(EvaluationResult.Unify(trueResult, falseResult)); } }
/// <summary> /// Helper to create a conditional expression out of two original IOperation values /// corresponding to the whenTrue and whenFalse parts. The helper will add the appropriate /// annotations and casts to ensure that the conditional expression preserves semantics, but /// is also properly simplified and formatted. /// </summary> protected async Task <TExpressionSyntax> CreateConditionalExpressionAsync( Document document, IConditionalOperation ifOperation, IOperation trueStatement, IOperation falseStatement, IOperation trueValue, IOperation falseValue, bool isRef, CancellationToken cancellationToken) { var generator = SyntaxGenerator.GetGenerator(document); var generatorInternal = document.GetRequiredLanguageService <SyntaxGeneratorInternal>(); var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); var condition = ifOperation.Condition.Syntax; if (!isRef) { // If we are going to generate "expr ? true : false" then just generate "expr" // instead. if (IsBooleanLiteral(trueValue, true) && IsBooleanLiteral(falseValue, false)) { return((TExpressionSyntax)condition.WithoutTrivia()); } // If we are going to generate "expr ? false : true" then just generate "!expr" // instead. if (IsBooleanLiteral(trueValue, false) && IsBooleanLiteral(falseValue, true)) { return((TExpressionSyntax)generator.Negate(generatorInternal, condition, semanticModel, cancellationToken).WithoutTrivia()); } } var conditionalExpression = (TConditionalExpressionSyntax)generator.ConditionalExpression( condition.WithoutTrivia(), MakeRef(generatorInternal, isRef, CastValueIfNecessary(generator, trueStatement, trueValue)), MakeRef(generatorInternal, isRef, CastValueIfNecessary(generator, falseStatement, falseValue))); conditionalExpression = conditionalExpression.WithAdditionalAnnotations(Simplifier.Annotation); var makeMultiLine = await MakeMultiLineAsync( document, condition, trueValue.Syntax, falseValue.Syntax, cancellationToken).ConfigureAwait(false); if (makeMultiLine) { conditionalExpression = conditionalExpression.WithAdditionalAnnotations( SpecializedFormattingAnnotation); } return(MakeRef(generatorInternal, isRef, conditionalExpression)); }
private void ConvertOnlyIfToConditionalExpression( SyntaxEditor editor, IConditionalOperation ifOperation, ISimpleAssignmentOperation trueAssignment, ISimpleAssignmentOperation falseAssignment, TExpressionSyntax conditionalExpression) { var generator = editor.Generator; var ifStatement = (TIfStatementSyntax)ifOperation.Syntax; var expressionStatement = (TStatementSyntax)generator.ExpressionStatement( generator.AssignmentStatement( trueAssignment.Target.Syntax, conditionalExpression)).WithTriviaFrom(ifStatement); editor.ReplaceNode( ifOperation.Syntax, this.WrapWithBlockIfAppropriate(ifStatement, expressionStatement)); }
private static SyntaxNode CreateThrowIfCancellationRequestedExpressionStatement( DocumentEditor editor, IConditionalOperation conditional, IPropertyReferenceOperation isCancellationRequestedPropertyReference) { SyntaxNode memberAccess = editor.Generator.MemberAccessExpression( isCancellationRequestedPropertyReference.Instance.Syntax, nameof(CancellationToken.ThrowIfCancellationRequested)); SyntaxNode invocation = editor.Generator.InvocationExpression(memberAccess, Array.Empty <SyntaxNode>()); var firstWhenTrueStatement = conditional.WhenTrue is IBlockOperation block?block.Operations.FirstOrDefault() : conditional.WhenTrue; var result = editor.Generator.ExpressionStatement(invocation); result = firstWhenTrueStatement is not null?result.WithTriviaFrom(firstWhenTrueStatement.Syntax) : result; return(result.WithAdditionalAnnotations(Formatter.Annotation)); }
public static bool TryMatchPattern( ISyntaxFacts syntaxFacts, IConditionalOperation ifOperation, [NotNullWhen(true)] out IOperation trueStatement, [NotNullWhen(true)] out IOperation falseStatement, out ISimpleAssignmentOperation?trueAssignment, out ISimpleAssignmentOperation?falseAssignment) { trueAssignment = null; falseAssignment = null; trueStatement = ifOperation.WhenTrue; falseStatement = ifOperation.WhenFalse; trueStatement = UseConditionalExpressionHelpers.UnwrapSingleStatementBlock(trueStatement); falseStatement = UseConditionalExpressionHelpers.UnwrapSingleStatementBlock(falseStatement); if (!TryGetAssignmentOrThrow(trueStatement, out trueAssignment, out var trueThrow) || !TryGetAssignmentOrThrow(falseStatement, out falseAssignment, out var falseThrow)) { return(false); } var anyAssignment = trueAssignment ?? falseAssignment; if (UseConditionalExpressionHelpers.HasInconvertibleThrowStatement( syntaxFacts, anyAssignment?.IsRef == true, trueThrow, falseThrow)) { return(false); } // The left side of both assignment statements has to be syntactically identical (modulo // trivia differences). if (trueAssignment != null && falseAssignment != null && !syntaxFacts.AreEquivalent(trueAssignment.Target.Syntax, falseAssignment.Target.Syntax)) { return(false); } return(UseConditionalExpressionHelpers.CanConvert( syntaxFacts, ifOperation, trueStatement, falseStatement)); }
public static bool CanConvert( ISyntaxFactsService syntaxFacts, IConditionalOperation ifOperation, IOperation whenTrue, IOperation whenFalse) { // Will likely screw things up if the if directive spans any preprocessor directives. // So do not offer for now. if (syntaxFacts.SpansPreprocessorDirective(ifOperation.Syntax)) { return(false); } // User may have comments on the when-true/when-false statements. These statements can // be very important. Often they indicate why the true/false branches are important in // the first place. We don't have any place to put these, so we don't offer here. if (HasRegularComments(syntaxFacts, whenTrue.Syntax) || HasRegularComments(syntaxFacts, whenFalse.Syntax)) { return(false); } return(true); }
protected abstract bool TryMatchPattern(IConditionalOperation ifOperation);
private bool HandleElseIf([NotNull] IConditionalOperation ifElseStatement) { Remove(ifElseStatement, owner.ifStatementsLeftToAnalyze); ifStatement = ifElseStatement; return(true); }
private bool IsIfElseIfConstruct([NotNull] IConditionalOperation ifStatement) { return(ifStatement.WhenFalse is IConditionalOperation); }
private bool TryFindMatchingLocalDeclarationImmediatelyAbove( IConditionalOperation ifOperation, ISimpleAssignmentOperation?trueAssignment, ISimpleAssignmentOperation?falseAssignment, [NotNullWhen(true)] out IVariableDeclarationGroupOperation?localDeclaration, [NotNullWhen(true)] out IVariableDeclaratorOperation?declarator) { localDeclaration = null; declarator = null; ILocalSymbol?local = null; if (trueAssignment != null) { if (!(trueAssignment.Target is ILocalReferenceOperation trueLocal)) { return(false); } local = trueLocal.Local; } if (falseAssignment != null) { if (!(falseAssignment.Target is ILocalReferenceOperation falseLocal)) { return(false); } // See if both assignments are to the same local. if (local != null && !Equals(local, falseLocal.Local)) { return(false); } local = falseLocal.Local; } // We weren't assigning to a local. if (local == null) { return(false); } // If so, see if that local was declared immediately above the if-statement. if (!(ifOperation.Parent is IBlockOperation parentBlock)) { return(false); } var ifIndex = parentBlock.Operations.IndexOf(ifOperation); if (ifIndex <= 0) { return(false); } localDeclaration = parentBlock.Operations[ifIndex - 1] as IVariableDeclarationGroupOperation; if (localDeclaration == null) { return(false); } if (localDeclaration.IsImplicit) { return(false); } if (localDeclaration.Declarations.Length != 1) { return(false); } var declaration = localDeclaration.Declarations[0]; var declarators = declaration.Declarators; if (declarators.Length != 1) { return(false); } declarator = declarators[0]; var variable = declarator.Symbol; if (!Equals(variable, local)) { // wasn't a declaration of the local we're assigning to. return(false); } var variableInitializer = declarator.Initializer ?? declaration.Initializer; if (variableInitializer?.Value != null) { var unwrapped = UnwrapImplicitConversion(variableInitializer.Value); // the variable has to either not have an initializer, or it needs to be basic // literal/default expression. if (!(unwrapped is ILiteralOperation) && !(unwrapped is IDefaultValueOperation)) { return(false); } } // If the variable is referenced in the condition of the 'if' block, we can't merge the // declaration and assignments. return(!ReferencesLocalVariable(ifOperation.Condition, variable)); }
protected abstract bool TryMatchPattern(IConditionalOperation ifOperation, ISymbol containingSymbol);
public static bool TryMatchPattern( ISyntaxFactsService syntaxFacts, IConditionalOperation ifOperation, out IReturnOperation trueReturn, out IReturnOperation falseReturn) { trueReturn = null; falseReturn = null; var trueStatement = ifOperation.WhenTrue; var falseStatement = ifOperation.WhenFalse; // we support: // // if (expr) // return a; // else // return b; // // and // // if (expr) // return a; // // return b; if (falseStatement == null) { var parentBlock = ifOperation.Parent as IBlockOperation; if (parentBlock == null) { return(false); } var ifIndex = parentBlock.Operations.IndexOf(ifOperation); if (ifIndex < 0) { return(false); } if (ifIndex + 1 < parentBlock.Operations.Length) { falseStatement = parentBlock.Operations[ifIndex + 1]; if (falseStatement.IsImplicit) { return(false); } } } trueStatement = UseConditionalExpressionHelpers.UnwrapSingleStatementBlock(trueStatement); falseStatement = UseConditionalExpressionHelpers.UnwrapSingleStatementBlock(falseStatement); // Both return-statements must be of the form "return value" if (!(trueStatement is IReturnOperation trueReturnOp) || !(falseStatement is IReturnOperation falseReturnOp) || trueReturnOp.ReturnedValue == null || falseReturnOp.ReturnedValue == null) { return(false); } if (trueReturnOp.Kind != falseReturnOp.Kind) { // Not allowed if these are different types of returns. i.e. // "yield return ..." and "return ...". return(false); } if (trueReturnOp.Kind == OperationKind.YieldBreak) { // This check is just paranoia. We likely shouldn't get here since we already // checked if .ReturnedValue was null above. return(false); } if (trueReturnOp.Kind == OperationKind.YieldReturn && ifOperation.WhenFalse == null) { // we have the following: // // if (...) { // yield return ... // } // // yield return ... // // It is *not* correct to replace this with: // // yield return ... ? ... ? ... // // as both yields need to be hit. return(false); } trueReturn = trueReturnOp; falseReturn = falseReturnOp; return(UseConditionalExpressionHelpers.CanConvert( syntaxFacts, ifOperation, trueReturn, falseReturn)); }