Skip to content

Commit a7c9730

Browse files
committed
Private ref warning
1 parent 290d418 commit a7c9730

File tree

8 files changed

+166
-7
lines changed

8 files changed

+166
-7
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Publish
1111
*.Cache
1212
msbuild.log
1313
package-lock.json
14+
.DS_Store
1415

1516
/packages
1617
/TestResults
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Collections.Immutable;
7+
using System.IO;
8+
using System.Linq;
9+
using System.Text;
10+
using System.Threading.Tasks;
11+
using Microsoft.CodeAnalysis;
12+
using Microsoft.CodeAnalysis.Diagnostics;
13+
14+
namespace Microsoft.Azure.WebJobs.Script.Description.DotNet.CSharp.Analyzers
15+
{
16+
[DiagnosticAnalyzer(LanguageNames.CSharp)]
17+
public class InvalidPrivateMetadataReferenceAnalyzer : DiagnosticAnalyzer
18+
{
19+
private const string Title = "Invalid private metadata reference";
20+
private const string MessageFormat = "The reference '{0}' is invalid. Private assembly references must include the file extension. Try using '{1}'.";
21+
private readonly DiagnosticDescriptor _supportedDiagnostic;
22+
private static readonly ImmutableArray<string> _validMetadataExtensions = ImmutableArray.Create(".dll", ".exe");
23+
24+
public InvalidPrivateMetadataReferenceAnalyzer()
25+
{
26+
_supportedDiagnostic = new DiagnosticDescriptor(DotNetConstants.InvalidPrivateMetadataReferenceCode,
27+
Title, MessageFormat, "Function", DiagnosticSeverity.Warning, true);
28+
}
29+
30+
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
31+
=> ImmutableArray.Create(_supportedDiagnostic);
32+
33+
public override void Initialize(AnalysisContext context)
34+
{
35+
context.RegisterCompilationAction(AnalyzeCompilation);
36+
}
37+
38+
private void AnalyzeCompilation(CompilationAnalysisContext context)
39+
{
40+
// Find CS0006: Metadata file '{0}' could not be found
41+
Diagnostic invalidMetadataDiagnostic = context.Compilation
42+
.GetDiagnostics().FirstOrDefault(d => string.Compare(d.Id, "CS0006") == 0);
43+
44+
if (invalidMetadataDiagnostic != null)
45+
{
46+
var argument = invalidMetadataDiagnostic.GetDiagnosticMessageArguments().First().ToString();
47+
var resolver = context.Compilation.Options.MetadataReferenceResolver as IFunctionMetadataResolver;
48+
if (argument != null && !_validMetadataExtensions.Contains(Path.GetExtension(argument)) &&
49+
resolver != null && resolver.TryResolvePrivateAssembly(argument, out string path))
50+
{
51+
var diagnostic = Diagnostic.Create(_supportedDiagnostic,
52+
invalidMetadataDiagnostic.Location,
53+
argument,
54+
Path.GetFileName(path));
55+
56+
context.ReportDiagnostic(diagnostic);
57+
}
58+
}
59+
}
60+
}
61+
}

src/WebJobs.Script/Description/DotNet/Compilation/CSharp/CSharpCompilation.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@ public sealed class CSharpCompilation : IDotNetCompilation
2626

2727
// Simply getting the built in analyzers for now.
2828
// This should eventually be enhanced to dynamically discover/load analyzers.
29-
private static ImmutableArray<DiagnosticAnalyzer> _analyzers = ImmutableArray.Create<DiagnosticAnalyzer>(new InvalidFileMetadataReferenceAnalyzer(), new AsyncVoidAnalyzer());
29+
private static ImmutableArray<DiagnosticAnalyzer> _analyzers = ImmutableArray.Create<DiagnosticAnalyzer>(
30+
new InvalidFileMetadataReferenceAnalyzer(),
31+
new AsyncVoidAnalyzer(),
32+
new InvalidPrivateMetadataReferenceAnalyzer());
3033

3134
public CSharpCompilation(Compilation compilation)
3235
{

src/WebJobs.Script/Description/DotNet/DotNetConstants.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,6 @@ public static class DotNetConstants
1717
public const string InvalidFileMetadataReferenceCode = "AF006";
1818
public const string InvalidEntryPointNameCompilationCode = "AF007";
1919
public const string AsyncVoidCode = "AF008";
20+
public const string InvalidPrivateMetadataReferenceCode = "AF009";
2021
}
2122
}

src/WebJobs.Script/Description/DotNet/FunctionMetadataResolver.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -245,19 +245,19 @@ public Assembly ResolveAssembly(string assemblyName)
245245
return assembly;
246246
}
247247

248-
private bool HasValidAssemblyFileExtension(string reference)
249-
{
250-
return _assemblyExtensions.Contains(Path.GetExtension(reference));
251-
}
252-
253-
private bool TryResolvePrivateAssembly(string name, out string assemblyPath)
248+
public bool TryResolvePrivateAssembly(string name, out string assemblyPath)
254249
{
255250
var names = GetProbingFilePaths(name);
256251
assemblyPath = names.FirstOrDefault(n => File.Exists(n));
257252

258253
return assemblyPath != null;
259254
}
260255

