Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,4 @@ private bool ContainsStringFormatCall(ExpressionSyntax expression)
return false;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,4 @@ public override void Analyze()
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,4 @@ protected override async Task<Document> ApplyFix(Document document, InvocationEx
return document.WithSyntaxRoot(rootNode);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,18 @@ private void Analyze(SyntaxNodeAnalysisContext context)
return;
}

// Bail out early.
TypeSyntax typeSyntax = creation.Type;
if (!typeSyntax.ToString().Contains("Regex"))
TypeInfo typeInfo = context.SemanticModel.GetTypeInfo(creation);
ITypeSymbol typeSymbol = typeInfo.Type;
var usedConvertedType = false;

// For implicit constructors like new (".*"), Type might be a tuple (?, ?) but ConvertedType has the actual type
// Only use ConvertedType if Type is null or appears to be an incomplete/invalid type
if (typeSymbol == null || typeSymbol.ToString().Contains("?"))
{
return;
typeSymbol = typeInfo.ConvertedType;
usedConvertedType = true;
}

ITypeSymbol typeSymbol = context.SemanticModel.GetTypeInfo(creation).Type;
if (typeSymbol == null)
{
return;
Expand All @@ -58,21 +62,33 @@ private void Analyze(SyntaxNodeAnalysisContext context)
return;
}

// Skip incomplete implicit constructor nodes that have no arguments but are resolved via ConvertedType
// These appear to be parser artifacts and the real analysis happens on the complete nodes
if (usedConvertedType && creation.ArgumentList?.Arguments.Count == 0)
{
return;
}

AnalyzeCreation(context, creation.ArgumentList);
}

private void AnalyzeCreation(SyntaxNodeAnalysisContext context, ArgumentListSyntax argumentList)
{
// We require to use the constructor with the Timeout argument.
if (creation.ArgumentList is not { Arguments.Count: not CorrectConstructorArgumentCount })
if (argumentList is not { Arguments.Count: not CorrectConstructorArgumentCount })
{
return;
}

// NET7 has RegexOptions.NonBacktracking, which we also accept.
if (creation.ArgumentList.ToString().Contains("NonBacktracking"))
if (argumentList.ToString().Contains("NonBacktracking"))
{
return;
}

Location location = creation.ArgumentList.GetLocation();
Location location = argumentList.GetLocation();
var diagnostic = Diagnostic.Create(Rule, location);
context.ReportDiagnostic(diagnostic);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,8 @@ public Regex MethodA()
}

[DataTestMethod]
// NOTE: Implicit names are not currently detected by this analyzer.
//[DataRow(@"("".*"", RegexOptions.Compiled)")]
//[DataRow(@"("".*"")")]
[DataRow(@"("".*"", RegexOptions.Compiled)")]
[DataRow(@"("".*"")")]
[DataRow(@"Regex("".*"", RegexOptions.Compiled)")]
[DataRow(@"Regex("".*"")")]
[DataRow(@"System.Text.RegularExpressions.Regex("".*"", RegexOptions.Compiled)")]
Expand Down