private void Recurse(SyntaxTreeAnalysisContext context, ReportDiagnostic severity, ArrayBuilder <SyntaxNode> stack) { var tree = context.Tree; var cancellationToken = context.CancellationToken; var root = tree.GetRoot(cancellationToken); var text = tree.GetText(cancellationToken); stack.Add(root); while (stack.Count > 0) { cancellationToken.ThrowIfCancellationRequested(); var current = stack.Last(); stack.RemoveLast(); // Don't bother analyzing nodes that have syntax errors in them. if (current.ContainsDiagnostics && current.GetDiagnostics().Any(d => d.Severity == DiagnosticSeverity.Error)) { continue; } foreach (var child in current.ChildNodesAndTokens()) { if (child.IsNode) { stack.Add(child.AsNode() !); } else if (child.IsToken) { ProcessToken(context, severity, text, child.AsToken()); } } } }
protected SyntaxNode CreateInterpolatedString( Document document, bool isVerbatimStringLiteral, ArrayBuilder <SyntaxNode> pieces) { var syntaxFacts = document.GetLanguageService <ISyntaxFactsService>(); var generator = SyntaxGenerator.GetGenerator(document); var startToken = generator.CreateInterpolatedStringStartToken(isVerbatimStringLiteral) .WithLeadingTrivia(pieces.First().GetLeadingTrivia()); var endToken = generator.CreateInterpolatedStringEndToken() .WithTrailingTrivia(pieces.Last().GetTrailingTrivia()); using var _ = ArrayBuilder <SyntaxNode> .GetInstance(pieces.Count, out var content); var previousContentWasStringLiteralExpression = false; foreach (var piece in pieces) { var isCharacterLiteral = syntaxFacts.IsCharacterLiteralExpression(piece); var currentContentIsStringOrCharacterLiteral = syntaxFacts.IsStringLiteralExpression(piece) || isCharacterLiteral; if (currentContentIsStringOrCharacterLiteral) { var text = piece.GetFirstToken().Text; var textWithEscapedBraces = text.Replace("{", "{{").Replace("}", "}}"); var textWithoutQuotes = GetTextWithoutQuotes(textWithEscapedBraces, isVerbatimStringLiteral, isCharacterLiteral); if (previousContentWasStringLiteralExpression) { // Last part we added to the content list was also an interpolated-string-text-node. // We need to combine these as the API for creating an interpolated strings // does not expect to be given a list containing non-contiguous string nodes. // Essentially if we combine '"A" + 1 + "B" + "C"' into '$"A{1}BC"' it must be: // {InterpolatedStringText}{Interpolation}{InterpolatedStringText} // not: // {InterpolatedStringText}{Interpolation}{InterpolatedStringText}{InterpolatedStringText} var existingInterpolatedStringTextNode = content.Last(); var newText = ConcatenateTextToTextNode(generator, existingInterpolatedStringTextNode, textWithoutQuotes); content[^ 1] = newText;
internal static MethodScope GetMethodScope(this ArrayBuilder <ISymUnmanagedScope> scopes, int methodToken, int methodVersion) { if (scopes.Count == 0) { return(null); } var scope = scopes.Last(); return(new MethodScope(methodToken, methodVersion, scope.GetStartOffset(), scope.GetEndOffset())); }
protected SyntaxNode CreateInterpolatedString( Document document, bool isVerbatimStringLiteral, ArrayBuilder <SyntaxNode> pieces) { var syntaxFacts = document.GetLanguageService <ISyntaxFactsService>(); var generator = SyntaxGenerator.GetGenerator(document); var startToken = generator.CreateInterpolatedStringStartToken(isVerbatimStringLiteral) .WithLeadingTrivia(pieces.First().GetLeadingTrivia()); var endToken = generator.CreateInterpolatedStringEndToken() .WithTrailingTrivia(pieces.Last().GetTrailingTrivia()); using var _ = ArrayBuilder <SyntaxNode> .GetInstance(pieces.Count, out var content); var previousContentWasStringLiteralExpression = false; foreach (var piece in pieces) { var isCharacterLiteral = syntaxFacts.IsCharacterLiteralExpression(piece); var currentContentIsStringOrCharacterLiteral = syntaxFacts.IsStringLiteralExpression(piece) || isCharacterLiteral; if (currentContentIsStringOrCharacterLiteral) { var text = piece.GetFirstToken().Text; var textWithEscapedBraces = text.Replace("{", "{{").Replace("}", "}}"); var textWithoutQuotes = GetTextWithoutQuotes(textWithEscapedBraces, isVerbatimStringLiteral, isCharacterLiteral); if (previousContentWasStringLiteralExpression) { // Last part we added to the content list was also an interpolated-string-text-node. // We need to combine these as the API for creating an interpolated strings // does not expect to be given a list containing non-contiguous string nodes. // Essentially if we combine '"A" + 1 + "B" + "C"' into '$"A{1}BC"' it must be: // {InterpolatedStringText}{Interpolation}{InterpolatedStringText} // not: // {InterpolatedStringText}{Interpolation}{InterpolatedStringText}{InterpolatedStringText} var existingInterpolatedStringTextNode = content.Last(); var newText = ConcatinateTextToTextNode(generator, existingInterpolatedStringTextNode, textWithoutQuotes); content[content.Count - 1] = newText; } else { // This is either the first string literal we have encountered or it is the most recent one we've seen // after adding an interpolation. Add a new interpolated-string-text-node to the list. content.Add(generator.InterpolatedStringText(generator.InterpolatedStringTextToken(textWithoutQuotes))); } } else { content.Add(generator.Interpolation(piece.WithoutTrivia())); } // Update this variable to be true every time we encounter a new string literal expression // so we know to concatenate future string literals together if we encounter them. previousContentWasStringLiteralExpression = currentContentIsStringOrCharacterLiteral; } return(generator.InterpolatedStringExpression(startToken, content, endToken)); }
internal BoundExpression PopLast() { if (init.Count == 0) { return(null); } var last = init.Last(); init.RemoveLast(); return(last); }
public static void AddExistingItems(BaseObjectCreationExpressionSyntax objectCreation, ArrayBuilder <SyntaxNodeOrToken> nodesAndTokens) { if (objectCreation.Initializer != null) { nodesAndTokens.AddRange(objectCreation.Initializer.Expressions.GetWithSeparators()); } // If we have an odd number of elements already, add a comma at the end so that we can add the rest of the // items afterwards without a syntax issue. if (nodesAndTokens.Count % 2 == 1) { var last = nodesAndTokens.Last(); nodesAndTokens.RemoveLast(); nodesAndTokens.Add(last.WithTrailingTrivia()); nodesAndTokens.Add(Token(SyntaxKind.CommaToken).WithTrailingTrivia(last.GetTrailingTrivia())); } }
private static ArrayBuilder <T> CreateBuilderFromSequence(IEnumerable <T> sequence) { Debug.Assert(sequence != null); var builder = new ArrayBuilder <T>(); int count = 0; foreach (T item in sequence) { count++; builder.Add(item); Assert.Equal(count, builder.Count); Assert.Equal(CalculateExpectedCapacity(count), builder.Capacity); VerifyBuilderContents(sequence.Take(count), builder); Assert.Equal(sequence.First(), builder.First()); Assert.Equal(item, builder.Last()); } return(builder); }
/// <summary> /// Takes the outputs from the previous deconstructionStep and depending on the structure of variables, will: /// - generate further deconstructions, /// - or simply conversions and assignments. /// /// Returns true for success, but false if has errors. /// </summary> private bool DeconstructOrAssignOutputs( BoundDeconstructionDeconstructStep deconstructionStep, ArrayBuilder<DeconstructionVariable> variables, CSharpSyntaxNode syntax, DiagnosticBag diagnostics, ArrayBuilder<BoundDeconstructionDeconstructStep> deconstructionSteps, ArrayBuilder<BoundDeconstructionAssignmentStep> conversionSteps, ArrayBuilder<BoundDeconstructionAssignmentStep> assignmentSteps, ArrayBuilder<BoundDeconstructionConstructionStep> constructionStepsOpt) { bool hasErrors = false; var constructionInputs = constructionStepsOpt == null ? null : ArrayBuilder<BoundDeconstructValuePlaceholder>.GetInstance(); int count = variables.Count; for (int i = 0; i < count; i++) { var variable = variables[i]; var valuePlaceholder = deconstructionStep.OutputPlaceholders[i]; if (variable.HasNestedVariables) { var nested = variable.NestedVariables; if (!DeconstructIntoSteps(valuePlaceholder, syntax, diagnostics, nested, deconstructionSteps, conversionSteps, assignmentSteps, constructionStepsOpt)) { hasErrors = true; } else if (constructionInputs != null) { constructionInputs.Add(constructionStepsOpt.Last().OutputPlaceholder); } } else { var conversion = MakeDeconstructionAssignmentStep(variable.Single, valuePlaceholder, syntax, diagnostics); conversionSteps.Add(conversion); var assignment = MakeDeconstructionAssignmentStep(variable.Single, conversion.OutputPlaceholder, syntax, diagnostics); assignmentSteps.Add(assignment); if (constructionInputs != null) { constructionInputs.Add(conversion.OutputPlaceholder); } } } if (constructionStepsOpt != null) { if (hasErrors) { constructionInputs.Free(); } else { var construct = MakeDeconstructionConstructionStep(syntax, diagnostics, constructionInputs.ToImmutableAndFree()); constructionStepsOpt.Add(construct); } } return !hasErrors; }
private SyntaxTriviaList RewriteTrivia( SyntaxTriviaList triviaList, int depth, bool isTrailing, bool indentAfterLineBreak, bool mustHaveSeparator, int lineBreaksAfter) { ArrayBuilder <SyntaxTrivia> currentTriviaList = ArrayBuilder <SyntaxTrivia> .GetInstance(triviaList.Count); try { foreach (var trivia in triviaList) { if (trivia.IsKind(SyntaxKind.WhitespaceTrivia) || trivia.IsKind(SyntaxKind.EndOfLineTrivia) || trivia.FullWidth == 0) { continue; } var needsSeparator = (currentTriviaList.Count > 0 && NeedsSeparatorBetween(currentTriviaList.Last())) || (currentTriviaList.Count == 0 && isTrailing); var needsLineBreak = NeedsLineBreakBefore(trivia, isTrailing) || (currentTriviaList.Count > 0 && NeedsLineBreakBetween(currentTriviaList.Last(), trivia, isTrailing)); if (needsLineBreak && !_afterLineBreak) { currentTriviaList.Add(GetEndOfLine()); _afterLineBreak = true; _afterIndentation = false; } if (_afterLineBreak) { if (!_afterIndentation && NeedsIndentAfterLineBreak(trivia)) { currentTriviaList.Add(this.GetIndentation(GetDeclarationDepth(trivia))); _afterIndentation = true; } } else if (needsSeparator) { currentTriviaList.Add(GetSpace()); _afterLineBreak = false; _afterIndentation = false; } if (trivia.HasStructure) { var tr = this.VisitStructuredTrivia(trivia); currentTriviaList.Add(tr); } else if (trivia.IsKind(SyntaxKind.DocumentationCommentExteriorTrivia)) { // recreate exterior to remove any leading whitespace currentTriviaList.Add(s_trimmedDocCommentExterior); } else { currentTriviaList.Add(trivia); } if (NeedsLineBreakAfter(trivia, isTrailing) && (currentTriviaList.Count == 0 || !EndsInLineBreak(currentTriviaList.Last()))) { currentTriviaList.Add(GetEndOfLine()); _afterLineBreak = true; _afterIndentation = false; } } if (lineBreaksAfter > 0) { if (currentTriviaList.Count > 0 && EndsInLineBreak(currentTriviaList.Last())) { lineBreaksAfter--; } for (int i = 0; i < lineBreaksAfter; i++) { currentTriviaList.Add(GetEndOfLine()); _afterLineBreak = true; _afterIndentation = false; } } else if (indentAfterLineBreak && _afterLineBreak && !_afterIndentation) { currentTriviaList.Add(this.GetIndentation(depth)); _afterIndentation = true; } else if (mustHaveSeparator) { currentTriviaList.Add(GetSpace()); _afterLineBreak = false; _afterIndentation = false; } if (currentTriviaList.Count == 0) { return(default(SyntaxTriviaList)); } else if (currentTriviaList.Count == 1) { return(SyntaxFactory.TriviaList(currentTriviaList.First())); } else { return(SyntaxFactory.TriviaList(currentTriviaList)); } } finally { currentTriviaList.Free(); } }
/// <summary> /// The strategy of this rewrite is to do rewrite "locally". /// We analyze arguments of the concat in a shallow fashion assuming that /// lowering and optimizations (including this one) is already done for the arguments. /// Based on the arguments we select the most appropriate pattern for the current node. /// /// NOTE: it is not guaranteed that the node that we chose will be the most optimal since we have only /// local information - i.e. we look at the arguments, but we do not know about siblings. /// When we move to the parent, the node may be rewritten by this or some another optimization. /// /// Example: /// result = ( "abc" + "def" + null ?? expr1 + "moo" + "baz" ) + expr2 /// /// Will rewrite into: /// result = Concat("abcdef", expr2) /// /// However there will be transient nodes like Concat(expr1 + "moo") that will not be present in the /// resulting tree. /// /// </summary> private BoundExpression RewriteStringConcatenation(SyntaxNode syntax, BinaryOperatorKind operatorKind, BoundExpression loweredLeft, BoundExpression loweredRight, TypeSymbol type) { Debug.Assert( operatorKind == BinaryOperatorKind.StringConcatenation || operatorKind == BinaryOperatorKind.StringAndObjectConcatenation || operatorKind == BinaryOperatorKind.ObjectAndStringConcatenation); if (_inExpressionLambda) { return(RewriteStringConcatInExpressionLambda(syntax, operatorKind, loweredLeft, loweredRight, type)); } // avoid run time boxing and ToString operations if we can reasonably convert to a string at compile time loweredLeft = ConvertConcatExprToStringIfPossible(syntax, loweredLeft); loweredRight = ConvertConcatExprToStringIfPossible(syntax, loweredRight); // try fold two args without flattening. var folded = TryFoldTwoConcatOperands(syntax, loweredLeft, loweredRight); if (folded != null) { return(folded); } // flatten and merge - ( expr1 + "A" ) + ("B" + expr2) ===> (expr1 + "AB" + expr2) ArrayBuilder <BoundExpression> leftFlattened = ArrayBuilder <BoundExpression> .GetInstance(); ArrayBuilder <BoundExpression> rightFlattened = ArrayBuilder <BoundExpression> .GetInstance(); FlattenConcatArg(loweredLeft, leftFlattened); FlattenConcatArg(loweredRight, rightFlattened); if (leftFlattened.Any() && rightFlattened.Any()) { folded = TryFoldTwoConcatOperands(syntax, leftFlattened.Last(), rightFlattened.First()); if (folded != null) { rightFlattened[0] = folded; leftFlattened.RemoveLast(); } } leftFlattened.AddRange(rightFlattened); rightFlattened.Free(); BoundExpression result; switch (leftFlattened.Count) { case 0: result = _factory.StringLiteral(string.Empty); break; case 1: result = leftFlattened[0]; break; case 2: var left = leftFlattened[0]; var right = leftFlattened[1]; result = RewriteStringConcatenationTwoExprs(syntax, left, right); break; case 3: var first = leftFlattened[0]; var second = leftFlattened[1]; var third = leftFlattened[2]; result = RewriteStringConcatenationThreeExprs(syntax, first, second, third); break; default: result = RewriteStringConcatenationManyExprs(syntax, leftFlattened.ToImmutable()); break; } leftFlattened.Free(); return(result); }
private SyntaxTriviaList RewriteTrivia( SyntaxTriviaList triviaList, int depth, bool isTrailing, bool mustBeIndented, bool mustHaveSeparator, int lineBreaksAfter) { ArrayBuilder <SyntaxTrivia> currentTriviaList = ArrayBuilder <SyntaxTrivia> .GetInstance(triviaList.Count); try { foreach (var trivia in triviaList) { if (trivia.CSharpKind() == SyntaxKind.WhitespaceTrivia || trivia.CSharpKind() == SyntaxKind.EndOfLineTrivia || trivia.FullWidth == 0) { continue; } var needsSeparator = (currentTriviaList.Count > 0 && NeedsSeparatorBetween(currentTriviaList.Last())) || (currentTriviaList.Count == 0 && isTrailing); var needsLineBreak = NeedsLineBreakBefore(trivia) || (currentTriviaList.Count > 0 && NeedsLineBreakBetween(currentTriviaList.Last(), trivia, isTrailing)); if (needsLineBreak && !afterLineBreak) { currentTriviaList.Add(GetCarriageReturnLineFeed()); afterLineBreak = true; afterIndentation = false; } if (afterLineBreak) { if (!afterIndentation && NeedsIndentAfterLineBreak(trivia)) { currentTriviaList.Add(this.GetIndentation(GetDeclarationDepth(trivia))); afterIndentation = true; } } else if (needsSeparator) { currentTriviaList.Add(GetSpace()); afterLineBreak = false; afterIndentation = false; } if (trivia.HasStructure) { var tr = this.VisitStructuredTrivia(trivia); currentTriviaList.Add(tr); } else { currentTriviaList.Add(trivia); } if (NeedsLineBreakAfter(trivia, isTrailing)) { currentTriviaList.Add(GetCarriageReturnLineFeed()); afterLineBreak = true; afterIndentation = false; } } if (lineBreaksAfter > 0) { if (currentTriviaList.Count > 0 && EndsInLineBreak(currentTriviaList.Last())) { lineBreaksAfter--; } for (int i = 0; i < lineBreaksAfter; i++) { currentTriviaList.Add(GetCarriageReturnLineFeed()); afterLineBreak = true; afterIndentation = false; } } else if (mustBeIndented) { currentTriviaList.Add(this.GetIndentation(depth)); afterIndentation = true; } else if (mustHaveSeparator) { currentTriviaList.Add(GetSpace()); afterLineBreak = false; afterIndentation = false; } if (currentTriviaList.Count == 0) { return(default(SyntaxTriviaList)); } else if (currentTriviaList.Count == 1) { return(SyntaxFactory.TriviaList(currentTriviaList.First())); } else { return(SyntaxFactory.TriviaList(currentTriviaList)); } } finally { currentTriviaList.Free(); } }
/// <summary> /// Takes the outputs from the previous deconstructionStep and depending on the structure of variables, will: /// - generate further deconstructions, /// - or simply conversions and assignments. /// /// Returns true for success, but false if has errors. /// </summary> private bool DeconstructOrAssignOutputs( BoundDeconstructionDeconstructStep deconstructionStep, ArrayBuilder <DeconstructionVariable> variables, CSharpSyntaxNode syntax, DiagnosticBag diagnostics, ArrayBuilder <BoundDeconstructionDeconstructStep> deconstructionSteps, ArrayBuilder <BoundDeconstructionAssignmentStep> conversionSteps, ArrayBuilder <BoundDeconstructionAssignmentStep> assignmentSteps, ArrayBuilder <BoundDeconstructionConstructionStep> constructionStepsOpt) { bool hasErrors = false; var constructionInputs = constructionStepsOpt == null ? null : ArrayBuilder <BoundDeconstructValuePlaceholder> .GetInstance(); int count = variables.Count; for (int i = 0; i < count; i++) { var variable = variables[i]; var valuePlaceholder = deconstructionStep.OutputPlaceholders[i]; if (variable.HasNestedVariables) { var nested = variable.NestedVariables; if (!DeconstructIntoSteps(valuePlaceholder, syntax, diagnostics, nested, deconstructionSteps, conversionSteps, assignmentSteps, constructionStepsOpt)) { hasErrors = true; } else if (constructionInputs != null) { constructionInputs.Add(constructionStepsOpt.Last().OutputPlaceholder); } } else { var conversion = MakeDeconstructionAssignmentStep(variable.Single, valuePlaceholder, syntax, diagnostics); conversionSteps.Add(conversion); var assignment = MakeDeconstructionAssignmentStep(variable.Single, conversion.OutputPlaceholder, syntax, diagnostics); assignmentSteps.Add(assignment); if (constructionInputs != null) { constructionInputs.Add(conversion.OutputPlaceholder); } } } if (constructionStepsOpt != null) { if (hasErrors) { constructionInputs.Free(); } else { var construct = MakeDeconstructionConstructionStep(syntax, diagnostics, constructionInputs.ToImmutableAndFree()); constructionStepsOpt.Add(construct); } } return(!hasErrors); }
/// <summary> /// The strategy of this rewrite is to do rewrite "locally". /// We analyze arguments of the concat in a shallow fashion assuming that /// lowering and optimizations (including this one) is already done for the arguments. /// Based on the arguments we select the most appropriate pattern for the current node. /// /// NOTE: it is not guaranteed that the node that we chose will be the most optimal since we have only /// local information - i.e. we look at the arguments, but we do not know about siblings. /// When we move to the parent, the node may be rewritten by this or some another optimization. /// /// Example: /// result = ( "abc" + "def" + null ?? expr1 + "moo" + "baz" ) + expr2 /// /// Will rewrite into: /// result = Concat("abcdef", expr2) /// /// However there will be transient nodes like Concat(expr1 + "moo") that will not be present in the /// resulting tree. /// /// </summary> private BoundExpression RewriteStringConcatenation(SyntaxNode syntax, BinaryOperatorKind operatorKind, BoundExpression loweredLeft, BoundExpression loweredRight, TypeSymbol type) { Debug.Assert( operatorKind == BinaryOperatorKind.StringConcatenation || operatorKind == BinaryOperatorKind.StringAndObjectConcatenation || operatorKind == BinaryOperatorKind.ObjectAndStringConcatenation); if (_inExpressionLambda) { return(RewriteStringConcatInExpressionLambda(syntax, operatorKind, loweredLeft, loweredRight, type)); } // Convert both sides to a string (calling ToString if necessary) loweredLeft = ConvertConcatExprToString(syntax, loweredLeft); loweredRight = ConvertConcatExprToString(syntax, loweredRight); Debug.Assert(loweredLeft.Type.IsStringType() || loweredLeft.ConstantValue?.IsNull == true || loweredLeft.Type.IsErrorType()); Debug.Assert(loweredRight.Type.IsStringType() || loweredRight.ConstantValue?.IsNull == true || loweredRight.Type.IsErrorType()); // try fold two args without flattening. var folded = TryFoldTwoConcatOperands(syntax, loweredLeft, loweredRight); if (folded != null) { return(folded); } // flatten and merge - ( expr1 + "A" ) + ("B" + expr2) ===> (expr1 + "AB" + expr2) ArrayBuilder <BoundExpression> leftFlattened = ArrayBuilder <BoundExpression> .GetInstance(); ArrayBuilder <BoundExpression> rightFlattened = ArrayBuilder <BoundExpression> .GetInstance(); FlattenConcatArg(loweredLeft, leftFlattened); FlattenConcatArg(loweredRight, rightFlattened); if (leftFlattened.Any() && rightFlattened.Any()) { folded = TryFoldTwoConcatOperands(syntax, leftFlattened.Last(), rightFlattened.First()); if (folded != null) { rightFlattened[0] = folded; leftFlattened.RemoveLast(); } } leftFlattened.AddRange(rightFlattened); rightFlattened.Free(); BoundExpression result; switch (leftFlattened.Count) { case 0: result = _factory.StringLiteral(string.Empty); break; case 1: // All code paths which reach here (through TryFoldTwoConcatOperands) have already called // RewriteStringConcatenationOneExpr if necessary result = leftFlattened[0]; break; case 2: var left = leftFlattened[0]; var right = leftFlattened[1]; result = RewriteStringConcatenationTwoExprs(syntax, left, right); break; case 3: { var first = leftFlattened[0]; var second = leftFlattened[1]; var third = leftFlattened[2]; result = RewriteStringConcatenationThreeExprs(syntax, first, second, third); } break; case 4: { var first = leftFlattened[0]; var second = leftFlattened[1]; var third = leftFlattened[2]; var fourth = leftFlattened[3]; result = RewriteStringConcatenationFourExprs(syntax, first, second, third, fourth); } break; default: result = RewriteStringConcatenationManyExprs(syntax, leftFlattened.ToImmutable()); break; } leftFlattened.Free(); return(result); }