Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix StackOverflowException in RoutePatternAnalyzer #48105

Merged
merged 3 commits into from
May 10, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -31,94 +31,82 @@ private void AnalyzeSemanticModel(SemanticModelAnalysisContext context)
var cancellationToken = context.CancellationToken;

var root = syntaxTree.GetRoot(cancellationToken);
var wellKnownTypes = WellKnownTypes.GetOrCreate(context.SemanticModel.Compilation);
var routeUsageCache = RouteUsageCache.GetOrCreate(context.SemanticModel.Compilation);
Analyze(context, root, wellKnownTypes, routeUsageCache, cancellationToken);

// Update to use FilterSpan when available. See https://github.com/dotnet/aspnetcore/issues/48157
foreach (var item in root.DescendantTokens())
JamesNK marked this conversation as resolved.
Show resolved Hide resolved
{
cancellationToken.ThrowIfCancellationRequested();

AnalyzeToken(context, routeUsageCache, item, cancellationToken);
}
}

private void Analyze(
SemanticModelAnalysisContext context,
SyntaxNode node,
WellKnownTypes wellKnownTypes,
RouteUsageCache routeUsageCache,
CancellationToken cancellationToken)
private static void AnalyzeToken(SemanticModelAnalysisContext context, RouteUsageCache routeUsageCache, SyntaxToken token, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();

foreach (var child in node.ChildNodesAndTokens())
if (!RouteStringSyntaxDetector.IsRouteStringSyntaxToken(token, context.SemanticModel, cancellationToken, out var options))
{
if (child.IsNode)
{
Analyze(context, child.AsNode()!, wellKnownTypes, routeUsageCache, cancellationToken);
}
else
{
var token = child.AsToken();
if (!RouteStringSyntaxDetector.IsRouteStringSyntaxToken(token, context.SemanticModel, cancellationToken, out var options))
{
continue;
}
return;
}

var routeUsage = routeUsageCache.Get(token, cancellationToken);
if (routeUsage is null)
{
continue;
}
var routeUsage = routeUsageCache.Get(token, cancellationToken);
if (routeUsage is null)
{
return;
}

foreach (var diag in routeUsage.RoutePattern.Diagnostics)
{
context.ReportDiagnostic(Diagnostic.Create(
DiagnosticDescriptors.RoutePatternIssue,
Location.Create(context.SemanticModel.SyntaxTree, diag.Span),
DiagnosticDescriptors.RoutePatternIssue.DefaultSeverity,
additionalLocations: null,
properties: null,
diag.Message));
}
foreach (var diag in routeUsage.RoutePattern.Diagnostics)
{
context.ReportDiagnostic(Diagnostic.Create(
DiagnosticDescriptors.RoutePatternIssue,
Location.Create(context.SemanticModel.SyntaxTree, diag.Span),
DiagnosticDescriptors.RoutePatternIssue.DefaultSeverity,
additionalLocations: null,
properties: null,
diag.Message));
}

if (routeUsage.UsageContext.MethodSymbol != null)
{
var routeParameterNames = new HashSet<string>(routeUsage.RoutePattern.RouteParameters.Select(p => p.Name), StringComparer.OrdinalIgnoreCase);
if (routeUsage.UsageContext.MethodSymbol != null)
{
var routeParameterNames = new HashSet<string>(routeUsage.RoutePattern.RouteParameters.Select(p => p.Name), StringComparer.OrdinalIgnoreCase);

foreach (var parameter in routeUsage.UsageContext.ResolvedParameters)
{
routeParameterNames.Remove(parameter.RouteParameterName);
}
foreach (var parameter in routeUsage.UsageContext.ResolvedParameters)
{
routeParameterNames.Remove(parameter.RouteParameterName);
}

foreach (var unusedParameterName in routeParameterNames)
foreach (var unusedParameterName in routeParameterNames)
{
var unusedParameter = routeUsage.RoutePattern.GetRouteParameter(unusedParameterName);

var parameterInsertIndex = -1;
var insertPoint = CalculateInsertPoint(
unusedParameter.Name,
routeUsage.RoutePattern.RouteParameters,
routeUsage.UsageContext.ResolvedParameters);
if (insertPoint is { } ip)
{
parameterInsertIndex = routeUsage.UsageContext.Parameters.IndexOf(ip.ExistingParameter);
if (!ip.Before)
{
var unusedParameter = routeUsage.RoutePattern.GetRouteParameter(unusedParameterName);

var parameterInsertIndex = -1;
var insertPoint = CalculateInsertPoint(
unusedParameter.Name,
routeUsage.RoutePattern.RouteParameters,
routeUsage.UsageContext.ResolvedParameters);
if (insertPoint is { } ip)
{
parameterInsertIndex = routeUsage.UsageContext.Parameters.IndexOf(ip.ExistingParameter);
if (!ip.Before)
{
parameterInsertIndex++;
}
}

// These properties are used by the fixer.
var propertiesBuilder = ImmutableDictionary.CreateBuilder<string, string?>();
propertiesBuilder.Add("RouteParameterName", unusedParameter.Name);
propertiesBuilder.Add("RouteParameterPolicy", string.Join(string.Empty, unusedParameter.Policies));
propertiesBuilder.Add("RouteParameterIsOptional", unusedParameter.IsOptional.ToString(CultureInfo.InvariantCulture));
propertiesBuilder.Add("RouteParameterInsertIndex", parameterInsertIndex.ToString(CultureInfo.InvariantCulture));

context.ReportDiagnostic(Diagnostic.Create(
DiagnosticDescriptors.RoutePatternUnusedParameter,
Location.Create(context.SemanticModel.SyntaxTree, unusedParameter.Span),
DiagnosticDescriptors.RoutePatternUnusedParameter.DefaultSeverity,
additionalLocations: null,
properties: propertiesBuilder.ToImmutableDictionary(),
unusedParameterName));
parameterInsertIndex++;
}
}

// These properties are used by the fixer.
var propertiesBuilder = ImmutableDictionary.CreateBuilder<string, string?>();
propertiesBuilder.Add("RouteParameterName", unusedParameter.Name);
propertiesBuilder.Add("RouteParameterPolicy", string.Join(string.Empty, unusedParameter.Policies));
propertiesBuilder.Add("RouteParameterIsOptional", unusedParameter.IsOptional.ToString(CultureInfo.InvariantCulture));
propertiesBuilder.Add("RouteParameterInsertIndex", parameterInsertIndex.ToString(CultureInfo.InvariantCulture));

context.ReportDiagnostic(Diagnostic.Create(
DiagnosticDescriptors.RoutePatternUnusedParameter,
Location.Create(context.SemanticModel.SyntaxTree, unusedParameter.Span),
DiagnosticDescriptors.RoutePatternUnusedParameter.DefaultSeverity,
additionalLocations: null,
properties: propertiesBuilder.ToImmutableDictionary(),
unusedParameterName));
}
}
}
Expand Down