Skip to content

Commit

Permalink
Third round of comments
Browse files Browse the repository at this point in the history
  • Loading branch information
CristianAmbrosini committed Mar 15, 2023
1 parent bc6adc4 commit 8e5d13d
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 65 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -147,5 +147,5 @@ public bool HasMatchingScope(DiagnosticDescriptor descriptor)
inclusions is { Length: 0 } || inclusions.Any(x => WildcardPatternMatcher.IsMatch(x, filePath, true));

private static bool IsExcluded(string[] exclusions, string filePath) =>
exclusions.Any(x => WildcardPatternMatcher.IsMatch(x, filePath));
exclusions.Any(x => WildcardPatternMatcher.IsMatch(x, filePath, false));
}
113 changes: 52 additions & 61 deletions analyzers/src/SonarAnalyzer.Common/Helpers/WildcardPatternMatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,87 +25,78 @@

namespace SonarAnalyzer.Helpers;

internal class WildcardPatternMatcher
internal static class WildcardPatternMatcher
{
public static bool IsMatch(string pattern, string input, bool timoutFallback = false) =>
private static readonly ConcurrentDictionary<string, Regex> Cache = new();

public static bool IsMatch(string pattern, string input, bool timeoutFallbackResult) =>
!(string.IsNullOrWhiteSpace(pattern) || string.IsNullOrWhiteSpace(input))
&& WildcardPattern.Create(pattern).Match(input, timoutFallback);
&& Cache.GetOrAdd(pattern + Path.DirectorySeparatorChar, _ => new Regex(ToRegex(pattern), RegexOptions.None, RegexConstants.DefaultTimeout)) is var regex
&& IsMatch(regex, input, timeoutFallbackResult);

/// <summary>
/// Copied from https://github.com/SonarSource/sonar-plugin-api/blob/a9bd7ff48f0f77811ed909070030678c443c975a/sonar-plugin-api/src/main/java/org/sonar/api/utils/WildcardPattern.java.
/// </summary>
private sealed class WildcardPattern
private static bool IsMatch(Regex regex, string value, bool timeoutFallbackResult)
{
private static readonly ConcurrentDictionary<string, WildcardPattern> Cache = new();
private readonly Regex pattern;

private WildcardPattern(string pattern) =>
this.pattern = new Regex(ToRegex(pattern), RegexOptions.None, RegexConstants.DefaultTimeout);

public bool Match(string value, bool timoutFallback)
try
{
try
{
return pattern.IsMatch(value.Trim('/'));
}
catch (RegexMatchTimeoutException)
{
return timoutFallback;
}
return regex.IsMatch(value.Trim('/'));
}
catch (RegexMatchTimeoutException)
{
return timeoutFallbackResult;
}
}

public static WildcardPattern Create(string pattern) =>
Cache.GetOrAdd(pattern + Path.DirectorySeparatorChar, _ => new WildcardPattern(pattern));

private static string ToRegex(string wildcardPattern)
/// <summary>
/// Copied from https://github.com/SonarSource/sonar-plugin-api/blob/a9bd7ff48f0f77811ed909070030678c443c975a/sonar-plugin-api/src/main/java/org/sonar/api/utils/WildcardPattern.java.
/// </summary>
private static string ToRegex(string wildcardPattern)
{
var escapedDirectorySeparator = Regex.Escape(Path.DirectorySeparatorChar.ToString());
var sb = new StringBuilder("^", wildcardPattern.Length);
var i = IsSlash(wildcardPattern[0]) ? 1 : 0;
while (i < wildcardPattern.Length)
{
var escapedDirectorySeparator = Regex.Escape(Path.DirectorySeparatorChar.ToString());
var sb = new StringBuilder("^", wildcardPattern.Length);
var i = IsSlash(wildcardPattern[0]) ? 1 : 0;
while (i < wildcardPattern.Length)
var ch = wildcardPattern[i];
if (ch == '*')
{
var ch = wildcardPattern[i];
if (ch == '*')
if (i + 1 < wildcardPattern.Length && wildcardPattern[i + 1] == '*')
{
if (i + 1 < wildcardPattern.Length && wildcardPattern[i + 1] == '*')
// Double asterisk - Zero or more directories
if (i + 2 < wildcardPattern.Length && IsSlash(wildcardPattern[i + 2]))
{
// Double asterisk - Zero or more directories
if (i + 2 < wildcardPattern.Length && IsSlash(wildcardPattern[i + 2]))
{
sb.Append($"(.*{escapedDirectorySeparator}|)");
i += 2;
}
else
{
sb.Append(".*");
i += 1;
}
sb.Append($"(.*{escapedDirectorySeparator}|)");
i += 2;
}
else
{
// Single asterisk - Zero or more characters excluding directory separator
sb.Append($"[^{escapedDirectorySeparator}]*?");
sb.Append(".*");
i += 1;
}
}
else if (ch == '?')
{
// Any single character excluding directory separator
sb.Append($"[^{escapedDirectorySeparator}]");
}
else if (IsSlash(ch))
{
sb.Append(escapedDirectorySeparator);
}
else
{
sb.Append(Regex.Escape(ch.ToString()));
// Single asterisk - Zero or more characters excluding directory separator
sb.Append($"[^{escapedDirectorySeparator}]*?");
}
i++;
}
return sb.Append('$').ToString();
else if (ch == '?')
{
// Any single character excluding directory separator
sb.Append($"[^{escapedDirectorySeparator}]");
}
else if (IsSlash(ch))
{
sb.Append(escapedDirectorySeparator);
}
else
{
sb.Append(Regex.Escape(ch.ToString()));
}
i++;
}

private static bool IsSlash(char ch) =>
ch == '/' || ch == '\\';
return sb.Append('$').ToString();
}

private static bool IsSlash(char ch) =>
ch == '/' || ch == '\\';
}
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public void IsMatch_MatchesPatternsAsExpected(string pattern, string input, bool
// The test cases are copied from the plugin-api and the directory separators need replacing as Roslyn will not give us the paths with '/'.
input = input.Replace("/", Path.DirectorySeparatorChar.ToString());

WildcardPatternMatcher.IsMatch(pattern, input).Should().Be(expectedResult);
WildcardPatternMatcher.IsMatch(pattern, input, false).Should().Be(expectedResult);
}

[DataTestMethod]
Expand All @@ -97,12 +97,12 @@ public void IsMatch_MatchesPatternsAsExpected(string pattern, string input, bool
[DataRow("/")]
[DataRow("\\")]
public void IsMatch_InvalidPattern_ReturnsFalse(string pattern) =>
WildcardPatternMatcher.IsMatch(pattern, "foo").Should().BeFalse();
WildcardPatternMatcher.IsMatch(pattern, "foo", false).Should().BeFalse();

[DataTestMethod]
[DataRow(null, "foo")]
[DataRow("foo", null)]
public void IsMatch_InputParametersArenull_DoesNotThrow(string pattern, string input) =>
WildcardPatternMatcher.IsMatch(pattern, input).Should().BeFalse();
WildcardPatternMatcher.IsMatch(pattern, input, false).Should().BeFalse();
}
}

0 comments on commit 8e5d13d

Please sign in to comment.