Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for detecting preview-ness in dependencies #5395

Merged
merged 3 commits into from
Aug 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
pgovind marked this conversation as resolved.
Show resolved Hide resolved
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