Skip to content

Commit

Permalink
Make it possible to analyze the dataflow of `ConstructorInitializerSy…
Browse files Browse the repository at this point in the history
…ntax` and `PrimaryConstructorBaseTypeSyntax` (#57576)

Fixes #57577.
  • Loading branch information
bernd5 authored Dec 1, 2021
1 parent 24a1d4b commit aeff023
Show file tree
Hide file tree
Showing 10 changed files with 496 additions and 5 deletions.
20 changes: 19 additions & 1 deletion src/Compilers/CSharp/Portable/CSharpExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1033,7 +1033,25 @@ public static AwaitExpressionInfo GetAwaitExpressionInfo(this SemanticModel? sem
}

/// <summary>
/// Analyze data-flow within an expression.
/// Analyze data-flow within a <see cref="ConstructorInitializerSyntax"/>.
/// </summary>
public static DataFlowAnalysis? AnalyzeDataFlow(this SemanticModel? semanticModel, ConstructorInitializerSyntax constructorInitializer)
{
var csmodel = semanticModel as CSharpSemanticModel;
return csmodel?.AnalyzeDataFlow(constructorInitializer);
}

/// <summary>
/// Analyze data-flow within a <see cref="PrimaryConstructorBaseTypeSyntax.ArgumentList"/> initializer.
/// </summary>
public static DataFlowAnalysis? AnalyzeDataFlow(this SemanticModel? semanticModel, PrimaryConstructorBaseTypeSyntax primaryConstructorBaseType)
{
var csmodel = semanticModel as CSharpSemanticModel;
return csmodel?.AnalyzeDataFlow(primaryConstructorBaseType);
}

/// <summary>
/// Analyze data-flow within an <see cref="ExpressionSyntax"/>.
/// </summary>
public static DataFlowAnalysis? AnalyzeDataFlow(this SemanticModel? semanticModel, ExpressionSyntax expression)
{
Expand Down
30 changes: 28 additions & 2 deletions src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2412,7 +2412,29 @@ public virtual ControlFlowAnalysis AnalyzeControlFlow(StatementSyntax statement)
}

/// <summary>
/// Analyze data-flow within an expression.
/// Analyze data-flow within an <see cref="ConstructorInitializerSyntax"/>.
/// </summary>
/// <param name="constructorInitializer">The ctor-init within the associated SyntaxTree to analyze.</param>
/// <returns>An object that can be used to obtain the result of the data flow analysis.</returns>
public virtual DataFlowAnalysis AnalyzeDataFlow(ConstructorInitializerSyntax constructorInitializer)
{
// Only supported on a SyntaxTreeSemanticModel.
throw new NotSupportedException();
}

/// <summary>
/// Analyze data-flow within an <see cref="PrimaryConstructorBaseTypeSyntax.ArgumentList"/>.
/// </summary>
/// <param name="primaryConstructorBaseType">The node within the associated SyntaxTree to analyze.</param>
/// <returns>An object that can be used to obtain the result of the data flow analysis.</returns>
public virtual DataFlowAnalysis AnalyzeDataFlow(PrimaryConstructorBaseTypeSyntax primaryConstructorBaseType)
{
// Only supported on a SyntaxTreeSemanticModel.
throw new NotSupportedException();
}

/// <summary>
/// Analyze data-flow within an <see cref="ExpressionSyntax"/>.
/// </summary>
/// <param name="expression">The expression within the associated SyntaxTree to analyze.</param>
/// <returns>An object that can be used to obtain the result of the data flow analysis.</returns>
Expand Down Expand Up @@ -5236,8 +5258,12 @@ protected sealed override DataFlowAnalysis AnalyzeDataFlowCore(SyntaxNode statem
return this.AnalyzeDataFlow(statementSyntax);
case ExpressionSyntax expressionSyntax:
return this.AnalyzeDataFlow(expressionSyntax);
case ConstructorInitializerSyntax constructorInitializer:
return this.AnalyzeDataFlow(constructorInitializer);
case PrimaryConstructorBaseTypeSyntax primaryConstructorBaseType:
return this.AnalyzeDataFlow(primaryConstructorBaseType);
default:
throw new ArgumentException("statementOrExpression is not a StatementSyntax or an ExpressionSyntax.");
throw new ArgumentException("statementOrExpression is not a StatementSyntax or an ExpressionSyntax or a ConstructorInitializerSyntax or a PrimaryConstructorBaseTypeSyntax.");
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2264,6 +2264,40 @@ public override DataFlowAnalysis AnalyzeDataFlow(ExpressionSyntax expression)
return result;
}

public override DataFlowAnalysis AnalyzeDataFlow(ConstructorInitializerSyntax constructorInitializer)
{
if (constructorInitializer == null)
{
throw new ArgumentNullException(nameof(constructorInitializer));
}

if (!IsInTree(constructorInitializer))
{
throw new ArgumentException("node not within tree");
}

var context = RegionAnalysisContext(constructorInitializer);
var result = new CSharpDataFlowAnalysis(context);
return result;
}

public override DataFlowAnalysis AnalyzeDataFlow(PrimaryConstructorBaseTypeSyntax primaryConstructorBaseType)
{
if (primaryConstructorBaseType == null)
{
throw new ArgumentNullException(nameof(primaryConstructorBaseType));
}

if (!IsInTree(primaryConstructorBaseType))
{
throw new ArgumentException("node not within tree");
}

var context = RegionAnalysisContext(primaryConstructorBaseType);
var result = new CSharpDataFlowAnalysis(context);
return result;
}

public override DataFlowAnalysis AnalyzeDataFlow(StatementSyntax firstStatement, StatementSyntax lastStatement)
{
ValidateStatementRange(firstStatement, lastStatement);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ private RegionAnalysisContext RegionAnalysisContext(ExpressionSyntax expression)
while (expression.Kind() == SyntaxKind.ParenthesizedExpression)
expression = ((ParenthesizedExpressionSyntax)expression).Expression;

return RegionAnalysisContext((CSharpSyntaxNode)expression);
}

private RegionAnalysisContext RegionAnalysisContext(CSharpSyntaxNode expression)
{
var memberModel = GetMemberModel(expression);
if (memberModel == null)
{
Expand Down
2 changes: 2 additions & 0 deletions src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ override Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSynt
override Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax.Name.get -> Microsoft.CodeAnalysis.CSharp.Syntax.NameSyntax
override Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax.NamespaceKeyword.get -> Microsoft.CodeAnalysis.SyntaxToken
override Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax.Usings.get -> Microsoft.CodeAnalysis.SyntaxList<Microsoft.CodeAnalysis.CSharp.Syntax.UsingDirectiveSyntax>
static Microsoft.CodeAnalysis.CSharp.CSharpExtensions.AnalyzeDataFlow(this Microsoft.CodeAnalysis.SemanticModel semanticModel, Microsoft.CodeAnalysis.CSharp.Syntax.ConstructorInitializerSyntax constructorInitializer) -> Microsoft.CodeAnalysis.DataFlowAnalysis
static Microsoft.CodeAnalysis.CSharp.CSharpExtensions.AnalyzeDataFlow(this Microsoft.CodeAnalysis.SemanticModel semanticModel, Microsoft.CodeAnalysis.CSharp.Syntax.PrimaryConstructorBaseTypeSyntax primaryConstructorBaseType) -> Microsoft.CodeAnalysis.DataFlowAnalysis
override Microsoft.CodeAnalysis.CSharp.Syntax.SlicePatternSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> void
override Microsoft.CodeAnalysis.CSharp.Syntax.SlicePatternSyntax.Accept<TResult>(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor<TResult> visitor) -> TResult
static Microsoft.CodeAnalysis.CSharp.CSharpExtensions.GetDeclaredSymbol(this Microsoft.CodeAnalysis.SemanticModel semanticModel, Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax declarationSyntax, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Microsoft.CodeAnalysis.INamespaceSymbol
Expand Down
52 changes: 52 additions & 0 deletions src/Compilers/CSharp/Test/Semantic/FlowAnalysis/FlowTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,16 @@ protected DataFlowAnalysis CompileAndAnalyzeDataFlowExpression(string program, p
return CompileAndGetModelAndExpression(program, (model, expression) => model.AnalyzeDataFlow(expression), references);
}

protected DataFlowAnalysis CompileAndAnalyzeDataFlowConstructorInitializer(string program, params MetadataReference[] references)
{
return CompileAndGetModelAndConstructorInitializer(program, (model, constructorInitializer) => model.AnalyzeDataFlow(constructorInitializer), references);
}

protected DataFlowAnalysis CompileAndAnalyzeDataFlowPrimaryConstructorInitializer(string program, params MetadataReference[] references)
{
return CompileAndGetModelAndPrimaryConstructorInitializer(program, (model, primaryConstructorInitializer) => model.AnalyzeDataFlow(primaryConstructorInitializer), references);
}

protected DataFlowAnalysis CompileAndAnalyzeDataFlowStatements(string program)
{
return CompileAndGetModelAndStatements(program, (model, stmt1, stmt2) => model.AnalyzeDataFlow(stmt1, stmt2));
Expand All @@ -93,6 +103,48 @@ protected DataFlowAnalysis CompileAndAnalyzeDataFlowStatements(string program)
return CompileAndGetModelAndStatements(program, (model, stmt1, stmt2) => (model.AnalyzeControlFlow(stmt1, stmt2), model.AnalyzeDataFlow(stmt1, stmt2)));
}

protected T CompileAndGetModelAndConstructorInitializer<T>(string program, Func<SemanticModel, ConstructorInitializerSyntax, T> analysisDelegate, params MetadataReference[] references)
{
var comp = CreateCompilation(program, parseOptions: TestOptions.RegularPreview, references: references);
var tree = comp.SyntaxTrees[0];
var model = comp.GetSemanticModel(tree);
int start = program.IndexOf(StartString, StringComparison.Ordinal) + StartString.Length;
int end = program.IndexOf(EndString, StringComparison.Ordinal);
ConstructorInitializerSyntax syntaxToBind = null;
foreach (var expr in GetSyntaxNodeList(tree).OfType<ConstructorInitializerSyntax>())
{
if (expr.SpanStart >= start && expr.Span.End <= end)
{
syntaxToBind = expr;
break;
}
}

Assert.NotNull(syntaxToBind);
return analysisDelegate(model, syntaxToBind);
}

protected T CompileAndGetModelAndPrimaryConstructorInitializer<T>(string program, Func<SemanticModel, PrimaryConstructorBaseTypeSyntax, T> analysisDelegate, params MetadataReference[] references)
{
var comp = CreateCompilation(program, parseOptions: TestOptions.RegularPreview, references: references);
var tree = comp.SyntaxTrees[0];
var model = comp.GetSemanticModel(tree);
int start = program.IndexOf(StartString, StringComparison.Ordinal) + StartString.Length;
int end = program.IndexOf(EndString, StringComparison.Ordinal);
PrimaryConstructorBaseTypeSyntax syntaxToBind = null;
foreach (var expr in GetSyntaxNodeList(tree).OfType<PrimaryConstructorBaseTypeSyntax>())
{
if (expr.SpanStart >= start && expr.Span.End <= end)
{
syntaxToBind = expr;
break;
}
}

Assert.NotNull(syntaxToBind);
return analysisDelegate(model, syntaxToBind);
}

protected T CompileAndGetModelAndExpression<T>(string program, Func<SemanticModel, ExpressionSyntax, T> analysisDelegate, params MetadataReference[] references)
{
var comp = CreateCompilation(program, parseOptions: TestOptions.RegularPreview, references: references);
Expand Down
Loading

0 comments on commit aeff023

Please sign in to comment.