Skip to content

Commit 3f04950

Browse files
committed
Simplify lightup
1 parent 841ceab commit 3f04950

File tree

5 files changed

+331
-133
lines changed

5 files changed

+331
-133
lines changed

StyleCop.Analyzers/StyleCop.Analyzers/Helpers/UsingAliasCache.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,8 @@
33

44
namespace StyleCop.Analyzers.Helpers
55
{
6-
using System;
76
using System.Collections.Concurrent;
8-
using System.Collections.Generic;
97
using System.Linq;
10-
using System.Text;
118
using System.Threading;
129
using Microsoft.CodeAnalysis;
1310
using Microsoft.CodeAnalysis.CSharp;
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
2+
// Licensed under the MIT License. See LICENSE in the project root for license information.
3+
4+
namespace StyleCop.Analyzers.Lightup
5+
{
6+
using System;
7+
using System.Collections;
8+
using System.Collections.Generic;
9+
using System.Collections.Immutable;
10+
11+
internal abstract class ImmutableArrayWrapper<T> : IEquatable<ImmutableArrayWrapper<T>>, IReadOnlyList<T>
12+
{
13+
public static ImmutableArrayWrapper<T> UnsupportedDefault { get; } =
14+
new UnsupportedImmutableArray();
15+
16+
public abstract bool IsEmpty { get; }
17+
18+
public abstract bool IsDefault { get; }
19+
20+
public abstract bool IsDefaultOrEmpty { get; }
21+
22+
public abstract int Length { get; }
23+
24+
int IReadOnlyCollection<T>.Count => this.Length;
25+
26+
public abstract T this[int index] { get; }
27+
28+
public static bool operator ==(ImmutableArrayWrapper<T>? left, ImmutableArrayWrapper<T>? right)
29+
{
30+
throw new NotImplementedException();
31+
}
32+
33+
public static bool operator !=(ImmutableArrayWrapper<T>? left, ImmutableArrayWrapper<T>? right)
34+
{
35+
throw new NotImplementedException();
36+
}
37+
38+
public bool Equals(ImmutableArrayWrapper<T>? other)
39+
{
40+
throw new NotImplementedException();
41+
}
42+
43+
public override bool Equals(object obj)
44+
{
45+
throw new NotImplementedException();
46+
}
47+
48+
public Enumerator GetEnumerator()
49+
{
50+
return new Enumerator(this);
51+
}
52+
53+
IEnumerator<T> IEnumerable<T>.GetEnumerator()
54+
{
55+
return this.GetEnumerator();
56+
}
57+
58+
IEnumerator IEnumerable.GetEnumerator()
59+
{
60+
return ((IEnumerable<T>)this).GetEnumerator();
61+
}
62+
63+
public override abstract int GetHashCode();
64+
65+
public struct Enumerator : IEnumerator<T>
66+
{
67+
private readonly ImmutableArrayWrapper<T> wrapper;
68+
private int index;
69+
private T current;
70+
71+
public Enumerator(ImmutableArrayWrapper<T> wrapper)
72+
{
73+
this.wrapper = wrapper;
74+
this.index = -1;
75+
this.current = default!;
76+
}
77+
78+
public T Current => this.current;
79+
80+
object IEnumerator.Current => this.Current!;
81+
82+
public override bool Equals(object obj)
83+
{
84+
Enumerator? otherOpt = obj as Enumerator?;
85+
if (!otherOpt.HasValue)
86+
{
87+
return false;
88+
}
89+
90+
Enumerator other = otherOpt.GetValueOrDefault();
91+
return other.wrapper == this.wrapper
92+
&& other.index == this.index;
93+
}
94+
95+
public override int GetHashCode()
96+
{
97+
if (this.wrapper == null)
98+
{
99+
return 0;
100+
}
101+
102+
return this.wrapper.GetHashCode() ^ this.index;
103+
}
104+
105+
public void Dispose()
106+
{
107+
}
108+
109+
public bool MoveNext()
110+
{
111+
if (this.index < -1)
112+
{
113+
return false;
114+
}
115+
116+
if (this.index == this.wrapper.Length - 1)
117+
{
118+
this.index = int.MinValue;
119+
return false;
120+
}
121+
122+
this.index++;
123+
this.current = this.wrapper[this.index];
124+
return true;
125+
}
126+
127+
public void Reset()
128+
{
129+
this.index = -1;
130+
this.current = default!;
131+
}
132+
}
133+
134+
internal sealed class AutoWrapImmutableArray<TElement> : ImmutableArrayWrapper<T>
135+
{
136+
private readonly ImmutableArray<TElement> array;
137+
138+
public AutoWrapImmutableArray()
139+
: this(default)
140+
{
141+
}
142+
143+
public AutoWrapImmutableArray(ImmutableArray<TElement> array)
144+
{
145+
this.array = array;
146+
}
147+
148+
public override int Length => this.array.Length;
149+
150+
public override bool IsEmpty => this.array.IsEmpty;
151+
152+
public override bool IsDefault => this.array.IsDefault;
153+
154+
public override bool IsDefaultOrEmpty => this.array.IsDefaultOrEmpty;
155+
156+
public override T this[int index]
157+
=> (T)WrapperHelper.Wrap(this.array[index]);
158+
159+
public override int GetHashCode()
160+
=> this.array.GetHashCode();
161+
}
162+
163+
private sealed class UnsupportedImmutableArray : ImmutableArrayWrapper<T>
164+
{
165+
private static readonly ImmutableArray<T> SyntaxList = default;
166+
167+
public UnsupportedImmutableArray()
168+
{
169+
}
170+
171+
public override int Length => SyntaxList.Length;
172+
173+
public override bool IsEmpty => SyntaxList.IsEmpty;
174+
175+
public override bool IsDefault => SyntaxList.IsDefault;
176+
177+
public override bool IsDefaultOrEmpty => SyntaxList.IsDefaultOrEmpty;
178+
179+
public override T this[int index]
180+
=> SyntaxList[index];
181+
182+
public override int GetHashCode()
183+
=> SyntaxList.GetHashCode();
184+
}
185+
}
186+
}

StyleCop.Analyzers/StyleCop.Analyzers/Lightup/LightupHelpers.cs

Lines changed: 103 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,105 @@ static bool FallbackAccessor(TSyntax syntax, TKey key, out TValue value)
470470
return expression.Compile();
471471
}
472472

