| File: Debugging\CSharpProximityExpressionsService.Worker.cs | Web Access |
| Project: src\roslyn\src\Features\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.Features.csproj (Microsoft.CodeAnalysis.CSharp.Features) |
// 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 disable using System.Collections.Generic; using System.Linq; using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Shared.Extensions; namespace Microsoft.CodeAnalysis.CSharp.Debugging; internal sealed partial class CSharpProximityExpressionsService { internal sealed class Worker(SyntaxTree syntaxTree, int position) { private readonly SyntaxTree _syntaxTree = syntaxTree; private readonly int _position = position; private StatementSyntax _parentStatement; private SyntaxToken _token; private readonly List<string> _expressions = []; internal IList<string> Do(CancellationToken cancellationToken) { // First, find the containing statement. We'll want to add the expressions in this // statement to the result. _token = _syntaxTree.GetRoot(cancellationToken).FindToken(_position); _parentStatement = _token.GetAncestor<StatementSyntax>(); if (_parentStatement == null) { return null; } AddRelevantExpressions(_parentStatement, _expressions, includeDeclarations: false); AddPrecedingRelevantExpressions(); AddFollowingRelevantExpressions(cancellationToken); AddCurrentDeclaration(); AddMethodParameters(); AddIndexerParameters(); AddCatchParameters(); AddThisExpression(); AddValueExpression(); var result = _expressions.Distinct().Where(e => e.Length > 0).ToList(); return result.Count == 0 ? null : result; } private void AddValueExpression() { // If we're in a setter/adder/remover then add "value". if (_parentStatement.GetAncestorOrThis<AccessorDeclarationSyntax>() is (kind: SyntaxKind.SetAccessorDeclaration or SyntaxKind.InitAccessorDeclaration or SyntaxKind.AddAccessorDeclaration or SyntaxKind.RemoveAccessorDeclaration)) { _expressions.Add("value"); } } private void AddThisExpression() { // If it's an instance member, then also add "this". var memberDeclaration = _parentStatement.GetAncestorOrThis<MemberDeclarationSyntax>(); if (!memberDeclaration.IsKind(SyntaxKind.GlobalStatement) && !memberDeclaration.GetModifiers().Any(SyntaxKind.StaticKeyword)) { _expressions.Add("this"); } } private void AddCatchParameters() { var block = GetImmediatelyContainingBlock(); // if we're the start of a "catch(Goo e)" clause, then add "e". if (block != null && block?.Parent is CatchClauseSyntax catchClause && catchClause.Declaration != null && catchClause.Declaration.Identifier.Kind() != SyntaxKind.None) { _expressions.Add(catchClause.Declaration.Identifier.ValueText); } } private BlockSyntax GetImmediatelyContainingBlock() { return IsFirstBlockStatement() ? (BlockSyntax)_parentStatement.Parent : _parentStatement is BlockSyntax block && block.OpenBraceToken == _token ? (BlockSyntax)_parentStatement : null; } private bool IsFirstBlockStatement() => _parentStatement.Parent is BlockSyntax parentBlockOpt && parentBlockOpt.Statements.FirstOrDefault() == _parentStatement; private void AddCurrentDeclaration() { if (_parentStatement is LocalDeclarationStatementSyntax) { AddRelevantExpressions(_parentStatement, _expressions, includeDeclarations: true); } } private void AddMethodParameters() { // and we're the start of a method, then also add the parameters of that method to // the proximity expressions. var block = GetImmediatelyContainingBlock(); if (block != null && block.Parent is MemberDeclarationSyntax memberDeclaration) { var parameterList = memberDeclaration.GetParameterList(); AddParameters(parameterList); } else if (block is null && _parentStatement.Parent is GlobalStatementSyntax { Parent: CompilationUnitSyntax compilationUnit } globalStatement && compilationUnit.Members.FirstOrDefault() == globalStatement) { _expressions.Add("args"); } } private void AddIndexerParameters() { var block = GetImmediatelyContainingBlock(); // and we're the start of a method, then also add the parameters of that method to // the proximity expressions. if (block != null && block.Parent is AccessorDeclarationSyntax && block.Parent.Parent is AccessorListSyntax && block.Parent.Parent.Parent is IndexerDeclarationSyntax indexerDeclaration) { var parameterList = indexerDeclaration.ParameterList; AddParameters(parameterList); } } private void AddParameters(BaseParameterListSyntax parameterList) { if (parameterList != null) { _expressions.AddRange( from p in parameterList.Parameters select p.Identifier.ValueText); } } private void AddFollowingRelevantExpressions(CancellationToken cancellationToken) { var line = _syntaxTree.GetText(cancellationToken).Lines.IndexOf(_position); // If there's are more statements following us on the same line, then add them as // well. for (var nextStatement = _parentStatement.GetNextStatement(); nextStatement != null && _syntaxTree.GetText(cancellationToken).Lines.IndexOf(nextStatement.SpanStart) == line; nextStatement = nextStatement.GetNextStatement()) { AddRelevantExpressions(nextStatement, _expressions, includeDeclarations: false); } } private void AddPrecedingRelevantExpressions() { // If we're not the first statement in this block, // and there's an expression or declaration statement directly above us, // then add the expressions from that as well. StatementSyntax previousStatement; if (_parentStatement is BlockSyntax block && block.CloseBraceToken == _token) { // If we're at the last brace of a block, use the last // statement in the block. previousStatement = block.Statements.LastOrDefault(); } else { previousStatement = _parentStatement.GetPreviousStatement(); } if (previousStatement != null) { switch (previousStatement.Kind()) { case SyntaxKind.ExpressionStatement: case SyntaxKind.LocalDeclarationStatement: AddRelevantExpressions(previousStatement, _expressions, includeDeclarations: true); break; case SyntaxKind.DoStatement: AddExpressionTerms((previousStatement as DoStatementSyntax).Condition, _expressions); AddLastStatementOfConstruct(previousStatement); break; case SyntaxKind.ForStatement: case SyntaxKind.ForEachStatement: case SyntaxKind.ForEachVariableStatement: case SyntaxKind.IfStatement: case SyntaxKind.CheckedStatement: case SyntaxKind.UncheckedStatement: case SyntaxKind.WhileStatement: case SyntaxKind.LockStatement: case SyntaxKind.SwitchStatement: case SyntaxKind.TryStatement: case SyntaxKind.UsingStatement: AddRelevantExpressions(previousStatement, _expressions, includeDeclarations: false); AddLastStatementOfConstruct(previousStatement); break; default: break; } } else { // This is the first statement of the block. Go to the nearest enclosing statement and add its expressions var statementAncestor = _parentStatement.Ancestors().OfType<StatementSyntax>().FirstOrDefault(node => !node.IsKind(SyntaxKind.Block)); if (statementAncestor != null) { AddRelevantExpressions(statementAncestor, _expressions, includeDeclarations: true); } } } private void AddLastStatementOfConstruct(StatementSyntax statement) { if (statement == null) { return; } switch (statement.Kind()) { case SyntaxKind.Block: AddLastStatementOfConstruct((statement as BlockSyntax).Statements.LastOrDefault()); break; case SyntaxKind.BreakStatement: case SyntaxKind.ContinueStatement: AddLastStatementOfConstruct(statement.GetPreviousStatement()); break; case SyntaxKind.CheckedStatement: case SyntaxKind.UncheckedStatement: AddLastStatementOfConstruct((statement as CheckedStatementSyntax).Block); break; case SyntaxKind.DoStatement: AddLastStatementOfConstruct((statement as DoStatementSyntax).Statement); break; case SyntaxKind.ForStatement: AddLastStatementOfConstruct((statement as ForStatementSyntax).Statement); break; case SyntaxKind.ForEachStatement: case SyntaxKind.ForEachVariableStatement: AddLastStatementOfConstruct((statement as CommonForEachStatementSyntax).Statement); break; case SyntaxKind.IfStatement: var ifStatement = statement as IfStatementSyntax; AddLastStatementOfConstruct(ifStatement.Statement); if (ifStatement.Else != null) { AddLastStatementOfConstruct(ifStatement.Else.Statement); } break; case SyntaxKind.LockStatement: AddLastStatementOfConstruct((statement as LockStatementSyntax).Statement); break; case SyntaxKind.SwitchStatement: var switchStatement = statement as SwitchStatementSyntax; foreach (var section in switchStatement.Sections) { AddLastStatementOfConstruct(section.Statements.LastOrDefault()); } break; case SyntaxKind.TryStatement: var tryStatement = statement as TryStatementSyntax; if (tryStatement.Finally != null) { AddLastStatementOfConstruct(tryStatement.Finally.Block); } else { AddLastStatementOfConstruct(tryStatement.Block); foreach (var catchClause in tryStatement.Catches) { AddLastStatementOfConstruct(catchClause.Block); } } break; case SyntaxKind.UsingStatement: AddLastStatementOfConstruct((statement as UsingStatementSyntax).Statement); break; case SyntaxKind.WhileStatement: AddLastStatementOfConstruct((statement as WhileStatementSyntax).Statement); break; default: AddRelevantExpressions(statement, _expressions, includeDeclarations: false); break; } } } }