256+
private bool HasValidAssemblyFileExtension(string reference)
257+
{
258+
return _assemblyExtensions.Contains(Path.GetExtension(reference));
259+
}
260+
261261
private IEnumerable<string> GetProbingFilePaths(string name)
262262
{
263263
var assemblyName = new AssemblyName(name);

src/WebJobs.Script/Description/DotNet/IFunctionMetadataResolver.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,7 @@ public interface IFunctionMetadataResolver
2626
bool RequiresPackageRestore(FunctionMetadata metadata);
2727

2828
bool TryGetPackageReference(string referenceName, out PackageReference package);
29+
30+
bool TryResolvePrivateAssembly(string name, out string assemblyPath);
2931
}
3032
}

src/WebJobs.Script/WebJobs.Script.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,7 @@
434434
<Compile Include="Description\Compilation\ICompilation.cs" />
435435
<Compile Include="Description\DiagnosticSeverityExtensions.cs" />
436436
<Compile Include="Description\DotNet\Compilation\CSharp\Analyzers\AsyncVoidAnalyzer.cs" />
437+
<Compile Include="Description\DotNet\Compilation\CSharp\Analyzers\InvalidPrivateMetadataReferenceAnalyzer.cs" />
437438
<Compile Include="Description\DotNet\Compilation\IDotNetCompilation.cs" />
438439
<Compile Include="Description\DotNet\Compilation\CSharp\Analyzers\InvalidFileMetadataReferenceAnalyzer.cs" />
439440
<Compile Include="Description\DotNet\Compilation\CSharp\CSharpCompilation.cs" />

test/WebJobs.Script.Tests/Description/DotNet/CSharp/CSharpCompilationTests.cs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the MIT License. See License.txt in the project root for license information.
33

4+
using System.Collections.Generic;
45
using System.Collections.Immutable;
6+
using System.IO;
57
using System.Linq;
8+
using System.Reflection;
9+
using System.Threading.Tasks;
610
using Microsoft.Azure.WebJobs.Script.Description;
711
using Microsoft.CodeAnalysis;
812
using Microsoft.CodeAnalysis.CSharp.Scripting;
@@ -40,6 +44,7 @@ public void Run(){
4044
invalid.SomeMethod();
4145
}";
4246
Script<object> script = CSharpScript.Create(code);
47+
4348
var compilation = new CSharpCompilation(script.GetCompilation());
4449

4550
ImmutableArray<Diagnostic> diagnostics = compilation.GetDiagnostics();
@@ -63,5 +68,90 @@ public async void Run(){
6368
diagnostic.GetMessage());
6469
Assert.Equal(DiagnosticSeverity.Warning, diagnostic.Severity);
6570
}
71+
72+
[Fact]
73+
public void InvalidPrivateReference_ReturnsExpectedDiagnostics()
74+
{
75+
string code = @"
76+
#r ""PrivateReference""
77+
public async void Run(){
78+
await System.Threading.Tasks.Task.Run(() => {});
79+
}";
80+
var file = Path.GetTempFileName();
81+
file = Path.ChangeExtension(file, "dll");
82+
83+
Script<object> script = CSharpScript.Create(code)
84+
.WithOptions(ScriptOptions.Default.WithMetadataResolver(new TestFunctionMetadataResolver(file)));
85+
86+
var compilation = new CSharpCompilation(script.GetCompilation());
87+
88+
var diagnostic = compilation
89+
.GetDiagnostics()
90+
.First(d => string.Equals(d.Id, DotNetConstants.InvalidPrivateMetadataReferenceCode, System.StringComparison.Ordinal));
91+
92+
Assert.Equal(DiagnosticSeverity.Warning, diagnostic.Severity);
93+
Assert.Contains(Path.GetFileName(file), diagnostic.GetMessage());
94+
}
95+
96+
private class TestFunctionMetadataResolver : MetadataReferenceResolver, IFunctionMetadataResolver
97+
{
98+
private readonly string _privateAssemblyFilePath;
99+
100+
public TestFunctionMetadataResolver(string privateAssemblyFilePath)
101+
{
102+
_privateAssemblyFilePath = privateAssemblyFilePath;
103+
}
104+
105+
public ScriptOptions CreateScriptOptions()
106+
{
107+
throw new System.NotImplementedException();
108+
}
109+
110+
public override bool Equals(object other)
111+
{
112+
throw new System.NotImplementedException();
113+
}
114+
115+
public IReadOnlyCollection<string> GetCompilationReferences()
116+
{
117+
throw new System.NotImplementedException();
118+
}
119+
120+
public override int GetHashCode()
121+
{
122+
throw new System.NotImplementedException();
123+
}
124+
125+
public bool RequiresPackageRestore(FunctionMetadata metadata)
126+
{
127+
throw new System.NotImplementedException();
128+
}
129+
130+
public Assembly ResolveAssembly(string assemblyName)
131+
{
132+
throw new System.NotImplementedException();
133+
}
134+
135+
public override ImmutableArray<PortableExecutableReference> ResolveReference(string reference, string baseFilePath, MetadataReferenceProperties properties)
136+
{
137+
return ImmutableArray<PortableExecutableReference>.Empty;
138+
}
139+
140+
public Task<PackageRestoreResult> RestorePackagesAsync()
141+
{
142+
throw new System.NotImplementedException();
143+
}
144+
145+
public bool TryGetPackageReference(string referenceName, out PackageReference package)
146+
{
147+
throw new System.NotImplementedException();
148+
}
149+
150+
public bool TryResolvePrivateAssembly(string name, out string assemblyPath)
151+
{
152+
assemblyPath = _privateAssemblyFilePath;
153+
return true;
154+
}
155+
}
66156
}
67157
}

0 commit comments

Comments
 (0)