Skip to content

Commit 4bbf2e6

Browse files
committed
Not pretty.
1 parent 730a1a9 commit 4bbf2e6

11 files changed

Lines changed: 191 additions & 78 deletions

File tree

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
namespace AspNetCoreAnalyzers.Tests.Helpers
2+
{
3+
using System;
4+
using Gu.Roslyn.Asserts;
5+
using Microsoft.CodeAnalysis.CSharp;
6+
using NUnit.Framework;
7+
8+
public class StringLiteralSpanTests
9+
{
10+
[TestCase("\"abc\"", "abc")]
11+
public void WhenEqual(string text, string expected)
12+
{
13+
var syntaxTree = CSharpSyntaxTree.ParseText(@"
14+
namespace AspBox.Controllers
15+
{
16+
using Microsoft.AspNetCore.Mvc;
17+
18+
[Route(""abc"")]
19+
class C
20+
{
21+
22+
}
23+
}".AssertReplace("\"abc\"", text));
24+
var literal = syntaxTree.FindLiteralExpression(text);
25+
var span = new StringLiteralSpan(new StringLiteral(literal), 0, 3);
26+
Assert.AreEqual(true, span.StartsWith(expected, StringComparison.Ordinal));
27+
Assert.AreEqual(true, span.EndsWith(expected, StringComparison.Ordinal));
28+
Assert.AreEqual(true, span.Equals(expected, StringComparison.Ordinal));
29+
}
30+
}
31+
}

AspNetCoreAnalyzers.Tests/Helpers/UrlTemplateTests.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
namespace AspNetCoreAnalyzers.Tests.Helpers
22
{
3+
using System;
34
using System.Linq;
45
using Gu.Roslyn.Asserts;
56
using Microsoft.CodeAnalysis.CSharp;
@@ -60,9 +61,10 @@ public async Task<IActionResult> GetOrder([FromRoute]int id)
6061
CollectionAssert.AreEqual(expected, template.Path.Select(x => x.Span.ToString()));
6162

6263
// ReSharper disable once PossibleInvalidOperationException
63-
var parameter = template.Path.Single(x => x.Parameter.HasValue)
64-
.Parameter.Value;
65-
Assert.AreEqual("id", parameter.Name.ToString());
64+
var segment = template.Path.Single(x => x.Parameter.HasValue);
65+
66+
Assert.AreEqual(expected.Single(x => x.StartsWith("{", StringComparison.Ordinal)), segment.Span.ToString());
67+
Assert.AreEqual("id", segment.Parameter.Value.Name.ToString());
6668
}
6769

6870
[TestCase("\"orders/{id}\"", new[] { "orders", "{id}" }, new string[0])]

AspNetCoreAnalyzers/Analyzers/AttributeAnalyzer.cs

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -36,38 +36,38 @@ context.ContainingSymbol is IMethodSymbol method &&
3636
{
3737
using (var pairs = GetPairs(template, method))
3838
{
39-
if (pairs.TrySingle(x => x.Template == null, out var withMethodParameter) &&
40-
methodDeclaration.TryFindParameter(withMethodParameter.Method.Name, out var parameterSyntax) &&
41-
pairs.TrySingle(x => x.Method == null, out var withTemplateParameter) &&
42-
withTemplateParameter.Template is TemplateParameter templateParameter)
39+
if (pairs.TrySingle(x => x.FromTemplate == null, out var withMethodParameter) &&
40+
methodDeclaration.TryFindParameter(withMethodParameter.FromMethodSymbol.Name, out var parameterSyntax) &&
41+
pairs.TrySingle(x => x.FromMethodSymbol == null, out var withTemplateParameter) &&
42+
withTemplateParameter.FromTemplate is TemplateParameter templateParameter)
4343
{
4444
context.ReportDiagnostic(
4545
Diagnostic.Create(
4646
ASP001ParameterName.Descriptor,
4747
parameterSyntax.Identifier.GetLocation(),
4848
ImmutableDictionary<string, string>.Empty.Add(
4949
nameof(NameSyntax),
50-
templateParameter.Name.Text)));
50+
templateParameter.Name.ToString())));
5151