473+
internal static Func<T, TArg1, TArg2, ImmutableArrayWrapper<TResult>> CreateImmutableArrayMethodAccessor<T, TArg1, TArg2, TResult>(Type type, Type argumentType1, Type argumentType2, string methodName)
474+
{
475+
ImmutableArrayWrapper<TResult> FallbackAccessor(T instance, TArg1 arg1, TArg2 arg2)
476+
{
477+
if (instance == null)
478+
{
479+
// Unlike an extension method which would throw ArgumentNullException here, the light-up
480+
// behavior needs to match behavior of the underlying property.
481+
throw new NullReferenceException();
482+
}
483+
484+
return ImmutableArrayWrapper<TResult>.UnsupportedDefault;
485+
}
486+
487+
if (type == null)
488+
{
489+
return FallbackAccessor;
490+
}
491+
492+
if (!typeof(T).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo()))
493+
{
494+
throw new InvalidOperationException();
495+
}
496+
497+
if (!typeof(TArg1).GetTypeInfo().IsAssignableFrom(argumentType1.GetTypeInfo()))
498+
{
499+
throw new InvalidOperationException();
500+
}
501+
502+
if (!typeof(TArg2).GetTypeInfo().IsAssignableFrom(argumentType2.GetTypeInfo()))
503+
{
504+
throw new InvalidOperationException();
505+
}
506+
507+
var methods = type.GetTypeInfo().GetDeclaredMethods(methodName);
508+
MethodInfo method = null;
509+
foreach (var candidate in methods)
510+
{
511+
var parameters = candidate.GetParameters();
512+
if (parameters.Length != 2)
513+
{
514+
continue;
515+
}
516+
517+
if (Equals(argumentType1, parameters[0].ParameterType)
518+
&& Equals(argumentType2, parameters[1].ParameterType))
519+
{
520+
method = candidate;
521+
break;
522+
}
523+
}
524+
525+
if (method == null)
526+
{
527+
return FallbackAccessor;
528+
}
529+
530+
if (method.ReturnType.GetGenericTypeDefinition() != typeof(ImmutableArray<>))
531+
{
532+
throw new InvalidOperationException();
533+
}
534+
535+
var methodResultType = method.ReturnType.GenericTypeArguments[0];
536+
if (!ValidatePropertyType(typeof(TResult), methodResultType))
537+
{
538+
throw new InvalidOperationException();
539+
}
540+
541+
var instanceParameter = Expression.Parameter(typeof(T), "instance");
542+
var arg1Parameter = Expression.Parameter(typeof(TArg1), "arg1");
543+
var arg2Parameter = Expression.Parameter(typeof(TArg2), "arg2");
544+
Expression instance =
545+
type.GetTypeInfo().IsAssignableFrom(typeof(T).GetTypeInfo())
546+
? (Expression)instanceParameter
547+
: Expression.Convert(instanceParameter, type);
548+
Expression argument1 =
549+
argumentType1.GetTypeInfo().IsAssignableFrom(typeof(TArg1).GetTypeInfo())
550+
? (Expression)arg1Parameter
551+
: Expression.Convert(arg1Parameter, argumentType1);
552+
Expression argument2 =
553+
argumentType2.GetTypeInfo().IsAssignableFrom(typeof(TArg2).GetTypeInfo())
554+
? (Expression)arg2Parameter
555+
: Expression.Convert(arg2Parameter, argumentType2);
556+
557+
Expression methodAccess = Expression.Call(instance, method, argument1, argument2);
558+
559+
var unboundWrapperType = typeof(ImmutableArrayWrapper<>.AutoWrapImmutableArray<>);
560+
var boundWrapperType = unboundWrapperType.MakeGenericType(typeof(TResult), methodResultType);
561+
var constructorInfo = boundWrapperType.GetTypeInfo().DeclaredConstructors.Single(constructor => constructor.GetParameters().Length == 1);
562+
563+
Expression<Func<T, TArg1, TArg2, ImmutableArrayWrapper<TResult>>> expression =
564+
Expression.Lambda<Func<T, TArg1, TArg2, ImmutableArrayWrapper<TResult>>>(
565+
Expression.New(constructorInfo, methodAccess),
566+
instanceParameter,
567+
arg1Parameter,
568+
arg2Parameter);
569+
return expression.Compile();
570+
}
571+
473572
internal static Func<TSyntax, SeparatedSyntaxListWrapper<TProperty>> CreateSeparatedSyntaxListPropertyAccessor<TSyntax, TProperty>(Type type, string propertyName)
474573
{
475574
SeparatedSyntaxListWrapper<TProperty> FallbackAccessor(TSyntax syntax)
@@ -668,7 +767,10 @@ TSyntax FallbackAccessor(TSyntax syntax, SeparatedSyntaxListWrapper<TProperty> n
668767

669768
private static bool ValidatePropertyType(Type returnType, Type actualType)
670769
{
671-
var requiredType = SyntaxWrapperHelper.GetWrappedType(returnType) ?? returnType;
770+
var requiredType = SyntaxWrapperHelper.GetWrappedType(returnType)
771+
?? OperationWrapperHelper.GetWrappedType(returnType)
772+
?? WrapperHelper.GetWrappedType(returnType)
773+
?? returnType;
672774
return requiredType == actualType;
673775
}
674776
}

0 commit comments

Comments
 (0)