Skip to content

Commit e28caec

Browse files
committed
Include NonCopyableAnalyzer
1 parent 58df35b commit e28caec

File tree

6 files changed

+414
-5
lines changed

6 files changed

+414
-5
lines changed

Directory.Build.props

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@
1313
<PrivateAssets>all</PrivateAssets>
1414
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
1515
</PackageReference>
16-
<PackageReference Include="Lost.NonCopyableAnalyzer" Version="0.7.0-m04">
17-
<PrivateAssets>all</PrivateAssets>
18-
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
19-
</PackageReference>
16+
<ProjectReference Include="$(MSBuildThisFileDirectory)src\noncopyable_analyzer\NonCopyable.csproj">
17+
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
18+
<OutputItemType>Analyzer</OutputItemType>
19+
</ProjectReference>
2020
</ItemGroup>
2121
</Project>

pythonnet.sln

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Microsoft Visual Studio Solution File, Format Version 12.00
1+
Microsoft Visual Studio Solution File, Format Version 12.00
22
# Visual Studio Version 16
33
VisualStudioVersion = 16.0.30717.126
44
MinimumVisualStudioVersion = 15.0.26124.0
@@ -56,6 +56,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{142A6752
5656
Directory.Build.props = Directory.Build.props
5757
EndProjectSection
5858
EndProject
59+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{D4963EF0-46CD-43AF-939D-BA47C1B091D1}"
60+
EndProject
61+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NonCopyable", "src\noncopyable_analyzer\NonCopyable.csproj", "{CA041F36-A4C2-4B18-9501-F670FDED87F4}"
62+
EndProject
5963
Global
6064
GlobalSection(SolutionConfigurationPlatforms) = preSolution
6165
Debug|Any CPU = Debug|Any CPU
@@ -162,11 +166,26 @@ Global
162166
{35CBBDEB-FC07-4D04-9D3E-F88FC180110B}.Release|x64.Build.0 = Release|Any CPU
163167
{35CBBDEB-FC07-4D04-9D3E-F88FC180110B}.Release|x86.ActiveCfg = Release|Any CPU
164168
{35CBBDEB-FC07-4D04-9D3E-F88FC180110B}.Release|x86.Build.0 = Release|Any CPU
169+
{CA041F36-A4C2-4B18-9501-F670FDED87F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
170+
{CA041F36-A4C2-4B18-9501-F670FDED87F4}.Debug|Any CPU.Build.0 = Debug|Any CPU
171+
{CA041F36-A4C2-4B18-9501-F670FDED87F4}.Debug|x64.ActiveCfg = Debug|Any CPU
172+
{CA041F36-A4C2-4B18-9501-F670FDED87F4}.Debug|x64.Build.0 = Debug|Any CPU
173+
{CA041F36-A4C2-4B18-9501-F670FDED87F4}.Debug|x86.ActiveCfg = Debug|Any CPU
174+
{CA041F36-A4C2-4B18-9501-F670FDED87F4}.Debug|x86.Build.0 = Debug|Any CPU
175+
{CA041F36-A4C2-4B18-9501-F670FDED87F4}.Release|Any CPU.ActiveCfg = Release|Any CPU
176+
{CA041F36-A4C2-4B18-9501-F670FDED87F4}.Release|Any CPU.Build.0 = Release|Any CPU
177+
{CA041F36-A4C2-4B18-9501-F670FDED87F4}.Release|x64.ActiveCfg = Release|Any CPU
178+
{CA041F36-A4C2-4B18-9501-F670FDED87F4}.Release|x64.Build.0 = Release|Any CPU
179+
{CA041F36-A4C2-4B18-9501-F670FDED87F4}.Release|x86.ActiveCfg = Release|Any CPU
180+
{CA041F36-A4C2-4B18-9501-F670FDED87F4}.Release|x86.Build.0 = Release|Any CPU
165181
EndGlobalSection
166182
GlobalSection(SolutionProperties) = preSolution
167183
HideSolutionNode = FALSE
168184
EndGlobalSection
169185
GlobalSection(ExtensibilityGlobals) = postSolution
170186
SolutionGuid = {C8845072-C642-4858-8627-27E862AD21BB}
171187
EndGlobalSection
188+
GlobalSection(NestedProjects) = preSolution
189+
{CA041F36-A4C2-4B18-9501-F670FDED87F4} = {D4963EF0-46CD-43AF-939D-BA47C1B091D1}
190+
EndGlobalSection
172191
EndGlobal

src/noncopyable_analyzer/LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2017 Nobuyuki Iwanaga
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>netstandard2.0</TargetFramework>
5+
<IncludeBuildOutput>false</IncludeBuildOutput>
6+
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
7+
<LangVersion>latest</LangVersion>
8+
</PropertyGroup>
9+
10+
<PropertyGroup>
11+
<PackageId>PythonNet.NonCopyableAnalyzer</PackageId>
12+
<Authors>Nobuyuki Iwanaga</Authors>
13+
<PackageLicenseUrl>https://github.com/ufcpp/NonCopyableAnalyzer/blob/master/LICENSE</PackageLicenseUrl>
14+
<PackageProjectUrl>https://github.com/ufcpp/NonCopyableAnalyzer</PackageProjectUrl>
15+
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
16+
<Description>Analyzer for Non-copyable struct</Description>
17+
<PackageReleaseNotes>Fixed false positive on conversion operators with in argument.</PackageReleaseNotes>
18+
<PackageTags>NonCopyable, analyzers</PackageTags>
19+
<NoPackageAnalysis>true</NoPackageAnalysis>
20+
</PropertyGroup>
21+
22+
<ItemGroup>
23+
<ProjectReference Remove="$(MSBuildThisFile)" />
24+
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="2.6.0" PrivateAssets="all" />
25+
</ItemGroup>
26+
27+
</Project>
Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
using System.Collections.Immutable;
2+
using Microsoft.CodeAnalysis;
3+
using Microsoft.CodeAnalysis.CSharp;
4+
using Microsoft.CodeAnalysis.Diagnostics;
5+
using Microsoft.CodeAnalysis.Operations;
6+
7+
namespace NonCopyable
8+
{
9+
[DiagnosticAnalyzer(LanguageNames.CSharp)]
10+
public class NonCopyableAnalyzer : DiagnosticAnalyzer
11+
{
12+
private static DiagnosticDescriptor CreateRule(int num, string type)
13+
=> new DiagnosticDescriptor("NoCopy" + num.ToString("00"), "non-copyable", "🚫 " + type + ". '{0}' is non-copyable.", "Correction", DiagnosticSeverity.Error, isEnabledByDefault: true);
14+
15+
private static DiagnosticDescriptor FieldDeclarationRule = CreateRule(1, "field declaration");
16+
private static DiagnosticDescriptor InitializerRule = CreateRule(2, "initializer");
17+
private static DiagnosticDescriptor AssignmentRule = CreateRule(3, "assignment");
18+
private static DiagnosticDescriptor ArgumentRule = CreateRule(4, "argument");
19+
private static DiagnosticDescriptor ReturnRule = CreateRule(5, "return");
20+
private static DiagnosticDescriptor ConversionRule = CreateRule(6, "conversion");
21+
private static DiagnosticDescriptor PatternRule = CreateRule(7, "pattern matching");
22+
private static DiagnosticDescriptor TupleRule = CreateRule(8, "tuple");
23+
private static DiagnosticDescriptor MemberRule = CreateRule(9, "member reference");
24+
private static DiagnosticDescriptor ReadOnlyInvokeRule = CreateRule(10, "readonly invoke");
25+
private static DiagnosticDescriptor GenericConstraintRule = CreateRule(11, "generic constraint");
26+
private static DiagnosticDescriptor DelegateRule = CreateRule(12, "delegate");
27+
28+
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(FieldDeclarationRule, InitializerRule, AssignmentRule, ArgumentRule, ReturnRule, ConversionRule, PatternRule, TupleRule, MemberRule, ReadOnlyInvokeRule, GenericConstraintRule, DelegateRule);
29+
30+
public override void Initialize(AnalysisContext context)
31+
{
32+
context.RegisterCompilationStartAction(csc =>
33+
{
34+
csc.RegisterOperationAction(oc =>
35+
{
36+
var op = (ISymbolInitializerOperation)oc.Operation;
37+
CheckCopyability(oc, op.Value, InitializerRule);
38+
}, OperationKind.FieldInitializer,
39+
OperationKind.ParameterInitializer,
40+
OperationKind.PropertyInitializer,
41+
OperationKind.VariableInitializer);
42+
43+
csc.RegisterOperationAction(oc =>
44+
{
45+
// including member initializer
46+
// including collection element initializer
47+
var op = (ISimpleAssignmentOperation)oc.Operation;
48+
if (op.IsRef) return;
49+
CheckCopyability(oc, op.Value, AssignmentRule);
50+
}, OperationKind.SimpleAssignment);
51+
52+
csc.RegisterOperationAction(oc =>
53+
{
54+
// including non-ref extension method invocation
55+
var op = (IArgumentOperation)oc.Operation;
56+
if (op.Parameter.RefKind != RefKind.None) return;
57+
CheckCopyability(oc, op.Value, ArgumentRule);
58+
}, OperationKind.Argument);
59+
60+
csc.RegisterOperationAction(oc =>
61+
{
62+
var op = (IReturnOperation)oc.Operation;
63+
if (op.ReturnedValue == null) return;
64+
CheckCopyability(oc, op.ReturnedValue, ReturnRule);
65+
}, OperationKind.Return,
66+
OperationKind.YieldReturn);
67+
68+
csc.RegisterOperationAction(oc =>
69+
{
70+
var op = (IConversionOperation)oc.Operation;
71+
var v = op.Operand;
72+
if (v.Kind == OperationKind.DefaultValue) return;
73+
var t = v.Type;
74+
if (!t.IsNonCopyable()) return;
75+
76+
if (op.OperatorMethod != null && op.OperatorMethod.Parameters.Length == 1)
77+
{
78+
var parameter = op.OperatorMethod.Parameters[0];
79+
if (parameter.RefKind != RefKind.None) return;
80+
}
81+
82+
if (op.Parent is IForEachLoopOperation &&
83+
op == ((IForEachLoopOperation)op.Parent).Collection &&
84+
op.Conversion.IsIdentity)
85+
{
86+
return;
87+
}
88+
89+
oc.ReportDiagnostic(Diagnostic.Create(ConversionRule, v.Syntax.GetLocation(), t.Name));
90+
}, OperationKind.Conversion);
91+
92+
csc.RegisterOperationAction(oc =>
93+
{
94+
var op = (IArrayInitializerOperation)oc.Operation;
95+
96+
if (!((IArrayTypeSymbol)((IArrayInitializerOperation)op.Parent).Type).ElementType.IsNonCopyable()) return;
97+
98+
foreach (var v in op.ElementValues)
99+
{
100+
CheckCopyability(oc, v, InitializerRule);
101+
}
102+
}, OperationKind.ArrayInitializer);
103+
104+
csc.RegisterOperationAction(oc =>
105+
{
106+
var op = (ICollectionElementInitializerOperation)oc.Operation;
107+
108+
if (!HasNonCopyableParameter(op.AddMethod)) return;
109+
110+
foreach (var a in op.Arguments)
111+
{
112+
CheckCopyability(oc, a, InitializerRule);
113+
}
114+
}, OperationKind.CollectionElementInitializer);
115+
116+
csc.RegisterOperationAction(oc =>
117+
{
118+
var op = (IDeclarationPatternOperation)oc.Operation;
119+
var t = ((ILocalSymbol)op.DeclaredSymbol).Type;
120+
if (!t.IsNonCopyable()) return;
121+
oc.ReportDiagnostic(Diagnostic.Create(PatternRule, op.Syntax.GetLocation(), t.Name));
122+
}, OperationKind.DeclarationPattern);
123+
124+
csc.RegisterOperationAction(oc =>
125+
{
126+
var op = (ITupleOperation)oc.Operation;
127+
128+
// exclude ParenthesizedVariableDesignationSyntax
129+
if (op.Syntax.Kind() != SyntaxKind.TupleExpression) return;
130+
131+
foreach (var v in op.Elements)
132+
{
133+
CheckCopyability(oc, v, TupleRule);
134+
}
135+
}, OperationKind.Tuple);
136+
137+
csc.RegisterOperationAction(oc =>
138+
{
139+
// instance property/event should not be referenced with in parameter/ref readonly local/readonly field
140+
var op = (IMemberReferenceOperation)oc.Operation;
141+
CheckInstanceReadonly(oc, op.Instance, MemberRule);
142+
}, OperationKind.PropertyReference,
143+
OperationKind.EventReference);
144+
145+
csc.RegisterOperationAction(oc =>
146+
{
147+
// instance method should not be invoked with in parameter/ref readonly local/readonly field
148+
var op = (IInvocationOperation)oc.Operation;
149+
150+
CheckGenericConstraints(oc, op, GenericConstraintRule);
151+
CheckInstanceReadonly(oc, op.Instance, ReadOnlyInvokeRule);
152+
153+
}, OperationKind.Invocation);
154+
155+
csc.RegisterOperationAction(oc =>
156+
{
157+
// delagate creation
158+
var op = (IMemberReferenceOperation)oc.Operation;
159+
if (op.Instance == null) return;
160+
if (!op.Instance.Type.IsNonCopyable()) return;
161+
oc.ReportDiagnostic(Diagnostic.Create(DelegateRule, op.Instance.Syntax.GetLocation(), op.Instance.Type.Name));
162+
}, OperationKind.MethodReference);
163+
164+
csc.RegisterSymbolAction(sac =>
165+
{
166+
var f = (IFieldSymbol)sac.Symbol;
167+
if (f.IsStatic) return;
168+
if (!f.Type.IsNonCopyable()) return;
169+
if (f.ContainingType.IsReferenceType) return;
170+
if (f.ContainingType.IsNonCopyable()) return;
171+
sac.ReportDiagnostic(Diagnostic.Create(FieldDeclarationRule, f.DeclaringSyntaxReferences[0].GetSyntax().GetLocation(), f.Type.Name));
172+
}, SymbolKind.Field);
173+
});
174+
175+
// not supported yet:
176+
// OperationKind.CompoundAssignment,
177+
// OperationKind.UnaryOperator,
178+
// OperationKind.BinaryOperator,
179+
}
180+
181+
private static void CheckGenericConstraints(in OperationAnalysisContext oc, IInvocationOperation op, DiagnosticDescriptor rule)
182+
{
183+
var m = op.TargetMethod;
184+
185+
if (m.IsGenericMethod)
186+
{
187+
var parameters = m.TypeParameters;
188+
var arguments = m.TypeArguments;
189+
for (int i = 0; i < parameters.Length; i++)
190+
{
191+
var p = parameters[i];
192+
var a = arguments[i];
193+
194+
if (a.IsNonCopyable() && !p.IsNonCopyable())
195+
oc.ReportDiagnostic(Diagnostic.Create(rule, op.Syntax.GetLocation(), a.Name));
196+
}
197+
}
198+
}
199+
200+
private static void CheckInstanceReadonly(in OperationAnalysisContext oc, IOperation instance, DiagnosticDescriptor rule)
201+
{
202+
if (instance == null) return;
203+
204+
var t = instance.Type;
205+
if (!t.IsNonCopyable()) return;
206+
207+
if (IsInstanceReadonly(instance))
208+
{
209+
oc.ReportDiagnostic(Diagnostic.Create(rule, instance.Syntax.GetLocation(), t.Name));
210+
}
211+
}
212+
213+
private static bool IsInstanceReadonly(IOperation instance)
214+
{
215+
bool isReadOnly = false;
216+
switch (instance)
217+
{
218+
case IFieldReferenceOperation r:
219+
isReadOnly = r.Field.IsReadOnly;
220+
break;
221+
case ILocalReferenceOperation r:
222+
isReadOnly = r.Local.RefKind == RefKind.In;
223+
break;
224+
case IParameterReferenceOperation r:
225+
isReadOnly = r.Parameter.RefKind == RefKind.In;
226+
break;
227+
}
228+
229+
return isReadOnly;
230+
}
231+
232+
private static bool HasNonCopyableParameter(IMethodSymbol m)
233+
{
234+
foreach (var p in m.Parameters)
235+
{
236+
if(p.RefKind == RefKind.None)
237+
{
238+
if (p.Type.IsNonCopyable()) return true;
239+
}
240+
}
241+
return false;
242+
}
243+
244+
private static void CheckCopyability(in OperationAnalysisContext oc, IOperation v, DiagnosticDescriptor rule)
245+
{
246+
var t = v.Type;
247+
if (!t.IsNonCopyable()) return;
248+
if (v.CanCopy()) return;
249+
oc.ReportDiagnostic(Diagnostic.Create(rule, v.Syntax.GetLocation(), t.Name));
250+
}
251+
}
252+
}

0 commit comments

Comments
 (0)