Skip to content

Commit

Permalink
Support for detecting preview-ness in dependencies (#5395)
Browse files Browse the repository at this point in the history
* Support for detecting preview-ness in dependencies

Unit tests

* Address comments

* Change ContainingType -> ContainingSymbol
  • Loading branch information
Prashanth Govindarajan authored Aug 23, 2021
1 parent 20d227a commit 3f186ae
Show file tree
Hide file tree
Showing 3 changed files with 224 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ private static void ProcessPropertyOrMethodAttributes(SymbolAnalysisContext cont
{
if (SymbolIsAnnotatedAsPreview(baseInterfaceMember, requiresPreviewFeaturesSymbols, previewFeatureAttributeSymbol))
{
string baseInterfaceMemberName = baseInterfaceMember.ContainingType != null ? baseInterfaceMember.ContainingType.Name + "." + baseInterfaceMember.Name : baseInterfaceMember.Name;
string baseInterfaceMemberName = baseInterfaceMember.ContainingSymbol != null ? baseInterfaceMember.ContainingSymbol.Name + "." + baseInterfaceMember.Name : baseInterfaceMember.Name;
context.ReportDiagnostic(propertyOrMethodSymbol.CreateDiagnostic(ImplementsPreviewMethodRule, propertyOrMethodSymbol.Name, baseInterfaceMemberName));
}
}
Expand All @@ -404,7 +404,7 @@ private static void ProcessPropertyOrMethodAttributes(SymbolAnalysisContext cont
ISymbol overridden = propertyOrMethodSymbol.GetOverriddenMember();
if (SymbolIsAnnotatedAsPreview(overridden, requiresPreviewFeaturesSymbols, previewFeatureAttributeSymbol))
{
string overriddenName = overridden.ContainingType != null ? overridden.ContainingType.Name + "." + overridden.Name : overridden.Name;
string overriddenName = overridden.ContainingSymbol != null ? overridden.ContainingSymbol.Name + "." + overridden.Name : overridden.Name;
context.ReportDiagnostic(propertyOrMethodSymbol.CreateDiagnostic(OverridesPreviewMethodRule, propertyOrMethodSymbol.Name, overriddenName));
}
}
Expand Down Expand Up @@ -551,11 +551,11 @@ private static bool OperationUsesPreviewFeatures(OperationAnalysisContext contex
if (SymbolIsAnnotatedOrUsesPreviewTypes(methodSymbol, requiresPreviewFeaturesSymbols, previewFeatureAttributeSymbol, out referencedPreviewSymbol))
{
// Constructor symbols have the name .ctor. Return the containing type instead so we get meaningful names in the diagnostic message
referencedPreviewSymbol = referencedPreviewSymbol.ContainingType;
referencedPreviewSymbol = referencedPreviewSymbol.ContainingSymbol;
return true;
}

if (SymbolIsAnnotatedOrUsesPreviewTypes(methodSymbol.ContainingType, requiresPreviewFeaturesSymbols, previewFeatureAttributeSymbol, out referencedPreviewSymbol))
if (SymbolIsAnnotatedOrUsesPreviewTypes(methodSymbol.ContainingSymbol, requiresPreviewFeaturesSymbols, previewFeatureAttributeSymbol, out referencedPreviewSymbol))
{
return true;
}
Expand Down Expand Up @@ -626,10 +626,10 @@ private static bool SymbolIsAnnotatedAsPreview(ISymbol symbol, ConcurrentDiction
return true;
}

INamedTypeSymbol? parent = symbol.ContainingType;
if (parent is INamespaceSymbol)
ISymbol? parent = symbol.ContainingSymbol;
while (parent is INamespaceSymbol)
{
parent = parent.ContainingType;
parent = parent.ContainingSymbol;
}

if (parent != null)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information.

using System.Threading.Tasks;
using Test.Utilities;
using Xunit;
using VerifyCS = Test.Utilities.CSharpCodeFixVerifier<
Microsoft.NetCore.Analyzers.Runtime.DetectPreviewFeatureAnalyzer,
Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>;

namespace Microsoft.NetCore.Analyzers.Runtime.UnitTests
{
public partial class DetectPreviewFeatureUnitTests
{
[Theory]
[InlineData("assembly")]
[InlineData("module")]
public async Task TestAssemblyDoesntUsePreviewDependency(string assemblyOrModule)
{
// No diagnostic when we don't use any APIs from an assembly marked with Preview
string csCurrentAssemblyCode = @"
using System;
public class Program
{
public void ProgramMethod()
{
new Program();
}
}";
string csDepedencyCode = @$"[{assemblyOrModule}: System.Runtime.Versioning.RequiresPreviewFeatures]";

var test = SetupDependencyAndTestCSWithOneSourceFile(csCurrentAssemblyCode, csDepedencyCode);
await test.RunAsync();
}

[Theory]
[InlineData("assembly")]
[InlineData("module")]
public async Task TestCallAPIsFromAssemblyMarkedAsPreview(string assemblyOrModule)
{
string csDependencyCode = @"
public class Library
{
public void AMethod() { }
private int _property;
public int AProperty
{
get => 1;
set
{
_property = value;
}
}
}";
csDependencyCode = @$"[{assemblyOrModule}: System.Runtime.Versioning.RequiresPreviewFeatures] {csDependencyCode}";

string csCurrentAssemblyCode = @"
using System;
public class Program
{
public void ProgramMethod()
{
Library library = {|#1:new Library()|};
{|#0:library.AMethod()|};
int prop = {|#2:library.AProperty|};
}
}";
var test = SetupDependencyAndTestCSWithOneSourceFile(csCurrentAssemblyCode, csDependencyCode);
test.ExpectedDiagnostics.Add(VerifyCS.Diagnostic(DetectPreviewFeatureAnalyzer.GeneralPreviewFeatureAttributeRule).WithLocation(0).WithArguments("AMethod"));
test.ExpectedDiagnostics.Add(VerifyCS.Diagnostic(DetectPreviewFeatureAnalyzer.GeneralPreviewFeatureAttributeRule).WithLocation(1).WithArguments("Library"));
test.ExpectedDiagnostics.Add(VerifyCS.Diagnostic(DetectPreviewFeatureAnalyzer.GeneralPreviewFeatureAttributeRule).WithLocation(2).WithArguments("AProperty"));
await test.RunAsync();
}

[Theory]
[InlineData("assembly")]
[InlineData("module")]
public async Task TestNoCallsToPreviewDependency(string assemblyOrModule)
{
string csDependencyCode = @"
public class Library
{
public void AMethod() { }
private int _property;
public int AProperty
{
get => 1;
set
{
_property = value;
}
}
}";
csDependencyCode = @$"[{assemblyOrModule}: System.Runtime.Versioning.RequiresPreviewFeatures] {csDependencyCode}";

string csCurrentAssemblyCode = @"
using System;
public class Program
{
public void ProgramMethod()
{
}
}";
var test = SetupDependencyAndTestCSWithOneSourceFile(csCurrentAssemblyCode, csDependencyCode);
await test.RunAsync();
}

[Fact]
public async Task TestMixtureOfPreviewAPIsInDependency()
{
string csDependencyCode = @"
public class Library
{
public void AMethod()
{
#pragma warning disable CA2252
APreviewMethod();
#pragma warning enable CA2252
}
[System.Runtime.Versioning.RequiresPreviewFeatures]
public void APreviewMethod() { }
}";

string csCurrentAssemblyCode = @"
using System;
public class Program
{
public void ProgramMethod()
{
Library library = new Library();
library.AMethod();
{|#0:library.APreviewMethod()|};
}
}";
var test = SetupDependencyAndTestCSWithOneSourceFile(csCurrentAssemblyCode, csDependencyCode);
test.ExpectedDiagnostics.Add(VerifyCS.Diagnostic(DetectPreviewFeatureAnalyzer.GeneralPreviewFeatureAttributeRule).WithLocation(0).WithArguments("APreviewMethod"));
await test.RunAsync();
}

[Fact]
public async Task TestDeepNestingOfPreviewAPIsInDependency()
{
string csDependencyCode = @"
public class Library
{
[System.Runtime.Versioning.RequiresPreviewFeatures]
public class NestedClass0
{
public class NestedClass1
{
public class NestedClass2
{
public class NestedClass3
{
public void APreviewMethod() { }
}
}
}
}
}";

string csCurrentAssemblyCode = @"
using System;
public class Program
{
public void ProgramMethod()
{
Library.NestedClass0.NestedClass1.NestedClass2.NestedClass3 nestedClass = {|#0:new()|};
{|#1:nestedClass.APreviewMethod()|};
}
}";
var test = SetupDependencyAndTestCSWithOneSourceFile(csCurrentAssemblyCode, csDependencyCode);
test.ExpectedDiagnostics.Add(VerifyCS.Diagnostic(DetectPreviewFeatureAnalyzer.GeneralPreviewFeatureAttributeRule).WithLocation(0).WithArguments("NestedClass3"));
test.ExpectedDiagnostics.Add(VerifyCS.Diagnostic(DetectPreviewFeatureAnalyzer.GeneralPreviewFeatureAttributeRule).WithLocation(1).WithArguments("APreviewMethod"));
await test.RunAsync();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,45 @@ private static VerifyCS.Test TestCS(string csInput)
{
return new VerifyCS.Test
{
ReferenceAssemblies = AdditionalMetadataReferences.Net60,
LanguageVersion = CodeAnalysis.CSharp.LanguageVersion.CSharp10,
TestState =
{
Sources =
{
csInput
}
},
},
};
}

private static VerifyCS.Test SetupDependencyAndTestCSWithOneSourceFile(string csInput, string csDependencyCode)
{
return new VerifyCS.Test
{
ReferenceAssemblies = AdditionalMetadataReferences.Net60,
LanguageVersion = CodeAnalysis.CSharp.LanguageVersion.CSharp10,
TestState =
{
Sources =
{
csInput
},
AdditionalProjects =
{
["PreviewAssembly"] =
{
Sources =
{
("/PreviewAssembly/AssemblyInfo.g.cs", csDependencyCode)
},
},
},
AdditionalProjectReferences =
{
"PreviewAssembly",
},
},
};
}

Expand Down

0 comments on commit 3f186ae

Please sign in to comment.