private bool IsValidFormatMethod(ISyntaxFactsService syntaxFacts, SyntaxNode expression) { // When calling string.Format(...), the expression will be MemberAccessExpressionSyntax if (syntaxFacts.IsSimpleMemberAccessExpression(expression)) { var nameOfMemberAccessExpression = syntaxFacts.GetNameOfMemberAccessExpression(expression); return(!syntaxFacts.IsGenericName(nameOfMemberAccessExpression) && syntaxFacts.GetIdentifierOfSimpleName(nameOfMemberAccessExpression).ValueText == (nameof(string.Format))); } // When using static System.String and calling Format(...), the expression will be // IdentifierNameSyntax if (syntaxFacts.IsIdentifierName(expression)) { return(syntaxFacts.GetIdentifierOfSimpleName(expression).ValueText == (nameof(string.Format))); } return(false); }
protected bool ValuePatternMatches(TExpressionSyntax expression) { if (_valuePattern.IsToken) { return(_syntaxFacts.IsIdentifierName(expression) && _syntaxFacts.AreEquivalent( _valuePattern.AsToken(), _syntaxFacts.GetIdentifierOfSimpleName(expression))); } else { return(_syntaxFacts.AreEquivalent( _valuePattern.AsNode(), expression)); } }
internal List <Match <TAssignmentStatementSyntax, TMemberAccessExpressionSyntax, TExpressionSyntax> > Analyze() { if (_syntaxFacts.GetObjectCreationInitializer(_objectCreationExpression) != null) { // Don't bother if this already has an initializer. return(null); } _containingStatement = _objectCreationExpression.FirstAncestorOrSelf <TStatementSyntax>(); if (_containingStatement == null) { return(null); } if (!TryInitializeVariableDeclarationCase() && !TryInitializeAssignmentCase()) { return(null); } var containingBlock = _containingStatement.Parent; var foundStatement = false; List <Match <TAssignmentStatementSyntax, TMemberAccessExpressionSyntax, TExpressionSyntax> > matches = null; HashSet <string> seenNames = null; foreach (var child in containingBlock.ChildNodesAndTokens()) { if (!foundStatement) { if (child == _containingStatement) { foundStatement = true; } continue; } if (child.IsToken) { break; } var statement = child.AsNode() as TAssignmentStatementSyntax; if (statement == null) { break; } if (!_syntaxFacts.IsSimpleAssignmentStatement(statement)) { break; } SyntaxNode left, right; _syntaxFacts.GetPartsOfAssignmentStatement(statement, out left, out right); var rightExpression = right as TExpressionSyntax; var leftMemberAccess = left as TMemberAccessExpressionSyntax; if (!_syntaxFacts.IsSimpleMemberAccessExpression(leftMemberAccess)) { break; } var expression = (TExpressionSyntax)_syntaxFacts.GetExpressionOfMemberAccessExpression(leftMemberAccess); if (!ValuePatternMatches(expression)) { break; } // found a match! seenNames = seenNames ?? new HashSet <string>(); matches = matches ?? new List <Match <TAssignmentStatementSyntax, TMemberAccessExpressionSyntax, TExpressionSyntax> >(); // If we see an assignment to the same property/field, we can't convert it // to an initializer. var name = _syntaxFacts.GetNameOfMemberAccessExpression(leftMemberAccess); var identifier = _syntaxFacts.GetIdentifierOfSimpleName(name); if (!seenNames.Add(identifier.ValueText)) { break; } matches.Add(new Match <TAssignmentStatementSyntax, TMemberAccessExpressionSyntax, TExpressionSyntax>( statement, leftMemberAccess, rightExpression)); } return(matches); }
internal ImmutableArray <Match <TExpressionSyntax, TStatementSyntax, TMemberAccessExpressionSyntax, TAssignmentStatementSyntax> >?Analyze() { if (_syntaxFacts.GetObjectCreationInitializer(_objectCreationExpression) != null) { // Don't bother if this already has an initializer. return(null); } _containingStatement = _objectCreationExpression.FirstAncestorOrSelf <TStatementSyntax>(); if (_containingStatement == null) { return(null); } if (!TryInitializeVariableDeclarationCase() && !TryInitializeAssignmentCase()) { return(null); } var containingBlock = _containingStatement.Parent; var foundStatement = false; var matches = ArrayBuilder <Match <TExpressionSyntax, TStatementSyntax, TMemberAccessExpressionSyntax, TAssignmentStatementSyntax> > .GetInstance(); HashSet <string> seenNames = null; foreach (var child in containingBlock.ChildNodesAndTokens()) { if (!foundStatement) { if (child == _containingStatement) { foundStatement = true; continue; } continue; } if (child.IsToken) { break; } var statement = child.AsNode() as TAssignmentStatementSyntax; if (statement == null) { break; } if (!_syntaxFacts.IsSimpleAssignmentStatement(statement)) { break; } _syntaxFacts.GetPartsOfAssignmentStatement( statement, out var left, out var right); var rightExpression = right as TExpressionSyntax; var leftMemberAccess = left as TMemberAccessExpressionSyntax; if (!_syntaxFacts.IsSimpleMemberAccessExpression(leftMemberAccess)) { break; } var expression = (TExpressionSyntax)_syntaxFacts.GetExpressionOfMemberAccessExpression(leftMemberAccess); if (!ValuePatternMatches(expression)) { break; } // Don't offer this fix if the value we're initializing is itself referenced // on the RHS of the assignment. For example: // // var v = new X(); // v.Prop = v.Prop.WithSomething(); // // Or with // // v = new X(); // v.Prop = v.Prop.WithSomething(); // // In the first case, 'v' is being initialized, and so will not be available // in the object initializer we create. // // In the second case we'd change semantics because we'd access the old value // before the new value got written. if (ExpressionContainsValuePattern(rightExpression)) { break; } // If we have code like "x.v = .Length.ToString()" // then we don't want to change this into: // // var x = new Whatever() With { .v = .Length.ToString() } // // The problem here is that .Length will change it's meaning to now refer to the // object that we're creating in our object-creation expression. if (ImplicitMemberAccessWouldBeAffected(rightExpression)) { break; } // found a match! seenNames = seenNames ?? new HashSet <string>(); // If we see an assignment to the same property/field, we can't convert it // to an initializer. var name = _syntaxFacts.GetNameOfMemberAccessExpression(leftMemberAccess); var identifier = _syntaxFacts.GetIdentifierOfSimpleName(name); if (!seenNames.Add(identifier.ValueText)) { break; } matches.Add(new Match <TExpressionSyntax, TStatementSyntax, TMemberAccessExpressionSyntax, TAssignmentStatementSyntax>( statement, leftMemberAccess, rightExpression)); } return(matches.ToImmutableAndFree()); }
private static void RemoveAwaitFromCallerIfPresent( SyntaxEditor editor, ISyntaxFactsService syntaxFacts, SyntaxNode root, ReferenceLocation referenceLocation, CancellationToken cancellationToken) { if (referenceLocation.IsImplicit) { return; } var location = referenceLocation.Location; var token = location.FindToken(cancellationToken); var nameNode = token.Parent; if (nameNode == null) { return; } // Look for the following forms: // await M(...) // await <expr>.M(...) // await M(...).ConfigureAwait(...) // await <expr>.M(...).ConfigureAwait(...) var expressionNode = nameNode; if (syntaxFacts.IsNameOfMemberAccessExpression(nameNode)) { expressionNode = nameNode.Parent; } if (!syntaxFacts.IsExpressionOfInvocationExpression(expressionNode)) { return; } // We now either have M(...) or <expr>.M(...) var invocationExpression = expressionNode.Parent; Debug.Assert(syntaxFacts.IsInvocationExpression(invocationExpression)); if (syntaxFacts.IsExpressionOfAwaitExpression(invocationExpression)) { // Handle the case where we're directly awaited. var awaitExpression = invocationExpression.Parent; editor.ReplaceNode(awaitExpression, (currentAwaitExpression, generator) => syntaxFacts.GetExpressionOfAwaitExpression(currentAwaitExpression) .WithTriviaFrom(currentAwaitExpression)); } else if (syntaxFacts.IsExpressionOfMemberAccessExpression(invocationExpression)) { // Check for the .ConfigureAwait case. var parentMemberAccessExpression = invocationExpression.Parent; var parentMemberAccessExpressionNameNode = syntaxFacts.GetNameOfMemberAccessExpression( parentMemberAccessExpression); var parentMemberAccessExpressionName = syntaxFacts.GetIdentifierOfSimpleName(parentMemberAccessExpressionNameNode).ValueText; if (parentMemberAccessExpressionName == nameof(Task.ConfigureAwait)) { var parentExpression = parentMemberAccessExpression.Parent; if (syntaxFacts.IsExpressionOfAwaitExpression(parentExpression)) { var awaitExpression = parentExpression.Parent; editor.ReplaceNode(awaitExpression, (currentAwaitExpression, generator) => { var currentConfigureAwaitInvocation = syntaxFacts.GetExpressionOfAwaitExpression(currentAwaitExpression); var currentMemberAccess = syntaxFacts.GetExpressionOfInvocationExpression(currentConfigureAwaitInvocation); var currentInvocationExpression = syntaxFacts.GetExpressionOfMemberAccessExpression(currentMemberAccess); return(currentInvocationExpression.WithTriviaFrom(currentAwaitExpression)); }); } } } }
private void RemoveAwaitFromCallerIfPresent( SyntaxEditor editor, ISyntaxFactsService syntaxFacts, SyntaxNode root, ReferenceLocation referenceLocation, CancellationToken cancellationToken) { if (referenceLocation.IsImplicit) { return; } var location = referenceLocation.Location; var token = location.FindToken(cancellationToken); var nameNode = token.Parent; if (nameNode == null) { return; } // Look for the following forms: // await M(...) // await <expr>.M(...) // await M(...).ConfigureAwait(...) // await <expr>.M(...).ConfigureAwait(...) var expressionNode = nameNode; if (syntaxFacts.IsNameOfMemberAccessExpression(nameNode)) { expressionNode = nameNode.Parent; } if (!syntaxFacts.IsExpressionOfInvocationExpression(expressionNode)) { return; } // We now either have M(...) or <expr>.M(...) var invocationExpression = expressionNode.Parent; Debug.Assert(syntaxFacts.IsInvocationExpression(invocationExpression)); if (syntaxFacts.IsExpressionOfAwaitExpression(invocationExpression)) { // Handle the case where we're directly awaited. var awaitExpression = invocationExpression.Parent; editor.ReplaceNode(awaitExpression, (currentAwaitExpression, generator) => syntaxFacts.GetExpressionOfAwaitExpression(currentAwaitExpression) .WithTriviaFrom(currentAwaitExpression)); } else if (syntaxFacts.IsExpressionOfMemberAccessExpression(invocationExpression)) { // Check for the .ConfigureAwait case. var parentMemberAccessExpression = invocationExpression.Parent; var parentMemberAccessExpressionNameNode = syntaxFacts.GetNameOfMemberAccessExpression( parentMemberAccessExpression); var parentMemberAccessExpressionName = syntaxFacts.GetIdentifierOfSimpleName(parentMemberAccessExpressionNameNode).ValueText; if (parentMemberAccessExpressionName == nameof(Task.ConfigureAwait)) { var parentExpression = parentMemberAccessExpression.Parent; if (syntaxFacts.IsExpressionOfAwaitExpression(parentExpression)) { var awaitExpression = parentExpression.Parent; editor.ReplaceNode(awaitExpression, (currentAwaitExpression, generator) => { var currentConfigureAwaitInvocation = syntaxFacts.GetExpressionOfAwaitExpression(currentAwaitExpression); var currentMemberAccess = syntaxFacts.GetExpressionOfInvocationExpression(currentConfigureAwaitInvocation); var currentInvocationExpression = syntaxFacts.GetExpressionOfMemberAccessExpression(currentMemberAccess); return currentInvocationExpression.WithTriviaFrom(currentAwaitExpression); }); } } } }