5252
context.ReportDiagnostic(
5353
Diagnostic.Create(
5454
ASP002MissingParameter.Descriptor,
5555
templateParameter.Name.GetLocation(),
5656
ImmutableDictionary<string, string>.Empty.Add(
5757
nameof(Text),
58-
withMethodParameter.Method.Name)));
58+
withMethodParameter.FromMethodSymbol.Name)));
5959
}
60-
else if (pairs.Count(x => x.Template == null) > 1 &&
61-
pairs.Count(x => x.Method == null) > 1)
60+
else if (pairs.Count(x => x.FromTemplate == null) > 1 &&
61+
pairs.Count(x => x.FromMethodSymbol == null) > 1)
6262
{
6363
context.ReportDiagnostic(
6464
Diagnostic.Create(
6565
ASP001ParameterName.Descriptor,
6666
methodDeclaration.ParameterList.GetLocation()));
6767
}
6868

69-
if (pairs.TryFirst(x => x.Method == null, out _) &&
70-
!pairs.TryFirst(x => x.Template == null, out _))
69+
if (pairs.TryFirst(x => x.FromMethodSymbol == null, out _) &&
70+
!pairs.TryFirst(x => x.FromTemplate == null, out _))
7171
{
7272
context.ReportDiagnostic(
7373
Diagnostic.Create(
@@ -78,7 +78,7 @@ context.ContainingSymbol is IMethodSymbol method &&
7878
foreach (var pair in pairs)
7979
{
8080
if (HasWrongType(pair, out var typeName) &&
81-
methodDeclaration.TryFindParameter(pair.Method?.Name, out parameterSyntax))
81+
methodDeclaration.TryFindParameter(pair.FromMethodSymbol?.Name, out parameterSyntax))
8282
{
8383
context.ReportDiagnostic(
8484
Diagnostic.Create(
@@ -164,7 +164,7 @@ private static PooledList<ParameterPair> GetPairs(UrlTemplate template, IMethodS
164164
{
165165
if (IsFromRoute(parameter))
166166
{
167-
list.Add(template.Path.TrySingle(x => x.Parameter?.Name.Text == parameter.Name, out var templateParameter)
167+
list.Add(template.Path.TrySingle(x => x.Parameter?.Name.Equals(parameter.Name, StringComparison.Ordinal) == true, out var templateParameter)
168168
? new ParameterPair(templateParameter.Parameter, parameter)
169169
: new ParameterPair(null, parameter));
170170
}
@@ -173,7 +173,7 @@ private static PooledList<ParameterPair> GetPairs(UrlTemplate template, IMethodS
173173
foreach (var component in template.Path)
174174
{
175175
if (component.Parameter is TemplateParameter templateParameter &&
176-
list.All(x => x.Template != templateParameter))
176+
list.All(x => x.FromTemplate != templateParameter))
177177
{
178178
list.Add(new ParameterPair(templateParameter, null));
179179
}
@@ -184,13 +184,13 @@ private static PooledList<ParameterPair> GetPairs(UrlTemplate template, IMethodS
184184

185185
private static bool HasWrongType(ParameterPair pair, out string correctType)
186186
{
187-
if (pair.Template?.Constraints is ImmutableArray<RouteConstraint> constraints &&
188-
pair.Method is IParameterSymbol parameter)
187+
if (pair.FromTemplate?.Constraints is ImmutableArray<RouteConstraint> constraints &&
188+
pair.FromMethodSymbol is IParameterSymbol parameter)
189189
{
190190
foreach (var constraint in constraints)
191191
{
192192
// https://docs.microsoft.com/en-us/aspnet/core/fundamentals/routing?view=aspnetcore-2.2#route-constraint-reference
193-
if (TryGetType(constraint.Span.Text, out var type))
193+
if (TryGetType(constraint.Span, out var type))
194194
{
195195
correctType = parameter.Type == type ? null : type.Alias ?? type.FullName;
196196
return correctType != null;
@@ -201,7 +201,7 @@ private static bool HasWrongType(ParameterPair pair, out string correctType)
201201
correctType = null;
202202
return false;
203203

204-
bool TryGetType(string constraint, out QualifiedType type)
204+
bool TryGetType(StringLiteralSpan constraint, out QualifiedType type)
205205
{
206206
if (constraint.Equals("bool", StringComparison.Ordinal))
207207
{
@@ -275,7 +275,7 @@ private static bool HasWrongSyntax(PathSegment segment, out Location location, o
275275
{
276276
foreach (var constraint in parameter.Constraints)
277277
{
278-
var text = constraint.Span.Text;
278+
var text = constraint.Span;
279279
if (text.StartsWith("min(", StringComparison.OrdinalIgnoreCase) ||
280280
text.StartsWith("max(", StringComparison.OrdinalIgnoreCase) ||
281281
text.StartsWith("minlength(", StringComparison.OrdinalIgnoreCase) ||
@@ -328,7 +328,7 @@ private static bool HasWrongSyntax(PathSegment segment, out Location location, o
328328
}
329329
else
330330
{
331-
var text = segment.Span.Text;
331+
var text = segment.Span;
332332
if (text.StartsWith("{", StringComparison.Ordinal) &&
333333
!text.EndsWith("}", StringComparison.Ordinal))
334334
{
@@ -352,7 +352,7 @@ private static bool HasWrongSyntax(PathSegment segment, out Location location, o
352352

353353
bool HasWrongIntArgumentSyntax(RouteConstraint constraint, string methodName, out Location result)
354354
{
355-
var text = constraint.Span.Text;
355+
var text = constraint.Span;
356356
if (text.Length > methodName.Length + 2 &&
357357
text.StartsWith(methodName, StringComparison.OrdinalIgnoreCase) &&
358358
text[methodName.Length] == '(' &&
@@ -379,7 +379,7 @@ private static bool HasWrongRegexSyntax(PathSegment segment, out Location locati
379379
{
380380
foreach (var constraint in parameter.Constraints)
381381
{
382-
var text = constraint.Span.Text;
382+
var text = constraint.Span;
383383
if (text.StartsWith("regex(", StringComparison.OrdinalIgnoreCase))
384384
{
385385
for (var i = 6; i < text.Length - 1; i++)
Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
namespace AspNetCoreAnalyzers
22
{
3+
using System.Diagnostics;
34
using Microsoft.CodeAnalysis;
45

6+
[DebuggerDisplay("{this.FromTemplate?.Name ?? this.FromMethodSymbol.Name}")]
57
public struct ParameterPair
68
{
7-
public ParameterPair(TemplateParameter? template, IParameterSymbol method)
9+
public ParameterPair(TemplateParameter? fromTemplate, IParameterSymbol fromMethodSymbol)
810
{
9-
this.Template = template;
10-
this.Method = method;
11+
this.FromTemplate = fromTemplate;
12+
this.FromMethodSymbol = fromMethodSymbol;
1113
}
1214

13-
public TemplateParameter? Template { get; }
15+
public TemplateParameter? FromTemplate { get; }
1416

15-
public IParameterSymbol Method { get; }
17+
public IParameterSymbol FromMethodSymbol { get; }
1618
}
1719
}

AspNetCoreAnalyzers/Helpers/PathSegment.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ namespace AspNetCoreAnalyzers
22
{
33
using System.Diagnostics;
44

5-
[DebuggerDisplay("{this.Span.Text}")]
5+
[DebuggerDisplay("{this.Span.ToString()}")]
66
public struct PathSegment
77
{
88
public PathSegment(StringLiteral literal, int start, int end)
@@ -20,7 +20,7 @@ public PathSegment(StringLiteral literal, int start, int end)
2020
public static bool TryRead(StringLiteral literal, int start, out PathSegment segment)
2121
{
2222
// https://tools.ietf.org/html/rfc3986
23-
var text = literal.LiteralExpression.Token.ValueText;
23+
var text = literal.ValueText;
2424
var pos = start;
2525
if (pos < text.Length - 1)
2626
{

AspNetCoreAnalyzers/Helpers/RouteConstraint.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ namespace AspNetCoreAnalyzers
33
using System;
44
using System.Diagnostics;
55

6-
[DebuggerDisplay("{this.Span.Text}")]
6+
[DebuggerDisplay("{this.Span.ToString()}")]
77
public struct RouteConstraint : IEquatable<RouteConstraint>
88
{
99
public RouteConstraint(StringLiteralSpan span)
@@ -26,7 +26,7 @@ public RouteConstraint(StringLiteralSpan span)
2626
public static bool TryRead(StringLiteralSpan span, int pos, out RouteConstraint constraint)
2727
{
2828
if (pos >= span.TextSpan.End ||
29-
span.Text[pos] != ':')
29+
span[pos] != ':')
3030
{
3131
constraint = default(RouteConstraint);
3232
return false;
@@ -35,10 +35,10 @@ public static bool TryRead(StringLiteralSpan span, int pos, out RouteConstraint
3535
pos++;
3636
for (var i = pos; i < span.TextSpan.Length; i++)
3737
{
38-
switch (span.Text[i])
38+
switch (span[i])
3939
{
40-
case '(' when Text.TrySkipPast(span.Text, ref i, "):") ||
41-
Text.TrySkipPast(span.Text, ref i, ")}"):
40+
case '(' when Text.TrySkipPast(span, ref i, "):") ||
41+
Text.TrySkipPast(span, ref i, ")}"):
4242
constraint = new RouteConstraint(span.Slice(pos, i - 1));
4343
return true;
4444
case '}':

AspNetCoreAnalyzers/Helpers/StringLiteral.cs

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,25 @@
11
namespace AspNetCoreAnalyzers
22
{
3+
using System.Diagnostics;
34
using Microsoft.CodeAnalysis;
45
using Microsoft.CodeAnalysis.CSharp.Syntax;
56
using Microsoft.CodeAnalysis.Text;
67

8+
[DebuggerDisplay("{this.Text}")]
79
public struct StringLiteral
810
{
11+
private readonly LiteralExpressionSyntax literalExpression;
12+
913
public StringLiteral(LiteralExpressionSyntax literalExpression)
1014
{
11-
this.LiteralExpression = literalExpression;
15+
this.literalExpression = literalExpression;
1216
}
1317

14-
public LiteralExpressionSyntax LiteralExpression { get; }
15-
1618
public bool IsVerbatim
1719
{
1820
get
1921
{
20-
foreach (var c in this.LiteralExpression.Token.Text)
22+
foreach (var c in this.literalExpression.Token.Text)
2123
{
2224
switch (c)
2325
{
@@ -32,9 +34,13 @@ public bool IsVerbatim
3234
}
3335
}
3436

37+
public string Text => this.literalExpression.Token.Text;
38+
39+
public string ValueText => this.literalExpression.Token.ValueText;
40+
3541
public Location GetLocation(TextSpan textSpan)
3642
{
37-
var text = this.LiteralExpression.Token.Text;
43+
var text = this.literalExpression.Token.Text;
3844
var start = 0;
3945
var verbatim = false;
4046
while (start < 3)
@@ -53,14 +59,15 @@ public Location GetLocation(TextSpan textSpan)
5359
start++;
5460
}
5561

56-
return Location.Create(this.LiteralExpression.SyntaxTree,
57-
verbatim
58-
? new TextSpan(
59-
this.LiteralExpression.SpanStart + start + textSpan.Start,
60-
textSpan.Length)
61-
: TextSpan.FromBounds(
62-
this.LiteralExpression.SpanStart + GetIndex(textSpan.Start),
63-
this.LiteralExpression.SpanStart + GetIndex(textSpan.End)));
62+
return Location.Create(
63+
this.literalExpression.SyntaxTree,
64+
verbatim
65+
? new TextSpan(
66+
this.literalExpression.SpanStart + start + textSpan.Start,
67+
textSpan.Length)
68+
: TextSpan.FromBounds(
69+
this.literalExpression.SpanStart + GetIndex(textSpan.Start),
70+
this.literalExpression.SpanStart + GetIndex(textSpan.End)));
6471

6572
int GetIndex(int pos)
6673
{

0 commit comments

Comments
 (0)