< Summary

Information
Class: AspxLint.Core.Rules.Attr002MixedQuotes
Assembly: AspxLint.Core
File(s): D:\a\claude-aspx-lint\claude-aspx-lint\src\AspxLint.Core\Rules\Attr002MixedQuotes.cs
Line coverage
100%
Covered lines: 18
Uncovered lines: 0
Coverable lines: 18
Total lines: 53
Line coverage: 100%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_Id()100%11100%
get_Name()100%11100%
get_Severity()100%11100%
get_Description()100%11100%
get_HasFix()100%11100%
.cctor()100%11100%
Fix(...)100%11100%

File(s)

D:\a\claude-aspx-lint\claude-aspx-lint\src\AspxLint.Core\Rules\Attr002MixedQuotes.cs

#LineLine coverage
 1using System.Text.RegularExpressions;
 2
 3namespace AspxLint.Core.Rules;
 4
 5public sealed class Attr002MixedQuotes : IRule
 6{
 2077    public string Id => "ATTR-002";
 288    public string Name => "Melange guillemets simples / doubles";
 259    public Severity Severity => Severity.Info;
 10    public string Description =>
 2511        "Pour la coherence du code, utilisez systematiquement les guillemets doubles (\") pour les attributs HTML/ASP.NE
 4612    public bool HasFix => true;
 13
 514    private static readonly Regex SingleQuotedAttr =
 515        new(@"\s([a-zA-Z][a-zA-Z0-9\-:_]*)='([^']*)'", RegexOptions.Compiled);
 16
 517    private static readonly Regex AspBlockOnLine =
 518        new(@"<%[\s\S]*?%>", RegexOptions.Compiled);
 19
 520    private static readonly Regex TagWithAttrs =
 521        new(@"(<[a-zA-Z][a-zA-Z0-9:_\-]*\b[^>]*?)>", RegexOptions.Compiled);
 22
 23    public IEnumerable<Issue> Detect(string content, string[] lines, RuleContext ctx)
 24    {
 25        for (int i = 0; i < lines.Length; i++)
 26        {
 27            var line = lines[i];
 28            // Si la ligne contient du code serveur, c'est trop ambigu (les ' peuvent etre du C#) — on saute.
 29            if (AspBlockOnLine.IsMatch(line)) continue;
 30
 31            foreach (Match m in SingleQuotedAttr.Matches(line))
 32            {
 33                var before = line[..m.Index];
 34                var lastOpen = before.LastIndexOf('<');
 35                var lastClose = before.LastIndexOf('>');
 36                if (lastOpen <= lastClose) continue;
 37
 38                yield return new Issue(Id, Name, Severity,
 39                    i + 1, m.Index + 1, m.Value.Trim(),
 40                    $"Preferer \"{m.Groups[1].Value}=\\\"{m.Groups[2].Value}\\\"\"");
 41            }
 42        }
 43    }
 44
 45    public string? Fix(string content, RuleContext ctx) =>
 2146        TagWithAttrs.Replace(content, m =>
 2147        {
 2148            var body = m.Groups[1].Value;
 2149            var fixedBody = SingleQuotedAttr.Replace(body, mm =>
 2150                $" {mm.Groups[1].Value}=\"{mm.Groups[2].Value.Replace("\"", "&quot;")}\"");
 2151            return fixedBody + ">";
 2152        });
 53}